1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2015 Google Inc.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and/or associated documentation files (the
9 * "Materials"), to deal in the Materials without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Materials, and to
12 * permit persons to whom the Materials are furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice(s) and this permission notice shall be
16 * included in all copies or substantial portions of the Materials.
18 * The Materials are Confidential Information as defined by the
19 * Khronos Membership Agreement until designated non-confidential by
20 * Khronos, at which point this condition clause shall be removed.
22 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
26 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
27 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
28 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
32 * \brief Test Case Skeleton Based on Compute Shaders
33 *//*--------------------------------------------------------------------*/
35 #include "vktSpvAsmComputeShaderCase.hpp"
37 #include "deSharedPtr.hpp"
39 #include "vkBuilderUtil.hpp"
40 #include "vkMemUtil.hpp"
41 #include "vkRefUtil.hpp"
42 #include "vkQueryUtil.hpp"
43 #include "vkTypeUtil.hpp"
51 typedef vkt::SpirVAssembly::AllocationMp AllocationMp;
52 typedef vkt::SpirVAssembly::AllocationSp AllocationSp;
54 typedef Unique<VkBuffer> BufferHandleUp;
55 typedef de::SharedPtr<BufferHandleUp> BufferHandleSp;
57 /*--------------------------------------------------------------------*//*!
58 * \brief Create storage buffer, allocate and bind memory for the buffer
60 * The memory is created as host visible and passed back as a vk::Allocation
61 * instance via outMemory.
62 *//*--------------------------------------------------------------------*/
63 Move<VkBuffer> createBufferAndBindMemory (const DeviceInterface& vkdi, const VkDevice& device, Allocator& allocator, size_t numBytes, AllocationMp* outMemory)
65 const VkBufferCreateInfo bufferCreateInfo =
67 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
71 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // usage
72 VK_SHARING_MODE_EXCLUSIVE, // sharingMode
73 0u, // queueFamilyCount
74 DE_NULL, // pQueueFamilyIndices
77 Move<VkBuffer> buffer (createBuffer(vkdi, device, &bufferCreateInfo));
78 const VkMemoryRequirements requirements = getBufferMemoryRequirements(vkdi, device, *buffer);
79 AllocationMp bufferMemory = allocator.allocate(requirements, MemoryRequirement::HostVisible);
81 VK_CHECK(vkdi.bindBufferMemory(device, *buffer, bufferMemory->getMemory(), bufferMemory->getOffset()));
82 *outMemory = bufferMemory;
87 void setMemory (const DeviceInterface& vkdi, const VkDevice& device, Allocation* destAlloc, size_t numBytes, const void* data)
89 void* const hostPtr = destAlloc->getHostPtr();
91 deMemcpy((deUint8*)hostPtr, data, numBytes);
92 flushMappedMemoryRange(vkdi, device, destAlloc->getMemory(), destAlloc->getOffset(), numBytes);
95 void fillMemoryWithValue (const DeviceInterface& vkdi, const VkDevice& device, Allocation* destAlloc, size_t numBytes, deUint8 value)
97 void* const hostPtr = destAlloc->getHostPtr();
99 deMemset((deUint8*)hostPtr, value, numBytes);
100 flushMappedMemoryRange(vkdi, device, destAlloc->getMemory(), destAlloc->getOffset(), numBytes);
103 /*--------------------------------------------------------------------*//*!
104 * \brief Create a descriptor set layout with numBindings descriptors
106 * All descriptors are created for shader storage buffer objects and
108 *//*--------------------------------------------------------------------*/
109 Move<VkDescriptorSetLayout> createDescriptorSetLayout (const DeviceInterface& vkdi, const VkDevice& device, size_t numBindings)
111 DescriptorSetLayoutBuilder builder;
113 for (size_t bindingNdx = 0; bindingNdx < numBindings; ++bindingNdx)
114 builder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
116 return builder.build(vkdi, device);
119 /*--------------------------------------------------------------------*//*!
120 * \brief Create a pipeline layout with one descriptor set
121 *//*--------------------------------------------------------------------*/
122 Move<VkPipelineLayout> createPipelineLayout (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorSetLayout descriptorSetLayout)
124 const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
126 VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
128 (VkPipelineLayoutCreateFlags)0,
129 1u, // descriptorSetCount
130 &descriptorSetLayout, // pSetLayouts
131 0u, // pushConstantRangeCount
132 DE_NULL, // pPushConstantRanges
135 return createPipelineLayout(vkdi, device, &pipelineLayoutCreateInfo);
138 /*--------------------------------------------------------------------*//*!
139 * \brief Create a one-time descriptor pool for one descriptor set
141 * The pool supports numDescriptors storage buffer descriptors.
142 *//*--------------------------------------------------------------------*/
143 inline Move<VkDescriptorPool> createDescriptorPool (const DeviceInterface& vkdi, const VkDevice& device, deUint32 numDescriptors)
145 return DescriptorPoolBuilder()
146 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, numDescriptors)
147 .build(vkdi, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, /* maxSets = */ 1);
150 /*--------------------------------------------------------------------*//*!
151 * \brief Create a descriptor set
153 * The descriptor set's layout should contain numViews descriptors.
154 * All the descriptors represent buffer views, and they are sequentially
155 * binded to binding point starting from 0.
156 *//*--------------------------------------------------------------------*/
157 Move<VkDescriptorSet> createDescriptorSet (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorPool pool, VkDescriptorSetLayout layout, size_t numViews, const vector<VkDescriptorBufferInfo>& descriptorInfos)
159 const VkDescriptorSetAllocateInfo allocInfo =
161 VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
168 Move<VkDescriptorSet> descriptorSet = allocateDescriptorSet(vkdi, device, &allocInfo);
169 DescriptorSetUpdateBuilder builder;
171 for (deUint32 descriptorNdx = 0; descriptorNdx < numViews; ++descriptorNdx)
172 builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(descriptorNdx), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfos[descriptorNdx]);
173 builder.update(vkdi, device);
175 return descriptorSet;
178 /*--------------------------------------------------------------------*//*!
179 * \brief Create a compute pipeline based on the given shader
180 *//*--------------------------------------------------------------------*/
181 Move<VkPipeline> createComputePipeline (const DeviceInterface& vkdi, const VkDevice& device, VkPipelineLayout pipelineLayout, VkShaderModule shader, const char* entryPoint, const vector<deUint32>& specConstants)
183 const deUint32 numSpecConstants = (deUint32)specConstants.size();
184 vector<VkSpecializationMapEntry> entries;
185 VkSpecializationInfo specInfo;
187 if (numSpecConstants != 0)
189 entries.reserve(numSpecConstants);
191 for (deUint32 ndx = 0; ndx < numSpecConstants; ++ndx)
193 entries[ndx].constantID = ndx;
194 entries[ndx].offset = ndx * (deUint32)sizeof(deUint32);
195 entries[ndx].size = sizeof(deUint32);
198 specInfo.mapEntryCount = numSpecConstants;
199 specInfo.pMapEntries = &entries[0];
200 specInfo.dataSize = numSpecConstants * sizeof(deUint32);
201 specInfo.pData = specConstants.data();
204 const VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo =
206 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType
208 (VkPipelineShaderStageCreateFlags)0, // flags
209 VK_SHADER_STAGE_COMPUTE_BIT, // stage
212 (numSpecConstants == 0) ? DE_NULL : &specInfo, // pSpecializationInfo
214 const VkComputePipelineCreateInfo pipelineCreateInfo =
216 VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType
218 (VkPipelineCreateFlags)0,
219 pipelineShaderStageCreateInfo, // cs
220 pipelineLayout, // layout
221 (VkPipeline)0, // basePipelineHandle
222 0u, // basePipelineIndex
225 return createComputePipeline(vkdi, device, (VkPipelineCache)0u, &pipelineCreateInfo);
228 /*--------------------------------------------------------------------*//*!
229 * \brief Create a command pool
231 * The created command pool is designated for use on the queue type
232 * represented by the given queueFamilyIndex.
233 *//*--------------------------------------------------------------------*/
234 Move<VkCommandPool> createCommandPool (const DeviceInterface& vkdi, VkDevice device, deUint32 queueFamilyIndex)
236 const VkCommandPoolCreateInfo cmdPoolCreateInfo =
238 VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // sType
241 queueFamilyIndex, // queueFamilyIndex
244 return createCommandPool(vkdi, device, &cmdPoolCreateInfo);
251 namespace SpirVAssembly
254 /*--------------------------------------------------------------------*//*!
255 * \brief Test instance for compute pipeline
257 * The compute shader is specified in the format of SPIR-V assembly, which
258 * is allowed to access MAX_NUM_INPUT_BUFFERS input storage buffers and
259 * MAX_NUM_OUTPUT_BUFFERS output storage buffers maximally. The shader
260 * source and input/output data are given in a ComputeShaderSpec object.
262 * This instance runs the given compute shader by feeding the data from input
263 * buffers and compares the data in the output buffers with the expected.
264 *//*--------------------------------------------------------------------*/
265 class SpvAsmComputeShaderInstance : public TestInstance
268 SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec);
269 tcu::TestStatus iterate (void);
272 const ComputeShaderSpec& m_shaderSpec;
275 // ComputeShaderTestCase implementations
277 SpvAsmComputeShaderCase::SpvAsmComputeShaderCase (tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec)
278 : TestCase (testCtx, name, description)
279 , m_shaderSpec (spec)
283 void SpvAsmComputeShaderCase::initPrograms (SourceCollections& programCollection) const
285 programCollection.spirvAsmSources.add("compute") << m_shaderSpec.assembly.c_str();
288 TestInstance* SpvAsmComputeShaderCase::createInstance (Context& ctx) const
290 return new SpvAsmComputeShaderInstance(ctx, m_shaderSpec);
293 // ComputeShaderTestInstance implementations
295 SpvAsmComputeShaderInstance::SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec)
297 , m_shaderSpec (spec)
301 tcu::TestStatus SpvAsmComputeShaderInstance::iterate (void)
303 const DeviceInterface& vkdi = m_context.getDeviceInterface();
304 const VkDevice& device = m_context.getDevice();
305 Allocator& allocator = m_context.getDefaultAllocator();
307 vector<AllocationSp> inputAllocs;
308 vector<AllocationSp> outputAllocs;
309 vector<BufferHandleSp> inputBuffers;
310 vector<BufferHandleSp> outputBuffers;
311 vector<VkDescriptorBufferInfo> descriptorInfos;
313 DE_ASSERT(!m_shaderSpec.outputs.empty());
314 const size_t numBuffers = m_shaderSpec.inputs.size() + m_shaderSpec.outputs.size();
316 // Create buffer object, allocate storage, and create view for all input/output buffers.
318 for (size_t inputNdx = 0; inputNdx < m_shaderSpec.inputs.size(); ++inputNdx)
321 const BufferSp& input = m_shaderSpec.inputs[inputNdx];
322 const size_t numBytes = input->getNumBytes();
323 BufferHandleUp* buffer = new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
325 setMemory(vkdi, device, &*alloc, numBytes, input->data());
326 descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
327 inputBuffers.push_back(BufferHandleSp(buffer));
328 inputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
331 for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
334 const BufferSp& output = m_shaderSpec.outputs[outputNdx];
335 const size_t numBytes = output->getNumBytes();
336 BufferHandleUp* buffer = new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
338 fillMemoryWithValue(vkdi, device, &*alloc, numBytes, 0xff);
339 descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
340 outputBuffers.push_back(BufferHandleSp(buffer));
341 outputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
344 // Create layouts and descriptor set.
346 Unique<VkDescriptorSetLayout> descriptorSetLayout (createDescriptorSetLayout(vkdi, device, numBuffers));
347 Unique<VkPipelineLayout> pipelineLayout (createPipelineLayout(vkdi, device, *descriptorSetLayout));
348 Unique<VkDescriptorPool> descriptorPool (createDescriptorPool(vkdi, device, (deUint32)numBuffers));
349 Unique<VkDescriptorSet> descriptorSet (createDescriptorSet(vkdi, device, *descriptorPool, *descriptorSetLayout, numBuffers, descriptorInfos));
351 // Create compute shader and pipeline.
353 const ProgramBinary& binary = m_context.getBinaryCollection().get("compute");
354 Unique<VkShaderModule> module (createShaderModule(vkdi, device, binary, (VkShaderModuleCreateFlags)0u));
356 Unique<VkPipeline> computePipeline (createComputePipeline(vkdi, device, *pipelineLayout, *module, m_shaderSpec.entryPoint.c_str(), m_shaderSpec.specConstants));
358 // Create command buffer and record commands
360 const Unique<VkCommandPool> cmdPool (createCommandPool(vkdi, device, m_context.getUniversalQueueFamilyIndex()));
361 const VkCommandBufferAllocateInfo cmdBufferCreateInfo =
363 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType
366 VK_COMMAND_BUFFER_LEVEL_PRIMARY, // level
370 Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vkdi, device, &cmdBufferCreateInfo));
372 const VkCommandBufferBeginInfo cmdBufferBeginInfo =
374 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // sType
376 VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
377 (VkRenderPass)0u, // renderPass
379 (VkFramebuffer)0u, // framebuffer
380 VK_FALSE, // occlusionQueryEnable
381 (VkQueryControlFlags)0,
382 (VkQueryPipelineStatisticFlags)0,
385 const tcu::IVec3& numWorkGroups = m_shaderSpec.numWorkGroups;
387 VK_CHECK(vkdi.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
388 vkdi.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipeline);
389 vkdi.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, DE_NULL);
390 vkdi.cmdDispatch(*cmdBuffer, numWorkGroups.x(), numWorkGroups.y(), numWorkGroups.z());
391 VK_CHECK(vkdi.endCommandBuffer(*cmdBuffer));
393 // Create fence and run.
395 const VkFenceCreateInfo fenceCreateInfo =
397 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // sType
401 const Unique<VkFence> cmdCompleteFence (createFence(vkdi, device, &fenceCreateInfo));
402 const deUint64 infiniteTimeout = ~(deUint64)0u;
403 const VkSubmitInfo submitInfo =
405 VK_STRUCTURE_TYPE_SUBMIT_INFO,
408 (const VkSemaphore*)DE_NULL,
412 (const VkSemaphore*)DE_NULL,
415 VK_CHECK(vkdi.queueSubmit(m_context.getUniversalQueue(), 1, &submitInfo, *cmdCompleteFence));
416 VK_CHECK(vkdi.waitForFences(device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
419 if (m_shaderSpec.verifyIO)
421 if (!(*m_shaderSpec.verifyIO)(m_shaderSpec.inputs, outputAllocs, m_shaderSpec.outputs))
422 return tcu::TestStatus::fail("Output doesn't match with expected");
426 for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
428 const BufferSp& expectedOutput = m_shaderSpec.outputs[outputNdx];
429 if (deMemCmp(expectedOutput->data(), outputAllocs[outputNdx]->getHostPtr(), expectedOutput->getNumBytes()))
430 return tcu::TestStatus::fail("Output doesn't match with expected");
434 return tcu::TestStatus::pass("Ouput match with expected");