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