blob: 7efbc74c2bc5d088d90caa6a3922a69052a34ecc [file] [log] [blame]
/*------------------------------------------------------------------------
* 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