| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2021 The Khronos Group Inc. |
| * Copyright (c) 2021 Google LLC. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief SSBO corner case tests. |
| *//*--------------------------------------------------------------------*/ |
| #include "deRandom.hpp" |
| |
| #include "vktSSBOCornerCase.hpp" |
| #include "vktTestCaseUtil.hpp" |
| #include "vkMemUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkRefUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| |
| #include <string> |
| |
| namespace vkt |
| { |
| namespace ssbo |
| { |
| using std::string; |
| using std::vector; |
| |
| namespace |
| { |
| class CornerCase : public TestCase |
| { |
| public: |
| CornerCase (tcu::TestContext &testCtx, const char *name, const char *description) |
| : TestCase (testCtx, name, description) |
| { |
| init(); |
| } |
| virtual void delayedInit (void); |
| virtual void initPrograms (vk::SourceCollections &programCollection) const; |
| virtual TestInstance* createInstance (Context &context) const; |
| |
| protected: |
| string m_computeShaderSrc; |
| const int m_testSize = 589; // This is the minimum value of the variable that causes a crash. |
| }; |
| |
| string useCornerCaseShader (int loopCount) |
| { |
| std::ostringstream src; |
| de::Random rnd(1); |
| |
| src << |
| "#version 310 es\n" |
| "#extension GL_EXT_buffer_reference : enable\n" |
| "layout(std430, buffer_reference) buffer BlockA\n" |
| "{\n" |
| " highp ivec4 a[];\n" |
| "};\n" |
| // ac_numIrrelevant is not used for anything, but is needed so that compiler doesn't optimize everything out. |
| "layout(std140, binding = 0) buffer AcBlock { highp uint ac_numIrrelevant; };\n" |
| "\n" |
| "layout (push_constant, std430) uniform PC {\n" |
| " BlockA blockA;\n" |
| "};\n" |
| "\n" |
| "bool compare_ivec4(highp ivec4 a, highp ivec4 b) { return a == b; }\n" |
| "\n" |
| "void main (void)\n" |
| "{\n" |
| " int allOk = int(true);\n"; |
| |
| for (int i = 0; i < loopCount; i++) |
| { |
| src << " allOk = allOk & int(compare_ivec4((blockA.a[" << i << "]), ivec4(" |
| << rnd.getInt(-9,9) << ", " |
| << rnd.getInt(-9,9) << ", " |
| << rnd.getInt(-9,9) << ", " |
| << rnd.getInt(-9,9) << ")));\n"; |
| } |
| |
| src << |
| " if (allOk != int(false))\n" |
| " {\n" |
| " ac_numIrrelevant++;\n" |
| " }\n" |
| "}\n"; |
| |
| return src.str(); |
| } |
| |
| struct Buffer |
| { |
| deUint32 buffer; |
| int size; |
| |
| Buffer (deUint32 buffer_, int size_) : buffer(buffer_), size(size_) {} |
| Buffer (void) : buffer(0), size(0) {} |
| }; |
| |
| de::MovePtr<vk::Allocation> allocateAndBindMemory (Context &context, vk::VkBuffer buffer, vk::MemoryRequirement memReqs) |
| { |
| const vk::DeviceInterface &vkd = context.getDeviceInterface(); |
| const vk::VkMemoryRequirements bufReqs = vk::getBufferMemoryRequirements(vkd, context.getDevice(), buffer); |
| de::MovePtr<vk::Allocation> memory = context.getDefaultAllocator().allocate(bufReqs, memReqs); |
| |
| vkd.bindBufferMemory(context.getDevice(), buffer, memory->getMemory(), memory->getOffset()); |
| |
| return memory; |
| } |
| |
| vk::Move<vk::VkBuffer> createBuffer (Context &context, vk::VkDeviceSize bufferSize, vk::VkBufferUsageFlags usageFlags) |
| { |
| const vk::VkDevice vkDevice = context.getDevice(); |
| const vk::DeviceInterface &vk = context.getDeviceInterface(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| |
| const vk::VkBufferCreateInfo bufferInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| bufferSize, // VkDeviceSize size; |
| usageFlags, // VkBufferUsageFlags usage; |
| vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| return vk::createBuffer(vk, vkDevice, &bufferInfo); |
| } |
| class SSBOCornerCaseInstance : public TestInstance |
| { |
| public: |
| SSBOCornerCaseInstance (Context& context, int testSize); |
| virtual ~SSBOCornerCaseInstance (void); |
| virtual tcu::TestStatus iterate (void); |
| |
| private: |
| int m_testSize; |
| }; |
| SSBOCornerCaseInstance::SSBOCornerCaseInstance (Context& context, int testSize) |
| : TestInstance (context) |
| , m_testSize (testSize) |
| { |
| } |
| SSBOCornerCaseInstance::~SSBOCornerCaseInstance (void) |
| { |
| } |
| |
| tcu::TestStatus SSBOCornerCaseInstance::iterate (void) |
| { |
| const vk::DeviceInterface& vk = m_context.getDeviceInterface(); |
| const vk::VkDevice device = m_context.getDevice(); |
| const vk::VkQueue queue = m_context.getUniversalQueue(); |
| const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); |
| |
| vk::Move<vk::VkBuffer> buffer; |
| de::MovePtr<vk::Allocation> alloc; |
| |
| // Create descriptor set |
| const deUint32 acBufferSize = 4; |
| vk::Move<vk::VkBuffer> acBuffer (createBuffer(m_context, acBufferSize, vk:: VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)); |
| de::UniquePtr<vk::Allocation> acBufferAlloc (allocateAndBindMemory(m_context, *acBuffer, vk::MemoryRequirement::HostVisible)); |
| |
| deMemset(acBufferAlloc->getHostPtr(), 0, acBufferSize); |
| flushMappedMemoryRange(vk, device, acBufferAlloc->getMemory(), acBufferAlloc->getOffset(), acBufferSize); |
| |
| vk::DescriptorSetLayoutBuilder setLayoutBuilder; |
| vk::DescriptorPoolBuilder poolBuilder; |
| |
| setLayoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT); |
| poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2); |
| |
| const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout (setLayoutBuilder.build(vk, device)); |
| const vk::Unique<vk::VkDescriptorPool> descriptorPool (poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); |
| |
| const vk::VkDescriptorSetAllocateInfo allocInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, |
| DE_NULL, |
| *descriptorPool, |
| 1u, |
| &descriptorSetLayout.get(), |
| }; |
| |
| const vk::Unique<vk::VkDescriptorSet> descriptorSet (allocateDescriptorSet(vk, device, &allocInfo)); |
| const vk::VkDescriptorBufferInfo descriptorInfo = makeDescriptorBufferInfo(*acBuffer, 0ull, acBufferSize); |
| |
| vk::DescriptorSetUpdateBuilder setUpdateBuilder; |
| vk::VkDescriptorBufferInfo descriptor; |
| |
| setUpdateBuilder |
| .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo); |
| |
| vk::VkFlags usageFlags = vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | vk::VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; |
| bool memoryDeviceAddress = false; |
| |
| if (m_context.isDeviceFunctionalitySupported("VK_KHR_buffer_device_address")) |
| memoryDeviceAddress = true; |
| |
| // Upload base buffers |
| const int bufferSize = 64 * m_testSize; |
| { |
| vk::VkPhysicalDeviceProperties properties; |
| m_context.getInstanceInterface().getPhysicalDeviceProperties(m_context.getPhysicalDevice(), &properties); |
| |
| DE_ASSERT(bufferSize > 0); |
| |
| buffer = createBuffer(m_context, bufferSize, usageFlags); |
| alloc = allocateAndBindMemory(m_context, *buffer, vk::MemoryRequirement::HostVisible | (memoryDeviceAddress ? vk::MemoryRequirement::DeviceAddress : vk::MemoryRequirement::Any)); |
| descriptor = makeDescriptorBufferInfo(*buffer, 0, bufferSize); |
| } |
| |
| // Query the buffer device address and push them via push constants |
| #ifndef CTS_USES_VULKANSC |
| const bool useKHR = m_context.isDeviceFunctionalitySupported("VK_KHR_buffer_device_address"); |
| #endif // CTS_USES_VULKANSC |
| |
| vk::VkBufferDeviceAddressInfo info = |
| { |
| vk::VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0, // VkBuffer buffer |
| }; |
| |
| info.buffer = descriptor.buffer; |
| vk::VkDeviceAddress addr; |
| #ifndef CTS_USES_VULKANSC |
| if (useKHR) |
| #endif // CTS_USES_VULKANSC |
| addr = vk.getBufferDeviceAddress(device, &info); |
| #ifndef CTS_USES_VULKANSC |
| else |
| addr = vk.getBufferDeviceAddressEXT(device, &info); |
| #endif // CTS_USES_VULKANSC |
| |
| setUpdateBuilder.update(vk, device); |
| |
| const vk::VkPushConstantRange pushConstRange = |
| { |
| vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags stageFlags |
| 0, // deUint32 offset |
| (deUint32)(sizeof(vk::VkDeviceAddress)) // deUint32 size |
| }; |
| |
| // Must fit in spec min max |
| DE_ASSERT(pushConstRange.size <= 128); |
| |
| const vk::VkPipelineLayoutCreateInfo pipelineLayoutParams = |
| { |
| vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (vk::VkPipelineLayoutCreateFlags)0, |
| 1u, // deUint32 descriptorSetCount; |
| &*descriptorSetLayout, // const VkDescriptorSetLayout* pSetLayouts; |
| 1u, // deUint32 pushConstantRangeCount; |
| &pushConstRange, // const VkPushConstantRange* pPushConstantRanges; |
| }; |
| vk::Move<vk::VkPipelineLayout> pipelineLayout (createPipelineLayout(vk, device, &pipelineLayoutParams)); |
| |
| vk::Move<vk::VkShaderModule> shaderModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("compute"), 0)); |
| const vk::VkPipelineShaderStageCreateInfo pipelineShaderStageParams = |
| { |
| vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,// VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (vk::VkPipelineShaderStageCreateFlags)0, |
| vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStage stage; |
| *shaderModule, // VkShader shader; |
| "main", // |
| DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; |
| }; |
| const vk::VkComputePipelineCreateInfo pipelineCreateInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0, // VkPipelineCreateFlags flags; |
| pipelineShaderStageParams, // VkPipelineShaderStageCreateInfo stage; |
| *pipelineLayout, // VkPipelineLayout layout; |
| DE_NULL, // VkPipeline basePipelineHandle; |
| 0, // deInt32 basePipelineIndex; |
| }; |
| vk::Move<vk::VkPipeline> pipeline (createComputePipeline(vk, device, DE_NULL, &pipelineCreateInfo)); |
| |
| vk::Move<vk::VkCommandPool> cmdPool (createCommandPool(vk, device, vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); |
| vk::Move<vk::VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| beginCommandBuffer(vk, *cmdBuffer, 0u); |
| |
| vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); |
| |
| vk.cmdPushConstants(*cmdBuffer, *pipelineLayout, vk::VK_SHADER_STAGE_COMPUTE_BIT,0, (deUint32)(sizeof(addr)), &addr); |
| |
| vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); |
| |
| vk.cmdDispatch(*cmdBuffer, 1, 1, 1); |
| |
| endCommandBuffer(vk, *cmdBuffer); |
| |
| submitCommandsAndWait(vk, device, queue, cmdBuffer.get()); |
| |
| // Test always passes if it doesn't cause a crash. |
| return tcu::TestStatus::pass("Test did not cause a crash"); |
| } |
| |
| void CornerCase::initPrograms (vk::SourceCollections& programCollection) const |
| { |
| DE_ASSERT(!m_computeShaderSrc.empty()); |
| |
| programCollection.glslSources.add("compute") << glu::ComputeSource(m_computeShaderSrc); |
| } |
| |
| TestInstance* CornerCase::createInstance (Context& context) const |
| { |
| if (!context.isBufferDeviceAddressSupported()) |
| TCU_THROW(NotSupportedError, "Physical storage buffer pointers not supported"); |
| return new SSBOCornerCaseInstance(context, m_testSize); |
| } |
| |
| void CornerCase::delayedInit (void) |
| { |
| m_computeShaderSrc = useCornerCaseShader(m_testSize); |
| } |
| } // anonymous |
| |
| tcu::TestCaseGroup* createSSBOCornerCaseTests (tcu::TestContext& testCtx) |
| { |
| de::MovePtr<tcu::TestCaseGroup> cornerCaseGroup (new tcu::TestCaseGroup(testCtx, "corner_case", "Corner cases")); |
| cornerCaseGroup->addChild(new CornerCase(testCtx, "long_shader_bitwise_and", "")); |
| return cornerCaseGroup.release(); |
| } |
| } // ssbo |
| } // vkt |