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