1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2018 The Khronos Group Inc.
6 * Copyright (c) 2018 Danylo Piliaiev <danylo.piliaiev@gmail.com>
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 Test for conditional rendering of vkCmdDispatch* functions
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktConditionalDispatchTests.hpp"
26 #include "vktConditionalRenderingTestUtil.hpp"
27
28 #include "vktTestCaseUtil.hpp"
29 #include "vktComputeTestsUtil.hpp"
30
31 #include "tcuTestLog.hpp"
32 #include "tcuResource.hpp"
33
34 #include "vkDefs.hpp"
35 #include "vkCmdUtil.hpp"
36 #include "vkBuilderUtil.hpp"
37 #include "vkObjUtil.hpp"
38
39 namespace vkt
40 {
41 namespace conditional
42 {
43 namespace
44 {
45
46 enum DispatchCommandType
47 {
48 DISPATCH_COMMAND_TYPE_DISPATCH = 0,
49 DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT,
50 DISPATCH_COMMAND_TYPE_DISPATCH_BASE,
51 DISPATCH_COMMAND_TYPE_DISPATCH_LAST
52 };
53
getDispatchCommandTypeName(DispatchCommandType command)54 const char* getDispatchCommandTypeName (DispatchCommandType command)
55 {
56 switch (command)
57 {
58 case DISPATCH_COMMAND_TYPE_DISPATCH: return "dispatch";
59 case DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT: return "dispatch_indirect";
60 case DISPATCH_COMMAND_TYPE_DISPATCH_BASE: return "dispatch_base";
61 default: DE_ASSERT(false);
62 }
63 return "";
64 }
65
66 struct ConditionalTestSpec
67 {
68 DispatchCommandType command;
69 int numCalls;
70 ConditionalData conditionalData;
71 };
72
73 class ConditionalDispatchTest : public vkt::TestCase
74 {
75 public:
76 ConditionalDispatchTest (tcu::TestContext& testCtx,
77 const std::string& name,
78 const std::string& description,
79 const ConditionalTestSpec& testSpec);
80
81 void initPrograms (vk::SourceCollections& sourceCollections) const;
82 void checkSupport (Context& context) const;
83 TestInstance* createInstance (Context& context) const;
84
85 private:
86 const ConditionalTestSpec m_testSpec;
87 };
88
89 class ConditionalDispatchTestInstance : public TestInstance
90 {
91 public:
92 ConditionalDispatchTestInstance (Context &context, ConditionalTestSpec testSpec);
93
94 virtual tcu::TestStatus iterate (void);
95 void recordDispatch (const vk::DeviceInterface& vk,
96 vk::VkCommandBuffer cmdBuffer,
97 compute::Buffer& indirectBuffer);
98
99 protected:
100 const DispatchCommandType m_command;
101 const int m_numCalls;
102 const ConditionalData m_conditionalData;
103 };
104
ConditionalDispatchTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const ConditionalTestSpec & testSpec)105 ConditionalDispatchTest::ConditionalDispatchTest(tcu::TestContext& testCtx,
106 const std::string& name,
107 const std::string& description,
108 const ConditionalTestSpec& testSpec)
109 : TestCase (testCtx, name, description)
110 , m_testSpec (testSpec)
111 {
112 }
113
initPrograms(vk::SourceCollections & sourceCollections) const114 void ConditionalDispatchTest::initPrograms (vk::SourceCollections& sourceCollections) const
115 {
116 std::ostringstream src;
117 src << "#version 310 es\n"
118 << "layout(local_size_x = 1u, local_size_y = 1u, local_size_z = 1u) in;\n"
119 << "layout(set = 0, binding = 0, std140) buffer Out\n"
120 << "{\n"
121 << " coherent uint count;\n"
122 << "};\n"
123 << "void main(void)\n"
124 << "{\n"
125 << " atomicAdd(count, 1u);\n"
126 << "}\n";
127
128 sourceCollections.glslSources.add("comp") << glu::ComputeSource(src.str());
129 }
130
checkSupport(Context & context) const131 void ConditionalDispatchTest::checkSupport(Context& context) const
132 {
133 checkConditionalRenderingCapabilities(context, m_testSpec.conditionalData);
134
135 if (m_testSpec.command == DISPATCH_COMMAND_TYPE_DISPATCH_BASE)
136 context.requireDeviceFunctionality("VK_KHR_device_group");
137 }
138
createInstance(Context & context) const139 TestInstance* ConditionalDispatchTest::createInstance (Context& context) const
140 {
141 return new ConditionalDispatchTestInstance(context, m_testSpec);
142 }
143
ConditionalDispatchTestInstance(Context & context,ConditionalTestSpec testSpec)144 ConditionalDispatchTestInstance::ConditionalDispatchTestInstance (Context &context, ConditionalTestSpec testSpec)
145 : TestInstance(context)
146 , m_command(testSpec.command)
147 , m_numCalls(testSpec.numCalls)
148 , m_conditionalData(testSpec.conditionalData)
149 {
150 }
151
recordDispatch(const vk::DeviceInterface & vk,vk::VkCommandBuffer cmdBuffer,compute::Buffer & indirectBuffer)152 void ConditionalDispatchTestInstance::recordDispatch (const vk::DeviceInterface& vk,
153 vk::VkCommandBuffer cmdBuffer,
154 compute::Buffer& indirectBuffer)
155 {
156 for (int i = 0; i < m_numCalls; i++)
157 {
158 switch (m_command)
159 {
160 case DISPATCH_COMMAND_TYPE_DISPATCH:
161 {
162 vk.cmdDispatch(cmdBuffer, 1, 1, 1);
163 break;
164 }
165 case DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT:
166 {
167 vk.cmdDispatchIndirect(cmdBuffer, *indirectBuffer, 0);
168 break;
169 }
170 case DISPATCH_COMMAND_TYPE_DISPATCH_BASE:
171 {
172 vk.cmdDispatchBase(cmdBuffer, 0, 0, 0, 1, 1, 1);
173 break;
174 }
175 default: DE_ASSERT(DE_FALSE);
176 }
177 }
178 }
179
iterate(void)180 tcu::TestStatus ConditionalDispatchTestInstance::iterate (void)
181 {
182 const vk::DeviceInterface& vk = m_context.getDeviceInterface();
183 const vk::VkDevice device = m_context.getDevice();
184 const vk::VkQueue queue = m_context.getUniversalQueue();
185 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
186 vk::Allocator& allocator = m_context.getDefaultAllocator();
187
188 // Create a buffer and host-visible memory for it
189
190 const vk::VkDeviceSize bufferSizeBytes = sizeof(deUint32);
191 const compute::Buffer outputBuffer(vk, device, allocator, vk::makeBufferCreateInfo(bufferSizeBytes, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), vk::MemoryRequirement::HostVisible);
192
193 {
194 const vk::Allocation& alloc = outputBuffer.getAllocation();
195 deUint8* outputBufferPtr = reinterpret_cast<deUint8*>(alloc.getHostPtr());
196 *(deUint32*)(outputBufferPtr) = 0;
197 vk::flushAlloc(vk, device, alloc);
198 }
199
200 // Create descriptor set
201
202 const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(
203 vk::DescriptorSetLayoutBuilder()
204 .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
205 .build(vk, device));
206
207 const vk::Unique<vk::VkDescriptorPool> descriptorPool(
208 vk::DescriptorPoolBuilder()
209 .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
210 .build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
211
212 const vk::Unique<vk::VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
213
214 const vk::VkDescriptorBufferInfo descriptorInfo = vk::makeDescriptorBufferInfo(*outputBuffer, 0ull, bufferSizeBytes);
215 vk::DescriptorSetUpdateBuilder()
216 .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo)
217 .update(vk, device);
218
219 // Setup pipeline
220
221 const vk::Unique<vk::VkShaderModule> shaderModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
222 const vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
223 const vk::Unique<vk::VkPipeline> pipeline (compute::makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
224
225 const vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, queueFamilyIndex));
226 const vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
227 const vk::Unique<vk::VkCommandBuffer> secondaryCmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY));
228
229 // Create indirect buffer
230 const vk::VkDispatchIndirectCommand dispatchCommands[] = { { 1u, 1u, 1u } };
231
232 compute::Buffer indirectBuffer(
233 vk, device, allocator,
234 vk::makeBufferCreateInfo(sizeof(dispatchCommands), vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
235 vk::MemoryRequirement::HostVisible);
236
237 deUint8* indirectBufferPtr = reinterpret_cast<deUint8*>(indirectBuffer.getAllocation().getHostPtr());
238 deMemcpy(indirectBufferPtr, &dispatchCommands[0], sizeof(dispatchCommands));
239
240 vk::flushAlloc(vk, device, indirectBuffer.getAllocation());
241
242 // Start recording commands
243
244 beginCommandBuffer(vk, *cmdBuffer);
245
246 vk::VkCommandBuffer targetCmdBuffer = *cmdBuffer;
247
248 const bool useSecondaryCmdBuffer = m_conditionalData.conditionInherited || m_conditionalData.conditionInSecondaryCommandBuffer;
249
250 if (useSecondaryCmdBuffer)
251 {
252 const vk::VkCommandBufferInheritanceConditionalRenderingInfoEXT conditionalRenderingInheritanceInfo =
253 {
254 vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT,
255 DE_NULL,
256 m_conditionalData.conditionInherited ? VK_TRUE : VK_FALSE // conditionalRenderingEnable
257 };
258
259 const vk::VkCommandBufferInheritanceInfo inheritanceInfo =
260 {
261 vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
262 &conditionalRenderingInheritanceInfo,
263 0u, // renderPass
264 0u, // subpass
265 0u, // framebuffer
266 VK_FALSE, // occlusionQueryEnable
267 (vk::VkQueryControlFlags)0u, // queryFlags
268 (vk::VkQueryPipelineStatisticFlags)0u, // pipelineStatistics
269 };
270
271 const vk::VkCommandBufferBeginInfo commandBufferBeginInfo =
272 {
273 vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
274 DE_NULL,
275 vk::VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
276 &inheritanceInfo
277 };
278
279 vk.beginCommandBuffer(*secondaryCmdBuffer, &commandBufferBeginInfo);
280
281 targetCmdBuffer = *secondaryCmdBuffer;
282 }
283
284 vk.cmdBindPipeline(targetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
285 vk.cmdBindDescriptorSets(targetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
286
287 de::SharedPtr<Draw::Buffer> conditionalBuffer = createConditionalRenderingBuffer(m_context, m_conditionalData);
288
289 if (m_conditionalData.conditionInSecondaryCommandBuffer)
290 {
291 beginConditionalRendering(vk, *secondaryCmdBuffer, *conditionalBuffer, m_conditionalData);
292 recordDispatch(vk, *secondaryCmdBuffer, indirectBuffer);
293 vk.cmdEndConditionalRenderingEXT(*secondaryCmdBuffer);
294 vk.endCommandBuffer(*secondaryCmdBuffer);
295 }
296 else if (m_conditionalData.conditionInherited)
297 {
298 recordDispatch(vk, *secondaryCmdBuffer, indirectBuffer);
299 vk.endCommandBuffer(*secondaryCmdBuffer);
300 }
301
302 if (m_conditionalData.conditionInPrimaryCommandBuffer)
303 {
304 beginConditionalRendering(vk, *cmdBuffer, *conditionalBuffer, m_conditionalData);
305
306 if (m_conditionalData.conditionInherited)
307 {
308 vk.cmdExecuteCommands(*cmdBuffer, 1, &secondaryCmdBuffer.get());
309 }
310 else
311 {
312 recordDispatch(vk, *cmdBuffer, indirectBuffer);
313 }
314
315 vk.cmdEndConditionalRenderingEXT(*cmdBuffer);
316 }
317 else if (useSecondaryCmdBuffer)
318 {
319 vk.cmdExecuteCommands(*cmdBuffer, 1, &secondaryCmdBuffer.get());
320 }
321
322 const vk::VkBufferMemoryBarrier outputBufferMemoryBarrier =
323 {
324 vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
325 DE_NULL,
326 vk::VK_ACCESS_SHADER_WRITE_BIT,
327 vk::VK_ACCESS_HOST_READ_BIT,
328 VK_QUEUE_FAMILY_IGNORED,
329 VK_QUEUE_FAMILY_IGNORED,
330 outputBuffer.get(),
331 0u,
332 VK_WHOLE_SIZE
333 };
334
335 vk.cmdPipelineBarrier(*cmdBuffer, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, 1u, &outputBufferMemoryBarrier, 0u, DE_NULL);
336
337 endCommandBuffer(vk, *cmdBuffer);
338
339 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
340
341 // Check result
342
343 qpTestResult res = QP_TEST_RESULT_PASS;
344
345 const vk::Allocation& outputBufferAllocation = outputBuffer.getAllocation();
346 invalidateAlloc(vk, device, outputBufferAllocation);
347
348 const deUint32* bufferPtr = static_cast<deUint32*>(outputBufferAllocation.getHostPtr());
349
350 const deUint32 expectedResult = m_conditionalData.expectCommandExecution ? m_numCalls : 0u;
351 if (bufferPtr[0] != expectedResult)
352 {
353 res = QP_TEST_RESULT_FAIL;
354 }
355
356 return tcu::TestStatus(res, qpGetTestResultName(res));
357 }
358
359 } // anonymous
360
ConditionalDispatchTests(tcu::TestContext & testCtx)361 ConditionalDispatchTests::ConditionalDispatchTests (tcu::TestContext &testCtx)
362 : TestCaseGroup (testCtx, "dispatch", "Conditional Rendering Of Dispatch Commands")
363 {
364 /* Left blank on purpose */
365 }
366
~ConditionalDispatchTests(void)367 ConditionalDispatchTests::~ConditionalDispatchTests (void) {}
368
init(void)369 void ConditionalDispatchTests::init (void)
370 {
371 for (int conditionNdx = 0; conditionNdx < DE_LENGTH_OF_ARRAY(conditional::s_testsData); conditionNdx++)
372 {
373 const ConditionalData& conditionData = conditional::s_testsData[conditionNdx];
374
375 de::MovePtr<tcu::TestCaseGroup> conditionalDrawRootGroup(new tcu::TestCaseGroup(m_testCtx, de::toString(conditionData).c_str(), "Conditionaly execute dispatch calls"));
376
377 for (deUint32 commandTypeIdx = 0; commandTypeIdx < DISPATCH_COMMAND_TYPE_DISPATCH_LAST; ++commandTypeIdx)
378 {
379 const DispatchCommandType command = DispatchCommandType(commandTypeIdx);
380
381 ConditionalTestSpec testSpec;
382 testSpec.command = command;
383 testSpec.numCalls = 3;
384 testSpec.conditionalData = conditionData;
385
386 conditionalDrawRootGroup->addChild(new ConditionalDispatchTest(m_testCtx, getDispatchCommandTypeName(command), "", testSpec));
387 }
388
389 addChild(conditionalDrawRootGroup.release());
390 }
391
392 // Tests verifying the condition is interpreted as a 32-bit value.
393 {
394 de::MovePtr<tcu::TestCaseGroup> conditionSizeGroup(new tcu::TestCaseGroup(m_testCtx, "condition_size", "Tests verifying the condition is being read as a 32-bit value"));
395
396 struct ValuePaddingExecution
397 {
398 deUint32 value;
399 bool padding;
400 bool execution;
401 const char* name;
402 };
403
404 const ValuePaddingExecution kConditionValueResults[] =
405 {
406 { 0x00000001u, false, true, "first_byte" },
407 { 0x00000100u, false, true, "second_byte" },
408 { 0x00010000u, false, true, "third_byte" },
409 { 0x01000000u, false, true, "fourth_byte" },
410 { 0u, true, false, "padded_zero" },
411 };
412
413 enum class ConditionSizeSubcaseType
414 {
415 PRIMARY_FLAT = 0,
416 PRIMARY_WITH_SECONDARY,
417 SECONDARY_NORMAL,
418 SECONDARY_INHERITED,
419 };
420
421 struct ConditionSizeSubcase
422 {
423 ConditionSizeSubcaseType type;
424 const char* name;
425 };
426
427 const ConditionSizeSubcase kConditionSizeSubcase[] =
428 {
429 { ConditionSizeSubcaseType::PRIMARY_FLAT, "primary" },
430 { ConditionSizeSubcaseType::PRIMARY_WITH_SECONDARY, "inherited" },
431 { ConditionSizeSubcaseType::SECONDARY_NORMAL, "secondary" },
432 { ConditionSizeSubcaseType::SECONDARY_INHERITED, "secondary_inherited" },
433 };
434
435 for (int subcaseNdx = 0; subcaseNdx < DE_LENGTH_OF_ARRAY(kConditionSizeSubcase); ++subcaseNdx)
436 {
437 const auto& subcase = kConditionSizeSubcase[subcaseNdx];
438
439 de::MovePtr<tcu::TestCaseGroup> subcaseGroup(new tcu::TestCaseGroup(m_testCtx, subcase.name, ""));
440
441 ConditionalData conditionalData = {};
442 conditionalData.conditionInverted = false;
443
444 switch (subcase.type)
445 {
446 case ConditionSizeSubcaseType::PRIMARY_FLAT:
447 conditionalData.conditionInPrimaryCommandBuffer = true;
448 conditionalData.conditionInSecondaryCommandBuffer = false;
449 conditionalData.conditionInherited = false;
450 break;
451
452 case ConditionSizeSubcaseType::PRIMARY_WITH_SECONDARY:
453 conditionalData.conditionInPrimaryCommandBuffer = true;
454 conditionalData.conditionInSecondaryCommandBuffer = false;
455 conditionalData.conditionInherited = true;
456 break;
457
458 case ConditionSizeSubcaseType::SECONDARY_NORMAL:
459 conditionalData.conditionInPrimaryCommandBuffer = false;
460 conditionalData.conditionInSecondaryCommandBuffer = true;
461 conditionalData.conditionInherited = false;
462 break;
463
464 case ConditionSizeSubcaseType::SECONDARY_INHERITED:
465 conditionalData.conditionInPrimaryCommandBuffer = false;
466 conditionalData.conditionInSecondaryCommandBuffer = true;
467 conditionalData.conditionInherited = true;
468 break;
469
470 default:
471 DE_ASSERT(false);
472 break;
473 }
474
475 for (int valueNdx = 0; valueNdx < DE_LENGTH_OF_ARRAY(kConditionValueResults); ++valueNdx)
476 {
477 const auto& valueResults = kConditionValueResults[valueNdx];
478
479 conditionalData.conditionValue = valueResults.value;
480 conditionalData.padConditionValue = valueResults.padding;
481 conditionalData.expectCommandExecution = valueResults.execution;
482
483 ConditionalTestSpec spec;
484 spec.command = DISPATCH_COMMAND_TYPE_DISPATCH;
485 spec.numCalls = 1;
486 spec.conditionalData = conditionalData;
487
488 subcaseGroup->addChild(new ConditionalDispatchTest(m_testCtx, valueResults.name, "", spec));
489 }
490
491 conditionSizeGroup->addChild(subcaseGroup.release());
492 }
493
494 addChild(conditionSizeGroup.release());
495 }
496 }
497
498 } // conditional
499 } // vkt
500