/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2016 The Khronos Group Inc. * Copyright (c) 2016 Imagination Technologies Ltd. * * 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 Robust Vertex Buffer Access Tests *//*--------------------------------------------------------------------*/ #include "vktRobustnessVertexAccessTests.hpp" #include "vktRobustnessUtil.hpp" #include "vktTestCaseUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkImageUtil.hpp" #include "vkMemUtil.hpp" #include "vkPrograms.hpp" #include "vkQueryUtil.hpp" #include "vkRef.hpp" #include "vkRefUtil.hpp" #include "vkTypeUtil.hpp" #include "tcuTestLog.hpp" #include "deMath.h" #include "deUniquePtr.hpp" #include namespace vkt { namespace robustness { using namespace vk; typedef std::vector BindingList; typedef std::vector AttributeList; class VertexAccessTest : public vkt::TestCase { public: VertexAccessTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances); virtual ~VertexAccessTest (void) {} void initPrograms (SourceCollections& programCollection) const; void checkSupport (Context& context) const; TestInstance* createInstance (Context& context) const = 0; protected: const VkFormat m_inputFormat; const deUint32 m_numVertexValues; const deUint32 m_numInstanceValues; const deUint32 m_numVertices; const deUint32 m_numInstances; }; class DrawAccessTest : public VertexAccessTest { public: DrawAccessTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances); virtual ~DrawAccessTest (void) {} TestInstance* createInstance (Context& context) const; protected: }; class DrawIndexedAccessTest : public VertexAccessTest { public: enum IndexConfig { INDEX_CONFIG_LAST_INDEX_OUT_OF_BOUNDS, INDEX_CONFIG_INDICES_OUT_OF_BOUNDS, INDEX_CONFIG_TRIANGLE_OUT_OF_BOUNDS, INDEX_CONFIG_COUNT }; const static std::vector s_indexConfigs[INDEX_CONFIG_COUNT]; DrawIndexedAccessTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, VkFormat inputFormat, IndexConfig indexConfig); virtual ~DrawIndexedAccessTest (void) {} TestInstance* createInstance (Context& context) const; protected: const IndexConfig m_indexConfig; }; class VertexAccessInstance : public vkt::TestInstance { public: VertexAccessInstance (Context& context, Move device, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances, const std::vector& indices); virtual ~VertexAccessInstance (void) {} virtual tcu::TestStatus iterate (void); virtual bool verifyResult (void); private: bool isValueWithinVertexBufferOrZero (void* vertexBuffer, VkDeviceSize vertexBufferSize, const void* value, deUint32 valueIndexa); protected: static bool isExpectedValueFromVertexBuffer (const void* vertexBuffer, deUint32 vertexIndex, VkFormat vertexFormat, const void* value); static VkDeviceSize getBufferSizeInBytes (deUint32 numScalars, VkFormat format); virtual void initVertexIds (deUint32 *indicesPtr, size_t indexCount) = 0; virtual deUint32 getIndex (deUint32 vertexNum) const = 0; Move m_device; const VkFormat m_inputFormat; const deUint32 m_numVertexValues; const deUint32 m_numInstanceValues; const deUint32 m_numVertices; const deUint32 m_numInstances; AttributeList m_vertexInputAttributes; BindingList m_vertexInputBindings; Move m_vertexRateBuffer; VkDeviceSize m_vertexRateBufferSize; de::MovePtr m_vertexRateBufferAlloc; VkDeviceSize m_vertexRateBufferAllocSize; Move m_instanceRateBuffer; VkDeviceSize m_instanceRateBufferSize; de::MovePtr m_instanceRateBufferAlloc; VkDeviceSize m_instanceRateBufferAllocSize; Move m_vertexNumBuffer; VkDeviceSize m_vertexNumBufferSize; de::MovePtr m_vertexNumBufferAlloc; Move m_indexBuffer; VkDeviceSize m_indexBufferSize; de::MovePtr m_indexBufferAlloc; Move m_outBuffer; // SSBO VkDeviceSize m_outBufferSize; de::MovePtr m_outBufferAlloc; Move m_descriptorPool; Move m_descriptorSetLayout; Move m_descriptorSet; Move m_fence; VkQueue m_queue; de::MovePtr m_graphicsTestEnvironment; }; class DrawAccessInstance : public VertexAccessInstance { public: DrawAccessInstance (Context& context, Move device, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances); virtual ~DrawAccessInstance (void) {} protected: virtual void initVertexIds (deUint32 *indicesPtr, size_t indexCount); virtual deUint32 getIndex (deUint32 vertexNum) const; }; class DrawIndexedAccessInstance : public VertexAccessInstance { public: DrawIndexedAccessInstance (Context& context, Move device, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances, const std::vector& indices); virtual ~DrawIndexedAccessInstance (void) {} protected: virtual void initVertexIds (deUint32 *indicesPtr, size_t indexCount); virtual deUint32 getIndex (deUint32 vertexNum) const; const std::vector m_indices; }; // VertexAccessTest VertexAccessTest::VertexAccessTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances) : vkt::TestCase (testContext, name, description) , m_inputFormat (inputFormat) , m_numVertexValues (numVertexValues) , m_numInstanceValues (numInstanceValues) , m_numVertices (numVertices) , m_numInstances (numInstances) { } void VertexAccessTest::checkSupport(Context& context) const { if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") && !context.getDeviceFeatures().robustBufferAccess) TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: robustBufferAccess not supported by this implementation"); } void VertexAccessTest::initPrograms (SourceCollections& programCollection) const { std::ostringstream attributeDeclaration; std::ostringstream attributeUse; std::ostringstream vertexShaderSource; std::ostringstream fragmentShaderSource; std::ostringstream attributeTypeStr; const int numChannels = getNumUsedChannels(mapVkFormat(m_inputFormat).order); const deUint32 numScalarsPerVertex = numChannels * 3; // Use 3 identical attributes deUint32 numValues = 0; const bool isR64 = (m_inputFormat == VK_FORMAT_R64_UINT || m_inputFormat == VK_FORMAT_R64_SINT); if (numChannels == 1) { if (isUintFormat(m_inputFormat)) attributeTypeStr << "uint"; else if (isIntFormat(m_inputFormat)) attributeTypeStr << "int"; else attributeTypeStr << "float"; attributeTypeStr << (isR64 ? "64_t" : " "); } else { if (isUintFormat(m_inputFormat)) attributeTypeStr << "uvec"; else if (isIntFormat(m_inputFormat)) attributeTypeStr << "ivec"; else attributeTypeStr << "vec"; attributeTypeStr << numChannels; } for (int attrNdx = 0; attrNdx < 3; attrNdx++) { attributeDeclaration << "layout(location = " << attrNdx << ") in " << attributeTypeStr.str() << " attr" << attrNdx << ";\n"; for (int chanNdx = 0; chanNdx < numChannels; chanNdx++) { attributeUse << "\toutData[(gl_InstanceIndex * " << numScalarsPerVertex * m_numVertices << ") + (vertexNum * " << numScalarsPerVertex << " + " << numValues++ << ")] = attr" << attrNdx; if (numChannels == 1) attributeUse << ";\n"; else attributeUse << "[" << chanNdx << "];\n"; } } attributeDeclaration << "layout(location = 3) in int vertexNum;\n"; attributeUse << "\n"; std::string outType = ""; if (isUintFormat(m_inputFormat)) outType = "uint"; else if (isIntFormat(m_inputFormat)) outType = "int"; else outType = "float"; outType += isR64 ? "64_t" : ""; std::string extensions = ""; std::string version = "#version 310 es\n"; if (isR64) { extensions = "#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require\n"; version = "#version 440\n"; } vertexShaderSource << version << "precision highp float;\n" << extensions << attributeDeclaration.str() << "layout(set = 0, binding = 0, std430) buffer outBuffer\n" "{\n" "\t" << outType << " outData[" << (m_numVertices * numValues) * m_numInstances << "];\n" "};\n\n" "void main (void)\n" "{\n" << attributeUse.str() << "\tgl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" "}\n"; programCollection.glslSources.add("vertex") << glu::VertexSource(vertexShaderSource.str()); fragmentShaderSource << "#version 310 es\n" "precision highp float;\n" "layout(location = 0) out vec4 fragColor;\n" "void main (void)\n" "{\n" "\tfragColor = vec4(1.0);\n" "}\n"; programCollection.glslSources.add("fragment") << glu::FragmentSource(fragmentShaderSource.str()); } // DrawAccessTest DrawAccessTest::DrawAccessTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances) : VertexAccessTest (testContext, name, description, inputFormat, numVertexValues, numInstanceValues, numVertices, numInstances) { } TestInstance* DrawAccessTest::createInstance (Context& context) const { Move device = createRobustBufferAccessDevice(context); return new DrawAccessInstance(context, device, m_inputFormat, m_numVertexValues, m_numInstanceValues, m_numVertices, m_numInstances); } // DrawIndexedAccessTest const deUint32 lastIndexOutOfBounds[] = { 0, 1, 2, 3, 4, 100, // Indices of 100 and above are out of bounds }; const deUint32 indicesOutOfBounds[] = { 0, 100, 2, 101, 3, 102, // Indices of 100 and above are out of bounds }; const deUint32 triangleOutOfBounds[] = { 100, 101, 102, 3, 4, 5, // Indices of 100 and above are out of bounds }; const std::vector DrawIndexedAccessTest::s_indexConfigs[INDEX_CONFIG_COUNT] = { std::vector(lastIndexOutOfBounds, lastIndexOutOfBounds + DE_LENGTH_OF_ARRAY(lastIndexOutOfBounds)), std::vector(indicesOutOfBounds, indicesOutOfBounds + DE_LENGTH_OF_ARRAY(indicesOutOfBounds)), std::vector(triangleOutOfBounds, triangleOutOfBounds + DE_LENGTH_OF_ARRAY(triangleOutOfBounds)), }; DrawIndexedAccessTest::DrawIndexedAccessTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, VkFormat inputFormat, IndexConfig indexConfig) : VertexAccessTest (testContext, name, description, inputFormat, getNumUsedChannels(mapVkFormat(inputFormat).order) * (deUint32)s_indexConfigs[indexConfig].size() * 2, // numVertexValues getNumUsedChannels(mapVkFormat(inputFormat).order), // numInstanceValues (deUint32)s_indexConfigs[indexConfig].size(), // numVertices 1) // numInstances , m_indexConfig (indexConfig) { } TestInstance* DrawIndexedAccessTest::createInstance (Context& context) const { Move device = createRobustBufferAccessDevice(context); return new DrawIndexedAccessInstance(context, device, m_inputFormat, m_numVertexValues, m_numInstanceValues, m_numVertices, m_numInstances, s_indexConfigs[m_indexConfig]); } // VertexAccessInstance VertexAccessInstance::VertexAccessInstance (Context& context, Move device, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances, const std::vector& indices) : vkt::TestInstance (context) , m_device (device) , m_inputFormat (inputFormat) , m_numVertexValues (numVertexValues) , m_numInstanceValues (numInstanceValues) , m_numVertices (numVertices) , m_numInstances (numInstances) { const DeviceInterface& vk = context.getDeviceInterface(); const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); SimpleAllocator memAlloc (vk, *m_device, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice())); const deUint32 formatSizeInBytes = tcu::getPixelSize(mapVkFormat(m_inputFormat)); // Check storage support if (!context.getDeviceFeatures().vertexPipelineStoresAndAtomics) { TCU_THROW(NotSupportedError, "Stores not supported in vertex stage"); } if (m_inputFormat == VK_FORMAT_R64_UINT || m_inputFormat == VK_FORMAT_R64_SINT) { const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(context.getInstanceInterface(), context.getPhysicalDevice(), m_inputFormat); context.requireDeviceFunctionality("VK_EXT_shader_image_atomic_int64"); if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) != VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) TCU_THROW(NotSupportedError, "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT not supported"); } const VkVertexInputAttributeDescription attributes[] = { // input rate: vertex { 0u, // deUint32 location; 0u, // deUint32 binding; m_inputFormat, // VkFormat format; 0u, // deUint32 offset; }, { 1u, // deUint32 location; 0u, // deUint32 binding; m_inputFormat, // VkFormat format; formatSizeInBytes, // deUint32 offset; }, // input rate: instance { 2u, // deUint32 location; 1u, // deUint32 binding; m_inputFormat, // VkFormat format; 0u, // deUint32 offset; }, // Attribute for vertex number { 3u, // deUint32 location; 2u, // deUint32 binding; VK_FORMAT_R32_SINT, // VkFormat format; 0, // deUint32 offset; }, }; const VkVertexInputBindingDescription bindings[] = { { 0u, // deUint32 binding; formatSizeInBytes * 2, // deUint32 stride; VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputRate inputRate; }, { 1u, // deUint32 binding; formatSizeInBytes, // deUint32 stride; VK_VERTEX_INPUT_RATE_INSTANCE // VkVertexInputRate inputRate; }, { 2u, // deUint32 binding; sizeof(deInt32), // deUint32 stride; VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputRate inputRate; }, }; m_vertexInputBindings = std::vector(bindings, bindings + DE_LENGTH_OF_ARRAY(bindings)); m_vertexInputAttributes = std::vector(attributes, attributes + DE_LENGTH_OF_ARRAY(attributes)); // Create vertex buffer for vertex input rate { VkMemoryRequirements bufferMemoryReqs; m_vertexRateBufferSize = getBufferSizeInBytes(m_numVertexValues, m_inputFormat); // All formats used in this test suite are 32-bit based. const VkBufferCreateInfo vertexRateBufferParams = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkBufferCreateFlags flags; m_vertexRateBufferSize, // VkDeviceSize size; VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex // const deUint32* pQueueFamilyIndices; }; m_vertexRateBuffer = createBuffer(vk, *m_device, &vertexRateBufferParams); bufferMemoryReqs = getBufferMemoryRequirements(vk, *m_device, *m_vertexRateBuffer); m_vertexRateBufferAllocSize = bufferMemoryReqs.size; m_vertexRateBufferAlloc = memAlloc.allocate(bufferMemoryReqs, MemoryRequirement::HostVisible); VK_CHECK(vk.bindBufferMemory(*m_device, *m_vertexRateBuffer, m_vertexRateBufferAlloc->getMemory(), m_vertexRateBufferAlloc->getOffset())); populateBufferWithTestValues(m_vertexRateBufferAlloc->getHostPtr(), (deUint32)m_vertexRateBufferAllocSize, m_inputFormat); flushMappedMemoryRange(vk, *m_device, m_vertexRateBufferAlloc->getMemory(), m_vertexRateBufferAlloc->getOffset(), VK_WHOLE_SIZE); } // Create vertex buffer for instance input rate { VkMemoryRequirements bufferMemoryReqs; m_instanceRateBufferSize = getBufferSizeInBytes(m_numInstanceValues, m_inputFormat); // All formats used in this test suite are 32-bit based. const VkBufferCreateInfo instanceRateBufferParams = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkBufferCreateFlags flags; m_instanceRateBufferSize, // VkDeviceSize size; VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex // const deUint32* pQueueFamilyIndices; }; m_instanceRateBuffer = createBuffer(vk, *m_device, &instanceRateBufferParams); bufferMemoryReqs = getBufferMemoryRequirements(vk, *m_device, *m_instanceRateBuffer); m_instanceRateBufferAllocSize = bufferMemoryReqs.size; m_instanceRateBufferAlloc = memAlloc.allocate(bufferMemoryReqs, MemoryRequirement::HostVisible); VK_CHECK(vk.bindBufferMemory(*m_device, *m_instanceRateBuffer, m_instanceRateBufferAlloc->getMemory(), m_instanceRateBufferAlloc->getOffset())); populateBufferWithTestValues(m_instanceRateBufferAlloc->getHostPtr(), (deUint32)m_instanceRateBufferAllocSize, m_inputFormat); flushMappedMemoryRange(vk, *m_device, m_instanceRateBufferAlloc->getMemory(), m_instanceRateBufferAlloc->getOffset(), VK_WHOLE_SIZE); } // Create vertex buffer that stores the vertex number (from 0 to m_numVertices - 1) { m_vertexNumBufferSize = 128 * sizeof(deInt32); // Allocate enough device memory for all indices (0 to 127). const VkBufferCreateInfo vertexNumBufferParams = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkBufferCreateFlags flags; m_vertexNumBufferSize, // VkDeviceSize size; VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex // const deUint32* pQueueFamilyIndices; }; m_vertexNumBuffer = createBuffer(vk, *m_device, &vertexNumBufferParams); m_vertexNumBufferAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, *m_device, *m_vertexNumBuffer), MemoryRequirement::HostVisible); VK_CHECK(vk.bindBufferMemory(*m_device, *m_vertexNumBuffer, m_vertexNumBufferAlloc->getMemory(), m_vertexNumBufferAlloc->getOffset())); } // Create index buffer if required if (!indices.empty()) { m_indexBufferSize = sizeof(deUint32) * indices.size(); const VkBufferCreateInfo indexBufferParams = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkBufferCreateFlags flags; m_indexBufferSize, // VkDeviceSize size; VK_BUFFER_USAGE_INDEX_BUFFER_BIT, // VkBufferUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex // const deUint32* pQueueFamilyIndices; }; m_indexBuffer = createBuffer(vk, *m_device, &indexBufferParams); m_indexBufferAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, *m_device, *m_indexBuffer), MemoryRequirement::HostVisible); VK_CHECK(vk.bindBufferMemory(*m_device, *m_indexBuffer, m_indexBufferAlloc->getMemory(), m_indexBufferAlloc->getOffset())); deMemcpy(m_indexBufferAlloc->getHostPtr(), indices.data(), (size_t)m_indexBufferSize); flushMappedMemoryRange(vk, *m_device, m_indexBufferAlloc->getMemory(), m_indexBufferAlloc->getOffset(), VK_WHOLE_SIZE); } // Create result ssbo { const int numChannels = getNumUsedChannels(mapVkFormat(m_inputFormat).order); m_outBufferSize = getBufferSizeInBytes(m_numVertices * m_numInstances * numChannels * 3, VK_FORMAT_R32_UINT); const VkBufferCreateInfo outBufferParams = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkBufferCreateFlags flags; m_outBufferSize, // VkDeviceSize size; VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // VkBufferUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex // const deUint32* pQueueFamilyIndices; }; m_outBuffer = createBuffer(vk, *m_device, &outBufferParams); m_outBufferAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, *m_device, *m_outBuffer), MemoryRequirement::HostVisible); VK_CHECK(vk.bindBufferMemory(*m_device, *m_outBuffer, m_outBufferAlloc->getMemory(), m_outBufferAlloc->getOffset())); deMemset(m_outBufferAlloc->getHostPtr(), 0xFF, (size_t)m_outBufferSize); flushMappedMemoryRange(vk, *m_device, m_outBufferAlloc->getMemory(), m_outBufferAlloc->getOffset(), VK_WHOLE_SIZE); } // Create descriptor set data { DescriptorPoolBuilder descriptorPoolBuilder; descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u); m_descriptorPool = descriptorPoolBuilder.build(vk, *m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); DescriptorSetLayoutBuilder setLayoutBuilder; setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_VERTEX_BIT); m_descriptorSetLayout = setLayoutBuilder.build(vk, *m_device); const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; *m_descriptorPool, // VkDescriptorPool desciptorPool; 1u, // deUint32 setLayoutCount; &m_descriptorSetLayout.get() // const VkDescriptorSetLayout* pSetLayouts; }; m_descriptorSet = allocateDescriptorSet(vk, *m_device, &descriptorSetAllocateInfo); const VkDescriptorBufferInfo outBufferDescriptorInfo = makeDescriptorBufferInfo(*m_outBuffer, 0ull, VK_WHOLE_SIZE); DescriptorSetUpdateBuilder setUpdateBuilder; setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outBufferDescriptorInfo); setUpdateBuilder.update(vk, *m_device); } // Create fence { const VkFenceCreateInfo fenceParams = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u // VkFenceCreateFlags flags; }; m_fence = createFence(vk, *m_device, &fenceParams); } // Get queue vk.getDeviceQueue(*m_device, queueFamilyIndex, 0, &m_queue); // Setup graphics test environment { GraphicsEnvironment::DrawConfig drawConfig; drawConfig.vertexBuffers.push_back(*m_vertexRateBuffer); drawConfig.vertexBuffers.push_back(*m_instanceRateBuffer); drawConfig.vertexBuffers.push_back(*m_vertexNumBuffer); drawConfig.vertexCount = m_numVertices; drawConfig.instanceCount = m_numInstances; drawConfig.indexBuffer = *m_indexBuffer; drawConfig.indexCount = (deUint32)(m_indexBufferSize / sizeof(deUint32)); m_graphicsTestEnvironment = de::MovePtr(new GraphicsEnvironment(m_context, *m_device, *m_descriptorSetLayout, *m_descriptorSet, GraphicsEnvironment::VertexBindings(bindings, bindings + DE_LENGTH_OF_ARRAY(bindings)), GraphicsEnvironment::VertexAttributes(attributes, attributes + DE_LENGTH_OF_ARRAY(attributes)), drawConfig)); } } tcu::TestStatus VertexAccessInstance::iterate (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const vk::VkCommandBuffer cmdBuffer = m_graphicsTestEnvironment->getCommandBuffer(); // Initialize vertex ids { deUint32 *bufferPtr = reinterpret_cast(m_vertexNumBufferAlloc->getHostPtr()); deMemset(bufferPtr, 0, (size_t)m_vertexNumBufferSize); initVertexIds(bufferPtr, (size_t)(m_vertexNumBufferSize / sizeof(deUint32))); flushMappedMemoryRange(vk, *m_device, m_vertexNumBufferAlloc->getMemory(), m_vertexNumBufferAlloc->getOffset(), VK_WHOLE_SIZE); } // Submit command buffer { const VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // deUint32 waitSemaphoreCount; DE_NULL, // const VkSemaphore* pWaitSemaphores; DE_NULL, // const VkPIpelineStageFlags* pWaitDstStageMask; 1u, // deUint32 commandBufferCount; &cmdBuffer, // const VkCommandBuffer* pCommandBuffers; 0u, // deUint32 signalSemaphoreCount; DE_NULL // const VkSemaphore* pSignalSemaphores; }; VK_CHECK(vk.resetFences(*m_device, 1, &m_fence.get())); VK_CHECK(vk.queueSubmit(m_queue, 1, &submitInfo, *m_fence)); VK_CHECK(vk.waitForFences(*m_device, 1, &m_fence.get(), true, ~(0ull) /* infinity */)); } // Prepare result buffer for read { const VkMappedMemoryRange outBufferRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, // VkStructureType sType; DE_NULL, // const void* pNext; m_outBufferAlloc->getMemory(), // VkDeviceMemory mem; 0ull, // VkDeviceSize offset; m_outBufferSize, // VkDeviceSize size; }; VK_CHECK(vk.invalidateMappedMemoryRanges(*m_device, 1u, &outBufferRange)); } if (verifyResult()) return tcu::TestStatus::pass("All values OK"); else return tcu::TestStatus::fail("Invalid value(s) found"); } bool VertexAccessInstance::verifyResult (void) { std::ostringstream logMsg; const DeviceInterface& vk = m_context.getDeviceInterface(); tcu::TestLog& log = m_context.getTestContext().getLog(); const deUint32 numChannels = getNumUsedChannels(mapVkFormat(m_inputFormat).order); const deUint32 numScalarsPerVertex = numChannels * 3; // Use 3 identical attributes void* outDataPtr = m_outBufferAlloc->getHostPtr(); const deUint32 outValueSize = static_cast((m_inputFormat == VK_FORMAT_R64_UINT || m_inputFormat == VK_FORMAT_R64_SINT) ? sizeof(deUint64) : sizeof(deUint32)); bool allOk = true; const VkMappedMemoryRange outBufferRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, // VkStructureType sType; DE_NULL, // const void* pNext; m_outBufferAlloc->getMemory(), // VkDeviceMemory mem; m_outBufferAlloc->getOffset(), // VkDeviceSize offset; m_outBufferSize, // VkDeviceSize size; }; VK_CHECK(vk.invalidateMappedMemoryRanges(*m_device, 1u, &outBufferRange)); for (deUint32 valueNdx = 0; valueNdx < m_outBufferSize / outValueSize; valueNdx++) { deUint32 numInBufferValues; void* inBufferPtr; VkDeviceSize inBufferAllocSize; deUint32 inBufferValueIndex; bool isOutOfBoundsAccess = false; const deUint32 attributeIndex = (valueNdx / numChannels) % 3; deUint32* ptr32 = (deUint32*)outDataPtr; deUint64* ptr64 = (deUint64*)outDataPtr; const void* outValuePtr = ((m_inputFormat == VK_FORMAT_R64_UINT || m_inputFormat == VK_FORMAT_R64_SINT) ? (void*)(ptr64 + valueNdx) : (void*)(ptr32 + valueNdx)); if (attributeIndex == 2) { // Instance rate const deUint32 elementIndex = valueNdx / (numScalarsPerVertex * m_numVertices); // instance id numInBufferValues = m_numInstanceValues; inBufferPtr = m_instanceRateBufferAlloc->getHostPtr(); inBufferAllocSize = m_instanceRateBufferAllocSize; inBufferValueIndex = (elementIndex * numChannels) + (valueNdx % numScalarsPerVertex) - (2 * numChannels); } else { // Vertex rate const deUint32 vertexNdx = valueNdx / numScalarsPerVertex; const deUint32 instanceNdx = vertexNdx / m_numVertices; const deUint32 elementIndex = valueNdx / numScalarsPerVertex; // vertex id numInBufferValues = m_numVertexValues; inBufferPtr = m_vertexRateBufferAlloc->getHostPtr(); inBufferAllocSize = m_vertexRateBufferAllocSize; inBufferValueIndex = (getIndex(elementIndex) * (numChannels * 2)) + (valueNdx % numScalarsPerVertex) - instanceNdx * (m_numVertices * numChannels * 2); // Binding 0 contains two attributes, so bounds checking for attribute 0 must also consider attribute 1 to determine if the binding is out of bounds. if ((attributeIndex == 0) && (numInBufferValues >= numChannels)) numInBufferValues -= numChannels; } isOutOfBoundsAccess = (inBufferValueIndex >= numInBufferValues); const deInt32 distanceToOutOfBounds = (deInt32)outValueSize * ((deInt32)numInBufferValues - (deInt32)inBufferValueIndex); if (!isOutOfBoundsAccess && (distanceToOutOfBounds < 16)) isOutOfBoundsAccess = (((inBufferValueIndex / numChannels) + 1) * numChannels > numInBufferValues); // Log value information { // Vertex separator if (valueNdx && valueNdx % numScalarsPerVertex == 0) logMsg << "\n"; logMsg << "\n" << valueNdx << ": Value "; // Result index and value if (m_inputFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) logValue(logMsg, outValuePtr, VK_FORMAT_R32_SFLOAT, 4); else logValue(logMsg, outValuePtr, m_inputFormat, 4); // Attribute name logMsg << "\tfrom attr" << attributeIndex; if (numChannels > 1) logMsg << "[" << valueNdx % numChannels << "]"; // Input rate if (attributeIndex == 2) logMsg << "\tinstance rate"; else logMsg << "\tvertex rate"; } if (isOutOfBoundsAccess) { const bool isValidValue = isValueWithinVertexBufferOrZero(inBufferPtr, inBufferAllocSize, outValuePtr, inBufferValueIndex); logMsg << "\t(out of bounds)"; if (!isValidValue) { // Check if we are satisfying the [0, 0, 0, x] pattern, where x may be either 0 or 1, // or the maximum representable positive integer value (if the format is integer-based). const bool canMatchVec4Pattern = ((valueNdx % numChannels == 3) || m_inputFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32); bool matchesVec4Pattern = false; if (canMatchVec4Pattern) { if (m_inputFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) matchesVec4Pattern = verifyOutOfBoundsVec4(outValuePtr, m_inputFormat); else matchesVec4Pattern = verifyOutOfBoundsVec4(((deUint32*)outValuePtr) - 3, m_inputFormat); } if (!canMatchVec4Pattern || !matchesVec4Pattern) { logMsg << ", Failed: expected a value within the buffer range or 0"; if (canMatchVec4Pattern) logMsg << ", or the [0, 0, 0, x] pattern"; allOk = false; } } } else if (!isExpectedValueFromVertexBuffer(inBufferPtr, inBufferValueIndex, m_inputFormat, outValuePtr)) { logMsg << ", Failed: unexpected value"; allOk = false; } } log << tcu::TestLog::Message << logMsg.str() << tcu::TestLog::EndMessage; return allOk; } bool VertexAccessInstance::isValueWithinVertexBufferOrZero(void* vertexBuffer, VkDeviceSize vertexBufferSize, const void* value, deUint32 valueIndex) { if (m_inputFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) { const float normValue = *reinterpret_cast(value); const deUint32 scalarIndex = valueIndex % 4; const bool isAlpha = (scalarIndex == 3); deUint32 encodedValue; if (isAlpha) encodedValue = deMin32(deUint32(deFloatRound(normValue * 0x3u)), 0x3u); else encodedValue = deMin32(deUint32(deFloatRound(normValue * 0x3FFu)), 0x3FFu); if (encodedValue == 0) return true; for (deUint32 i = 0; i < vertexBufferSize / 4; i++) { const deUint32 packedValue = reinterpret_cast(vertexBuffer)[i]; deUint32 unpackedValue; if (scalarIndex < 3) unpackedValue = (packedValue >> (10 * scalarIndex)) & 0x3FFu; else unpackedValue = (packedValue >> 30) & 0x3u; if (unpackedValue == encodedValue) return true; } return false; } else { return isValueWithinBufferOrZero(vertexBuffer, vertexBufferSize, value, sizeof(deUint32)); } } bool VertexAccessInstance::isExpectedValueFromVertexBuffer (const void* vertexBuffer, deUint32 vertexIndex, VkFormat vertexFormat, const void* value) { if (isUintFormat(vertexFormat)) { if (vertexFormat == VK_FORMAT_R64_UINT || vertexFormat == VK_FORMAT_R64_SINT) { const deUint64* bufferPtr = reinterpret_cast(vertexBuffer); return bufferPtr[vertexIndex] == *reinterpret_cast(value); } else { const deUint32* bufferPtr = reinterpret_cast(vertexBuffer); return bufferPtr[vertexIndex] == *reinterpret_cast(value); } } else if (isIntFormat(vertexFormat)) { if (vertexFormat == VK_FORMAT_R64_UINT || vertexFormat == VK_FORMAT_R64_SINT) { const deInt64* bufferPtr = reinterpret_cast(vertexBuffer); return bufferPtr[vertexIndex] == *reinterpret_cast(value); } else { const deInt32* bufferPtr = reinterpret_cast(vertexBuffer); return bufferPtr[vertexIndex] == *reinterpret_cast(value); } } else if (isFloatFormat(vertexFormat)) { const float* bufferPtr = reinterpret_cast(vertexBuffer); return areEqual(bufferPtr[vertexIndex], *reinterpret_cast(value)); } else if (vertexFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) { const deUint32* bufferPtr = reinterpret_cast(vertexBuffer); const deUint32 packedValue = bufferPtr[vertexIndex / 4]; const deUint32 scalarIndex = vertexIndex % 4; float normValue; if (scalarIndex < 3) normValue = float((packedValue >> (10 * scalarIndex)) & 0x3FFu) / 0x3FFu; else normValue = float(packedValue >> 30) / 0x3u; return areEqual(normValue, *reinterpret_cast(value)); } DE_ASSERT(false); return false; } VkDeviceSize VertexAccessInstance::getBufferSizeInBytes (deUint32 numScalars, VkFormat format) { if (isUintFormat(format) || isIntFormat(format) || isFloatFormat(format)) { return numScalars * ((format == VK_FORMAT_R64_UINT || format == VK_FORMAT_R64_SINT) ? 8 : 4); } else if (format == VK_FORMAT_A2B10G10R10_UNORM_PACK32) { DE_ASSERT(numScalars % 4 == 0); return numScalars; } DE_ASSERT(false); return 0; } // DrawAccessInstance DrawAccessInstance::DrawAccessInstance (Context& context, Move device, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances) : VertexAccessInstance (context, device, inputFormat, numVertexValues, numInstanceValues, numVertices, numInstances, std::vector()) // No index buffer { } void DrawAccessInstance::initVertexIds (deUint32 *indicesPtr, size_t indexCount) { for (deUint32 i = 0; i < indexCount; i++) indicesPtr[i] = i; } deUint32 DrawAccessInstance::getIndex (deUint32 vertexNum) const { return vertexNum; } // DrawIndexedAccessInstance DrawIndexedAccessInstance::DrawIndexedAccessInstance (Context& context, Move device, VkFormat inputFormat, deUint32 numVertexValues, deUint32 numInstanceValues, deUint32 numVertices, deUint32 numInstances, const std::vector& indices) : VertexAccessInstance (context, device, inputFormat, numVertexValues, numInstanceValues, numVertices, numInstances, indices) , m_indices (indices) { } void DrawIndexedAccessInstance::initVertexIds (deUint32 *indicesPtr, size_t indexCount) { DE_UNREF(indexCount); for (deUint32 i = 0; i < m_indices.size(); i++) { DE_ASSERT(m_indices[i] < indexCount); indicesPtr[m_indices[i]] = i; } } deUint32 DrawIndexedAccessInstance::getIndex (deUint32 vertexNum) const { DE_ASSERT(vertexNum < (deUint32)m_indices.size()); return m_indices[vertexNum]; } // Test node creation functions static tcu::TestCaseGroup* createDrawTests (tcu::TestContext& testCtx, VkFormat format) { struct TestConfig { std::string name; std::string description; VkFormat inputFormat; deUint32 numVertexValues; deUint32 numInstanceValues; deUint32 numVertices; deUint32 numInstances; }; const deUint32 numChannels = getNumUsedChannels(mapVkFormat(format).order); const TestConfig testConfigs[] = { // name description format numVertexValues numInstanceValues numVertices numInstances { "vertex_out_of_bounds", "Create data for 6 vertices, draw 9 vertices", format, numChannels * 2 * 6, numChannels, 9, 1 }, { "vertex_incomplete", "Create data for half a vertex, draw 3 vertices", format, numChannels, numChannels, 3, 1 }, { "instance_out_of_bounds", "Create data for 1 instance, draw 3 instances", format, numChannels * 2 * 9, numChannels, 3, 3 }, }; de::MovePtr drawTests (new tcu::TestCaseGroup(testCtx, "draw", "")); for (int i = 0; i < DE_LENGTH_OF_ARRAY(testConfigs); i++) { const TestConfig &config = testConfigs[i]; drawTests->addChild(new DrawAccessTest(testCtx, config.name, config.description, config.inputFormat, config.numVertexValues, config.numInstanceValues, config.numVertices, config.numInstances)); } return drawTests.release(); } static tcu::TestCaseGroup* createDrawIndexedTests (tcu::TestContext& testCtx, VkFormat format) { struct TestConfig { std::string name; std::string description; VkFormat inputFormat; DrawIndexedAccessTest::IndexConfig indexConfig; }; const TestConfig testConfigs[] = { // name description format indexConfig { "last_index_out_of_bounds", "Only last index is out of bounds", format, DrawIndexedAccessTest::INDEX_CONFIG_LAST_INDEX_OUT_OF_BOUNDS }, { "indices_out_of_bounds", "Random indices out of bounds", format, DrawIndexedAccessTest::INDEX_CONFIG_INDICES_OUT_OF_BOUNDS }, { "triangle_out_of_bounds", "First triangle is out of bounds", format, DrawIndexedAccessTest::INDEX_CONFIG_TRIANGLE_OUT_OF_BOUNDS }, }; de::MovePtr drawTests (new tcu::TestCaseGroup(testCtx, "draw_indexed", "")); for (int i = 0; i < DE_LENGTH_OF_ARRAY(testConfigs); i++) { const TestConfig &config = testConfigs[i]; drawTests->addChild(new DrawIndexedAccessTest(testCtx, config.name, config.description, config.inputFormat, config.indexConfig)); } return drawTests.release(); } static void addVertexFormatTests (tcu::TestContext& testCtx, tcu::TestCaseGroup* parentGroup) { const VkFormat vertexFormats[] = { VK_FORMAT_R32_UINT, VK_FORMAT_R32_SINT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_UINT, VK_FORMAT_R32G32B32_SINT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R64_UINT, VK_FORMAT_R64_SINT, VK_FORMAT_A2B10G10R10_UNORM_PACK32 }; for (int i = 0; i < DE_LENGTH_OF_ARRAY(vertexFormats); i++) { const std::string formatName = getFormatName(vertexFormats[i]); de::MovePtr formatGroup (new tcu::TestCaseGroup(testCtx, de::toLower(formatName.substr(10)).c_str(), "")); formatGroup->addChild(createDrawTests(testCtx, vertexFormats[i])); formatGroup->addChild(createDrawIndexedTests(testCtx, vertexFormats[i])); parentGroup->addChild(formatGroup.release()); } } tcu::TestCaseGroup* createVertexAccessTests (tcu::TestContext& testCtx) { de::MovePtr vertexAccessTests (new tcu::TestCaseGroup(testCtx, "vertex_access", "")); addVertexFormatTests(testCtx, vertexAccessTests.get()); return vertexAccessTests.release(); } } // robustness } // vkt