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