| /*------------------------------------------------------------------------ |
| * 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 <vector> |
| |
| namespace vkt |
| { |
| namespace robustness |
| { |
| |
| using namespace vk; |
| |
| typedef std::vector<VkVertexInputBindingDescription> BindingList; |
| typedef std::vector<VkVertexInputAttributeDescription> 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<deUint32> 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<VkDevice> device, |
| VkFormat inputFormat, |
| deUint32 numVertexValues, |
| deUint32 numInstanceValues, |
| deUint32 numVertices, |
| deUint32 numInstances, |
| const std::vector<deUint32>& 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<VkDevice> 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<VkBuffer> m_vertexRateBuffer; |
| VkDeviceSize m_vertexRateBufferSize; |
| de::MovePtr<Allocation> m_vertexRateBufferAlloc; |
| VkDeviceSize m_vertexRateBufferAllocSize; |
| |
| Move<VkBuffer> m_instanceRateBuffer; |
| VkDeviceSize m_instanceRateBufferSize; |
| de::MovePtr<Allocation> m_instanceRateBufferAlloc; |
| VkDeviceSize m_instanceRateBufferAllocSize; |
| |
| Move<VkBuffer> m_vertexNumBuffer; |
| VkDeviceSize m_vertexNumBufferSize; |
| de::MovePtr<Allocation> m_vertexNumBufferAlloc; |
| |
| Move<VkBuffer> m_indexBuffer; |
| VkDeviceSize m_indexBufferSize; |
| de::MovePtr<Allocation> m_indexBufferAlloc; |
| |
| Move<VkBuffer> m_outBuffer; // SSBO |
| VkDeviceSize m_outBufferSize; |
| de::MovePtr<Allocation> m_outBufferAlloc; |
| |
| Move<VkDescriptorPool> m_descriptorPool; |
| Move<VkDescriptorSetLayout> m_descriptorSetLayout; |
| Move<VkDescriptorSet> m_descriptorSet; |
| |
| Move<VkFence> m_fence; |
| VkQueue m_queue; |
| |
| de::MovePtr<GraphicsEnvironment> m_graphicsTestEnvironment; |
| }; |
| |
| class DrawAccessInstance : public VertexAccessInstance |
| { |
| public: |
| DrawAccessInstance (Context& context, |
| Move<VkDevice> 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<VkDevice> device, |
| VkFormat inputFormat, |
| deUint32 numVertexValues, |
| deUint32 numInstanceValues, |
| deUint32 numVertices, |
| deUint32 numInstances, |
| const std::vector<deUint32>& indices); |
| |
| virtual ~DrawIndexedAccessInstance (void) {} |
| |
| protected: |
| virtual void initVertexIds (deUint32 *indicesPtr, size_t indexCount); |
| virtual deUint32 getIndex (deUint32 vertexNum) const; |
| |
| const std::vector<deUint32> 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<VkDevice> 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<deUint32> DrawIndexedAccessTest::s_indexConfigs[INDEX_CONFIG_COUNT] = |
| { |
| std::vector<deUint32>(lastIndexOutOfBounds, lastIndexOutOfBounds + DE_LENGTH_OF_ARRAY(lastIndexOutOfBounds)), |
| std::vector<deUint32>(indicesOutOfBounds, indicesOutOfBounds + DE_LENGTH_OF_ARRAY(indicesOutOfBounds)), |
| std::vector<deUint32>(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<VkDevice> 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<VkDevice> device, |
| VkFormat inputFormat, |
| deUint32 numVertexValues, |
| deUint32 numInstanceValues, |
| deUint32 numVertices, |
| deUint32 numInstances, |
| const std::vector<deUint32>& 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<VkVertexInputBindingDescription>(bindings, bindings + DE_LENGTH_OF_ARRAY(bindings)); |
| m_vertexInputAttributes = std::vector<VkVertexInputAttributeDescription>(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<GraphicsEnvironment>(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<deUint32*>(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<deUint32>((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<const float*>(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<deUint32*>(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<const deUint64*>(vertexBuffer); |
| return bufferPtr[vertexIndex] == *reinterpret_cast<const deUint64 *>(value); |
| } |
| else |
| { |
| const deUint32* bufferPtr = reinterpret_cast<const deUint32*>(vertexBuffer); |
| return bufferPtr[vertexIndex] == *reinterpret_cast<const deUint32 *>(value); |
| } |
| } |
| else if (isIntFormat(vertexFormat)) |
| { |
| if (vertexFormat == VK_FORMAT_R64_UINT || vertexFormat == VK_FORMAT_R64_SINT) |
| { |
| const deInt64* bufferPtr = reinterpret_cast<const deInt64*>(vertexBuffer); |
| return bufferPtr[vertexIndex] == *reinterpret_cast<const deInt64 *>(value); |
| } |
| else |
| { |
| const deInt32* bufferPtr = reinterpret_cast<const deInt32*>(vertexBuffer); |
| return bufferPtr[vertexIndex] == *reinterpret_cast<const deInt32 *>(value); |
| } |
| } |
| else if (isFloatFormat(vertexFormat)) |
| { |
| const float* bufferPtr = reinterpret_cast<const float*>(vertexBuffer); |
| |
| return areEqual(bufferPtr[vertexIndex], *reinterpret_cast<const float *>(value)); |
| } |
| else if (vertexFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) |
| { |
| const deUint32* bufferPtr = reinterpret_cast<const deUint32*>(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<const float *>(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<VkDevice> device, |
| VkFormat inputFormat, |
| deUint32 numVertexValues, |
| deUint32 numInstanceValues, |
| deUint32 numVertices, |
| deUint32 numInstances) |
| : VertexAccessInstance (context, |
| device, |
| inputFormat, |
| numVertexValues, |
| numInstanceValues, |
| numVertices, |
| numInstances, |
| std::vector<deUint32>()) // 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<VkDevice> device, |
| VkFormat inputFormat, |
| deUint32 numVertexValues, |
| deUint32 numInstanceValues, |
| deUint32 numVertices, |
| deUint32 numInstances, |
| const std::vector<deUint32>& 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<tcu::TestCaseGroup> 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<tcu::TestCaseGroup> 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<tcu::TestCaseGroup> 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<tcu::TestCaseGroup> vertexAccessTests (new tcu::TestCaseGroup(testCtx, "vertex_access", "")); |
| |
| addVertexFormatTests(testCtx, vertexAccessTests.get()); |
| |
| return vertexAccessTests.release(); |
| } |
| |
| } // robustness |
| } // vkt |