1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2022 The Khronos Group Inc.
6 * Copyright (c) 2022 Valve Corporation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*
21 * \file
22 * \brief Tests involving dynamic patch control points
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktPipelineDynamicControlPoints.hpp"
26 #include "vktTestCase.hpp"
27 #include "vkPrograms.hpp"
28 #include "vkRefUtil.hpp"
29 #include "vktTestCaseUtil.hpp"
30 #include "vkImageWithMemory.hpp"
31
32 #include "vktPipelineImageUtil.hpp"
33 #include "vktTestCase.hpp"
34
35 #include "vkDefs.hpp"
36 #include "vkTypeUtil.hpp"
37 #include "vkQueryUtil.hpp"
38 #include "vkObjUtil.hpp"
39 #include "vkBufferWithMemory.hpp"
40 #include "vkImageWithMemory.hpp"
41 #include "vkBuilderUtil.hpp"
42 #include "vkCmdUtil.hpp"
43 #include "vkImageUtil.hpp"
44
45 #include "tcuVector.hpp"
46 #include "tcuMaybe.hpp"
47 #include "tcuImageCompare.hpp"
48 #include "tcuDefs.hpp"
49 #include "tcuTextureUtil.hpp"
50 #include "tcuTestLog.hpp"
51 #include "tcuVectorUtil.hpp"
52 #include "tcuStringTemplate.hpp"
53
54 #include "deUniquePtr.hpp"
55 #include "deStringUtil.hpp"
56
57 #include <vector>
58 #include <sstream>
59 #include <algorithm>
60 #include <utility>
61 #include <iterator>
62 #include <string>
63 #include <limits>
64 #include <memory>
65 #include <functional>
66 #include <cstddef>
67 #include <set>
68
69 namespace vkt
70 {
71 namespace pipeline
72 {
73
74 struct TestConfig {
75 vk::PipelineConstructionType constructionType;
76 bool changeOutput;
77 bool firstClockwise;
78 bool secondClockwise;
79 vk::VkCullModeFlags cullMode;
80 tcu::Vec4 expectedFirst;
81 tcu::Vec4 expectedSecond;
82 };
83
84 class DynamicControlPointsTestCase : public vkt::TestCase
85 {
86 public:
87 DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, const std::string& description, TestConfig config);
88 void initPrograms (vk::SourceCollections& programCollection) const override;
89 TestInstance* createInstance (Context& context) const override;
90 void checkSupport (Context& context) const override;
91
92 private:
93 TestConfig m_config;
94 };
95
96
97 class DynamicControlPointsTestInstance : public vkt::TestInstance
98 {
99 public:
100 DynamicControlPointsTestInstance(Context& context, TestConfig config);
101 virtual tcu::TestStatus iterate (void);
102 private:
103 TestConfig m_config;
104 };
105
106
DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, const std::string& description, TestConfig config)107 DynamicControlPointsTestCase::DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, const std::string& description,
108 TestConfig config) : vkt::TestCase (context, name, description), m_config(config)
109 {
110 }
111
checkSupport(Context& context) const112 void DynamicControlPointsTestCase::checkSupport(Context& context) const
113 {
114 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER);
115 checkPipelineLibraryRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_config.constructionType);
116 const auto& eds2Features = context.getExtendedDynamicState2FeaturesEXT();
117 if (!eds2Features.extendedDynamicState2PatchControlPoints) {
118 TCU_THROW(NotSupportedError, "Dynamic patch control points aren't supported");
119 }
120 }
121
initPrograms(vk::SourceCollections& collection) const122 void DynamicControlPointsTestCase::initPrograms(vk::SourceCollections& collection) const
123 {
124 const std::string firstWinding = m_config.firstClockwise ? "cw" : "ccw";
125 const std::string secondWinding = m_config.secondClockwise ? "cw" : "ccw";
126
127 {
128 std::ostringstream src;
129 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
130 << "vec2 positions[6] = vec2[](\n"
131 << " vec2(-1.0, -1.0),"
132 << " vec2(-1.0, 1.0),"
133 << " vec2(1.0, -1.0),"
134 << " vec2(1.0, -1.0),"
135 << " vec2(-1.0, 1.0),"
136 << " vec2(1.0, 1.0)"
137 << ");\n"
138 << "void main() {\n"
139 << " gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);\n"
140 << "}";
141 collection.glslSources.add("vert") << glu::VertexSource(src.str());
142 }
143
144 {
145 std::ostringstream src;
146 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
147 << "layout(location = 0) out vec4 outColor;\n"
148 << "layout(location = 0) in vec3 fragColor;"
149 << "void main() {\n"
150 << " outColor = vec4(fragColor, 1.0);\n"
151 << "}";
152 collection.glslSources.add("frag") << glu::FragmentSource(src.str());
153 }
154
155 {
156 std::ostringstream src;
157 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
158 << "layout(vertices = 3) out;\n"
159 << "void main (void)\n"
160 << "{\n"
161 << " gl_TessLevelInner[0] = 2.0;\n"
162 << " gl_TessLevelOuter[0] = 2.0;\n"
163 << " gl_TessLevelOuter[1] = 2.0;\n"
164 << " gl_TessLevelOuter[2] = 2.0;\n"
165 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
166 << "}\n";
167 collection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
168 }
169
170 {
171 std::ostringstream src;
172 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
173 << "layout(triangles, " << firstWinding << ") in;\n"
174 << "layout(location = 0) out vec3 fragColor;"
175 << "\n"
176 << "void main (void)\n"
177 << "{\n"
178 << " vec4 p0 = gl_TessCoord.x * gl_in[0].gl_Position;\n"
179 << " vec4 p1 = gl_TessCoord.y * gl_in[1].gl_Position;\n"
180 << " vec4 p2 = gl_TessCoord.z * gl_in[2].gl_Position;\n"
181 << " gl_Position = p0 + p1 + p2;\n"
182 << " fragColor = vec3(1.0, 0.0, 1.0); "
183 << "}\n";
184 collection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
185 }
186
187 {
188 std::ostringstream src;
189 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
190 << "layout(vertices = " << (m_config.changeOutput ? 4 : 3) << ") out;\n"
191 << "void main (void)\n"
192 << "{\n"
193 << " gl_TessLevelInner[0] = 2;\n"
194 << " gl_TessLevelOuter[0] = 2.0;\n"
195 << " gl_TessLevelOuter[1] = 2.0;\n"
196 << " gl_TessLevelOuter[2] = 2.0;\n"
197 << "if (gl_InvocationID < 3) {"
198 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
199 << "} else {"
200 << " gl_out[gl_InvocationID].gl_Position = vec4(1.0, 0.0, 1.0, 1.0);"
201 << "}"
202 << "}\n";
203 collection.glslSources.add("tesc2") << glu::TessellationControlSource(src.str());
204 }
205
206 {
207 std::ostringstream src;
208 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
209 << "layout(triangles, " << secondWinding << ") in;\n"
210 << "layout(location = 0) out vec3 fragColor;"
211 << "\n"
212 << "void main (void)\n"
213 << "{\n"
214 << " vec4 p0 = gl_TessCoord.x * gl_in[0].gl_Position;\n"
215 << " vec4 p1 = gl_TessCoord.y * gl_in[1].gl_Position;\n"
216 << " vec4 p2 = gl_TessCoord.z * gl_in[2].gl_Position;\n"
217 << " gl_Position = p0 + p1 + p2;\n";
218 if (m_config.changeOutput) {
219 src << " fragColor = vec3(gl_in[3].gl_Position.xyz);";
220 } else {
221 src << " fragColor = vec3(1.0, 0.0, 1.0);";
222 }
223 src << "}\n";
224 collection.glslSources.add("tese2") << glu::TessellationEvaluationSource(src.str());
225 }
226 }
227
createInstance(Context& context) const228 TestInstance* DynamicControlPointsTestCase::createInstance(Context& context) const
229 {
230 return new DynamicControlPointsTestInstance(context, m_config);
231 }
232
DynamicControlPointsTestInstance(Context& context, TestConfig config)233 DynamicControlPointsTestInstance::DynamicControlPointsTestInstance(Context& context, TestConfig config) :
234 vkt::TestInstance (context), m_config (config) { }
235
236 //make a buffer to read an image back after rendering
makeBufferForImage(const vk::DeviceInterface& vkd, const vk::VkDevice device, vk::Allocator& allocator, tcu::TextureFormat tcuFormat, vk::VkExtent3D imageExtent)237 std::unique_ptr<vk::BufferWithMemory> makeBufferForImage(const vk::DeviceInterface& vkd, const vk::VkDevice device, vk::Allocator& allocator, tcu::TextureFormat tcuFormat, vk::VkExtent3D imageExtent)
238 {
239 const auto outBufferSize = static_cast<vk::VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) * imageExtent.width * imageExtent.height);
240 const auto outBufferUsage = vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT;
241 const auto outBufferInfo = makeBufferCreateInfo(outBufferSize, outBufferUsage);
242 auto outBuffer = std::unique_ptr<vk::BufferWithMemory>(new vk::BufferWithMemory(vkd, device, allocator, outBufferInfo, vk::MemoryRequirement::HostVisible));
243
244 return outBuffer;
245 }
246
iterate(void)247 tcu::TestStatus DynamicControlPointsTestInstance::iterate(void)
248 {
249 const auto& vkd = m_context.getDeviceInterface();
250 const auto device = m_context.getDevice();
251 auto& alloc = m_context.getDefaultAllocator();
252 auto imageFormat = vk::VK_FORMAT_R8G8B8A8_UNORM;
253 auto imageExtent = vk::makeExtent3D(4, 4, 1u);
254 auto mappedFormat = mapVkFormat(imageFormat);
255
256 const tcu::IVec3 imageDim (static_cast<int>(imageExtent.width), static_cast<int>(imageExtent.height), static_cast<int>(imageExtent.depth));
257 const tcu::IVec2 imageSize (imageDim.x(), imageDim.y());
258
259 de::MovePtr<vk::ImageWithMemory> colorAttachment;
260
261 vk::GraphicsPipelineWrapper pipeline1(vkd, device, m_config.constructionType);
262 vk::GraphicsPipelineWrapper pipeline2(vkd, device, m_config.constructionType);
263 const auto qIndex = m_context.getUniversalQueueFamilyIndex();
264
265 const auto imageUsage = static_cast<vk::VkImageUsageFlags>(vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
266 const vk::VkImageCreateInfo imageCreateInfo =
267 {
268 vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
269 nullptr, // const void* pNext;
270 0u, // VkImageCreateFlags flags;
271 vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
272 imageFormat, // VkFormat format;
273 imageExtent, // VkExtent3D extent;
274 1u, // deUint32 mipLevels;
275 1u, // deUint32 arrayLayers;
276 vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
277 vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
278 imageUsage, // VkImageUsageFlags usage;
279 vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
280 0, // deUint32 queueFamilyIndexCount;
281 nullptr, // const deUint32* pQueueFamilyIndices;
282 vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
283 };
284
285 const auto subresourceRange = vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
286 colorAttachment = de::MovePtr<vk::ImageWithMemory>(new vk::ImageWithMemory(vkd, device, alloc, imageCreateInfo, vk::MemoryRequirement::Any));
287 auto colorAttachmentView = vk::makeImageView(vkd, device, colorAttachment->get(), vk::VK_IMAGE_VIEW_TYPE_2D, imageFormat, subresourceRange);
288
289 auto renderPass = makeRenderPass(vkd, device, imageFormat);
290 auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorAttachmentView.get(), imageExtent.width, imageExtent.height);
291
292 //buffer to read the output image
293 auto outBuffer = makeBufferForImage(vkd, device, alloc, mappedFormat, imageExtent);
294 auto& outBufferAlloc = outBuffer->getAllocation();
295 void* outBufferData = outBufferAlloc.getHostPtr();
296
297 const vk::VkPipelineVertexInputStateCreateInfo vertexInputState = vk::initVulkanStructure();
298
299 const std::vector<vk::VkViewport> viewport_left { vk::makeViewport(0.0f, 0.0f, (float)imageExtent.width / 2, (float)imageExtent.height, 0.0f, 1.0f) };
300 const std::vector<vk::VkViewport> viewport_right { vk::makeViewport((float)imageExtent.width / 2, 0.0f, (float)imageExtent.width / 2, (float)imageExtent.height, 0.0f, 1.0f) };
301 const std::vector<vk::VkRect2D> scissors_left { vk::makeRect2D(0.0f, 0.0f, imageExtent.width / 2, imageExtent.height) };
302 const std::vector<vk::VkRect2D> scissors_right { vk::makeRect2D(imageExtent.width / 2, 0.0, imageExtent.width / 2, imageExtent.height) };
303 auto graphicsPipelineLayout = vk::makePipelineLayout(vkd, device);
304
305 auto vtxshader = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"));
306 auto frgshader = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"));
307 auto tscshader1 = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("tesc"));
308 auto tscshader2 = vk::createShaderModule(vkd, device,
309 m_config.changeOutput ? m_context.getBinaryCollection().get("tesc2") : m_context.getBinaryCollection().get("tesc"));
310 auto tseshader1 = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("tese"));
311 auto tseshader2 = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("tese2"));
312
313 vk::VkPipelineRasterizationStateCreateInfo raster = {
314 vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType
315 DE_NULL, // const void* pNext
316 0u, // VkPipelineRasterizationStateCreateFlags flags
317 VK_FALSE, // VkBool32 depthClampEnable
318 VK_FALSE, // VkBool32 rasterizerDiscardEnable
319 vk::VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode
320 m_config.cullMode, // VkCullModeFlags cullMode
321 vk::VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace
322 VK_FALSE, // VkBool32 depthBiasEnable
323 0.0f, // float depthBiasConstantFactor
324 0.0f, // float depthBiasClamp
325 0.0f, // float depthBiasSlopeFactor
326 1.0f // float lineWidth
327 };
328
329 vk::VkDynamicState dynamicStates = vk::VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT;
330 vk::VkPipelineDynamicStateCreateInfo dynamicInfo = {
331 vk::VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
332 nullptr,
333 0,
334 1,
335 &dynamicStates
336 };
337
338 pipeline1.setDefaultTopology(vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)
339 .setDynamicState(&dynamicInfo)
340 .setDefaultRasterizationState()
341 .setDefaultMultisampleState()
342 .setDefaultDepthStencilState()
343 .setDefaultColorBlendState()
344 .setupVertexInputState(&vertexInputState)
345 .setupPreRasterizationShaderState(
346 viewport_left,
347 scissors_left,
348 *graphicsPipelineLayout,
349 *renderPass,
350 0u,
351 *vtxshader, &raster,
352 *tscshader1, *tseshader1)
353 .setupFragmentShaderState(*graphicsPipelineLayout, *renderPass, 0u,
354 *frgshader, 0)
355 .setupFragmentOutputState(*renderPass, 0u)
356 .setMonolithicPipelineLayout(*graphicsPipelineLayout).buildPipeline();
357
358 pipeline2.setDefaultTopology(vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)
359 .setDynamicState(&dynamicInfo)
360 .setDefaultRasterizationState()
361 .setDefaultMultisampleState()
362 .setDefaultDepthStencilState()
363 .setDefaultColorBlendState()
364 .setupVertexInputState(&vertexInputState)
365 .setupPreRasterizationShaderState(
366 viewport_right,
367 scissors_right,
368 *graphicsPipelineLayout,
369 *renderPass,
370 0u,
371 *vtxshader, &raster,
372 *tscshader2, *tseshader2)
373 .setupFragmentShaderState(*graphicsPipelineLayout, *renderPass, 0u,
374 *frgshader, 0)
375 .setupFragmentOutputState(*renderPass, 0u)
376 .setMonolithicPipelineLayout(*graphicsPipelineLayout).buildPipeline();
377
378 auto commandPool = createCommandPool(vkd, device, vk::VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, qIndex);
379 auto commandBuffer = vk::allocateCommandBuffer(vkd, device, commandPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
380
381 const tcu::Vec4 clearColor(1.0f, 1.0f, 1.0f, 1.0f);
382
383 const vk::VkRect2D renderArea =
384 {
385 { 0u, 0u },
386 { imageExtent.width, imageExtent.height }
387 };
388
389 //render 2 triangles with each pipeline, covering the entire screen
390 //depending on the test settings one of them might be culled
391 vk::beginCommandBuffer(vkd, commandBuffer.get());
392 vk::beginRenderPass(vkd, *commandBuffer, *renderPass, *framebuffer, renderArea, clearColor);
393 vkd.cmdSetPatchControlPointsEXT(commandBuffer.get(), 3);
394 vkd.cmdBindPipeline(commandBuffer.get(), vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline1.getPipeline());
395 vkd.cmdDraw(commandBuffer.get(), 6, 1, 0, 0);
396 vkd.cmdBindPipeline(commandBuffer.get(), vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline2.getPipeline());
397 vkd.cmdDraw(commandBuffer.get(), 6, 1, 0, 0);
398 vk::endRenderPass(vkd, commandBuffer.get());
399 vk::copyImageToBuffer(vkd, commandBuffer.get(), colorAttachment.get()->get(), (*outBuffer).get(), imageSize);
400 vk::endCommandBuffer(vkd, commandBuffer.get());
401 vk::submitCommandsAndWait(vkd, device, m_context.getUniversalQueue(), commandBuffer.get());
402
403 invalidateAlloc(vkd, device, outBufferAlloc);
404 tcu::ConstPixelBufferAccess outPixels(mappedFormat, imageDim, outBufferData);
405
406 auto expectedFirst = m_config.expectedFirst;
407 auto expectedSecond = m_config.expectedSecond;
408
409 tcu::TextureLevel referenceLevel(mappedFormat, imageExtent.height, imageExtent.height);
410 tcu::PixelBufferAccess reference = referenceLevel.getAccess();
411 tcu::clear(getSubregion(reference, 0, 0, imageExtent.width / 2, imageExtent.height), expectedFirst);
412 tcu::clear(getSubregion(reference, imageExtent.width / 2, 0, imageExtent.width / 2, imageExtent.height), expectedSecond);
413
414 if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", reference, outPixels, tcu::Vec4(0.0), tcu::COMPARE_LOG_ON_ERROR))
415 return tcu::TestStatus::fail("Color output does not match reference, image added to log");
416
417 return tcu::TestStatus::pass("Pass");
418 }
419
createDynamicControlPointTests(tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType)420 tcu::TestCaseGroup* createDynamicControlPointTests (tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType)
421 {
422 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "dynamic_control_points", "Tests checking dynamic bind points and switching pipelines"));
423
424 group->addChild(new DynamicControlPointsTestCase(testCtx, "change_output", "test switching pipelines with dynamic control points while changing the number of tcs invocations",
425 TestConfig {
426 pipelineConstructionType,
427 true,
428 false,
429 false,
430 vk::VK_CULL_MODE_NONE,
431 tcu::Vec4(1.0, 0.0, 1.0, 1.0),
432 tcu::Vec4(1.0, 0.0, 1.0, 1.0),
433 }));
434
435 group->addChild(new DynamicControlPointsTestCase(testCtx, "change_winding", "test switching pipelines with dynamic control points while switching winding",
436 TestConfig {
437 pipelineConstructionType,
438 false,
439 true,
440 false,
441 vk::VK_CULL_MODE_FRONT_BIT,
442 tcu::Vec4(1.0, 1.0, 1.0, 1.0),
443 tcu::Vec4(1.0, 0.0, 1.0, 1.0)
444 }));
445
446 group->addChild(new DynamicControlPointsTestCase(testCtx, "change_output_winding", "test switching pipelines with dynamic control points while switching winding and number of tcs invocations",
447 TestConfig {
448 pipelineConstructionType,
449 true,
450 true,
451 false,
452 vk::VK_CULL_MODE_FRONT_BIT,
453 tcu::Vec4(1.0, 1.0, 1.0, 1.0),
454 tcu::Vec4(1.0, 0.0, 1.0, 1.0)
455 }));
456 return group.release();
457 }
458
459 } // pipeline
460 } // vkt
461