| /*------------------------------------------------------------------------ |
| * 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 buffer access tests for uniform/storage buffers and |
| * uniform/storage texel buffers. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktRobustnessBufferAccessTests.hpp" |
| #include "vktRobustnessUtil.hpp" |
| #include "vktTestCaseUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkImageUtil.hpp" |
| #include "vkPrograms.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkRef.hpp" |
| #include "vkRefUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include <limits> |
| #include <sstream> |
| |
| namespace vkt |
| { |
| namespace robustness |
| { |
| |
| using namespace vk; |
| |
| enum ShaderType |
| { |
| SHADER_TYPE_MATRIX_COPY, |
| SHADER_TYPE_VECTOR_COPY, |
| SHADER_TYPE_SCALAR_COPY, |
| SHADER_TYPE_TEXEL_COPY, |
| |
| SHADER_TYPE_COUNT |
| }; |
| |
| enum BufferAccessType |
| { |
| BUFFER_ACCESS_TYPE_READ, |
| BUFFER_ACCESS_TYPE_READ_FROM_STORAGE, |
| BUFFER_ACCESS_TYPE_WRITE, |
| }; |
| |
| static VkDeviceSize min (VkDeviceSize a, VkDeviceSize b) |
| { |
| return (a < b) ? a : b; |
| } |
| |
| class RobustBufferAccessTest : public vkt::TestCase |
| { |
| public: |
| static const deUint32 s_testArraySize; |
| static const deUint32 s_numberOfBytesAccessed; |
| |
| RobustBufferAccessTest (tcu::TestContext& testContext, |
| const std::string& name, |
| const std::string& description, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat); |
| |
| virtual ~RobustBufferAccessTest (void) {} |
| |
| virtual void checkSupport (Context& context) const; |
| |
| private: |
| static void genBufferShaderAccess (ShaderType shaderType, |
| VkFormat bufferFormat, |
| bool readFromStorage, |
| std::ostringstream& bufferDefinition, |
| std::ostringstream& bufferUse); |
| |
| static void genTexelBufferShaderAccess (VkFormat bufferFormat, |
| std::ostringstream& bufferDefinition, |
| std::ostringstream& bufferUse, |
| bool readFromStorage); |
| |
| protected: |
| static void initBufferAccessPrograms (SourceCollections& programCollection, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat, |
| bool readFromStorage); |
| |
| const VkShaderStageFlags m_shaderStage; |
| const ShaderType m_shaderType; |
| const VkFormat m_bufferFormat; |
| }; |
| |
| class RobustBufferReadTest : public RobustBufferAccessTest |
| { |
| public: |
| RobustBufferReadTest (tcu::TestContext& testContext, |
| const std::string& name, |
| const std::string& description, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat, |
| VkDeviceSize readAccessRange, |
| bool readFromStorage, |
| bool accessOutOfBackingMemory); |
| |
| virtual ~RobustBufferReadTest (void) {} |
| |
| virtual void initPrograms (SourceCollections& programCollection) const; |
| virtual TestInstance* createInstance (Context& context) const; |
| |
| private: |
| const bool m_readFromStorage; |
| const VkDeviceSize m_readAccessRange; |
| const bool m_accessOutOfBackingMemory; |
| }; |
| |
| class RobustBufferWriteTest : public RobustBufferAccessTest |
| { |
| public: |
| RobustBufferWriteTest (tcu::TestContext& testContext, |
| const std::string& name, |
| const std::string& description, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat, |
| VkDeviceSize writeAccessRange, |
| bool accessOutOfBackingMemory); |
| |
| virtual ~RobustBufferWriteTest (void) {} |
| |
| virtual void initPrograms (SourceCollections& programCollection) const; |
| virtual TestInstance* createInstance (Context& context) const; |
| |
| private: |
| const VkDeviceSize m_writeAccessRange; |
| const bool m_accessOutOfBackingMemory; |
| }; |
| |
| class BufferAccessInstance : public vkt::TestInstance |
| { |
| public: |
| BufferAccessInstance (Context& context, |
| Move<VkDevice> device, |
| ShaderType shaderType, |
| VkShaderStageFlags shaderStage, |
| VkFormat bufferFormat, |
| BufferAccessType bufferAccessType, |
| VkDeviceSize inBufferAccessRange, |
| VkDeviceSize outBufferAccessRange, |
| bool accessOutOfBackingMemory); |
| |
| virtual ~BufferAccessInstance (void) {} |
| |
| virtual tcu::TestStatus iterate (void); |
| |
| virtual bool verifyResult (void); |
| |
| private: |
| bool isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes, const void* valuePtr, VkDeviceSize valueSize); |
| bool isOutBufferValueUnchanged (VkDeviceSize offsetInBytes, VkDeviceSize valueSize); |
| |
| protected: |
| Move<VkDevice> m_device; |
| de::MovePtr<TestEnvironment> m_testEnvironment; |
| |
| const ShaderType m_shaderType; |
| const VkShaderStageFlags m_shaderStage; |
| |
| const VkFormat m_bufferFormat; |
| const BufferAccessType m_bufferAccessType; |
| |
| const VkDeviceSize m_inBufferAccessRange; |
| Move<VkBuffer> m_inBuffer; |
| de::MovePtr<Allocation> m_inBufferAlloc; |
| VkDeviceSize m_inBufferAllocSize; |
| VkDeviceSize m_inBufferMaxAccessRange; |
| |
| const VkDeviceSize m_outBufferAccessRange; |
| Move<VkBuffer> m_outBuffer; |
| de::MovePtr<Allocation> m_outBufferAlloc; |
| VkDeviceSize m_outBufferAllocSize; |
| VkDeviceSize m_outBufferMaxAccessRange; |
| |
| Move<VkBuffer> m_indicesBuffer; |
| de::MovePtr<Allocation> m_indicesBufferAlloc; |
| |
| Move<VkDescriptorPool> m_descriptorPool; |
| Move<VkDescriptorSetLayout> m_descriptorSetLayout; |
| Move<VkDescriptorSet> m_descriptorSet; |
| |
| Move<VkFence> m_fence; |
| VkQueue m_queue; |
| |
| // Used when m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT |
| Move<VkBuffer> m_vertexBuffer; |
| de::MovePtr<Allocation> m_vertexBufferAlloc; |
| |
| // Used when m_shaderType == SHADER_TYPE_TEXEL_COPY |
| Move<VkBufferView> m_inTexelBufferView; |
| Move<VkBufferView> m_outTexelBufferView; |
| |
| const bool m_accessOutOfBackingMemory; |
| }; |
| |
| class BufferReadInstance: public BufferAccessInstance |
| { |
| public: |
| BufferReadInstance (Context& context, |
| Move<VkDevice> device, |
| ShaderType shaderType, |
| VkShaderStageFlags shaderStage, |
| VkFormat bufferFormat, |
| bool readFromStorage, |
| VkDeviceSize inBufferAccessRange, |
| bool accessOutOfBackingMemory); |
| |
| virtual ~BufferReadInstance (void) {} |
| |
| private: |
| }; |
| |
| class BufferWriteInstance: public BufferAccessInstance |
| { |
| public: |
| BufferWriteInstance (Context& context, |
| Move<VkDevice> device, |
| ShaderType shaderType, |
| VkShaderStageFlags shaderStage, |
| VkFormat bufferFormat, |
| VkDeviceSize writeBufferAccessRange, |
| bool accessOutOfBackingMemory); |
| |
| virtual ~BufferWriteInstance (void) {} |
| }; |
| |
| // RobustBufferAccessTest |
| |
| const deUint32 RobustBufferAccessTest::s_testArraySize = 1024; |
| const deUint32 RobustBufferAccessTest::s_numberOfBytesAccessed = (deUint32)(16 * sizeof(float)); // size of mat4 |
| |
| RobustBufferAccessTest::RobustBufferAccessTest (tcu::TestContext& testContext, |
| const std::string& name, |
| const std::string& description, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat) |
| : vkt::TestCase (testContext, name, description) |
| , m_shaderStage (shaderStage) |
| , m_shaderType (shaderType) |
| , m_bufferFormat (bufferFormat) |
| { |
| DE_ASSERT(m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT || m_shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT || m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT); |
| } |
| |
| void RobustBufferAccessTest::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 RobustBufferAccessTest::genBufferShaderAccess (ShaderType shaderType, |
| VkFormat bufferFormat, |
| bool readFromStorage, |
| std::ostringstream& bufferDefinition, |
| std::ostringstream& bufferUse) |
| { |
| if (isFloatFormat(bufferFormat)) |
| { |
| bufferDefinition << |
| "layout(binding = 0, " << (readFromStorage ? "std430" : "std140") << ") " << (readFromStorage ? "buffer" : "uniform") << " InBuffer\n" |
| "{\n" |
| " mat4 inMatrix[" << s_testArraySize << "];\n" |
| "};\n\n"; |
| |
| bufferDefinition << |
| "layout(binding = 1, std430) buffer OutBuffer\n" |
| "{\n" |
| " mat4 outMatrix[" << s_testArraySize << "];\n" |
| "};\n\n"; |
| |
| bufferDefinition << |
| "layout(binding = 2, std140) uniform Indices\n" |
| "{\n" |
| " int inIndex;\n" |
| " int outIndex;\n" |
| "};\n\n"; |
| |
| switch (shaderType) |
| { |
| case SHADER_TYPE_MATRIX_COPY: |
| bufferUse << |
| " mat4 tmp = inMatrix[inIndex];\n" |
| " outMatrix[outIndex] = tmp;\n"; |
| break; |
| |
| case SHADER_TYPE_VECTOR_COPY: |
| bufferUse << |
| " outMatrix[outIndex][0] = inMatrix[inIndex][0];\n" |
| " outMatrix[outIndex][1] = inMatrix[inIndex][1];\n" |
| " outMatrix[outIndex][2] = inMatrix[inIndex][2];\n" |
| " outMatrix[outIndex][3] = inMatrix[inIndex][3];\n"; |
| break; |
| |
| case SHADER_TYPE_SCALAR_COPY: |
| bufferUse << |
| " outMatrix[outIndex][0][0] = inMatrix[inIndex][0][0];\n" |
| " outMatrix[outIndex][0][1] = inMatrix[inIndex][0][1];\n" |
| " outMatrix[outIndex][0][2] = inMatrix[inIndex][0][2];\n" |
| " outMatrix[outIndex][0][3] = inMatrix[inIndex][0][3];\n" |
| |
| " outMatrix[outIndex][1][0] = inMatrix[inIndex][1][0];\n" |
| " outMatrix[outIndex][1][1] = inMatrix[inIndex][1][1];\n" |
| " outMatrix[outIndex][1][2] = inMatrix[inIndex][1][2];\n" |
| " outMatrix[outIndex][1][3] = inMatrix[inIndex][1][3];\n" |
| |
| " outMatrix[outIndex][2][0] = inMatrix[inIndex][2][0];\n" |
| " outMatrix[outIndex][2][1] = inMatrix[inIndex][2][1];\n" |
| " outMatrix[outIndex][2][2] = inMatrix[inIndex][2][2];\n" |
| " outMatrix[outIndex][2][3] = inMatrix[inIndex][2][3];\n" |
| |
| " outMatrix[outIndex][3][0] = inMatrix[inIndex][3][0];\n" |
| " outMatrix[outIndex][3][1] = inMatrix[inIndex][3][1];\n" |
| " outMatrix[outIndex][3][2] = inMatrix[inIndex][3][2];\n" |
| " outMatrix[outIndex][3][3] = inMatrix[inIndex][3][3];\n"; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else |
| { |
| std::string typePrefixStr; |
| |
| if (isUintFormat(bufferFormat)) |
| { |
| typePrefixStr = "u"; |
| } |
| else if (isIntFormat(bufferFormat)) |
| { |
| typePrefixStr = "i"; |
| } |
| else |
| { |
| DE_ASSERT(false); |
| } |
| |
| typePrefixStr += (bufferFormat == vk::VK_FORMAT_R64_UINT || bufferFormat == vk::VK_FORMAT_R64_SINT) ? |
| "64" : ""; |
| |
| bufferDefinition << |
| "layout(binding = 0, " << (readFromStorage ? "std430" : "std140") << ") " << (readFromStorage ? "buffer readonly" : "uniform") << " InBuffer\n" |
| "{\n" |
| " " << typePrefixStr << "vec4 inVecs[" << s_testArraySize << "][4];\n" |
| "};\n\n"; |
| |
| bufferDefinition << |
| "layout(binding = 1, std430) buffer OutBuffer\n" |
| "{\n" |
| " " << typePrefixStr << "vec4 outVecs[" << s_testArraySize << "][4];\n" |
| "};\n\n"; |
| |
| bufferDefinition << |
| "layout(binding = 2, std140) uniform Indices\n" |
| "{\n" |
| " int inIndex;\n" |
| " int outIndex;\n" |
| "};\n\n"; |
| |
| switch (shaderType) |
| { |
| case SHADER_TYPE_MATRIX_COPY: |
| // Shader type not supported for integer types. |
| DE_ASSERT(false); |
| break; |
| |
| case SHADER_TYPE_VECTOR_COPY: |
| bufferUse << |
| " outVecs[outIndex][0] = inVecs[inIndex][0];\n" |
| " outVecs[outIndex][1] = inVecs[inIndex][1];\n" |
| " outVecs[outIndex][2] = inVecs[inIndex][2];\n" |
| " outVecs[outIndex][3] = inVecs[inIndex][3];\n"; |
| break; |
| |
| case SHADER_TYPE_SCALAR_COPY: |
| bufferUse << |
| " outVecs[outIndex][0][0] = inVecs[inIndex][0][0];\n" |
| " outVecs[outIndex][0][1] = inVecs[inIndex][0][1];\n" |
| " outVecs[outIndex][0][2] = inVecs[inIndex][0][2];\n" |
| " outVecs[outIndex][0][3] = inVecs[inIndex][0][3];\n" |
| |
| " outVecs[outIndex][1][0] = inVecs[inIndex][1][0];\n" |
| " outVecs[outIndex][1][1] = inVecs[inIndex][1][1];\n" |
| " outVecs[outIndex][1][2] = inVecs[inIndex][1][2];\n" |
| " outVecs[outIndex][1][3] = inVecs[inIndex][1][3];\n" |
| |
| " outVecs[outIndex][2][0] = inVecs[inIndex][2][0];\n" |
| " outVecs[outIndex][2][1] = inVecs[inIndex][2][1];\n" |
| " outVecs[outIndex][2][2] = inVecs[inIndex][2][2];\n" |
| " outVecs[outIndex][2][3] = inVecs[inIndex][2][3];\n" |
| |
| " outVecs[outIndex][3][0] = inVecs[inIndex][3][0];\n" |
| " outVecs[outIndex][3][1] = inVecs[inIndex][3][1];\n" |
| " outVecs[outIndex][3][2] = inVecs[inIndex][3][2];\n" |
| " outVecs[outIndex][3][3] = inVecs[inIndex][3][3];\n"; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| } |
| |
| void RobustBufferAccessTest::genTexelBufferShaderAccess (VkFormat bufferFormat, |
| std::ostringstream& bufferDefinition, |
| std::ostringstream& bufferUse, |
| bool readFromStorage) |
| { |
| const char* layoutTypeStr; |
| const char* inTexelBufferTypeStr; |
| const char* outTexelBufferTypeStr; |
| const deUint32 texelSize = mapVkFormat(bufferFormat).getPixelSize(); |
| |
| if (isFloatFormat(bufferFormat)) |
| { |
| layoutTypeStr = "rgba32f"; |
| inTexelBufferTypeStr = readFromStorage ? "imageBuffer" : "textureBuffer"; |
| outTexelBufferTypeStr = "imageBuffer"; |
| } |
| else if (isUintFormat(bufferFormat)) |
| { |
| layoutTypeStr = "rgba32ui"; |
| inTexelBufferTypeStr = readFromStorage ? "uimageBuffer" : "utextureBuffer"; |
| outTexelBufferTypeStr = "uimageBuffer"; |
| } |
| else if (isIntFormat(bufferFormat)) |
| { |
| layoutTypeStr = "rgba32i"; |
| inTexelBufferTypeStr = readFromStorage ? "iimageBuffer" : "itextureBuffer"; |
| outTexelBufferTypeStr = "iimageBuffer"; |
| } |
| else if (bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) |
| { |
| layoutTypeStr = "rgb10_a2"; |
| inTexelBufferTypeStr = readFromStorage ? "imageBuffer" : "textureBuffer"; outTexelBufferTypeStr = "imageBuffer"; |
| } |
| else |
| { |
| TCU_THROW(NotSupportedError, (std::string("Unsupported format: ") + getFormatName(bufferFormat)).c_str()); |
| } |
| |
| bufferDefinition << "layout(set = 0, binding = 0" << ((readFromStorage) ? (std::string(", ") + layoutTypeStr) : "") << ") uniform highp " |
| << ((readFromStorage) ? "readonly " : "") << inTexelBufferTypeStr << " inImage;\n"; |
| |
| bufferDefinition << "layout(set = 0, binding = 1, " << layoutTypeStr << ") uniform highp writeonly " << outTexelBufferTypeStr << " outImage;\n"; |
| |
| bufferDefinition << |
| "layout(binding = 2, std140) uniform Offsets\n" |
| "{\n" |
| " int inOffset;\n" |
| " int outOffset;\n" |
| "};\n\n"; |
| |
| bufferUse << " for (int i = 0; i < " << (s_numberOfBytesAccessed / texelSize) << "; i++)\n" |
| << " {\n" |
| << " imageStore(outImage, outOffset + i, " << (readFromStorage ? "imageLoad" : "texelFetch") << "(inImage, inOffset + i));\n" |
| << " }\n"; |
| } |
| |
| void RobustBufferAccessTest::initBufferAccessPrograms (SourceCollections& programCollection, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat, |
| bool readFromStorage) |
| { |
| std::ostringstream bufferDefinition; |
| std::ostringstream bufferUse; |
| std::string extensions; |
| |
| if (bufferFormat == vk::VK_FORMAT_R64_UINT || bufferFormat == vk::VK_FORMAT_R64_SINT) |
| { |
| extensions = "#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require\n"; |
| } |
| |
| if (shaderType != SHADER_TYPE_TEXEL_COPY) |
| { |
| genBufferShaderAccess(shaderType, bufferFormat, readFromStorage, bufferDefinition, bufferUse); |
| } |
| |
| if (shaderStage == VK_SHADER_STAGE_COMPUTE_BIT) |
| { |
| std::ostringstream computeShaderSource; |
| |
| if (shaderType == SHADER_TYPE_TEXEL_COPY) |
| genTexelBufferShaderAccess(bufferFormat, bufferDefinition, bufferUse, readFromStorage); |
| |
| computeShaderSource << |
| "#version 440\n" |
| "#extension GL_EXT_texture_buffer : require\n" |
| << extensions << |
| "precision highp float;\n" |
| "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" |
| << bufferDefinition.str() << |
| "void main (void)\n" |
| "{\n" |
| << bufferUse.str() << |
| "}\n"; |
| |
| programCollection.glslSources.add("compute") << glu::ComputeSource(computeShaderSource.str()); |
| } |
| else |
| { |
| std::ostringstream vertexShaderSource; |
| std::ostringstream fragmentShaderSource; |
| |
| if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT) |
| { |
| if (shaderType == SHADER_TYPE_TEXEL_COPY) |
| genTexelBufferShaderAccess(bufferFormat, bufferDefinition, bufferUse, readFromStorage); |
| |
| vertexShaderSource << |
| "#version 440\n" |
| "#extension GL_EXT_texture_buffer : require\n" |
| << extensions << |
| "precision highp float;\n" |
| "layout(location = 0) in vec4 position;\n\n" |
| << bufferDefinition.str() << "\n" |
| "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n\n" |
| "void main (void)\n" |
| "{\n" |
| << bufferUse.str() << |
| " gl_Position = position;\n" |
| "}\n"; |
| } |
| else |
| { |
| vertexShaderSource << |
| "#version 440\n" |
| "precision highp float;\n" |
| "layout(location = 0) in vec4 position;\n\n" |
| "out gl_PerVertex {\n" |
| " vec4 gl_Position;\n" |
| "};\n\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = position;\n" |
| "}\n"; |
| } |
| |
| programCollection.glslSources.add("vertex") << glu::VertexSource(vertexShaderSource.str()); |
| |
| if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) |
| { |
| if (shaderType == SHADER_TYPE_TEXEL_COPY) |
| genTexelBufferShaderAccess(bufferFormat, bufferDefinition, bufferUse, readFromStorage); |
| |
| fragmentShaderSource << |
| "#version 440\n" |
| "#extension GL_EXT_texture_buffer : require\n" |
| << extensions << |
| "precision highp float;\n" |
| "layout(location = 0) out vec4 fragColor;\n" |
| << bufferDefinition.str() << |
| "void main (void)\n" |
| "{\n" |
| << bufferUse.str() << |
| " fragColor = vec4(1.0);\n" |
| "}\n"; |
| } |
| else |
| { |
| fragmentShaderSource << |
| "#version 440\n" |
| "precision highp float;\n" |
| "layout(location = 0) out vec4 fragColor;\n\n" |
| "void main (void)\n" |
| "{\n" |
| " fragColor = vec4(1.0);\n" |
| "}\n"; |
| } |
| |
| programCollection.glslSources.add("fragment") << glu::FragmentSource(fragmentShaderSource.str()); |
| } |
| } |
| |
| // RobustBufferReadTest |
| |
| RobustBufferReadTest::RobustBufferReadTest (tcu::TestContext& testContext, |
| const std::string& name, |
| const std::string& description, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat, |
| VkDeviceSize readAccessRange, |
| bool readFromStorage, |
| bool accessOutOfBackingMemory) |
| : RobustBufferAccessTest (testContext, name, description, shaderStage, shaderType, bufferFormat) |
| , m_readFromStorage (readFromStorage) |
| , m_readAccessRange (readAccessRange) |
| , m_accessOutOfBackingMemory (accessOutOfBackingMemory) |
| { |
| } |
| |
| void RobustBufferReadTest::initPrograms (SourceCollections& programCollection) const |
| { |
| initBufferAccessPrograms(programCollection, m_shaderStage, m_shaderType, m_bufferFormat, m_readFromStorage); |
| } |
| |
| TestInstance* RobustBufferReadTest::createInstance (Context& context) const |
| { |
| Move<VkDevice> device = createRobustBufferAccessDevice(context); |
| |
| return new BufferReadInstance(context, device, m_shaderType, m_shaderStage, m_bufferFormat, m_readFromStorage, m_readAccessRange, m_accessOutOfBackingMemory); |
| } |
| |
| // RobustBufferWriteTest |
| |
| RobustBufferWriteTest::RobustBufferWriteTest (tcu::TestContext& testContext, |
| const std::string& name, |
| const std::string& description, |
| VkShaderStageFlags shaderStage, |
| ShaderType shaderType, |
| VkFormat bufferFormat, |
| VkDeviceSize writeAccessRange, |
| bool accessOutOfBackingMemory) |
| |
| : RobustBufferAccessTest (testContext, name, description, shaderStage, shaderType, bufferFormat) |
| , m_writeAccessRange (writeAccessRange) |
| , m_accessOutOfBackingMemory (accessOutOfBackingMemory) |
| { |
| } |
| |
| void RobustBufferWriteTest::initPrograms (SourceCollections& programCollection) const |
| { |
| initBufferAccessPrograms(programCollection, m_shaderStage, m_shaderType, m_bufferFormat, false /* readFromStorage */); |
| } |
| |
| TestInstance* RobustBufferWriteTest::createInstance (Context& context) const |
| { |
| Move<VkDevice> device = createRobustBufferAccessDevice(context); |
| |
| return new BufferWriteInstance(context, device, m_shaderType, m_shaderStage, m_bufferFormat, m_writeAccessRange, m_accessOutOfBackingMemory); |
| } |
| |
| // BufferAccessInstance |
| |
| BufferAccessInstance::BufferAccessInstance (Context& context, |
| Move<VkDevice> device, |
| ShaderType shaderType, |
| VkShaderStageFlags shaderStage, |
| VkFormat bufferFormat, |
| BufferAccessType bufferAccessType, |
| VkDeviceSize inBufferAccessRange, |
| VkDeviceSize outBufferAccessRange, |
| bool accessOutOfBackingMemory) |
| : vkt::TestInstance (context) |
| , m_device (device) |
| , m_shaderType (shaderType) |
| , m_shaderStage (shaderStage) |
| , m_bufferFormat (bufferFormat) |
| , m_bufferAccessType (bufferAccessType) |
| , m_inBufferAccessRange (inBufferAccessRange) |
| , m_outBufferAccessRange (outBufferAccessRange) |
| , m_accessOutOfBackingMemory (accessOutOfBackingMemory) |
| { |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| const bool isTexelAccess = !!(m_shaderType == SHADER_TYPE_TEXEL_COPY); |
| const bool readFromStorage = !!(m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE); |
| SimpleAllocator memAlloc (vk, *m_device, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice())); |
| tcu::TestLog& log = m_context.getTestContext().getLog(); |
| |
| DE_ASSERT(RobustBufferAccessTest::s_numberOfBytesAccessed % sizeof(deUint32) == 0); |
| DE_ASSERT(inBufferAccessRange <= RobustBufferAccessTest::s_numberOfBytesAccessed); |
| DE_ASSERT(outBufferAccessRange <= RobustBufferAccessTest::s_numberOfBytesAccessed); |
| |
| if (m_bufferFormat == VK_FORMAT_R64_UINT || m_bufferFormat == VK_FORMAT_R64_SINT) |
| { |
| context.requireDeviceFunctionality("VK_EXT_shader_image_atomic_int64"); |
| } |
| |
| // Check storage support |
| if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT) |
| { |
| if (!context.getDeviceFeatures().vertexPipelineStoresAndAtomics) |
| { |
| TCU_THROW(NotSupportedError, "Stores not supported in vertex stage"); |
| } |
| } |
| else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) |
| { |
| if (!context.getDeviceFeatures().fragmentStoresAndAtomics) |
| { |
| TCU_THROW(NotSupportedError, "Stores not supported in fragment stage"); |
| } |
| } |
| |
| // Check format support |
| { |
| VkFormatFeatureFlags requiredFormatFeatures = 0; |
| const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(context.getInstanceInterface(), context.getPhysicalDevice(), m_bufferFormat); |
| |
| if (isTexelAccess) |
| { |
| requiredFormatFeatures = VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT; |
| } |
| |
| if ((formatProperties.bufferFeatures & requiredFormatFeatures) != requiredFormatFeatures) |
| { |
| TCU_THROW(NotSupportedError, (std::string("Format cannot be used in uniform and storage") + (isTexelAccess ? " texel" : "") + " buffers: " |
| + getFormatName(m_bufferFormat)).c_str()); |
| } |
| } |
| |
| // Create buffer to read data from |
| { |
| VkBufferUsageFlags inBufferUsageFlags; |
| VkMemoryRequirements inBufferMemoryReqs; |
| |
| if (isTexelAccess) |
| { |
| inBufferUsageFlags = readFromStorage ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; |
| } |
| else |
| { |
| inBufferUsageFlags = readFromStorage ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT : VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| } |
| |
| const VkBufferCreateInfo inBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| m_inBufferAccessRange, // VkDeviceSize size; |
| inBufferUsageFlags, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount; |
| DE_NULL // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_inBuffer = createBuffer(vk, *m_device, &inBufferParams); |
| |
| inBufferMemoryReqs = getBufferMemoryRequirements(vk, *m_device, *m_inBuffer); |
| m_inBufferAllocSize = inBufferMemoryReqs.size; |
| m_inBufferAlloc = memAlloc.allocate(inBufferMemoryReqs, MemoryRequirement::HostVisible); |
| |
| // Size of the most restrictive bound |
| m_inBufferMaxAccessRange = min(m_inBufferAllocSize, min(inBufferParams.size, m_inBufferAccessRange)); |
| |
| VK_CHECK(vk.bindBufferMemory(*m_device, *m_inBuffer, m_inBufferAlloc->getMemory(), m_inBufferAlloc->getOffset())); |
| populateBufferWithTestValues(m_inBufferAlloc->getHostPtr(), m_inBufferAllocSize, m_bufferFormat); |
| flushMappedMemoryRange(vk, *m_device, m_inBufferAlloc->getMemory(), m_inBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| |
| log << tcu::TestLog::Message << "inBufferAllocSize = " << m_inBufferAllocSize << tcu::TestLog::EndMessage; |
| log << tcu::TestLog::Message << "inBufferMaxAccessRange = " << m_inBufferMaxAccessRange << tcu::TestLog::EndMessage; |
| } |
| |
| // Create buffer to write data into |
| { |
| VkMemoryRequirements outBufferMemoryReqs; |
| const VkBufferUsageFlags outBufferUsageFlags = (m_shaderType == SHADER_TYPE_TEXEL_COPY) ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
| : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; |
| |
| const VkBufferCreateInfo outBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| m_outBufferAccessRange, // VkDeviceSize size; |
| outBufferUsageFlags, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount; |
| DE_NULL // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_outBuffer = createBuffer(vk, *m_device, &outBufferParams); |
| |
| outBufferMemoryReqs = getBufferMemoryRequirements(vk, *m_device, *m_outBuffer); |
| m_outBufferAllocSize = outBufferMemoryReqs.size; |
| m_outBufferAlloc = memAlloc.allocate(outBufferMemoryReqs, MemoryRequirement::HostVisible); |
| |
| // If we are requesting access out of the memory that backs the buffer, make sure the test is able to do so. |
| if (m_accessOutOfBackingMemory) |
| { |
| if (m_outBufferAllocSize >= ((RobustBufferAccessTest::s_testArraySize + 1) * RobustBufferAccessTest::s_numberOfBytesAccessed)) |
| { |
| TCU_THROW(NotSupportedError, "Cannot access beyond the end of the memory that backs the buffer"); |
| } |
| } |
| |
| // Size of the most restrictive bound |
| m_outBufferMaxAccessRange = min(m_outBufferAllocSize, min(outBufferParams.size, m_outBufferAccessRange)); |
| |
| VK_CHECK(vk.bindBufferMemory(*m_device, *m_outBuffer, m_outBufferAlloc->getMemory(), m_outBufferAlloc->getOffset())); |
| deMemset(m_outBufferAlloc->getHostPtr(), 0xFF, (size_t)m_outBufferAllocSize); |
| flushMappedMemoryRange(vk, *m_device, m_outBufferAlloc->getMemory(), m_outBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| |
| log << tcu::TestLog::Message << "outBufferAllocSize = " << m_outBufferAllocSize << tcu::TestLog::EndMessage; |
| log << tcu::TestLog::Message << "outBufferMaxAccessRange = " << m_outBufferMaxAccessRange << tcu::TestLog::EndMessage; |
| } |
| |
| // Create buffer for indices/offsets |
| { |
| struct IndicesBuffer |
| { |
| deInt32 inIndex; |
| deInt32 outIndex; |
| }; |
| |
| IndicesBuffer indices = { 0, 0 }; |
| |
| const VkBufferCreateInfo indicesBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| sizeof(IndicesBuffer), // VkDeviceSize size; |
| VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount; |
| DE_NULL, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_indicesBuffer = createBuffer(vk, *m_device, &indicesBufferParams); |
| m_indicesBufferAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, *m_device, *m_indicesBuffer), MemoryRequirement::HostVisible); |
| |
| VK_CHECK(vk.bindBufferMemory(*m_device, *m_indicesBuffer, m_indicesBufferAlloc->getMemory(), m_indicesBufferAlloc->getOffset())); |
| |
| if (m_accessOutOfBackingMemory) |
| { |
| if (m_bufferAccessType == BUFFER_ACCESS_TYPE_WRITE) |
| { |
| indices.outIndex = RobustBufferAccessTest::s_testArraySize - 1; |
| } |
| else |
| { |
| indices.inIndex = RobustBufferAccessTest::s_testArraySize - 1; |
| } |
| } |
| |
| deMemcpy(m_indicesBufferAlloc->getHostPtr(), &indices, sizeof(IndicesBuffer)); |
| |
| flushMappedMemoryRange(vk, *m_device, m_indicesBufferAlloc->getMemory(), m_indicesBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| |
| log << tcu::TestLog::Message << "inIndex = " << indices.inIndex << tcu::TestLog::EndMessage; |
| log << tcu::TestLog::Message << "outIndex = " << indices.outIndex << tcu::TestLog::EndMessage; |
| } |
| |
| // Create descriptor data |
| { |
| VkDescriptorType inBufferDescriptorType; |
| VkDescriptorType outBufferDescriptorType; |
| |
| if (isTexelAccess) |
| { |
| inBufferDescriptorType = readFromStorage ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; |
| outBufferDescriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; |
| } |
| else |
| { |
| inBufferDescriptorType = readFromStorage ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| outBufferDescriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
| } |
| |
| DescriptorPoolBuilder descriptorPoolBuilder; |
| descriptorPoolBuilder.addType(inBufferDescriptorType, 1u); |
| descriptorPoolBuilder.addType(outBufferDescriptorType, 1u); |
| descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u); |
| m_descriptorPool = descriptorPoolBuilder.build(vk, *m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); |
| |
| DescriptorSetLayoutBuilder setLayoutBuilder; |
| setLayoutBuilder.addSingleBinding(inBufferDescriptorType, VK_SHADER_STAGE_ALL); |
| setLayoutBuilder.addSingleBinding(outBufferDescriptorType, VK_SHADER_STAGE_ALL); |
| setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_ALL); |
| 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 descriptorPool; |
| 1u, // deUint32 setLayoutCount; |
| &m_descriptorSetLayout.get() // const VkDescriptorSetLayout* pSetLayouts; |
| }; |
| |
| m_descriptorSet = allocateDescriptorSet(vk, *m_device, &descriptorSetAllocateInfo); |
| |
| DescriptorSetUpdateBuilder setUpdateBuilder; |
| |
| if (isTexelAccess) |
| { |
| const VkBufferViewCreateInfo inBufferViewCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferViewCreateFlags flags; |
| *m_inBuffer, // VkBuffer buffer; |
| m_bufferFormat, // VkFormat format; |
| 0ull, // VkDeviceSize offset; |
| m_inBufferAccessRange // VkDeviceSize range; |
| }; |
| m_inTexelBufferView = createBufferView(vk, *m_device, &inBufferViewCreateInfo, DE_NULL); |
| |
| const VkBufferViewCreateInfo outBufferViewCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferViewCreateFlags flags; |
| *m_outBuffer, // VkBuffer buffer; |
| m_bufferFormat, // VkFormat format; |
| 0ull, // VkDeviceSize offset; |
| m_outBufferAccessRange, // VkDeviceSize range; |
| }; |
| m_outTexelBufferView = createBufferView(vk, *m_device, &outBufferViewCreateInfo, DE_NULL); |
| |
| setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0), inBufferDescriptorType, &m_inTexelBufferView.get()); |
| setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1), outBufferDescriptorType, &m_outTexelBufferView.get()); |
| } |
| else |
| { |
| const VkDescriptorBufferInfo inBufferDescriptorInfo = makeDescriptorBufferInfo(*m_inBuffer, 0ull, m_inBufferAccessRange); |
| const VkDescriptorBufferInfo outBufferDescriptorInfo = makeDescriptorBufferInfo(*m_outBuffer, 0ull, m_outBufferAccessRange); |
| |
| setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0), inBufferDescriptorType, &inBufferDescriptorInfo); |
| setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1), outBufferDescriptorType, &outBufferDescriptorInfo); |
| } |
| |
| const VkDescriptorBufferInfo indicesBufferDescriptorInfo = makeDescriptorBufferInfo(*m_indicesBuffer, 0ull, 8ull); |
| setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &indicesBufferDescriptorInfo); |
| |
| 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); |
| |
| if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT) |
| { |
| m_testEnvironment = de::MovePtr<TestEnvironment>(new ComputeEnvironment(m_context, *m_device, *m_descriptorSetLayout, *m_descriptorSet)); |
| } |
| else |
| { |
| using tcu::Vec4; |
| |
| const VkVertexInputBindingDescription vertexInputBindingDescription = |
| { |
| 0u, // deUint32 binding; |
| sizeof(tcu::Vec4), // deUint32 strideInBytes; |
| VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate; |
| }; |
| |
| const VkVertexInputAttributeDescription vertexInputAttributeDescription = |
| { |
| 0u, // deUint32 location; |
| 0u, // deUint32 binding; |
| VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format; |
| 0u // deUint32 offset; |
| }; |
| |
| const Vec4 vertices[] = |
| { |
| Vec4(-1.0f, -1.0f, 0.0f, 1.0f), |
| Vec4(-1.0f, 1.0f, 0.0f, 1.0f), |
| Vec4(1.0f, -1.0f, 0.0f, 1.0f), |
| }; |
| |
| // Create vertex buffer |
| { |
| const VkDeviceSize vertexBufferSize = (VkDeviceSize)(4u * sizeof(tcu::Vec4)); |
| const VkBufferCreateInfo vertexBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| vertexBufferSize, // VkDeviceSize size; |
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount; |
| DE_NULL // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| DE_ASSERT(vertexBufferSize > 0); |
| |
| m_vertexBuffer = createBuffer(vk, *m_device, &vertexBufferParams); |
| m_vertexBufferAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, *m_device, *m_vertexBuffer), MemoryRequirement::HostVisible); |
| |
| VK_CHECK(vk.bindBufferMemory(*m_device, *m_vertexBuffer, m_vertexBufferAlloc->getMemory(), m_vertexBufferAlloc->getOffset())); |
| |
| // Load vertices into vertex buffer |
| deMemcpy(m_vertexBufferAlloc->getHostPtr(), vertices, sizeof(tcu::Vec4) * DE_LENGTH_OF_ARRAY(vertices)); |
| flushMappedMemoryRange(vk, *m_device, m_vertexBufferAlloc->getMemory(), m_vertexBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| } |
| |
| const GraphicsEnvironment::DrawConfig drawWithOneVertexBuffer = |
| { |
| std::vector<VkBuffer>(1, *m_vertexBuffer), // std::vector<VkBuffer> vertexBuffers; |
| DE_LENGTH_OF_ARRAY(vertices), // deUint32 vertexCount; |
| 1, // deUint32 instanceCount; |
| DE_NULL, // VkBuffer indexBuffer; |
| 0u, // deUint32 indexCount; |
| }; |
| |
| m_testEnvironment = de::MovePtr<TestEnvironment>(new GraphicsEnvironment(m_context, |
| *m_device, |
| *m_descriptorSetLayout, |
| *m_descriptorSet, |
| GraphicsEnvironment::VertexBindings(1, vertexInputBindingDescription), |
| GraphicsEnvironment::VertexAttributes(1, vertexInputAttributeDescription), |
| drawWithOneVertexBuffer)); |
| } |
| } |
| |
| // Verifies if the buffer has the value initialized by BufferAccessInstance::populateReadBuffer at a given offset. |
| bool BufferAccessInstance::isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes, const void* valuePtr, VkDeviceSize valueSize) |
| { |
| DE_ASSERT(offsetInBytes % 4 == 0); |
| DE_ASSERT(offsetInBytes < m_inBufferAllocSize); |
| |
| const deUint32 valueIndex = deUint32(offsetInBytes / 4) + 2; |
| |
| if (isUintFormat(m_bufferFormat)) |
| { |
| return !deMemCmp(valuePtr, &valueIndex, (size_t)valueSize); |
| } |
| else if (isIntFormat(m_bufferFormat)) |
| { |
| const deInt32 value = -deInt32(valueIndex); |
| return !deMemCmp(valuePtr, &value, (size_t)valueSize); |
| } |
| else if (isFloatFormat(m_bufferFormat)) |
| { |
| const float value = float(valueIndex); |
| return !deMemCmp(valuePtr, &value, (size_t)valueSize); |
| } |
| else if (m_bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) |
| { |
| const deUint32 r = ((valueIndex + 0) & ((2u << 10) - 1u)); |
| const deUint32 g = ((valueIndex + 1) & ((2u << 10) - 1u)); |
| const deUint32 b = ((valueIndex + 2) & ((2u << 10) - 1u)); |
| const deUint32 a = ((valueIndex + 0) & ((2u << 2) - 1u)); |
| const deUint32 abgr = (a << 30) | (b << 20) | (g << 10) | r; |
| |
| return !deMemCmp(valuePtr, &abgr, (size_t)valueSize); |
| } |
| else |
| { |
| DE_ASSERT(false); |
| return false; |
| } |
| } |
| |
| bool BufferAccessInstance::isOutBufferValueUnchanged (VkDeviceSize offsetInBytes, VkDeviceSize valueSize) |
| { |
| const deUint8 *const outValuePtr = (deUint8*)m_outBufferAlloc->getHostPtr() + offsetInBytes; |
| const deUint32 defaultValue = 0xFFFFFFFFu; |
| |
| return !deMemCmp(outValuePtr, &defaultValue, (size_t)valueSize); |
| } |
| |
| tcu::TestStatus BufferAccessInstance::iterate (void) |
| { |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const vk::VkCommandBuffer cmdBuffer = m_testEnvironment->getCommandBuffer(); |
| |
| // 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_outBufferAllocSize, // 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 BufferAccessInstance::verifyResult (void) |
| { |
| std::ostringstream logMsg; |
| tcu::TestLog& log = m_context.getTestContext().getLog(); |
| const bool isReadAccess = !!(m_bufferAccessType == BUFFER_ACCESS_TYPE_READ || m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE); |
| const void* inDataPtr = m_inBufferAlloc->getHostPtr(); |
| const void* outDataPtr = m_outBufferAlloc->getHostPtr(); |
| bool allOk = true; |
| deUint32 valueNdx = 0; |
| const VkDeviceSize maxAccessRange = isReadAccess ? m_inBufferMaxAccessRange : m_outBufferMaxAccessRange; |
| |
| for (VkDeviceSize offsetInBytes = 0; offsetInBytes < m_outBufferAllocSize; offsetInBytes += 4) |
| { |
| deUint8* outValuePtr = (deUint8*)outDataPtr + offsetInBytes; |
| const size_t outValueSize = (size_t)min(4, (m_outBufferAllocSize - offsetInBytes)); |
| |
| if (offsetInBytes >= RobustBufferAccessTest::s_numberOfBytesAccessed) |
| { |
| // The shader will only write 16 values into the result buffer. The rest of the values |
| // should remain unchanged or may be modified if we are writing out of bounds. |
| if (!isOutBufferValueUnchanged(offsetInBytes, outValueSize) |
| && (isReadAccess || !isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, 4))) |
| { |
| logMsg << "\nValue " << valueNdx++ << " has been modified with an unknown value: " << *((deUint32 *)outValuePtr); |
| allOk = false; |
| } |
| } |
| else |
| { |
| const deInt32 distanceToOutOfBounds = (deInt32)maxAccessRange - (deInt32)offsetInBytes; |
| bool isOutOfBoundsAccess = false; |
| |
| logMsg << "\n" << valueNdx++ << ": "; |
| |
| logValue(logMsg, outValuePtr, m_bufferFormat, outValueSize); |
| |
| if (m_accessOutOfBackingMemory) |
| { |
| isOutOfBoundsAccess = true; |
| } |
| else |
| { |
| // Check if the shader operation accessed an operand located less than 16 bytes away |
| // from the out of bounds address. |
| |
| deUint32 operandSize = 0; |
| |
| switch (m_shaderType) |
| { |
| case SHADER_TYPE_SCALAR_COPY: |
| operandSize = 4; // Size of scalar |
| break; |
| |
| case SHADER_TYPE_VECTOR_COPY: |
| operandSize = 4 * ((m_bufferFormat == vk::VK_FORMAT_R64_UINT || m_bufferFormat == vk::VK_FORMAT_R64_SINT) ? 8 : 4);// Size of vec4 |
| break; |
| |
| case SHADER_TYPE_MATRIX_COPY: |
| operandSize = 4 * 16; // Size of mat4 |
| break; |
| |
| case SHADER_TYPE_TEXEL_COPY: |
| operandSize = mapVkFormat(m_bufferFormat).getPixelSize(); |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| isOutOfBoundsAccess = (maxAccessRange < 16) |
| || (((offsetInBytes / operandSize + 1) * operandSize) > (maxAccessRange - 16)); |
| } |
| |
| if (isOutOfBoundsAccess) |
| { |
| logMsg << " (out of bounds " << (isReadAccess ? "read": "write") << ")"; |
| |
| const bool isValuePartiallyOutOfBounds = ((distanceToOutOfBounds > 0) && ((deUint32)distanceToOutOfBounds < 4)); |
| bool isValidValue = false; |
| |
| if (isValuePartiallyOutOfBounds && !m_accessOutOfBackingMemory) |
| { |
| // The value is partially out of bounds |
| |
| bool isOutOfBoundsPartOk = true; |
| bool isWithinBoundsPartOk = true; |
| |
| if (isReadAccess) |
| { |
| isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, distanceToOutOfBounds); |
| isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, (deUint8*)outValuePtr + distanceToOutOfBounds , outValueSize - distanceToOutOfBounds); |
| } |
| else |
| { |
| isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, distanceToOutOfBounds) |
| || isOutBufferValueUnchanged(offsetInBytes, distanceToOutOfBounds); |
| |
| isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, (deUint8*)outValuePtr + distanceToOutOfBounds, outValueSize - distanceToOutOfBounds) |
| || isOutBufferValueUnchanged(offsetInBytes + distanceToOutOfBounds, outValueSize - distanceToOutOfBounds); |
| } |
| |
| logMsg << ", first " << distanceToOutOfBounds << " byte(s) " << (isWithinBoundsPartOk ? "OK": "wrong"); |
| logMsg << ", last " << outValueSize - distanceToOutOfBounds << " byte(s) " << (isOutOfBoundsPartOk ? "OK": "wrong"); |
| |
| isValidValue = isWithinBoundsPartOk && isOutOfBoundsPartOk; |
| } |
| else |
| { |
| if (isReadAccess) |
| { |
| isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, outValueSize); |
| } |
| else |
| { |
| isValidValue = isOutBufferValueUnchanged(offsetInBytes, outValueSize); |
| |
| if (!isValidValue) |
| { |
| // Out of bounds writes may modify values withing the memory ranges bound to the buffer |
| isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, outValueSize); |
| |
| if (isValidValue) |
| logMsg << ", OK, written within the memory range bound to the buffer"; |
| } |
| } |
| } |
| |
| 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 = (isReadAccess |
| && !isValuePartiallyOutOfBounds |
| && (m_shaderType == SHADER_TYPE_VECTOR_COPY || m_shaderType == SHADER_TYPE_TEXEL_COPY) |
| && ((offsetInBytes / 4 + 1) % 4 == 0 || m_bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32)); |
| bool matchesVec4Pattern = false; |
| |
| if (canMatchVec4Pattern) |
| { |
| if (m_bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32) |
| matchesVec4Pattern = verifyOutOfBoundsVec4(outValuePtr, m_bufferFormat); |
| else |
| matchesVec4Pattern = verifyOutOfBoundsVec4(reinterpret_cast<deUint32*>(outValuePtr) - 3, m_bufferFormat); |
| } |
| |
| if (!canMatchVec4Pattern || !matchesVec4Pattern) |
| { |
| logMsg << ". Failed: "; |
| |
| if (isReadAccess) |
| { |
| logMsg << "expected value within the buffer range or 0"; |
| |
| if (canMatchVec4Pattern) |
| logMsg << ", or the [0, 0, 0, x] pattern"; |
| } |
| else |
| { |
| logMsg << "written out of the range"; |
| } |
| |
| allOk = false; |
| } |
| } |
| } |
| else // We are within bounds |
| { |
| if (isReadAccess) |
| { |
| if (!isExpectedValueFromInBuffer(offsetInBytes, outValuePtr, 4)) |
| { |
| logMsg << ", Failed: unexpected value"; |
| allOk = false; |
| } |
| } |
| else |
| { |
| // Out of bounds writes may change values within the bounds. |
| if (!isValueWithinBufferOrZero(inDataPtr, m_inBufferAccessRange, outValuePtr, 4)) |
| { |
| logMsg << ", Failed: unexpected value"; |
| allOk = false; |
| } |
| } |
| } |
| } |
| } |
| |
| log << tcu::TestLog::Message << logMsg.str() << tcu::TestLog::EndMessage; |
| |
| return allOk; |
| } |
| |
| // BufferReadInstance |
| |
| BufferReadInstance::BufferReadInstance (Context& context, |
| Move<VkDevice> device, |
| ShaderType shaderType, |
| VkShaderStageFlags shaderStage, |
| VkFormat bufferFormat, |
| bool readFromStorage, |
| VkDeviceSize inBufferAccessRange, |
| bool accessOutOfBackingMemory) |
| |
| : BufferAccessInstance (context, device, shaderType, shaderStage, bufferFormat, |
| readFromStorage ? BUFFER_ACCESS_TYPE_READ_FROM_STORAGE : BUFFER_ACCESS_TYPE_READ, |
| inBufferAccessRange, |
| RobustBufferAccessTest::s_numberOfBytesAccessed, // outBufferAccessRange |
| accessOutOfBackingMemory) |
| { |
| } |
| |
| // BufferWriteInstance |
| |
| BufferWriteInstance::BufferWriteInstance (Context& context, |
| Move<VkDevice> device, |
| ShaderType shaderType, |
| VkShaderStageFlags shaderStage, |
| VkFormat bufferFormat, |
| VkDeviceSize writeBufferAccessRange, |
| bool accessOutOfBackingMemory) |
| |
| : BufferAccessInstance (context, device, shaderType, shaderStage, bufferFormat, |
| BUFFER_ACCESS_TYPE_WRITE, |
| RobustBufferAccessTest::s_numberOfBytesAccessed, // inBufferAccessRange |
| writeBufferAccessRange, |
| accessOutOfBackingMemory) |
| { |
| } |
| |
| // Test node creation functions |
| |
| static const char* getShaderStageName (VkShaderStageFlagBits shaderStage) |
| { |
| switch (shaderStage) |
| { |
| case VK_SHADER_STAGE_VERTEX_BIT: return "vertex"; |
| case VK_SHADER_STAGE_FRAGMENT_BIT: return "fragment"; |
| case VK_SHADER_STAGE_COMPUTE_BIT: return "compute"; |
| case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return "tess_control"; |
| case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return "tess_eval"; |
| case VK_SHADER_STAGE_GEOMETRY_BIT: return "geometry"; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| return DE_NULL; |
| } |
| |
| static void addBufferAccessTests (tcu::TestContext& testCtx, tcu::TestCaseGroup* parentNode) |
| { |
| struct BufferRangeConfig |
| { |
| const char* name; |
| VkDeviceSize range; |
| }; |
| |
| const VkShaderStageFlagBits bufferAccessStages[] = |
| { |
| VK_SHADER_STAGE_VERTEX_BIT, |
| VK_SHADER_STAGE_FRAGMENT_BIT, |
| VK_SHADER_STAGE_COMPUTE_BIT, |
| }; |
| |
| const VkFormat bufferFormats[] = |
| { |
| VK_FORMAT_R32_SINT, |
| VK_FORMAT_R32_UINT, |
| VK_FORMAT_R64_SINT, |
| VK_FORMAT_R64_UINT, |
| VK_FORMAT_R32_SFLOAT |
| }; |
| |
| const VkFormat texelBufferFormats[] = |
| { |
| VK_FORMAT_R32G32B32A32_SINT, |
| VK_FORMAT_R32G32B32A32_UINT, |
| VK_FORMAT_R32G32B32A32_SFLOAT, |
| |
| VK_FORMAT_A2B10G10R10_UNORM_PACK32 |
| }; |
| |
| const BufferRangeConfig bufferRangeConfigs[] = |
| { |
| { "range_1_byte", 1ull }, |
| { "range_3_bytes", 3ull }, |
| { "range_4_bytes", 4ull }, // size of float |
| { "range_32_bytes", 32ull }, // size of half mat4 |
| }; |
| |
| const BufferRangeConfig texelBufferRangeConfigs[] = |
| { |
| { "range_1_texel", 1u }, |
| { "range_3_texels", 3u }, |
| }; |
| |
| const char* shaderTypeNames[SHADER_TYPE_COUNT] = |
| { |
| "mat4_copy", |
| "vec4_copy", |
| "scalar_copy", |
| "texel_copy", |
| }; |
| |
| for (int stageNdx = 0; stageNdx < DE_LENGTH_OF_ARRAY(bufferAccessStages); stageNdx++) |
| { |
| const VkShaderStageFlagBits stage = bufferAccessStages[stageNdx]; |
| de::MovePtr<tcu::TestCaseGroup> stageTests (new tcu::TestCaseGroup(testCtx, getShaderStageName(stage), "")); |
| |
| for (int shaderTypeNdx = 0; shaderTypeNdx < SHADER_TYPE_COUNT; shaderTypeNdx++) |
| { |
| const VkFormat* formats; |
| size_t formatsLength; |
| const BufferRangeConfig* ranges; |
| size_t rangesLength; |
| deUint32 rangeMultiplier; |
| de::MovePtr<tcu::TestCaseGroup> shaderTypeTests (new tcu::TestCaseGroup(testCtx, shaderTypeNames[shaderTypeNdx], "")); |
| |
| if ((ShaderType)shaderTypeNdx == SHADER_TYPE_TEXEL_COPY) |
| { |
| formats = texelBufferFormats; |
| formatsLength = DE_LENGTH_OF_ARRAY(texelBufferFormats); |
| |
| ranges = texelBufferRangeConfigs; |
| rangesLength = DE_LENGTH_OF_ARRAY(texelBufferRangeConfigs); |
| } |
| else |
| { |
| formats = bufferFormats; |
| formatsLength = DE_LENGTH_OF_ARRAY(bufferFormats); |
| |
| ranges = bufferRangeConfigs; |
| rangesLength = DE_LENGTH_OF_ARRAY(bufferRangeConfigs); |
| } |
| |
| for (size_t formatNdx = 0; formatNdx < formatsLength; formatNdx++) |
| { |
| const VkFormat bufferFormat = formats[formatNdx]; |
| |
| rangeMultiplier = ((ShaderType)shaderTypeNdx == SHADER_TYPE_TEXEL_COPY) ? mapVkFormat(bufferFormat).getPixelSize() : 1; |
| |
| if (!isFloatFormat(bufferFormat) && ((ShaderType)shaderTypeNdx) == SHADER_TYPE_MATRIX_COPY) |
| { |
| // Use SHADER_TYPE_MATRIX_COPY with floating-point formats only |
| break; |
| } |
| |
| const std::string formatName = getFormatName(bufferFormat); |
| de::MovePtr<tcu::TestCaseGroup> formatTests (new tcu::TestCaseGroup(testCtx, de::toLower(formatName.substr(10)).c_str(), "")); |
| |
| de::MovePtr<tcu::TestCaseGroup> uboReadTests (new tcu::TestCaseGroup(testCtx, "oob_uniform_read", "")); |
| de::MovePtr<tcu::TestCaseGroup> ssboReadTests (new tcu::TestCaseGroup(testCtx, "oob_storage_read", "")); |
| de::MovePtr<tcu::TestCaseGroup> ssboWriteTests (new tcu::TestCaseGroup(testCtx, "oob_storage_write", "")); |
| |
| for (size_t rangeNdx = 0; rangeNdx < rangesLength; rangeNdx++) |
| { |
| const BufferRangeConfig& rangeConfig = ranges[rangeNdx]; |
| const VkDeviceSize rangeInBytes = rangeConfig.range * rangeMultiplier; |
| |
| uboReadTests->addChild(new RobustBufferReadTest(testCtx, rangeConfig.name, "", stage, (ShaderType)shaderTypeNdx, bufferFormat, rangeInBytes, false, false)); |
| ssboReadTests->addChild(new RobustBufferReadTest(testCtx, rangeConfig.name, "", stage, (ShaderType)shaderTypeNdx, bufferFormat, rangeInBytes, true, false)); |
| ssboWriteTests->addChild(new RobustBufferWriteTest(testCtx, rangeConfig.name, "", stage, (ShaderType)shaderTypeNdx, bufferFormat, rangeInBytes, false)); |
| |
| } |
| |
| formatTests->addChild(uboReadTests.release()); |
| formatTests->addChild(ssboReadTests.release()); |
| formatTests->addChild(ssboWriteTests.release()); |
| |
| shaderTypeTests->addChild(formatTests.release()); |
| } |
| |
| // Read/write out of the memory that backs the buffer |
| { |
| de::MovePtr<tcu::TestCaseGroup> outOfAllocTests (new tcu::TestCaseGroup(testCtx, "out_of_alloc", "")); |
| |
| const VkFormat format = (((ShaderType)shaderTypeNdx == SHADER_TYPE_TEXEL_COPY ) ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R32_SFLOAT); |
| |
| outOfAllocTests->addChild(new RobustBufferReadTest(testCtx, "oob_uniform_read", "", stage, (ShaderType)shaderTypeNdx, format, 16, false, true)); |
| outOfAllocTests->addChild(new RobustBufferReadTest(testCtx, "oob_storage_read", "", stage, (ShaderType)shaderTypeNdx, format, 16, true, true)); |
| outOfAllocTests->addChild(new RobustBufferWriteTest(testCtx, "oob_storage_write", "", stage, (ShaderType)shaderTypeNdx, format, 16, true)); |
| |
| shaderTypeTests->addChild(outOfAllocTests.release()); |
| } |
| |
| stageTests->addChild(shaderTypeTests.release()); |
| } |
| parentNode->addChild(stageTests.release()); |
| } |
| } |
| |
| tcu::TestCaseGroup* createBufferAccessTests (tcu::TestContext& testCtx) |
| { |
| de::MovePtr<tcu::TestCaseGroup> bufferAccessTests (new tcu::TestCaseGroup(testCtx, "buffer_access", "")); |
| |
| addBufferAccessTests(testCtx, bufferAccessTests.get()); |
| |
| return bufferAccessTests.release(); |
| } |
| |
| } // robustness |
| } // vkt |