1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2015 Google Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Test Case Skeleton Based on Compute Shaders
22 *//*--------------------------------------------------------------------*/
24 #include "vktSpvAsmComputeShaderCase.hpp"
26 #include "deSharedPtr.hpp"
27 #include "deSTLUtil.hpp"
29 #include "vkBuilderUtil.hpp"
30 #include "vkMemUtil.hpp"
31 #include "vkPlatform.hpp"
32 #include "vkRefUtil.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkTypeUtil.hpp"
42 typedef vkt::SpirVAssembly::AllocationMp AllocationMp;
43 typedef vkt::SpirVAssembly::AllocationSp AllocationSp;
45 typedef Unique<VkBuffer> BufferHandleUp;
46 typedef de::SharedPtr<BufferHandleUp> BufferHandleSp;
48 /*--------------------------------------------------------------------*//*!
49 * \brief Create storage buffer, allocate and bind memory for the buffer
51 * The memory is created as host visible and passed back as a vk::Allocation
52 * instance via outMemory.
53 *//*--------------------------------------------------------------------*/
54 Move<VkBuffer> createBufferAndBindMemory (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorType dtype, Allocator& allocator, size_t numBytes, AllocationMp* outMemory)
56 VkBufferUsageFlags usageBit = (VkBufferUsageFlags)0;
60 case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: usageBit = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; break;
61 case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: usageBit = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; break;
62 default: DE_ASSERT(false);
65 const VkBufferCreateInfo bufferCreateInfo =
67 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
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 the given descriptor types
106 * All descriptors are created for compute pipeline.
107 *//*--------------------------------------------------------------------*/
108 Move<VkDescriptorSetLayout> createDescriptorSetLayout (const DeviceInterface& vkdi, const VkDevice& device, const vector<VkDescriptorType>& dtypes)
110 DescriptorSetLayoutBuilder builder;
112 for (size_t bindingNdx = 0; bindingNdx < dtypes.size(); ++bindingNdx)
113 builder.addSingleBinding(dtypes[bindingNdx], VK_SHADER_STAGE_COMPUTE_BIT);
115 return builder.build(vkdi, device);
118 /*--------------------------------------------------------------------*//*!
119 * \brief Create a pipeline layout with one descriptor set
120 *//*--------------------------------------------------------------------*/
121 Move<VkPipelineLayout> createPipelineLayout (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorSetLayout descriptorSetLayout, const vkt::SpirVAssembly::BufferSp& pushConstants)
123 VkPipelineLayoutCreateInfo createInfo =
125 VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
127 (VkPipelineLayoutCreateFlags)0,
128 1u, // descriptorSetCount
129 &descriptorSetLayout, // pSetLayouts
130 0u, // pushConstantRangeCount
131 DE_NULL, // pPushConstantRanges
134 VkPushConstantRange range =
136 VK_SHADER_STAGE_COMPUTE_BIT, // stageFlags
141 if (pushConstants != DE_NULL)
143 range.size = static_cast<deUint32>(pushConstants->getNumBytes());
144 createInfo.pushConstantRangeCount = 1;
145 createInfo.pPushConstantRanges = ⦥
148 return createPipelineLayout(vkdi, device, &createInfo);
151 /*--------------------------------------------------------------------*//*!
152 * \brief Create a one-time descriptor pool for one descriptor set that
153 * support the given descriptor types.
154 *//*--------------------------------------------------------------------*/
155 inline Move<VkDescriptorPool> createDescriptorPool (const DeviceInterface& vkdi, const VkDevice& device, const vector<VkDescriptorType>& dtypes)
157 DescriptorPoolBuilder builder;
159 for (size_t typeNdx = 0; typeNdx < dtypes.size(); ++typeNdx)
160 builder.addType(dtypes[typeNdx], 1);
162 return builder.build(vkdi, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, /* maxSets = */ 1);
165 /*--------------------------------------------------------------------*//*!
166 * \brief Create a descriptor set
168 * The descriptor set's layout contains the given descriptor types,
169 * sequentially binded to binding points starting from 0.
170 *//*--------------------------------------------------------------------*/
171 Move<VkDescriptorSet> createDescriptorSet (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorPool pool, VkDescriptorSetLayout layout, const vector<VkDescriptorType>& dtypes, const vector<VkDescriptorBufferInfo>& descriptorInfos)
173 DE_ASSERT(dtypes.size() == descriptorInfos.size());
175 const VkDescriptorSetAllocateInfo allocInfo =
177 VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
184 Move<VkDescriptorSet> descriptorSet = allocateDescriptorSet(vkdi, device, &allocInfo);
185 DescriptorSetUpdateBuilder builder;
187 for (deUint32 descriptorNdx = 0; descriptorNdx < dtypes.size(); ++descriptorNdx)
188 builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(descriptorNdx), dtypes[descriptorNdx], &descriptorInfos[descriptorNdx]);
189 builder.update(vkdi, device);
191 return descriptorSet;
194 /*--------------------------------------------------------------------*//*!
195 * \brief Create a compute pipeline based on the given shader
196 *//*--------------------------------------------------------------------*/
197 Move<VkPipeline> createComputePipeline (const DeviceInterface& vkdi, const VkDevice& device, VkPipelineLayout pipelineLayout, VkShaderModule shader, const char* entryPoint, const vector<deUint32>& specConstants)
199 const deUint32 numSpecConstants = (deUint32)specConstants.size();
200 vector<VkSpecializationMapEntry> entries;
201 VkSpecializationInfo specInfo;
203 if (numSpecConstants != 0)
205 entries.resize(numSpecConstants);
207 for (deUint32 ndx = 0; ndx < numSpecConstants; ++ndx)
209 entries[ndx].constantID = ndx;
210 entries[ndx].offset = ndx * (deUint32)sizeof(deUint32);
211 entries[ndx].size = sizeof(deUint32);
214 specInfo.mapEntryCount = numSpecConstants;
215 specInfo.pMapEntries = &entries[0];
216 specInfo.dataSize = numSpecConstants * sizeof(deUint32);
217 specInfo.pData = specConstants.data();
220 const VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo =
222 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType
224 (VkPipelineShaderStageCreateFlags)0, // flags
225 VK_SHADER_STAGE_COMPUTE_BIT, // stage
228 (numSpecConstants == 0) ? DE_NULL : &specInfo, // pSpecializationInfo
230 const VkComputePipelineCreateInfo pipelineCreateInfo =
232 VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType
234 (VkPipelineCreateFlags)0,
235 pipelineShaderStageCreateInfo, // cs
236 pipelineLayout, // layout
237 (VkPipeline)0, // basePipelineHandle
238 0u, // basePipelineIndex
241 return createComputePipeline(vkdi, device, (VkPipelineCache)0u, &pipelineCreateInfo);
244 /*--------------------------------------------------------------------*//*!
245 * \brief Create a command pool
247 * The created command pool is designated for use on the queue type
248 * represented by the given queueFamilyIndex.
249 *//*--------------------------------------------------------------------*/
250 Move<VkCommandPool> createCommandPool (const DeviceInterface& vkdi, VkDevice device, deUint32 queueFamilyIndex)
252 return createCommandPool(vkdi, device, 0u, queueFamilyIndex);
259 namespace SpirVAssembly
262 /*--------------------------------------------------------------------*//*!
263 * \brief Test instance for compute pipeline
265 * The compute shader is specified in the format of SPIR-V assembly, which
266 * is allowed to access MAX_NUM_INPUT_BUFFERS input storage buffers and
267 * MAX_NUM_OUTPUT_BUFFERS output storage buffers maximally. The shader
268 * source and input/output data are given in a ComputeShaderSpec object.
270 * This instance runs the given compute shader by feeding the data from input
271 * buffers and compares the data in the output buffers with the expected.
272 *//*--------------------------------------------------------------------*/
273 class SpvAsmComputeShaderInstance : public TestInstance
276 SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec, const ComputeTestFeatures features);
277 tcu::TestStatus iterate (void);
280 const ComputeShaderSpec& m_shaderSpec;
281 const ComputeTestFeatures m_features;
284 // ComputeShaderTestCase implementations
286 SpvAsmComputeShaderCase::SpvAsmComputeShaderCase (tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec, const ComputeTestFeatures features)
287 : TestCase (testCtx, name, description)
288 , m_shaderSpec (spec)
289 , m_features (features)
293 void SpvAsmComputeShaderCase::initPrograms (SourceCollections& programCollection) const
295 programCollection.spirvAsmSources.add("compute") << m_shaderSpec.assembly.c_str();
298 TestInstance* SpvAsmComputeShaderCase::createInstance (Context& ctx) const
300 return new SpvAsmComputeShaderInstance(ctx, m_shaderSpec, m_features);
303 // ComputeShaderTestInstance implementations
305 SpvAsmComputeShaderInstance::SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec, const ComputeTestFeatures features)
307 , m_shaderSpec (spec)
308 , m_features (features)
312 tcu::TestStatus SpvAsmComputeShaderInstance::iterate (void)
314 const VkPhysicalDeviceFeatures& features = m_context.getDeviceFeatures();
315 const vector<std::string>& extensions = m_context.getDeviceExtensions();
317 for (deUint32 extNdx = 0; extNdx < m_shaderSpec.extensions.size(); ++extNdx)
319 const std::string& ext = m_shaderSpec.extensions[extNdx];
321 if (!de::contains(extensions.begin(), extensions.end(), ext))
323 TCU_THROW(NotSupportedError, (std::string("Device extension not supported: ") + ext).c_str());
327 if ((m_features == COMPUTE_TEST_USES_INT16 || m_features == COMPUTE_TEST_USES_INT16_INT64) && !features.shaderInt16)
329 TCU_THROW(NotSupportedError, "shaderInt16 feature is not supported");
332 if ((m_features == COMPUTE_TEST_USES_INT64 || m_features == COMPUTE_TEST_USES_INT16_INT64) && !features.shaderInt64)
334 TCU_THROW(NotSupportedError, "shaderInt64 feature is not supported");
338 const InstanceInterface& vki = m_context.getInstanceInterface();
339 const VkPhysicalDevice physicalDevice = m_context.getPhysicalDevice();
341 // 16bit storage features
343 if (!is16BitStorageFeaturesSupported(vki, physicalDevice, m_context.getInstanceExtensions(), m_shaderSpec.requestedVulkanFeatures.ext16BitStorage))
344 TCU_THROW(NotSupportedError, "Requested 16bit storage features not supported");
347 // VariablePointers features
349 if (!isVariablePointersFeaturesSupported(vki, physicalDevice, m_context.getInstanceExtensions(), m_shaderSpec.requestedVulkanFeatures.extVariablePointers))
350 TCU_THROW(NotSupportedError, "Request Variable Pointer feature not supported");
354 // defer device and resource creation until after feature checks
355 const Unique<VkDevice> vkDevice (createDeviceWithExtensions(m_context, m_context.getUniversalQueueFamilyIndex(), m_context.getDeviceExtensions(), m_shaderSpec.extensions));
356 const VkDevice& device = *vkDevice;
357 const DeviceDriver vkDeviceInterface (m_context.getInstanceInterface(), device);
358 const DeviceInterface& vkdi = vkDeviceInterface;
359 const de::UniquePtr<vk::Allocator> vkAllocator (createAllocator(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), vkDeviceInterface, device));
360 Allocator& allocator = *vkAllocator;
361 const VkQueue queue (getDeviceQueue(vkDeviceInterface, device, m_context.getUniversalQueueFamilyIndex(), 0));
363 vector<AllocationSp> inputAllocs;
364 vector<AllocationSp> outputAllocs;
365 vector<BufferHandleSp> inputBuffers;
366 vector<BufferHandleSp> outputBuffers;
367 vector<VkDescriptorBufferInfo> descriptorInfos;
368 vector<VkDescriptorType> descriptorTypes;
370 DE_ASSERT(!m_shaderSpec.outputs.empty());
372 // Create buffer object, allocate storage, and create view for all input/output buffers.
374 for (deUint32 inputNdx = 0; inputNdx < m_shaderSpec.inputs.size(); ++inputNdx)
376 if (m_shaderSpec.inputTypes.count(inputNdx) != 0)
377 descriptorTypes.push_back(m_shaderSpec.inputTypes.at(inputNdx));
379 descriptorTypes.push_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
382 const BufferSp& input = m_shaderSpec.inputs[inputNdx];
383 const size_t numBytes = input->getNumBytes();
384 BufferHandleUp* buffer = new BufferHandleUp(createBufferAndBindMemory(vkdi, device, descriptorTypes.back(), allocator, numBytes, &alloc));
386 setMemory(vkdi, device, &*alloc, numBytes, input->data());
387 descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
388 inputBuffers.push_back(BufferHandleSp(buffer));
389 inputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
392 for (deUint32 outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
394 descriptorTypes.push_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
397 const BufferSp& output = m_shaderSpec.outputs[outputNdx];
398 const size_t numBytes = output->getNumBytes();
399 BufferHandleUp* buffer = new BufferHandleUp(createBufferAndBindMemory(vkdi, device, descriptorTypes.back(), allocator, numBytes, &alloc));
401 fillMemoryWithValue(vkdi, device, &*alloc, numBytes, 0xff);
402 descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
403 outputBuffers.push_back(BufferHandleSp(buffer));
404 outputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
407 // Create layouts and descriptor set.
409 Unique<VkDescriptorSetLayout> descriptorSetLayout (createDescriptorSetLayout(vkdi, device, descriptorTypes));
410 Unique<VkPipelineLayout> pipelineLayout (createPipelineLayout(vkdi, device, *descriptorSetLayout, m_shaderSpec.pushConstants));
411 Unique<VkDescriptorPool> descriptorPool (createDescriptorPool(vkdi, device, descriptorTypes));
412 Unique<VkDescriptorSet> descriptorSet (createDescriptorSet(vkdi, device, *descriptorPool, *descriptorSetLayout, descriptorTypes, descriptorInfos));
414 // Create compute shader and pipeline.
416 const ProgramBinary& binary = m_context.getBinaryCollection().get("compute");
417 Unique<VkShaderModule> module (createShaderModule(vkdi, device, binary, (VkShaderModuleCreateFlags)0u));
419 Unique<VkPipeline> computePipeline (createComputePipeline(vkdi, device, *pipelineLayout, *module, m_shaderSpec.entryPoint.c_str(), m_shaderSpec.specConstants));
421 // Create command buffer and record commands
423 const Unique<VkCommandPool> cmdPool (createCommandPool(vkdi, device, m_context.getUniversalQueueFamilyIndex()));
424 Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vkdi, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
426 const VkCommandBufferBeginInfo cmdBufferBeginInfo =
428 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // sType
430 VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
431 (const VkCommandBufferInheritanceInfo*)DE_NULL,
434 const tcu::IVec3& numWorkGroups = m_shaderSpec.numWorkGroups;
436 VK_CHECK(vkdi.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
437 vkdi.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipeline);
438 vkdi.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, DE_NULL);
439 if (m_shaderSpec.pushConstants != DE_NULL)
441 const deUint32 size = static_cast<deUint32>(m_shaderSpec.pushConstants->getNumBytes());
442 const void* data = m_shaderSpec.pushConstants->data();
444 vkdi.cmdPushConstants(*cmdBuffer, *pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, /* offset = */ 0, /* size = */ size, data);
446 vkdi.cmdDispatch(*cmdBuffer, numWorkGroups.x(), numWorkGroups.y(), numWorkGroups.z());
447 VK_CHECK(vkdi.endCommandBuffer(*cmdBuffer));
449 // Create fence and run.
451 const Unique<VkFence> cmdCompleteFence (createFence(vkdi, device));
452 const deUint64 infiniteTimeout = ~(deUint64)0u;
453 const VkSubmitInfo submitInfo =
455 VK_STRUCTURE_TYPE_SUBMIT_INFO,
458 (const VkSemaphore*)DE_NULL,
459 (const VkPipelineStageFlags*)DE_NULL,
463 (const VkSemaphore*)DE_NULL,
466 VK_CHECK(vkdi.queueSubmit(queue, 1, &submitInfo, *cmdCompleteFence));
467 VK_CHECK(vkdi.waitForFences(device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
470 if (m_shaderSpec.verifyIO)
472 if (!(*m_shaderSpec.verifyIO)(m_shaderSpec.inputs, outputAllocs, m_shaderSpec.outputs, m_context.getTestContext().getLog()))
473 return tcu::TestStatus(m_shaderSpec.failResult, m_shaderSpec.failMessage);
477 for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
479 const BufferSp& expectedOutput = m_shaderSpec.outputs[outputNdx];
480 if (deMemCmp(expectedOutput->data(), outputAllocs[outputNdx]->getHostPtr(), expectedOutput->getNumBytes()))
481 return tcu::TestStatus(m_shaderSpec.failResult, m_shaderSpec.failMessage);
485 return tcu::TestStatus::pass("Output match with expected");