blob: e79217622d38dd2a73f9ca533ce508fdb1893f0e [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2019 The Khronos Group Inc.
* Copyright (c) 2019 Google LLC.
*
* 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 Tests for provoking vertex
*//*--------------------------------------------------------------------*/
#include "vktRasterizationProvokingVertexTests.hpp"
#include "vkCmdUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkRefUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vktTestGroupUtil.hpp"
#include "tcuRGBA.hpp"
#include "tcuSurface.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuTestLog.hpp"
using namespace vk;
namespace vkt
{
namespace rasterization
{
namespace
{
enum ProvokingVertexMode
{
PROVOKING_VERTEX_DEFAULT,
PROVOKING_VERTEX_FIRST,
PROVOKING_VERTEX_LAST,
PROVOKING_VERTEX_PER_PIPELINE
};
struct Params
{
VkFormat format;
tcu::UVec2 size;
VkPrimitiveTopology primitiveTopology;
bool requireGeometryShader;
bool transformFeedback;
ProvokingVertexMode provokingVertexMode;
};
static VkDeviceSize getXfbBufferSize (deUint32 vertexCount, VkPrimitiveTopology topology)
{
switch (topology)
{
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
return vertexCount * sizeof(tcu::Vec4);
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
return (vertexCount - 1) * 2 * sizeof(tcu::Vec4);
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
return (vertexCount - 2) * 3 * sizeof(tcu::Vec4);
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
return vertexCount / 2 * sizeof(tcu::Vec4);
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
return (vertexCount - 3) * 2 * sizeof(tcu::Vec4);
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
return (vertexCount / 2 - 2) * 3 * sizeof(tcu::Vec4);
default:
DE_FATAL("Unknown primitive topology");
return 0;
}
}
static bool verifyXfbBuffer (const tcu::Vec4* const xfbResults,
deUint32 count,
VkPrimitiveTopology topology,
ProvokingVertexMode mode,
std::string& errorMessage)
{
const deUint32 primitiveSize = ((topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST) ||
(topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) ||
(topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY) ||
(topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY))
? 2
: 3;
const tcu::Vec4 expected (1.0f, 0.0f, 0.0f, 1.0f);
const deUint32 start = (mode == PROVOKING_VERTEX_LAST)
? primitiveSize - 1
: 0;
DE_ASSERT(count % primitiveSize == 0);
for (deUint32 ndx = start; ndx < count; ndx += primitiveSize)
{
if (xfbResults[ndx] != expected)
{
errorMessage = "Vertex " + de::toString(ndx) +
": Expected red, got " + de::toString(xfbResults[ndx]);
return false;
}
}
errorMessage = "";
return true;
}
class ProvokingVertexTestInstance : public TestInstance
{
public:
ProvokingVertexTestInstance (Context& context, Params params);
tcu::TestStatus iterate (void);
private:
Params m_params;
};
ProvokingVertexTestInstance::ProvokingVertexTestInstance (Context& context, Params params)
: TestInstance (context)
, m_params (params)
{
}
class ProvokingVertexTestCase : public TestCase
{
public:
ProvokingVertexTestCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const Params params);
virtual void initPrograms (SourceCollections& programCollection) const;
virtual void checkSupport (Context& context) const;
virtual TestInstance* createInstance (Context& context) const;
private:
const Params m_params;
};
ProvokingVertexTestCase::ProvokingVertexTestCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const Params params)
: TestCase (testCtx, name, description)
, m_params (params)
{
}
void ProvokingVertexTestCase::initPrograms (SourceCollections& programCollection) const
{
std::ostringstream vertShader;
vertShader << "#version 450\n"
<< "layout(location = 0) in vec4 in_position;\n"
<< "layout(location = 1) in vec4 in_color;\n"
<< "layout(location = 0) flat out vec4 out_color;\n";
if (m_params.transformFeedback)
vertShader << "layout(xfb_buffer = 0, xfb_offset = 0, location = 1) out vec4 out_xfb;\n";
vertShader << "void main()\n"
<< "{\n";
if (m_params.transformFeedback)
vertShader << " out_xfb = in_color;\n";
vertShader << " out_color = in_color;\n"
<< " gl_Position = in_position;\n"
<< "}\n";
const std::string fragShader (
"#version 450\n"
"layout(location = 0) flat in vec4 in_color;\n"
"layout(location = 0) out vec4 out_color;\n"
"void main()\n"
"{\n"
" out_color = in_color;\n"
"}\n");
programCollection.glslSources.add("vert") << glu::VertexSource(vertShader.str());
programCollection.glslSources.add("frag") << glu::FragmentSource(fragShader);
}
void ProvokingVertexTestCase::checkSupport (Context& context) const
{
if (m_params.requireGeometryShader)
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
if (m_params.transformFeedback)
context.requireDeviceFunctionality("VK_EXT_transform_feedback");
if (m_params.provokingVertexMode != PROVOKING_VERTEX_DEFAULT)
{
const VkPhysicalDeviceProvokingVertexFeaturesEXT& features = context.getProvokingVertexFeaturesEXT();
const VkPhysicalDeviceProvokingVertexPropertiesEXT& properties = context.getProvokingVertexPropertiesEXT();
context.requireDeviceFunctionality("VK_EXT_provoking_vertex");
if (m_params.transformFeedback && features.transformFeedbackPreservesProvokingVertex != VK_TRUE)
TCU_THROW(NotSupportedError, "transformFeedbackPreservesProvokingVertex not supported");
if (m_params.transformFeedback && (m_params.primitiveTopology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN) && (properties.transformFeedbackPreservesTriangleFanProvokingVertex != VK_TRUE))
TCU_THROW(NotSupportedError, "transformFeedbackPreservesTriangleFanProvokingVertex not supported");
if (m_params.provokingVertexMode != PROVOKING_VERTEX_FIRST)
{
if (features.provokingVertexLast != VK_TRUE)
TCU_THROW(NotSupportedError, "VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT not supported");
if ((m_params.provokingVertexMode == PROVOKING_VERTEX_PER_PIPELINE) && (properties.provokingVertexModePerPipeline != VK_TRUE))
TCU_THROW(NotSupportedError, "provokingVertexModePerPipeline not supported");
}
}
if (m_params.primitiveTopology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN &&
context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") &&
!context.getPortabilitySubsetFeatures().triangleFans)
{
TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Triangle fans are not supported by this implementation");
}
}
TestInstance* ProvokingVertexTestCase::createInstance (Context& context) const
{
return new ProvokingVertexTestInstance(context, m_params);
}
tcu::TestStatus ProvokingVertexTestInstance::iterate (void)
{
const bool useProvokingVertexExt = (m_params.provokingVertexMode != PROVOKING_VERTEX_DEFAULT);
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const tcu::TextureFormat textureFormat = vk::mapVkFormat(m_params.format);
Allocator& allocator = m_context.getDefaultAllocator();
Move<VkImage> image;
Move<VkImageView> imageView;
de::MovePtr<Allocation> imageMemory;
Move<VkBuffer> resultBuffer;
de::MovePtr<Allocation> resultBufferMemory;
Move<VkBuffer> xfbBuffer;
de::MovePtr<Allocation> xfbBufferMemory;
VkDeviceSize xfbBufferSize = 0;
Move<VkBuffer> vertexBuffer;
de::MovePtr<Allocation> vertexBufferMemory;
Move<VkRenderPass> renderPass;
Move<VkFramebuffer> framebuffer;
Move<VkPipeline> pipeline;
Move<VkPipeline> altPipeline;
deUint32 vertexCount = 0;
// Image
{
const VkExtent3D extent = makeExtent3D(m_params.size.x(), m_params.size.y(), 1u);
const VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
const VkImageCreateInfo createInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
DE_NULL, // pNext
0u, // flags
VK_IMAGE_TYPE_2D, // imageType
m_params.format, // format
extent, // extent
1u, // mipLevels
1u, // arrayLayers
VK_SAMPLE_COUNT_1_BIT, // samples
VK_IMAGE_TILING_OPTIMAL, // tiling
usage, // usage
VK_SHARING_MODE_EXCLUSIVE, // sharingMode
1u, // queueFamilyIndexCount
&queueFamilyIndex, // pQueueFamilyIndices
VK_IMAGE_LAYOUT_UNDEFINED // initialLayout
};
image = createImage(vk, device, &createInfo, DE_NULL);
imageMemory = allocator.allocate(getImageMemoryRequirements(vk, device, *image), MemoryRequirement::Any);
VK_CHECK(vk.bindImageMemory(device, *image, imageMemory->getMemory(), imageMemory->getOffset()));
}
// Image view
{
const VkImageSubresourceRange subresourceRange =
{
VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
0u, // baseMipLevel
1u, // mipLevels
0u, // baseArrayLayer
1u // arraySize
};
imageView = makeImageView(vk, device, *image, VK_IMAGE_VIEW_TYPE_2D, m_params.format, subresourceRange, DE_NULL);
}
// Result Buffer
{
const VkDeviceSize bufferSize = textureFormat.getPixelSize() * m_params.size.x() * m_params.size.y();
const VkBufferCreateInfo createInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
resultBuffer = createBuffer(vk, device, &createInfo);
resultBufferMemory = allocator.allocate(getBufferMemoryRequirements(vk, device, *resultBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vk.bindBufferMemory(device, *resultBuffer, resultBufferMemory->getMemory(), resultBufferMemory->getOffset()));
}
// Render pass and framebuffer
{
renderPass = makeRenderPass(vk,
device,
m_params.format, // colorFormat
VK_FORMAT_UNDEFINED, // depthStencilFormat
VK_ATTACHMENT_LOAD_OP_CLEAR, // loadOperation
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // finalLayoutColor
VK_IMAGE_LAYOUT_UNDEFINED, // finalLayoutDepthStencil
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // subpassLayoutColor
VK_IMAGE_LAYOUT_UNDEFINED, // subpassLayoutDepthStencil
DE_NULL); // allocationCallbacks
framebuffer = makeFramebuffer(vk, device, *renderPass, *imageView, m_params.size.x(), m_params.size.y(), 1u);
}
// Pipelines
{
const Unique<VkShaderModule> vertexShader (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
const Unique<VkShaderModule> fragmentShader (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
const std::vector<VkViewport> viewports (1, makeViewport(tcu::UVec2(m_params.size)));
const std::vector<VkRect2D> scissors (1, makeRect2D(tcu::UVec2(m_params.size)));
const Move<VkPipelineLayout> pipelineLayout = makePipelineLayout(vk, device, 0, DE_NULL);
const VkVertexInputBindingDescription vertexInputBindingDescription =
{
0, // binding
sizeof(tcu::Vec4) * 2, // strideInBytes
VK_VERTEX_INPUT_RATE_VERTEX // stepRate
};
const VkVertexInputAttributeDescription vertexAttributeDescriptions[2] =
{
// Position
{
0u, // location
0u, // binding
VK_FORMAT_R32G32B32A32_SFLOAT, // format
0u // offsetInBytes
},
// Color
{
1u, // location
0u, // binding
VK_FORMAT_R32G32B32A32_SFLOAT, // format
sizeof(tcu::Vec4) // offsetInBytes
}
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // sType
DE_NULL, // pNext
0, // flags
1u, // bindingCount
&vertexInputBindingDescription, // pVertexBindingDescriptions
2u, // attributeCount
vertexAttributeDescriptions // pVertexAttributeDescriptions
};
const VkProvokingVertexModeEXT provokingVertexMode = m_params.provokingVertexMode == PROVOKING_VERTEX_LAST
? VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT
: VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT;
const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, // sType
DE_NULL, // pNext
provokingVertexMode // provokingVertexMode
};
const VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // sType
useProvokingVertexExt ? &provokingVertexCreateInfo : DE_NULL, // pNext
0, // flags
false, // depthClipEnable
false, // rasterizerDiscardEnable
VK_POLYGON_MODE_FILL, // fillMode
VK_CULL_MODE_NONE, // cullMode
VK_FRONT_FACE_COUNTER_CLOCKWISE, // frontFace
VK_FALSE, // depthBiasEnable
0.0f, // depthBias
0.0f, // depthBiasClamp
0.0f, // slopeScaledDepthBias
1.0f // lineWidth
};
pipeline = makeGraphicsPipeline(vk,
device,
*pipelineLayout,
*vertexShader,
DE_NULL, // tessellationControlShaderModule
DE_NULL, // tessellationEvalShaderModule
DE_NULL, // geometryShaderModule
*fragmentShader,
*renderPass,
viewports,
scissors,
m_params.primitiveTopology,
0u, // subpass
0u, // patchControlPoints
&vertexInputStateParams,
&rasterizationStateCreateInfo,
DE_NULL, // multisampleStateCreateInfo
DE_NULL, // depthStencilStateCreateInfo
DE_NULL, // colorBlendStateCreateInfo
DE_NULL); // dynamicStateCreateInfo
if (m_params.provokingVertexMode == PROVOKING_VERTEX_PER_PIPELINE)
{
const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT altProvokingVertexCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, // sType
DE_NULL, // pNext
VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT // provokingVertexMode
};
const VkPipelineRasterizationStateCreateInfo altRasterizationStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // sType
&altProvokingVertexCreateInfo, // pNext
0, // flags
false, // depthClipEnable
false, // rasterizerDiscardEnable
VK_POLYGON_MODE_FILL, // fillMode
VK_CULL_MODE_NONE, // cullMode
VK_FRONT_FACE_COUNTER_CLOCKWISE, // frontFace
VK_FALSE, // depthBiasEnable
0.0f, // depthBias
0.0f, // depthBiasClamp
0.0f, // slopeScaledDepthBias
1.0f, // lineWidth
};
altPipeline = makeGraphicsPipeline(vk,
device,
*pipelineLayout,
*vertexShader,
DE_NULL, // tessellationControlShaderModule
DE_NULL, // tessellationEvalShaderModule
DE_NULL, // geometryShaderModule
*fragmentShader,
*renderPass,
viewports,
scissors,
m_params.primitiveTopology,
0u, // subpass
0u, // patchControlPoints
&vertexInputStateParams,
&altRasterizationStateCreateInfo,
DE_NULL, // multisampleStateCreateInfo
DE_NULL, // depthStencilStateCreateInfo
DE_NULL, // colorBlendStateCreateInfo
DE_NULL); // dynamicStateCreateInfo
}
}
// Vertex buffer
{
const tcu::Vec4 red (1.0f, 0.0f, 0.0f, 1.0f);
const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f);
const tcu::Vec4 white (1.0f, 1.0f, 1.0f, 1.0f);
std::vector<tcu::Vec4> vertices;
switch (m_params.primitiveTopology)
{
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
// Position //Color
vertices.push_back(tcu::Vec4(-1.0f,-0.5f, 0.0f, 1.0f)); vertices.push_back(red); // line 0
vertices.push_back(tcu::Vec4( 1.0f,-0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4(-1.0f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red); // line 1
vertices.push_back(tcu::Vec4( 1.0f, 0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4(-0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // line 1 reverse
vertices.push_back(tcu::Vec4(-0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // line 0 reverse
vertices.push_back(tcu::Vec4( 0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
break;
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
// Position // Color
vertices.push_back(tcu::Vec4(-1.0f,-0.5f, 0.0f, 1.0f)); vertices.push_back(red); // line strip
vertices.push_back(tcu::Vec4( 1.0f,-0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4(-1.0f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 1.0f, 0.5f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4(-0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green); // line strip reverse
vertices.push_back(tcu::Vec4(-0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
// Position // Color
vertices.push_back(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red); // triangle 0
vertices.push_back(tcu::Vec4(-0.6f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4(-0.2f, 1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 0.2f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red); // triangle 1
vertices.push_back(tcu::Vec4( 0.6f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4(-1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // triangle 1 reverse
vertices.push_back(tcu::Vec4(-0.6f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4(-0.2f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.2f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // triangle 0 reverse
vertices.push_back(tcu::Vec4( 0.6f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4(-1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
// Position // Color
vertices.push_back(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red); // triangle strip
vertices.push_back(tcu::Vec4(-0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4(-1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // triangle strip reverse
vertices.push_back(tcu::Vec4(-0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
// Position // Color
vertices.push_back(tcu::Vec4( 0.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green); // triangle fan
vertices.push_back(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4(-0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 0.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green); // triangle fan reverse
vertices.push_back(tcu::Vec4(-1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4(-0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
break;
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
// Position // Color
vertices.push_back(tcu::Vec4(-1.0f,-0.5f, 0.0f, 1.0f)); vertices.push_back(green); // line 0
vertices.push_back(tcu::Vec4(-0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 1.0f,-0.5f, 0.0f, 1.0f)); vertices.push_back(yellow);
vertices.push_back(tcu::Vec4(-1.0f, 0.5f, 0.0f, 1.0f)); vertices.push_back(green); // line 1
vertices.push_back(tcu::Vec4(-0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 1.0f, 0.5f, 0.0f, 1.0f)); vertices.push_back(yellow);
vertices.push_back(tcu::Vec4(-0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(yellow); // line 1 reverse
vertices.push_back(tcu::Vec4(-0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4(-0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4(-0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(yellow); // line 0 reverse
vertices.push_back(tcu::Vec4( 0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
break;
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
// Position // Color
vertices.push_back(tcu::Vec4(-1.0f,-0.5f, 0.0f, 1.0f)); vertices.push_back(green); // line strip
vertices.push_back(tcu::Vec4(-0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4(-0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 1.0f, 0.5f, 0.0f, 1.0f)); vertices.push_back(yellow);
vertices.push_back(tcu::Vec4(-0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(yellow); // line strip reverse
vertices.push_back(tcu::Vec4(-0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4(-0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f,-0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 0.5f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
// Position // Color
vertices.push_back(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red); // triangle 0
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-0.6f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-0.2f, 1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.2f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red); // triangle 1
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.6f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // triangle 1 reverse
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-0.6f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-0.2f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.2f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // triangle 0 reverse
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.6f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
// Position // Color
vertices.push_back(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red); // triangle strip
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.5f,-1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f)); vertices.push_back(blue);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(blue); // triangle strip reverse
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4(-0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(green);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 0.5f, 1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
vertices.push_back(tcu::Vec4( 1.0f,-1.0f, 0.0f, 1.0f)); vertices.push_back(red);
vertices.push_back(tcu::Vec4( 0.0f, 0.0f, 0.0f, 1.0f)); vertices.push_back(white);
break;
default:
DE_FATAL("Unknown primitive topology");
}
const size_t bufferSize = vertices.size() * sizeof(tcu::Vec4);
const VkBufferCreateInfo createInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
vertexCount = (deUint32)vertices.size() / 4;
vertexBuffer = createBuffer(vk, device, &createInfo);
vertexBufferMemory = allocator.allocate(getBufferMemoryRequirements(vk, device, *vertexBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vk.bindBufferMemory(device, *vertexBuffer, vertexBufferMemory->getMemory(), vertexBufferMemory->getOffset()));
deMemcpy(vertexBufferMemory->getHostPtr(), &vertices[0], bufferSize);
flushAlloc(vk, device, *vertexBufferMemory);
}
// Transform feedback buffer
if (m_params.transformFeedback)
{
xfbBufferSize = getXfbBufferSize(vertexCount, m_params.primitiveTopology);
if (m_params.provokingVertexMode ==PROVOKING_VERTEX_PER_PIPELINE)
xfbBufferSize = xfbBufferSize * 2;
const int bufferUsage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
const VkBufferCreateInfo createInfo = makeBufferCreateInfo(xfbBufferSize, bufferUsage);
xfbBuffer = createBuffer(vk, device, &createInfo);
xfbBufferMemory = allocator.allocate(getBufferMemoryRequirements(vk, device, *xfbBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vk.bindBufferMemory(device, *xfbBuffer, xfbBufferMemory->getMemory(), xfbBufferMemory->getOffset()));
}
// Clear the color buffer to red and check the drawing doesn't add any
// other colors from non-provoking vertices
{
const VkQueue queue = m_context.getUniversalQueue();
const VkRect2D renderArea = makeRect2D(m_params.size.x(), m_params.size.y());
const VkDeviceSize vertexBufferOffset = 0;
const VkClearValue clearValue = makeClearValueColor(tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
const VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
const VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
const VkImageSubresourceRange subResourcerange =
{
VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
0, // baseMipLevel
1, // levelCount
0, // baseArrayLayer
1 // layerCount
};
const VkImageMemoryBarrier imageBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
DE_NULL, // pNext
0, // srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dstAccessMask
VK_IMAGE_LAYOUT_UNDEFINED, // oldLayout
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
VK_QUEUE_FAMILY_IGNORED, // srcQueueFamilyIndex
VK_QUEUE_FAMILY_IGNORED, // destQueueFamilyIndex
*image, // image
subResourcerange // subresourceRange
};
const VkMemoryBarrier xfbMemoryBarrier =
{
VK_STRUCTURE_TYPE_MEMORY_BARRIER, // sType
DE_NULL, // pNext
VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, // srcAccessMask
VK_ACCESS_HOST_READ_BIT // dstAccessMask
};
// The first half of the vertex buffer is for PROVOKING_VERTEX_FIRST,
// the second half for PROVOKING_VERTEX_LAST
const deUint32 firstVertex = m_params.provokingVertexMode == PROVOKING_VERTEX_LAST
? vertexCount
: 0u;
Move<VkCommandPool> commandPool = makeCommandPool(vk, device, queueFamilyIndex);
Move<VkCommandBuffer> commandBuffer = allocateCommandBuffer(vk, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
beginCommandBuffer(vk, *commandBuffer, 0u);
{
vk.cmdPipelineBarrier(*commandBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &imageBarrier);
beginRenderPass(vk, *commandBuffer, *renderPass, *framebuffer, renderArea, 1, &clearValue);
{
vk.cmdBindVertexBuffers(*commandBuffer, 0, 1, &*vertexBuffer, &vertexBufferOffset);
vk.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
if (m_params.transformFeedback)
{
const VkDeviceSize xfbBufferOffset = 0;
vk.cmdBindTransformFeedbackBuffersEXT(*commandBuffer, 0, 1, &*xfbBuffer, &xfbBufferOffset, &xfbBufferSize);
vk.cmdBeginTransformFeedbackEXT(*commandBuffer, 0, 0, DE_NULL, DE_NULL);
}
vk.cmdDraw(*commandBuffer, vertexCount, 1u, firstVertex, 0u);
if (m_params.provokingVertexMode == PROVOKING_VERTEX_PER_PIPELINE)
{
vk.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *altPipeline);
vk.cmdDraw(*commandBuffer, vertexCount, 1u, vertexCount, 0u);
}
if (m_params.transformFeedback)
vk.cmdEndTransformFeedbackEXT(*commandBuffer, 0, 0, DE_NULL, DE_NULL);
}
endRenderPass(vk, *commandBuffer);
if (m_params.transformFeedback)
vk.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &xfbMemoryBarrier, 0u, DE_NULL, 0u, DE_NULL);
copyImageToBuffer(vk, *commandBuffer, *image, *resultBuffer, tcu::IVec2(m_params.size.x(), m_params.size.y()));
}
endCommandBuffer(vk, *commandBuffer);
submitCommandsAndWait(vk, device, queue, commandBuffer.get());
invalidateAlloc(vk, device, *resultBufferMemory);
if (m_params.transformFeedback)
invalidateAlloc(vk, device, *xfbBufferMemory);
}
// Verify result
{
tcu::TestLog& log = m_context.getTestContext().getLog();
const size_t bufferSize = textureFormat.getPixelSize() * m_params.size.x() * m_params.size.y();
tcu::Surface referenceSurface (m_params.size.x(), m_params.size.y());
tcu::ConstPixelBufferAccess referenceAccess = referenceSurface.getAccess();
tcu::Surface resultSurface (m_params.size.x(), m_params.size.y());
tcu::ConstPixelBufferAccess resultAccess (textureFormat,
tcu::IVec3(m_params.size.x(), m_params.size.y(), 1),
resultBufferMemory->getHostPtr());
// Verify transform feedback buffer
if (m_params.transformFeedback)
{
const tcu::Vec4* const xfbResults = static_cast<tcu::Vec4*>(xfbBufferMemory->getHostPtr());
const deUint32 count = static_cast<deUint32>(xfbBufferSize / sizeof(tcu::Vec4));
std::string errorMessage = "";
log << tcu::TestLog::Section("XFB Vertex colors", "vertex colors");
for (deUint32 i = 0; i < count; i++)
{
log << tcu::TestLog::Message
<< "[" << de::toString(i) << "]\t"
<< de::toString(xfbResults[i])
<< tcu::TestLog::EndMessage;
}
log << tcu::TestLog::EndSection;
if (m_params.provokingVertexMode != PROVOKING_VERTEX_PER_PIPELINE)
{
if (!verifyXfbBuffer(xfbResults, count, m_params.primitiveTopology, m_params.provokingVertexMode, errorMessage))
return tcu::TestStatus::fail(errorMessage);
}
else
{
const deUint32 halfCount = count / 2;
if (!verifyXfbBuffer(xfbResults, halfCount, m_params.primitiveTopology, PROVOKING_VERTEX_FIRST, errorMessage))
return tcu::TestStatus::fail(errorMessage);
if (!verifyXfbBuffer(&xfbResults[halfCount], halfCount, m_params.primitiveTopology, PROVOKING_VERTEX_LAST, errorMessage))
return tcu::TestStatus::fail(errorMessage);
}
}
// Create reference
for (deUint32 y = 0; y < m_params.size.y(); y++)
for (deUint32 x = 0; x < m_params.size.x(); x++)
referenceSurface.setPixel(x, y, tcu::RGBA::red());
// Copy result
tcu::copy(resultSurface.getAccess(), resultAccess);
// Compare
if (deMemCmp(referenceAccess.getDataPtr(), resultAccess.getDataPtr(), bufferSize) != 0)
{
log << tcu::TestLog::ImageSet("Result of rendering", "Result of rendering")
<< tcu::TestLog::Image("Result", "Result", resultSurface)
<< tcu::TestLog::EndImageSet;
return tcu::TestStatus::fail("Incorrect rendering");
}
}
return tcu::TestStatus::pass("Solid red");
}
void createTests (tcu::TestCaseGroup* testGroup)
{
tcu::TestContext& testCtx = testGroup->getTestContext();
const struct Provoking
{
const char* name;
const char* desc;
ProvokingVertexMode mode;
} provokingVertexModes[] =
{
{ "default", "Default provoking vertex convention", PROVOKING_VERTEX_DEFAULT, },
{ "first", "VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT", PROVOKING_VERTEX_FIRST, },
{ "last", "VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT", PROVOKING_VERTEX_LAST, },
{ "per_pipeline", "Pipelines with different provokingVertexModes", PROVOKING_VERTEX_PER_PIPELINE }
};
const struct Topology
{
std::string name;
VkPrimitiveTopology type;
bool requiresGeometryShader;
} topologies[] =
{
{ "line_list", VK_PRIMITIVE_TOPOLOGY_LINE_LIST, false },
{ "line_strip", VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, false },
{ "triangle_list", VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false },
{ "triangle_strip", VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, false },
{ "triangle_fan", VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, false },
{ "line_list_with_adjacency", VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, true },
{ "line_strip_with_adjacency", VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, true },
{ "triangle_list_with_adjacency", VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, true },
{ "triangle_strip_with_adjacency", VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, true }
};
const struct TestType
{
const char* name;
const char* desc;
bool transformFeedback;
} testTypes[] =
{
{ "draw", "Test that primitives are flat shaded with the provoking vertex color", false },
{ "transform_feedback", "Test that transform feedback preserves the position of the provoking vertex", true }
};
for (const TestType& testType: testTypes)
{
tcu::TestCaseGroup* const typeGroup = new tcu::TestCaseGroup(testCtx, testType.name, testType.desc);
for (const Provoking& provoking : provokingVertexModes)
{
// Only test transformFeedbackPreservesProvokingVertex with VK_EXT_provoking_vertex
if (testType.transformFeedback && (provoking.mode == PROVOKING_VERTEX_DEFAULT))
continue;
tcu::TestCaseGroup* const provokingGroup = new tcu::TestCaseGroup(testCtx, provoking.name, provoking.desc);
for (const Topology& topology : topologies)
{
const std::string caseName = topology.name;
const std::string caseDesc = getPrimitiveTopologyName(topology.type);
const Params params =
{
VK_FORMAT_R8G8B8A8_UNORM, // format
tcu::UVec2(32, 32), // size
topology.type, // primitiveTopology
topology.requiresGeometryShader, // requireGeometryShader
testType.transformFeedback, // transformFeedback
provoking.mode // provokingVertexMode
};
provokingGroup->addChild(new ProvokingVertexTestCase(testCtx, caseName, caseDesc, params));
}
typeGroup->addChild(provokingGroup);
}
testGroup->addChild(typeGroup);
}
}
} // anonymous
tcu::TestCaseGroup* createProvokingVertexTests (tcu::TestContext& testCtx)
{
return createTestGroup(testCtx,
"provoking_vertex",
"Tests for provoking vertex",
createTests);
}
} // rasterization
} // vkt