1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2023 The Khronos Group Inc.
6  * Copyright (c) 2023 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 Extended dynamic state misc tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktPipelineExtendedDynamicStateMiscTests.hpp"
26 #include "vktTestCase.hpp"
27 #include "vktTestCaseUtil.hpp"
28 #include "vkBuilderUtil.hpp"
29 #include "vkCmdUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkObjUtil.hpp"
32 
33 #include "tcuImageCompare.hpp"
34 
35 #include "deUniquePtr.hpp"
36 
37 #include <sstream>
38 #include <vector>
39 #include <memory>
40 #include <utility>
41 
42 namespace vkt
43 {
44 namespace pipeline
45 {
46 
47 namespace
48 {
49 
50 using namespace vk;
51 
52 constexpr uint32_t kVertexCount = 4u;
53 
checkDynamicRasterizationSamplesSupport(Context & context)54 void checkDynamicRasterizationSamplesSupport(Context &context)
55 {
56 #ifndef CTS_USES_VULKANSC
57     if (!context.getExtendedDynamicState3FeaturesEXT().extendedDynamicState3RasterizationSamples)
58         TCU_THROW(NotSupportedError, "extendedDynamicState3RasterizationSamples not supported");
59 #else
60     DE_UNREF(context);
61     TCU_THROW(NotSupportedError, "extendedDynamicState3RasterizationSamples not supported");
62 #endif // CTS_USES_VULKANSC
63 }
64 
sampleShadingWithDynamicSampleCountSupport(Context & context,PipelineConstructionType pipelineConstructionType)65 void sampleShadingWithDynamicSampleCountSupport(Context &context, PipelineConstructionType pipelineConstructionType)
66 {
67     checkPipelineConstructionRequirements(context.getInstanceInterface(), context.getPhysicalDevice(),
68                                           pipelineConstructionType);
69     checkDynamicRasterizationSamplesSupport(context);
70     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_FRAGMENT_STORES_AND_ATOMICS);
71     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING);
72 }
73 
initFullScreenQuadVertexProgram(vk::SourceCollections & programCollection,const char * name)74 void initFullScreenQuadVertexProgram(vk::SourceCollections &programCollection, const char *name)
75 {
76     std::ostringstream vert;
77     vert << "#version 460\n"
78          << "vec2 positions[" << kVertexCount << "] = vec2[](\n"
79          << "    vec2(-1.0, -1.0),\n"
80          << "    vec2(-1.0,  1.0),\n"
81          << "    vec2( 1.0, -1.0),\n"
82          << "    vec2( 1.0,  1.0)\n"
83          << ");\n"
84          << "void main (void) {\n"
85          << "    gl_Position = vec4(positions[gl_VertexIndex % " << kVertexCount << "], 0.0, 1.0);\n"
86          << "}\n";
87     programCollection.glslSources.add(name) << glu::VertexSource(vert.str());
88 }
89 
initBlueAndAtomicCounterFragmentProgram(vk::SourceCollections & programCollection,const char * name)90 void initBlueAndAtomicCounterFragmentProgram(vk::SourceCollections &programCollection, const char *name)
91 {
92     std::ostringstream frag;
93     frag << "#version 460\n"
94          << "layout (location=0) out vec4 outColor;\n"
95          << "layout (set=0, binding=0) buffer InvocationCounterBlock { uint invocations; } counterBuffer;\n"
96          << "void main (void) {\n"
97          << "    uint sampleId = gl_SampleID;\n" // Enable sample shading for shader objects by reading gl_SampleID
98          << "    atomicAdd(counterBuffer.invocations, 1u);\n"
99          << "    outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
100          << "}\n";
101     programCollection.glslSources.add(name) << glu::FragmentSource(frag.str());
102 }
103 
sampleShadingWithDynamicSampleCountPrograms(vk::SourceCollections & programCollection,PipelineConstructionType)104 void sampleShadingWithDynamicSampleCountPrograms(vk::SourceCollections &programCollection, PipelineConstructionType)
105 {
106     initFullScreenQuadVertexProgram(programCollection, "vert");
107     initBlueAndAtomicCounterFragmentProgram(programCollection, "frag");
108 }
109 
verifyValueInRange(uint32_t value,uint32_t minValue,uint32_t maxValue,const char * valueDesc)110 void verifyValueInRange(uint32_t value, uint32_t minValue, uint32_t maxValue, const char *valueDesc)
111 {
112     if (value < minValue || value > maxValue)
113     {
114         std::ostringstream msg;
115         msg << "Unexpected value found for " << valueDesc << ": " << value << " not in range [" << minValue << ", "
116             << maxValue << "]";
117         TCU_FAIL(msg.str());
118     }
119 }
120 /*
121  * begin cmdbuf
122  * bind pipeline with sample shading disabled
123  * call vkCmdSetRasterizationSamplesEXT(samples > 1)
124  * draw
125  * bind pipeline with sample shading enabled
126  * draw
127  * sample shading should work for both draws with the expected number of samples
128  *
129  * Each draw will use one half of the framebuffer, controlled by the viewport and scissor.
130  */
sampleShadingWithDynamicSampleCount(Context & context,PipelineConstructionType constructionType)131 tcu::TestStatus sampleShadingWithDynamicSampleCount(Context &context, PipelineConstructionType constructionType)
132 {
133     const auto ctx = context.getContextCommonData();
134     const tcu::IVec3 fbExtent(2, 2, 1);
135     const auto vkExtent           = makeExtent3D(fbExtent);
136     const auto colorFormat        = VK_FORMAT_R8G8B8A8_UNORM;
137     const auto colorUsage         = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
138     const auto descriptorType     = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
139     const auto descriptorStages   = VK_SHADER_STAGE_FRAGMENT_BIT;
140     const auto kNumDraws          = 2u;
141     const auto bindPoint          = VK_PIPELINE_BIND_POINT_GRAPHICS;
142     const auto colorSRR           = makeDefaultImageSubresourceRange();
143     const auto kMultiSampleCount  = VK_SAMPLE_COUNT_4_BIT;
144     const auto kSingleSampleCount = VK_SAMPLE_COUNT_1_BIT;
145     const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 0.0f);
146     const tcu::Vec4 geomColor(0.0f, 0.0f, 1.0f, 1.0f); // Must match frag shader.
147     const auto topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
148 
149     // Color buffers.
150     ImageWithBuffer colorBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, VK_IMAGE_TYPE_2D,
151                                 colorSRR, 1u, kMultiSampleCount);
152     ImageWithBuffer resolveBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage,
153                                   VK_IMAGE_TYPE_2D, colorSRR, 1u, kSingleSampleCount);
154 
155     // Counter buffers.
156     using BufferPtr = std::unique_ptr<BufferWithMemory>;
157     using BufferVec = std::vector<BufferPtr>;
158 
159     const auto counterBufferSize = static_cast<VkDeviceSize>(sizeof(uint32_t));
160     const auto counterBufferInfo = makeBufferCreateInfo(counterBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
161 
162     BufferVec counterBuffers;
163 
164     for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
165     {
166         BufferPtr counterBuffer(new BufferWithMemory(ctx.vkd, ctx.device, ctx.allocator, counterBufferInfo,
167                                                      MemoryRequirement::HostVisible));
168         auto &counterBufferAlloc = counterBuffer->getAllocation();
169         void *counterBufferPtr   = counterBufferAlloc.getHostPtr();
170 
171         deMemset(counterBufferPtr, 0, static_cast<size_t>(counterBufferSize));
172         flushAlloc(ctx.vkd, ctx.device, counterBufferAlloc);
173 
174         counterBuffers.emplace_back(std::move(counterBuffer));
175     }
176 
177     // Descriptor set layout, pool and set.
178     DescriptorSetLayoutBuilder setLayoutbuilder;
179     setLayoutbuilder.addSingleBinding(descriptorType, descriptorStages);
180     const auto setLayout = setLayoutbuilder.build(ctx.vkd, ctx.device);
181 
182     DescriptorPoolBuilder poolBuilder;
183     poolBuilder.addType(descriptorType, kNumDraws);
184     const auto descriptorPool =
185         poolBuilder.build(ctx.vkd, ctx.device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, kNumDraws);
186 
187     using DescriptorSetVec = std::vector<Move<VkDescriptorSet>>;
188     DescriptorSetVec descriptorSets;
189 
190     for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
191     {
192         descriptorSets.emplace_back(makeDescriptorSet(ctx.vkd, ctx.device, *descriptorPool, *setLayout));
193 
194         DescriptorSetUpdateBuilder updateBuilder;
195         const auto counterBufferDescriptorInfo =
196             makeDescriptorBufferInfo(counterBuffers.at(drawIdx)->get(), 0ull, counterBufferSize);
197         updateBuilder.writeSingle(*descriptorSets.back(), DescriptorSetUpdateBuilder::Location::binding(0u),
198                                   descriptorType, &counterBufferDescriptorInfo);
199         updateBuilder.update(ctx.vkd, ctx.device);
200     }
201 
202     // Render pass and framebuffer.
203     const std::vector<VkAttachmentDescription> attachmentDescs{
204         // Multisample attachment.
205         makeAttachmentDescription(0u, colorFormat, kMultiSampleCount, VK_ATTACHMENT_LOAD_OP_CLEAR,
206                                   VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
207                                   VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
208                                   VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL),
209 
210         // Resolve attachment.
211         makeAttachmentDescription(0u, colorFormat, kSingleSampleCount, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
212                                   VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
213                                   VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
214                                   VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL),
215     };
216 
217     const auto colorAttRef   = makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
218     const auto resolveAttRef = makeAttachmentReference(1u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
219     const auto subpassDescription =
220         makeSubpassDescription(0u, bindPoint, 0u, nullptr, 1u, &colorAttRef, &resolveAttRef, nullptr, 0u, nullptr);
221 
222     const VkRenderPassCreateInfo renderPassCreateInfo = {
223         VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
224         nullptr,                                   // const void* pNext;
225         0u,                                        // VkRenderPassCreateFlags flags;
226         de::sizeU32(attachmentDescs),              // uint32_t attachmentCount;
227         de::dataOrNull(attachmentDescs),           // const VkAttachmentDescription* pAttachments;
228         1u,                                        // uint32_t subpassCount;
229         &subpassDescription,                       // const VkSubpassDescription* pSubpasses;
230         0u,                                        // uint32_t dependencyCount;
231         nullptr,                                   // const VkSubpassDependency* pDependencies;
232     };
233     auto renderPass = RenderPassWrapper(constructionType, ctx.vkd, ctx.device, &renderPassCreateInfo);
234 
235     const std::vector<VkImage> images{colorBuffer.getImage(), resolveBuffer.getImage()};
236     const std::vector<VkImageView> imageViews{colorBuffer.getImageView(), resolveBuffer.getImageView()};
237     renderPass.createFramebuffer(ctx.vkd, ctx.device, de::sizeU32(imageViews), de::dataOrNull(images),
238                                  de::dataOrNull(imageViews), vkExtent.width, vkExtent.height);
239 
240     // Pipelines.
241     const auto &binaries   = context.getBinaryCollection();
242     const auto &vertModule = ShaderWrapper(ctx.vkd, ctx.device, binaries.get("vert"));
243     const auto &fragModule = ShaderWrapper(ctx.vkd, ctx.device, binaries.get("frag"));
244 
245     const std::vector<VkDynamicState> dynamicStates{
246 #ifndef CTS_USES_VULKANSC
247         VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT,
248 #endif // CTS_USES_VULKANSC
249         VK_DYNAMIC_STATE_SCISSOR,
250         VK_DYNAMIC_STATE_VIEWPORT,
251     };
252 
253     const VkPipelineDynamicStateCreateInfo dynamicStateInfo = {
254         VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
255         nullptr,                                              // const void* pNext;
256         0u,                                                   // VkPipelineDynamicStateCreateFlags flags;
257         de::sizeU32(dynamicStates),                           // uint32_t dynamicStateCount;
258         de::dataOrNull(dynamicStates),                        // const VkDynamicState* pDynamicStates;
259     };
260 
261     const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = initVulkanStructureConst();
262 
263     VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
264         VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
265         nullptr,                                                  // const void* pNext;
266         0u,                                                       // VkPipelineMultisampleStateCreateFlags flags;
267         VK_SAMPLE_COUNT_64_BIT,                                   // VkSampleCountFlagBits rasterizationSamples;
268         VK_FALSE,                                                 // VkBool32 sampleShadingEnable;
269         1.0f,                                                     // float minSampleShading;
270         nullptr,                                                  // const VkSampleMask* pSampleMask;
271         VK_FALSE,                                                 // VkBool32 alphaToCoverageEnable;
272         VK_FALSE,                                                 // VkBool32 alphaToOneEnable;
273     };
274 
275     const std::vector<VkViewport> staticViewports(1u, makeViewport(0u, 0u));
276     const std::vector<VkRect2D> staticScissors(1u, makeRect2D(0u, 0u));
277     const PipelineLayoutWrapper pipelineLayout(constructionType, ctx.vkd, ctx.device, *setLayout);
278     const auto renderArea     = makeRect2D(fbExtent);
279     const int halfWidth       = fbExtent.x() / 2;
280     const uint32_t halfWidthU = static_cast<uint32_t>(halfWidth);
281     const float halfWidthF    = static_cast<float>(halfWidth);
282     const float heightF       = static_cast<float>(vkExtent.height);
283     const std::vector<VkRect2D> dynamicScissors{makeRect2D(0, 0, halfWidthU, vkExtent.height),
284                                                 makeRect2D(halfWidth, 0, halfWidthU, vkExtent.height)};
285     const std::vector<VkViewport> dynamicViewports{
286         makeViewport(0.0f, 0.0f, halfWidthF, heightF, 0.0f, 1.0f),
287         makeViewport(halfWidthF, 0.0f, halfWidthF, heightF, 0.0f, 1.0f),
288     };
289 
290     using WrapperPtr = std::unique_ptr<GraphicsPipelineWrapper>;
291     using WrapperVec = std::vector<WrapperPtr>;
292 
293     WrapperVec wrappers;
294 
295     for (const auto sampleShadingEnable : {false, true})
296     {
297         multisampleStateCreateInfo.sampleShadingEnable = sampleShadingEnable;
298 
299         WrapperPtr pipelineWrapper(new GraphicsPipelineWrapper(ctx.vki, ctx.vkd, ctx.physicalDevice, ctx.device,
300                                                                context.getDeviceExtensions(), constructionType));
301         pipelineWrapper->setDefaultTopology(topology)
302             .setDefaultRasterizationState()
303             .setDefaultColorBlendState()
304             .setDynamicState(&dynamicStateInfo)
305             .setupVertexInputState(&vertexInputStateCreateInfo)
306             .setupPreRasterizationShaderState(staticViewports, staticScissors, pipelineLayout, *renderPass, 0u,
307                                               vertModule)
308             .setupFragmentShaderState(pipelineLayout, *renderPass, 0u, fragModule, nullptr, &multisampleStateCreateInfo)
309             .setupFragmentOutputState(*renderPass, 0u, nullptr, &multisampleStateCreateInfo)
310             .setMonolithicPipelineLayout(pipelineLayout)
311             .buildPipeline();
312 
313         wrappers.emplace_back(std::move(pipelineWrapper));
314     }
315 
316     CommandPoolWithBuffer cmd(ctx.vkd, ctx.device, ctx.qfIndex);
317     const auto cmdBuffer = cmd.cmdBuffer.get();
318 
319     beginCommandBuffer(ctx.vkd, cmdBuffer);
320     renderPass.begin(ctx.vkd, cmdBuffer, renderArea, clearColor);
321     for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
322     {
323         wrappers.at(drawIdx)->bind(cmdBuffer);
324         if (drawIdx == 0u)
325         {
326 #ifndef CTS_USES_VULKANSC
327             ctx.vkd.cmdSetRasterizationSamplesEXT(cmdBuffer, kMultiSampleCount);
328 #else
329             DE_ASSERT(false);
330 #endif // CTS_USES_VULKANSC
331         }
332 #ifndef CTS_USES_VULKANSC
333         if (isConstructionTypeShaderObject(constructionType))
334         {
335             ctx.vkd.cmdSetScissorWithCount(cmdBuffer, 1u, &dynamicScissors.at(drawIdx));
336             ctx.vkd.cmdSetViewportWithCount(cmdBuffer, 1u, &dynamicViewports.at(drawIdx));
337         }
338         else
339 #endif // CTS_USES_VULKANSC
340         {
341             ctx.vkd.cmdSetScissor(cmdBuffer, 0u, 1u, &dynamicScissors.at(drawIdx));
342             ctx.vkd.cmdSetViewport(cmdBuffer, 0u, 1u, &dynamicViewports.at(drawIdx));
343         }
344         ctx.vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, *pipelineLayout, 0u, 1u, &descriptorSets.at(drawIdx).get(),
345                                       0u, nullptr);
346         ctx.vkd.cmdDraw(cmdBuffer, kVertexCount, 1u, 0u, 0u);
347     }
348     renderPass.end(ctx.vkd, cmdBuffer);
349     copyImageToBuffer(ctx.vkd, cmdBuffer, resolveBuffer.getImage(), resolveBuffer.getBuffer(), fbExtent.swizzle(0, 1),
350                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1u,
351                       VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT,
352                       VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
353     endCommandBuffer(ctx.vkd, cmdBuffer);
354     submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer);
355 
356     // Verify resolve buffer and counter buffers.
357     auto &log = context.getTestContext().getLog();
358     {
359         const tcu::Vec4 threshold(0.0f, 0.0f, 0.0f, 0.0f); // Expect exact results.
360         const auto tcuFormat           = mapVkFormat(colorFormat);
361         const auto &resolveBufferAlloc = resolveBuffer.getBufferAllocation();
362         const auto resolveBufferData   = resolveBufferAlloc.getHostPtr();
363 
364         invalidateAlloc(ctx.vkd, ctx.device, resolveBufferAlloc);
365         const tcu::ConstPixelBufferAccess resultAccess(tcuFormat, fbExtent, resolveBufferData);
366 
367         if (!tcu::floatThresholdCompare(log, "Result", "", geomColor, resultAccess, threshold,
368                                         tcu::COMPARE_LOG_ON_ERROR))
369             return tcu::TestStatus::fail("Unexpected color buffer results -- check log for details");
370     }
371     {
372         std::vector<uint32_t> counterResults(kNumDraws, 0u);
373         for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
374         {
375             const auto &bufferAlloc = counterBuffers.at(drawIdx)->getAllocation();
376             invalidateAlloc(ctx.vkd, ctx.device, bufferAlloc);
377             deMemcpy(&counterResults.at(drawIdx), bufferAlloc.getHostPtr(), sizeof(counterResults.at(drawIdx)));
378             log << tcu::TestLog::Message << "Draw " << drawIdx << ": " << counterResults.at(drawIdx) << " invocations"
379                 << tcu::TestLog::EndMessage;
380         }
381 
382         // The first result is run without sample shading enabled, so it can have any value from 1 to 4 invocations per pixel.
383         // The second result runs with sample shading enabled, so it must have exactly 4 invocations per pixel.
384         const uint32_t minInvs = (vkExtent.width * vkExtent.height) / 2u;
385         const uint32_t maxInvs = minInvs * static_cast<uint32_t>(kMultiSampleCount);
386 
387         verifyValueInRange(counterResults.at(0u), minInvs, maxInvs, "invocation counter without sample shading");
388         verifyValueInRange(counterResults.at(1u), maxInvs, maxInvs, "invocation counter with sample shading");
389     }
390 
391     return tcu::TestStatus::pass("Pass");
392 }
393 
394 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
395 
396 } // anonymous namespace
397 
createExtendedDynamicStateMiscTests(tcu::TestContext & testCtx,vk::PipelineConstructionType pipelineConstructionType)398 tcu::TestCaseGroup *createExtendedDynamicStateMiscTests(tcu::TestContext &testCtx,
399                                                         vk::PipelineConstructionType pipelineConstructionType)
400 {
401     GroupPtr miscGroup(new tcu::TestCaseGroup(testCtx, "misc"));
402     addFunctionCaseWithPrograms(miscGroup.get(), "sample_shading_dynamic_sample_count",
403                                 sampleShadingWithDynamicSampleCountSupport, sampleShadingWithDynamicSampleCountPrograms,
404                                 sampleShadingWithDynamicSampleCount, pipelineConstructionType);
405     return miscGroup.release();
406 }
407 
408 } // namespace pipeline
409 } // namespace vkt
410