blob: b2a30885c20258dc95dde8afebe33ff380d2e15f [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2016 The Khronos Group Inc.
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* Copyright (c) 2014 The Android Open Source Project
*
* 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 Functional rasterization tests.
*//*--------------------------------------------------------------------*/
#include "vktTestGroupUtil.hpp"
#include "vktAmberTestCase.hpp"
#include "vktRasterizationTests.hpp"
#include "vktRasterizationFragShaderSideEffectsTests.hpp"
#include "vktRasterizationProvokingVertexTests.hpp"
#include "tcuRasterizationVerifier.hpp"
#include "tcuSurface.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuVectorUtil.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuResultCollector.hpp"
#include "tcuFloatFormat.hpp"
#include "vkImageUtil.hpp"
#include "deStringUtil.hpp"
#include "deRandom.hpp"
#include "vktTestCase.hpp"
#include "vktTestCaseUtil.hpp"
#include "vkPrograms.hpp"
#include "vkMemUtil.hpp"
#include "vkRefUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkBufferWithMemory.hpp"
#include "vkImageWithMemory.hpp"
#include "vkBarrierUtil.hpp"
#include <vector>
#include <sstream>
using namespace vk;
namespace vkt
{
namespace rasterization
{
namespace
{
using tcu::RasterizationArguments;
using tcu::TriangleSceneSpec;
using tcu::PointSceneSpec;
using tcu::LineSceneSpec;
using tcu::LineInterpolationMethod;
static const char* const s_shaderVertexTemplate = "#version 310 es\n"
"layout(location = 0) in highp vec4 a_position;\n"
"layout(location = 1) in highp vec4 a_color;\n"
"layout(location = 0) ${INTERPOLATION}out highp vec4 v_color;\n"
"layout (set=0, binding=0) uniform PointSize {\n"
" highp float u_pointSize;\n"
"};\n"
"void main ()\n"
"{\n"
" gl_Position = a_position;\n"
" gl_PointSize = u_pointSize;\n"
" v_color = a_color;\n"
"}\n";
static const char* const s_shaderFragmentTemplate = "#version 310 es\n"
"layout(location = 0) out highp vec4 fragColor;\n"
"layout(location = 0) ${INTERPOLATION}in highp vec4 v_color;\n"
"void main ()\n"
"{\n"
" fragColor = v_color;\n"
"}\n";
enum InterpolationCaseFlags
{
INTERPOLATIONFLAGS_NONE = 0,
INTERPOLATIONFLAGS_PROJECTED = (1 << 1),
INTERPOLATIONFLAGS_FLATSHADE = (1 << 2),
};
enum ResolutionValues
{
RESOLUTION_POT = 256,
RESOLUTION_NPOT = 258
};
enum PrimitiveWideness
{
PRIMITIVEWIDENESS_NARROW = 0,
PRIMITIVEWIDENESS_WIDE,
PRIMITIVEWIDENESS_LAST
};
enum LineStipple
{
LINESTIPPLE_DISABLED = 0,
LINESTIPPLE_STATIC,
LINESTIPPLE_DYNAMIC,
LINESTIPPLE_LAST
};
static const deUint32 lineStippleFactor = 2;
static const deUint32 lineStipplePattern = 0x0F0F;
enum class LineStippleFactorCase
{
DEFAULT = 0,
ZERO,
LARGE,
};
enum PrimitiveStrictness
{
PRIMITIVESTRICTNESS_STRICT = 0,
PRIMITIVESTRICTNESS_NONSTRICT,
PRIMITIVESTRICTNESS_IGNORE,
PRIMITIVESTRICTNESS_LAST
};
class BaseRenderingTestCase : public TestCase
{
public:
BaseRenderingTestCase (tcu::TestContext& context, const std::string& name, const std::string& description, VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT, deBool flatshade = DE_FALSE);
virtual ~BaseRenderingTestCase (void);
virtual void initPrograms (vk::SourceCollections& programCollection) const;
protected:
const VkSampleCountFlagBits m_sampleCount;
const deBool m_flatshade;
};
BaseRenderingTestCase::BaseRenderingTestCase (tcu::TestContext& context, const std::string& name, const std::string& description, VkSampleCountFlagBits sampleCount, deBool flatshade)
: TestCase(context, name, description)
, m_sampleCount (sampleCount)
, m_flatshade (flatshade)
{
}
void BaseRenderingTestCase::initPrograms (vk::SourceCollections& programCollection) const
{
tcu::StringTemplate vertexSource (s_shaderVertexTemplate);
tcu::StringTemplate fragmentSource (s_shaderFragmentTemplate);
std::map<std::string, std::string> params;
params["INTERPOLATION"] = (m_flatshade) ? ("flat ") : ("");
programCollection.glslSources.add("vertext_shader") << glu::VertexSource(vertexSource.specialize(params));
programCollection.glslSources.add("fragment_shader") << glu::FragmentSource(fragmentSource.specialize(params));
}
BaseRenderingTestCase::~BaseRenderingTestCase (void)
{
}
class BaseRenderingTestInstance : public TestInstance
{
public:
BaseRenderingTestInstance (Context& context, VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT, deUint32 renderSize = RESOLUTION_POT, VkFormat imageFormat = VK_FORMAT_R8G8B8A8_UNORM, deUint32 additionalRenderSize = 0);
~BaseRenderingTestInstance (void);
protected:
void addImageTransitionBarrier (VkCommandBuffer commandBuffer, VkImage image, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageLayout oldLayout, VkImageLayout newLayout) const;
virtual void drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& vertexData, VkPrimitiveTopology primitiveTopology);
void drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& vertexData, const std::vector<tcu::Vec4>& coloDrata, VkPrimitiveTopology primitiveTopology);
void drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& positionData, const std::vector<tcu::Vec4>& colorData, VkPrimitiveTopology primitiveTopology,
VkImage image, VkImage resolvedImage, VkFramebuffer frameBuffer, const deUint32 renderSize, VkBuffer resultBuffer, const Allocation& resultBufferMemory);
virtual float getLineWidth (void) const;
virtual float getPointSize (void) const;
virtual bool getLineStippleDynamic (void) const { return false; }
virtual
const VkPipelineRasterizationStateCreateInfo* getRasterizationStateCreateInfo (void) const;
virtual
VkPipelineRasterizationLineStateCreateInfoEXT initLineRasterizationStateCreateInfo (void) const;
virtual
const VkPipelineRasterizationLineStateCreateInfoEXT* getLineRasterizationStateCreateInfo (void);
virtual
const VkPipelineColorBlendStateCreateInfo* getColorBlendStateCreateInfo (void) const;
const tcu::TextureFormat& getTextureFormat (void) const;
const deUint32 m_renderSize;
const VkSampleCountFlagBits m_sampleCount;
deUint32 m_subpixelBits;
const deBool m_multisampling;
const VkFormat m_imageFormat;
const tcu::TextureFormat m_textureFormat;
Move<VkCommandPool> m_commandPool;
Move<VkImage> m_image;
de::MovePtr<Allocation> m_imageMemory;
Move<VkImageView> m_imageView;
Move<VkImage> m_resolvedImage;
de::MovePtr<Allocation> m_resolvedImageMemory;
Move<VkImageView> m_resolvedImageView;
Move<VkRenderPass> m_renderPass;
Move<VkFramebuffer> m_frameBuffer;
Move<VkDescriptorPool> m_descriptorPool;
Move<VkDescriptorSet> m_descriptorSet;
Move<VkDescriptorSetLayout> m_descriptorSetLayout;
Move<VkBuffer> m_uniformBuffer;
de::MovePtr<Allocation> m_uniformBufferMemory;
const VkDeviceSize m_uniformBufferSize;
Move<VkPipelineLayout> m_pipelineLayout;
Move<VkShaderModule> m_vertexShaderModule;
Move<VkShaderModule> m_fragmentShaderModule;
Move<VkBuffer> m_resultBuffer;
de::MovePtr<Allocation> m_resultBufferMemory;
const VkDeviceSize m_resultBufferSize;
const deUint32 m_additionalRenderSize;
const VkDeviceSize m_additionalResultBufferSize;
VkPipelineRasterizationLineStateCreateInfoEXT m_lineRasterizationStateInfo;
private:
virtual int getIteration (void) const { TCU_THROW(InternalError, "Iteration undefined in the base class"); }
};
BaseRenderingTestInstance::BaseRenderingTestInstance (Context& context, VkSampleCountFlagBits sampleCount, deUint32 renderSize, VkFormat imageFormat, deUint32 additionalRenderSize)
: TestInstance (context)
, m_renderSize (renderSize)
, m_sampleCount (sampleCount)
, m_subpixelBits (context.getDeviceProperties().limits.subPixelPrecisionBits)
, m_multisampling (m_sampleCount != VK_SAMPLE_COUNT_1_BIT)
, m_imageFormat (imageFormat)
, m_textureFormat (vk::mapVkFormat(m_imageFormat))
, m_uniformBufferSize (sizeof(float))
, m_resultBufferSize (renderSize * renderSize * m_textureFormat.getPixelSize())
, m_additionalRenderSize(additionalRenderSize)
, m_additionalResultBufferSize(additionalRenderSize * additionalRenderSize * m_textureFormat.getPixelSize())
, m_lineRasterizationStateInfo ()
{
const DeviceInterface& vkd = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
Allocator& allocator = m_context.getDefaultAllocator();
DescriptorPoolBuilder descriptorPoolBuilder;
DescriptorSetLayoutBuilder descriptorSetLayoutBuilder;
// Command Pool
m_commandPool = createCommandPool(vkd, vkDevice, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
// Image
{
const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VkImageFormatProperties properties;
if ((m_context.getInstanceInterface().getPhysicalDeviceImageFormatProperties(m_context.getPhysicalDevice(),
m_imageFormat,
VK_IMAGE_TYPE_2D,
VK_IMAGE_TILING_OPTIMAL,
imageUsage,
0,
&properties) == VK_ERROR_FORMAT_NOT_SUPPORTED))
{
TCU_THROW(NotSupportedError, "Format not supported");
}
if ((properties.sampleCounts & m_sampleCount) != m_sampleCount)
{
TCU_THROW(NotSupportedError, "Format not supported");
}
const VkImageCreateInfo imageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_imageFormat, // VkFormat format;
{ m_renderSize, m_renderSize, 1u }, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
m_sampleCount, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
imageUsage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
};
m_image = vk::createImage(vkd, vkDevice, &imageCreateInfo, DE_NULL);
m_imageMemory = allocator.allocate(getImageMemoryRequirements(vkd, vkDevice, *m_image), MemoryRequirement::Any);
VK_CHECK(vkd.bindImageMemory(vkDevice, *m_image, m_imageMemory->getMemory(), m_imageMemory->getOffset()));
}
// Image View
{
const VkImageViewCreateInfo imageViewCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
*m_image, // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_imageFormat, // VkFormat format;
makeComponentMappingRGBA(), // VkComponentMapping components;
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 baseMipLevel;
1u, // deUint32 mipLevels;
0u, // deUint32 baseArrayLayer;
1u, // deUint32 arraySize;
}, // VkImageSubresourceRange subresourceRange;
};
m_imageView = vk::createImageView(vkd, vkDevice, &imageViewCreateInfo, DE_NULL);
}
if (m_multisampling)
{
{
// Resolved Image
const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkImageFormatProperties properties;
if ((m_context.getInstanceInterface().getPhysicalDeviceImageFormatProperties(m_context.getPhysicalDevice(),
m_imageFormat,
VK_IMAGE_TYPE_2D,
VK_IMAGE_TILING_OPTIMAL,
imageUsage,
0,
&properties) == VK_ERROR_FORMAT_NOT_SUPPORTED))
{
TCU_THROW(NotSupportedError, "Format not supported");
}
const VkImageCreateInfo imageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_imageFormat, // VkFormat format;
{ m_renderSize, m_renderSize, 1u }, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
imageUsage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
};
m_resolvedImage = vk::createImage(vkd, vkDevice, &imageCreateInfo, DE_NULL);
m_resolvedImageMemory = allocator.allocate(getImageMemoryRequirements(vkd, vkDevice, *m_resolvedImage), MemoryRequirement::Any);
VK_CHECK(vkd.bindImageMemory(vkDevice, *m_resolvedImage, m_resolvedImageMemory->getMemory(), m_resolvedImageMemory->getOffset()));
}
// Resolved Image View
{
const VkImageViewCreateInfo imageViewCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
*m_resolvedImage, // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_imageFormat, // VkFormat format;
makeComponentMappingRGBA(), // VkComponentMapping components;
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 baseMipLevel;
1u, // deUint32 mipLevels;
0u, // deUint32 baseArrayLayer;
1u, // deUint32 arraySize;
}, // VkImageSubresourceRange subresourceRange;
};
m_resolvedImageView = vk::createImageView(vkd, vkDevice, &imageViewCreateInfo, DE_NULL);
}
}
// Render Pass
{
const VkImageLayout imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
const VkAttachmentDescription attachmentDesc[] =
{
{
0u, // VkAttachmentDescriptionFlags flags;
m_imageFormat, // VkFormat format;
m_sampleCount, // VkSampleCountFlagBits samples;
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
imageLayout, // VkImageLayout initialLayout;
imageLayout, // VkImageLayout finalLayout;
},
{
0u, // VkAttachmentDescriptionFlags flags;
m_imageFormat, // VkFormat format;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp;
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
imageLayout, // VkImageLayout initialLayout;
imageLayout, // VkImageLayout finalLayout;
}
};
const VkAttachmentReference attachmentRef =
{
0u, // deUint32 attachment;
imageLayout, // VkImageLayout layout;
};
const VkAttachmentReference resolveAttachmentRef =
{
1u, // deUint32 attachment;
imageLayout, // VkImageLayout layout;
};
const VkSubpassDescription subpassDesc =
{
0u, // VkSubpassDescriptionFlags flags;
VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
0u, // deUint32 inputAttachmentCount;
DE_NULL, // const VkAttachmentReference* pInputAttachments;
1u, // deUint32 colorAttachmentCount;
&attachmentRef, // const VkAttachmentReference* pColorAttachments;
m_multisampling ? &resolveAttachmentRef : DE_NULL, // const VkAttachmentReference* pResolveAttachments;
DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment;
0u, // deUint32 preserveAttachmentCount;
DE_NULL, // const VkAttachmentReference* pPreserveAttachments;
};
const VkRenderPassCreateInfo renderPassCreateInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkRenderPassCreateFlags flags;
m_multisampling ? 2u : 1u, // deUint32 attachmentCount;
attachmentDesc, // const VkAttachmentDescription* pAttachments;
1u, // deUint32 subpassCount;
&subpassDesc, // const VkSubpassDescription* pSubpasses;
0u, // deUint32 dependencyCount;
DE_NULL, // const VkSubpassDependency* pDependencies;
};
m_renderPass = createRenderPass(vkd, vkDevice, &renderPassCreateInfo, DE_NULL);
}
// FrameBuffer
{
const VkImageView attachments[] =
{
*m_imageView,
*m_resolvedImageView
};
const VkFramebufferCreateInfo framebufferCreateInfo =
{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkFramebufferCreateFlags flags;
*m_renderPass, // VkRenderPass renderPass;
m_multisampling ? 2u : 1u, // deUint32 attachmentCount;
attachments, // const VkImageView* pAttachments;
m_renderSize, // deUint32 width;
m_renderSize, // deUint32 height;
1u, // deUint32 layers;
};
m_frameBuffer = createFramebuffer(vkd, vkDevice, &framebufferCreateInfo, DE_NULL);
}
// Uniform Buffer
{
const VkBufferCreateInfo bufferCreateInfo =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkBufferCreateFlags flags;
m_uniformBufferSize, // VkDeviceSize size;
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex // const deUint32* pQueueFamilyIndices;
};
m_uniformBuffer = createBuffer(vkd, vkDevice, &bufferCreateInfo);
m_uniformBufferMemory = allocator.allocate(getBufferMemoryRequirements(vkd, vkDevice, *m_uniformBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vkd.bindBufferMemory(vkDevice, *m_uniformBuffer, m_uniformBufferMemory->getMemory(), m_uniformBufferMemory->getOffset()));
}
// Descriptors
{
descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
m_descriptorPool = descriptorPoolBuilder.build(vkd, vkDevice, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
descriptorSetLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_ALL);
m_descriptorSetLayout = descriptorSetLayoutBuilder.build(vkd, vkDevice);
const VkDescriptorSetAllocateInfo descriptorSetParams =
{
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
DE_NULL,
*m_descriptorPool,
1u,
&m_descriptorSetLayout.get(),
};
m_descriptorSet = allocateDescriptorSet(vkd, vkDevice, &descriptorSetParams);
const VkDescriptorBufferInfo descriptorBufferInfo =
{
*m_uniformBuffer, // VkBuffer buffer;
0u, // VkDeviceSize offset;
VK_WHOLE_SIZE // VkDeviceSize range;
};
const VkWriteDescriptorSet writeDescritporSet =
{
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType;
DE_NULL, // const void* pNext;
*m_descriptorSet, // VkDescriptorSet destSet;
0, // deUint32 destBinding;
0, // deUint32 destArrayElement;
1u, // deUint32 count;
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // VkDescriptorType descriptorType;
DE_NULL, // const VkDescriptorImageInfo* pImageInfo;
&descriptorBufferInfo, // const VkDescriptorBufferInfo* pBufferInfo;
DE_NULL // const VkBufferView* pTexelBufferView;
};
vkd.updateDescriptorSets(vkDevice, 1u, &writeDescritporSet, 0u, DE_NULL);
}
// Pipeline Layout
{
const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineLayoutCreateFlags flags;
1u, // deUint32 descriptorSetCount;
&m_descriptorSetLayout.get(), // const VkDescriptorSetLayout* pSetLayouts;
0u, // deUint32 pushConstantRangeCount;
DE_NULL // const VkPushConstantRange* pPushConstantRanges;
};
m_pipelineLayout = createPipelineLayout(vkd, vkDevice, &pipelineLayoutCreateInfo);
}
// Shaders
{
m_vertexShaderModule = createShaderModule(vkd, vkDevice, m_context.getBinaryCollection().get("vertext_shader"), 0);
m_fragmentShaderModule = createShaderModule(vkd, vkDevice, m_context.getBinaryCollection().get("fragment_shader"), 0);
}
// Result Buffer
{
const VkBufferCreateInfo bufferCreateInfo =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkBufferCreateFlags flags;
m_resultBufferSize, // VkDeviceSize size;
VK_BUFFER_USAGE_TRANSFER_DST_BIT, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex // const deUint32* pQueueFamilyIndices;
};
m_resultBuffer = createBuffer(vkd, vkDevice, &bufferCreateInfo);
m_resultBufferMemory = allocator.allocate(getBufferMemoryRequirements(vkd, vkDevice, *m_resultBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vkd.bindBufferMemory(vkDevice, *m_resultBuffer, m_resultBufferMemory->getMemory(), m_resultBufferMemory->getOffset()));
}
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Sample count = " << getSampleCountFlagsStr(m_sampleCount) << tcu::TestLog::EndMessage;
m_context.getTestContext().getLog() << tcu::TestLog::Message << "SUBPIXEL_BITS = " << m_subpixelBits << tcu::TestLog::EndMessage;
}
BaseRenderingTestInstance::~BaseRenderingTestInstance (void)
{
}
void BaseRenderingTestInstance::addImageTransitionBarrier(VkCommandBuffer commandBuffer, VkImage image, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageLayout oldLayout, VkImageLayout newLayout) const
{
const DeviceInterface& vkd = m_context.getDeviceInterface();
const VkImageSubresourceRange subResourcerange =
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0, // deUint32 baseMipLevel;
1, // deUint32 levelCount;
0, // deUint32 baseArrayLayer;
1 // deUint32 layerCount;
};
const VkImageMemoryBarrier imageBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
srcAccessMask, // VkAccessFlags srcAccessMask;
dstAccessMask, // VkAccessFlags dstAccessMask;
oldLayout, // VkImageLayout oldLayout;
newLayout, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
image, // VkImage image;
subResourcerange // VkImageSubresourceRange subresourceRange;
};
vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &imageBarrier);
}
void BaseRenderingTestInstance::drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& vertexData, VkPrimitiveTopology primitiveTopology)
{
// default to color white
const std::vector<tcu::Vec4> colorData(vertexData.size(), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
drawPrimitives(result, vertexData, colorData, primitiveTopology);
}
void BaseRenderingTestInstance::drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& positionData, const std::vector<tcu::Vec4>& colorData, VkPrimitiveTopology primitiveTopology)
{
drawPrimitives(result, positionData, colorData, primitiveTopology, *m_image, *m_resolvedImage, *m_frameBuffer, m_renderSize, *m_resultBuffer, *m_resultBufferMemory);
}
void BaseRenderingTestInstance::drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& positionData, const std::vector<tcu::Vec4>& colorData, VkPrimitiveTopology primitiveTopology,
VkImage image, VkImage resolvedImage, VkFramebuffer frameBuffer, const deUint32 renderSize, VkBuffer resultBuffer, const Allocation& resultBufferMemory)
{
const DeviceInterface& vkd = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
Allocator& allocator = m_context.getDefaultAllocator();
const size_t attributeBatchSize = positionData.size() * sizeof(tcu::Vec4);
Move<VkCommandBuffer> commandBuffer;
Move<VkPipeline> graphicsPipeline;
Move<VkBuffer> vertexBuffer;
de::MovePtr<Allocation> vertexBufferMemory;
const VkPhysicalDeviceProperties properties = m_context.getDeviceProperties();
if (attributeBatchSize > properties.limits.maxVertexInputAttributeOffset)
{
std::stringstream message;
message << "Larger vertex input attribute offset is needed (" << attributeBatchSize << ") than the available maximum (" << properties.limits.maxVertexInputAttributeOffset << ").";
TCU_THROW(NotSupportedError, message.str().c_str());
}
// Create Graphics Pipeline
{
const VkVertexInputBindingDescription vertexInputBindingDescription =
{
0u, // deUint32 binding;
sizeof(tcu::Vec4), // deUint32 strideInBytes;
VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate stepRate;
};
const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] =
{
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
},
{
1u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
(deUint32)attributeBatchSize // deUint32 offsetInBytes;
}
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0, // VkPipelineVertexInputStateCreateFlags flags;
1u, // deUint32 bindingCount;
&vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
2u, // deUint32 attributeCount;
vertexInputAttributeDescriptions // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
const std::vector<VkViewport> viewports (1, makeViewport(tcu::UVec2(renderSize)));
const std::vector<VkRect2D> scissors (1, makeRect2D(tcu::UVec2(renderSize)));
const VkPipelineMultisampleStateCreateInfo multisampleStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineMultisampleStateCreateFlags flags;
m_sampleCount, // VkSampleCountFlagBits rasterizationSamples;
VK_FALSE, // VkBool32 sampleShadingEnable;
0.0f, // float minSampleShading;
DE_NULL, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable;
VK_FALSE // VkBool32 alphaToOneEnable;
};
VkPipelineRasterizationStateCreateInfo rasterizationStateInfo = *getRasterizationStateCreateInfo();
const VkPipelineRasterizationLineStateCreateInfoEXT* lineRasterizationStateInfo = getLineRasterizationStateCreateInfo();
if (lineRasterizationStateInfo != DE_NULL)
appendStructurePtrToVulkanChain(&rasterizationStateInfo.pNext, lineRasterizationStateInfo);
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
0u, // VkPipelineDynamicStateCreateFlags flags
0u, // deUint32 dynamicStateCount
DE_NULL // const VkDynamicState* pDynamicStates
};
VkDynamicState dynamicState = VK_DYNAMIC_STATE_LINE_STIPPLE_EXT;
if (getLineStippleDynamic())
{
dynamicStateCreateInfo.dynamicStateCount = 1;
dynamicStateCreateInfo.pDynamicStates = &dynamicState;
}
graphicsPipeline = makeGraphicsPipeline(vkd, // const DeviceInterface& vk
vkDevice, // const VkDevice device
*m_pipelineLayout, // const VkPipelineLayout pipelineLayout
*m_vertexShaderModule, // const VkShaderModule vertexShaderModule
DE_NULL, // const VkShaderModule tessellationControlShaderModule
DE_NULL, // const VkShaderModule tessellationEvalShaderModule
DE_NULL, // const VkShaderModule geometryShaderModule
*m_fragmentShaderModule, // const VkShaderModule fragmentShaderModule
*m_renderPass, // const VkRenderPass renderPass
viewports, // const std::vector<VkViewport>& viewports
scissors, // const std::vector<VkRect2D>& scissors
primitiveTopology, // const VkPrimitiveTopology topology
0u, // const deUint32 subpass
0u, // const deUint32 patchControlPoints
&vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
&rasterizationStateInfo, // const VkPipelineRasterizationStateCreateInfo* rasterizationStateCreateInfo
&multisampleStateParams, // const VkPipelineMultisampleStateCreateInfo* multisampleStateCreateInfo
DE_NULL, // const VkPipelineDepthStencilStateCreateInfo* depthStencilStateCreateInfo,
getColorBlendStateCreateInfo(), // const VkPipelineColorBlendStateCreateInfo* colorBlendStateCreateInfo,
&dynamicStateCreateInfo); // const VkPipelineDynamicStateCreateInfo* dynamicStateCreateInfo
}
// Create Vertex Buffer
{
const VkBufferCreateInfo vertexBufferParams =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkBufferCreateFlags flags;
attributeBatchSize * 2, // VkDeviceSize size;
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyCount;
&queueFamilyIndex // const deUint32* pQueueFamilyIndices;
};
vertexBuffer = createBuffer(vkd, vkDevice, &vertexBufferParams);
vertexBufferMemory = allocator.allocate(getBufferMemoryRequirements(vkd, vkDevice, *vertexBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vkd.bindBufferMemory(vkDevice, *vertexBuffer, vertexBufferMemory->getMemory(), vertexBufferMemory->getOffset()));
// Load vertices into vertex buffer
deMemcpy(vertexBufferMemory->getHostPtr(), positionData.data(), attributeBatchSize);
deMemcpy(reinterpret_cast<deUint8*>(vertexBufferMemory->getHostPtr()) + attributeBatchSize, colorData.data(), attributeBatchSize);
flushAlloc(vkd, vkDevice, *vertexBufferMemory);
}
// Create Command Buffer
commandBuffer = allocateCommandBuffer(vkd, vkDevice, *m_commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
// Begin Command Buffer
beginCommandBuffer(vkd, *commandBuffer);
addImageTransitionBarrier(*commandBuffer, image,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // VkPipelineStageFlags srcStageMask
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // VkPipelineStageFlags dstStageMask
0, // VkAccessFlags srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // VkImageLayout newLayout;
if (m_multisampling) {
addImageTransitionBarrier(*commandBuffer, resolvedImage,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // VkPipelineStageFlags srcStageMask
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // VkPipelineStageFlags dstStageMask
0, // VkAccessFlags srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // VkImageLayout newLayout;
}
// Begin Render Pass
beginRenderPass(vkd, *commandBuffer, *m_renderPass, frameBuffer, vk::makeRect2D(0, 0, renderSize, renderSize), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
const VkDeviceSize vertexBufferOffset = 0;
vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
vkd.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayout, 0u, 1, &m_descriptorSet.get(), 0u, DE_NULL);
vkd.cmdBindVertexBuffers(*commandBuffer, 0, 1, &vertexBuffer.get(), &vertexBufferOffset);
if (getLineStippleDynamic())
vkd.cmdSetLineStippleEXT(*commandBuffer, lineStippleFactor, lineStipplePattern);
vkd.cmdDraw(*commandBuffer, (deUint32)positionData.size(), 1, 0, 0);
endRenderPass(vkd, *commandBuffer);
// Copy Image
copyImageToBuffer(vkd, *commandBuffer, m_multisampling ? resolvedImage : image, resultBuffer, tcu::IVec2(renderSize, renderSize));
endCommandBuffer(vkd, *commandBuffer);
// Set Point Size
{
float pointSize = getPointSize();
deMemcpy(m_uniformBufferMemory->getHostPtr(), &pointSize, (size_t)m_uniformBufferSize);
flushAlloc(vkd, vkDevice, *m_uniformBufferMemory);
}
// Submit
submitCommandsAndWait(vkd, vkDevice, queue, commandBuffer.get());
invalidateAlloc(vkd, vkDevice, resultBufferMemory);
tcu::copy(result.getAccess(), tcu::ConstPixelBufferAccess(m_textureFormat, tcu::IVec3(renderSize, renderSize, 1), resultBufferMemory.getHostPtr()));
}
float BaseRenderingTestInstance::getLineWidth (void) const
{
return 1.0f;
}
float BaseRenderingTestInstance::getPointSize (void) const
{
return 1.0f;
}
const VkPipelineRasterizationStateCreateInfo* BaseRenderingTestInstance::getRasterizationStateCreateInfo (void) const
{
static VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0, // VkPipelineRasterizationStateCreateFlags flags;
false, // VkBool32 depthClipEnable;
false, // VkBool32 rasterizerDiscardEnable;
VK_POLYGON_MODE_FILL, // VkFillMode fillMode;
VK_CULL_MODE_NONE, // VkCullMode cullMode;
VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
VK_FALSE, // VkBool32 depthBiasEnable;
0.0f, // float depthBias;
0.0f, // float depthBiasClamp;
0.0f, // float slopeScaledDepthBias;
getLineWidth(), // float lineWidth;
};
rasterizationStateCreateInfo.lineWidth = getLineWidth();
return &rasterizationStateCreateInfo;
}
VkPipelineRasterizationLineStateCreateInfoEXT BaseRenderingTestInstance::initLineRasterizationStateCreateInfo (void) const
{
VkPipelineRasterizationLineStateCreateInfoEXT lineRasterizationStateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT, // VkLineRasterizationModeEXT lineRasterizationMode;
VK_FALSE, // VkBool32 stippledLineEnable;
1, // uint32_t lineStippleFactor;
0xFFFF, // uint16_t lineStipplePattern;
};
return lineRasterizationStateInfo;
}
const VkPipelineRasterizationLineStateCreateInfoEXT* BaseRenderingTestInstance::getLineRasterizationStateCreateInfo (void)
{
if (m_lineRasterizationStateInfo.sType != VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT)
m_lineRasterizationStateInfo = initLineRasterizationStateCreateInfo();
return &m_lineRasterizationStateInfo;
}
const VkPipelineColorBlendStateCreateInfo* BaseRenderingTestInstance::getColorBlendStateCreateInfo (void) const
{
static const VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
{
false, // VkBool32 blendEnable;
VK_BLEND_FACTOR_ONE, // VkBlend srcBlendColor;
VK_BLEND_FACTOR_ZERO, // VkBlend destBlendColor;
VK_BLEND_OP_ADD, // VkBlendOp blendOpColor;
VK_BLEND_FACTOR_ONE, // VkBlend srcBlendAlpha;
VK_BLEND_FACTOR_ZERO, // VkBlend destBlendAlpha;
VK_BLEND_OP_ADD, // VkBlendOp blendOpAlpha;
(VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT) // VkChannelFlags channelWriteMask;
};
static const VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0, // VkPipelineColorBlendStateCreateFlags flags;
false, // VkBool32 logicOpEnable;
VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
1u, // deUint32 attachmentCount;
&colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
{ 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConst[4];
};
return &colorBlendStateParams;
}
const tcu::TextureFormat& BaseRenderingTestInstance::getTextureFormat (void) const
{
return m_textureFormat;
}
class BaseTriangleTestInstance : public BaseRenderingTestInstance
{
public:
BaseTriangleTestInstance (Context& context, VkPrimitiveTopology primitiveTopology, VkSampleCountFlagBits sampleCount, deUint32 renderSize = RESOLUTION_POT);
virtual tcu::TestStatus iterate (void);
protected:
int getIteration (void) const { return m_iteration; }
int getIterationCount (void) const { return m_iterationCount; }
private:
virtual void generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles) = DE_NULL;
virtual bool compareAndVerify (std::vector<TriangleSceneSpec::SceneTriangle>& triangles,
tcu::Surface& resultImage,
std::vector<tcu::Vec4>& drawBuffer);
int m_iteration;
const int m_iterationCount;
VkPrimitiveTopology m_primitiveTopology;
bool m_allIterationsPassed;
};
BaseTriangleTestInstance::BaseTriangleTestInstance (Context& context, VkPrimitiveTopology primitiveTopology, VkSampleCountFlagBits sampleCount, deUint32 renderSize)
: BaseRenderingTestInstance (context, sampleCount, renderSize)
, m_iteration (0)
, m_iterationCount (3)
, m_primitiveTopology (primitiveTopology)
, m_allIterationsPassed (true)
{
}
tcu::TestStatus BaseTriangleTestInstance::iterate (void)
{
const std::string iterationDescription = "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
const tcu::ScopedLogSection section (m_context.getTestContext().getLog(), iterationDescription, iterationDescription);
tcu::Surface resultImage (m_renderSize, m_renderSize);
std::vector<tcu::Vec4> drawBuffer;
std::vector<TriangleSceneSpec::SceneTriangle> triangles;
generateTriangles(m_iteration, drawBuffer, triangles);
// draw image
drawPrimitives(resultImage, drawBuffer, m_primitiveTopology);
// compare
{
const bool compareOk = compareAndVerify(triangles, resultImage, drawBuffer);
if (!compareOk)
m_allIterationsPassed = false;
}
// result
if (++m_iteration == m_iterationCount)
{
if (m_allIterationsPassed)
return tcu::TestStatus::pass("Pass");
else
return tcu::TestStatus::fail("Incorrect rasterization");
}
else
return tcu::TestStatus::incomplete();
}
bool BaseTriangleTestInstance::compareAndVerify (std::vector<TriangleSceneSpec::SceneTriangle>& triangles, tcu::Surface& resultImage, std::vector<tcu::Vec4>&)
{
RasterizationArguments args;
TriangleSceneSpec scene;
tcu::IVec4 colorBits = tcu::getTextureFormatBitDepth(getTextureFormat());
args.numSamples = m_multisampling ? 1 : 0;
args.subpixelBits = m_subpixelBits;
args.redBits = colorBits[0];
args.greenBits = colorBits[1];
args.blueBits = colorBits[2];
scene.triangles.swap(triangles);
return verifyTriangleGroupRasterization(resultImage, scene, args, m_context.getTestContext().getLog());
}
class BaseLineTestInstance : public BaseRenderingTestInstance
{
public:
BaseLineTestInstance (Context& context,
VkPrimitiveTopology primitiveTopology,
PrimitiveWideness wideness,
PrimitiveStrictness strictness,
VkSampleCountFlagBits sampleCount,
LineStipple stipple,
VkLineRasterizationModeEXT lineRasterizationMode,
LineStippleFactorCase stippleFactor,
const deUint32 additionalRenderSize = 0,
const deUint32 renderSize = RESOLUTION_POT,
const float narrowLineWidth = 1.0f);
virtual tcu::TestStatus iterate (void);
virtual float getLineWidth (void) const;
bool getLineStippleEnable (void) const { return m_stipple != LINESTIPPLE_DISABLED; }
virtual bool getLineStippleDynamic (void) const { return m_stipple == LINESTIPPLE_DYNAMIC; }
virtual
VkPipelineRasterizationLineStateCreateInfoEXT initLineRasterizationStateCreateInfo (void) const;
virtual
const VkPipelineRasterizationLineStateCreateInfoEXT* getLineRasterizationStateCreateInfo (void);
protected:
int getIteration (void) const { return m_iteration; }
int getIterationCount (void) const { return m_iterationCount; }
private:
virtual void generateLines (int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines) = DE_NULL;
virtual bool compareAndVerify (std::vector<LineSceneSpec::SceneLine>& lines,
tcu::Surface& resultImage,
std::vector<tcu::Vec4>& drawBuffer);
bool resultHasAlpha (tcu::Surface& result);
int m_iteration;
const int m_iterationCount;
VkPrimitiveTopology m_primitiveTopology;
const PrimitiveWideness m_primitiveWideness;
const PrimitiveStrictness m_primitiveStrictness;
bool m_allIterationsPassed;
bool m_qualityWarning;
float m_maxLineWidth;
std::vector<float> m_lineWidths;
LineStipple m_stipple;
VkLineRasterizationModeEXT m_lineRasterizationMode;
LineStippleFactorCase m_stippleFactor;
Move<VkImage> m_additionalImage;
de::MovePtr<Allocation> m_additionalImageMemory;
Move<VkImageView> m_additionalImageView;
Move<VkImage> m_additionalResolvedImage;
de::MovePtr<Allocation> m_additionalResolvedImageMemory;
Move<VkImageView> m_additionalResolvedImageView;
Move<VkFramebuffer> m_additionalFrameBuffer;
Move<VkBuffer> m_additionalResultBuffer;
de::MovePtr<Allocation> m_additionalResultBufferMemory;
};
BaseLineTestInstance::BaseLineTestInstance (Context& context,
VkPrimitiveTopology primitiveTopology,
PrimitiveWideness wideness,
PrimitiveStrictness strictness,
VkSampleCountFlagBits sampleCount,
LineStipple stipple,
VkLineRasterizationModeEXT lineRasterizationMode,
LineStippleFactorCase stippleFactor,
const deUint32 additionalRenderSize,
const deUint32 renderSize,
const float narrowLineWidth)
: BaseRenderingTestInstance (context, sampleCount, renderSize, VK_FORMAT_R8G8B8A8_UNORM, additionalRenderSize)
, m_iteration (0)
, m_iterationCount (3)
, m_primitiveTopology (primitiveTopology)
, m_primitiveWideness (wideness)
, m_primitiveStrictness (strictness)
, m_allIterationsPassed (true)
, m_qualityWarning (false)
, m_maxLineWidth (1.0f)
, m_stipple (stipple)
, m_lineRasterizationMode (lineRasterizationMode)
, m_stippleFactor (stippleFactor)
{
DE_ASSERT(m_primitiveWideness < PRIMITIVEWIDENESS_LAST);
if (m_lineRasterizationMode != VK_LINE_RASTERIZATION_MODE_EXT_LAST)
{
if (context.isDeviceFunctionalitySupported("VK_EXT_line_rasterization"))
{
VkPhysicalDeviceLineRasterizationPropertiesEXT lineRasterizationProperties =
{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT, // VkStructureType sType;
DE_NULL, // void* pNext;
0u, // deUint32 lineSubPixelPrecisionBits;
};
VkPhysicalDeviceProperties2 deviceProperties2;
deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
deviceProperties2.pNext = &lineRasterizationProperties;
context.getInstanceInterface().getPhysicalDeviceProperties2(m_context.getPhysicalDevice(), &deviceProperties2);
m_subpixelBits = lineRasterizationProperties.lineSubPixelPrecisionBits;
}
}
// create line widths
if (m_primitiveWideness == PRIMITIVEWIDENESS_NARROW)
{
m_lineWidths.resize(m_iterationCount, narrowLineWidth);
// Bump up m_maxLineWidth for conservative rasterization
if (narrowLineWidth > m_maxLineWidth)
m_maxLineWidth = narrowLineWidth;
}
else if (m_primitiveWideness == PRIMITIVEWIDENESS_WIDE)
{
const float* range = context.getDeviceProperties().limits.lineWidthRange;
m_context.getTestContext().getLog() << tcu::TestLog::Message << "ALIASED_LINE_WIDTH_RANGE = [" << range[0] << ", " << range[1] << "]" << tcu::TestLog::EndMessage;
DE_ASSERT(range[1] > 1.0f);
// set hand picked sizes
m_lineWidths.push_back(5.0f);
m_lineWidths.push_back(10.0f);
// Do not pick line width with 0.5 fractional value as rounding direction is not defined.
if (deFloatFrac(range[1]) == 0.5f)
{
m_lineWidths.push_back(range[1] - context.getDeviceProperties().limits.lineWidthGranularity);
}
else
{
m_lineWidths.push_back(range[1]);
}
DE_ASSERT((int)m_lineWidths.size() == m_iterationCount);
m_maxLineWidth = range[1];
}
else
DE_ASSERT(false);
// Create image, image view and frame buffer for testing at an additional resolution if required.
if (m_additionalRenderSize != 0)
{
const DeviceInterface& vkd = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
Allocator& allocator = m_context.getDefaultAllocator();
DescriptorPoolBuilder descriptorPoolBuilder;
DescriptorSetLayoutBuilder descriptorSetLayoutBuilder;
{
const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
const VkImageCreateInfo imageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_imageFormat, // VkFormat format;
{ m_additionalRenderSize, m_additionalRenderSize, 1u }, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
m_sampleCount, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
imageUsage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
};
m_additionalImage = vk::createImage(vkd, vkDevice, &imageCreateInfo, DE_NULL);
m_additionalImageMemory = allocator.allocate(getImageMemoryRequirements(vkd, vkDevice, *m_additionalImage), MemoryRequirement::Any);
VK_CHECK(vkd.bindImageMemory(vkDevice, *m_additionalImage, m_additionalImageMemory->getMemory(), m_additionalImageMemory->getOffset()));
}
// Image View
{
const VkImageViewCreateInfo imageViewCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
*m_additionalImage, // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_imageFormat, // VkFormat format;
makeComponentMappingRGBA(), // VkComponentMapping components;
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 baseMipLevel;
1u, // deUint32 mipLevels;
0u, // deUint32 baseArrayLayer;
1u, // deUint32 arraySize;
}, // VkImageSubresourceRange subresourceRange;
};
m_additionalImageView = vk::createImageView(vkd, vkDevice, &imageViewCreateInfo, DE_NULL);
}
if (m_multisampling)
{
{
const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
const VkImageCreateInfo imageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_imageFormat, // VkFormat format;
{ m_additionalRenderSize, m_additionalRenderSize, 1u }, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
imageUsage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
};
m_additionalResolvedImage = vk::createImage(vkd, vkDevice, &imageCreateInfo, DE_NULL);
m_additionalResolvedImageMemory = allocator.allocate(getImageMemoryRequirements(vkd, vkDevice, *m_additionalResolvedImage), MemoryRequirement::Any);
VK_CHECK(vkd.bindImageMemory(vkDevice, *m_additionalResolvedImage, m_additionalResolvedImageMemory->getMemory(), m_additionalResolvedImageMemory->getOffset()));
}
// Image view
{
const VkImageViewCreateInfo imageViewCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
*m_additionalResolvedImage, // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_imageFormat, // VkFormat format;
makeComponentMappingRGBA(), // VkComponentMapping components;
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 baseMipLevel;
1u, // deUint32 mipLevels;
0u, // deUint32 baseArrayLayer;
1u, // deUint32 arraySize;
}, // VkImageSubresourceRange subresourceRange;
};
m_additionalResolvedImageView = vk::createImageView(vkd, vkDevice, &imageViewCreateInfo, DE_NULL);
}
}
{
const VkImageView attachments[] =
{
*m_additionalImageView,
*m_additionalResolvedImageView
};
const VkFramebufferCreateInfo framebufferCreateInfo =
{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkFramebufferCreateFlags flags;
*m_renderPass, // VkRenderPass renderPass;
m_multisampling ? 2u : 1u, // deUint32 attachmentCount;
attachments, // const VkImageView* pAttachments;
m_additionalRenderSize, // deUint32 width;
m_additionalRenderSize, // deUint32 height;
1u, // deUint32 layers;
};
m_additionalFrameBuffer = createFramebuffer(vkd, vkDevice, &framebufferCreateInfo, DE_NULL);
}
// Framebuffer
{
const VkBufferCreateInfo bufferCreateInfo =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkBufferCreateFlags flags;
m_additionalResultBufferSize, // VkDeviceSize size;
VK_BUFFER_USAGE_TRANSFER_DST_BIT, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex // const deUint32* pQueueFamilyIndices;
};
m_additionalResultBuffer = createBuffer(vkd, vkDevice, &bufferCreateInfo);
m_additionalResultBufferMemory = allocator.allocate(getBufferMemoryRequirements(vkd, vkDevice, *m_additionalResultBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vkd.bindBufferMemory(vkDevice, *m_additionalResultBuffer, m_additionalResultBufferMemory->getMemory(), m_additionalResultBufferMemory->getOffset()));
}
}
}
bool BaseLineTestInstance::resultHasAlpha(tcu::Surface& resultImage)
{
bool hasAlpha = false;
for (int y = 0; y < resultImage.getHeight() && !hasAlpha; ++y)
for (int x = 0; x < resultImage.getWidth(); ++x)
{
const tcu::RGBA color = resultImage.getPixel(x, y);
if (color.getAlpha() > 0 && color.getAlpha() < 0xFF)
{
hasAlpha = true;
break;
}
}
return hasAlpha;
}
tcu::TestStatus BaseLineTestInstance::iterate (void)
{
const std::string iterationDescription = "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
const tcu::ScopedLogSection section (m_context.getTestContext().getLog(), iterationDescription, iterationDescription);
const float lineWidth = getLineWidth();
tcu::Surface resultImage (m_renderSize, m_renderSize);
std::vector<tcu::Vec4> drawBuffer;
std::vector<LineSceneSpec::SceneLine> lines;
// supported?
if (lineWidth <= m_maxLineWidth)
{
// gen data
generateLines(m_iteration, drawBuffer, lines);
// draw image
drawPrimitives(resultImage, drawBuffer, m_primitiveTopology);
// compare
{
const bool compareOk = compareAndVerify(lines, resultImage, drawBuffer);
if (!compareOk)
m_allIterationsPassed = false;
}
}
else
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Line width " << lineWidth << " not supported, skipping iteration." << tcu::TestLog::EndMessage;
// result
if (++m_iteration == m_iterationCount)
{
if (!m_allIterationsPassed)
return tcu::TestStatus::fail("Incorrect rasterization");
else if (m_qualityWarning)
return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality line rasterization");
else
return tcu::TestStatus::pass("Pass");
}
else
return tcu::TestStatus::incomplete();
}
bool BaseLineTestInstance::compareAndVerify (std::vector<LineSceneSpec::SceneLine>& lines, tcu::Surface& resultImage, std::vector<tcu::Vec4>& drawBuffer)
{
const float lineWidth = getLineWidth();
bool result = true;
tcu::Surface additionalResultImage (m_additionalRenderSize, m_additionalRenderSize);
RasterizationArguments args;
LineSceneSpec scene;
tcu::IVec4 colorBits = tcu::getTextureFormatBitDepth(getTextureFormat());
bool strict = m_primitiveStrictness == PRIMITIVESTRICTNESS_STRICT;
args.numSamples = m_multisampling ? 1 : 0;
args.subpixelBits = m_subpixelBits;
args.redBits = colorBits[0];
args.greenBits = colorBits[1];
args.blueBits = colorBits[2];
scene.lines.swap(lines);
scene.lineWidth = lineWidth;
scene.stippleEnable = getLineStippleEnable();
scene.stippleFactor = getLineStippleEnable() ? lineStippleFactor : 1;
scene.stipplePattern = getLineStippleEnable() ? lineStipplePattern : 0xFFFF;
scene.isStrip = m_primitiveTopology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
scene.isSmooth = m_lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT;
scene.isRectangular = m_lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT ||
m_lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT;
// Choose verification mode. Smooth lines assume mostly over-rasterization (bloated lines with a falloff).
// Stippled lines lose some precision across segments in a strip, so need a weaker threshold than normal
// lines. For simple cases, check for an exact match (STRICT).
if (scene.isSmooth)
scene.verificationMode = tcu::VERIFICATIONMODE_SMOOTH;
else if (scene.stippleEnable)
scene.verificationMode = tcu::VERIFICATIONMODE_WEAKER;
else
scene.verificationMode = tcu::VERIFICATIONMODE_STRICT;
if (m_lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT)
{
// bresenham is "no AA" in GL, so set numSamples to zero.
args.numSamples = 0;
if (!verifyLineGroupRasterization(resultImage, scene, args, m_context.getTestContext().getLog()))
result = false;
}
else
{
if (scene.isSmooth)
{
// Smooth lines get the fractional coverage multiplied into the alpha component,
// so do a sanity check to validate that there is at least one pixel in the image
// with a fractional opacity.
bool hasAlpha = resultHasAlpha(resultImage);
if (!hasAlpha)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Missing alpha transparency (failed)." << tcu::TestLog::EndMessage;
result = false;
}
}
if (!verifyRelaxedLineGroupRasterization(resultImage, scene, args, m_context.getTestContext().getLog(), (0 == m_multisampling), strict))
{
// Retry with weaker verification. If it passes, consider it a quality warning.
scene.verificationMode = tcu::VERIFICATIONMODE_WEAKER;
if (!verifyRelaxedLineGroupRasterization(resultImage, scene, args, m_context.getTestContext().getLog(), false, strict))
result = false;
else
m_qualityWarning = true;
}
if (m_additionalRenderSize != 0)
{
const std::vector<tcu::Vec4> colorData(drawBuffer.size(), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
if (scene.isSmooth)
scene.verificationMode = tcu::VERIFICATIONMODE_SMOOTH;
else if (scene.stippleEnable)
scene.verificationMode = tcu::VERIFICATIONMODE_WEAKER;
else
scene.verificationMode = tcu::VERIFICATIONMODE_STRICT;
drawPrimitives(additionalResultImage, drawBuffer, colorData, m_primitiveTopology, *m_additionalImage, *m_additionalResolvedImage, *m_additionalFrameBuffer, m_additionalRenderSize, *m_additionalResultBuffer, *m_additionalResultBufferMemory);
// Compare
if (!verifyRelaxedLineGroupRasterization(additionalResultImage, scene, args, m_context.getTestContext().getLog(), (0 == m_multisampling), strict))
{
if (strict)
{
result = false;
}
else
{
// Retry with weaker verification. If it passes, consider it a quality warning.
scene.verificationMode = tcu::VERIFICATIONMODE_WEAKER;
if (!verifyRelaxedLineGroupRasterization(resultImage, scene, args, m_context.getTestContext().getLog(), (0 == m_multisampling), strict))
result = false;
else
m_qualityWarning = true;
}
}
}
}
return result;
}
float BaseLineTestInstance::getLineWidth (void) const
{
return m_lineWidths[m_iteration];
}
VkPipelineRasterizationLineStateCreateInfoEXT BaseLineTestInstance::initLineRasterizationStateCreateInfo (void) const
{
VkPipelineRasterizationLineStateCreateInfoEXT lineRasterizationStateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
m_lineRasterizationMode, // VkLineRasterizationModeEXT lineRasterizationMode;
getLineStippleEnable() ? VK_TRUE : VK_FALSE, // VkBool32 stippledLineEnable;
1, // uint32_t lineStippleFactor;
0xFFFF, // uint16_t lineStipplePattern;
};
if (m_stipple == LINESTIPPLE_STATIC)
{
lineRasterizationStateInfo.lineStippleFactor = lineStippleFactor;
lineRasterizationStateInfo.lineStipplePattern = lineStipplePattern;
}
else if (m_stipple == LINESTIPPLE_DISABLED)
{
if (m_stippleFactor == LineStippleFactorCase::ZERO)
lineRasterizationStateInfo.lineStippleFactor = 0u;
else if (m_stippleFactor == LineStippleFactorCase::LARGE)
lineRasterizationStateInfo.lineStippleFactor = 0xFEDCBA98u;
}
return lineRasterizationStateInfo;
}
const VkPipelineRasterizationLineStateCreateInfoEXT* BaseLineTestInstance::getLineRasterizationStateCreateInfo (void)
{
if (m_lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_EXT_LAST)
return DE_NULL;
if (m_lineRasterizationStateInfo.sType != VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT)
m_lineRasterizationStateInfo = initLineRasterizationStateCreateInfo();
return &m_lineRasterizationStateInfo;
}
class PointTestInstance : public BaseRenderingTestInstance
{
public:
PointTestInstance (Context& context,
PrimitiveWideness wideness,
PrimitiveStrictness strictness, // ignored
VkSampleCountFlagBits sampleCount,
LineStipple stipple, // ignored
VkLineRasterizationModeEXT lineRasterizationMode, // ignored
LineStippleFactorCase stippleFactor, // ignored
deUint32 additionalRenderSize, // ignored
deUint32 renderSize = RESOLUTION_POT,
float pointSizeNarrow = 1.0f);
virtual tcu::TestStatus iterate (void);
virtual float getPointSize (void) const;
protected:
int getIteration (void) const { return m_iteration; }
int getIterationCount (void) const { return m_iterationCount; }
private:
virtual void generatePoints (int iteration, std::vector<tcu::Vec4>& outData, std::vector<PointSceneSpec::ScenePoint>& outPoints);
virtual bool compareAndVerify (std::vector<PointSceneSpec::ScenePoint>& points,
tcu::Surface& resultImage,
std::vector<tcu::Vec4>& drawBuffer);
int m_iteration;
const int m_iterationCount;
const PrimitiveWideness m_primitiveWideness;
bool m_allIterationsPassed;
float m_maxPointSize;
std::vector<float> m_pointSizes;
};
PointTestInstance::PointTestInstance (Context& context,
PrimitiveWideness wideness,
PrimitiveStrictness strictness,
VkSampleCountFlagBits sampleCount,
LineStipple stipple,
VkLineRasterizationModeEXT lineRasterizationMode,
LineStippleFactorCase stippleFactor,
deUint32 additionalRenderSize,
deUint32 renderSize,
float pointSizeNarrow)
: BaseRenderingTestInstance (context, sampleCount, renderSize)
, m_iteration (0)
, m_iterationCount (3)
, m_primitiveWideness (wideness)
, m_allIterationsPassed (true)
, m_maxPointSize (pointSizeNarrow)
{
DE_UNREF(strictness);
DE_UNREF(stipple);
DE_UNREF(lineRasterizationMode);
DE_UNREF(stippleFactor);
DE_UNREF(additionalRenderSize);
// create point sizes
if (m_primitiveWideness == PRIMITIVEWIDENESS_NARROW)
{
m_pointSizes.resize(m_iterationCount, pointSizeNarrow);
}
else if (m_primitiveWideness == PRIMITIVEWIDENESS_WIDE)
{
const float* range = context.getDeviceProperties().limits.pointSizeRange;
m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_ALIASED_POINT_SIZE_RANGE = [" << range[0] << ", " << range[1] << "]" << tcu::TestLog::EndMessage;
DE_ASSERT(range[1] > 1.0f);
// set hand picked sizes
m_pointSizes.push_back(10.0f);
m_pointSizes.push_back(25.0f);
m_pointSizes.push_back(range[1]);
DE_ASSERT((int)m_pointSizes.size() == m_iterationCount);
m_maxPointSize = range[1];
}
else
DE_ASSERT(false);
}
tcu::TestStatus PointTestInstance::iterate (void)
{
const std::string iterationDescription = "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
const tcu::ScopedLogSection section (m_context.getTestContext().getLog(), iterationDescription, iterationDescription);
const float pointSize = getPointSize();
tcu::Surface resultImage (m_renderSize, m_renderSize);
std::vector<tcu::Vec4> drawBuffer;
std::vector<PointSceneSpec::ScenePoint> points;
// supported?
if (pointSize <= m_maxPointSize)
{
// gen data
generatePoints(m_iteration, drawBuffer, points);
// draw image
drawPrimitives(resultImage, drawBuffer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
// compare
{
const bool compareOk = compareAndVerify(points, resultImage, drawBuffer);
if (!compareOk)
m_allIterationsPassed = false;
}
}
else
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Point size " << pointSize << " not supported, skipping iteration." << tcu::TestLog::EndMessage;
// result
if (++m_iteration == m_iterationCount)
{
if (m_allIterationsPassed)
return tcu::TestStatus::pass("Pass");
else
return tcu::TestStatus::fail("Incorrect rasterization");
}
else
return tcu::TestStatus::incomplete();
}
bool PointTestInstance::compareAndVerify (std::vector<PointSceneSpec::ScenePoint>& points,
tcu::Surface& resultImage,
std::vector<tcu::Vec4>& drawBuffer)
{
RasterizationArguments args;
PointSceneSpec scene;
tcu::IVec4 colorBits = tcu::getTextureFormatBitDepth(getTextureFormat());
args.numSamples = m_multisampling ? 1 : 0;
args.subpixelBits = m_subpixelBits;
args.redBits = colorBits[0];
args.greenBits = colorBits[1];
args.blueBits = colorBits[2];
scene.points.swap(points);
DE_UNREF(drawBuffer);
return verifyPointGroupRasterization(resultImage, scene, args, m_context.getTestContext().getLog());
}
float PointTestInstance::getPointSize (void) const
{
return m_pointSizes[m_iteration];
}
void PointTestInstance::generatePoints (int iteration, std::vector<tcu::Vec4>& outData, std::vector<PointSceneSpec::ScenePoint>& outPoints)
{
outData.resize(6);
switch (iteration)
{
case 0:
// \note: these values are chosen arbitrarily
outData[0] = tcu::Vec4( 0.2f, 0.8f, 0.0f, 1.0f);
outData[1] = tcu::Vec4( 0.5f, 0.2f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.5f, 0.3f, 0.0f, 1.0f);
outData[3] = tcu::Vec4(-0.5f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4(-0.2f, -0.4f, 0.0f, 1.0f);
outData[5] = tcu::Vec4(-0.4f, 0.2f, 0.0f, 1.0f);
break;
case 1:
outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
outData[1] = tcu::Vec4(-0.501f, -0.3f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.11f, -0.2f, 0.0f, 1.0f);
outData[3] = tcu::Vec4( 0.11f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.9f, 0.0f, 1.0f);
outData[5] = tcu::Vec4( 0.4f, 1.2f, 0.0f, 1.0f);
break;
case 2:
outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
outData[1] = tcu::Vec4( 0.3f, -0.9f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( -0.4f, -0.1f, 0.0f, 1.0f);
outData[3] = tcu::Vec4(-0.11f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.7f, 0.0f, 1.0f);
outData[5] = tcu::Vec4( -0.4f, 0.4f, 0.0f, 1.0f);
break;
}
outPoints.resize(outData.size());
for (int pointNdx = 0; pointNdx < (int)outPoints.size(); ++pointNdx)
{
outPoints[pointNdx].position = outData[pointNdx];
outPoints[pointNdx].pointSize = getPointSize();
}
// log
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Rendering " << outPoints.size() << " point(s): (point size = " << getPointSize() << ")" << tcu::TestLog::EndMessage;
for (int pointNdx = 0; pointNdx < (int)outPoints.size(); ++pointNdx)
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Point " << (pointNdx+1) << ":\t" << outPoints[pointNdx].position << tcu::TestLog::EndMessage;
}
template <typename ConcreteTestInstance>
class PointSizeTestCase : public BaseRenderingTestCase
{
public:
PointSizeTestCase (tcu::TestContext& context,
std::string& name,
std::string& description,
deUint32 renderSize,
float pointSize,
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT)
: BaseRenderingTestCase (context, name, description, sampleCount)
, m_pointSize (pointSize)
, m_renderSize (renderSize)
{}
virtual TestInstance* createInstance (Context& context) const
{
VkPhysicalDeviceProperties properties (context.getDeviceProperties());
if (m_renderSize > properties.limits.maxViewportDimensions[0] || m_renderSize > properties.limits.maxViewportDimensions[1])
TCU_THROW(NotSupportedError , "Viewport dimensions not supported");
if (m_renderSize > properties.limits.maxFramebufferWidth || m_renderSize > properties.limits.maxFramebufferHeight)
TCU_THROW(NotSupportedError , "Framebuffer width/height not supported");
return new ConcreteTestInstance(context, m_renderSize, m_pointSize);
}
virtual void checkSupport (Context& context) const
{
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
}
protected:
const float m_pointSize;
const deUint32 m_renderSize;
};
class PointSizeTestInstance : public BaseRenderingTestInstance
{
public:
PointSizeTestInstance (Context& context, deUint32 renderSize, float pointSize);
virtual tcu::TestStatus iterate (void);
virtual float getPointSize (void) const;
private:
void generatePointData (PointSceneSpec::ScenePoint& outPoint);
void drawPoint (tcu::PixelBufferAccess& result, tcu::PointSceneSpec::ScenePoint& point);
bool verifyPoint (tcu::TestLog& log, tcu::PixelBufferAccess& access, float pointSize);
bool isPointSizeClamped (float pointSize, float maxPointSizeLimit);
const float m_pointSize;
const float m_maxPointSize;
const deUint32 m_renderSize;
const VkFormat m_format;
};
PointSizeTestInstance::PointSizeTestInstance (Context& context, deUint32 renderSize, float pointSize)
: BaseRenderingTestInstance (context, vk::VK_SAMPLE_COUNT_1_BIT, renderSize, VK_FORMAT_R8_UNORM)
, m_pointSize (pointSize)
, m_maxPointSize (context.getDeviceProperties().limits.pointSizeRange[1])
, m_renderSize (renderSize)
, m_format (VK_FORMAT_R8_UNORM) // Use single-channel format to minimize memory allocation when using large render targets
{
}
tcu::TestStatus PointSizeTestInstance::iterate (void)
{
tcu::TextureLevel resultBuffer (mapVkFormat(m_format), m_renderSize, m_renderSize);
tcu::PixelBufferAccess access (resultBuffer.getAccess());
PointSceneSpec::ScenePoint point;
// Generate data
generatePointData(point);
// Draw
drawPoint(access, point);
// Compare
{
// pointSize must either be specified pointSize or clamped to device limit pointSizeRange[1]
const float pointSize (deFloatMin(m_pointSize, m_maxPointSize));
const bool compareOk (verifyPoint(m_context.getTestContext().getLog(), access, pointSize));
// Result
if (compareOk)
return isPointSizeClamped(pointSize, m_maxPointSize) ? tcu::TestStatus::pass("Pass, pointSize clamped to pointSizeRange[1]") : tcu::TestStatus::pass("Pass");
else
return tcu::TestStatus::fail("Incorrect rasterization");
}
}
float PointSizeTestInstance::getPointSize (void) const
{
return m_pointSize;
}
void PointSizeTestInstance::generatePointData (PointSceneSpec::ScenePoint& outPoint)
{
const tcu::PointSceneSpec::ScenePoint point =
{
tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), // position
tcu::Vec4(1.0f, 0.0f, 0.0f, 0.0f), // color
m_pointSize // pointSize
};
outPoint = point;
// log
{
tcu::TestLog& log = m_context.getTestContext().getLog();
log << tcu::TestLog::Message << "Point position: " << de::toString(point.position) << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Point color: " << de::toString(point.color) << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Point size: " << de::toString(point.pointSize) << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Render size: " << de::toString(m_renderSize) << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Format: " << de::toString(m_format) << tcu::TestLog::EndMessage;
}
}
void PointSizeTestInstance::drawPoint (tcu::PixelBufferAccess& result, PointSceneSpec::ScenePoint& point)
{
const tcu::Vec4 positionData (point.position);
const tcu::Vec4 colorData (point.color);
const DeviceInterface& vkd (m_context.getDeviceInterface());
const VkDevice vkDevice (m_context.getDevice());
const VkQueue queue (m_context.getUniversalQueue());
const deUint32 queueFamilyIndex (m_context.getUniversalQueueFamilyIndex());
const size_t attributeBatchSize (sizeof(tcu::Vec4));
Allocator& allocator (m_context.getDefaultAllocator());
Move<VkCommandBuffer> commandBuffer;
Move<VkPipeline> graphicsPipeline;
Move<VkBuffer> vertexBuffer;
de::MovePtr<Allocation> vertexBufferMemory;
// Create Graphics Pipeline
{
const std::vector<VkViewport> viewports (1, makeViewport(tcu::UVec2(m_renderSize)));
const std::vector<VkRect2D> scissors (1, makeRect2D(tcu::UVec2(m_renderSize)));
const VkVertexInputBindingDescription vertexInputBindingDescription =
{
0u, // deUint32 binding;
(deUint32)(2 * sizeof(tcu::Vec4)), // deUint32 strideInBytes;
VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate stepRate;
};
const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] =
{
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
},
{
1u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
(deUint32)sizeof(tcu::Vec4) // deUint32 offsetInBytes;
}
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0, // VkPipelineVertexInputStateCreateFlags flags;
1u, // deUint32 bindingCount;
&vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
2u, // deUint32 attributeCount;
vertexInputAttributeDescriptions // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
graphicsPipeline = makeGraphicsPipeline(vkd, // const DeviceInterface& vk
vkDevice, // const VkDevice device
*m_pipelineLayout, // const VkPipelineLayout pipelineLayout
*m_vertexShaderModule, // const VkShaderModule vertexShaderModule
DE_NULL, // const VkShaderModule tessellationControlShaderModule
DE_NULL, // const VkShaderModule tessellationEvalShaderModule
DE_NULL, // const VkShaderModule geometryShaderModule
*m_fragmentShaderModule, // const VkShaderModule fragmentShaderModule
*m_renderPass, // const VkRenderPass renderPass
viewports, // const std::vector<VkViewport>& viewports
scissors, // const std::vector<VkRect2D>& scissors
VK_PRIMITIVE_TOPOLOGY_POINT_LIST, // const VkPrimitiveTopology topology
0u, // const deUint32 subpass
0u, // const deUint32 patchControlPoints
&vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
getRasterizationStateCreateInfo(), // const VkPipelineRasterizationStateCreateInfo* rasterizationStateCreateInfo
DE_NULL, // const VkPipelineMultisampleStateCreateInfo* multisampleStateCreateInfo
DE_NULL, // const VkPipelineDepthStencilStateCreateInfo* depthStencilStateCreateInfo,
getColorBlendStateCreateInfo()); // const VkPipelineColorBlendStateCreateInfo* colorBlendStateCreateInfo
}
// Create Vertex Buffer
{
const VkBufferCreateInfo vertexBufferParams =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkBufferCreateFlags flags;
attributeBatchSize * 2, // VkDeviceSize size;
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyCount;
&queueFamilyIndex // const deUint32* pQueueFamilyIndices;
};
vertexBuffer = createBuffer(vkd, vkDevice, &vertexBufferParams);
vertexBufferMemory = allocator.allocate(getBufferMemoryRequirements(vkd, vkDevice, *vertexBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vkd.bindBufferMemory(vkDevice, *vertexBuffer, vertexBufferMemory->getMemory(), vertexBufferMemory->getOffset()));
// Load vertices into vertex buffer
deMemcpy(vertexBufferMemory->getHostPtr(), &positionData, attributeBatchSize);
deMemcpy(reinterpret_cast<deUint8*>(vertexBufferMemory->getHostPtr()) + attributeBatchSize, &colorData, attributeBatchSize);
flushAlloc(vkd, vkDevice, *vertexBufferMemory);
}
// Create Command Buffer
commandBuffer = allocateCommandBuffer(vkd, vkDevice, *m_commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
// Begin Command Buffer
beginCommandBuffer(vkd, *commandBuffer);
addImageTransitionBarrier(*commandBuffer, *m_image,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // VkPipelineStageFlags srcStageMask
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // VkPipelineStageFlags dstStageMask
0, // VkAccessFlags srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // VkImageLayout newLayout;
// Begin Render Pass
beginRenderPass(vkd, *commandBuffer, *m_renderPass, *m_frameBuffer, vk::makeRect2D(0, 0, m_renderSize, m_renderSize), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
const VkDeviceSize vertexBufferOffset = 0;
vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
vkd.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayout, 0u, 1, &m_descriptorSet.get(), 0u, DE_NULL);
vkd.cmdBindVertexBuffers(*commandBuffer, 0, 1, &vertexBuffer.get(), &vertexBufferOffset);
vkd.cmdDraw(*commandBuffer, 1, 1, 0, 0);
endRenderPass(vkd, *commandBuffer);
// Copy Image
copyImageToBuffer(vkd, *commandBuffer, *m_image, *m_resultBuffer, tcu::IVec2(m_renderSize, m_renderSize));
endCommandBuffer(vkd, *commandBuffer);
// Set Point Size
{
float pointSize = getPointSize();
deMemcpy(m_uniformBufferMemory->getHostPtr(), &pointSize, (size_t)m_uniformBufferSize);
flushAlloc(vkd, vkDevice, *m_uniformBufferMemory);
}
// Submit
submitCommandsAndWait(vkd, vkDevice, queue, commandBuffer.get());
invalidateAlloc(vkd, vkDevice, *m_resultBufferMemory);
tcu::copy(result, tcu::ConstPixelBufferAccess(m_textureFormat, tcu::IVec3(m_renderSize, m_renderSize, 1), m_resultBufferMemory->getHostPtr()));
}
bool PointSizeTestInstance::verifyPoint (tcu::TestLog& log, tcu::PixelBufferAccess& image, float pointSize)
{
const float expectedPointColor (1.0f);
const float expectedBackgroundColor (0.0f);
deUint32 pointWidth (0u);
deUint32 pointHeight (0u);
bool incorrectlyColoredPixelsFound (false);
bool isOk (true);
// Verify rasterized point width and color
for (size_t x = 0; x < (deUint32)image.getWidth(); x++)
{
float pixelColor = image.getPixel((deUint32)x, image.getHeight() / 2).x();
if (pixelColor == expectedPointColor)
pointWidth++;
if ((pixelColor != expectedPointColor) && (pixelColor != expectedBackgroundColor))
incorrectlyColoredPixelsFound = true;
}
// Verify rasterized point height and color
for (size_t y = 0; y < (deUint32)image.getHeight(); y++)
{
float pixelColor = image.getPixel((deUint32)y, image.getWidth() / 2).x();
if (pixelColor == expectedPointColor)
pointHeight++;
if ((pixelColor != expectedPointColor) && (pixelColor != expectedBackgroundColor))
incorrectlyColoredPixelsFound = true;
}
// Compare amount of rasterized point pixels to expected pointSize.
if ((pointWidth != (deUint32)deRoundFloatToInt32(pointSize)) || (pointHeight != (deUint32)deRoundFloatToInt32(pointSize)))
{
log << tcu::TestLog::Message << "Incorrect point size. Expected pointSize: " << de::toString(pointSize)
<< ". Rasterized point width: " << pointWidth << " pixels, height: "
<< pointHeight << " pixels." << tcu::TestLog::EndMessage;
isOk = false;
}
// Check incorrectly colored pixels
if (incorrectlyColoredPixelsFound)
{
log << tcu::TestLog::Message << "Incorrectly colored pixels found." << tcu::TestLog::EndMessage;
isOk = false;
}
return isOk;
}
bool PointSizeTestInstance::isPointSizeClamped (float pointSize, float maxPointSizeLimit)
{
return (pointSize == maxPointSizeLimit);
}
template <typename ConcreteTestInstance>
class BaseTestCase : public BaseRenderingTestCase
{
public:
BaseTestCase (tcu::TestContext& context, const std::string& name, const std::string& description, VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT)
: BaseRenderingTestCase(context, name, description, sampleCount)
{}
virtual TestInstance* createInstance (Context& context) const
{
return new ConcreteTestInstance(context, m_sampleCount);
}
};
class TrianglesTestInstance : public BaseTriangleTestInstance
{
public:
TrianglesTestInstance (Context& context, VkSampleCountFlagBits sampleCount)
: BaseTriangleTestInstance(context, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, sampleCount)
{}
void generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
};
void TrianglesTestInstance::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
{
outData.resize(6);
switch (iteration)
{
case 0:
// \note: these values are chosen arbitrarily
outData[0] = tcu::Vec4( 0.2f, 0.8f, 0.0f, 1.0f);
outData[1] = tcu::Vec4( 0.5f, 0.2f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.5f, 0.3f, 0.0f, 1.0f);
outData[3] = tcu::Vec4(-0.5f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4(-1.5f, -0.4f, 0.0f, 1.0f);
outData[5] = tcu::Vec4(-0.4f, 0.2f, 0.0f, 1.0f);
break;
case 1:
outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
outData[1] = tcu::Vec4(-0.501f, -0.3f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.11f, -0.2f, 0.0f, 1.0f);
outData[3] = tcu::Vec4( 0.11f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.9f, 0.0f, 1.0f);
outData[5] = tcu::Vec4( 0.4f, 1.2f, 0.0f, 1.0f);
break;
case 2:
outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
outData[1] = tcu::Vec4( 1.1f, -0.9f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( -1.1f, -0.1f, 0.0f, 1.0f);
outData[3] = tcu::Vec4(-0.11f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.7f, 0.0f, 1.0f);
outData[5] = tcu::Vec4( -0.4f, 0.4f, 0.0f, 1.0f);
break;
}
outTriangles.resize(2);
outTriangles[0].positions[0] = outData[0]; outTriangles[0].sharedEdge[0] = false;
outTriangles[0].positions[1] = outData[1]; outTriangles[0].sharedEdge[1] = false;
outTriangles[0].positions[2] = outData[2]; outTriangles[0].sharedEdge[2] = false;
outTriangles[1].positions[0] = outData[3]; outTriangles[1].sharedEdge[0] = false;
outTriangles[1].positions[1] = outData[4]; outTriangles[1].sharedEdge[1] = false;
outTriangles[1].positions[2] = outData[5]; outTriangles[1].sharedEdge[2] = false;
// log
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Rendering " << outTriangles.size() << " triangle(s):" << tcu::TestLog::EndMessage;
for (int triangleNdx = 0; triangleNdx < (int)outTriangles.size(); ++triangleNdx)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "Triangle " << (triangleNdx+1) << ":"
<< "\n\t" << outTriangles[triangleNdx].positions[0]
<< "\n\t" << outTriangles[triangleNdx].positions[1]
<< "\n\t" << outTriangles[triangleNdx].positions[2]
<< tcu::TestLog::EndMessage;
}
}
class TriangleStripTestInstance : public BaseTriangleTestInstance
{
public:
TriangleStripTestInstance (Context& context, VkSampleCountFlagBits sampleCount)
: BaseTriangleTestInstance(context, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, sampleCount)
{}
void generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
};
void TriangleStripTestInstance::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
{
outData.resize(5);
switch (iteration)
{
case 0:
// \note: these values are chosen arbitrarily
outData[0] = tcu::Vec4(-0.504f, 0.8f, 0.0f, 1.0f);
outData[1] = tcu::Vec4(-0.2f, -0.2f, 0.0f, 1.0f);
outData[2] = tcu::Vec4(-0.2f, 0.199f, 0.0f, 1.0f);
outData[3] = tcu::Vec4( 0.5f, 0.201f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 1.5f, 0.4f, 0.0f, 1.0f);
break;
case 1:
outData[0] = tcu::Vec4(-0.499f, 0.129f, 0.0f, 1.0f);
outData[1] = tcu::Vec4(-0.501f, -0.3f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.11f, -0.2f, 0.0f, 1.0f);
outData[3] = tcu::Vec4( 0.11f, -0.31f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.9f, 0.0f, 1.0f);
break;
case 2:
outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
outData[1] = tcu::Vec4( 1.1f, -0.9f, 0.0f, 1.0f);
outData[2] = tcu::Vec4(-0.87f, -0.1f, 0.0f, 1.0f);
outData[3] = tcu::Vec4(-0.11f, 0.19f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.7f, 0.0f, 1.0f);
break;
}
outTriangles.resize(3);
outTriangles[0].positions[0] = outData[0]; outTriangles[0].sharedEdge[0] = false;
outTriangles[0].positions[1] = outData[1]; outTriangles[0].sharedEdge[1] = true;
outTriangles[0].positions[2] = outData[2]; outTriangles[0].sharedEdge[2] = false;
outTriangles[1].positions[0] = outData[2]; outTriangles[1].sharedEdge[0] = true;
outTriangles[1].positions[1] = outData[1]; outTriangles[1].sharedEdge[1] = false;
outTriangles[1].positions[2] = outData[3]; outTriangles[1].sharedEdge[2] = true;
outTriangles[2].positions[0] = outData[2]; outTriangles[2].sharedEdge[0] = true;
outTriangles[2].positions[1] = outData[3]; outTriangles[2].sharedEdge[1] = false;
outTriangles[2].positions[2] = outData[4]; outTriangles[2].sharedEdge[2] = false;
// log
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Rendering triangle strip, " << outData.size() << " vertices." << tcu::TestLog::EndMessage;
for (int vtxNdx = 0; vtxNdx < (int)outData.size(); ++vtxNdx)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "\t" << outData[vtxNdx]
<< tcu::TestLog::EndMessage;
}
}
class TriangleFanTestInstance : public BaseTriangleTestInstance
{
public:
TriangleFanTestInstance (Context& context, VkSampleCountFlagBits sampleCount);
void generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
};
TriangleFanTestInstance::TriangleFanTestInstance (Context& context, VkSampleCountFlagBits sampleCount)
: BaseTriangleTestInstance(context, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, sampleCount)
{
if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") &&
!context.getPortabilitySubsetFeatures().triangleFans)
{
TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Triangle fans are not supported by this implementation");
}
}
void TriangleFanTestInstance::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
{
outData.resize(5);
switch (iteration)
{
case 0:
// \note: these values are chosen arbitrarily
outData[0] = tcu::Vec4( 0.01f, 0.0f, 0.0f, 1.0f);
outData[1] = tcu::Vec4( 0.5f, 0.2f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.46f, 0.3f, 0.0f, 1.0f);
outData[3] = tcu::Vec4(-0.5f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4(-1.5f, -0.4f, 0.0f, 1.0f);
break;
case 1:
outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
outData[1] = tcu::Vec4(-0.501f, -0.3f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.11f, -0.2f, 0.0f, 1.0f);
outData[3] = tcu::Vec4( 0.11f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.9f, 0.0f, 1.0f);
break;
case 2:
outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
outData[1] = tcu::Vec4( 1.1f, -0.9f, 0.0f, 1.0f);
outData[2] = tcu::Vec4( 0.7f, -0.1f, 0.0f, 1.0f);
outData[3] = tcu::Vec4( 0.11f, 0.2f, 0.0f, 1.0f);
outData[4] = tcu::Vec4( 0.88f, 0.7f, 0.0f, 1.0f);
break;
}
outTriangles.resize(3);
outTriangles[0].positions[0] = outData[0]; outTriangles[0].sharedEdge[0] = false;
outTriangles[0].positions[1] = outData[1]; outTriangles[0].sharedEdge[1] = false;
outTriangles[0].positions[2] = outData[2]; outTriangles[0].sharedEdge[2] = true;
outTriangles[1].positions[0] = outData[0]; outTriangles[1].sharedEdge[0] = true;
outTriangles[1].positions[1] = outData[2]; outTriangles[1].sharedEdge[1] = false;
outTriangles[1].positions[2] = outData[3]; outTriangles[1].sharedEdge[2] = true;
outTriangles[2].positions[0] = outData[0]; outTriangles[2].sharedEdge[0] = true;
outTriangles[2].positions[1] = outData[3]; outTriangles[2].sharedEdge[1] = false;
outTriangles[2].positions[2] = outData[4]; outTriangles[2].sharedEdge[2] = false;
// log
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Rendering triangle fan, " << outData.size() << " vertices." << tcu::TestLog::EndMessage;
for (int vtxNdx = 0; vtxNdx < (int)outData.size(); ++vtxNdx)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "\t" << outData[vtxNdx]
<< tcu::TestLog::EndMessage;
}
}
struct ConservativeTestConfig
{
VkConservativeRasterizationModeEXT conservativeRasterizationMode;
float extraOverestimationSize;
VkPrimitiveTopology primitiveTopology;
bool degeneratePrimitives;
float lineWidth;
deUint32 resolution;
};
float getExtraOverestimationSize (const float overestimationSizeDesired, const VkPhysicalDeviceConservativeRasterizationPropertiesEXT& conservativeRasterizationProperties)
{
const float extraOverestimationSize = overestimationSizeDesired == TCU_INFINITY ? conservativeRasterizationProperties.maxExtraPrimitiveOverestimationSize
: overestimationSizeDesired == -TCU_INFINITY ? conservativeRasterizationProperties.extraPrimitiveOverestimationSizeGranularity
: overestimationSizeDesired;
return extraOverestimationSize;
}
template <typename ConcreteTestInstance>
class ConservativeTestCase : public BaseRenderingTestCase
{
public:
ConservativeTestCase (tcu::TestContext& context,
const std::string& name,
const std::string& description,
const ConservativeTestConfig& conservativeTestConfig,
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT)
: BaseRenderingTestCase (context, name, description, sampleCount)
, m_conservativeTestConfig (conservativeTestConfig)
{}
virtual void checkSupport (Context& context) const;
virtual TestInstance* createInstance (Context& context) const
{
return new ConcreteTestInstance(context, m_conservativeTestConfig, m_sampleCount);
}
protected:
bool isUseLineSubPixel (Context& context) const;
deUint32 getSubPixelResolution (Context& context) const;
const ConservativeTestConfig m_conservativeTestConfig;
};
template <typename ConcreteTestInstance>
bool ConservativeTestCase<ConcreteTestInstance>::isUseLineSubPixel (Context& context) const
{
return (isPrimitiveTopologyLine(m_conservativeTestConfig.primitiveTopology) && context.isDeviceFunctionalitySupported("VK_EXT_line_rasterization"));
}
template <typename ConcreteTestInstance>
deUint32 ConservativeTestCase<ConcreteTestInstance>::getSubPixelResolution (Context& context) const
{
if (isUseLineSubPixel(context))
{
const VkPhysicalDeviceLineRasterizationPropertiesEXT lineRasterizationPropertiesEXT = context.getLineRasterizationPropertiesEXT();
return lineRasterizationPropertiesEXT.lineSubPixelPrecisionBits;
}
else
{
return context.getDeviceProperties().limits.subPixelPrecisionBits;
}
}
template <typename ConcreteTestInstance>
void ConservativeTestCase<ConcreteTestInstance>::checkSupport (Context& context) const
{
context.requireDeviceFunctionality("VK_EXT_conservative_rasterization");
const VkPhysicalDeviceConservativeRasterizationPropertiesEXT conservativeRasterizationProperties = context.getConservativeRasterizationPropertiesEXT();
const deUint32 subPixelPrecisionBits = getSubPixelResolution(context);
const deUint32 subPixelPrecision = 1<<subPixelPrecisionBits;
const bool linesPrecision = isUseLineSubPixel(context);
const float primitiveOverestimationSizeMult = float(subPixelPrecision) * conservativeRasterizationProperties.primitiveOverestimationSize;
const bool topologyLineOrPoint = isPrimitiveTopologyLine(m_conservativeTestConfig.primitiveTopology) || isPrimitiveTopologyPoint(m_conservativeTestConfig.primitiveTopology);
DE_ASSERT(subPixelPrecisionBits < sizeof(deUint32) * 8);
context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "maxExtraPrimitiveOverestimationSize=" << conservativeRasterizationProperties.maxExtraPrimitiveOverestimationSize << '\n'
<< "extraPrimitiveOverestimationSizeGranularity=" << conservativeRasterizationProperties.extraPrimitiveOverestimationSizeGranularity << '\n'
<< "degenerateLinesRasterized=" << conservativeRasterizationProperties.degenerateLinesRasterized << '\n'
<< "degenerateTrianglesRasterized=" << conservativeRasterizationProperties.degenerateTrianglesRasterized << '\n'
<< "primitiveOverestimationSize=" << conservativeRasterizationProperties.primitiveOverestimationSize << " (==" << primitiveOverestimationSizeMult << '/' << subPixelPrecision << ")\n"
<< "subPixelPrecisionBits=" << subPixelPrecisionBits << (linesPrecision ? " (using VK_EXT_line_rasterization)" : " (using limits)") << '\n'
<< tcu::TestLog::EndMessage;
if (conservativeRasterizationProperties.extraPrimitiveOverestimationSizeGranularity > conservativeRasterizationProperties.maxExtraPrimitiveOverestimationSize)
TCU_FAIL("Granularity cannot be greater than maximum extra size");
if (topologyLineOrPoint)
{
if (!conservativeRasterizationProperties.conservativePointAndLineRasterization)
TCU_THROW(NotSupportedError, "Conservative line and point rasterization is not supported");
}
if (m_conservativeTestConfig.conservativeRasterizationMode == VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT)
{
if (conservativeRasterizationProperties.primitiveUnderestimation == DE_FALSE)
TCU_THROW(NotSupportedError, "Underestimation is not supported");
if (isPrimitiveTopologyLine(m_conservativeTestConfig.primitiveTopology))
{
const float testLineWidth = m_conservativeTestConfig.lineWidth;
if (testLineWidth != 1.0f)
{
const VkPhysicalDeviceLimits& limits = context.getDeviceProperties().limits;
const float lineWidthRange[2] = { limits.lineWidthRange[0], limits.lineWidthRange[1] };
const float lineWidthGranularity = limits.lineWidthGranularity;
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_WIDE_LINES);
if (lineWidthGranularity == 0.0f)
TCU_THROW(NotSupportedError, "Wide lines required for test, but are not supported");
DE_ASSERT(lineWidthGranularity > 0.0f && lineWidthRange[0] > 0.0f && lineWidthRange[1] >= lineWidthRange[0]);
if (!de::inBounds(testLineWidth, lineWidthRange[0], lineWidthRange[1]))
TCU_THROW(NotSupportedError, "Tested line width is not supported");
const float n = (testLineWidth - lineWidthRange[0]) / lineWidthGranularity;
if (deFloatFrac(n) != 0.0f || n * lineWidthGranularity + lineWidthRange[0] != testLineWidth)
TCU_THROW(NotSupportedError, "Exact match of line width is required for the test");
}
}
else if (isPrimitiveTopologyPoint(m_conservativeTestConfig.primitiveTopology))
{
const float testPointSize = m_conservativeTestConfig.lineWidth;
if (testPointSize != 1.0f)
{
const VkPhysicalDeviceLimits& limits = context.getDeviceProperties().limits;
const float pointSizeRange[2] = { limits.pointSizeRange[0], limits.pointSizeRange[1] };
const float pointSizeGranularity = limits.pointSizeGranularity;
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
if (pointSizeGranularity == 0.0f)
TCU_THROW(NotSupportedError, "Large points required for test, but are not supported");
DE_ASSERT(pointSizeGranularity > 0.0f && pointSizeRange[0] > 0.0f && pointSizeRange[1] >= pointSizeRange[0]);
if (!de::inBounds(testPointSize, pointSizeRange[0], pointSizeRange[1]))
TCU_THROW(NotSupportedError, "Tested point size is not supported");
const float n = (testPointSize - pointSizeRange[0]) / pointSizeGranularity;
if (deFloatFrac(n) != 0.0f || n * pointSizeGranularity + pointSizeRange[0] != testPointSize)
TCU_THROW(NotSupportedError, "Exact match of point size is required for the test");
}
}
}
else if (m_conservativeTestConfig.conservativeRasterizationMode == VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT)
{
const float extraOverestimationSize = getExtraOverestimationSize(m_conservativeTestConfig.extraOverestimationSize, conservativeRasterizationProperties);
if (extraOverestimationSize > conservativeRasterizationProperties.maxExtraPrimitiveOverestimationSize)
TCU_THROW(NotSupportedError, "Specified overestimation size is not supported");
if (topologyLineOrPoint)
{
if (!conservativeRasterizationProperties.conservativePointAndLineRasterization)
TCU_THROW(NotSupportedError, "Conservative line and point rasterization is not supported");
}
if (isPrimitiveTopologyTriangle(m_conservativeTestConfig.primitiveTopology))
{
if (m_conservativeTestConfig.degeneratePrimitives)
{
// Enforce specification minimum required limit to avoid division by zero
DE_ASSERT(subPixelPrecisionBits >= 4);
// Make sure float precision of 22 bits is enough, i.e. resoultion in subpixel quarters less than float precision
if (m_conservativeTestConfig.resolution * (1<<(subPixelPrecisionBits + 2)) > (1<<21))
TCU_THROW(NotSupportedError, "Subpixel resolution is too high to generate degenerate primitives");
}
}
}
else
TCU_THROW(InternalError, "Non-conservative mode tests are not supported by this class");
}
class ConservativeTraingleTestInstance : public BaseTriangleTestInstance
{
public:
ConservativeTraingleTestInstance (Context& context,
ConservativeTestConfig conservativeTestConfig,
VkSampleCountFlagBits sampleCount)
: BaseTriangleTestInstance (context,
conservativeTestConfig.primitiveTopology,
sampleCount,
conservativeTestConfig.resolution)
, m_conservativeTestConfig (conservativeTestConfig)
, m_conservativeRasterizationProperties (context.getConservativeRasterizationPropertiesEXT())
, m_rasterizationConservativeStateCreateInfo (initRasterizationConservativeStateCreateInfo())
, m_rasterizationStateCreateInfo (initRasterizationStateCreateInfo())
{}
void generateTriangles (int iteration,
std::vector<tcu::Vec4>& outData,
std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
const VkPipelineRasterizationStateCreateInfo* getRasterizationStateCreateInfo (void) const;
protected:
virtual const VkPipelineRasterizationLineStateCreateInfoEXT* getLineRasterizationStateCreateInfo (void);
virtual bool compareAndVerify (std::vector<TriangleSceneSpec::SceneTriangle>& triangles,
tcu::Surface& resultImage,
std::vector<tcu::Vec4>& drawBuffer);
virtual bool compareAndVerifyOverestimatedNormal (std::vector<TriangleSceneSpec::SceneTriangle>& triangles,
tcu::Surface& resultImage);
virtual bool compareAndVerifyOverestimatedDegenerate (std::vector<TriangleSceneSpec::SceneTriangle>& triangles,
tcu::Surface& resultImage);
virtual bool compareAndVerifyUnderestimatedNormal (std::vector<TriangleSceneSpec::SceneTriangle>& triangles,
tcu::Surface& resultImage);
virtual bool compareAndVerifyUnderestimatedDegenerate (std::vector<TriangleSceneSpec::SceneTriangle>& triangles,
tcu::Surface& resultImage);
void generateNormalTriangles (int iteration,
std::vector<tcu::Vec4>& outData,
std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
void generateDegenerateTriangles (int iteration,
std::vector<tcu::Vec4>& outData,
std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
void drawPrimitives (tcu::Surface& result,
const std::vector<tcu::Vec4>& vertexData,
VkPrimitiveTopology primitiveTopology);
private:
const std::vector<VkPipelineRasterizationConservativeStateCreateInfoEXT> initRasterizationConservativeStateCreateInfo (void);
const std::vector<VkPipelineRasterizationStateCreateInfo> initRasterizationStateCreateInfo (void);
const ConservativeTestConfig m_conservativeTestConfig;
const VkPhysicalDeviceConservativeRasterizationPropertiesEXT m_conservativeRasterizationProperties;
const std::vector<VkPipelineRasterizationConservativeStateCreateInfoEXT> m_rasterizationConservativeStateCreateInfo;
const std::vector<VkPipelineRasterizationStateCreateInfo> m_rasterizationStateCreateInfo;
};
void ConservativeTraingleTestInstance::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
{
if (m_conservativeTestConfig.degeneratePrimitives)
generateDegenerateTriangles(iteration, outData, outTriangles);
else
generateNormalTriangles(iteration, outData, outTriangles);
}
void ConservativeTraingleTestInstance::generateNormalTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
{
const float halfPixel = 1.0f / float(m_renderSize);
const float extraOverestimationSize = getExtraOverestimationSize(m_conservativeTestConfig.extraOverestimationSize, m_conservativeRasterizationProperties);
const float overestimate = 2.0f * halfPixel * (m_conservativeRasterizationProperties.primitiveOverestimationSize + extraOverestimationSize);
const float overestimateMargin = overestimate;
const float underestimateMargin = 0.0f;
const bool isOverestimate = m_conservativeTestConfig.conservativeRasterizationMode == VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT;
const float margin = isOverestimate ? overestimateMargin : underestimateMargin;
const char* overestimateIterationComments[] = { "Corner touch", "Any portion pixel coverage", "Edge touch" };
outData.resize(6);
switch (iteration)
{
case 0:
{
// Corner touch
const float edge = 2 * halfPixel + margin;
const float left = -1.0f + edge;
const float right = +1.0f - edge;
const float up = -1.0f + edge;
const float down = +1.0f - edge;
outData[0] = tcu::Vec4( left, down, 0.0f, 1.0f);
outData[1] = tcu::Vec4( left, up, 0.0f, 1.0f);
outData[2] = tcu::Vec4(right, down, 0.0f, 1.0f);
outData[3] = tcu::Vec4( left, up, 0.0f, 1.0f);
outData[4] = tcu::Vec4(right, down, 0.0f, 1.0f);
outData[5] = tcu::Vec4(right, up, 0.0f, 1.0f);
break;
}
case 1:
{
// Partial coverage
const float eps = halfPixel / 32.0f;
const float edge = 4.0f * halfPixel + margin - eps;
const float left = -1.0f + edge;
const float right = +1.0f - edge;
const float up = -1.0f + edge;
const float down = +1.0f - edge;
outData[0] = tcu::Vec4( left, down, 0.0f, 1.0f);
outData[1] = tcu::Vec4( left, up, 0.0f, 1.0f);
outData[2] = tcu::Vec4(right, down, 0.0f, 1.0f);
outData[3] = tcu::Vec4( left, up, 0.0f, 1.0f);
outData[4] = tcu::Vec4(right, down, 0.0f, 1.0f);
outData[5] = tcu::Vec4(right, up, 0.0f, 1.0f);
break;
}
case 2:
{
// Edge touch
const float edge = 6.0f * halfPixel + margin;
const float left = -1.0f + edge;
const float right = +1.0f - edge;
const float up = -1.0f + edge;
const float down = +1.0f - edge;
outData[0] = tcu::Vec4( left, down, 0.0f, 1.0f);
outData[1] = tcu::Vec4( left, up, 0.0f, 1.0f);
outData[2] = tcu::Vec4(right, down, 0.0f, 1.0f);
outData[3] = tcu::Vec4( left, up, 0.0f, 1.0f);
outData[4] = tcu::Vec4(right, down, 0.0f, 1.0f);
outData[5] = tcu::Vec4(right, up, 0.0f, 1.0f);
break;
}
default:
TCU_THROW(InternalError, "Unexpected iteration");
}
outTriangles.resize(outData.size() / 3);
for (size_t ndx = 0; ndx < outTriangles.size(); ++ndx)
{
outTriangles[ndx].positions[0] = outData[3 * ndx + 0]; outTriangles[ndx].sharedEdge[0] = false;
outTriangles[ndx].positions[1] = outData[3 * ndx + 1]; outTriangles[ndx].sharedEdge[1] = false;
outTriangles[ndx].positions[2] = outData[3 * ndx + 2]; outTriangles[ndx].sharedEdge[2] = false;
}
// log
if (isOverestimate)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "Testing " << overestimateIterationComments[iteration] << " "
<< "with rendering " << outTriangles.size() << " triangle(s):"
<< tcu::TestLog::EndMessage;
}
else
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "Rendering " << outTriangles.size() << " triangle(s):"
<< tcu::TestLog::EndMessage;
}
for (size_t ndx = 0; ndx < outTriangles.size(); ++ndx)
{
const deUint32 multiplier = m_renderSize / 2;
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "Triangle " << (ndx + 1) << ":"
<< "\n\t" << outTriangles[ndx].positions[0] << " == " << (float(multiplier) * outTriangles[ndx].positions[0]) << "/" << multiplier
<< "\n\t" << outTriangles[ndx].positions[1] << " == " << (float(multiplier) * outTriangles[ndx].positions[1]) << "/" << multiplier
<< "\n\t" << outTriangles[ndx].positions[2] << " == " << (float(multiplier) * outTriangles[ndx].positions[2]) << "/" << multiplier
<< tcu::TestLog::EndMessage;
}
}
void ConservativeTraingleTestInstance::generateDegenerateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleScen