blob: f5d4bbee6185db1f619b0f9f57b7236d33d171b6 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2017 The Khronos Group Inc.
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Protected memory YCbCr image conversion tests
*//*--------------------------------------------------------------------*/
#include "vktProtectedMemYCbCrConversionTests.hpp"
#include "tcuImageCompare.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuTestLog.hpp"
#include "vkBuilderUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkPrograms.hpp"
#include "vkTypeUtil.hpp"
#include "vkYCbCrImageWithMemory.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vktProtectedMemContext.hpp"
#include "vktProtectedMemUtils.hpp"
#include "vktTestCaseUtil.hpp"
#include "vktYCbCrUtil.hpp"
namespace vkt
{
namespace ProtectedMem
{
namespace
{
static const vk::VkFormat s_colorFormat = vk::VK_FORMAT_R8G8B8A8_UNORM;
enum {
CHECK_SIZE = 50,
};
struct YCbCrValidationData {
tcu::Vec4 coord;
tcu::Vec4 minBound;
tcu::Vec4 maxBound;
};
std::vector<tcu::Vec2> computeVertexPositions (int numValues, const tcu::IVec2& renderSize)
{
std::vector<tcu::Vec2> positions(numValues);
for (int valNdx = 0; valNdx < numValues; valNdx++)
{
const int ix = valNdx % renderSize.x();
const int iy = valNdx / renderSize.x();
const float fx = -1.0f + 2.0f*((float(ix) + 0.5f) / float(renderSize.x()));
const float fy = -1.0f + 2.0f*((float(iy) + 0.5f) / float(renderSize.y()));
positions[valNdx] = tcu::Vec2(fx, fy);
}
return positions;
}
void genTexCoords (std::vector<tcu::Vec2>& coords, const tcu::UVec2& size)
{
for (deUint32 y = 0; y < size.y(); y++)
for (deUint32 x = 0; x < size.x(); x++)
{
const float fx = (float)x;
const float fy = (float)y;
const float fw = (float)size.x();
const float fh = (float)size.y();
const float s = 1.5f * ((fx * 1.5f * fw + fx) / (1.5f * fw * 1.5f * fw)) - 0.25f;
const float t = 1.5f * ((fy * 1.5f * fh + fy) / (1.5f * fh * 1.5f * fh)) - 0.25f;
coords.push_back(tcu::Vec2(s, t));
}
}
struct TestConfig
{
TestConfig (glu::ShaderType shaderType_,
vk::VkFormat format_,
vk::VkImageTiling imageTiling_,
vk::VkFilter textureFilter_,
vk::VkSamplerAddressMode addressModeU_,
vk::VkSamplerAddressMode addressModeV_,
vk::VkFilter chromaFilter_,
vk::VkChromaLocation xChromaOffset_,
vk::VkChromaLocation yChromaOffset_,
bool explicitReconstruction_,
bool disjoint_,
vk::VkSamplerYcbcrRange colorRange_,
vk::VkSamplerYcbcrModelConversion colorModel_,
vk::VkComponentMapping componentMapping_)
: shaderType (shaderType_)
, format (format_)
, imageTiling (imageTiling_)
, textureFilter (textureFilter_)
, addressModeU (addressModeU_)
, addressModeV (addressModeV_)
, chromaFilter (chromaFilter_)
, xChromaOffset (xChromaOffset_)
, yChromaOffset (yChromaOffset_)
, explicitReconstruction (explicitReconstruction_)
, disjoint (disjoint_)
, colorRange (colorRange_)
, colorModel (colorModel_)
, componentMapping (componentMapping_)
{
}
glu::ShaderType shaderType;
vk::VkFormat format;
vk::VkImageTiling imageTiling;
vk::VkFilter textureFilter;
vk::VkSamplerAddressMode addressModeU;
vk::VkSamplerAddressMode addressModeV;
vk::VkFilter chromaFilter;
vk::VkChromaLocation xChromaOffset;
vk::VkChromaLocation yChromaOffset;
bool explicitReconstruction;
bool disjoint;
vk::VkSamplerYcbcrRange colorRange;
vk::VkSamplerYcbcrModelConversion colorModel;
vk::VkComponentMapping componentMapping;
};
void checkSupport (Context& context, const TestConfig)
{
checkProtectedQueueSupport(context);
}
void validateFormatSupport (ProtectedContext& context, TestConfig& config)
{
tcu::TestLog& log (context.getTestContext().getLog());
try
{
const vk::VkFormatProperties properties (vk::getPhysicalDeviceFormatProperties(context.getInstanceDriver(), context.getPhysicalDevice(), config.format));
const vk::VkFormatFeatureFlags features (config.imageTiling == vk::VK_IMAGE_TILING_OPTIMAL
? properties.optimalTilingFeatures
: properties.linearTilingFeatures);
if ((features & (vk::VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT | vk::VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT)) == 0)
TCU_THROW(NotSupportedError, "Format doesn't support YCbCr conversions");
if ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) == 0)
TCU_THROW(NotSupportedError, "Format doesn't support sampling");
if (config.textureFilter == vk::VK_FILTER_LINEAR && ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support YCbCr linear chroma reconstruction");
if (config.chromaFilter == vk::VK_FILTER_LINEAR && ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support YCbCr linear chroma reconstruction");
if (config.chromaFilter != config.textureFilter && ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support different chroma and texture filters");
if (config.explicitReconstruction && ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support explicit chroma reconstruction");
if (config.disjoint && ((features & vk::VK_FORMAT_FEATURE_DISJOINT_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't disjoint planes");
if (ycbcr::isXChromaSubsampled(config.format) && (config.xChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN) && ((features & vk::VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support cosited chroma samples");
if (ycbcr::isXChromaSubsampled(config.format) && (config.xChromaOffset == vk::VK_CHROMA_LOCATION_MIDPOINT) && ((features & vk::VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support midpoint chroma samples");
if (ycbcr::isYChromaSubsampled(config.format) && (config.yChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN) && ((features & vk::VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support cosited chroma samples");
if (ycbcr::isYChromaSubsampled(config.format) && (config.yChromaOffset == vk::VK_CHROMA_LOCATION_MIDPOINT) && ((features & vk::VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT) == 0))
TCU_THROW(NotSupportedError, "Format doesn't support midpoint chroma samples");
if ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT) != 0)
config.explicitReconstruction = true;
log << tcu::TestLog::Message << "FormatFeatures: " << vk::getFormatFeatureFlagsStr(features) << tcu::TestLog::EndMessage;
}
catch (const vk::Error& err)
{
if (err.getError() == vk::VK_ERROR_FORMAT_NOT_SUPPORTED)
TCU_THROW(NotSupportedError, "Format not supported");
throw;
}
}
vk::Move<vk::VkSampler> createSampler (const vk::DeviceInterface& vkd,
const vk::VkDevice device,
const vk::VkFilter textureFilter,
const vk::VkSamplerAddressMode addressModeU,
const vk::VkSamplerAddressMode addressModeV,
const vk::VkSamplerYcbcrConversion conversion)
{
const vk::VkSamplerYcbcrConversionInfo samplerConversionInfo =
{
vk::VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
DE_NULL,
conversion
};
const vk::VkSamplerCreateInfo createInfo =
{
vk::VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
&samplerConversionInfo,
0u,
textureFilter,
textureFilter,
vk::VK_SAMPLER_MIPMAP_MODE_NEAREST,
addressModeU,
addressModeV,
vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
0.0f,
VK_FALSE,
1.0f,
VK_FALSE,
vk::VK_COMPARE_OP_ALWAYS,
0.0f,
0.0f,
vk::VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
VK_FALSE,
};
return createSampler(vkd, device, &createInfo);
}
vk::Move<vk::VkImageView> createImageView (const vk::DeviceInterface& vkd,
const vk::VkDevice device,
const vk::VkImage image,
const vk::VkFormat format,
const vk::VkSamplerYcbcrConversion conversion)
{
const vk::VkSamplerYcbcrConversionInfo conversionInfo =
{
vk::VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
DE_NULL,
conversion
};
const vk::VkImageViewCreateInfo viewInfo =
{
vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
&conversionInfo,
(vk::VkImageViewCreateFlags)0,
image,
vk::VK_IMAGE_VIEW_TYPE_2D,
format,
{
vk::VK_COMPONENT_SWIZZLE_IDENTITY,
vk::VK_COMPONENT_SWIZZLE_IDENTITY,
vk::VK_COMPONENT_SWIZZLE_IDENTITY,
vk::VK_COMPONENT_SWIZZLE_IDENTITY,
},
{ vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u },
};
return vk::createImageView(vkd, device, &viewInfo);
}
vk::Move<vk::VkSamplerYcbcrConversion> createConversion (const vk::DeviceInterface& vkd,
const vk::VkDevice device,
const vk::VkFormat format,
const vk::VkSamplerYcbcrModelConversion colorModel,
const vk::VkSamplerYcbcrRange colorRange,
const vk::VkChromaLocation xChromaOffset,
const vk::VkChromaLocation yChromaOffset,
const vk::VkFilter chromaFilter,
const vk::VkComponentMapping& componentMapping,
const bool explicitReconstruction)
{
const vk::VkSamplerYcbcrConversionCreateInfo conversionInfo =
{
vk::VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
DE_NULL,
format,
colorModel,
colorRange,
componentMapping,
xChromaOffset,
yChromaOffset,
chromaFilter,
explicitReconstruction ? VK_TRUE : VK_FALSE
};
return vk::createSamplerYcbcrConversion(vkd, device, &conversionInfo);
}
void uploadYCbCrImage (ProtectedContext& ctx,
const vk::VkImage image,
const ycbcr::MultiPlaneImageData& imageData,
const vk::VkAccessFlags nextAccess,
const vk::VkImageLayout finalLayout)
{
const vk::DeviceInterface& vk = ctx.getDeviceInterface();
const vk::VkDevice device = ctx.getDevice();
const vk::VkQueue queue = ctx.getQueue();
const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
const vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
const vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
const vk::PlanarFormatDescription& formatDesc = imageData.getDescription();
std::vector<de::SharedPtr<de::MovePtr<vk::BufferWithMemory> > > stagingBuffers;
std::vector<vk::VkBufferMemoryBarrier> bufferBarriers;
for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
{
de::MovePtr<vk::BufferWithMemory> buffer (makeBuffer(ctx,
PROTECTION_DISABLED,
queueFamilyIndex,
(deUint32)imageData.getPlaneSize(planeNdx),
vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT|vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
vk::MemoryRequirement::HostVisible));
const vk::VkBufferMemoryBarrier bufferBarrier =
{
vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
DE_NULL,
(vk::VkAccessFlags)0,
vk::VK_ACCESS_TRANSFER_READ_BIT,
queueFamilyIndex,
queueFamilyIndex,
**buffer,
0,
(deUint32)imageData.getPlaneSize(planeNdx)
};
bufferBarriers.push_back(bufferBarrier);
deMemcpy(buffer->getAllocation().getHostPtr(), imageData.getPlanePtr(planeNdx), imageData.getPlaneSize(planeNdx));
flushMappedMemoryRange(vk, device, buffer->getAllocation().getMemory(), buffer->getAllocation().getOffset(), (deUint32)imageData.getPlaneSize(planeNdx));
stagingBuffers.push_back(de::SharedPtr<de::MovePtr<vk::BufferWithMemory> >(new de::MovePtr<vk::BufferWithMemory>(buffer.release())));
}
beginCommandBuffer(vk, *cmdBuffer);
for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
{
const vk::VkImageAspectFlagBits aspect = formatDesc.numPlanes > 1
? vk::getPlaneAspect(planeNdx)
: vk::VK_IMAGE_ASPECT_COLOR_BIT;
const vk::VkImageMemoryBarrier preCopyBarrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
(vk::VkAccessFlags)0,
vk::VK_ACCESS_TRANSFER_WRITE_BIT,
vk::VK_IMAGE_LAYOUT_UNDEFINED,
vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
queueFamilyIndex,
queueFamilyIndex,
image,
{ aspect, 0u, 1u, 0u, 1u }
};
vk.cmdPipelineBarrier(*cmdBuffer,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_HOST_BIT,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
(vk::VkDependencyFlags)0u,
0u, (const vk::VkMemoryBarrier*)DE_NULL,
(deUint32)bufferBarriers.size(), &bufferBarriers[0],
1u, &preCopyBarrier);
}
for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
{
const vk::VkImageAspectFlagBits aspect = (formatDesc.numPlanes > 1)
? vk::getPlaneAspect(planeNdx)
: vk::VK_IMAGE_ASPECT_COLOR_BIT;
const deUint32 planeW = (formatDesc.numPlanes > 1)
? imageData.getSize().x() / formatDesc.planes[planeNdx].widthDivisor
: imageData.getSize().x();
const deUint32 planeH = (formatDesc.numPlanes > 1)
? imageData.getSize().y() / formatDesc.planes[planeNdx].heightDivisor
: imageData.getSize().y();
const vk::VkBufferImageCopy copy =
{
0u, // bufferOffset
0u, // bufferRowLength
0u, // bufferImageHeight
{ (vk::VkImageAspectFlags)aspect, 0u, 0u, 1u },
vk::makeOffset3D(0u, 0u, 0u),
vk::makeExtent3D(planeW, planeH, 1u),
};
vk.cmdCopyBufferToImage(*cmdBuffer, ***stagingBuffers[planeNdx], image, vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, &copy);
}
for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
{
const vk::VkImageAspectFlagBits aspect = formatDesc.numPlanes > 1
? vk::getPlaneAspect(planeNdx)
: vk::VK_IMAGE_ASPECT_COLOR_BIT;
const vk::VkImageMemoryBarrier postCopyBarrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
vk::VK_ACCESS_TRANSFER_WRITE_BIT,
nextAccess,
vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
finalLayout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
image,
{ aspect, 0u, 1u, 0u, 1u }
};
vk.cmdPipelineBarrier(*cmdBuffer,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
(vk::VkDependencyFlags)0u,
0u, (const vk::VkMemoryBarrier*)DE_NULL,
0u, (const vk::VkBufferMemoryBarrier*)DE_NULL,
1u, &postCopyBarrier);
}
endCommandBuffer(vk, *cmdBuffer);
{
const vk::Unique<vk::VkFence> fence (createFence(vk, device));
VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
}
}
void logTestCaseInfo (tcu::TestLog& log, const TestConfig& config)
{
log << tcu::TestLog::Message << "ShaderType: " << config.shaderType << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Format: " << config.format << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "ImageTiling: " << config.imageTiling << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "TextureFilter: " << config.textureFilter << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "AddressModeU: " << config.addressModeU << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "AddressModeV: " << config.addressModeV << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "ChromaFilter: " << config.chromaFilter << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "XChromaOffset: " << config.xChromaOffset << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "YChromaOffset: " << config.yChromaOffset << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "ExplicitReconstruction: " << (config.explicitReconstruction ? "true" : "false") << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Disjoint: " << (config.disjoint ? "true" : "false") << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "ColorRange: " << config.colorRange << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "ColorModel: " << config.colorModel << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "ComponentMapping: " << config.componentMapping << tcu::TestLog::EndMessage;
}
void logBoundImages (tcu::TestLog& log, const tcu::UVec2 size, const std::vector<tcu::Vec4>& minBounds, const std::vector<tcu::Vec4>& maxBounds)
{
tcu::TextureLevel minImage (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), size.x(), size.y());
tcu::TextureLevel maxImage (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), size.x(), size.y());
for (int y = 0; y < (int)(size.y()); y++)
for (int x = 0; x < (int)(size.x()); x++)
{
const int ndx = x + y * (int)(size.x());
minImage.getAccess().setPixel(minBounds[ndx], x, y);
maxImage.getAccess().setPixel(maxBounds[ndx], x, y);
}
const tcu::Vec4 scale (1.0f);
const tcu::Vec4 bias (0.0f);
log << tcu::TestLog::Image("MinBoundImage", "MinBoundImage", minImage.getAccess(), scale, bias);
log << tcu::TestLog::Image("MaxBoundImage", "MaxBoundImage", maxImage.getAccess(), scale, bias);
}
bool validateImage (ProtectedContext& ctx,
const std::vector<YCbCrValidationData>& refData,
const vk::VkSampler sampler,
const vk::VkImageView imageView)
{
{
tcu::TestLog& log (ctx.getTestContext().getLog());
log << tcu::TestLog::Message << "Reference values:" << tcu::TestLog::EndMessage;
for (deUint32 ndx = 0; ndx < refData.size(); ndx++)
{
log << tcu::TestLog::Message << (ndx + 1) << refData[ndx].coord << ": [" << refData[ndx].minBound << ", " << refData[ndx].maxBound << "]" << tcu::TestLog::EndMessage;
}
}
const deUint64 oneSec = 1000 * 1000 * 1000;
const vk::DeviceInterface& vk = ctx.getDeviceInterface();
const vk::VkDevice device = ctx.getDevice();
const vk::VkQueue queue = ctx.getQueue();
const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
DE_ASSERT(refData.size() >= CHECK_SIZE && CHECK_SIZE > 0);
const deUint32 refUniformSize = (deUint32)(sizeof(YCbCrValidationData) * refData.size());
const de::UniquePtr<vk::BufferWithMemory> refUniform (makeBuffer(ctx,
PROTECTION_DISABLED,
queueFamilyIndex,
refUniformSize,
vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
vk::MemoryRequirement::HostVisible));
// Set the reference uniform data
{
deMemcpy(refUniform->getAllocation().getHostPtr(), &refData[0], refUniformSize);
vk::flushMappedMemoryRange(vk, device, refUniform->getAllocation().getMemory(), refUniform->getAllocation().getOffset(), refUniformSize);
}
const deUint32 helperBufferSize = (deUint32)(2 * sizeof(deUint32));
const de::MovePtr<vk::BufferWithMemory> helperBuffer (makeBuffer(ctx,
PROTECTION_ENABLED,
queueFamilyIndex,
helperBufferSize,
vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
vk::MemoryRequirement::Protected));
const vk::Unique<vk::VkShaderModule> resetSSBOShader (vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("ResetSSBO"), 0));
const vk::Unique<vk::VkShaderModule> validatorShader (vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("ImageValidator"), 0));
// Create descriptors
const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(vk::DescriptorSetLayoutBuilder()
.addSingleSamplerBinding(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, vk::VK_SHADER_STAGE_COMPUTE_BIT, &sampler)
.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
.build(vk, device));
const vk::Unique<vk::VkDescriptorPool> descriptorPool(vk::DescriptorPoolBuilder()
.addType(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1u)
.addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u)
.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
const vk::Unique<vk::VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
// Update descriptor set infirmation
{
vk::VkDescriptorBufferInfo descRefUniform = makeDescriptorBufferInfo(**refUniform, 0, refUniformSize);
vk::VkDescriptorBufferInfo descBuffer = makeDescriptorBufferInfo(**helperBuffer, 0, helperBufferSize);
vk::VkDescriptorImageInfo descSampledImg = makeDescriptorImageInfo(sampler, imageView, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vk::DescriptorSetUpdateBuilder()
.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &descSampledImg)
.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descRefUniform)
.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descBuffer)
.update(vk, device);
}
const vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
const vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
// Reset helper SSBO
{
const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
const vk::Unique<vk::VkPipeline> resetSSBOPipeline (makeComputePipeline(vk, device, *pipelineLayout, *resetSSBOShader, DE_NULL));
const vk::Unique<vk::VkCommandBuffer> resetCmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
beginCommandBuffer(vk, *resetCmdBuffer);
vk.cmdBindPipeline(*resetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *resetSSBOPipeline);
vk.cmdBindDescriptorSets(*resetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
vk.cmdDispatch(*resetCmdBuffer, 1u, 1u, 1u);
endCommandBuffer(vk, *resetCmdBuffer);
VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *resetCmdBuffer, *fence, ~0ull));
}
// Create validation compute commands & submit
vk::VkResult queueSubmitResult;
{
const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
const vk::Unique<vk::VkPipeline> validationPipeline (makeComputePipeline(vk, device, *pipelineLayout, *validatorShader, DE_NULL));
const vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
beginCommandBuffer(vk, *cmdBuffer);
vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *validationPipeline);
vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
vk.cmdDispatch(*cmdBuffer, CHECK_SIZE, 1u, 1u);
endCommandBuffer(vk, *cmdBuffer);
queueSubmitResult = queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, oneSec * 5);
}
// \todo do we need to check the fence status?
if (queueSubmitResult == vk::VK_TIMEOUT)
return false;
// at this point the submit result should be VK_TRUE
VK_CHECK(queueSubmitResult);
return true;
}
void testShaders (vk::SourceCollections& dst, const TestConfig config)
{
const char* const shaderHeader =
"layout(constant_id = 1) const float threshold = 0.01f;\n"
"layout(set = 0, binding = 0) uniform highp sampler2D protectedImage;\n"
"\n"
"struct validationData {\n"
" highp vec4 imageCoord;\n"
" highp vec4 imageRefMinBound;\n"
" highp vec4 imageRefMaxBound;\n"
"};\n"
"layout(std140, set = 0, binding = 1) uniform Data\n"
"{\n"
" validationData ref[250];\n"
"};\n";
const char* const compareFunction =
"bool compare(highp vec4 value, highp vec4 minValue, highp vec4 maxValue)\n"
"{\n"
" return all(greaterThanEqual(value, minValue - threshold)) && all(lessThanEqual(value, maxValue + threshold));\n"
"}\n";
std::map<std::string, std::string> validatorSpec;
validatorSpec["CHECK_SIZE"] = de::toString((deUint32)CHECK_SIZE);
validatorSpec["SHADER_HEADER"] = shaderHeader;
validatorSpec["COMPARE_FUNCTION"] = compareFunction;
const char* const validatorShader =
"#version 450\n"
"\n"
"${SHADER_HEADER}"
"\n"
"layout(std140, set = 0, binding = 2) buffer ProtectedHelper\n"
"{\n"
" highp uint zero;\n"
" highp uint dummyOut;\n"
"} helper;\n"
"\n"
"void error()\n"
"{\n"
" for (uint x = 0u; x < 10u; x += helper.zero)\n"
" atomicAdd(helper.dummyOut, 1u);\n"
"}\n"
"\n"
"${COMPARE_FUNCTION}"
"\n"
"void main(void)\n"
"{\n"
" int idx = int(gl_GlobalInvocationID.x);\n"
" vec4 currentValue = texture(protectedImage, ref[idx].imageCoord.xy);\n"
" if (!compare(currentValue, ref[idx].imageRefMinBound, ref[idx].imageRefMaxBound))\n"
" {\n"
" error();\n"
" }\n"
"}\n";
const char* const resetSSBOShader =
"#version 450\n"
"layout(local_size_x = 1) in;\n"
"\n"
"layout(std140, set=0, binding=2) buffer ProtectedHelper\n"
"{\n"
" highp uint zero; // set to 0\n"
" highp uint dummyOut;\n"
"} helper;\n"
"\n"
"void main (void)\n"
"{\n"
" helper.zero = 0;\n"
" helper.dummyOut = 0;\n"
"}\n";
dst.glslSources.add("ResetSSBO") << glu::ComputeSource(resetSSBOShader);
dst.glslSources.add("ImageValidator") << glu::ComputeSource(tcu::StringTemplate(validatorShader).specialize(validatorSpec));
if (config.shaderType == glu::SHADERTYPE_COMPUTE)
return; // Bail early as the YCbCr image validator already have the test programs set for compute tests
const char* const compareOperation =
" highp vec4 currentValue = texture(protectedImage, ref[v_idx].imageCoord.xy);\n"
" if (compare(currentValue, ref[v_idx].imageRefMinBound, ref[v_idx].imageRefMaxBound))\n"
" {\n"
" o_color = vec4(0.0f, 1.0f, 0.0f, 1.0f);\n" // everything is ok, green
" }\n"
" else"
" {\n"
" o_color = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
" }\n";
std::map<std::string, std::string> shaderSpec;
shaderSpec["SHADER_HEADER"] = shaderHeader;
shaderSpec["COMPARE_FUNCTION"] = compareFunction;
shaderSpec["COMPARE_OPERATION"] = compareOperation;
if (config.shaderType == glu::SHADERTYPE_VERTEX)
{
const char* const vertexShader =
"#version 450\n"
"${SHADER_HEADER}\n"
"\n"
"layout(location = 0) in highp vec2 a_position;\n"
"layout(location = 0) flat out highp vec4 o_color;\n"
"\n"
"${COMPARE_FUNCTION}"
"\n"
"void main(void)\n"
"{\n"
" gl_Position = vec4(a_position, 0.0f, 1.0f);\n"
" gl_PointSize = 1.0f;\n"
" int v_idx = gl_VertexIndex;\n"
"${COMPARE_OPERATION}"
"}\n";
const char* const fragmentShader =
"#version 450\n"
"\n"
"layout(location = 0) flat in highp vec4 v_color;\n"
"layout(location = 0) out highp vec4 o_color;\n"
"\n"
"void main(void)\n"
"{\n"
" o_color = v_color;\n"
"}\n";
dst.glslSources.add("vert") << glu::VertexSource(tcu::StringTemplate(vertexShader).specialize(shaderSpec));
dst.glslSources.add("frag") << glu::FragmentSource(fragmentShader);
}
else if (config.shaderType == glu::SHADERTYPE_FRAGMENT)
{
const char* const vertexShader =
"#version 450\n"
"layout(location = 0) in highp vec2 a_position;\n"
"layout(location = 0) flat out highp int o_idx;\n"
"\n"
"void main(void)\n"
"{\n"
" gl_Position = vec4(a_position, 0.0f, 1.0f);\n"
" gl_PointSize = 1.0f;\n"
" o_idx = gl_VertexIndex;\n"
"}\n";
const char* const fragmentShader =
"#version 450\n"
"${SHADER_HEADER}\n"
"\n"
"layout(location = 0) flat in highp int v_idx;\n"
"layout(location = 0) out highp vec4 o_color;\n"
"\n"
"${COMPARE_FUNCTION}"
"\n"
"void main(void)\n"
"{\n"
"${COMPARE_OPERATION}"
"}\n";
dst.glslSources.add("vert") << glu::VertexSource(vertexShader);
dst.glslSources.add("frag") << glu::FragmentSource(tcu::StringTemplate(fragmentShader).specialize(shaderSpec));
}
}
de::MovePtr<vk::YCbCrImageWithMemory> createYcbcrImage2D (ProtectedContext& context,
const ProtectionMode protectionMode,
const deUint32 width,
const deUint32 height,
const vk::VkFormat format,
const vk::VkImageCreateFlags createFlags,
const vk::VkImageUsageFlags usageFlags)
{
const vk::DeviceInterface& vk = context.getDeviceInterface();
const vk::VkDevice& device = context.getDevice();
vk::Allocator& allocator = context.getDefaultAllocator();
const deUint32 queueIdx = context.getQueueFamilyIndex();
#ifndef NOT_PROTECTED
const deUint32 flags = (protectionMode == PROTECTION_ENABLED) ? vk::VK_IMAGE_CREATE_PROTECTED_BIT : 0x0;
const vk::MemoryRequirement memReq = (protectionMode == PROTECTION_ENABLED) ? vk::MemoryRequirement::Protected : vk::MemoryRequirement::Any;
#else
const deUint32 flags = 0x0;
const vk::MemoryRequirement memReq = vk::MemoryRequirement::Any;
DE_UNREF(protectionMode);
#endif
const vk::VkImageCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType stype
DE_NULL, // const void* pNext
(vk::VkImageCreateFlags)(flags | createFlags), // VkImageCreateFlags flags
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType
format, // VkFormat format
{ width, height, 1 }, // VkExtent3D extent
1u, // deUint32 mipLevels
1u, // deUint32 arrayLayers
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling
usageFlags, // VkImageUsageFlags usage
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
1u, // deUint32 queueFamilyIndexCount
&queueIdx, // const deUint32* pQueueFamilyIndices
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout
};
return de::MovePtr<vk::YCbCrImageWithMemory>(new vk::YCbCrImageWithMemory(vk, device, allocator, params, memReq));
}
void renderYCbCrToColor (ProtectedContext& ctx,
const tcu::UVec2 size,
const vk::VkSampler ycbcrSampler,
const vk::VkImageView ycbcrImageView,
const vk::VkImage colorImage,
const vk::VkImageView colorImageView,
const std::vector<YCbCrValidationData>& referenceData,
const std::vector<tcu::Vec2>& posCoords)
{
const vk::DeviceInterface& vk = ctx.getDeviceInterface();
const vk::VkDevice device = ctx.getDevice();
const vk::VkQueue queue = ctx.getQueue();
const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
const vk::Unique<vk::VkRenderPass> renderPass (createRenderPass(ctx, s_colorFormat));
const vk::Unique<vk::VkFramebuffer> framebuffer (createFramebuffer(ctx, size.x(), size.y(), *renderPass, colorImageView));
const vk::Unique<vk::VkShaderModule> vertexShader (createShaderModule(vk, device, ctx.getBinaryCollection().get("vert"), 0));
const vk::Unique<vk::VkShaderModule> fragmentShader (createShaderModule(vk, device, ctx.getBinaryCollection().get("frag"), 0));
const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout (vk::DescriptorSetLayoutBuilder()
.addSingleSamplerBinding(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
vk::VK_SHADER_STAGE_ALL,
&ycbcrSampler)
.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_ALL)
.build(vk, device));
const vk::Unique<vk::VkDescriptorPool> descriptorPool (vk::DescriptorPoolBuilder()
.addType(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1u)
.addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u)
.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
const vk::Unique<vk::VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
const vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
const deUint32 refUniformSize = (deUint32)(sizeof(YCbCrValidationData) * referenceData.size());
const de::UniquePtr<vk::BufferWithMemory> refUniform (makeBuffer(ctx,
PROTECTION_DISABLED,
queueFamilyIndex,
refUniformSize,
vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
vk::MemoryRequirement::HostVisible));
// Set the reference uniform data
{
deMemcpy(refUniform->getAllocation().getHostPtr(), &referenceData[0], refUniformSize);
vk::flushMappedMemoryRange(vk, device, refUniform->getAllocation().getMemory(), refUniform->getAllocation().getOffset(), refUniformSize);
}
// Update descriptor set
{
vk::VkDescriptorImageInfo ycbcrSampled (makeDescriptorImageInfo(ycbcrSampler, ycbcrImageView, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL));
vk::VkDescriptorBufferInfo descRefUniform = makeDescriptorBufferInfo(**refUniform, 0, refUniformSize);
vk::DescriptorSetUpdateBuilder()
.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &ycbcrSampled)
.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descRefUniform)
.update(vk, device);
}
VertexBindings vertexBindings;
VertexAttribs vertexAttribs;
de::MovePtr<vk::BufferWithMemory> vertexBuffer;
{
const deUint32 bufferSize = (deUint32)(sizeof(tcu::Vec2) * posCoords.size());
{
const vk::VkVertexInputBindingDescription inputBinding =
{
0u, // deUint32 binding;
sizeof(tcu::Vec2), // deUint32 strideInBytes;
vk::VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate;
};
const vk::VkVertexInputAttributeDescription inputAttribute =
{
0u, // deUint32 location;
0u, // deUint32 binding;
vk::VK_FORMAT_R32G32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
};
vertexBindings.push_back(inputBinding);
vertexAttribs.push_back(inputAttribute);
}
vertexBuffer = makeBuffer(ctx,
PROTECTION_DISABLED,
queueFamilyIndex,
bufferSize,
vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
vk::MemoryRequirement::HostVisible);
deMemcpy(vertexBuffer->getAllocation().getHostPtr(), &posCoords[0], bufferSize);
vk::flushMappedMemoryRange(vk, device, vertexBuffer->getAllocation().getMemory(), vertexBuffer->getAllocation().getOffset(), bufferSize);
}
const vk::Unique<vk::VkPipeline> pipeline (makeGraphicsPipeline(vk,
device,
*pipelineLayout,
*renderPass,
*vertexShader,
*fragmentShader,
vertexBindings,
vertexAttribs,
size,
vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST));
const vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
const vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
beginCommandBuffer(vk, *cmdBuffer);
{
const vk::VkImageMemoryBarrier attachmentStartBarrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
0u,
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
vk::VK_IMAGE_LAYOUT_UNDEFINED,
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
queueFamilyIndex,
queueFamilyIndex,
colorImage,
{ vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }
};
vk.cmdPipelineBarrier(*cmdBuffer,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
(vk::VkDependencyFlags)0u,
0u, (const vk::VkMemoryBarrier*)DE_NULL,
0u, (const vk::VkBufferMemoryBarrier*)DE_NULL,
1u, &attachmentStartBarrier);
}
beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, vk::makeRect2D(0, 0, size.x(), size.y()), tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f));
vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
{
const vk::VkDeviceSize vertexBufferOffset = 0;
vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &**vertexBuffer, &vertexBufferOffset);
}
vk.cmdDraw(*cmdBuffer, /*vertexCount*/ (deUint32)posCoords.size(), 1u, 0u, 0u);
endRenderPass(vk, *cmdBuffer);
// color attachment render end barrier
{
const vk::VkImageMemoryBarrier attachmentEndBarrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
vk::VK_ACCESS_SHADER_READ_BIT,
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
queueFamilyIndex,
queueFamilyIndex,
colorImage,
{ vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }
};
vk.cmdPipelineBarrier(*cmdBuffer,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
(vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
(vk::VkDependencyFlags)0u,
0u, (const vk::VkMemoryBarrier*)DE_NULL,
0u, (const vk::VkBufferMemoryBarrier*)DE_NULL,
1u, &attachmentEndBarrier);
}
endCommandBuffer(vk, *cmdBuffer);
// Submit command buffer
{
const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
}
}
void generateYCbCrImage (ProtectedContext& ctx,
const TestConfig& config,
const tcu::UVec2 size,
const std::vector<tcu::Vec2>& texCoords,
ycbcr::MultiPlaneImageData& ycbcrSrc,
std::vector<tcu::Vec4>& ycbcrMinBounds,
std::vector<tcu::Vec4>& ycbcrMaxBounds)
{
tcu::TestLog& log (ctx.getTestContext().getLog());
const std::vector<tcu::FloatFormat> filteringPrecision (ycbcr::getPrecision(config.format));
const std::vector<tcu::FloatFormat> conversionPrecision (ycbcr::getPrecision(config.format));
const tcu::UVec4 bitDepth (ycbcr::getYCbCrBitDepth(config.format));
bool explicitReconstruction = config.explicitReconstruction;
const deUint32 subTexelPrecisionBits (vk::getPhysicalDeviceProperties(ctx.getInstanceDriver(),
ctx.getPhysicalDevice()).limits.subTexelPrecisionBits);
const vk::PlanarFormatDescription planeInfo (vk::getPlanarFormatDescription(config.format));
deUint32 nullAccessData (0u);
ycbcr::ChannelAccess nullAccess (tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT, 1u, tcu::IVec3(size.x(), size.y(), 1), tcu::IVec3(0, 0, 0), &nullAccessData, 0u);
deUint32 nullAccessAlphaData (~0u);
ycbcr::ChannelAccess nullAccessAlpha (tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT, 1u, tcu::IVec3(size.x(), size.y(), 1), tcu::IVec3(0, 0, 0), &nullAccessAlphaData, 0u);
ycbcr::ChannelAccess rChannelAccess (planeInfo.hasChannelNdx(0) ? getChannelAccess(ycbcrSrc, planeInfo, size, 0) : nullAccess);
ycbcr::ChannelAccess gChannelAccess (planeInfo.hasChannelNdx(1) ? getChannelAccess(ycbcrSrc, planeInfo, size, 1) : nullAccess);
ycbcr::ChannelAccess bChannelAccess (planeInfo.hasChannelNdx(2) ? getChannelAccess(ycbcrSrc, planeInfo, size, 2) : nullAccess);
ycbcr::ChannelAccess aChannelAccess (planeInfo.hasChannelNdx(3) ? getChannelAccess(ycbcrSrc, planeInfo, size, 3) : nullAccessAlpha);
const bool implicitNearestCosited ((config.chromaFilter == vk::VK_FILTER_NEAREST && !explicitReconstruction) &&
(config.xChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN_KHR || config.yChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN_KHR));
for (deUint32 planeNdx = 0; planeNdx < planeInfo.numPlanes; planeNdx++)
deMemset(ycbcrSrc.getPlanePtr(planeNdx), 0u, ycbcrSrc.getPlaneSize(planeNdx));
// \todo Limit values to only values that produce defined values using selected colorRange and colorModel? The verification code handles those cases already correctly.
if (planeInfo.hasChannelNdx(0))
{
for (int y = 0; y < rChannelAccess.getSize().y(); y++)
for (int x = 0; x < rChannelAccess.getSize().x(); x++)
rChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)x / (float)rChannelAccess.getSize().x());
}
if (planeInfo.hasChannelNdx(1))
{
for (int y = 0; y < gChannelAccess.getSize().y(); y++)
for (int x = 0; x < gChannelAccess.getSize().x(); x++)
gChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)y / (float)gChannelAccess.getSize().y());
}
if (planeInfo.hasChannelNdx(2))
{
for (int y = 0; y < bChannelAccess.getSize().y(); y++)
for (int x = 0; x < bChannelAccess.getSize().x(); x++)
bChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)(x + y) / (float)(bChannelAccess.getSize().x() + bChannelAccess.getSize().y()));
}
if (planeInfo.hasChannelNdx(3))
{
for (int y = 0; y < aChannelAccess.getSize().y(); y++)
for (int x = 0; x < aChannelAccess.getSize().x(); x++)
aChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)(x * y) / (float)(aChannelAccess.getSize().x() * aChannelAccess.getSize().y()));
}
std::vector<tcu::Vec4> uvBounds;
std::vector<tcu::IVec4> ijBounds;
ycbcr::calculateBounds(rChannelAccess, gChannelAccess, bChannelAccess, aChannelAccess, bitDepth, texCoords, filteringPrecision, conversionPrecision, subTexelPrecisionBits, config.textureFilter, config.colorModel, config.colorRange, config.chromaFilter, config.xChromaOffset, config.yChromaOffset, config.componentMapping, explicitReconstruction, config.addressModeU, config.addressModeV, ycbcrMinBounds, ycbcrMaxBounds, uvBounds, ijBounds);
// Handle case: If implicit reconstruction and chromaFilter == NEAREST, an implementation may behave as if both chroma offsets are MIDPOINT.
if (implicitNearestCosited)
{
std::vector<tcu::Vec4> relaxedYcbcrMinBounds;
std::vector<tcu::Vec4> relaxedYcbcrMaxBounds;
ycbcr::calculateBounds(rChannelAccess, gChannelAccess, bChannelAccess, aChannelAccess, bitDepth, texCoords, filteringPrecision, conversionPrecision, subTexelPrecisionBits, config.textureFilter, config.colorModel, config.colorRange, config.chromaFilter, vk::VK_CHROMA_LOCATION_MIDPOINT_KHR, vk::VK_CHROMA_LOCATION_MIDPOINT_KHR, config.componentMapping, explicitReconstruction, config.addressModeU, config.addressModeV, relaxedYcbcrMinBounds, relaxedYcbcrMaxBounds, uvBounds, ijBounds);
DE_ASSERT(relaxedYcbcrMinBounds.size() == ycbcrMinBounds.size());
DE_ASSERT(relaxedYcbcrMaxBounds.size() == ycbcrMaxBounds.size());
for (size_t i = 0; i < ycbcrMinBounds.size(); i++)
{
ycbcrMinBounds[i] = tcu::Vec4(de::min<float>(ycbcrMinBounds[i].x(), relaxedYcbcrMinBounds[i].x()),
de::min<float>(ycbcrMinBounds[i].y(), relaxedYcbcrMinBounds[i].y()),
de::min<float>(ycbcrMinBounds[i].z(), relaxedYcbcrMinBounds[i].z()),
de::min<float>(ycbcrMinBounds[i].w(), relaxedYcbcrMinBounds[i].w()));
ycbcrMaxBounds[i] = tcu::Vec4(de::max<float>(ycbcrMaxBounds[i].x(), relaxedYcbcrMaxBounds[i].x()),
de::max<float>(ycbcrMaxBounds[i].y(), relaxedYcbcrMaxBounds[i].y()),
de::max<float>(ycbcrMaxBounds[i].z(), relaxedYcbcrMaxBounds[i].z()),
de::max<float>(ycbcrMaxBounds[i].w(), relaxedYcbcrMaxBounds[i].w()));
}
}
if (vk::isYCbCrFormat(config.format))
{
tcu::TextureLevel rImage (tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT), rChannelAccess.getSize().x(), rChannelAccess.getSize().y());
tcu::TextureLevel gImage (tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT), gChannelAccess.getSize().x(), gChannelAccess.getSize().y());
tcu::TextureLevel bImage (tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT), bChannelAccess.getSize().x(), bChannelAccess.getSize().y());
tcu::TextureLevel aImage (tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT), aChannelAccess.getSize().x(), aChannelAccess.getSize().y());
for (int y = 0; y < (int)rChannelAccess.getSize().y(); y++)
for (int x = 0; x < (int)rChannelAccess.getSize().x(); x++)
rImage.getAccess().setPixel(tcu::Vec4(rChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
for (int y = 0; y < (int)gChannelAccess.getSize().y(); y++)
for (int x = 0; x < (int)gChannelAccess.getSize().x(); x++)
gImage.getAccess().setPixel(tcu::Vec4(gChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
for (int y = 0; y < (int)bChannelAccess.getSize().y(); y++)
for (int x = 0; x < (int)bChannelAccess.getSize().x(); x++)
bImage.getAccess().setPixel(tcu::Vec4(bChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
for (int y = 0; y < (int)aChannelAccess.getSize().y(); y++)
for (int x = 0; x < (int)aChannelAccess.getSize().x(); x++)
aImage.getAccess().setPixel(tcu::Vec4(aChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
{
const tcu::Vec4 scale (1.0f);
const tcu::Vec4 bias (0.0f);
log << tcu::TestLog::Image("SourceImageR", "SourceImageR", rImage.getAccess(), scale, bias);
log << tcu::TestLog::Image("SourceImageG", "SourceImageG", gImage.getAccess(), scale, bias);
log << tcu::TestLog::Image("SourceImageB", "SourceImageB", bImage.getAccess(), scale, bias);
log << tcu::TestLog::Image("SourceImageA", "SourceImageA", aImage.getAccess(), scale, bias);
}
}
else
{
tcu::TextureLevel ycbcrSrcImage (vk::mapVkFormat(config.format), size.x(), size.y());
for (int y = 0; y < (int)size.y(); y++)
for (int x = 0; x < (int)size.x(); x++)
{
const tcu::IVec3 pos (x, y, 0);
ycbcrSrcImage.getAccess().setPixel(tcu::Vec4(rChannelAccess.getChannel(pos),
gChannelAccess.getChannel(pos),
bChannelAccess.getChannel(pos),
aChannelAccess.getChannel(pos)),
x, y);
}
log << tcu::TestLog::Image("SourceImage", "SourceImage", ycbcrSrcImage.getAccess());
}
}
tcu::TestStatus conversionTest (Context& context, TestConfig config)
{
std::vector<std::string> requiredDevExt;
requiredDevExt.push_back("VK_KHR_sampler_ycbcr_conversion");
requiredDevExt.push_back("VK_KHR_get_memory_requirements2");
requiredDevExt.push_back("VK_KHR_bind_memory2");
requiredDevExt.push_back("VK_KHR_maintenance1");
const tcu::UVec2 size (ycbcr::isXChromaSubsampled(config.format) ? 12 : 7,
ycbcr::isYChromaSubsampled(config.format) ? 8 : 13);
ProtectedContext ctx (context, std::vector<std::string>(), requiredDevExt);
const vk::DeviceInterface& vk = ctx.getDeviceInterface();
const vk::VkDevice device = ctx.getDevice();
const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
tcu::TestLog& log (context.getTestContext().getLog());
validateFormatSupport(ctx, config);
logTestCaseInfo(log, config);
const vk::VkImageCreateFlagBits ycbcrImageFlags = config.disjoint
? vk::VK_IMAGE_CREATE_DISJOINT_BIT
: (vk::VkImageCreateFlagBits)0u;
const de::MovePtr<vk::YCbCrImageWithMemory> ycbcrImage (createYcbcrImage2D(ctx, PROTECTION_ENABLED,
size.x(), size.y(),
config.format,
ycbcrImageFlags,
vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT
| vk::VK_IMAGE_USAGE_SAMPLED_BIT));
const vk::Unique<vk::VkSamplerYcbcrConversion> conversion (createConversion(vk,
device,
config.format,
config.colorModel,
config.colorRange,
config.xChromaOffset,
config.yChromaOffset,
config.chromaFilter,
config.componentMapping,
config.explicitReconstruction));
const vk::Unique<vk::VkSampler> ycbcrSampler (createSampler(vk,
device,
config.textureFilter,
config.addressModeU,
config.addressModeV,
*conversion));
const vk::Unique<vk::VkImageView> ycbcrImageView (createImageView(vk, device, **ycbcrImage, config.format, *conversion));
// Input attributes
std::vector<tcu::Vec2> texCoords;
std::vector<tcu::Vec2> posCoords;
genTexCoords(texCoords, size);
posCoords = computeVertexPositions((deUint32)texCoords.size(), size.cast<int>());
// Input validation data
std::vector<tcu::Vec4> ycbcrMinBounds;
std::vector<tcu::Vec4> ycbcrMaxBounds;
// Generate input ycbcr image and conversion reference
{
ycbcr::MultiPlaneImageData ycbcrSrc (config.format, size);
generateYCbCrImage(ctx, config, size, texCoords, ycbcrSrc, ycbcrMinBounds, ycbcrMaxBounds);
logBoundImages(log, size, ycbcrMinBounds, ycbcrMaxBounds);
uploadYCbCrImage(ctx,
**ycbcrImage,
ycbcrSrc,
vk::VK_ACCESS_SHADER_READ_BIT,
vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
// Build up the reference data structure
DE_ASSERT(posCoords.size() == ycbcrMinBounds.size());
DE_ASSERT(posCoords.size() == ycbcrMaxBounds.size());
DE_ASSERT(texCoords.size() >= CHECK_SIZE);
std::vector<YCbCrValidationData> referenceData;
std::vector<YCbCrValidationData> colorReferenceData;
for (deUint32 ndx = 0; ndx < texCoords.size(); ++ndx)
{
YCbCrValidationData data;
data.coord = texCoords[ndx].toWidth<4>();
data.minBound = ycbcrMinBounds[ndx];
data.maxBound = ycbcrMaxBounds[ndx];
referenceData.push_back(data);
YCbCrValidationData colorData;
colorData.coord = posCoords[ndx].toWidth<4>();
colorData.minBound = tcu::Vec4(0.0f, 0.9f, 0.0f, 1.0f);
colorData.maxBound = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
colorReferenceData.push_back(colorData);
}
if (config.shaderType == glu::SHADERTYPE_VERTEX
|| config.shaderType == glu::SHADERTYPE_FRAGMENT)
{
const de::UniquePtr<vk::ImageWithMemory> colorImage (createImage2D(ctx,
PROTECTION_ENABLED,
queueFamilyIndex,
size.x(),
size.y(),
s_colorFormat,
vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| vk::VK_IMAGE_USAGE_SAMPLED_BIT));
const vk::Unique<vk::VkImageView> colorImageView (createImageView(ctx, **colorImage, s_colorFormat));
const vk::Unique<vk::VkSampler> colorSampler (makeSampler(vk, device));
renderYCbCrToColor(ctx, size, *ycbcrSampler, *ycbcrImageView, **colorImage, *colorImageView, referenceData, posCoords);
if (!validateImage(ctx, colorReferenceData, *colorSampler, *colorImageView))
return tcu::TestStatus::fail("YCbCr image conversion via fragment shader failed");
}
else if (config.shaderType == glu::SHADERTYPE_COMPUTE)
{
if (!validateImage(ctx, referenceData, *ycbcrSampler, *ycbcrImageView))
return tcu::TestStatus::fail("YCbCr image conversion via compute shader failed");
}
else
{
TCU_THROW(NotSupportedError, "Unsupported shader test type");
}
return tcu::TestStatus::pass("YCbCr image conversion was OK");
}
} // anonymous
tcu::TestCaseGroup* createYCbCrConversionTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> testGroup (new tcu::TestCaseGroup(testCtx, "ycbcr", "YCbCr conversion tests"));
struct {
const char * name;
const glu::ShaderType type;
} shaderTypes[] =
{
{ "fragment", glu::SHADERTYPE_FRAGMENT },
{ "compute", glu::SHADERTYPE_COMPUTE }
};
struct RangeNamePair
{
const char* name;
vk::VkSamplerYcbcrRange value;
};
struct ChromaLocationNamePair
{
const char* name;
vk::VkChromaLocation value;
};
const vk::VkComponentMapping identitySwizzle =
{
vk::VK_COMPONENT_SWIZZLE_IDENTITY,
vk::VK_COMPONENT_SWIZZLE_IDENTITY,
vk::VK_COMPONENT_SWIZZLE_IDENTITY,
vk::VK_COMPONENT_SWIZZLE_IDENTITY
};
const RangeNamePair colorRanges[] =
{
{ "itu_full", vk::VK_SAMPLER_YCBCR_RANGE_ITU_FULL },
{ "itu_narrow", vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW }
};
const ChromaLocationNamePair chromaLocations[] =
{
{ "cosited", vk::VK_CHROMA_LOCATION_COSITED_EVEN },
{ "midpoint", vk::VK_CHROMA_LOCATION_MIDPOINT }
};
const struct
{
const char* const name;
const vk::VkSamplerYcbcrModelConversion value;
} colorModels[] =
{
{ "rgb_identity", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY },
{ "ycbcr_identity", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY },
{ "ycbcr_709", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709 },
{ "ycbcr_601", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601 },
{ "ycbcr_2020", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 }
};
const struct
{
const char* name;
vk::VkImageTiling value;
} imageTilings[] =
{
{ "tiling_linear", vk::VK_IMAGE_TILING_LINEAR },
{ "tiling_optimal", vk::VK_IMAGE_TILING_OPTIMAL }
};
const deUint32 tilingNdx = 1;
const vk::VkImageTiling tiling = imageTilings[tilingNdx].value;
const char* tilingName = imageTilings[tilingNdx].name;
const vk::VkFormat testFormats[] =
{
// noChromaSubsampledFormats
vk::VK_FORMAT_R4G4B4A4_UNORM_PACK16,
vk::VK_FORMAT_B4G4R4A4_UNORM_PACK16,
vk::VK_FORMAT_R5G6B5_UNORM_PACK16,
vk::VK_FORMAT_B5G6R5_UNORM_PACK16,
vk::VK_FORMAT_R5G5B5A1_UNORM_PACK16,
vk::VK_FORMAT_B5G5R5A1_UNORM_PACK16,
vk::VK_FORMAT_A1R5G5B5_UNORM_PACK16,
vk::VK_FORMAT_R8G8B8_UNORM,
vk::VK_FORMAT_B8G8R8_UNORM,
vk::VK_FORMAT_R8G8B8A8_UNORM,
vk::VK_FORMAT_B8G8R8A8_UNORM,
vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32,
vk::VK_FORMAT_A2R10G10B10_UNORM_PACK32,
vk::VK_FORMAT_A2B10G10R10_UNORM_PACK32,
vk::VK_FORMAT_R16G16B16_UNORM,
vk::VK_FORMAT_R16G16B16A16_UNORM,
vk::VK_FORMAT_R10X6_UNORM_PACK16,
vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16,
vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
vk::VK_FORMAT_R12X4_UNORM_PACK16,
vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16,
vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16,
vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM,
vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16,
vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16,
vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
// xChromaSubsampledFormats
vk::VK_FORMAT_G8B8G8R8_422_UNORM,
vk::VK_FORMAT_B8G8R8G8_422_UNORM,
vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM,
vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM,
vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16,
vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16,
vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16,
vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16,
vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16,
vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16,
vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16,
vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16,
vk::VK_FORMAT_G16B16G16R16_422_UNORM,
vk::VK_FORMAT_B16G16R16G16_422_UNORM,
vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
// xyChromaSubsampledFormats
vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16,
vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16,
vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16,
vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM,
vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM,
};
for (size_t formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(testFormats); formatNdx++)
{
const vk::VkFormat format (testFormats[formatNdx]);
const std::string formatName (de::toLower(std::string(getFormatName(format)).substr(10)));
de::MovePtr<tcu::TestCaseGroup> formatGroup (new tcu::TestCaseGroup(testCtx, formatName.c_str(), ("Tests for color conversion using format " + formatName).c_str()));
for (size_t shaderNdx = 0; shaderNdx < DE_LENGTH_OF_ARRAY(shaderTypes); shaderNdx++)
{
const char* shaderTypeName = shaderTypes[shaderNdx].name;
de::MovePtr<tcu::TestCaseGroup> shaderGroup (new tcu::TestCaseGroup(testCtx, shaderTypeName, "YCbCr conversion tests"));
for (size_t modelNdx = 0; modelNdx < DE_LENGTH_OF_ARRAY(colorModels); modelNdx++)
{
const char* const colorModelName (colorModels[modelNdx].name);
const vk::VkSamplerYcbcrModelConversion colorModel (colorModels[modelNdx].value);
if (colorModel != vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY && ycbcr::getYCbCrFormatChannelCount(format) < 3)
continue;
de::MovePtr<tcu::TestCaseGroup> colorModelGroup (new tcu::TestCaseGroup(testCtx, colorModelName, "YCbCr conversion tests"));
for (size_t rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(colorRanges); rangeNdx++)
{
const char* const colorRangeName (colorRanges[rangeNdx].name);
const vk::VkSamplerYcbcrRange colorRange (colorRanges[rangeNdx].value);
// Narrow range doesn't really work with formats that have less than 8 bits
if (colorRange == vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW)
{
const tcu::UVec4 bitDepth (ycbcr::getYCbCrBitDepth(format));
if (bitDepth[0] < 8 || bitDepth[1] < 8 || bitDepth[2] < 8)
continue;
}
de::MovePtr<tcu::TestCaseGroup> colorRangeGroup (new tcu::TestCaseGroup(testCtx, colorRangeName, ("Tests for color range " + std::string(colorRangeName)).c_str()));
for (size_t chromaOffsetNdx = 0; chromaOffsetNdx < DE_LENGTH_OF_ARRAY(chromaLocations); chromaOffsetNdx++)
{
const char* const chromaOffsetName (chromaLocations[chromaOffsetNdx].name);
const vk::VkChromaLocation chromaOffset (chromaLocations[chromaOffsetNdx].value);
for (deUint32 disjointNdx = 0; disjointNdx < 2; ++disjointNdx)
{
bool disjoint = (disjointNdx == 1);
const TestConfig config (shaderTypes[shaderNdx].type,
format,
tiling,
vk::VK_FILTER_NEAREST,
vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
vk::VK_FILTER_NEAREST,
chromaOffset,
chromaOffset,
false,
disjoint,
colorRange,
colorModel,
identitySwizzle);
addFunctionCaseWithPrograms(colorRangeGroup.get(),
std::string(tilingName) + "_" + chromaOffsetName + (disjoint ? "_disjoint" : ""),
"",
checkSupport,
testShaders,
conversionTest,
config);
}
}
colorModelGroup->addChild(colorRangeGroup.release());
}
shaderGroup->addChild(colorModelGroup.release());
}
formatGroup->addChild(shaderGroup.release());
}
testGroup->addChild(formatGroup.release());
}
return testGroup.release();
}
} // ProtectedMem
} // vkt