1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 Google Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Test Case Skeleton Based on Compute Shaders
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktSpvAsmComputeShaderCase.hpp"
25
26 #include "deSharedPtr.hpp"
27
28 #include "vkBuilderUtil.hpp"
29 #include "vkMemUtil.hpp"
30 #include "vkRefUtil.hpp"
31 #include "vkQueryUtil.hpp"
32 #include "vkTypeUtil.hpp"
33
34 namespace
35 {
36
37 using namespace vk;
38 using std::vector;
39
40 typedef vkt::SpirVAssembly::AllocationMp AllocationMp;
41 typedef vkt::SpirVAssembly::AllocationSp AllocationSp;
42
43 typedef Unique<VkBuffer> BufferHandleUp;
44 typedef de::SharedPtr<BufferHandleUp> BufferHandleSp;
45
46 /*--------------------------------------------------------------------*//*!
47 * \brief Create storage buffer, allocate and bind memory for the buffer
48 *
49 * The memory is created as host visible and passed back as a vk::Allocation
50 * instance via outMemory.
51 *//*--------------------------------------------------------------------*/
createBufferAndBindMemory(const DeviceInterface & vkdi,const VkDevice & device,Allocator & allocator,size_t numBytes,AllocationMp * outMemory)52 Move<VkBuffer> createBufferAndBindMemory (const DeviceInterface& vkdi, const VkDevice& device, Allocator& allocator, size_t numBytes, AllocationMp* outMemory)
53 {
54 const VkBufferCreateInfo bufferCreateInfo =
55 {
56 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
57 DE_NULL, // pNext
58 0u, // flags
59 numBytes, // size
60 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // usage
61 VK_SHARING_MODE_EXCLUSIVE, // sharingMode
62 0u, // queueFamilyCount
63 DE_NULL, // pQueueFamilyIndices
64 };
65
66 Move<VkBuffer> buffer (createBuffer(vkdi, device, &bufferCreateInfo));
67 const VkMemoryRequirements requirements = getBufferMemoryRequirements(vkdi, device, *buffer);
68 AllocationMp bufferMemory = allocator.allocate(requirements, MemoryRequirement::HostVisible);
69
70 VK_CHECK(vkdi.bindBufferMemory(device, *buffer, bufferMemory->getMemory(), bufferMemory->getOffset()));
71 *outMemory = bufferMemory;
72
73 return buffer;
74 }
75
setMemory(const DeviceInterface & vkdi,const VkDevice & device,Allocation * destAlloc,size_t numBytes,const void * data)76 void setMemory (const DeviceInterface& vkdi, const VkDevice& device, Allocation* destAlloc, size_t numBytes, const void* data)
77 {
78 void* const hostPtr = destAlloc->getHostPtr();
79
80 deMemcpy((deUint8*)hostPtr, data, numBytes);
81 flushMappedMemoryRange(vkdi, device, destAlloc->getMemory(), destAlloc->getOffset(), numBytes);
82 }
83
fillMemoryWithValue(const DeviceInterface & vkdi,const VkDevice & device,Allocation * destAlloc,size_t numBytes,deUint8 value)84 void fillMemoryWithValue (const DeviceInterface& vkdi, const VkDevice& device, Allocation* destAlloc, size_t numBytes, deUint8 value)
85 {
86 void* const hostPtr = destAlloc->getHostPtr();
87
88 deMemset((deUint8*)hostPtr, value, numBytes);
89 flushMappedMemoryRange(vkdi, device, destAlloc->getMemory(), destAlloc->getOffset(), numBytes);
90 }
91
92 /*--------------------------------------------------------------------*//*!
93 * \brief Create a descriptor set layout with numBindings descriptors
94 *
95 * All descriptors are created for shader storage buffer objects and
96 * compute pipeline.
97 *//*--------------------------------------------------------------------*/
createDescriptorSetLayout(const DeviceInterface & vkdi,const VkDevice & device,size_t numBindings)98 Move<VkDescriptorSetLayout> createDescriptorSetLayout (const DeviceInterface& vkdi, const VkDevice& device, size_t numBindings)
99 {
100 DescriptorSetLayoutBuilder builder;
101
102 for (size_t bindingNdx = 0; bindingNdx < numBindings; ++bindingNdx)
103 builder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
104
105 return builder.build(vkdi, device);
106 }
107
108 /*--------------------------------------------------------------------*//*!
109 * \brief Create a pipeline layout with one descriptor set
110 *//*--------------------------------------------------------------------*/
createPipelineLayout(const DeviceInterface & vkdi,const VkDevice & device,VkDescriptorSetLayout descriptorSetLayout)111 Move<VkPipelineLayout> createPipelineLayout (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorSetLayout descriptorSetLayout)
112 {
113 const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
114 {
115 VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
116 DE_NULL, // pNext
117 (VkPipelineLayoutCreateFlags)0,
118 1u, // descriptorSetCount
119 &descriptorSetLayout, // pSetLayouts
120 0u, // pushConstantRangeCount
121 DE_NULL, // pPushConstantRanges
122 };
123
124 return createPipelineLayout(vkdi, device, &pipelineLayoutCreateInfo);
125 }
126
127 /*--------------------------------------------------------------------*//*!
128 * \brief Create a one-time descriptor pool for one descriptor set
129 *
130 * The pool supports numDescriptors storage buffer descriptors.
131 *//*--------------------------------------------------------------------*/
createDescriptorPool(const DeviceInterface & vkdi,const VkDevice & device,deUint32 numDescriptors)132 inline Move<VkDescriptorPool> createDescriptorPool (const DeviceInterface& vkdi, const VkDevice& device, deUint32 numDescriptors)
133 {
134 return DescriptorPoolBuilder()
135 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, numDescriptors)
136 .build(vkdi, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, /* maxSets = */ 1);
137 }
138
139 /*--------------------------------------------------------------------*//*!
140 * \brief Create a descriptor set
141 *
142 * The descriptor set's layout should contain numViews descriptors.
143 * All the descriptors represent buffer views, and they are sequentially
144 * binded to binding point starting from 0.
145 *//*--------------------------------------------------------------------*/
createDescriptorSet(const DeviceInterface & vkdi,const VkDevice & device,VkDescriptorPool pool,VkDescriptorSetLayout layout,size_t numViews,const vector<VkDescriptorBufferInfo> & descriptorInfos)146 Move<VkDescriptorSet> createDescriptorSet (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorPool pool, VkDescriptorSetLayout layout, size_t numViews, const vector<VkDescriptorBufferInfo>& descriptorInfos)
147 {
148 const VkDescriptorSetAllocateInfo allocInfo =
149 {
150 VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
151 DE_NULL,
152 pool,
153 1u,
154 &layout
155 };
156
157 Move<VkDescriptorSet> descriptorSet = allocateDescriptorSet(vkdi, device, &allocInfo);
158 DescriptorSetUpdateBuilder builder;
159
160 for (deUint32 descriptorNdx = 0; descriptorNdx < numViews; ++descriptorNdx)
161 builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(descriptorNdx), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfos[descriptorNdx]);
162 builder.update(vkdi, device);
163
164 return descriptorSet;
165 }
166
167 /*--------------------------------------------------------------------*//*!
168 * \brief Create a compute pipeline based on the given shader
169 *//*--------------------------------------------------------------------*/
createComputePipeline(const DeviceInterface & vkdi,const VkDevice & device,VkPipelineLayout pipelineLayout,VkShaderModule shader,const char * entryPoint,const vector<deUint32> & specConstants)170 Move<VkPipeline> createComputePipeline (const DeviceInterface& vkdi, const VkDevice& device, VkPipelineLayout pipelineLayout, VkShaderModule shader, const char* entryPoint, const vector<deUint32>& specConstants)
171 {
172 const deUint32 numSpecConstants = (deUint32)specConstants.size();
173 vector<VkSpecializationMapEntry> entries;
174 VkSpecializationInfo specInfo;
175
176 if (numSpecConstants != 0)
177 {
178 entries.resize(numSpecConstants);
179
180 for (deUint32 ndx = 0; ndx < numSpecConstants; ++ndx)
181 {
182 entries[ndx].constantID = ndx;
183 entries[ndx].offset = ndx * (deUint32)sizeof(deUint32);
184 entries[ndx].size = sizeof(deUint32);
185 }
186
187 specInfo.mapEntryCount = numSpecConstants;
188 specInfo.pMapEntries = &entries[0];
189 specInfo.dataSize = numSpecConstants * sizeof(deUint32);
190 specInfo.pData = specConstants.data();
191 }
192
193 const VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo =
194 {
195 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType
196 DE_NULL, // pNext
197 (VkPipelineShaderStageCreateFlags)0, // flags
198 VK_SHADER_STAGE_COMPUTE_BIT, // stage
199 shader, // module
200 entryPoint, // pName
201 (numSpecConstants == 0) ? DE_NULL : &specInfo, // pSpecializationInfo
202 };
203 const VkComputePipelineCreateInfo pipelineCreateInfo =
204 {
205 VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType
206 DE_NULL, // pNext
207 (VkPipelineCreateFlags)0,
208 pipelineShaderStageCreateInfo, // cs
209 pipelineLayout, // layout
210 (VkPipeline)0, // basePipelineHandle
211 0u, // basePipelineIndex
212 };
213
214 return createComputePipeline(vkdi, device, (VkPipelineCache)0u, &pipelineCreateInfo);
215 }
216
217 /*--------------------------------------------------------------------*//*!
218 * \brief Create a command pool
219 *
220 * The created command pool is designated for use on the queue type
221 * represented by the given queueFamilyIndex.
222 *//*--------------------------------------------------------------------*/
createCommandPool(const DeviceInterface & vkdi,VkDevice device,deUint32 queueFamilyIndex)223 Move<VkCommandPool> createCommandPool (const DeviceInterface& vkdi, VkDevice device, deUint32 queueFamilyIndex)
224 {
225 const VkCommandPoolCreateInfo cmdPoolCreateInfo =
226 {
227 VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // sType
228 DE_NULL, // pNext
229 0u, // flags
230 queueFamilyIndex, // queueFamilyIndex
231 };
232
233 return createCommandPool(vkdi, device, &cmdPoolCreateInfo);
234 }
235
236 } // anonymous
237
238 namespace vkt
239 {
240 namespace SpirVAssembly
241 {
242
243 /*--------------------------------------------------------------------*//*!
244 * \brief Test instance for compute pipeline
245 *
246 * The compute shader is specified in the format of SPIR-V assembly, which
247 * is allowed to access MAX_NUM_INPUT_BUFFERS input storage buffers and
248 * MAX_NUM_OUTPUT_BUFFERS output storage buffers maximally. The shader
249 * source and input/output data are given in a ComputeShaderSpec object.
250 *
251 * This instance runs the given compute shader by feeding the data from input
252 * buffers and compares the data in the output buffers with the expected.
253 *//*--------------------------------------------------------------------*/
254 class SpvAsmComputeShaderInstance : public TestInstance
255 {
256 public:
257 SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec);
258 tcu::TestStatus iterate (void);
259
260 private:
261 const ComputeShaderSpec& m_shaderSpec;
262 };
263
264 // ComputeShaderTestCase implementations
265
SpvAsmComputeShaderCase(tcu::TestContext & testCtx,const char * name,const char * description,const ComputeShaderSpec & spec)266 SpvAsmComputeShaderCase::SpvAsmComputeShaderCase (tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec)
267 : TestCase (testCtx, name, description)
268 , m_shaderSpec (spec)
269 {
270 }
271
initPrograms(SourceCollections & programCollection) const272 void SpvAsmComputeShaderCase::initPrograms (SourceCollections& programCollection) const
273 {
274 programCollection.spirvAsmSources.add("compute") << m_shaderSpec.assembly.c_str();
275 }
276
createInstance(Context & ctx) const277 TestInstance* SpvAsmComputeShaderCase::createInstance (Context& ctx) const
278 {
279 return new SpvAsmComputeShaderInstance(ctx, m_shaderSpec);
280 }
281
282 // ComputeShaderTestInstance implementations
283
SpvAsmComputeShaderInstance(Context & ctx,const ComputeShaderSpec & spec)284 SpvAsmComputeShaderInstance::SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec)
285 : TestInstance (ctx)
286 , m_shaderSpec (spec)
287 {
288 }
289
iterate(void)290 tcu::TestStatus SpvAsmComputeShaderInstance::iterate (void)
291 {
292 const DeviceInterface& vkdi = m_context.getDeviceInterface();
293 const VkDevice& device = m_context.getDevice();
294 Allocator& allocator = m_context.getDefaultAllocator();
295
296 vector<AllocationSp> inputAllocs;
297 vector<AllocationSp> outputAllocs;
298 vector<BufferHandleSp> inputBuffers;
299 vector<BufferHandleSp> outputBuffers;
300 vector<VkDescriptorBufferInfo> descriptorInfos;
301
302 DE_ASSERT(!m_shaderSpec.outputs.empty());
303 const size_t numBuffers = m_shaderSpec.inputs.size() + m_shaderSpec.outputs.size();
304
305 // Create buffer object, allocate storage, and create view for all input/output buffers.
306
307 for (size_t inputNdx = 0; inputNdx < m_shaderSpec.inputs.size(); ++inputNdx)
308 {
309 AllocationMp alloc;
310 const BufferSp& input = m_shaderSpec.inputs[inputNdx];
311 const size_t numBytes = input->getNumBytes();
312 BufferHandleUp* buffer = new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
313
314 setMemory(vkdi, device, &*alloc, numBytes, input->data());
315 descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
316 inputBuffers.push_back(BufferHandleSp(buffer));
317 inputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
318 }
319
320 for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
321 {
322 AllocationMp alloc;
323 const BufferSp& output = m_shaderSpec.outputs[outputNdx];
324 const size_t numBytes = output->getNumBytes();
325 BufferHandleUp* buffer = new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
326
327 fillMemoryWithValue(vkdi, device, &*alloc, numBytes, 0xff);
328 descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
329 outputBuffers.push_back(BufferHandleSp(buffer));
330 outputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
331 }
332
333 // Create layouts and descriptor set.
334
335 Unique<VkDescriptorSetLayout> descriptorSetLayout (createDescriptorSetLayout(vkdi, device, numBuffers));
336 Unique<VkPipelineLayout> pipelineLayout (createPipelineLayout(vkdi, device, *descriptorSetLayout));
337 Unique<VkDescriptorPool> descriptorPool (createDescriptorPool(vkdi, device, (deUint32)numBuffers));
338 Unique<VkDescriptorSet> descriptorSet (createDescriptorSet(vkdi, device, *descriptorPool, *descriptorSetLayout, numBuffers, descriptorInfos));
339
340 // Create compute shader and pipeline.
341
342 const ProgramBinary& binary = m_context.getBinaryCollection().get("compute");
343 Unique<VkShaderModule> module (createShaderModule(vkdi, device, binary, (VkShaderModuleCreateFlags)0u));
344
345 Unique<VkPipeline> computePipeline (createComputePipeline(vkdi, device, *pipelineLayout, *module, m_shaderSpec.entryPoint.c_str(), m_shaderSpec.specConstants));
346
347 // Create command buffer and record commands
348
349 const Unique<VkCommandPool> cmdPool (createCommandPool(vkdi, device, m_context.getUniversalQueueFamilyIndex()));
350 const VkCommandBufferAllocateInfo cmdBufferCreateInfo =
351 {
352 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType
353 NULL, // pNext
354 *cmdPool, // cmdPool
355 VK_COMMAND_BUFFER_LEVEL_PRIMARY, // level
356 1u // count
357 };
358
359 Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vkdi, device, &cmdBufferCreateInfo));
360
361 const VkCommandBufferBeginInfo cmdBufferBeginInfo =
362 {
363 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // sType
364 DE_NULL, // pNext
365 VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
366 (const VkCommandBufferInheritanceInfo*)DE_NULL,
367 };
368
369 const tcu::IVec3& numWorkGroups = m_shaderSpec.numWorkGroups;
370
371 VK_CHECK(vkdi.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
372 vkdi.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipeline);
373 vkdi.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, DE_NULL);
374 vkdi.cmdDispatch(*cmdBuffer, numWorkGroups.x(), numWorkGroups.y(), numWorkGroups.z());
375 VK_CHECK(vkdi.endCommandBuffer(*cmdBuffer));
376
377 // Create fence and run.
378
379 const VkFenceCreateInfo fenceCreateInfo =
380 {
381 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // sType
382 NULL, // pNext
383 0 // flags
384 };
385 const Unique<VkFence> cmdCompleteFence (createFence(vkdi, device, &fenceCreateInfo));
386 const deUint64 infiniteTimeout = ~(deUint64)0u;
387 const VkSubmitInfo submitInfo =
388 {
389 VK_STRUCTURE_TYPE_SUBMIT_INFO,
390 DE_NULL,
391 0u,
392 (const VkSemaphore*)DE_NULL,
393 (const VkPipelineStageFlags*)DE_NULL,
394 1u,
395 &cmdBuffer.get(),
396 0u,
397 (const VkSemaphore*)DE_NULL,
398 };
399
400 VK_CHECK(vkdi.queueSubmit(m_context.getUniversalQueue(), 1, &submitInfo, *cmdCompleteFence));
401 VK_CHECK(vkdi.waitForFences(device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
402
403 // Check output.
404 if (m_shaderSpec.verifyIO)
405 {
406 if (!(*m_shaderSpec.verifyIO)(m_shaderSpec.inputs, outputAllocs, m_shaderSpec.outputs))
407 return tcu::TestStatus::fail("Output doesn't match with expected");
408 }
409 else
410 {
411 for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
412 {
413 const BufferSp& expectedOutput = m_shaderSpec.outputs[outputNdx];
414 if (deMemCmp(expectedOutput->data(), outputAllocs[outputNdx]->getHostPtr(), expectedOutput->getNumBytes()))
415 return tcu::TestStatus::fail("Output doesn't match with expected");
416 }
417 }
418
419 return tcu::TestStatus::pass("Ouput match with expected");
420 }
421
422 } // SpirVAssembly
423 } // vkt
424