blob: f0df4a2d7f2b4fc1eb592af69c782b13fd4efd76 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2016 The Khronos Group Inc.
* Copyright (c) 2016 Samsung Electronics Co., 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 Instanced Draw Tests
*//*--------------------------------------------------------------------*/
#include "vktDrawInstancedTests.hpp"
#include <climits>
#include "deSharedPtr.hpp"
#include "rrRenderer.hpp"
#include "tcuImageCompare.hpp"
#include "tcuRGBA.hpp"
#include "tcuTextureUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkPrograms.hpp"
#include "vkCmdUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vktDrawBufferObjectUtil.hpp"
#include "vktDrawCreateInfoUtil.hpp"
#include "vktDrawImageObjectUtil.hpp"
#include "vktDrawTestCaseUtil.hpp"
namespace vkt
{
namespace Draw
{
namespace
{
static const int QUAD_GRID_SIZE = 8;
static const int WIDTH = 128;
static const int HEIGHT = 128;
struct TestParams
{
enum DrawFunction
{
FUNCTION_DRAW = 0,
FUNCTION_DRAW_INDEXED,
FUNCTION_DRAW_INDIRECT,
FUNCTION_DRAW_INDEXED_INDIRECT,
FUNTION_LAST
};
DrawFunction function;
vk::VkPrimitiveTopology topology;
deBool testAttribDivisor;
deUint32 attribDivisor;
deBool testMultiview;
};
struct VertexPositionAndColor
{
VertexPositionAndColor (tcu::Vec4 position_, tcu::Vec4 color_)
: position (position_)
, color (color_)
{
}
tcu::Vec4 position;
tcu::Vec4 color;
};
std::ostream & operator<<(std::ostream & str, TestParams const & v)
{
std::ostringstream string;
switch (v.function)
{
case TestParams::FUNCTION_DRAW:
string << "draw";
break;
case TestParams::FUNCTION_DRAW_INDEXED:
string << "draw_indexed";
break;
case TestParams::FUNCTION_DRAW_INDIRECT:
string << "draw_indirect";
break;
case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
string << "draw_indexed_indirect";
break;
default:
DE_ASSERT(false);
}
string << "_" << de::toString(v.topology);
if (v.testAttribDivisor)
string << "_attrib_divisor_" << v.attribDivisor;
if (v.testMultiview)
string << "_multiview";
return str << string.str();
}
rr::PrimitiveType mapVkPrimitiveTopology (vk::VkPrimitiveTopology primitiveTopology)
{
switch (primitiveTopology)
{
case vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST: return rr::PRIMITIVETYPE_POINTS;
case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST: return rr::PRIMITIVETYPE_LINES;
case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: return rr::PRIMITIVETYPE_LINE_STRIP;
case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: return rr::PRIMITIVETYPE_TRIANGLES;
case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: return rr::PRIMITIVETYPE_TRIANGLE_FAN;
case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: return rr::PRIMITIVETYPE_TRIANGLE_STRIP;
case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: return rr::PRIMITIVETYPE_LINES_ADJACENCY;
case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: return rr::PRIMITIVETYPE_LINE_STRIP_ADJACENCY;
case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: return rr::PRIMITIVETYPE_TRIANGLES_ADJACENCY;
case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: return rr::PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY;
default:
DE_ASSERT(false);
}
return rr::PRIMITIVETYPE_LAST;
}
template<typename T>
de::SharedPtr<Buffer> createAndUploadBuffer(const std::vector<T> data, const vk::DeviceInterface& vk, const Context& context, vk::VkBufferUsageFlags usage)
{
const vk::VkDeviceSize dataSize = data.size() * sizeof(T);
de::SharedPtr<Buffer> buffer = Buffer::createAndAlloc(vk, context.getDevice(),
BufferCreateInfo(dataSize, usage),
context.getDefaultAllocator(),
vk::MemoryRequirement::HostVisible);
deUint8* ptr = reinterpret_cast<deUint8*>(buffer->getBoundMemory().getHostPtr());
deMemcpy(ptr, &data[0], static_cast<size_t>(dataSize));
vk::flushAlloc(vk, context.getDevice(), buffer->getBoundMemory());
return buffer;
}
class TestVertShader : public rr::VertexShader
{
public:
TestVertShader (int numInstances, int firstInstance)
: rr::VertexShader (3, 1)
, m_numInstances (numInstances)
, m_firstInstance (firstInstance)
{
m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
m_inputs[1].type = rr::GENERICVECTYPE_FLOAT;
m_inputs[2].type = rr::GENERICVECTYPE_FLOAT;
m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
}
void shadeVertices (const rr::VertexAttrib* inputs,
rr::VertexPacket* const* packets,
const int numPackets) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
{
const int instanceNdx = packets[packetNdx]->instanceNdx + m_firstInstance;
const tcu::Vec4 position = rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx, m_firstInstance);
const tcu::Vec4 color = rr::readVertexAttribFloat(inputs[1], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx, m_firstInstance);
const tcu::Vec4 color2 = rr::readVertexAttribFloat(inputs[2], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx, m_firstInstance);
packets[packetNdx]->position = position + tcu::Vec4((float)(packets[packetNdx]->instanceNdx * 2.0 / m_numInstances), 0.0, 0.0, 0.0);
packets[packetNdx]->outputs[0] = color + tcu::Vec4((float)instanceNdx / (float)m_numInstances, 0.0, 0.0, 1.0) + color2;
}
}
private:
const int m_numInstances;
const int m_firstInstance;
};
class TestFragShader : public rr::FragmentShader
{
public:
TestFragShader (void)
: rr::FragmentShader(1, 1)
{
m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
}
void shadeFragments (rr::FragmentPacket* packets,
const int numPackets,
const rr::FragmentShadingContext& context) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
{
rr::FragmentPacket& packet = packets[packetNdx];
for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx)
{
const tcu::Vec4 color = rr::readVarying<float>(packet, context, 0, fragNdx);
rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
}
}
}
};
class InstancedDrawInstance : public TestInstance
{
public:
InstancedDrawInstance (Context& context, TestParams params);
virtual tcu::TestStatus iterate (void);
private:
void prepareVertexData (int instanceCount, int firstInstance, int instanceDivisor);
const TestParams m_params;
const vk::DeviceInterface& m_vk;
vk::VkFormat m_colorAttachmentFormat;
vk::Move<vk::VkPipeline> m_pipeline;
vk::Move<vk::VkPipelineLayout> m_pipelineLayout;
de::SharedPtr<Image> m_colorTargetImage;
vk::Move<vk::VkImageView> m_colorTargetView;
PipelineCreateInfo::VertexInputState m_vertexInputState;
vk::Move<vk::VkCommandPool> m_cmdPool;
vk::Move<vk::VkCommandBuffer> m_cmdBuffer;
vk::Move<vk::VkFramebuffer> m_framebuffer;
vk::Move<vk::VkRenderPass> m_renderPass;
// Vertex data
std::vector<VertexPositionAndColor> m_data;
std::vector<deUint32> m_indexes;
std::vector<tcu::Vec4> m_instancedColor;
};
class InstancedDrawCase : public TestCase
{
public:
InstancedDrawCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& desc,
TestParams params)
: TestCase (testCtx, name, desc)
, m_params (params)
{
m_vertexShader = "#version 430\n"
"layout(location = 0) in vec4 in_position;\n"
"layout(location = 1) in vec4 in_color;\n"
"layout(location = 2) in vec4 in_color_2;\n"
"layout(push_constant) uniform TestParams {\n"
" float firstInstance;\n"
" float instanceCount;\n"
"} params;\n"
"layout(location = 0) out vec4 out_color;\n"
"out gl_PerVertex {\n"
" vec4 gl_Position;\n"
" float gl_PointSize;\n"
"};\n"
"void main() {\n"
" gl_PointSize = 1.0;\n"
" gl_Position = in_position + vec4(float(gl_InstanceIndex - params.firstInstance) * 2.0 / params.instanceCount, 0.0, 0.0, 0.0);\n"
" out_color = in_color + vec4(float(gl_InstanceIndex) / params.instanceCount, 0.0, 0.0, 1.0) + in_color_2;\n"
"}\n";
m_fragmentShader = "#version 430\n"
"layout(location = 0) in vec4 in_color;\n"
"layout(location = 0) out vec4 out_color;\n"
"void main()\n"
"{\n"
" out_color = in_color;\n"
"}\n";
}
virtual void checkSupport (Context& context) const
{
if (m_params.testAttribDivisor)
{
context.requireDeviceFunctionality("VK_EXT_vertex_attribute_divisor");
const vk::VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT& vertexAttributeDivisorFeatures = context.getVertexAttributeDivisorFeaturesEXT();
if (m_params.attribDivisor != 1 && !vertexAttributeDivisorFeatures.vertexAttributeInstanceRateDivisor)
TCU_THROW(NotSupportedError, "Implementation does not support vertexAttributeInstanceRateDivisor");
if (m_params.attribDivisor == 0 && !vertexAttributeDivisorFeatures.vertexAttributeInstanceRateZeroDivisor)
TCU_THROW(NotSupportedError, "Implementation does not support vertexAttributeInstanceRateDivisorZero");
if (m_params.testMultiview)
{
context.requireDeviceFunctionality("VK_KHR_multiview");
const vk::VkPhysicalDeviceMultiviewFeatures& multiviewFeatures = context.getMultiviewFeatures();
if (!multiviewFeatures.multiview)
TCU_THROW(NotSupportedError, "Implementation does not support multiview feature");
}
}
}
TestInstance* createInstance (Context& context) const
{
return new InstancedDrawInstance(context, m_params);
}
virtual void initPrograms (vk::SourceCollections& programCollection) const
{
programCollection.glslSources.add("InstancedDrawVert") << glu::VertexSource(m_vertexShader);
programCollection.glslSources.add("InstancedDrawFrag") << glu::FragmentSource(m_fragmentShader);
}
private:
const TestParams m_params;
std::string m_vertexShader;
std::string m_fragmentShader;
};
InstancedDrawInstance::InstancedDrawInstance(Context &context, TestParams params)
: TestInstance (context)
, m_params (params)
, m_vk (context.getDeviceInterface())
, m_colorAttachmentFormat (vk::VK_FORMAT_R8G8B8A8_UNORM)
{
const vk::VkDevice device = m_context.getDevice();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const vk::VkPushConstantRange pushConstantRange = {
vk::VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlags stageFlags;
0u, // uint32_t offset;
(deUint32)sizeof(float) * 2, // uint32_t size;
};
const PipelineLayoutCreateInfo pipelineLayoutCreateInfo(0, DE_NULL, 1, &pushConstantRange);
m_pipelineLayout = vk::createPipelineLayout(m_vk, device, &pipelineLayoutCreateInfo);
deUint32 arrayLayers = m_params.testMultiview ? 2 : 1;
const vk::VkExtent3D targetImageExtent = { WIDTH, HEIGHT, 1 };
const ImageCreateInfo targetImageCreateInfo(vk::VK_IMAGE_TYPE_2D, m_colorAttachmentFormat, targetImageExtent, 1, arrayLayers, vk::VK_SAMPLE_COUNT_1_BIT,
vk::VK_IMAGE_TILING_OPTIMAL, vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
m_colorTargetImage = Image::createAndAlloc(m_vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
const enum vk::VkImageViewType imageViewType = m_params.testMultiview ? vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY : vk::VK_IMAGE_VIEW_TYPE_2D;
ImageSubresourceRange subresourceRange = ImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT);
if (m_params.testMultiview)
subresourceRange.layerCount = 2;
const ImageViewCreateInfo colorTargetViewInfo(m_colorTargetImage->object(), imageViewType, m_colorAttachmentFormat, subresourceRange);
m_colorTargetView = vk::createImageView(m_vk, device, &colorTargetViewInfo);
RenderPassCreateInfo renderPassCreateInfo;
renderPassCreateInfo.addAttachment(AttachmentDescription(m_colorAttachmentFormat,
vk::VK_SAMPLE_COUNT_1_BIT,
vk::VK_ATTACHMENT_LOAD_OP_LOAD,
vk::VK_ATTACHMENT_STORE_OP_STORE,
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
vk::VK_ATTACHMENT_STORE_OP_STORE,
vk::VK_IMAGE_LAYOUT_GENERAL,
vk::VK_IMAGE_LAYOUT_GENERAL));
const vk::VkAttachmentReference colorAttachmentReference =
{
0,
vk::VK_IMAGE_LAYOUT_GENERAL
};
renderPassCreateInfo.addSubpass(SubpassDescription(vk::VK_PIPELINE_BIND_POINT_GRAPHICS,
0,
0,
DE_NULL,
1,
&colorAttachmentReference,
DE_NULL,
AttachmentReference(),
0,
DE_NULL));
vk::VkRenderPassMultiviewCreateInfo renderPassMultiviewCreateInfo;
// Bit mask that specifies which view rendering is broadcast to
// 0011 = Broadcast to first and second view (layer)
const deUint32 viewMask = 0x3;
// Bit mask that specifices correlation between views
// An implementation may use this for optimizations (concurrent render)
const deUint32 correlationMask = 0x3;
if (m_params.testMultiview)
{
DE_ASSERT(renderPassCreateInfo.subpassCount == 1);
renderPassMultiviewCreateInfo.sType = vk::VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
renderPassMultiviewCreateInfo.pNext = DE_NULL;
renderPassMultiviewCreateInfo.subpassCount = renderPassCreateInfo.subpassCount;
renderPassMultiviewCreateInfo.pViewMasks = &viewMask;
renderPassMultiviewCreateInfo.correlationMaskCount = 1u;
renderPassMultiviewCreateInfo.pCorrelationMasks = &correlationMask;
renderPassMultiviewCreateInfo.pViewOffsets = DE_NULL;
renderPassMultiviewCreateInfo.dependencyCount = 0u;
renderPassCreateInfo.pNext = &renderPassMultiviewCreateInfo;
}
m_renderPass = vk::createRenderPass(m_vk, device, &renderPassCreateInfo);
std::vector<vk::VkImageView> colorAttachments(1);
colorAttachments[0] = *m_colorTargetView;
const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
m_framebuffer = vk::createFramebuffer(m_vk, device, &framebufferCreateInfo);
const vk::VkVertexInputBindingDescription vertexInputBindingDescription[2] =
{
{
0u,
(deUint32)sizeof(VertexPositionAndColor),
vk::VK_VERTEX_INPUT_RATE_VERTEX,
},
{
1u,
(deUint32)sizeof(tcu::Vec4),
vk::VK_VERTEX_INPUT_RATE_INSTANCE,
},
};
const vk::VkVertexInputAttributeDescription vertexInputAttributeDescriptions[] =
{
{
0u,
0u,
vk::VK_FORMAT_R32G32B32A32_SFLOAT,
0u
},
{
1u,
0u,
vk::VK_FORMAT_R32G32B32A32_SFLOAT,
(deUint32)sizeof(tcu::Vec4),
},
{
2u,
1u,
vk::VK_FORMAT_R32G32B32A32_SFLOAT,
0,
}
};
m_vertexInputState = PipelineCreateInfo::VertexInputState(2,
vertexInputBindingDescription,
DE_LENGTH_OF_ARRAY(vertexInputAttributeDescriptions),
vertexInputAttributeDescriptions);
const vk::VkVertexInputBindingDivisorDescriptionEXT vertexInputBindingDivisorDescription =
{
1u,
m_params.attribDivisor,
};
if (m_params.testAttribDivisor)
m_vertexInputState.addDivisors(1, &vertexInputBindingDivisorDescription);
const CmdPoolCreateInfo cmdPoolCreateInfo(queueFamilyIndex);
m_cmdPool = vk::createCommandPool(m_vk, device, &cmdPoolCreateInfo);
m_cmdBuffer = vk::allocateCommandBuffer(m_vk, device, *m_cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const vk::Unique<vk::VkShaderModule> vs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawVert"), 0));
const vk::Unique<vk::VkShaderModule> fs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawFrag"), 0));
const PipelineCreateInfo::ColorBlendState::Attachment vkCbAttachmentState;
vk::VkViewport viewport = vk::makeViewport(WIDTH, HEIGHT);
vk::VkRect2D scissor = vk::makeRect2D(WIDTH, HEIGHT);
PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, 0);
pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vs, "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fs, "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(m_vertexInputState));
pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(m_params.topology));
pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &vkCbAttachmentState));
pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(1, std::vector<vk::VkViewport>(1, viewport), std::vector<vk::VkRect2D>(1, scissor)));
pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
m_pipeline = vk::createGraphicsPipeline(m_vk, device, DE_NULL, &pipelineCreateInfo);
}
tcu::TestStatus InstancedDrawInstance::iterate()
{
const vk::VkQueue queue = m_context.getUniversalQueue();
const vk::VkDevice device = m_context.getDevice();
static const deUint32 instanceCounts[] = { 0, 1, 2, 4, 20 };
static const deUint32 firstInstanceIndices[] = { 0, 1, 3, 4, 20 };
const deUint32 numLayers = m_params.testMultiview ? 2 : 1;
qpTestResult res = QP_TEST_RESULT_PASS;
const vk::VkClearColorValue clearColor = { { 0.0f, 0.0f, 0.0f, 1.0f } };
int firstInstanceIndicesCount = 1;
// Require 'drawIndirectFirstInstance' feature to run non-zero firstInstance indirect draw tests.
if (m_context.getDeviceFeatures().drawIndirectFirstInstance)
firstInstanceIndicesCount = DE_LENGTH_OF_ARRAY(firstInstanceIndices);
for (int instanceCountNdx = 0; instanceCountNdx < DE_LENGTH_OF_ARRAY(instanceCounts); instanceCountNdx++)
{
const deUint32 instanceCount = instanceCounts[instanceCountNdx];
for (int firstInstanceIndexNdx = 0; firstInstanceIndexNdx < firstInstanceIndicesCount; firstInstanceIndexNdx++)
{
// Prepare vertex data for at least one instance
const deUint32 prepareCount = de::max(instanceCount, 1u);
const deUint32 firstInstance = firstInstanceIndices[firstInstanceIndexNdx];
prepareVertexData(prepareCount, firstInstance, m_params.testAttribDivisor ? m_params.attribDivisor : 1);
const de::SharedPtr<Buffer> vertexBuffer = createAndUploadBuffer(m_data, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
const de::SharedPtr<Buffer> instancedVertexBuffer = createAndUploadBuffer(m_instancedColor, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
de::SharedPtr<Buffer> indexBuffer;
de::SharedPtr<Buffer> indirectBuffer;
beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
if (m_params.testMultiview)
{
vk::VkImageMemoryBarrier barrier;
barrier.sType = vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.pNext = DE_NULL;
barrier.srcAccessMask = 0u;
barrier.dstAccessMask = vk::VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.oldLayout = vk::VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = vk::VK_IMAGE_LAYOUT_GENERAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_colorTargetImage->object();
barrier.subresourceRange.aspectMask = vk::VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = numLayers;
m_vk.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, (vk::VkDependencyFlags)0, 0, (const vk::VkMemoryBarrier*)DE_NULL,
0, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1, &barrier);
}
else
{
initialTransitionColor2DImage(m_vk, *m_cmdBuffer, m_colorTargetImage->object(), vk::VK_IMAGE_LAYOUT_GENERAL,
vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT);
}
const ImageSubresourceRange subresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, numLayers);
m_vk.cmdClearColorImage(*m_cmdBuffer, m_colorTargetImage->object(),
vk::VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange);
const vk::VkMemoryBarrier memBarrier =
{
vk::VK_STRUCTURE_TYPE_MEMORY_BARRIER,
DE_NULL,
vk::VK_ACCESS_TRANSFER_WRITE_BIT,
vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
const vk::VkRect2D renderArea = vk::makeRect2D(WIDTH, HEIGHT);
beginRenderPass(m_vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, renderArea);
if (m_params.function == TestParams::FUNCTION_DRAW_INDEXED || m_params.function == TestParams::FUNCTION_DRAW_INDEXED_INDIRECT)
{
indexBuffer = createAndUploadBuffer(m_indexes, m_vk, m_context, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
m_vk.cmdBindIndexBuffer(*m_cmdBuffer, indexBuffer->object(), 0, vk::VK_INDEX_TYPE_UINT32);
}
const vk::VkBuffer vertexBuffers[] =
{
vertexBuffer->object(),
instancedVertexBuffer->object(),
};
const vk::VkDeviceSize vertexBufferOffsets[] =
{
0, // vertexBufferOffset
0, // instancedVertexBufferOffset
};
m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, DE_LENGTH_OF_ARRAY(vertexBuffers), vertexBuffers, vertexBufferOffsets);
const float pushConstants[] = { (float)firstInstance, (float)instanceCount };
m_vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, vk::VK_SHADER_STAGE_VERTEX_BIT, 0u, (deUint32)sizeof(pushConstants), pushConstants);
m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
switch (m_params.function)
{
case TestParams::FUNCTION_DRAW:
m_vk.cmdDraw(*m_cmdBuffer, (deUint32)m_data.size(), instanceCount, 0u, firstInstance);
break;
case TestParams::FUNCTION_DRAW_INDEXED:
m_vk.cmdDrawIndexed(*m_cmdBuffer, (deUint32)m_indexes.size(), instanceCount, 0u, 0u, firstInstance);
break;
case TestParams::FUNCTION_DRAW_INDIRECT:
{
vk::VkDrawIndirectCommand drawCommand =
{
(deUint32)m_data.size(), // uint32_t vertexCount;
instanceCount, // uint32_t instanceCount;
0u, // uint32_t firstVertex;
firstInstance, // uint32_t firstInstance;
};
std::vector<vk::VkDrawIndirectCommand> drawCommands;
drawCommands.push_back(drawCommand);
indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
m_vk.cmdDrawIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
break;
}
case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
{
vk::VkDrawIndexedIndirectCommand drawCommand =
{
(deUint32)m_indexes.size(), // uint32_t indexCount;
instanceCount, // uint32_t instanceCount;
0u, // uint32_t firstIndex;
0, // int32_t vertexOffset;
firstInstance, // uint32_t firstInstance;
};
std::vector<vk::VkDrawIndexedIndirectCommand> drawCommands;
drawCommands.push_back(drawCommand);
indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
break;
}
default:
DE_ASSERT(false);
}
endRenderPass(m_vk, *m_cmdBuffer);
endCommandBuffer(m_vk, *m_cmdBuffer);
submitCommandsAndWait(m_vk, device, queue, m_cmdBuffer.get());
// Reference rendering
std::vector<tcu::Vec4> vetrices;
std::vector<tcu::Vec4> colors;
for (std::vector<VertexPositionAndColor>::const_iterator it = m_data.begin(); it != m_data.end(); ++it)
{
vetrices.push_back(it->position);
colors.push_back(it->color);
}
tcu::TextureLevel refImage (vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
tcu::clear(refImage.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
const TestVertShader vertShader(instanceCount, firstInstance);
const TestFragShader fragShader;
const rr::Program program (&vertShader, &fragShader);
const rr::MultisamplePixelBufferAccess colorBuffer = rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(refImage.getAccess());
const rr::RenderTarget renderTarget (colorBuffer);
const rr::RenderState renderState ((rr::ViewportState(colorBuffer)), m_context.getDeviceProperties().limits.subPixelPrecisionBits);
const rr::Renderer renderer;
const rr::VertexAttrib vertexAttribs[] =
{
rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &vetrices[0]),
rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &colors[0]),
// The reference renderer treats a divisor of 0 as meaning per-vertex. Use INT_MAX instead; it should work just as well.
rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), m_params.testAttribDivisor ? (m_params.attribDivisor == 0 ? INT_MAX : m_params.attribDivisor) : 1, &m_instancedColor[0])
};
if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
{
const rr::PrimitiveList primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)vetrices.size(), 0);
const rr::DrawCommand command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
primitives);
renderer.drawInstanced(command, instanceCount);
}
else
{
const rr::DrawIndices indicies(m_indexes.data());
const rr::PrimitiveList primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)m_indexes.size(), indicies);
const rr::DrawCommand command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
primitives);
renderer.drawInstanced(command, instanceCount);
}
const vk::VkOffset3D zeroOffset = { 0, 0, 0 };
for (deUint32 i = 0; i < numLayers; i++)
{
const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT, 0, i);
tcu::TestLog &log = m_context.getTestContext().getLog();
std::ostringstream resultDesc;
resultDesc << "Image layer " << i << " comparison result. Instance count: " << instanceCount << " first instance index: " << firstInstance;
if (m_params.topology == vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
{
const bool ok = tcu::intThresholdPositionDeviationCompare(
log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame,
tcu::UVec4(4u), // color threshold
tcu::IVec3(1, 1, 0), // position deviation tolerance
true, // don't check the pixels at the boundary
tcu::COMPARE_LOG_RESULT);
if (!ok)
res = QP_TEST_RESULT_FAIL;
}
else
{
if (!tcu::fuzzyCompare(log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT))
res = QP_TEST_RESULT_FAIL;
}
}
}
}
return tcu::TestStatus(res, qpGetTestResultName(res));
}
void InstancedDrawInstance::prepareVertexData(int instanceCount, int firstInstance, int instanceDivisor)
{
m_data.clear();
m_indexes.clear();
m_instancedColor.clear();
if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
{
for (int y = 0; y < QUAD_GRID_SIZE; y++)
{
for (int x = 0; x < QUAD_GRID_SIZE; x++)
{
const float fx0 = -1.0f + (float)(x+0) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
const float fx1 = -1.0f + (float)(x+1) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
const float fy0 = -1.0f + (float)(y+0) / (float)QUAD_GRID_SIZE * 2.0f;
const float fy1 = -1.0f + (float)(y+1) / (float)QUAD_GRID_SIZE * 2.0f;
// Vertices of a quad's lower-left triangle: (fx0, fy0), (fx1, fy0) and (fx0, fy1)
m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
// Vertices of a quad's upper-right triangle: (fx1, fy1), (fx0, fy1) and (fx1, fy0)
m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
}
}
}
else
{
for (int y = 0; y < QUAD_GRID_SIZE + 1; y++)
{
for (int x = 0; x < QUAD_GRID_SIZE + 1; x++)
{
const float fx = -1.0f + (float)x / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
const float fy = -1.0f + (float)y / (float)QUAD_GRID_SIZE * 2.0f;
m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx, fy, 1.0f, 1.0f),
(y % 2 ? tcu::RGBA::blue().toVec() : tcu::RGBA::green().toVec())));
}
}
for (int y = 0; y < QUAD_GRID_SIZE; y++)
{
for (int x = 0; x < QUAD_GRID_SIZE; x++)
{
const int ndx00 = y*(QUAD_GRID_SIZE + 1) + x;
const int ndx10 = y*(QUAD_GRID_SIZE + 1) + x + 1;
const int ndx01 = (y + 1)*(QUAD_GRID_SIZE + 1) + x;
const int ndx11 = (y + 1)*(QUAD_GRID_SIZE + 1) + x + 1;
// Lower-left triangle of a quad.
m_indexes.push_back((deUint16)ndx00);
m_indexes.push_back((deUint16)ndx10);
m_indexes.push_back((deUint16)ndx01);
// Upper-right triangle of a quad.
m_indexes.push_back((deUint16)ndx11);
m_indexes.push_back((deUint16)ndx01);
m_indexes.push_back((deUint16)ndx10);
}
}
}
const int colorCount = instanceDivisor == 0 ? 1 : (instanceCount + firstInstance + instanceDivisor - 1) / instanceDivisor;
for (int i = 0; i < instanceCount + firstInstance; i++)
{
m_instancedColor.push_back(tcu::Vec4(0.0, (float)(1.0 - i * 1.0 / colorCount) / 2, 0.0, 1.0));
}
}
} // anonymus
InstancedTests::InstancedTests(tcu::TestContext& testCtx)
: TestCaseGroup (testCtx, "instanced", "Instanced drawing tests")
{
static const vk::VkPrimitiveTopology topologies[] =
{
vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
};
static const TestParams::DrawFunction functions[] =
{
TestParams::FUNCTION_DRAW,
TestParams::FUNCTION_DRAW_INDEXED,
TestParams::FUNCTION_DRAW_INDIRECT,
TestParams::FUNCTION_DRAW_INDEXED_INDIRECT,
};
static const deBool multiviews[] = { DE_FALSE, DE_TRUE };
static const deUint32 divisors[] = { 0, 1, 2, 4, 20 };
for (int topologyNdx = 0; topologyNdx < DE_LENGTH_OF_ARRAY(topologies); topologyNdx++)
{
for (int functionNdx = 0; functionNdx < DE_LENGTH_OF_ARRAY(functions); functionNdx++)
{
for (int testAttribDivisor = 0; testAttribDivisor < 2; testAttribDivisor++)
{
for (int divisorNdx = 0; divisorNdx < DE_LENGTH_OF_ARRAY(divisors); divisorNdx++)
{
for (int multiviewNdx = 0; multiviewNdx < DE_LENGTH_OF_ARRAY(multiviews); multiviewNdx++)
{
// If we don't have VK_EXT_vertex_attribute_divisor, we only get a divisor or 1.
if (!testAttribDivisor && divisors[divisorNdx] != 1)
continue;
TestParams param;
param.function = functions[functionNdx];
param.topology = topologies[topologyNdx];
param.testAttribDivisor = testAttribDivisor ? DE_TRUE : DE_FALSE;
param.attribDivisor = divisors[divisorNdx];
param.testMultiview = multiviews[multiviewNdx];
// Add multiview tests only when vertex attribute divisor is enabled.
if (param.testMultiview && !testAttribDivisor)
continue;
std::string testName = de::toString(param);
addChild(new InstancedDrawCase(m_testCtx, de::toLower(testName), "Instanced drawing test", param));
}
}
}
}
}
}
InstancedTests::~InstancedTests() {}
} // DrawTests
} // vkt