/*------------------------------------------------------------------------ * 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 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 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 memory = context.getDefaultAllocator().allocate(bufReqs, memReqs); vkd.bindBufferMemory(context.getDevice(), buffer, memory->getMemory(), memory->getOffset()); return memory; } vk::Move 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 buffer; de::MovePtr alloc; // Create descriptor set const deUint32 acBufferSize = 4; vk::Move acBuffer (createBuffer(m_context, acBufferSize, vk:: VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)); de::UniquePtr 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 descriptorSetLayout (setLayoutBuilder.build(vk, device)); const vk::Unique 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 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 const bool useKHR = m_context.isDeviceFunctionalitySupported("VK_KHR_buffer_device_address"); 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; if (useKHR) addr = vk.getBufferDeviceAddress(device, &info); else addr = vk.getBufferDeviceAddressEXT(device, &info); 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 pipelineLayout (createPipelineLayout(vk, device, &pipelineLayoutParams)); vk::Move 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 pipeline (createComputePipeline(vk, device, DE_NULL, &pipelineCreateInfo)); vk::Move cmdPool (createCommandPool(vk, device, vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); vk::Move 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 cornerCaseGroup (new tcu::TestCaseGroup(testCtx, "corner_case", "Corner cases")); cornerCaseGroup->addChild(new CornerCase(testCtx, "long_shader_bitwise_and", "")); return cornerCaseGroup.release(); } } // ssbo } // vkt