blob: 0da5f695abd87ebca3c80e09a33243910fe9d507 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2019 Google Inc.
* Copyright (c) 2019 The Khronos Group Inc.
*
* 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 YCbCr Test Utilities
*//*--------------------------------------------------------------------*/
#include "vktYCbCrUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkRefUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkCmdUtil.hpp"
#include "tcuTextureUtil.hpp"
#include "deMath.h"
#include "deFloat16.h"
#include "tcuVector.hpp"
#include "tcuVectorUtil.hpp"
#include "deSTLUtil.hpp"
#include "deUniquePtr.hpp"
namespace vkt
{
namespace ycbcr
{
using namespace vk;
using de::MovePtr;
using tcu::FloatFormat;
using tcu::Interval;
using tcu::IVec2;
using tcu::IVec4;
using tcu::UVec2;
using tcu::UVec4;
using tcu::Vec2;
using tcu::Vec4;
using std::vector;
using std::string;
// MultiPlaneImageData
MultiPlaneImageData::MultiPlaneImageData (VkFormat format, const UVec2& size)
: m_format (format)
, m_description (getPlanarFormatDescription(format))
, m_size (size)
{
for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
m_planeData[planeNdx].resize(getPlaneSizeInBytes(m_description, size, planeNdx, 0, BUFFER_IMAGE_COPY_OFFSET_GRANULARITY));
}
MultiPlaneImageData::MultiPlaneImageData (const MultiPlaneImageData& other)
: m_format (other.m_format)
, m_description (other.m_description)
, m_size (other.m_size)
{
for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
m_planeData[planeNdx] = other.m_planeData[planeNdx];
}
MultiPlaneImageData::~MultiPlaneImageData (void)
{
}
tcu::PixelBufferAccess MultiPlaneImageData::getChannelAccess (deUint32 channelNdx)
{
void* planePtrs[PlanarFormatDescription::MAX_PLANES];
deUint32 planeRowPitches[PlanarFormatDescription::MAX_PLANES];
for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
{
const deUint32 planeW = m_size.x() / ( m_description.blockWidth * m_description.planes[planeNdx].widthDivisor);
planeRowPitches[planeNdx] = m_description.planes[planeNdx].elementSizeBytes * planeW;
planePtrs[planeNdx] = &m_planeData[planeNdx][0];
}
return vk::getChannelAccess(m_description,
m_size,
planeRowPitches,
planePtrs,
channelNdx);
}
tcu::ConstPixelBufferAccess MultiPlaneImageData::getChannelAccess (deUint32 channelNdx) const
{
const void* planePtrs[PlanarFormatDescription::MAX_PLANES];
deUint32 planeRowPitches[PlanarFormatDescription::MAX_PLANES];
for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
{
const deUint32 planeW = m_size.x() / (m_description.blockWidth * m_description.planes[planeNdx].widthDivisor);
planeRowPitches[planeNdx] = m_description.planes[planeNdx].elementSizeBytes * planeW;
planePtrs[planeNdx] = &m_planeData[planeNdx][0];
}
return vk::getChannelAccess(m_description,
m_size,
planeRowPitches,
planePtrs,
channelNdx);
}
// Misc utilities
namespace
{
void allocateStagingBuffers (const DeviceInterface& vkd,
VkDevice device,
Allocator& allocator,
const MultiPlaneImageData& imageData,
vector<VkBufferSp>* buffers,
vector<AllocationSp>* allocations)
{
for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
{
const VkBufferCreateInfo bufferInfo =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
DE_NULL,
(VkBufferCreateFlags)0u,
(VkDeviceSize)imageData.getPlaneSize(planeNdx),
VK_BUFFER_USAGE_TRANSFER_SRC_BIT|VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0u,
(const deUint32*)DE_NULL,
};
Move<VkBuffer> buffer (createBuffer(vkd, device, &bufferInfo));
MovePtr<Allocation> allocation (allocator.allocate(getBufferMemoryRequirements(vkd, device, *buffer),
MemoryRequirement::HostVisible|MemoryRequirement::Any));
VK_CHECK(vkd.bindBufferMemory(device, *buffer, allocation->getMemory(), allocation->getOffset()));
buffers->push_back(VkBufferSp(new Unique<VkBuffer>(buffer)));
allocations->push_back(AllocationSp(allocation.release()));
}
}
void allocateAndWriteStagingBuffers (const DeviceInterface& vkd,
VkDevice device,
Allocator& allocator,
const MultiPlaneImageData& imageData,
vector<VkBufferSp>* buffers,
vector<AllocationSp>* allocations)
{
allocateStagingBuffers(vkd, device, allocator, imageData, buffers, allocations);
for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
{
deMemcpy((*allocations)[planeNdx]->getHostPtr(), imageData.getPlanePtr(planeNdx), imageData.getPlaneSize(planeNdx));
flushMappedMemoryRange(vkd, device, (*allocations)[planeNdx]->getMemory(), 0u, VK_WHOLE_SIZE);
}
}
void readStagingBuffers (MultiPlaneImageData* imageData,
const DeviceInterface& vkd,
VkDevice device,
const vector<AllocationSp>& allocations)
{
for (deUint32 planeNdx = 0; planeNdx < imageData->getDescription().numPlanes; ++planeNdx)
{
invalidateMappedMemoryRange(vkd, device, allocations[planeNdx]->getMemory(), 0u, VK_WHOLE_SIZE);
deMemcpy(imageData->getPlanePtr(planeNdx), allocations[planeNdx]->getHostPtr(), imageData->getPlaneSize(planeNdx));
}
}
} // anonymous
void checkImageSupport (Context& context, VkFormat format, VkImageCreateFlags createFlags, VkImageTiling tiling)
{
const bool disjoint = (createFlags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0;
const VkPhysicalDeviceSamplerYcbcrConversionFeatures features = context.getSamplerYcbcrConversionFeatures();
vector<string> reqExts;
if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_sampler_ycbcr_conversion"))
reqExts.push_back("VK_KHR_sampler_ycbcr_conversion");
if (disjoint)
{
if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_bind_memory2"))
reqExts.push_back("VK_KHR_bind_memory2");
if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_get_memory_requirements2"))
reqExts.push_back("VK_KHR_get_memory_requirements2");
}
for (const string& ext : reqExts)
context.requireDeviceFunctionality(ext);
if (features.samplerYcbcrConversion == VK_FALSE)
{
TCU_THROW(NotSupportedError, "samplerYcbcrConversion is not supported");
}
{
const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(context.getInstanceInterface(),
context.getPhysicalDevice(),
format);
const VkFormatFeatureFlags featureFlags = tiling == VK_IMAGE_TILING_OPTIMAL
? formatProperties.optimalTilingFeatures
: formatProperties.linearTilingFeatures;
if ((featureFlags & (VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT | VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT)) == 0)
TCU_THROW(NotSupportedError, "YCbCr conversion is not supported for format");
if (disjoint && ((featureFlags & VK_FORMAT_FEATURE_DISJOINT_BIT) == 0))
TCU_THROW(NotSupportedError, "Disjoint planes are not supported for format");
}
}
void fillRandom (de::Random* randomGen, MultiPlaneImageData* imageData)
{
// \todo [pyry] Optimize, take into account bits that must be 0
for (deUint32 planeNdx = 0; planeNdx < imageData->getDescription().numPlanes; ++planeNdx)
{
const size_t planeSize = imageData->getPlaneSize(planeNdx);
deUint8* const planePtr = (deUint8*)imageData->getPlanePtr(planeNdx);
for (size_t ndx = 0; ndx < planeSize; ++ndx)
planePtr[ndx] = randomGen->getUint8();
}
}
void fillGradient (MultiPlaneImageData* imageData, const tcu::Vec4& minVal, const tcu::Vec4& maxVal)
{
const PlanarFormatDescription& formatInfo = imageData->getDescription();
// \todo [pyry] Optimize: no point in re-rendering source gradient for each channel.
for (deUint32 channelNdx = 0; channelNdx < 4; channelNdx++)
{
if (formatInfo.hasChannelNdx(channelNdx))
{
const tcu::PixelBufferAccess channelAccess = imageData->getChannelAccess(channelNdx);
tcu::TextureLevel tmpTexture (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), channelAccess.getWidth(), channelAccess.getHeight());
const tcu::ConstPixelBufferAccess tmpAccess = tmpTexture.getAccess();
tcu::fillWithComponentGradients(tmpTexture, minVal, maxVal);
for (int y = 0; y < channelAccess.getHeight(); ++y)
for (int x = 0; x < channelAccess.getWidth(); ++x)
{
channelAccess.setPixel(tcu::Vec4(tmpAccess.getPixel(x, y)[channelNdx]), x, y);
}
}
}
}
void fillZero (MultiPlaneImageData* imageData)
{
for (deUint32 planeNdx = 0; planeNdx < imageData->getDescription().numPlanes; ++planeNdx)
deMemset(imageData->getPlanePtr(planeNdx), 0, imageData->getPlaneSize(planeNdx));
}
vector<AllocationSp> allocateAndBindImageMemory (const DeviceInterface& vkd,
VkDevice device,
Allocator& allocator,
VkImage image,
VkFormat format,
VkImageCreateFlags createFlags,
vk::MemoryRequirement requirement)
{
vector<AllocationSp> allocations;
if ((createFlags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0)
{
const deUint32 numPlanes = getPlaneCount(format);
bindImagePlanesMemory(vkd, device, image, numPlanes, allocations, allocator, requirement);
}
else
{
const VkMemoryRequirements reqs = getImageMemoryRequirements(vkd, device, image);
allocations.push_back(AllocationSp(allocator.allocate(reqs, requirement).release()));
VK_CHECK(vkd.bindImageMemory(device, image, allocations.back()->getMemory(), allocations.back()->getOffset()));
}
return allocations;
}
void uploadImage (const DeviceInterface& vkd,
VkDevice device,
deUint32 queueFamilyNdx,
Allocator& allocator,
VkImage image,
const MultiPlaneImageData& imageData,
VkAccessFlags nextAccess,
VkImageLayout finalLayout,
deUint32 arrayLayer)
{
const VkQueue queue = getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
const Unique<VkCommandPool> cmdPool (createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
vector<VkBufferSp> stagingBuffers;
vector<AllocationSp> stagingMemory;
const PlanarFormatDescription& formatDesc = imageData.getDescription();
allocateAndWriteStagingBuffers(vkd, device, allocator, imageData, &stagingBuffers, &stagingMemory);
beginCommandBuffer(vkd, *cmdBuffer);
{
const VkImageMemoryBarrier preCopyBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
(VkAccessFlags)0,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
image,
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, arrayLayer, 1u }
};
vkd.cmdPipelineBarrier(*cmdBuffer,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
(VkDependencyFlags)0u,
0u,
(const VkMemoryBarrier*)DE_NULL,
0u,
(const VkBufferMemoryBarrier*)DE_NULL,
1u,
&preCopyBarrier);
}
for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
{
const VkImageAspectFlagBits aspect = (formatDesc.numPlanes > 1)
? getPlaneAspect(planeNdx)
: VK_IMAGE_ASPECT_COLOR_BIT;
const VkExtent3D imageExtent = makeExtent3D(imageData.getSize().x(), imageData.getSize().y(), 1u);
const VkExtent3D planeExtent = getPlaneExtent(formatDesc, imageExtent, planeNdx, 0);
const VkBufferImageCopy copy =
{
0u, // bufferOffset
0u, // bufferRowLength
0u, // bufferImageHeight
{ (VkImageAspectFlags)aspect, 0u, arrayLayer, 1u },
makeOffset3D(0u, 0u, 0u),
planeExtent
};
vkd.cmdCopyBufferToImage(*cmdBuffer, **stagingBuffers[planeNdx], image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, &copy);
}
{
const VkImageMemoryBarrier postCopyBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
VK_ACCESS_TRANSFER_WRITE_BIT,
nextAccess,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
finalLayout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
image,
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, arrayLayer, 1u }
};
vkd.cmdPipelineBarrier(*cmdBuffer,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
(VkDependencyFlags)0u,
0u,
(const VkMemoryBarrier*)DE_NULL,
0u,
(const VkBufferMemoryBarrier*)DE_NULL,
1u,
&postCopyBarrier);
}
endCommandBuffer(vkd, *cmdBuffer);
submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
}
void fillImageMemory (const vk::DeviceInterface& vkd,
vk::VkDevice device,
deUint32 queueFamilyNdx,
vk::VkImage image,
const std::vector<de::SharedPtr<vk::Allocation> >& allocations,
const MultiPlaneImageData& imageData,
vk::VkAccessFlags nextAccess,
vk::VkImageLayout finalLayout,
deUint32 arrayLayer)
{
const VkQueue queue = getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
const Unique<VkCommandPool> cmdPool (createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
const PlanarFormatDescription& formatDesc = imageData.getDescription();
for (deUint32 planeNdx = 0; planeNdx < formatDesc.numPlanes; ++planeNdx)
{
const VkImageAspectFlagBits aspect = (formatDesc.numPlanes > 1)
? getPlaneAspect(planeNdx)
: VK_IMAGE_ASPECT_COLOR_BIT;
const de::SharedPtr<Allocation>& allocation = allocations.size() > 1
? allocations[planeNdx]
: allocations[0];
const size_t planeSize = imageData.getPlaneSize(planeNdx);
const deUint32 planeH = imageData.getSize().y() / formatDesc.planes[planeNdx].heightDivisor;
const VkImageSubresource subresource =
{
static_cast<vk::VkImageAspectFlags>(aspect),
0u,
arrayLayer,
};
VkSubresourceLayout layout;
vkd.getImageSubresourceLayout(device, image, &subresource, &layout);
for (deUint32 row = 0; row < planeH; ++row)
{
const size_t rowSize = planeSize / planeH;
void* const dstPtr = ((deUint8*)allocation->getHostPtr()) + layout.offset + layout.rowPitch * row;
const void* const srcPtr = ((const deUint8*)imageData.getPlanePtr(planeNdx)) + row * rowSize;
deMemcpy(dstPtr, srcPtr, rowSize);
}
flushMappedMemoryRange(vkd, device, allocation->getMemory(), 0u, VK_WHOLE_SIZE);
}
beginCommandBuffer(vkd, *cmdBuffer);
{
const VkImageMemoryBarrier postCopyBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
0u,
nextAccess,
VK_IMAGE_LAYOUT_PREINITIALIZED,
finalLayout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
image,
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, arrayLayer, 1u }
};
vkd.cmdPipelineBarrier(*cmdBuffer,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
(VkDependencyFlags)0u,
0u,
(const VkMemoryBarrier*)DE_NULL,
0u,
(const VkBufferMemoryBarrier*)DE_NULL,
1u,
&postCopyBarrier);
}
endCommandBuffer(vkd, *cmdBuffer);
submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
}
void downloadImage (const DeviceInterface& vkd,
VkDevice device,
deUint32 queueFamilyNdx,
Allocator& allocator,
VkImage image,
MultiPlaneImageData* imageData,
VkAccessFlags prevAccess,
VkImageLayout initialLayout)
{
const VkQueue queue = getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
const Unique<VkCommandPool> cmdPool (createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
vector<VkBufferSp> stagingBuffers;
vector<AllocationSp> stagingMemory;
const PlanarFormatDescription& formatDesc = imageData->getDescription();
allocateStagingBuffers(vkd, device, allocator, *imageData, &stagingBuffers, &stagingMemory);
beginCommandBuffer(vkd, *cmdBuffer);
for (deUint32 planeNdx = 0; planeNdx < imageData->getDescription().numPlanes; ++planeNdx)
{
const VkImageAspectFlagBits aspect = (formatDesc.numPlanes > 1)
? getPlaneAspect(planeNdx)
: VK_IMAGE_ASPECT_COLOR_BIT;
{
const VkImageMemoryBarrier preCopyBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
prevAccess,
VK_ACCESS_TRANSFER_READ_BIT,
initialLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
image,
{
static_cast<vk::VkImageAspectFlags>(aspect),
0u,
1u,
0u,
1u
}
};
vkd.cmdPipelineBarrier(*cmdBuffer,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
(VkDependencyFlags)0u,
0u,
(const VkMemoryBarrier*)DE_NULL,
0u,
(const VkBufferMemoryBarrier*)DE_NULL,
1u,
&preCopyBarrier);
}
{
const VkExtent3D imageExtent = makeExtent3D(imageData->getSize().x(), imageData->getSize().y(), 1u);
const VkExtent3D planeExtent = getPlaneExtent(formatDesc, imageExtent, planeNdx, 0);
const VkBufferImageCopy copy =
{
0u, // bufferOffset
0u, // bufferRowLength
0u, // bufferImageHeight
{ (VkImageAspectFlags)aspect, 0u, 0u, 1u },
makeOffset3D(0u, 0u, 0u),
planeExtent
};
vkd.cmdCopyImageToBuffer(*cmdBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **stagingBuffers[planeNdx], 1u, &copy);
}
{
const VkBufferMemoryBarrier postCopyBarrier =
{
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
DE_NULL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_HOST_READ_BIT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
**stagingBuffers[planeNdx],
0u,
VK_WHOLE_SIZE
};
vkd.cmdPipelineBarrier(*cmdBuffer,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
(VkDependencyFlags)0u,
0u,
(const VkMemoryBarrier*)DE_NULL,
1u,
&postCopyBarrier,
0u,
(const VkImageMemoryBarrier*)DE_NULL);
}
}
endCommandBuffer(vkd, *cmdBuffer);
submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
readStagingBuffers(imageData, vkd, device, stagingMemory);
}
void readImageMemory (const vk::DeviceInterface& vkd,
vk::VkDevice device,
deUint32 queueFamilyNdx,
vk::VkImage image,
const std::vector<de::SharedPtr<vk::Allocation> >& allocations,
MultiPlaneImageData* imageData,
vk::VkAccessFlags prevAccess,
vk::VkImageLayout initialLayout)
{
const VkQueue queue = getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
const Unique<VkCommandPool> cmdPool (createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
const PlanarFormatDescription& formatDesc = imageData->getDescription();
beginCommandBuffer(vkd, *cmdBuffer);
{
const VkImageMemoryBarrier preCopyBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
prevAccess,
vk::VK_ACCESS_HOST_READ_BIT,
initialLayout,
VK_IMAGE_LAYOUT_GENERAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
image,
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }
};
vkd.cmdPipelineBarrier(*cmdBuffer,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
(VkDependencyFlags)0u,
0u,
(const VkMemoryBarrier*)DE_NULL,
0u,
(const VkBufferMemoryBarrier*)DE_NULL,
1u,
&preCopyBarrier);
}
endCommandBuffer(vkd, *cmdBuffer);
submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
for (deUint32 planeNdx = 0; planeNdx < formatDesc.numPlanes; ++planeNdx)
{
const VkImageAspectFlagBits aspect = (formatDesc.numPlanes > 1)
? getPlaneAspect(planeNdx)
: VK_IMAGE_ASPECT_COLOR_BIT;
const de::SharedPtr<Allocation>& allocation = allocations.size() > 1
? allocations[planeNdx]
: allocations[0];
const size_t planeSize = imageData->getPlaneSize(planeNdx);
const deUint32 planeH = imageData->getSize().y() / formatDesc.planes[planeNdx].heightDivisor;
const VkImageSubresource subresource =
{
static_cast<vk::VkImageAspectFlags>(aspect),
0u,
0u,
};
VkSubresourceLayout layout;
vkd.getImageSubresourceLayout(device, image, &subresource, &layout);
invalidateMappedMemoryRange(vkd, device, allocation->getMemory(), 0u, VK_WHOLE_SIZE);
for (deUint32 row = 0; row < planeH; ++row)
{
const size_t rowSize = planeSize / planeH;
const void* const srcPtr = ((const deUint8*)allocation->getHostPtr()) + layout.offset + layout.rowPitch * row;
void* const dstPtr = ((deUint8*)imageData->getPlanePtr(planeNdx)) + row * rowSize;
deMemcpy(dstPtr, srcPtr, rowSize);
}
}
}
// ChannelAccess utilities
namespace
{
//! Extend < 32b signed integer to 32b
inline deInt32 signExtend (deUint32 src, int bits)
{
const deUint32 signBit = 1u << (bits-1);
src |= ~((src & signBit) - 1);
return (deInt32)src;
}
deUint32 divRoundUp (deUint32 a, deUint32 b)
{
if (a % b == 0)
return a / b;
else
return (a / b) + 1;
}
// \todo Taken from tcuTexture.cpp
// \todo [2011-09-21 pyry] Move to tcutil?
template <typename T>
inline T convertSatRte (float f)
{
// \note Doesn't work for 64-bit types
DE_STATIC_ASSERT(sizeof(T) < sizeof(deUint64));
DE_STATIC_ASSERT((-3 % 2 != 0) && (-4 % 2 == 0));
deInt64 minVal = std::numeric_limits<T>::min();
deInt64 maxVal = std::numeric_limits<T>::max();
float q = deFloatFrac(f);
deInt64 intVal = (deInt64)(f-q);
// Rounding.
if (q == 0.5f)
{
if (intVal % 2 != 0)
intVal++;
}
else if (q > 0.5f)
intVal++;
// else Don't add anything
// Saturate.
intVal = de::max(minVal, de::min(maxVal, intVal));
return (T)intVal;
}
} // anonymous
ChannelAccess::ChannelAccess (tcu::TextureChannelClass channelClass,
deUint8 channelSize,
const tcu::IVec3& size,
const tcu::IVec3& bitPitch,
void* data,
deUint32 bitOffset)
: m_channelClass (channelClass)
, m_channelSize (channelSize)
, m_size (size)
, m_bitPitch (bitPitch)
, m_data ((deUint8*)data + (bitOffset / 8))
, m_bitOffset (bitOffset % 8)
{
}
deUint32 ChannelAccess::getChannelUint (const tcu::IVec3& pos) const
{
DE_ASSERT(pos[0] < m_size[0]);
DE_ASSERT(pos[1] < m_size[1]);
DE_ASSERT(pos[2] < m_size[2]);
const deInt32 bitOffset (m_bitOffset + tcu::dot(m_bitPitch, pos));
const deUint8* const firstByte = ((const deUint8*)m_data) + (bitOffset / 8);
const deUint32 byteCount = divRoundUp((bitOffset + m_channelSize) - 8u * (bitOffset / 8u), 8u);
const deUint32 mask (m_channelSize == 32u ? ~0x0u : (0x1u << m_channelSize) - 1u);
const deUint32 offset = bitOffset % 8;
deUint32 bits = 0u;
deMemcpy(&bits, firstByte, byteCount);
return (bits >> offset) & mask;
}
void ChannelAccess::setChannel (const tcu::IVec3& pos, deUint32 x)
{
DE_ASSERT(pos[0] < m_size[0]);
DE_ASSERT(pos[1] < m_size[1]);
DE_ASSERT(pos[2] < m_size[2]);
const deInt32 bitOffset (m_bitOffset + tcu::dot(m_bitPitch, pos));
deUint8* const firstByte = ((deUint8*)m_data) + (bitOffset / 8);
const deUint32 byteCount = divRoundUp((bitOffset + m_channelSize) - 8u * (bitOffset / 8u), 8u);
const deUint32 mask (m_channelSize == 32u ? ~0x0u : (0x1u << m_channelSize) - 1u);
const deUint32 offset = bitOffset % 8;
const deUint32 bits = (x & mask) << offset;
deUint32 oldBits = 0;
deMemcpy(&oldBits, firstByte, byteCount);
{
const deUint32 newBits = bits | (oldBits & (~(mask << offset)));
deMemcpy(firstByte, &newBits, byteCount);
}
}
float ChannelAccess::getChannel (const tcu::IVec3& pos) const
{
const deUint32 bits (getChannelUint(pos));
switch (m_channelClass)
{
case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
return (float)bits / (float)(m_channelSize == 32 ? ~0x0u : ((0x1u << m_channelSize) - 1u));
case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
return (float)bits;
case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
return de::max(-1.0f, (float)signExtend(bits, m_channelSize) / (float)((0x1u << (m_channelSize - 1u)) - 1u));
case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
return (float)signExtend(bits, m_channelSize);
case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
if (m_channelSize == 32)
return tcu::Float32(bits).asFloat();
else
{
DE_FATAL("Float type not supported");
return -1.0f;
}
default:
DE_FATAL("Unknown texture channel class");
return -1.0f;
}
}
tcu::Interval ChannelAccess::getChannel (const tcu::FloatFormat& conversionFormat,
const tcu::IVec3& pos) const
{
const deUint32 bits (getChannelUint(pos));
switch (m_channelClass)
{
case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
return conversionFormat.roundOut(conversionFormat.roundOut((double)bits, false)
/ conversionFormat.roundOut((double)(m_channelSize == 32 ? ~0x0u : ((0x1u << m_channelSize) - 1u)), false), false);
case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
return conversionFormat.roundOut((double)bits, false);
case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
{
const tcu::Interval result (conversionFormat.roundOut(conversionFormat.roundOut((double)signExtend(bits, m_channelSize), false)
/ conversionFormat.roundOut((double)((0x1u << (m_channelSize - 1u)) - 1u), false), false));
return tcu::Interval(de::max(-1.0, result.lo()), de::max(-1.0, result.hi()));
}
case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
return conversionFormat.roundOut((double)signExtend(bits, m_channelSize), false);
case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
if (m_channelSize == 32)
return conversionFormat.roundOut(tcu::Float32(bits).asFloat(), false);
else
{
DE_FATAL("Float type not supported");
return tcu::Interval();
}
default:
DE_FATAL("Unknown texture channel class");
return tcu::Interval();
}
}
void ChannelAccess::setChannel (const tcu::IVec3& pos, float x)
{
DE_ASSERT(pos[0] < m_size[0]);
DE_ASSERT(pos[1] < m_size[1]);
DE_ASSERT(pos[2] < m_size[2]);
const deUint32 mask (m_channelSize == 32u ? ~0x0u : (0x1u << m_channelSize) - 1u);
switch (m_channelClass)
{
case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
{
const deUint32 maxValue (mask);
const deUint32 value (de::min(maxValue, (deUint32)convertSatRte<deUint32>(x * (float)maxValue)));
setChannel(pos, value);
break;
}
case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
{
const deInt32 range ((0x1u << (m_channelSize - 1u)) - 1u);
const deUint32 value ((deUint32)de::clamp<deInt32>(convertSatRte<deInt32>(x * (float)range), -range, range));
setChannel(pos, value);
break;
}
case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
{
const deUint32 maxValue (mask);
const deUint32 value (de::min(maxValue, (deUint32)x));
setChannel(pos, value);
break;
}
case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
{
const deInt32 minValue (-(deInt32)(1u << (m_channelSize - 1u)));
const deInt32 maxValue ((deInt32)((1u << (m_channelSize - 1u)) - 1u));
const deUint32 value ((deUint32)de::clamp((deInt32)x, minValue, maxValue));
setChannel(pos, value);
break;
}
case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
{
if (m_channelSize == 32)
{
const deUint32 value = tcu::Float32(x).bits();
setChannel(pos, value);
}
else
DE_FATAL("Float type not supported");
break;
}
default:
DE_FATAL("Unknown texture channel class");
}
}
ChannelAccess getChannelAccess (MultiPlaneImageData& data,
const vk::PlanarFormatDescription& formatInfo,
const UVec2& size,
int channelNdx)
{
DE_ASSERT(formatInfo.hasChannelNdx(channelNdx));
const deUint32 planeNdx = formatInfo.channels[channelNdx].planeNdx;
const deUint32 valueOffsetBits = formatInfo.channels[channelNdx].offsetBits;
const deUint32 pixelStrideBytes = formatInfo.channels[channelNdx].strideBytes;
const deUint32 pixelStrideBits = pixelStrideBytes * 8;
const deUint8 sizeBits = formatInfo.channels[channelNdx].sizeBits;
DE_ASSERT(size.x() % (formatInfo.blockWidth * formatInfo.planes[planeNdx].widthDivisor) == 0);
DE_ASSERT(size.y() % (formatInfo.blockHeight * formatInfo.planes[planeNdx].heightDivisor) == 0);
deUint32 accessWidth = size.x() / ( formatInfo.blockWidth * formatInfo.planes[planeNdx].widthDivisor );
const deUint32 accessHeight = size.y() / ( formatInfo.blockHeight * formatInfo.planes[planeNdx].heightDivisor );
const deUint32 elementSizeBytes = formatInfo.planes[planeNdx].elementSizeBytes;
const deUint32 rowPitch = formatInfo.planes[planeNdx].elementSizeBytes * accessWidth;
const deUint32 rowPitchBits = rowPitch * 8;
if (pixelStrideBytes != elementSizeBytes)
{
DE_ASSERT(elementSizeBytes % pixelStrideBytes == 0);
accessWidth *= elementSizeBytes/pixelStrideBytes;
}
return ChannelAccess((tcu::TextureChannelClass)formatInfo.channels[channelNdx].type, sizeBits, tcu::IVec3(accessWidth, accessHeight, 1u), tcu::IVec3((int)pixelStrideBits, (int)rowPitchBits, 0), data.getPlanePtr(planeNdx), (deUint32)valueOffsetBits);
}
bool isXChromaSubsampled (vk::VkFormat format)
{
switch (format)
{
case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
return true;
default:
return false;
}
}
bool isYChromaSubsampled (vk::VkFormat format)
{
switch (format)
{
case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
return true;
default:
return false;
}
}
bool areLsb6BitsDontCare(vk::VkFormat srcFormat, vk::VkFormat dstFormat)
{
if ((srcFormat == vk::VK_FORMAT_R10X6_UNORM_PACK16) ||
(dstFormat == vk::VK_FORMAT_R10X6_UNORM_PACK16) ||
(srcFormat == vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16) ||
(dstFormat == vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16) ||
(srcFormat == vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16) ||
(dstFormat == vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16) ||
(srcFormat == vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16) ||
(dstFormat == vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16) ||
(srcFormat == vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16) ||
(dstFormat == vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16) ||
(srcFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16))
{
return true;
}
return false;
}
bool areLsb4BitsDontCare(vk::VkFormat srcFormat, vk::VkFormat dstFormat)
{
if ((srcFormat == vk::VK_FORMAT_R12X4_UNORM_PACK16) ||
(dstFormat == vk::VK_FORMAT_R12X4_UNORM_PACK16) ||
(srcFormat == vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16) ||
(dstFormat == vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16) ||
(srcFormat == vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16) ||
(dstFormat == vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16) ||
(srcFormat == vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16) ||
(dstFormat == vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16) ||
(srcFormat == vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16) ||
(dstFormat == vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16) ||
(srcFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16) ||
(srcFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16) ||
(dstFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16))
{
return true;
}
return false;
}
// \note Used for range expansion
tcu::UVec4 getYCbCrBitDepth (vk::VkFormat format)
{
switch (format)
{
case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
return tcu::UVec4(8, 8, 8, 0);
case vk::VK_FORMAT_R10X6_UNORM_PACK16:
return tcu::UVec4(10, 0, 0, 0);
case vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
return tcu::UVec4(10, 10, 0, 0);
case vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
return tcu::UVec4(10, 10, 10, 10);
case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
return tcu::UVec4(10, 10, 10, 0);
case vk::VK_FORMAT_R12X4_UNORM_PACK16:
return tcu::UVec4(12, 0, 0, 0);
case vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
return tcu::UVec4(12, 12, 0, 0);
case vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
return tcu::UVec4(12, 12, 12, 12);
case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
return tcu::UVec4(16, 16, 16, 0);
default:
return tcu::getTextureFormatBitDepth(vk::mapVkFormat(format)).cast<deUint32>();
}
}
std::vector<tcu::FloatFormat> getPrecision (VkFormat format)
{
std::vector<FloatFormat> floatFormats;
UVec4 channelDepth = getYCbCrBitDepth (format);
for (deUint32 channelIdx = 0; channelIdx < 4; channelIdx++)
floatFormats.push_back(tcu::FloatFormat(0, 0, channelDepth[channelIdx], false, tcu::YES));
return floatFormats;
}
deUint32 getYCbCrFormatChannelCount (vk::VkFormat format)
{
switch (format)
{
case vk::VK_FORMAT_A1R5G5B5_UNORM_PACK16:
case vk::VK_FORMAT_A2B10G10R10_UNORM_PACK32:
case vk::VK_FORMAT_A2R10G10B10_UNORM_PACK32:
case vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32:
case vk::VK_FORMAT_B4G4R4A4_UNORM_PACK16:
case vk::VK_FORMAT_B5G5R5A1_UNORM_PACK16:
case vk::VK_FORMAT_B8G8R8A8_UNORM:
case vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
case vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
case vk::VK_FORMAT_R16G16B16A16_UNORM:
case vk::VK_FORMAT_R4G4B4A4_UNORM_PACK16:
case vk::VK_FORMAT_R5G5B5A1_UNORM_PACK16:
case vk::VK_FORMAT_R8G8B8A8_UNORM:
return 4;
case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
case vk::VK_FORMAT_B5G6R5_UNORM_PACK16:
case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
case vk::VK_FORMAT_B8G8R8_UNORM:
case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
case vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
case vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
case vk::VK_FORMAT_R16G16B16_UNORM:
case vk::VK_FORMAT_R5G6B5_UNORM_PACK16:
case vk::VK_FORMAT_R8G8B8_UNORM:
return 3;
case vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
case vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
return 2;
case vk::VK_FORMAT_R10X6_UNORM_PACK16:
case vk::VK_FORMAT_R12X4_UNORM_PACK16:
return 1;
default:
DE_FATAL("Unknown number of channels");
return -1;
}
}
// YCbCr color conversion utilities
namespace
{
tcu::Interval rangeExpandChroma (vk::VkSamplerYcbcrRange range,
const tcu::FloatFormat& conversionFormat,
const deUint32 bits,
const tcu::Interval& sample)
{
const deUint32 values (0x1u << bits);
switch (range)
{
case vk::VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
return conversionFormat.roundOut(sample - conversionFormat.roundOut(tcu::Interval((double)(0x1u << (bits - 1u)) / (double)((0x1u << bits) - 1u)), false), false);
case vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
{
const tcu::Interval a (conversionFormat.roundOut(sample * tcu::Interval((double)(values - 1u)), false));
const tcu::Interval dividend (conversionFormat.roundOut(a - tcu::Interval((double)(128u * (0x1u << (bits - 8u)))), false));
const tcu::Interval divisor ((double)(224u * (0x1u << (bits - 8u))));
const tcu::Interval result (conversionFormat.roundOut(dividend / divisor, false));
return result;
}
default:
DE_FATAL("Unknown YCbCrRange");
return tcu::Interval();
}
}
tcu::Interval rangeExpandLuma (vk::VkSamplerYcbcrRange range,
const tcu::FloatFormat& conversionFormat,
const deUint32 bits,
const tcu::Interval& sample)
{
const deUint32 values (0x1u << bits);
switch (range)
{
case vk::VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
return conversionFormat.roundOut(sample, false);
case vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
{
const tcu::Interval a (conversionFormat.roundOut(sample * tcu::Interval((double)(values - 1u)), false));
const tcu::Interval dividend (conversionFormat.roundOut(a - tcu::Interval((double)(16u * (0x1u << (bits - 8u)))), false));
const tcu::Interval divisor ((double)(219u * (0x1u << (bits - 8u))));
const tcu::Interval result (conversionFormat.roundOut(dividend / divisor, false));
return result;
}
default:
DE_FATAL("Unknown YCbCrRange");
return tcu::Interval();
}
}
tcu::Interval clampMaybe (const tcu::Interval& x,
double min,
double max)
{
tcu::Interval result = x;
DE_ASSERT(min <= max);
if (x.lo() < min)
result = result | tcu::Interval(min);
if (x.hi() > max)
result = result | tcu::Interval(max);
return result;
}
void convertColor (vk::VkSamplerYcbcrModelConversion colorModel,
vk::VkSamplerYcbcrRange range,
const vector<tcu::FloatFormat>& conversionFormat,
const tcu::UVec4& bitDepth,
const tcu::Interval input[4],
tcu::Interval output[4])
{
switch (colorModel)
{
case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY:
{
for (size_t ndx = 0; ndx < 4; ndx++)
output[ndx] = input[ndx];
break;
}
case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY:
{
output[0] = clampMaybe(rangeExpandChroma(range, conversionFormat[0], bitDepth[0], input[0]), -0.5, 0.5);
output[1] = clampMaybe(rangeExpandLuma(range, conversionFormat[1], bitDepth[1], input[1]), 0.0, 1.0);
output[2] = clampMaybe(rangeExpandChroma(range, conversionFormat[2], bitDepth[2], input[2]), -0.5, 0.5);
output[3] = input[3];
break;
}
case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601:
case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709:
case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020:
{
const tcu::Interval y (rangeExpandLuma(range, conversionFormat[1], bitDepth[1], input[1]));
const tcu::Interval cr (rangeExpandChroma(range, conversionFormat[0], bitDepth[0], input[0]));
const tcu::Interval cb (rangeExpandChroma(range, conversionFormat[2], bitDepth[2], input[2]));
const tcu::Interval yClamped (clampMaybe(y, 0.0, 1.0));
const tcu::Interval crClamped (clampMaybe(cr, -0.5, 0.5));
const tcu::Interval cbClamped (clampMaybe(cb, -0.5, 0.5));
if (colorModel == vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601)
{
output[0] = conversionFormat[0].roundOut(yClamped + conversionFormat[0].roundOut(1.402 * crClamped, false), false);
output[1] = conversionFormat[1].roundOut(conversionFormat[1].roundOut(yClamped - conversionFormat[1].roundOut((0.202008 / 0.587) * cbClamped, false), false) - conversionFormat[1].roundOut((0.419198 / 0.587) * crClamped, false), false);
output[2] = conversionFormat[2].roundOut(yClamped + conversionFormat[2].roundOut(1.772 * cbClamped, false), false);
}
else if (colorModel == vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709)
{
output[0] = conversionFormat[0].roundOut(yClamped + conversionFormat[0].roundOut(1.5748 * crClamped, false), false);
output[1] = conversionFormat[1].roundOut(conversionFormat[1].roundOut(yClamped - conversionFormat[1].roundOut((0.13397432 / 0.7152) * cbClamped, false), false) - conversionFormat[1].roundOut((0.33480248 / 0.7152) * crClamped, false), false);
output[2] = conversionFormat[2].roundOut(yClamped + conversionFormat[2].roundOut(1.8556 * cbClamped, false), false);
}
else
{
output[0] = conversionFormat[0].roundOut(yClamped + conversionFormat[0].roundOut(1.4746 * crClamped, false), false);
output[1] = conversionFormat[1].roundOut(conversionFormat[1].roundOut(yClamped - conversionFormat[1].roundOut(conversionFormat[1].roundOut(0.11156702 / 0.6780, false) * cbClamped, false), false) - conversionFormat[1].roundOut(conversionFormat[1].roundOut(0.38737742 / 0.6780, false) * crClamped, false), false);
output[2] = conversionFormat[2].roundOut(yClamped + conversionFormat[2].roundOut(1.8814 * cbClamped, false), false);
}
output[3] = input[3];
break;
}
default:
DE_FATAL("Unknown YCbCrModel");
}
if (colorModel != vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY)
{
for (int ndx = 0; ndx < 3; ndx++)
output[ndx] = clampMaybe(output[ndx], 0.0, 1.0);
}
}
int mirror (int coord)
{
return coord >= 0 ? coord : -(1 + coord);
}
int imod (int a, int b)
{
int m = a % b;
return m < 0 ? m + b : m;
}
tcu::Interval frac (const tcu::Interval& x)
{
if (x.hi() - x.lo() >= 1.0)
return tcu::Interval(0.0, 1.0);
else
{
const tcu::Interval ret (deFrac(x.lo()), deFrac(x.hi()));
return ret;
}
}
tcu::Interval calculateUV (const tcu::FloatFormat& coordFormat,
const tcu::Interval& st,
const int size)
{
return coordFormat.roundOut(coordFormat.roundOut(st, false) * tcu::Interval((double)size), false);
}
tcu::IVec2 calculateNearestIJRange (const tcu::FloatFormat& coordFormat,
const tcu::Interval& uv)
{
const tcu::Interval ij (coordFormat.roundOut(coordFormat.roundOut(uv, false) - tcu::Interval(0.5), false));
return tcu::IVec2(deRoundToInt32(ij.lo() - coordFormat.ulp(ij.lo(), 1)), deRoundToInt32(ij.hi() + coordFormat.ulp(ij.hi(), 1)));
}
// Calculate range of pixel coordinates that can be used as lower coordinate for linear sampling
tcu::IVec2 calculateLinearIJRange (const tcu::FloatFormat& coordFormat,
const tcu::Interval& uv)
{
const tcu::Interval ij (coordFormat.roundOut(uv - tcu::Interval(0.5), false));
return tcu::IVec2(deFloorToInt32(ij.lo()), deFloorToInt32(ij.hi()));
}
tcu::IVec2 calculateIJRange (vk::VkFilter filter,
const tcu::FloatFormat& coordFormat,
const tcu::Interval& uv)
{
DE_ASSERT(filter == vk::VK_FILTER_NEAREST || filter == vk::VK_FILTER_LINEAR);
return (filter == vk::VK_FILTER_LINEAR) ? calculateLinearIJRange(coordFormat, uv)
: calculateNearestIJRange(coordFormat, uv);
}
tcu::Interval calculateAB (const deUint32 subTexelPrecisionBits,
const tcu::Interval& uv,
int ij)
{
const deUint32 subdivisions = 0x1u << subTexelPrecisionBits;
const tcu::Interval ab (frac((uv - 0.5) & tcu::Interval((double)ij, (double)(ij + 1))));
const tcu::Interval gridAB (ab * tcu::Interval(subdivisions));
const tcu::Interval rounded (de::max(deFloor(gridAB.lo()) / subdivisions, 0.0) , de::min(deCeil(gridAB.hi()) / subdivisions, 1.0));
return rounded;
}
tcu::Interval lookupWrapped (const ChannelAccess& access,
const tcu::FloatFormat& conversionFormat,
vk::VkSamplerAddressMode addressModeU,
vk::VkSamplerAddressMode addressModeV,
const tcu::IVec2& coord)
{
return access.getChannel(conversionFormat,
tcu::IVec3(wrap(addressModeU, coord.x(), access.getSize().x()), wrap(addressModeV, coord.y(), access.getSize().y()), 0));
}
tcu::Interval linearInterpolate (const tcu::FloatFormat& filteringFormat,
const tcu::Interval& a,
const tcu::Interval& b,
const tcu::Interval& p00,
const tcu::Interval& p10,
const tcu::Interval& p01,
const tcu::Interval& p11)
{
const tcu::Interval p[4] =
{
p00,
p10,
p01,
p11
};
tcu::Interval result (0.0);
for (size_t ndx = 0; ndx < 4; ndx++)
{
const tcu::Interval weightA (filteringFormat.roundOut((ndx % 2) == 0 ? (1.0 - a) : a, false));
const tcu::Interval weightB (filteringFormat.roundOut((ndx / 2) == 0 ? (1.0 - b) : b, false));
const tcu::Interval weight (filteringFormat.roundOut(weightA * weightB, false));
result = filteringFormat.roundOut(result + filteringFormat.roundOut(p[ndx] * weight, false), false);
}
return result;
}
tcu::Interval calculateImplicitChromaUV (const tcu::FloatFormat& coordFormat,
vk::VkChromaLocation offset,
const tcu::Interval& uv)
{
if (offset == vk::VK_CHROMA_LOCATION_COSITED_EVEN)
return coordFormat.roundOut(0.5 * coordFormat.roundOut(uv + 0.5, false), false);
else
return coordFormat.roundOut(0.5 * uv, false);
}
tcu::Interval linearSample (const ChannelAccess& access,
const tcu::FloatFormat& conversionFormat,
const tcu::FloatFormat& filteringFormat,
vk::VkSamplerAddressMode addressModeU,
vk::VkSamplerAddressMode addressModeV,
const tcu::IVec2& coord,
const tcu::Interval& a,
const tcu::Interval& b)
{
return linearInterpolate(filteringFormat, a, b,
lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(0, 0)),
lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(1, 0)),
lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(0, 1)),
lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(1, 1)));
}
tcu::Interval reconstructLinearXChromaSample (const tcu::FloatFormat& filteringFormat,
const tcu::FloatFormat& conversionFormat,
vk::VkChromaLocation offset,
vk::VkSamplerAddressMode addressModeU,
vk::VkSamplerAddressMode addressModeV,
const ChannelAccess& access,
int i,
int j)
{
const int subI = offset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
? divFloor(i, 2)
: (i % 2 == 0 ? divFloor(i, 2) - 1 : divFloor(i, 2));
const double a = offset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
? (i % 2 == 0 ? 0.0 : 0.5)
: (i % 2 == 0 ? 0.25 : 0.75);
const tcu::Interval A (filteringFormat.roundOut( a * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI, j)), false));
const tcu::Interval B (filteringFormat.roundOut((1.0 - a) * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI + 1, j)), false));
return filteringFormat.roundOut(A + B, false);
}
tcu::Interval reconstructLinearXYChromaSample (const tcu::FloatFormat& filteringFormat,
const tcu::FloatFormat& conversionFormat,
vk::VkChromaLocation xOffset,
vk::VkChromaLocation yOffset,
vk::VkSamplerAddressMode addressModeU,
vk::VkSamplerAddressMode addressModeV,
const ChannelAccess& access,
int i,
int j)
{
const int subI = xOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
? divFloor(i, 2)
: (i % 2 == 0 ? divFloor(i, 2) - 1 : divFloor(i, 2));
const int subJ = yOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
? divFloor(j, 2)
: (j % 2 == 0 ? divFloor(j, 2) - 1 : divFloor(j, 2));
const double a = xOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
? (i % 2 == 0 ? 0.0 : 0.5)
: (i % 2 == 0 ? 0.25 : 0.75);
const double b = yOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
? (j % 2 == 0 ? 0.0 : 0.5)
: (j % 2 == 0 ? 0.25 : 0.75);
return linearSample(access, conversionFormat, filteringFormat, addressModeU, addressModeV, tcu::IVec2(subI, subJ), a, b);
}
const ChannelAccess& swizzle (vk::VkComponentSwizzle swizzle,
const ChannelAccess& identityPlane,
const ChannelAccess& rPlane,
const ChannelAccess& gPlane,
const ChannelAccess& bPlane,
const ChannelAccess& aPlane)
{
switch (swizzle)
{
case vk::VK_COMPONENT_SWIZZLE_IDENTITY: return identityPlane;
case vk::VK_COMPONENT_SWIZZLE_R: return rPlane;
case vk::VK_COMPONENT_SWIZZLE_G: return gPlane;
case vk::VK_COMPONENT_SWIZZLE_B: return bPlane;
case vk::VK_COMPONENT_SWIZZLE_A: return aPlane;
default:
DE_FATAL("Unsupported swizzle");
return identityPlane;
}
}
} // anonymous
int wrap (vk::VkSamplerAddressMode addressMode,
int coord,
int size)
{
switch (addressMode)
{
case vk::VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
return (size - 1) - mirror(imod(coord, 2 * size) - size);
case vk::VK_SAMPLER_ADDRESS_MODE_REPEAT:
return imod(coord, size);
case vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
return de::clamp(coord, 0, size - 1);
case vk::VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
return de::clamp(mirror(coord), 0, size - 1);
default:
DE_FATAL("Unknown wrap mode");
return ~0;
}
}
int divFloor (int a, int b)
{
if (a % b == 0)
return a / b;
else if (a > 0)
return a / b;
else
return (a / b) - 1;
}
void calculateBounds (const ChannelAccess& rPlane,
const ChannelAccess& gPlane,
const ChannelAccess& bPlane,
const ChannelAccess& aPlane,
const UVec4& bitDepth,
const vector<Vec2>& sts,
const vector<FloatFormat>& filteringFormat,
const vector<FloatFormat>& conversionFormat,
const deUint32 subTexelPrecisionBits,
vk::VkFilter filter,
vk::VkSamplerYcbcrModelConversion colorModel,
vk::VkSamplerYcbcrRange range,
vk::VkFilter chromaFilter,
vk::VkChromaLocation xChromaOffset,
vk::VkChromaLocation yChromaOffset,
const vk::VkComponentMapping& componentMapping,
bool explicitReconstruction,
vk::VkSamplerAddressMode addressModeU,
vk::VkSamplerAddressMode addressModeV,
std::vector<Vec4>& minBounds,
std::vector<Vec4>& maxBounds,
std::vector<Vec4>& uvBounds,
std::vector<IVec4>& ijBounds)
{
const FloatFormat highp (-126, 127, 23, true,
tcu::MAYBE, // subnormals
tcu::YES, // infinities
tcu::MAYBE); // NaN
const FloatFormat coordFormat (-32, 32, 16, true);
const ChannelAccess& rAccess (swizzle(componentMapping.r, rPlane, rPlane, gPlane, bPlane, aPlane));
const ChannelAccess& gAccess (swizzle(componentMapping.g, gPlane, rPlane, gPlane, bPlane, aPlane));
const ChannelAccess& bAccess (swizzle(componentMapping.b, bPlane, rPlane, gPlane, bPlane, aPlane));
const ChannelAccess& aAccess (swizzle(componentMapping.a, aPlane, rPlane, gPlane, bPlane, aPlane));
const bool subsampledX = gAccess.getSize().x() > rAccess.getSize().x();
const bool subsampledY = gAccess.getSize().y() > rAccess.getSize().y();
minBounds.resize(sts.size(), Vec4(TCU_INFINITY));
maxBounds.resize(sts.size(), Vec4(-TCU_INFINITY));
uvBounds.resize(sts.size(), Vec4(TCU_INFINITY, -TCU_INFINITY, TCU_INFINITY, -TCU_INFINITY));
ijBounds.resize(sts.size(), IVec4(0x7FFFFFFF, -1 -0x7FFFFFFF, 0x7FFFFFFF, -1 -0x7FFFFFFF));
// Chroma plane sizes must match
DE_ASSERT(rAccess.getSize() == bAccess.getSize());
// Luma plane sizes must match
DE_ASSERT(gAccess.getSize() == aAccess.getSize());
// Luma plane size must match chroma plane or be twice as big
DE_ASSERT(rAccess.getSize().x() == gAccess.getSize().x() || 2 * rAccess.getSize().x() == gAccess.getSize().x());
DE_ASSERT(rAccess.getSize().y() == gAccess.getSize().y() || 2 * rAccess.getSize().y() == gAccess.getSize().y());
DE_ASSERT(filter == vk::VK_FILTER_NEAREST || filter == vk::VK_FILTER_LINEAR);
DE_ASSERT(chromaFilter == vk::VK_FILTER_NEAREST || chromaFilter == vk::VK_FILTER_LINEAR);
DE_ASSERT(subsampledX || !subsampledY);
for (size_t ndx = 0; ndx < sts.size(); ndx++)
{
const Vec2 st (sts[ndx]);
Interval bounds[4];
const Interval u (calculateUV(coordFormat, st[0], gAccess.getSize().x()));
const Interval v (calculateUV(coordFormat, st[1], gAccess.getSize().y()));
uvBounds[ndx][0] = (float)u.lo();
uvBounds[ndx][1] = (float)u.hi();
uvBounds[ndx][2] = (float)v.lo();
uvBounds[ndx][3] = (float)v.hi();
const IVec2 iRange (calculateIJRange(filter, coordFormat, u));
const IVec2 jRange (calculateIJRange(filter, coordFormat, v));
ijBounds[ndx][0] = iRange[0];
ijBounds[ndx][1] = iRange[1];
ijBounds[ndx][2] = jRange[0];
ijBounds[ndx][3] = jRange[1];
for (int j = jRange.x(); j <= jRange.y(); j++)
for (int i = iRange.x(); i <= iRange.y(); i++)
{
if (filter == vk::VK_FILTER_NEAREST)
{
const Interval gValue (lookupWrapped(gAccess, conversionFormat[1], addressModeU, addressModeV, IVec2(i, j)));
const Interval aValue (lookupWrapped(aAccess, conversionFormat[3], addressModeU, addressModeV, IVec2(i, j)));
if (explicitReconstruction || !(subsampledX || subsampledY))
{
Interval rValue, bValue;
if (chromaFilter == vk::VK_FILTER_NEAREST || !subsampledX)
{
// Reconstruct using nearest if needed, otherwise, just take what's already there.
const int subI = subsampledX ? i / 2 : i;
const int subJ = subsampledY ? j / 2 : j;
rValue = lookupWrapped(rAccess, conversionFormat[0], addressModeU, addressModeV, IVec2(subI, subJ));
bValue = lookupWrapped(bAccess, conversionFormat[2], addressModeU, addressModeV, IVec2(subI, subJ));
}
else // vk::VK_FILTER_LINEAR
{
if (subsampledY)
{
rValue = reconstructLinearXYChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i, j);
bValue = reconstructLinearXYChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i, j);
}
else
{
rValue = reconstructLinearXChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, addressModeU, addressModeV, rAccess, i, j);
bValue = reconstructLinearXChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, addressModeU, addressModeV, bAccess, i, j);
}
}
const Interval srcColor[] =
{
rValue,
gValue,
bValue,
aValue
};
Interval dstColor[4];
convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
for (size_t compNdx = 0; compNdx < 4; compNdx++)
bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
}
else
{
const Interval chromaU (subsampledX ? calculateImplicitChromaUV(coordFormat, xChromaOffset, u) : u);
const Interval chromaV (subsampledY ? calculateImplicitChromaUV(coordFormat, yChromaOffset, v) : v);
// Reconstructed chroma samples with implicit filtering
const IVec2 chromaIRange (subsampledX ? calculateIJRange(chromaFilter, coordFormat, chromaU) : IVec2(i, i));
const IVec2 chromaJRange (subsampledY ? calculateIJRange(chromaFilter, coordFormat, chromaV) : IVec2(j, j));
for (int chromaJ = chromaJRange.x(); chromaJ <= chromaJRange.y(); chromaJ++)
for (int chromaI = chromaIRange.x(); chromaI <= chromaIRange.y(); chromaI++)
{
Interval rValue, bValue;
if (chromaFilter == vk::VK_FILTER_NEAREST)
{
rValue = lookupWrapped(rAccess, conversionFormat[0], addressModeU, addressModeV, IVec2(chromaI, chromaJ));
bValue = lookupWrapped(bAccess, conversionFormat[2], addressModeU, addressModeV, IVec2(chromaI, chromaJ));
}
else // vk::VK_FILTER_LINEAR
{
const Interval chromaA (calculateAB(subTexelPrecisionBits, chromaU, chromaI));
const Interval chromaB (calculateAB(subTexelPrecisionBits, chromaV, chromaJ));
rValue = linearSample(rAccess, conversionFormat[0], filteringFormat[0], addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB);
bValue = linearSample(bAccess, conversionFormat[2], filteringFormat[2], addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB);
}
const Interval srcColor[] =
{
rValue,
gValue,
bValue,
aValue
};
Interval dstColor[4];
convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
for (size_t compNdx = 0; compNdx < 4; compNdx++)
bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
}
}
}
else // filter == vk::VK_FILTER_LINEAR
{
const Interval lumaA (calculateAB(subTexelPrecisionBits, u, i));
const Interval lumaB (calculateAB(subTexelPrecisionBits, v, j));
const Interval gValue (linearSample(gAccess, conversionFormat[1], filteringFormat[1], addressModeU, addressModeV, IVec2(i, j), lumaA, lumaB));
const Interval aValue (linearSample(aAccess, conversionFormat[3], filteringFormat[3], addressModeU, addressModeV, IVec2(i, j), lumaA, lumaB));
if (explicitReconstruction || !(subsampledX || subsampledY))
{
Interval rValue, bValue;
if (chromaFilter == vk::VK_FILTER_NEAREST || !subsampledX)
{
rValue = linearInterpolate(filteringFormat[0], lumaA, lumaB,
lookupWrapped(rAccess, conversionFormat[0], addressModeU, addressModeV, IVec2(i / (subsampledX ? 2 : 1), j / (subsampledY ? 2 : 1))),
lookupWrapped(rAccess, conversionFormat[0], addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), j / (subsampledY ? 2 : 1))),
lookupWrapped(rAccess, conversionFormat[0], addressModeU, addressModeV, IVec2(i / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1))),
lookupWrapped(rAccess, conversionFormat[0], addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1))));
bValue = linearInterpolate(filteringFormat[2], lumaA, lumaB,
lookupWrapped(bAccess, conversionFormat[2], addressModeU, addressModeV, IVec2(i / (subsampledX ? 2 : 1), j / (subsampledY ? 2 : 1))),
lookupWrapped(bAccess, conversionFormat[2], addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), j / (subsampledY ? 2 : 1))),
lookupWrapped(bAccess, conversionFormat[2], addressModeU, addressModeV, IVec2(i / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1))),
lookupWrapped(bAccess, conversionFormat[2], addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1))));
}
else // vk::VK_FILTER_LINEAR
{
if (subsampledY)
{
// Linear, Reconstructed xx chroma samples with explicit linear filtering
rValue = linearInterpolate(filteringFormat[0], lumaA, lumaB,
reconstructLinearXYChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i, j),
reconstructLinearXYChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j),
reconstructLinearXYChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i , j + 1),
reconstructLinearXYChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j + 1));
bValue = linearInterpolate(filteringFormat[2], lumaA, lumaB,
reconstructLinearXYChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i, j),
reconstructLinearXYChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j),
reconstructLinearXYChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i , j + 1),
reconstructLinearXYChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j + 1));
}
else
{
// Linear, Reconstructed x chroma samples with explicit linear filtering
rValue = linearInterpolate(filteringFormat[0], lumaA, lumaB,
reconstructLinearXChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, addressModeU, addressModeV, rAccess, i, j),
reconstructLinearXChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j),
reconstructLinearXChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, addressModeU, addressModeV, rAccess, i , j + 1),
reconstructLinearXChromaSample(filteringFormat[0], conversionFormat[0], xChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j + 1));
bValue = linearInterpolate(filteringFormat[2], lumaA, lumaB,
reconstructLinearXChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, addressModeU, addressModeV, bAccess, i, j),
reconstructLinearXChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j),
reconstructLinearXChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, addressModeU, addressModeV, bAccess, i , j + 1),
reconstructLinearXChromaSample(filteringFormat[2], conversionFormat[2], xChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j + 1));
}
}
const Interval srcColor[] =
{
rValue,
gValue,
bValue,
aValue
};
Interval dstColor[4];
convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
for (size_t compNdx = 0; compNdx < 4; compNdx++)
bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
}
else
{
const Interval chromaU (subsampledX ? calculateImplicitChromaUV(coordFormat, xChromaOffset, u) : u);
const Interval chromaV (subsampledY ? calculateImplicitChromaUV(coordFormat, yChromaOffset, v) : v);
// TODO: It looks incorrect to ignore the chroma filter here. Is it?
const IVec2 chromaIRange (calculateNearestIJRange(coordFormat, chromaU));
const IVec2 chromaJRange (calculateNearestIJRange(coordFormat, chromaV));
for (int chromaJ = chromaJRange.x(); chromaJ <= chromaJRange.y(); chromaJ++)
for (int chromaI = chromaIRange.x(); chromaI <= chromaIRange.y(); chromaI++)
{
Interval rValue, bValue;
if (chromaFilter == vk::VK_FILTER_NEAREST)
{
rValue = lookupWrapped(rAccess, conversionFormat[1], addressModeU, addressModeV, IVec2(chromaI, chromaJ));
bValue = lookupWrapped(bAccess, conversionFormat[3], addressModeU, addressModeV, IVec2(chromaI, chromaJ));
}
else // vk::VK_FILTER_LINEAR
{
const Interval chromaA (calculateAB(subTexelPrecisionBits, chromaU, chromaI));
const Interval chromaB (calculateAB(subTexelPrecisionBits, chromaV, chromaJ));
rValue = linearSample(rAccess, conversionFormat[0], filteringFormat[0], addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB);
bValue = linearSample(bAccess, conversionFormat[2], filteringFormat[2], addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB);
}
const Interval srcColor[] =
{
rValue,
gValue,
bValue,
aValue
};
Interval dstColor[4];
convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
for (size_t compNdx = 0; compNdx < 4; compNdx++)
bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
}
}
}
}
minBounds[ndx] = Vec4((float)bounds[0].lo(), (float)bounds[1].lo(), (float)bounds[2].lo(), (float)bounds[3].lo());
maxBounds[ndx] = Vec4((float)bounds[0].hi(), (float)bounds[1].hi(), (float)bounds[2].hi(), (float)bounds[3].hi());
}
}
} // ycbcr
} // vkt