blob: 9f0568b49d9c1beccc5926d58fc40143498c461b [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2020 The Khronos Group Inc.
* Copyright (c) 2020 Valve Corporation.
*
* 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 Logic Operators Tests
*//*--------------------------------------------------------------------*/
#include "vktPipelineLogicOpTests.hpp"
#include "vktPipelineImageUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkMemUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkImageWithMemory.hpp"
#include "tcuVectorUtil.hpp"
#include "tcuImageCompare.hpp"
#include "tcuTestLog.hpp"
#include <string>
#include <limits>
namespace vkt
{
namespace pipeline
{
using namespace vk;
namespace
{
bool isSupportedColorAttachmentFormat (const InstanceInterface& instanceInterface,
VkPhysicalDevice device,
VkFormat format)
{
VkFormatProperties formatProps;
instanceInterface.getPhysicalDeviceFormatProperties(device, format, &formatProps);
// Format also needs to be INT, UINT, or SINT but as we are the ones setting the
// color attachment format we only need to check that it is a valid color attachment
// format here.
return (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
}
struct TestParams
{
VkLogicOp logicOp; // Operation.
tcu::UVec4 fbColor; // Framebuffer color.
tcu::UVec4 quadColor; // Geometry color.
VkFormat format; // Framebuffer format.
std::string name; // Logic operator test name.
};
deUint32 calcOpResult(VkLogicOp op, deUint32 src, deUint32 dst)
{
// See section 29.2 "Logical Operations" in the spec.
//
// AND: SRC & DST = 1010 & 1100 = 1000 = 0x8
// AND_REVERSE: SRC & ~DST = 0011 & 1010 = 0010 = 0x2
// COPY: SRC = 1010 = 1010 = 0xa
// AND_INVERTED: ~SRC & DST = 0101 & 1100 = 0100 = 0x4
// NO_OP: DST = 1010 = 1010 = 0xa
// XOR: SRC ^ DST = 1010 ^ 1100 = 0110 = 0x6
// OR: SRC | DST = 1010 | 1100 = 1110 = 0xe
// NOR: ~(SRC | DST) = ~(1010 | 1100) = 0001 = 0x1
// EQUIVALENT: ~(SRC ^ DST) = ~(1010 ^ 1100) = 1001 = 0x9
// INVERT: ~DST = ~1100 = 0011 = 0x3
// OR_REVERSE: SRC | ~DST = 1010 | 0011 = 1011 = 0xb
// COPY_INVERTED: ~SRC = 0101 = 0101 = 0x5
// OR_INVERTED: ~SRC | DST = 0101 | 1100 = 1101 = 0xd
// NAND: ~(SRC & DST) = ~(1010 &1100) = 0111 = 0x7
// SET: = 1111 = 1111 = 0xf (sets all bits)
switch (op)
{
case VK_LOGIC_OP_CLEAR: return (0u);
case VK_LOGIC_OP_AND: return (src & dst);
case VK_LOGIC_OP_AND_REVERSE: return (src & ~dst);
case VK_LOGIC_OP_COPY: return (src);
case VK_LOGIC_OP_AND_INVERTED: return (~src & dst);
case VK_LOGIC_OP_NO_OP: return (dst);
case VK_LOGIC_OP_XOR: return (src ^ dst);
case VK_LOGIC_OP_OR: return (src | dst);
case VK_LOGIC_OP_NOR: return (~(src | dst));
case VK_LOGIC_OP_EQUIVALENT: return (~(src ^ dst));
case VK_LOGIC_OP_INVERT: return (~dst);
case VK_LOGIC_OP_OR_REVERSE: return (src | ~dst);
case VK_LOGIC_OP_COPY_INVERTED: return (~src);
case VK_LOGIC_OP_OR_INVERTED: return (~src | dst);
case VK_LOGIC_OP_NAND: return (~(src & dst));
case VK_LOGIC_OP_SET: return (std::numeric_limits<deUint32>::max());
default: DE_ASSERT(false); break;
}
DE_ASSERT(false);
return 0u;
}
// Gets a bitmask to filter out unused bits according to the channel size (e.g. 0xFFu for 8-bit channels).
// channelSize in bytes.
deUint32 getChannelMask (int channelSize)
{
DE_ASSERT(channelSize >= 1 && channelSize <= 4);
deUint64 mask = 1u;
mask <<= (channelSize * 8);
--mask;
return static_cast<deUint32>(mask);
}
class LogicOpTest : public vkt::TestCase
{
public:
LogicOpTest (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const TestParams &testParams);
virtual ~LogicOpTest (void);
virtual void initPrograms (SourceCollections& sourceCollections) const;
virtual void checkSupport (Context& context) const;
virtual TestInstance* createInstance (Context& context) const;
private:
TestParams m_params;
};
LogicOpTest::LogicOpTest (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const TestParams& testParams)
: vkt::TestCase (testCtx, name, description)
, m_params (testParams)
{
DE_ASSERT(m_params.format != VK_FORMAT_UNDEFINED);
}
LogicOpTest::~LogicOpTest (void)
{
}
void LogicOpTest::checkSupport (Context &ctx) const
{
const auto& features = ctx.getDeviceFeatures();
if (!features.logicOp)
TCU_THROW(NotSupportedError, "Logic operations not supported");
if (!isSupportedColorAttachmentFormat(ctx.getInstanceInterface(), ctx.getPhysicalDevice(), m_params.format))
TCU_THROW(NotSupportedError, "Unsupported color attachment format: " + std::string(getFormatName(m_params.format)));
}
void LogicOpTest::initPrograms (SourceCollections& sourceCollections) const
{
sourceCollections.glslSources.add("color_vert") << glu::VertexSource(
"#version 430\n"
"vec2 vdata[] = vec2[] (\n"
"vec2(-1.0, -1.0),\n"
"vec2(1.0, -1.0),\n"
"vec2(-1.0, 1.0),\n"
"vec2(1.0, 1.0));\n"
"void main (void)\n"
"{\n"
" gl_Position = vec4(vdata[gl_VertexIndex], 0.0, 1.0);\n"
"}\n");
sourceCollections.glslSources.add("color_frag") << glu::FragmentSource(
"#version 430\n"
"layout(push_constant) uniform quadColor {\n"
" uvec4 val;\n"
"} QUAD_COLOR;\n"
"layout(location = 0) out uvec4 fragColor;\n"
"void main (void)\n"
"{\n"
" fragColor = QUAD_COLOR.val;\n"
"}\n");
}
class LogicOpTestInstance : public vkt::TestInstance
{
public:
LogicOpTestInstance(Context& context,
const TestParams& params);
~LogicOpTestInstance(void);
virtual tcu::TestStatus iterate(void);
private:
tcu::TestStatus verifyImage(void);
TestParams m_params;
// Derived from m_params.
const tcu::TextureFormat m_tcuFormat;
const int m_numChannels;
const int m_channelSize;
const deUint32 m_channelMask;
const tcu::UVec2 m_renderSize;
VkImageCreateInfo m_colorImageCreateInfo;
de::MovePtr<ImageWithMemory> m_colorImage;
Move<VkImageView> m_colorAttachmentView;
Move<VkRenderPass> m_renderPass;
Move<VkFramebuffer> m_framebuffer;
Move<VkShaderModule> m_vertexShaderModule;
Move<VkShaderModule> m_fragmentShaderModule;
Move<VkPipelineLayout> m_pipelineLayout;
Move<VkPipeline> m_graphicsPipeline;
Move<VkCommandPool> m_cmdPool;
Move<VkCommandBuffer> m_cmdBuffer;
};
LogicOpTestInstance::LogicOpTestInstance (Context &ctx, const TestParams &testParams)
: vkt::TestInstance (ctx)
, m_params (testParams)
, m_tcuFormat (mapVkFormat(m_params.format))
, m_numChannels (tcu::getNumUsedChannels(m_tcuFormat.order))
, m_channelSize (tcu::getChannelSize(m_tcuFormat.type))
, m_channelMask (getChannelMask(m_channelSize))
, m_renderSize (32u, 32u)
{
DE_ASSERT(isUintFormat(m_params.format));
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
Allocator& memAlloc = m_context.getDefaultAllocator();
constexpr auto kPushConstantSize = static_cast<deUint32>(sizeof(m_params.quadColor));
// create color image
{
const VkImageCreateInfo colorImageParams =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_params.format, // VkFormat format;
{ m_renderSize.x(), m_renderSize.y(), 1u }, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
};
m_colorImageCreateInfo = colorImageParams;
m_colorImage = de::MovePtr<ImageWithMemory>(new ImageWithMemory(vk, vkDevice, memAlloc, m_colorImageCreateInfo, MemoryRequirement::Any));
// create color attachment view
const VkImageViewCreateInfo colorAttachmentViewParams =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
m_colorImage->get(), // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_params.format, // VkFormat format;
{ VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY },
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
};
m_colorAttachmentView = createImageView(vk, vkDevice, &colorAttachmentViewParams);
}
m_renderPass = makeRenderPass(vk, vkDevice, m_params.format);
m_framebuffer = makeFramebuffer(vk, vkDevice, *m_renderPass, m_colorAttachmentView.get(), m_renderSize.x(), m_renderSize.y());
// create pipeline layout
{
const VkPushConstantRange pcRange =
{
VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlags stageFlags;
0u, // deUint32 offset;
kPushConstantSize, // deUint32 size;
};
const VkPipelineLayoutCreateInfo pipelineLayoutParams =
{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineLayoutCreateFlags flags;
0u, // deUint32 setLayoutCount;
DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
1u, // deUint32 pushConstantRangeCount;
&pcRange, // const VkPushConstantRange* pPushConstantRanges;
};
m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
}
m_vertexShaderModule = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_vert"), 0);
m_fragmentShaderModule = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_frag"), 0);
// create pipeline
{
const VkPipelineVertexInputStateCreateInfo vertexInputStateParams = initVulkanStructure();
const std::vector<VkViewport> viewports (1, makeViewport(m_renderSize));
const std::vector<VkRect2D> scissors (1, makeRect2D(m_renderSize));
VkColorComponentFlags colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
const VkPipelineColorBlendAttachmentState blendAttachmentState =
{
VK_FALSE, // VkBool32 blendEnable;
(VkBlendFactor) 0, // VkBlendFactor srcColorBlendFactor;
(VkBlendFactor) 0, // VkBlendFactor dstColorBlendFactor;
(VkBlendOp) 0, // VkBlendOp colorBlendOp;
(VkBlendFactor) 0, // VkBlendFactor srcAlphaBlendFactor;
(VkBlendFactor) 0, // VkBlendFactor dstAlphaBlendFactor;
(VkBlendOp) 0, // VkBlendOp alphaBlendOp;
colorWriteMask, // VkColorComponentFlags colorWriteMask;
};
const VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
DE_NULL, // VkPipelineColorBlendStateCreateFlags flags;
VK_TRUE, // VkBool32 logicOpEnable;
m_params.logicOp, // VkLogicOp logicOp;
1u, // uint32_t attachmentCount;
&blendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
{ 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConstants[4];
};
m_graphicsPipeline = makeGraphicsPipeline(vk,
vkDevice, // const VkDevice device
*m_pipelineLayout, // const VkPipelineLayout pipelineLayout
*m_vertexShaderModule, // const VkShaderModule vertexShaderModule
DE_NULL, // const VkShaderModule tessellationControlModule
DE_NULL, // const VkShaderModule tessellationEvalModule
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_TRIANGLE_STRIP, // const VkPrimitiveTopology topology
0u, // const deUint32 subpass
0u, // const deUint32 patchControlPoints
&vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
DE_NULL, // const VkPipelineRasterizationStateCreateInfo* rasterizationStateCreateInfo
DE_NULL, // const VkPipelineMultisampleStateCreateInfo* multisampleStateCreateInfo
DE_NULL, // const VkPipelineDepthStencilStateCreateInfo* depthStencilStateCreateInfo
&colorBlendStateParams); // const VkPipelineColorBlendStateCreateInfo* colorBlendStateCreateInfo
}
// create command pool
m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex);
// allocate and record command buffer
{
// Prepare clear color value and quad color taking into account the channel mask.
VkClearValue attachmentClearValue;
tcu::UVec4 quadColor(0u, 0u, 0u, 0u);
deMemset(&attachmentClearValue.color, 0, sizeof(attachmentClearValue.color));
for (int c = 0; c < m_numChannels; ++c)
attachmentClearValue.color.uint32[c] = (m_params.fbColor[c] & m_channelMask);
for (int c = 0; c < m_numChannels; ++c)
quadColor[c] = (m_params.quadColor[c] & m_channelMask);
m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
beginCommandBuffer(vk, *m_cmdBuffer, 0u);
beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), attachmentClearValue);
// Update push constant values
vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0u, kPushConstantSize, &quadColor);
vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_graphicsPipeline);
vk.cmdDraw(*m_cmdBuffer, 4u, 1u, 0u, 0u);
endRenderPass(vk, *m_cmdBuffer);
endCommandBuffer(vk, *m_cmdBuffer);
}
}
LogicOpTestInstance::~LogicOpTestInstance (void)
{
}
tcu::TestStatus LogicOpTestInstance::iterate (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());
return verifyImage();
}
tcu::TestStatus LogicOpTestInstance::verifyImage (void)
{
const DeviceInterface& vk = 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();
auto& log = m_context.getTestContext().getLog();
const auto result = readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, m_colorImage->get(), m_params.format, m_renderSize).release();
const auto resultAccess = result->getAccess();
const int iWidth = static_cast<int>(m_renderSize.x());
const int iHeight = static_cast<int>(m_renderSize.y());
tcu::UVec4 expectedColor (0u, 0u, 0u, 0u); // Overwritten below.
tcu::TextureLevel referenceTexture (m_tcuFormat, iWidth, iHeight);
auto referenceAccess = referenceTexture.getAccess();
tcu::UVec4 threshold (0u, 0u, 0u, 0u); // Exact results.
// Calculate proper expected color values.
for (int c = 0; c < m_numChannels; ++c)
{
expectedColor[c] = calcOpResult(m_params.logicOp, m_params.quadColor[c], m_params.fbColor[c]);
expectedColor[c] &= m_channelMask;
}
for (int y = 0; y < iHeight; ++y)
for (int x = 0; x < iWidth; ++x)
referenceAccess.setPixel(expectedColor, x, y);
// Check result.
bool resultOk = tcu::intThresholdCompare(log, "TestResults", "Test Result Images", referenceAccess, resultAccess, threshold, tcu::COMPARE_LOG_ON_ERROR);
if (!resultOk)
TCU_FAIL("Result does not match expected values; check log for details");
return tcu::TestStatus::pass("Pass");
}
TestInstance *LogicOpTest::createInstance (Context& context) const
{
return new LogicOpTestInstance(context, m_params);
}
std::string getSimpleFormatName (VkFormat format)
{
return de::toLower(std::string(getFormatName(format)).substr(std::string("VK_FORMAT_").size()));
}
} // anonymous namespace
tcu::TestCaseGroup* createLogicOpTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> logicOpTests (new tcu::TestCaseGroup(testCtx, "logic_op", "Logical Operations tests"));
// 4 bits are enough to check all possible combinations of logical operation inputs at once, for example s AND d:
//
// 1 0 1 0
// AND 1 1 0 0
// ------------
// 1 0 0 0
//
// However, we will choose color values such that both higher bits and lower bits are used, and the implementation will not be
// able to mix channels by mistake.
//
// 0011 0101 1010 1100
// 3 5 a c
// 0101 0011 1100 1010
// 5 3 c a
const tcu::UVec4 kQuadColor = { 0x35acU, 0x5ac3U, 0xac35U, 0xc35aU };
const tcu::UVec4 kFbColor = { 0x53caU, 0x3ca5U, 0xca53U, 0xa53cU };
// Note: the format will be chosen and changed later.
std::vector<TestParams> logicOpTestParams =
{
{ VK_LOGIC_OP_CLEAR, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "clear" },
{ VK_LOGIC_OP_AND, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "and" },
{ VK_LOGIC_OP_AND_REVERSE, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "and_reverse" },
{ VK_LOGIC_OP_COPY, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "copy" },
{ VK_LOGIC_OP_AND_INVERTED, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "and_inverted" },
{ VK_LOGIC_OP_NO_OP, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "no_op" },
{ VK_LOGIC_OP_XOR, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "xor" },
{ VK_LOGIC_OP_OR, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "or" },
{ VK_LOGIC_OP_NOR, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "nor" },
{ VK_LOGIC_OP_EQUIVALENT, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "equivalent" },
{ VK_LOGIC_OP_INVERT, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "invert" },
{ VK_LOGIC_OP_OR_REVERSE, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "or_reverse" },
{ VK_LOGIC_OP_COPY_INVERTED, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "copy_inverted" },
{ VK_LOGIC_OP_OR_INVERTED, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "or_inverted" },
{ VK_LOGIC_OP_NAND, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "nand" },
{ VK_LOGIC_OP_SET, kFbColor, kQuadColor, VK_FORMAT_UNDEFINED, "set" },
};
const VkFormat formatList[] =
{
VK_FORMAT_R8_UINT,
VK_FORMAT_R8G8_UINT,
VK_FORMAT_R8G8B8_UINT,
VK_FORMAT_B8G8R8_UINT,
VK_FORMAT_R8G8B8A8_UINT,
VK_FORMAT_B8G8R8A8_UINT,
VK_FORMAT_R16_UINT,
VK_FORMAT_R16G16_UINT,
VK_FORMAT_R16G16B16_UINT,
VK_FORMAT_R16G16B16A16_UINT,
VK_FORMAT_R32_UINT,
VK_FORMAT_R32G32_UINT,
VK_FORMAT_R32G32B32_UINT,
VK_FORMAT_R32G32B32A32_UINT,
};
for (int formatIdx = 0; formatIdx < DE_LENGTH_OF_ARRAY(formatList); ++formatIdx)
{
const auto& format = formatList[formatIdx];
const auto formatName = getSimpleFormatName(format);
const auto formatDesc = "Logical operator tests with format " + formatName;
de::MovePtr<tcu::TestCaseGroup> formatGroup (new tcu::TestCaseGroup(testCtx, formatName.c_str(), formatDesc.c_str()));
for (auto& params : logicOpTestParams)
{
params.format = format;
formatGroup->addChild(new LogicOpTest(testCtx, params.name, "Tests the " + params.name + " logical operator", params));
}
logicOpTests->addChild(formatGroup.release());
}
return logicOpTests.release();
}
} // pipeline namespace
} // vkt namespace