Home | History | Annotate | Download | only in ycbcr
      1 /*-------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright (c) 2017 Google Inc.
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief YCbCr Test Utilities
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "vktYCbCrUtil.hpp"
     25 
     26 #include "vkQueryUtil.hpp"
     27 #include "vkRefUtil.hpp"
     28 #include "vkTypeUtil.hpp"
     29 #include "vkCmdUtil.hpp"
     30 
     31 #include "tcuTextureUtil.hpp"
     32 #include "deMath.h"
     33 #include "deFloat16.h"
     34 #include "tcuVector.hpp"
     35 #include "tcuVectorUtil.hpp"
     36 
     37 #include "deSTLUtil.hpp"
     38 #include "deUniquePtr.hpp"
     39 
     40 namespace vkt
     41 {
     42 namespace ycbcr
     43 {
     44 
     45 using namespace vk;
     46 
     47 using de::MovePtr;
     48 using tcu::FloatFormat;
     49 using tcu::Interval;
     50 using tcu::IVec2;
     51 using tcu::IVec4;
     52 using tcu::UVec2;
     53 using tcu::UVec4;
     54 using tcu::Vec2;
     55 using tcu::Vec4;
     56 using std::vector;
     57 using std::string;
     58 
     59 typedef de::SharedPtr<Allocation>				AllocationSp;
     60 typedef de::SharedPtr<vk::Unique<VkBuffer> >	VkBufferSp;
     61 
     62 // MultiPlaneImageData
     63 
     64 MultiPlaneImageData::MultiPlaneImageData (VkFormat format, const UVec2& size)
     65 	: m_format		(format)
     66 	, m_description	(getPlanarFormatDescription(format))
     67 	, m_size		(size)
     68 {
     69 	for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
     70 	{
     71 		const deUint32	planeW		= size.x() / m_description.planes[planeNdx].widthDivisor;
     72 		const deUint32	planeH		= size.y() / m_description.planes[planeNdx].heightDivisor;
     73 		const deUint32	planeSize	= m_description.planes[planeNdx].elementSizeBytes * planeW * planeH;
     74 
     75 		m_planeData[planeNdx].resize(planeSize);
     76 	}
     77 }
     78 
     79 MultiPlaneImageData::MultiPlaneImageData (const MultiPlaneImageData& other)
     80 	: m_format		(other.m_format)
     81 	, m_description	(other.m_description)
     82 	, m_size		(other.m_size)
     83 {
     84 	for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
     85 		m_planeData[planeNdx] = other.m_planeData[planeNdx];
     86 }
     87 
     88 MultiPlaneImageData::~MultiPlaneImageData (void)
     89 {
     90 }
     91 
     92 tcu::PixelBufferAccess MultiPlaneImageData::getChannelAccess (deUint32 channelNdx)
     93 {
     94 	void*		planePtrs[PlanarFormatDescription::MAX_PLANES];
     95 	deUint32	planeRowPitches[PlanarFormatDescription::MAX_PLANES];
     96 
     97 	for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
     98 	{
     99 		const deUint32	planeW		= m_size.x() / m_description.planes[planeNdx].widthDivisor;
    100 
    101 		planeRowPitches[planeNdx]	= m_description.planes[planeNdx].elementSizeBytes * planeW;
    102 		planePtrs[planeNdx]			= &m_planeData[planeNdx][0];
    103 	}
    104 
    105 	return vk::getChannelAccess(m_description,
    106 								m_size,
    107 								planeRowPitches,
    108 								planePtrs,
    109 								channelNdx);
    110 }
    111 
    112 tcu::ConstPixelBufferAccess MultiPlaneImageData::getChannelAccess (deUint32 channelNdx) const
    113 {
    114 	const void*	planePtrs[PlanarFormatDescription::MAX_PLANES];
    115 	deUint32	planeRowPitches[PlanarFormatDescription::MAX_PLANES];
    116 
    117 	for (deUint32 planeNdx = 0; planeNdx < m_description.numPlanes; ++planeNdx)
    118 	{
    119 		const deUint32	planeW		= m_size.x() / m_description.planes[planeNdx].widthDivisor;
    120 
    121 		planeRowPitches[planeNdx]	= m_description.planes[planeNdx].elementSizeBytes * planeW;
    122 		planePtrs[planeNdx]			= &m_planeData[planeNdx][0];
    123 	}
    124 
    125 	return vk::getChannelAccess(m_description,
    126 								m_size,
    127 								planeRowPitches,
    128 								planePtrs,
    129 								channelNdx);
    130 }
    131 
    132 // Misc utilities
    133 
    134 namespace
    135 {
    136 
    137 void allocateStagingBuffers (const DeviceInterface&			vkd,
    138 							 VkDevice						device,
    139 							 Allocator&						allocator,
    140 							 const MultiPlaneImageData&		imageData,
    141 							 vector<VkBufferSp>*			buffers,
    142 							 vector<AllocationSp>*			allocations)
    143 {
    144 	for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
    145 	{
    146 		const VkBufferCreateInfo	bufferInfo	=
    147 		{
    148 			VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
    149 			DE_NULL,
    150 			(VkBufferCreateFlags)0u,
    151 			(VkDeviceSize)imageData.getPlaneSize(planeNdx),
    152 			VK_BUFFER_USAGE_TRANSFER_SRC_BIT|VK_BUFFER_USAGE_TRANSFER_DST_BIT,
    153 			VK_SHARING_MODE_EXCLUSIVE,
    154 			0u,
    155 			(const deUint32*)DE_NULL,
    156 		};
    157 		Move<VkBuffer>				buffer		(createBuffer(vkd, device, &bufferInfo));
    158 		MovePtr<Allocation>			allocation	(allocator.allocate(getBufferMemoryRequirements(vkd, device, *buffer),
    159 																	MemoryRequirement::HostVisible|MemoryRequirement::Any));
    160 
    161 		VK_CHECK(vkd.bindBufferMemory(device, *buffer, allocation->getMemory(), allocation->getOffset()));
    162 
    163 		buffers->push_back(VkBufferSp(new Unique<VkBuffer>(buffer)));
    164 		allocations->push_back(AllocationSp(allocation.release()));
    165 	}
    166 }
    167 
    168 void allocateAndWriteStagingBuffers (const DeviceInterface&		vkd,
    169 									  VkDevice						device,
    170 									  Allocator&					allocator,
    171 									  const MultiPlaneImageData&	imageData,
    172 									  vector<VkBufferSp>*			buffers,
    173 									  vector<AllocationSp>*			allocations)
    174 {
    175 	allocateStagingBuffers(vkd, device, allocator, imageData, buffers, allocations);
    176 
    177 	for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
    178 	{
    179 		deMemcpy((*allocations)[planeNdx]->getHostPtr(), imageData.getPlanePtr(planeNdx), imageData.getPlaneSize(planeNdx));
    180 		flushMappedMemoryRange(vkd, device, (*allocations)[planeNdx]->getMemory(), 0u, VK_WHOLE_SIZE);
    181 	}
    182 }
    183 
    184 void readStagingBuffers (MultiPlaneImageData*			imageData,
    185 						 const DeviceInterface&			vkd,
    186 						 VkDevice						device,
    187 						 const vector<AllocationSp>&	allocations)
    188 {
    189 	for (deUint32 planeNdx = 0; planeNdx < imageData->getDescription().numPlanes; ++planeNdx)
    190 	{
    191 		invalidateMappedMemoryRange(vkd, device, allocations[planeNdx]->getMemory(), 0u, VK_WHOLE_SIZE);
    192 		deMemcpy(imageData->getPlanePtr(planeNdx), allocations[planeNdx]->getHostPtr(), imageData->getPlaneSize(planeNdx));
    193 	}
    194 }
    195 
    196 } // anonymous
    197 
    198 void checkImageSupport (Context& context, VkFormat format, VkImageCreateFlags createFlags, VkImageTiling tiling)
    199 {
    200 	const bool													disjoint	= (createFlags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0;
    201 	const VkPhysicalDeviceSamplerYcbcrConversionFeatures		features	= context.getSamplerYCbCrConversionFeatures();
    202 	vector<string>												reqExts;
    203 
    204 	if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_sampler_ycbcr_conversion"))
    205 		reqExts.push_back("VK_KHR_sampler_ycbcr_conversion");
    206 
    207 	if (disjoint)
    208 	{
    209 		if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_bind_memory2"))
    210 			reqExts.push_back("VK_KHR_bind_memory2");
    211 		if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_get_memory_requirements2"))
    212 			reqExts.push_back("VK_KHR_get_memory_requirements2");
    213 	}
    214 
    215 	for (vector<string>::const_iterator extIter = reqExts.begin(); extIter != reqExts.end(); ++extIter)
    216 	{
    217 		if (!isDeviceExtensionSupported(context.getUsedApiVersion(), context.getDeviceExtensions(), *extIter))
    218 			TCU_THROW(NotSupportedError, (*extIter + " is not supported").c_str());
    219 	}
    220 
    221 	if (features.samplerYcbcrConversion == VK_FALSE)
    222 	{
    223 		TCU_THROW(NotSupportedError, "samplerYcbcrConversion is not supported");
    224 	}
    225 
    226 	{
    227 		const VkFormatProperties	formatProperties	= getPhysicalDeviceFormatProperties(context.getInstanceInterface(),
    228 																							context.getPhysicalDevice(),
    229 																							format);
    230 		const VkFormatFeatureFlags	featureFlags		= tiling == VK_IMAGE_TILING_OPTIMAL
    231 														? formatProperties.optimalTilingFeatures
    232 														: formatProperties.linearTilingFeatures;
    233 
    234 		if ((featureFlags & (VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT | VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT)) == 0)
    235 			TCU_THROW(NotSupportedError, "YCbCr conversion is not supported for format");
    236 
    237 		if (disjoint && ((featureFlags & VK_FORMAT_FEATURE_DISJOINT_BIT) == 0))
    238 			TCU_THROW(NotSupportedError, "Disjoint planes are not supported for format");
    239 	}
    240 }
    241 
    242 void fillRandom (de::Random* randomGen, MultiPlaneImageData* imageData)
    243 {
    244 	// \todo [pyry] Optimize, take into account bits that must be 0
    245 
    246 	for (deUint32 planeNdx = 0; planeNdx < imageData->getDescription().numPlanes; ++planeNdx)
    247 	{
    248 		const size_t	planeSize	= imageData->getPlaneSize(planeNdx);
    249 		deUint8* const	planePtr	= (deUint8*)imageData->getPlanePtr(planeNdx);
    250 
    251 		for (size_t ndx = 0; ndx < planeSize; ++ndx)
    252 			planePtr[ndx] = randomGen->getUint8();
    253 	}
    254 }
    255 
    256 void fillGradient (MultiPlaneImageData* imageData, const tcu::Vec4& minVal, const tcu::Vec4& maxVal)
    257 {
    258 	const PlanarFormatDescription&	formatInfo	= imageData->getDescription();
    259 
    260 	// \todo [pyry] Optimize: no point in re-rendering source gradient for each channel.
    261 
    262 	for (deUint32 channelNdx = 0; channelNdx < 4; channelNdx++)
    263 	{
    264 		if (formatInfo.hasChannelNdx(channelNdx))
    265 		{
    266 			const tcu::PixelBufferAccess		channelAccess	= imageData->getChannelAccess(channelNdx);
    267 			tcu::TextureLevel					tmpTexture		(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT),  channelAccess.getWidth(), channelAccess.getHeight());
    268 			const tcu::ConstPixelBufferAccess	tmpAccess		= tmpTexture.getAccess();
    269 
    270 			tcu::fillWithComponentGradients(tmpTexture, minVal, maxVal);
    271 
    272 			for (int y = 0; y < channelAccess.getHeight(); ++y)
    273 			for (int x = 0; x < channelAccess.getWidth(); ++x)
    274 			{
    275 				channelAccess.setPixel(tcu::Vec4(tmpAccess.getPixel(x, y)[channelNdx]), x, y);
    276 			}
    277 		}
    278 	}
    279 }
    280 
    281 vector<AllocationSp> allocateAndBindImageMemory (const DeviceInterface&	vkd,
    282 												 VkDevice				device,
    283 												 Allocator&				allocator,
    284 												 VkImage				image,
    285 												 VkFormat				format,
    286 												 VkImageCreateFlags		createFlags,
    287 												 vk::MemoryRequirement	requirement)
    288 {
    289 	vector<AllocationSp> allocations;
    290 
    291 	if ((createFlags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0)
    292 	{
    293 		const deUint32	numPlanes	= getPlaneCount(format);
    294 
    295 		for (deUint32 planeNdx = 0; planeNdx < numPlanes; ++planeNdx)
    296 		{
    297 			const VkImageAspectFlagBits	planeAspect	= getPlaneAspect(planeNdx);
    298 			const VkMemoryRequirements	reqs		= getImagePlaneMemoryRequirements(vkd, device, image, planeAspect);
    299 
    300 			allocations.push_back(AllocationSp(allocator.allocate(reqs, requirement).release()));
    301 
    302 			bindImagePlaneMemory(vkd, device, image, allocations.back()->getMemory(), allocations.back()->getOffset(), planeAspect);
    303 		}
    304 	}
    305 	else
    306 	{
    307 		const VkMemoryRequirements	reqs	= getImageMemoryRequirements(vkd, device, image);
    308 
    309 		allocations.push_back(AllocationSp(allocator.allocate(reqs, requirement).release()));
    310 
    311 		VK_CHECK(vkd.bindImageMemory(device, image, allocations.back()->getMemory(), allocations.back()->getOffset()));
    312 	}
    313 
    314 	return allocations;
    315 }
    316 
    317 void uploadImage (const DeviceInterface&		vkd,
    318 				  VkDevice						device,
    319 				  deUint32						queueFamilyNdx,
    320 				  Allocator&					allocator,
    321 				  VkImage						image,
    322 				  const MultiPlaneImageData&	imageData,
    323 				  VkAccessFlags					nextAccess,
    324 				  VkImageLayout					finalLayout)
    325 {
    326 	const VkQueue					queue			= getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
    327 	const Unique<VkCommandPool>		cmdPool			(createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
    328 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    329 	vector<VkBufferSp>				stagingBuffers;
    330 	vector<AllocationSp>			stagingMemory;
    331 
    332 	const PlanarFormatDescription&	formatDesc		= imageData.getDescription();
    333 
    334 	allocateAndWriteStagingBuffers(vkd, device, allocator, imageData, &stagingBuffers, &stagingMemory);
    335 
    336 	beginCommandBuffer(vkd, *cmdBuffer);
    337 
    338 	{
    339 		const VkImageMemoryBarrier		preCopyBarrier	=
    340 		{
    341 			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
    342 			DE_NULL,
    343 			(VkAccessFlags)0,
    344 			VK_ACCESS_TRANSFER_WRITE_BIT,
    345 			VK_IMAGE_LAYOUT_UNDEFINED,
    346 			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
    347 			VK_QUEUE_FAMILY_IGNORED,
    348 			VK_QUEUE_FAMILY_IGNORED,
    349 			image,
    350 			{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }
    351 		};
    352 
    353 		vkd.cmdPipelineBarrier(*cmdBuffer,
    354 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
    355 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
    356 								(VkDependencyFlags)0u,
    357 								0u,
    358 								(const VkMemoryBarrier*)DE_NULL,
    359 								0u,
    360 								(const VkBufferMemoryBarrier*)DE_NULL,
    361 								1u,
    362 								&preCopyBarrier);
    363 	}
    364 
    365 	for (deUint32 planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
    366 	{
    367 		const VkImageAspectFlagBits	aspect	= (formatDesc.numPlanes > 1)
    368 											? getPlaneAspect(planeNdx)
    369 											: VK_IMAGE_ASPECT_COLOR_BIT;
    370 		const deUint32				planeW	= (formatDesc.numPlanes > 1)
    371 											? imageData.getSize().x() / formatDesc.planes[planeNdx].widthDivisor
    372 											: imageData.getSize().x();
    373 		const deUint32				planeH	= (formatDesc.numPlanes > 1)
    374 											? imageData.getSize().y() / formatDesc.planes[planeNdx].heightDivisor
    375 											: imageData.getSize().y();
    376 		const VkBufferImageCopy		copy	=
    377 		{
    378 			0u,		// bufferOffset
    379 			0u,		// bufferRowLength
    380 			0u,		// bufferImageHeight
    381 			{ (VkImageAspectFlags)aspect, 0u, 0u, 1u },
    382 			makeOffset3D(0u, 0u, 0u),
    383 			makeExtent3D(planeW, planeH, 1u),
    384 		};
    385 
    386 		vkd.cmdCopyBufferToImage(*cmdBuffer, **stagingBuffers[planeNdx], image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, &copy);
    387 	}
    388 
    389 	{
    390 		const VkImageMemoryBarrier		postCopyBarrier	=
    391 		{
    392 			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
    393 			DE_NULL,
    394 			VK_ACCESS_TRANSFER_WRITE_BIT,
    395 			nextAccess,
    396 			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
    397 			finalLayout,
    398 			VK_QUEUE_FAMILY_IGNORED,
    399 			VK_QUEUE_FAMILY_IGNORED,
    400 			image,
    401 			{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }
    402 		};
    403 
    404 		vkd.cmdPipelineBarrier(*cmdBuffer,
    405 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
    406 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
    407 								(VkDependencyFlags)0u,
    408 								0u,
    409 								(const VkMemoryBarrier*)DE_NULL,
    410 								0u,
    411 								(const VkBufferMemoryBarrier*)DE_NULL,
    412 								1u,
    413 								&postCopyBarrier);
    414 	}
    415 
    416 	endCommandBuffer(vkd, *cmdBuffer);
    417 
    418 	submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
    419 }
    420 
    421 void fillImageMemory (const vk::DeviceInterface&							vkd,
    422 					  vk::VkDevice											device,
    423 					  deUint32												queueFamilyNdx,
    424 					  vk::VkImage											image,
    425 					  const std::vector<de::SharedPtr<vk::Allocation> >&	allocations,
    426 					  const MultiPlaneImageData&							imageData,
    427 					  vk::VkAccessFlags										nextAccess,
    428 					  vk::VkImageLayout										finalLayout)
    429 {
    430 	const VkQueue					queue			= getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
    431 	const Unique<VkCommandPool>		cmdPool			(createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
    432 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    433 	const PlanarFormatDescription&	formatDesc		= imageData.getDescription();
    434 
    435 	for (deUint32 planeNdx = 0; planeNdx < formatDesc.numPlanes; ++planeNdx)
    436 	{
    437 		const VkImageAspectFlagBits			aspect		= (formatDesc.numPlanes > 1)
    438 														? getPlaneAspect(planeNdx)
    439 														: VK_IMAGE_ASPECT_COLOR_BIT;
    440 		const de::SharedPtr<Allocation>&	allocation	= allocations.size() > 1
    441 														? allocations[planeNdx]
    442 														: allocations[0];
    443 		const size_t						planeSize	= imageData.getPlaneSize(planeNdx);
    444 		const deUint32						planeH		= imageData.getSize().y() / formatDesc.planes[planeNdx].heightDivisor;
    445 		const VkImageSubresource			subresource	=
    446 		{
    447 			static_cast<vk::VkImageAspectFlags>(aspect),
    448 			0u,
    449 			0u,
    450 		};
    451 		VkSubresourceLayout			layout;
    452 
    453 		vkd.getImageSubresourceLayout(device, image, &subresource, &layout);
    454 
    455 		for (deUint32 row = 0; row < planeH; ++row)
    456 		{
    457 			const size_t		rowSize		= planeSize / planeH;
    458 			void* const			dstPtr		= ((deUint8*)allocation->getHostPtr()) + layout.offset + layout.rowPitch * row;
    459 			const void* const	srcPtr		= ((const deUint8*)imageData.getPlanePtr(planeNdx)) + row * rowSize;
    460 
    461 			deMemcpy(dstPtr, srcPtr, rowSize);
    462 		}
    463 		flushMappedMemoryRange(vkd, device, allocation->getMemory(), 0u, VK_WHOLE_SIZE);
    464 	}
    465 
    466 	beginCommandBuffer(vkd, *cmdBuffer);
    467 
    468 	{
    469 		const VkImageMemoryBarrier		postCopyBarrier	=
    470 		{
    471 			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
    472 			DE_NULL,
    473 			0u,
    474 			nextAccess,
    475 			VK_IMAGE_LAYOUT_PREINITIALIZED,
    476 			finalLayout,
    477 			VK_QUEUE_FAMILY_IGNORED,
    478 			VK_QUEUE_FAMILY_IGNORED,
    479 			image,
    480 			{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }
    481 		};
    482 
    483 		vkd.cmdPipelineBarrier(*cmdBuffer,
    484 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
    485 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
    486 								(VkDependencyFlags)0u,
    487 								0u,
    488 								(const VkMemoryBarrier*)DE_NULL,
    489 								0u,
    490 								(const VkBufferMemoryBarrier*)DE_NULL,
    491 								1u,
    492 								&postCopyBarrier);
    493 	}
    494 
    495 	endCommandBuffer(vkd, *cmdBuffer);
    496 
    497 	submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
    498 }
    499 
    500 void downloadImage (const DeviceInterface&	vkd,
    501 					VkDevice				device,
    502 					deUint32				queueFamilyNdx,
    503 					Allocator&				allocator,
    504 					VkImage					image,
    505 					MultiPlaneImageData*	imageData,
    506 					VkAccessFlags			prevAccess,
    507 					VkImageLayout			initialLayout)
    508 {
    509 	const VkQueue					queue			= getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
    510 	const Unique<VkCommandPool>		cmdPool			(createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
    511 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    512 	vector<VkBufferSp>				stagingBuffers;
    513 	vector<AllocationSp>			stagingMemory;
    514 
    515 	const PlanarFormatDescription&	formatDesc		= imageData->getDescription();
    516 
    517 	allocateStagingBuffers(vkd, device, allocator, *imageData, &stagingBuffers, &stagingMemory);
    518 
    519 	beginCommandBuffer(vkd, *cmdBuffer);
    520 
    521 	for (deUint32 planeNdx = 0; planeNdx < imageData->getDescription().numPlanes; ++planeNdx)
    522 	{
    523 		const VkImageAspectFlagBits	aspect	= (formatDesc.numPlanes > 1)
    524 											? getPlaneAspect(planeNdx)
    525 											: VK_IMAGE_ASPECT_COLOR_BIT;
    526 		{
    527 			const VkImageMemoryBarrier		preCopyBarrier	=
    528 			{
    529 				VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
    530 				DE_NULL,
    531 				prevAccess,
    532 				VK_ACCESS_TRANSFER_READ_BIT,
    533 				initialLayout,
    534 				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
    535 				VK_QUEUE_FAMILY_IGNORED,
    536 				VK_QUEUE_FAMILY_IGNORED,
    537 				image,
    538 				{
    539 					static_cast<vk::VkImageAspectFlags>(aspect),
    540 					0u,
    541 					1u,
    542 					0u,
    543 					1u
    544 				}
    545 			};
    546 
    547 			vkd.cmdPipelineBarrier(*cmdBuffer,
    548 									(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
    549 									(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
    550 									(VkDependencyFlags)0u,
    551 									0u,
    552 									(const VkMemoryBarrier*)DE_NULL,
    553 									0u,
    554 									(const VkBufferMemoryBarrier*)DE_NULL,
    555 									1u,
    556 									&preCopyBarrier);
    557 		}
    558 		{
    559 			const deUint32				planeW	= (formatDesc.numPlanes > 1)
    560 												? imageData->getSize().x() / formatDesc.planes[planeNdx].widthDivisor
    561 												: imageData->getSize().x();
    562 			const deUint32				planeH	= (formatDesc.numPlanes > 1)
    563 												? imageData->getSize().y() / formatDesc.planes[planeNdx].heightDivisor
    564 												: imageData->getSize().y();
    565 			const VkBufferImageCopy		copy	=
    566 			{
    567 				0u,		// bufferOffset
    568 				0u,		// bufferRowLength
    569 				0u,		// bufferImageHeight
    570 				{ (VkImageAspectFlags)aspect, 0u, 0u, 1u },
    571 				makeOffset3D(0u, 0u, 0u),
    572 				makeExtent3D(planeW, planeH, 1u),
    573 			};
    574 
    575 			vkd.cmdCopyImageToBuffer(*cmdBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **stagingBuffers[planeNdx], 1u, &copy);
    576 		}
    577 		{
    578 			const VkBufferMemoryBarrier		postCopyBarrier	=
    579 			{
    580 				VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
    581 				DE_NULL,
    582 				VK_ACCESS_TRANSFER_WRITE_BIT,
    583 				VK_ACCESS_HOST_READ_BIT,
    584 				VK_QUEUE_FAMILY_IGNORED,
    585 				VK_QUEUE_FAMILY_IGNORED,
    586 				**stagingBuffers[planeNdx],
    587 				0u,
    588 				VK_WHOLE_SIZE
    589 			};
    590 
    591 			vkd.cmdPipelineBarrier(*cmdBuffer,
    592 									(VkPipelineStageFlags)VK_PIPELINE_STAGE_TRANSFER_BIT,
    593 									(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
    594 									(VkDependencyFlags)0u,
    595 									0u,
    596 									(const VkMemoryBarrier*)DE_NULL,
    597 									1u,
    598 									&postCopyBarrier,
    599 									0u,
    600 									(const VkImageMemoryBarrier*)DE_NULL);
    601 		}
    602 	}
    603 
    604 	endCommandBuffer(vkd, *cmdBuffer);
    605 
    606 	submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
    607 
    608 	readStagingBuffers(imageData, vkd, device, stagingMemory);
    609 }
    610 
    611 void readImageMemory (const vk::DeviceInterface&							vkd,
    612 					  vk::VkDevice											device,
    613 					  deUint32												queueFamilyNdx,
    614 					  vk::VkImage											image,
    615 					  const std::vector<de::SharedPtr<vk::Allocation> >&	allocations,
    616 					  MultiPlaneImageData*									imageData,
    617 					  vk::VkAccessFlags										prevAccess,
    618 					  vk::VkImageLayout										initialLayout)
    619 {
    620 	const VkQueue					queue			= getDeviceQueue(vkd, device, queueFamilyNdx, 0u);
    621 	const Unique<VkCommandPool>		cmdPool			(createCommandPool(vkd, device, (VkCommandPoolCreateFlags)0, queueFamilyNdx));
    622 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    623 	const PlanarFormatDescription&	formatDesc		= imageData->getDescription();
    624 
    625 	beginCommandBuffer(vkd, *cmdBuffer);
    626 
    627 	{
    628 		const VkImageMemoryBarrier		preCopyBarrier	=
    629 		{
    630 			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
    631 			DE_NULL,
    632 			prevAccess,
    633 			vk::VK_ACCESS_HOST_READ_BIT,
    634 			initialLayout,
    635 			VK_IMAGE_LAYOUT_GENERAL,
    636 			VK_QUEUE_FAMILY_IGNORED,
    637 			VK_QUEUE_FAMILY_IGNORED,
    638 			image,
    639 			{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }
    640 		};
    641 
    642 		vkd.cmdPipelineBarrier(*cmdBuffer,
    643 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
    644 								(VkPipelineStageFlags)VK_PIPELINE_STAGE_HOST_BIT,
    645 								(VkDependencyFlags)0u,
    646 								0u,
    647 								(const VkMemoryBarrier*)DE_NULL,
    648 								0u,
    649 								(const VkBufferMemoryBarrier*)DE_NULL,
    650 								1u,
    651 								&preCopyBarrier);
    652 	}
    653 
    654 	endCommandBuffer(vkd, *cmdBuffer);
    655 
    656 	submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
    657 
    658 	for (deUint32 planeNdx = 0; planeNdx < formatDesc.numPlanes; ++planeNdx)
    659 	{
    660 		const VkImageAspectFlagBits			aspect		= (formatDesc.numPlanes > 1)
    661 														? getPlaneAspect(planeNdx)
    662 														: VK_IMAGE_ASPECT_COLOR_BIT;
    663 		const de::SharedPtr<Allocation>&	allocation	= allocations.size() > 1
    664 														? allocations[planeNdx]
    665 														: allocations[0];
    666 		const size_t						planeSize	= imageData->getPlaneSize(planeNdx);
    667 		const deUint32						planeH		= imageData->getSize().y() / formatDesc.planes[planeNdx].heightDivisor;
    668 		const VkImageSubresource			subresource	=
    669 		{
    670 			static_cast<vk::VkImageAspectFlags>(aspect),
    671 			0u,
    672 			0u,
    673 		};
    674 		VkSubresourceLayout			layout;
    675 
    676 		vkd.getImageSubresourceLayout(device, image, &subresource, &layout);
    677 
    678 		invalidateMappedMemoryRange(vkd, device, allocation->getMemory(), 0u, VK_WHOLE_SIZE);
    679 
    680 		for (deUint32 row = 0; row < planeH; ++row)
    681 		{
    682 			const size_t		rowSize	= planeSize / planeH;
    683 			const void* const	srcPtr	= ((const deUint8*)allocation->getHostPtr()) + layout.offset + layout.rowPitch * row;
    684 			void* const			dstPtr	= ((deUint8*)imageData->getPlanePtr(planeNdx)) + row * rowSize;
    685 
    686 			deMemcpy(dstPtr, srcPtr, rowSize);
    687 		}
    688 	}
    689 }
    690 
    691 // ChannelAccess utilities
    692 namespace
    693 {
    694 
    695 //! Extend < 32b signed integer to 32b
    696 inline deInt32 signExtend (deUint32 src, int bits)
    697 {
    698 	const deUint32 signBit = 1u << (bits-1);
    699 
    700 	src |= ~((src & signBit) - 1);
    701 
    702 	return (deInt32)src;
    703 }
    704 
    705 deUint32 divRoundUp (deUint32 a, deUint32 b)
    706 {
    707 	if (a % b == 0)
    708 		return a / b;
    709 	else
    710 		return (a / b) + 1;
    711 }
    712 
    713 // \todo Taken from tcuTexture.cpp
    714 // \todo [2011-09-21 pyry] Move to tcutil?
    715 template <typename T>
    716 inline T convertSatRte (float f)
    717 {
    718 	// \note Doesn't work for 64-bit types
    719 	DE_STATIC_ASSERT(sizeof(T) < sizeof(deUint64));
    720 	DE_STATIC_ASSERT((-3 % 2 != 0) && (-4 % 2 == 0));
    721 
    722 	deInt64	minVal	= std::numeric_limits<T>::min();
    723 	deInt64 maxVal	= std::numeric_limits<T>::max();
    724 	float	q		= deFloatFrac(f);
    725 	deInt64 intVal	= (deInt64)(f-q);
    726 
    727 	// Rounding.
    728 	if (q == 0.5f)
    729 	{
    730 		if (intVal % 2 != 0)
    731 			intVal++;
    732 	}
    733 	else if (q > 0.5f)
    734 		intVal++;
    735 	// else Don't add anything
    736 
    737 	// Saturate.
    738 	intVal = de::max(minVal, de::min(maxVal, intVal));
    739 
    740 	return (T)intVal;
    741 }
    742 
    743 } // anonymous
    744 
    745 ChannelAccess::ChannelAccess (tcu::TextureChannelClass	channelClass,
    746 							  deUint8					channelSize,
    747 							  const tcu::IVec3&			size,
    748 							  const tcu::IVec3&			bitPitch,
    749 							  void*						data,
    750 							  deUint32					bitOffset)
    751 	: m_channelClass	(channelClass)
    752 	, m_channelSize		(channelSize)
    753 	, m_size			(size)
    754 	, m_bitPitch		(bitPitch)
    755 
    756 	, m_data			((deUint8*)data + (bitOffset / 8))
    757 	, m_bitOffset		(bitOffset % 8)
    758 {
    759 }
    760 
    761 deUint32 ChannelAccess::getChannelUint (const tcu::IVec3& pos) const
    762 {
    763 	DE_ASSERT(pos[0] < m_size[0]);
    764 	DE_ASSERT(pos[1] < m_size[1]);
    765 	DE_ASSERT(pos[2] < m_size[2]);
    766 
    767 	const deInt32			bitOffset	(m_bitOffset + tcu::dot(m_bitPitch, pos));
    768 	const deUint8* const	firstByte	= ((const deUint8*)m_data) + (bitOffset / 8);
    769 	const deUint32			byteCount	= divRoundUp((bitOffset + m_channelSize) - 8u * (bitOffset / 8u), 8u);
    770 	const deUint32			mask		(m_channelSize == 32u ? ~0x0u : (0x1u << m_channelSize) - 1u);
    771 	const deUint32			offset		= bitOffset % 8;
    772 	deUint32				bits		= 0u;
    773 
    774 	deMemcpy(&bits, firstByte, byteCount);
    775 
    776 	return (bits >> offset) & mask;
    777 }
    778 
    779 void ChannelAccess::setChannel (const tcu::IVec3& pos, deUint32 x)
    780 {
    781 	DE_ASSERT(pos[0] < m_size[0]);
    782 	DE_ASSERT(pos[1] < m_size[1]);
    783 	DE_ASSERT(pos[2] < m_size[2]);
    784 
    785 	const deInt32	bitOffset	(m_bitOffset + tcu::dot(m_bitPitch, pos));
    786 	deUint8* const	firstByte	= ((deUint8*)m_data) + (bitOffset / 8);
    787 	const deUint32	byteCount	= divRoundUp((bitOffset + m_channelSize) - 8u * (bitOffset / 8u), 8u);
    788 	const deUint32	mask		(m_channelSize == 32u ? ~0x0u : (0x1u << m_channelSize) - 1u);
    789 	const deUint32	offset		= bitOffset % 8;
    790 
    791 	const deUint32	bits		= (x & mask) << offset;
    792 	deUint32		oldBits		= 0;
    793 
    794 	deMemcpy(&oldBits, firstByte, byteCount);
    795 
    796 	{
    797 		const deUint32	newBits	= bits | (oldBits & (~(mask << offset)));
    798 
    799 		deMemcpy(firstByte, &newBits,  byteCount);
    800 	}
    801 }
    802 
    803 float ChannelAccess::getChannel (const tcu::IVec3& pos) const
    804 {
    805 	const deUint32	bits	(getChannelUint(pos));
    806 
    807 	switch (m_channelClass)
    808 	{
    809 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
    810 			return (float)bits / (float)(m_channelSize == 32 ? ~0x0u : ((0x1u << m_channelSize) - 1u));
    811 
    812 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
    813 			return (float)bits;
    814 
    815 		case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
    816 			return de::max(-1.0f, (float)signExtend(bits, m_channelSize) / (float)((0x1u << (m_channelSize - 1u)) - 1u));
    817 
    818 		case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
    819 			return (float)signExtend(bits, m_channelSize);
    820 
    821 		case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
    822 			if (m_channelSize == 32)
    823 				return tcu::Float32(bits).asFloat();
    824 			else
    825 			{
    826 				DE_FATAL("Float type not supported");
    827 				return -1.0f;
    828 			}
    829 
    830 		default:
    831 			DE_FATAL("Unknown texture channel class");
    832 			return -1.0f;
    833 	}
    834 }
    835 
    836 tcu::Interval ChannelAccess::getChannel (const tcu::FloatFormat&	conversionFormat,
    837 										 const tcu::IVec3&			pos) const
    838 {
    839 	const deUint32	bits	(getChannelUint(pos));
    840 
    841 	switch (m_channelClass)
    842 	{
    843 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
    844 			return conversionFormat.roundOut(conversionFormat.roundOut((double)bits, false)
    845 											/ conversionFormat.roundOut((double)(m_channelSize == 32 ? ~0x0u : ((0x1u << m_channelSize) - 1u)), false), false);
    846 
    847 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
    848 			return conversionFormat.roundOut((double)bits, false);
    849 
    850 		case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
    851 		{
    852 			const tcu::Interval result (conversionFormat.roundOut(conversionFormat.roundOut((double)signExtend(bits, m_channelSize), false)
    853 																/ conversionFormat.roundOut((double)((0x1u << (m_channelSize - 1u)) - 1u), false), false));
    854 
    855 			return tcu::Interval(de::max(-1.0, result.lo()), de::max(-1.0, result.hi()));
    856 		}
    857 
    858 		case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
    859 			return conversionFormat.roundOut((double)signExtend(bits, m_channelSize), false);
    860 
    861 		case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
    862 			if (m_channelSize == 32)
    863 				return conversionFormat.roundOut(tcu::Float32(bits).asFloat(), false);
    864 			else
    865 			{
    866 				DE_FATAL("Float type not supported");
    867 				return tcu::Interval();
    868 			}
    869 
    870 		default:
    871 			DE_FATAL("Unknown texture channel class");
    872 			return tcu::Interval();
    873 	}
    874 }
    875 
    876 void ChannelAccess::setChannel (const tcu::IVec3& pos, float x)
    877 {
    878 	DE_ASSERT(pos[0] < m_size[0]);
    879 	DE_ASSERT(pos[1] < m_size[1]);
    880 	DE_ASSERT(pos[2] < m_size[2]);
    881 
    882 	const deUint32	mask	(m_channelSize == 32u ? ~0x0u : (0x1u << m_channelSize) - 1u);
    883 
    884 	switch (m_channelClass)
    885 	{
    886 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
    887 		{
    888 			const deUint32	maxValue	(mask);
    889 			const deUint32	value		(de::min(maxValue, (deUint32)convertSatRte<deUint32>(x * (float)maxValue)));
    890 			setChannel(pos, value);
    891 			break;
    892 		}
    893 
    894 		case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
    895 		{
    896 			const deInt32	range	((0x1u << (m_channelSize - 1u)) - 1u);
    897 			const deUint32	value	((deUint32)de::clamp<deInt32>(convertSatRte<deInt32>(x * (float)range), -range, range));
    898 			setChannel(pos, value);
    899 			break;
    900 		}
    901 
    902 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
    903 		{
    904 			const deUint32	maxValue	(mask);
    905 			const deUint32	value		(de::min(maxValue, (deUint32)x));
    906 			setChannel(pos, value);
    907 			break;
    908 		}
    909 
    910 		case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
    911 		{
    912 			const deInt32	minValue	(-(deInt32)(1u << (m_channelSize - 1u)));
    913 			const deInt32	maxValue	((deInt32)((1u << (m_channelSize - 1u)) - 1u));
    914 			const deUint32	value		((deUint32)de::clamp((deInt32)x, minValue, maxValue));
    915 			setChannel(pos, value);
    916 			break;
    917 		}
    918 
    919 		case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
    920 		{
    921 			if (m_channelSize == 32)
    922 			{
    923 				const deUint32	value		= tcu::Float32(x).bits();
    924 				setChannel(pos, value);
    925 			}
    926 			else
    927 				DE_FATAL("Float type not supported");
    928 			break;
    929 		}
    930 
    931 		default:
    932 			DE_FATAL("Unknown texture channel class");
    933 	}
    934 }
    935 
    936 ChannelAccess getChannelAccess (MultiPlaneImageData&				data,
    937 								const vk::PlanarFormatDescription&	formatInfo,
    938 								const UVec2&						size,
    939 								int									channelNdx)
    940 {
    941 	DE_ASSERT(formatInfo.hasChannelNdx(channelNdx));
    942 
    943 	const deUint32	planeNdx			= formatInfo.channels[channelNdx].planeNdx;
    944 	const deUint32	valueOffsetBits		= formatInfo.channels[channelNdx].offsetBits;
    945 	const deUint32	pixelStrideBytes	= formatInfo.channels[channelNdx].strideBytes;
    946 	const deUint32	pixelStrideBits		= pixelStrideBytes * 8;
    947 	const deUint8	sizeBits			= formatInfo.channels[channelNdx].sizeBits;
    948 
    949 	DE_ASSERT(size.x() % formatInfo.planes[planeNdx].widthDivisor == 0);
    950 	DE_ASSERT(size.y() % formatInfo.planes[planeNdx].heightDivisor == 0);
    951 
    952 	deUint32		accessWidth			= size.x() / formatInfo.planes[planeNdx].widthDivisor;
    953 	const deUint32	accessHeight		= size.y() / formatInfo.planes[planeNdx].heightDivisor;
    954 	const deUint32	elementSizeBytes	= formatInfo.planes[planeNdx].elementSizeBytes;
    955 
    956 	const deUint32	rowPitch			= formatInfo.planes[planeNdx].elementSizeBytes * accessWidth;
    957 	const deUint32	rowPitchBits		= rowPitch * 8;
    958 
    959 	if (pixelStrideBytes != elementSizeBytes)
    960 	{
    961 		DE_ASSERT(elementSizeBytes % pixelStrideBytes == 0);
    962 		accessWidth *= elementSizeBytes/pixelStrideBytes;
    963 	}
    964 
    965 	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);
    966 }
    967 
    968 bool isXChromaSubsampled (vk::VkFormat format)
    969 {
    970 	switch (format)
    971 	{
    972 		case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
    973 		case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
    974 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
    975 		case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
    976 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
    977 		case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
    978 		case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
    979 		case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
    980 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
    981 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
    982 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
    983 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
    984 		case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
    985 		case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
    986 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
    987 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
    988 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
    989 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
    990 		case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
    991 		case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
    992 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
    993 		case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
    994 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
    995 		case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
    996 			return true;
    997 
    998 		default:
    999 			return false;
   1000 	}
   1001 }
   1002 
   1003 bool isYChromaSubsampled (vk::VkFormat format)
   1004 {
   1005 	switch (format)
   1006 	{
   1007 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
   1008 		case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
   1009 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
   1010 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
   1011 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
   1012 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
   1013 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
   1014 		case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
   1015 			return true;
   1016 
   1017 		default:
   1018 			return false;
   1019 	}
   1020 }
   1021 
   1022 bool areLsb6BitsDontCare(vk::VkFormat srcFormat, vk::VkFormat dstFormat)
   1023 {
   1024 	if ((srcFormat == vk::VK_FORMAT_R10X6_UNORM_PACK16)	                        ||
   1025 		(dstFormat == vk::VK_FORMAT_R10X6_UNORM_PACK16)                         ||
   1026 		(srcFormat == vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16)                   ||
   1027 		(dstFormat == vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16)                   ||
   1028 		(srcFormat == vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)         ||
   1029 		(dstFormat == vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)         ||
   1030 		(srcFormat == vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16)     ||
   1031 		(dstFormat == vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16)     ||
   1032 		(srcFormat == vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16)     ||
   1033 		(dstFormat == vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16)     ||
   1034 		(srcFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16)  ||
   1035 		(dstFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16)  ||
   1036 		(srcFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16) ||
   1037 		(dstFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16) ||
   1038 		(srcFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16) ||
   1039 		(dstFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16) ||
   1040 		(srcFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16)  ||
   1041 		(dstFormat == vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16)  ||
   1042 		(srcFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16) ||
   1043 		(dstFormat == vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16))
   1044 	{
   1045 		return true;
   1046 	}
   1047 
   1048 	return false;
   1049 }
   1050 
   1051 bool areLsb4BitsDontCare(vk::VkFormat srcFormat, vk::VkFormat dstFormat)
   1052 {
   1053 	if ((srcFormat == vk::VK_FORMAT_R12X4_UNORM_PACK16)                         ||
   1054 		(dstFormat == vk::VK_FORMAT_R12X4_UNORM_PACK16)                         ||
   1055 		(srcFormat == vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16)                   ||
   1056 		(dstFormat == vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16)                   ||
   1057 		(srcFormat == vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16)         ||
   1058 		(dstFormat == vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16)         ||
   1059 		(srcFormat == vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16)     ||
   1060 		(dstFormat == vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16)     ||
   1061 		(srcFormat == vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16)     ||
   1062 		(dstFormat == vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16)     ||
   1063 		(srcFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16) ||
   1064 		(dstFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16) ||
   1065 		(srcFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16)  ||
   1066 		(dstFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16)  ||
   1067 		(srcFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16) ||
   1068 		(dstFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16) ||
   1069 		(srcFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16)  ||
   1070 		(dstFormat == vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16)  ||
   1071 		(srcFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16) ||
   1072 		(dstFormat == vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16))
   1073 	{
   1074 		return true;
   1075 	}
   1076 
   1077 	return false;
   1078 }
   1079 
   1080 // \note Used for range expansion
   1081 tcu::UVec4 getYCbCrBitDepth (vk::VkFormat format)
   1082 {
   1083 	switch (format)
   1084 	{
   1085 		case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
   1086 		case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
   1087 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
   1088 		case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
   1089 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
   1090 		case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
   1091 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
   1092 			return tcu::UVec4(8, 8, 8, 0);
   1093 
   1094 		case vk::VK_FORMAT_R10X6_UNORM_PACK16:
   1095 			return tcu::UVec4(10, 0, 0, 0);
   1096 
   1097 		case vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
   1098 			return tcu::UVec4(10, 10, 0, 0);
   1099 
   1100 		case vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
   1101 			return tcu::UVec4(10, 10, 10, 10);
   1102 
   1103 		case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
   1104 		case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
   1105 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
   1106 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
   1107 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
   1108 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
   1109 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
   1110 			return tcu::UVec4(10, 10, 10, 0);
   1111 
   1112 		case vk::VK_FORMAT_R12X4_UNORM_PACK16:
   1113 			return tcu::UVec4(12, 0, 0, 0);
   1114 
   1115 		case vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
   1116 			return tcu::UVec4(12, 12, 0, 0);
   1117 
   1118 		case vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
   1119 		case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
   1120 		case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
   1121 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
   1122 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
   1123 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
   1124 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
   1125 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
   1126 			return tcu::UVec4(12, 12, 12, 12);
   1127 
   1128 		case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
   1129 		case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
   1130 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
   1131 		case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
   1132 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
   1133 		case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
   1134 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
   1135 			return tcu::UVec4(16, 16, 16, 0);
   1136 
   1137 		default:
   1138 			return tcu::getTextureFormatBitDepth(vk::mapVkFormat(format)).cast<deUint32>();
   1139 	}
   1140 }
   1141 
   1142 // \note Taken from explicit lod filtering tests
   1143 tcu::FloatFormat getYCbCrFilteringPrecision (vk::VkFormat format)
   1144 {
   1145 	const tcu::FloatFormat	reallyLow	(0, 0, 6, false, tcu::YES);
   1146 	const tcu::FloatFormat	low			(0, 0, 7, false, tcu::YES);
   1147 	const tcu::FloatFormat	fp16		(-14, 15, 10, false);
   1148 	const tcu::FloatFormat	fp32		(-126, 127, 23, true);
   1149 
   1150 	switch (format)
   1151 	{
   1152 		case vk::VK_FORMAT_R4G4B4A4_UNORM_PACK16:
   1153 		case vk::VK_FORMAT_B4G4R4A4_UNORM_PACK16:
   1154 		case vk::VK_FORMAT_R5G6B5_UNORM_PACK16:
   1155 		case vk::VK_FORMAT_B5G6R5_UNORM_PACK16:
   1156 		case vk::VK_FORMAT_R5G5B5A1_UNORM_PACK16:
   1157 		case vk::VK_FORMAT_B5G5R5A1_UNORM_PACK16:
   1158 		case vk::VK_FORMAT_A1R5G5B5_UNORM_PACK16:
   1159 			return reallyLow;
   1160 
   1161 		case vk::VK_FORMAT_R8G8B8_UNORM:
   1162 		case vk::VK_FORMAT_B8G8R8_UNORM:
   1163 		case vk::VK_FORMAT_R8G8B8A8_UNORM:
   1164 		case vk::VK_FORMAT_B8G8R8A8_UNORM:
   1165 		case vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32:
   1166 		case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
   1167 		case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
   1168 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
   1169 		case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
   1170 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
   1171 		case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
   1172 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
   1173 		case vk::VK_FORMAT_A2R10G10B10_UNORM_PACK32:
   1174 		case vk::VK_FORMAT_A2B10G10R10_UNORM_PACK32:
   1175 			return low;
   1176 
   1177 		case vk::VK_FORMAT_R16G16B16_UNORM:
   1178 		case vk::VK_FORMAT_R16G16B16A16_UNORM:
   1179 		case vk::VK_FORMAT_R10X6_UNORM_PACK16:
   1180 		case vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
   1181 		case vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
   1182 		case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
   1183 		case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
   1184 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
   1185 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
   1186 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
   1187 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
   1188 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
   1189 		case vk::VK_FORMAT_R12X4_UNORM_PACK16:
   1190 		case vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
   1191 		case vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
   1192 		case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
   1193 		case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
   1194 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
   1195 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
   1196 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
   1197 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
   1198 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
   1199 		case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
   1200 		case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
   1201 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
   1202 		case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
   1203 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
   1204 		case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
   1205 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
   1206 			return fp16;
   1207 
   1208 		default:
   1209 			DE_FATAL("Precision not defined for format");
   1210 			return fp32;
   1211 	}
   1212 }
   1213 
   1214 // \note Taken from explicit lod filtering tests
   1215 tcu::FloatFormat getYCbCrConversionPrecision (vk::VkFormat format)
   1216 {
   1217 	const tcu::FloatFormat	reallyLow	(0, 0, 8, false, tcu::YES);
   1218 	const tcu::FloatFormat	fp16		(-14, 15, 10, false);
   1219 	const tcu::FloatFormat	fp32		(-126, 127, 23, true);
   1220 
   1221 	switch (format)
   1222 	{
   1223 		case vk::VK_FORMAT_R4G4B4A4_UNORM_PACK16:
   1224 		case vk::VK_FORMAT_B4G4R4A4_UNORM_PACK16:
   1225 		case vk::VK_FORMAT_R5G6B5_UNORM_PACK16:
   1226 		case vk::VK_FORMAT_B5G6R5_UNORM_PACK16:
   1227 		case vk::VK_FORMAT_R5G5B5A1_UNORM_PACK16:
   1228 		case vk::VK_FORMAT_B5G5R5A1_UNORM_PACK16:
   1229 		case vk::VK_FORMAT_A1R5G5B5_UNORM_PACK16:
   1230 			return reallyLow;
   1231 
   1232 		case vk::VK_FORMAT_R8G8B8_UNORM:
   1233 		case vk::VK_FORMAT_B8G8R8_UNORM:
   1234 		case vk::VK_FORMAT_R8G8B8A8_UNORM:
   1235 		case vk::VK_FORMAT_B8G8R8A8_UNORM:
   1236 		case vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32:
   1237 		case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
   1238 		case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
   1239 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
   1240 		case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
   1241 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
   1242 		case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
   1243 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
   1244 			return reallyLow;
   1245 
   1246 		case vk::VK_FORMAT_A2R10G10B10_UNORM_PACK32:
   1247 		case vk::VK_FORMAT_A2B10G10R10_UNORM_PACK32:
   1248 		case vk::VK_FORMAT_R16G16B16_UNORM:
   1249 		case vk::VK_FORMAT_R16G16B16A16_UNORM:
   1250 		case vk::VK_FORMAT_R10X6_UNORM_PACK16:
   1251 		case vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
   1252 		case vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
   1253 		case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
   1254 		case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
   1255 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
   1256 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
   1257 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
   1258 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
   1259 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
   1260 		case vk::VK_FORMAT_R12X4_UNORM_PACK16:
   1261 		case vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
   1262 		case vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
   1263 		case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
   1264 		case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
   1265 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
   1266 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
   1267 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
   1268 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
   1269 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
   1270 		case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
   1271 		case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
   1272 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
   1273 		case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
   1274 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
   1275 		case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
   1276 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
   1277 			return fp16;
   1278 
   1279 		default:
   1280 			DE_FATAL("Precision not defined for format");
   1281 			return fp32;
   1282 	}
   1283 }
   1284 
   1285 deUint32 getYCbCrFormatChannelCount (vk::VkFormat format)
   1286 {
   1287 	switch (format)
   1288 	{
   1289 		case vk::VK_FORMAT_A1R5G5B5_UNORM_PACK16:
   1290 		case vk::VK_FORMAT_A2B10G10R10_UNORM_PACK32:
   1291 		case vk::VK_FORMAT_A2R10G10B10_UNORM_PACK32:
   1292 		case vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32:
   1293 		case vk::VK_FORMAT_B4G4R4A4_UNORM_PACK16:
   1294 		case vk::VK_FORMAT_B5G5R5A1_UNORM_PACK16:
   1295 		case vk::VK_FORMAT_B8G8R8A8_UNORM:
   1296 		case vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
   1297 		case vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
   1298 		case vk::VK_FORMAT_R16G16B16A16_UNORM:
   1299 		case vk::VK_FORMAT_R4G4B4A4_UNORM_PACK16:
   1300 		case vk::VK_FORMAT_R5G5B5A1_UNORM_PACK16:
   1301 		case vk::VK_FORMAT_R8G8B8A8_UNORM:
   1302 			return 4;
   1303 
   1304 		case vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
   1305 		case vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
   1306 		case vk::VK_FORMAT_B16G16R16G16_422_UNORM:
   1307 		case vk::VK_FORMAT_B5G6R5_UNORM_PACK16:
   1308 		case vk::VK_FORMAT_B8G8R8G8_422_UNORM:
   1309 		case vk::VK_FORMAT_B8G8R8_UNORM:
   1310 		case vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
   1311 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
   1312 		case vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
   1313 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
   1314 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
   1315 		case vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
   1316 		case vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
   1317 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
   1318 		case vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
   1319 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
   1320 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
   1321 		case vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
   1322 		case vk::VK_FORMAT_G16B16G16R16_422_UNORM:
   1323 		case vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
   1324 		case vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
   1325 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
   1326 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
   1327 		case vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
   1328 		case vk::VK_FORMAT_G8B8G8R8_422_UNORM:
   1329 		case vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
   1330 		case vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
   1331 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
   1332 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
   1333 		case vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
   1334 		case vk::VK_FORMAT_R16G16B16_UNORM:
   1335 		case vk::VK_FORMAT_R5G6B5_UNORM_PACK16:
   1336 		case vk::VK_FORMAT_R8G8B8_UNORM:
   1337 			return 3;
   1338 
   1339 		case vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
   1340 		case vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
   1341 			return 2;
   1342 
   1343 		case vk::VK_FORMAT_R10X6_UNORM_PACK16:
   1344 		case vk::VK_FORMAT_R12X4_UNORM_PACK16:
   1345 			return 1;
   1346 
   1347 		default:
   1348 			DE_FATAL("Unknown number of channels");
   1349 			return -1;
   1350 	}
   1351 }
   1352 
   1353 // YCbCr color conversion utilities
   1354 namespace
   1355 {
   1356 
   1357 tcu::Interval rangeExpandChroma (vk::VkSamplerYcbcrRange		range,
   1358 								 const tcu::FloatFormat&		conversionFormat,
   1359 								 const deUint32					bits,
   1360 								 const tcu::Interval&			sample)
   1361 {
   1362 	const deUint32	values	(0x1u << bits);
   1363 
   1364 	switch (range)
   1365 	{
   1366 		case vk::VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
   1367 			return conversionFormat.roundOut(sample - conversionFormat.roundOut(tcu::Interval((double)(0x1u << (bits - 1u)) / (double)((0x1u << bits) - 1u)), false), false);
   1368 
   1369 		case vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
   1370 		{
   1371 			const tcu::Interval	a			(conversionFormat.roundOut(sample * tcu::Interval((double)(values - 1u)), false));
   1372 			const tcu::Interval	dividend	(conversionFormat.roundOut(a - tcu::Interval((double)(128u * (0x1u << (bits - 8u)))), false));
   1373 			const tcu::Interval	divisor		((double)(224u * (0x1u << (bits - 8u))));
   1374 			const tcu::Interval	result		(conversionFormat.roundOut(dividend / divisor, false));
   1375 
   1376 			return result;
   1377 		}
   1378 
   1379 		default:
   1380 			DE_FATAL("Unknown YCbCrRange");
   1381 			return tcu::Interval();
   1382 	}
   1383 }
   1384 
   1385 tcu::Interval rangeExpandLuma (vk::VkSamplerYcbcrRange		range,
   1386 							   const tcu::FloatFormat&		conversionFormat,
   1387 							   const deUint32				bits,
   1388 							   const tcu::Interval&			sample)
   1389 {
   1390 	const deUint32	values	(0x1u << bits);
   1391 
   1392 	switch (range)
   1393 	{
   1394 		case vk::VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
   1395 			return conversionFormat.roundOut(sample, false);
   1396 
   1397 		case vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
   1398 		{
   1399 			const tcu::Interval	a			(conversionFormat.roundOut(sample * tcu::Interval((double)(values - 1u)), false));
   1400 			const tcu::Interval	dividend	(conversionFormat.roundOut(a - tcu::Interval((double)(16u * (0x1u << (bits - 8u)))), false));
   1401 			const tcu::Interval	divisor		((double)(219u * (0x1u << (bits - 8u))));
   1402 			const tcu::Interval	result		(conversionFormat.roundOut(dividend / divisor, false));
   1403 
   1404 			return result;
   1405 		}
   1406 
   1407 		default:
   1408 			DE_FATAL("Unknown YCbCrRange");
   1409 			return tcu::Interval();
   1410 	}
   1411 }
   1412 
   1413 tcu::Interval clampMaybe (const tcu::Interval&	x,
   1414 						  double				min,
   1415 						  double				max)
   1416 {
   1417 	tcu::Interval result = x;
   1418 
   1419 	DE_ASSERT(min <= max);
   1420 
   1421 	if (x.lo() < min)
   1422 		result = result | tcu::Interval(min);
   1423 
   1424 	if (x.hi() > max)
   1425 		result = result | tcu::Interval(max);
   1426 
   1427 	return result;
   1428 }
   1429 
   1430 void convertColor (vk::VkSamplerYcbcrModelConversion	colorModel,
   1431 				   vk::VkSamplerYcbcrRange				range,
   1432 				   const tcu::FloatFormat&				conversionFormat,
   1433 				   const tcu::UVec4&					bitDepth,
   1434 				   const tcu::Interval					input[4],
   1435 				   tcu::Interval						output[4])
   1436 {
   1437 	switch (colorModel)
   1438 	{
   1439 		case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY:
   1440 		{
   1441 			for (size_t ndx = 0; ndx < 4; ndx++)
   1442 				output[ndx] = input[ndx];
   1443 			break;
   1444 		}
   1445 
   1446 		case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY:
   1447 		{
   1448 			output[0] = clampMaybe(rangeExpandChroma(range, conversionFormat, bitDepth[0], input[0]), -0.5, 0.5);
   1449 			output[1] = clampMaybe(rangeExpandLuma(range, conversionFormat, bitDepth[1], input[1]), 0.0, 1.0);
   1450 			output[2] = clampMaybe(rangeExpandChroma(range, conversionFormat, bitDepth[2], input[2]), -0.5, 0.5);
   1451 			output[3] = input[3];
   1452 			break;
   1453 		}
   1454 
   1455 		case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601:
   1456 		{
   1457 			const tcu::Interval	y			(rangeExpandLuma(range, conversionFormat, bitDepth[1], input[1]));
   1458 			const tcu::Interval	cr			(rangeExpandChroma(range, conversionFormat, bitDepth[0], input[0]));
   1459 			const tcu::Interval	cb			(rangeExpandChroma(range, conversionFormat, bitDepth[2], input[2]));
   1460 
   1461 			const tcu::Interval	yClamped	(clampMaybe(y,   0.0, 1.0));
   1462 			const tcu::Interval	crClamped	(clampMaybe(cr, -0.5, 0.5));
   1463 			const tcu::Interval	cbClamped	(clampMaybe(cb, -0.5, 0.5));
   1464 
   1465 			output[0] = conversionFormat.roundOut(yClamped + conversionFormat.roundOut(1.402 * crClamped, false), false);
   1466 			output[1] = conversionFormat.roundOut(conversionFormat.roundOut(yClamped - conversionFormat.roundOut((0.202008 / 0.587) * cbClamped, false), false) - conversionFormat.roundOut((0.419198 / 0.587) * crClamped, false), false);
   1467 			output[2] = conversionFormat.roundOut(yClamped + conversionFormat.roundOut(1.772 * cbClamped, false), false);
   1468 			output[3] = input[3];
   1469 			break;
   1470 		}
   1471 
   1472 		case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709:
   1473 		{
   1474 			const tcu::Interval	y			(rangeExpandLuma(range, conversionFormat, bitDepth[1], input[1]));
   1475 			const tcu::Interval	cr			(rangeExpandChroma(range, conversionFormat, bitDepth[0], input[0]));
   1476 			const tcu::Interval	cb			(rangeExpandChroma(range, conversionFormat, bitDepth[2], input[2]));
   1477 
   1478 			const tcu::Interval	yClamped	(clampMaybe(y,   0.0, 1.0));
   1479 			const tcu::Interval	crClamped	(clampMaybe(cr, -0.5, 0.5));
   1480 			const tcu::Interval	cbClamped	(clampMaybe(cb, -0.5, 0.5));
   1481 
   1482 			output[0] = conversionFormat.roundOut(yClamped + conversionFormat.roundOut(1.5748 * crClamped, false), false);
   1483 			output[1] = conversionFormat.roundOut(conversionFormat.roundOut(yClamped - conversionFormat.roundOut((0.13397432 / 0.7152) * cbClamped, false), false) - conversionFormat.roundOut((0.33480248 / 0.7152) * crClamped, false), false);
   1484 			output[2] = conversionFormat.roundOut(yClamped + conversionFormat.roundOut(1.8556 * cbClamped, false), false);
   1485 			output[3] = input[3];
   1486 			break;
   1487 		}
   1488 
   1489 		case vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020:
   1490 		{
   1491 			const tcu::Interval	y			(rangeExpandLuma(range, conversionFormat, bitDepth[1], input[1]));
   1492 			const tcu::Interval	cr			(rangeExpandChroma(range, conversionFormat, bitDepth[0], input[0]));
   1493 			const tcu::Interval	cb			(rangeExpandChroma(range, conversionFormat, bitDepth[2], input[2]));
   1494 
   1495 			const tcu::Interval	yClamped	(clampMaybe(y,   0.0, 1.0));
   1496 			const tcu::Interval	crClamped	(clampMaybe(cr, -0.5, 0.5));
   1497 			const tcu::Interval	cbClamped	(clampMaybe(cb, -0.5, 0.5));
   1498 
   1499 			output[0] = conversionFormat.roundOut(yClamped + conversionFormat.roundOut(1.4746 * crClamped, false), false);
   1500 			output[1] = conversionFormat.roundOut(conversionFormat.roundOut(yClamped - conversionFormat.roundOut(conversionFormat.roundOut(0.11156702 / 0.6780, false) * cbClamped, false), false) - conversionFormat.roundOut(conversionFormat.roundOut(0.38737742 / 0.6780, false) * crClamped, false), false);
   1501 			output[2] = conversionFormat.roundOut(yClamped + conversionFormat.roundOut(1.8814 * cbClamped, false), false);
   1502 			output[3] = input[3];
   1503 			break;
   1504 		}
   1505 
   1506 		default:
   1507 			DE_FATAL("Unknown YCbCrModel");
   1508 	}
   1509 
   1510 	if (colorModel != vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY)
   1511 	{
   1512 		for (int ndx = 0; ndx < 3; ndx++)
   1513 			output[ndx] = clampMaybe(output[ndx], 0.0, 1.0);
   1514 	}
   1515 }
   1516 
   1517 int mirror (int coord)
   1518 {
   1519 	return coord >= 0 ? coord : -(1 + coord);
   1520 }
   1521 
   1522 int imod (int a, int b)
   1523 {
   1524 	int m = a % b;
   1525 	return m < 0 ? m + b : m;
   1526 }
   1527 
   1528 tcu::Interval frac (const tcu::Interval& x)
   1529 {
   1530 	if (x.hi() - x.lo() >= 1.0)
   1531 		return tcu::Interval(0.0, 1.0);
   1532 	else
   1533 	{
   1534 		const tcu::Interval ret (deFrac(x.lo()), deFrac(x.hi()));
   1535 
   1536 		return ret;
   1537 	}
   1538 }
   1539 
   1540 tcu::Interval calculateUV (const tcu::FloatFormat&	coordFormat,
   1541 						   const tcu::Interval&		st,
   1542 						   const int				size)
   1543 {
   1544 	return coordFormat.roundOut(coordFormat.roundOut(st, false) * tcu::Interval((double)size), false);
   1545 }
   1546 
   1547 tcu::IVec2 calculateNearestIJRange (const tcu::FloatFormat&	coordFormat,
   1548 								    const tcu::Interval&	uv)
   1549 {
   1550 	const tcu::Interval	ij	(coordFormat.roundOut(coordFormat.roundOut(uv, false) - tcu::Interval(0.5), false));
   1551 
   1552 	return tcu::IVec2(deRoundToInt32(ij.lo() - coordFormat.ulp(ij.lo(), 1)), deRoundToInt32(ij.hi() + coordFormat.ulp(ij.hi(), 1)));
   1553 }
   1554 
   1555 // Calculate range of pixel coordinates that can be used as lower coordinate for linear sampling
   1556 tcu::IVec2 calculateLinearIJRange (const tcu::FloatFormat&	coordFormat,
   1557 								   const tcu::Interval&		uv)
   1558 {
   1559 	const tcu::Interval	ij	(coordFormat.roundOut(uv - tcu::Interval(0.5), false));
   1560 
   1561 	return tcu::IVec2(deFloorToInt32(ij.lo()), deFloorToInt32(ij.hi()));
   1562 }
   1563 
   1564 tcu::Interval calculateAB (const deUint32		subTexelPrecisionBits,
   1565 						   const tcu::Interval&	uv,
   1566 						   int					ij)
   1567 {
   1568 	const deUint32		subdivisions	= 0x1u << subTexelPrecisionBits;
   1569 	const tcu::Interval	ab				(frac((uv - 0.5) & tcu::Interval((double)ij, (double)(ij + 1))));
   1570 	const tcu::Interval	gridAB			(ab * tcu::Interval(subdivisions));
   1571 	const tcu::Interval	rounded			(de::max(deFloor(gridAB.lo()) / subdivisions, 0.0) , de::min(deCeil(gridAB.hi()) / subdivisions, 1.0));
   1572 
   1573 	return rounded;
   1574 }
   1575 
   1576 tcu::Interval lookupWrapped (const ChannelAccess&		access,
   1577 							 const tcu::FloatFormat&	conversionFormat,
   1578 							 vk::VkSamplerAddressMode	addressModeU,
   1579 							 vk::VkSamplerAddressMode	addressModeV,
   1580 							 const tcu::IVec2&			coord)
   1581 {
   1582 	return access.getChannel(conversionFormat,
   1583 							 tcu::IVec3(wrap(addressModeU, coord.x(), access.getSize().x()), wrap(addressModeV, coord.y(), access.getSize().y()), 0));
   1584 }
   1585 
   1586 tcu::Interval linearInterpolate (const tcu::FloatFormat&	filteringFormat,
   1587 								 const tcu::Interval&		a,
   1588 								 const tcu::Interval&		b,
   1589 								 const tcu::Interval&		p00,
   1590 								 const tcu::Interval&		p10,
   1591 								 const tcu::Interval&		p01,
   1592 								 const tcu::Interval&		p11)
   1593 {
   1594 	const tcu::Interval	p[4] =
   1595 	{
   1596 		p00,
   1597 		p10,
   1598 		p01,
   1599 		p11
   1600 	};
   1601 	tcu::Interval		result	(0.0);
   1602 
   1603 	for (size_t ndx = 0; ndx < 4; ndx++)
   1604 	{
   1605 		const tcu::Interval	weightA	(filteringFormat.roundOut((ndx % 2) == 0 ? (1.0 - a) : a, false));
   1606 		const tcu::Interval	weightB	(filteringFormat.roundOut((ndx / 2) == 0 ? (1.0 - b) : b, false));
   1607 		const tcu::Interval	weight	(filteringFormat.roundOut(weightA * weightB, false));
   1608 
   1609 		result = filteringFormat.roundOut(result + filteringFormat.roundOut(p[ndx] * weight, false), false);
   1610 	}
   1611 
   1612 	return result;
   1613 }
   1614 
   1615 tcu::Interval calculateImplicitChromaUV (const tcu::FloatFormat&	coordFormat,
   1616 										 vk::VkChromaLocation		offset,
   1617 										 const tcu::Interval&		uv)
   1618 {
   1619 	if (offset == vk::VK_CHROMA_LOCATION_COSITED_EVEN)
   1620 		return coordFormat.roundOut(0.5 * coordFormat.roundOut(uv + 0.5, false), false);
   1621 	else
   1622 		return coordFormat.roundOut(0.5 * uv, false);
   1623 }
   1624 
   1625 tcu::Interval linearSample (const ChannelAccess&		access,
   1626 						    const tcu::FloatFormat&		conversionFormat,
   1627 						    const tcu::FloatFormat&		filteringFormat,
   1628 						    vk::VkSamplerAddressMode	addressModeU,
   1629 						    vk::VkSamplerAddressMode	addressModeV,
   1630 						    const tcu::IVec2&			coord,
   1631 						    const tcu::Interval&		a,
   1632 						    const tcu::Interval&		b)
   1633 {
   1634 	return linearInterpolate(filteringFormat, a, b,
   1635 									lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(0, 0)),
   1636 									lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(1, 0)),
   1637 									lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(0, 1)),
   1638 									lookupWrapped(access, conversionFormat, addressModeU, addressModeV, coord + tcu::IVec2(1, 1)));
   1639 }
   1640 
   1641 tcu::Interval reconstructLinearXChromaSample (const tcu::FloatFormat&	filteringFormat,
   1642 											  const tcu::FloatFormat&	conversionFormat,
   1643 											  vk::VkChromaLocation		offset,
   1644 											  vk::VkSamplerAddressMode	addressModeU,
   1645 											  vk::VkSamplerAddressMode	addressModeV,
   1646 											  const ChannelAccess&		access,
   1647 											  int						i,
   1648 											  int						j)
   1649 {
   1650 	const int subI	= divFloor(i, 2);
   1651 
   1652 	if (offset == vk::VK_CHROMA_LOCATION_COSITED_EVEN)
   1653 	{
   1654 		if (i % 2 == 0)
   1655 			return lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI, j));
   1656 		else
   1657 		{
   1658 			const tcu::Interval	a	(filteringFormat.roundOut(0.5 * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI, j)), false));
   1659 			const tcu::Interval	b	(filteringFormat.roundOut(0.5 * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI + 1, j)), false));
   1660 
   1661 			return filteringFormat.roundOut(a + b, false);
   1662 		}
   1663 	}
   1664 	else if (offset == vk::VK_CHROMA_LOCATION_MIDPOINT)
   1665 	{
   1666 		if (i % 2 == 0)
   1667 		{
   1668 			const tcu::Interval	a	(filteringFormat.roundOut(0.25 * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI - 1, j)), false));
   1669 			const tcu::Interval	b	(filteringFormat.roundOut(0.75 * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI, j)), false));
   1670 
   1671 			return filteringFormat.roundOut(a + b, false);
   1672 		}
   1673 		else
   1674 		{
   1675 			const tcu::Interval	a	(filteringFormat.roundOut(0.25 * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI + 1, j)), false));
   1676 			const tcu::Interval	b	(filteringFormat.roundOut(0.75 * lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI, j)), false));
   1677 
   1678 			return filteringFormat.roundOut(a + b, false);
   1679 		}
   1680 	}
   1681 	else
   1682 	{
   1683 		DE_FATAL("Unknown sample location");
   1684 		return tcu::Interval();
   1685 	}
   1686 }
   1687 
   1688 tcu::Interval reconstructLinearXYChromaSample (const tcu::FloatFormat&	filteringFormat,
   1689 										  const tcu::FloatFormat&		conversionFormat,
   1690 										  vk::VkChromaLocation			xOffset,
   1691 										  vk::VkChromaLocation			yOffset,
   1692 										  vk::VkSamplerAddressMode		addressModeU,
   1693 										  vk::VkSamplerAddressMode		addressModeV,
   1694 										  const ChannelAccess&			access,
   1695 										  int							i,
   1696 										  int							j)
   1697 {
   1698 	const int		subI	= xOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
   1699 							? divFloor(i, 2)
   1700 							: (i % 2 == 0 ? divFloor(i, 2) - 1 : divFloor(i, 2));
   1701 	const int		subJ	= yOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
   1702 							? divFloor(j, 2)
   1703 							: (j % 2 == 0 ? divFloor(j, 2) - 1 : divFloor(j, 2));
   1704 
   1705 	const double	a		= xOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
   1706 							? (i % 2 == 0 ? 0.0 : 0.5)
   1707 							: (i % 2 == 0 ? 0.25 : 0.75);
   1708 	const double	b		= yOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN
   1709 							? (j % 2 == 0 ? 0.0 : 0.5)
   1710 							: (j % 2 == 0 ? 0.25 : 0.75);
   1711 
   1712 	return linearInterpolate(filteringFormat, a, b,
   1713 								lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI, subJ)),
   1714 								lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI + 1, subJ)),
   1715 								lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI, subJ + 1)),
   1716 								lookupWrapped(access, conversionFormat, addressModeU, addressModeV, tcu::IVec2(subI + 1, subJ + 1)));
   1717 }
   1718 
   1719 const ChannelAccess& swizzle (vk::VkComponentSwizzle	swizzle,
   1720 							  const ChannelAccess&		identityPlane,
   1721 							  const ChannelAccess&		rPlane,
   1722 							  const ChannelAccess&		gPlane,
   1723 							  const ChannelAccess&		bPlane,
   1724 							  const ChannelAccess&		aPlane)
   1725 {
   1726 	switch (swizzle)
   1727 	{
   1728 		case vk::VK_COMPONENT_SWIZZLE_IDENTITY:	return identityPlane;
   1729 		case vk::VK_COMPONENT_SWIZZLE_R:		return rPlane;
   1730 		case vk::VK_COMPONENT_SWIZZLE_G:		return gPlane;
   1731 		case vk::VK_COMPONENT_SWIZZLE_B:		return bPlane;
   1732 		case vk::VK_COMPONENT_SWIZZLE_A:		return aPlane;
   1733 
   1734 		default:
   1735 			DE_FATAL("Unsupported swizzle");
   1736 			return identityPlane;
   1737 	}
   1738 }
   1739 
   1740 } // anonymous
   1741 
   1742 int wrap (vk::VkSamplerAddressMode	addressMode,
   1743 		  int						coord,
   1744 		  int						size)
   1745 {
   1746 	switch (addressMode)
   1747 	{
   1748 		case vk::VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
   1749 			return (size - 1) - mirror(imod(coord, 2 * size) - size);
   1750 
   1751 		case vk::VK_SAMPLER_ADDRESS_MODE_REPEAT:
   1752 			return imod(coord, size);
   1753 
   1754 		case vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
   1755 			return de::clamp(coord, 0, size - 1);
   1756 
   1757 		case vk::VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
   1758 			return de::clamp(mirror(coord), 0, size - 1);
   1759 
   1760 		default:
   1761 			DE_FATAL("Unknown wrap mode");
   1762 			return ~0;
   1763 	}
   1764 }
   1765 
   1766 int divFloor (int a, int b)
   1767 {
   1768 	if (a % b == 0)
   1769 		return a / b;
   1770 	else if (a > 0)
   1771 		return a / b;
   1772 	else
   1773 		return (a / b) - 1;
   1774 }
   1775 
   1776 void calculateBounds (const ChannelAccess&					rPlane,
   1777 					  const ChannelAccess&					gPlane,
   1778 					  const ChannelAccess&					bPlane,
   1779 					  const ChannelAccess&					aPlane,
   1780 					  const UVec4&							bitDepth,
   1781 					  const vector<Vec2>&					sts,
   1782 					  const FloatFormat&					filteringFormat,
   1783 					  const FloatFormat&					conversionFormat,
   1784 					  const deUint32						subTexelPrecisionBits,
   1785 					  vk::VkFilter							filter,
   1786 					  vk::VkSamplerYcbcrModelConversion		colorModel,
   1787 					  vk::VkSamplerYcbcrRange				range,
   1788 					  vk::VkFilter							chromaFilter,
   1789 					  vk::VkChromaLocation					xChromaOffset,
   1790 					  vk::VkChromaLocation					yChromaOffset,
   1791 					  const vk::VkComponentMapping&			componentMapping,
   1792 					  bool									explicitReconstruction,
   1793 					  vk::VkSamplerAddressMode				addressModeU,
   1794 					  vk::VkSamplerAddressMode				addressModeV,
   1795 					  std::vector<Vec4>&					minBounds,
   1796 					  std::vector<Vec4>&					maxBounds,
   1797 					  std::vector<Vec4>&					uvBounds,
   1798 					  std::vector<IVec4>&					ijBounds)
   1799 {
   1800 	const FloatFormat		highp			(-126, 127, 23, true,
   1801 											 tcu::MAYBE,	// subnormals
   1802 											 tcu::YES,		// infinities
   1803 											 tcu::MAYBE);	// NaN
   1804 	const FloatFormat		coordFormat		(-32, 32, 16, true);
   1805 	const ChannelAccess&	rAccess			(swizzle(componentMapping.r, rPlane, rPlane, gPlane, bPlane, aPlane));
   1806 	const ChannelAccess&	gAccess			(swizzle(componentMapping.g, gPlane, rPlane, gPlane, bPlane, aPlane));
   1807 	const ChannelAccess&	bAccess			(swizzle(componentMapping.b, bPlane, rPlane, gPlane, bPlane, aPlane));
   1808 	const ChannelAccess&	aAccess			(swizzle(componentMapping.a, aPlane, rPlane, gPlane, bPlane, aPlane));
   1809 
   1810 	const bool				subsampledX		= gAccess.getSize().x() > rAccess.getSize().x();
   1811 	const bool				subsampledY		= gAccess.getSize().y() > rAccess.getSize().y();
   1812 
   1813 	minBounds.resize(sts.size(), Vec4(TCU_INFINITY));
   1814 	maxBounds.resize(sts.size(), Vec4(-TCU_INFINITY));
   1815 
   1816 	uvBounds.resize(sts.size(), Vec4(TCU_INFINITY, -TCU_INFINITY, TCU_INFINITY, -TCU_INFINITY));
   1817 	ijBounds.resize(sts.size(), IVec4(0x7FFFFFFF, -1 -0x7FFFFFFF, 0x7FFFFFFF, -1 -0x7FFFFFFF));
   1818 
   1819 	// Chroma plane sizes must match
   1820 	DE_ASSERT(rAccess.getSize() == bAccess.getSize());
   1821 
   1822 	// Luma plane sizes must match
   1823 	DE_ASSERT(gAccess.getSize() == aAccess.getSize());
   1824 
   1825 	// Luma plane size must match chroma plane or be twice as big
   1826 	DE_ASSERT(rAccess.getSize().x() == gAccess.getSize().x() || 2 * rAccess.getSize().x() == gAccess.getSize().x());
   1827 	DE_ASSERT(rAccess.getSize().y() == gAccess.getSize().y() || 2 * rAccess.getSize().y() == gAccess.getSize().y());
   1828 
   1829 	for (size_t ndx = 0; ndx < sts.size(); ndx++)
   1830 	{
   1831 		const Vec2	st		(sts[ndx]);
   1832 		Interval	bounds[4];
   1833 
   1834 		const Interval	u	(calculateUV(coordFormat, st[0], gAccess.getSize().x()));
   1835 		const Interval	v	(calculateUV(coordFormat, st[1], gAccess.getSize().y()));
   1836 
   1837 		uvBounds[ndx][0] = (float)u.lo();
   1838 		uvBounds[ndx][1] = (float)u.hi();
   1839 
   1840 		uvBounds[ndx][2] = (float)v.lo();
   1841 		uvBounds[ndx][3] = (float)v.hi();
   1842 
   1843 		if (filter == vk::VK_FILTER_NEAREST)
   1844 		{
   1845 			const IVec2	iRange	(calculateNearestIJRange(coordFormat, u));
   1846 			const IVec2	jRange	(calculateNearestIJRange(coordFormat, v));
   1847 
   1848 			ijBounds[ndx][0] = iRange[0];
   1849 			ijBounds[ndx][1] = iRange[1];
   1850 
   1851 			ijBounds[ndx][2] = jRange[0];
   1852 			ijBounds[ndx][3] = jRange[1];
   1853 
   1854 			for (int j = jRange.x(); j <= jRange.y(); j++)
   1855 			for (int i = iRange.x(); i <= iRange.y(); i++)
   1856 			{
   1857 				const Interval	gValue	(lookupWrapped(gAccess, conversionFormat, addressModeU, addressModeV, IVec2(i, j)));
   1858 				const Interval	aValue	(lookupWrapped(aAccess, conversionFormat, addressModeU, addressModeV, IVec2(i, j)));
   1859 
   1860 				if (subsampledX || subsampledY)
   1861 				{
   1862 					if (explicitReconstruction)
   1863 					{
   1864 						if (chromaFilter == vk::VK_FILTER_NEAREST)
   1865 						{
   1866 							// Nearest, Reconstructed chroma with explicit nearest filtering
   1867 							const int		subI		= subsampledX ? i / 2 : i;
   1868 							const int		subJ		= subsampledY ? j / 2 : j;
   1869 							const Interval	srcColor[]	=
   1870 							{
   1871 								lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2(subI, subJ)),
   1872 								gValue,
   1873 								lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2(subI, subJ)),
   1874 								aValue
   1875 							};
   1876 							Interval		dstColor[4];
   1877 
   1878 							convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   1879 
   1880 							for (size_t compNdx = 0; compNdx < 4; compNdx++)
   1881 								bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   1882 						}
   1883 						else if (chromaFilter == vk::VK_FILTER_LINEAR)
   1884 						{
   1885 							if (subsampledX && subsampledY)
   1886 							{
   1887 								// Nearest, Reconstructed both chroma samples with explicit linear filtering
   1888 								const Interval	rValue	(reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i, j));
   1889 								const Interval	bValue	(reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i, j));
   1890 								const Interval	srcColor[]	=
   1891 								{
   1892 									rValue,
   1893 									gValue,
   1894 									bValue,
   1895 									aValue
   1896 								};
   1897 								Interval		dstColor[4];
   1898 
   1899 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   1900 
   1901 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   1902 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   1903 							}
   1904 							else if (subsampledX)
   1905 							{
   1906 								// Nearest, Reconstructed x chroma samples with explicit linear filtering
   1907 								const Interval	rValue	(reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, rAccess, i, j));
   1908 								const Interval	bValue	(reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, bAccess, i, j));
   1909 								const Interval	srcColor[]	=
   1910 								{
   1911 									rValue,
   1912 									gValue,
   1913 									bValue,
   1914 									aValue
   1915 								};
   1916 								Interval		dstColor[4];
   1917 
   1918 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   1919 
   1920 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   1921 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   1922 							}
   1923 							else
   1924 								DE_FATAL("Unexpected chroma reconstruction");
   1925 						}
   1926 						else
   1927 							DE_FATAL("Unknown filter");
   1928 					}
   1929 					else
   1930 					{
   1931 						const Interval	chromaU	(subsampledX ? calculateImplicitChromaUV(coordFormat, xChromaOffset, u) : u);
   1932 						const Interval	chromaV	(subsampledY ? calculateImplicitChromaUV(coordFormat, yChromaOffset, v) : v);
   1933 
   1934 						if (chromaFilter == vk::VK_FILTER_NEAREST)
   1935 						{
   1936 							// Nearest, reconstructed chroma samples with implicit nearest filtering
   1937 							const IVec2	chromaIRange	(subsampledX ? calculateNearestIJRange(coordFormat, chromaU) : IVec2(i, i));
   1938 							const IVec2	chromaJRange	(subsampledY ? calculateNearestIJRange(coordFormat, chromaV) : IVec2(j, j));
   1939 
   1940 							for (int chromaJ = chromaJRange.x(); chromaJ <= chromaJRange.y(); chromaJ++)
   1941 							for (int chromaI = chromaIRange.x(); chromaI <= chromaIRange.y(); chromaI++)
   1942 							{
   1943 								const Interval	srcColor[]	=
   1944 								{
   1945 									lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ)),
   1946 									gValue,
   1947 									lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ)),
   1948 									aValue
   1949 								};
   1950 								Interval		dstColor[4];
   1951 
   1952 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   1953 
   1954 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   1955 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   1956 							}
   1957 						}
   1958 						else if (chromaFilter == vk::VK_FILTER_LINEAR)
   1959 						{
   1960 							// Nearest, reconstructed chroma samples with implicit linear filtering
   1961 							const IVec2	chromaIRange	(subsampledX ? calculateLinearIJRange(coordFormat, chromaU) : IVec2(i, i));
   1962 							const IVec2	chromaJRange	(subsampledY ? calculateLinearIJRange(coordFormat, chromaV) : IVec2(j, j));
   1963 
   1964 							for (int chromaJ = chromaJRange.x(); chromaJ <= chromaJRange.y(); chromaJ++)
   1965 							for (int chromaI = chromaIRange.x(); chromaI <= chromaIRange.y(); chromaI++)
   1966 							{
   1967 								const Interval	chromaA	(calculateAB(subTexelPrecisionBits, chromaU, chromaI));
   1968 								const Interval	chromaB	(calculateAB(subTexelPrecisionBits, chromaV, chromaJ));
   1969 
   1970 								const Interval	srcColor[]	=
   1971 								{
   1972 									linearSample(rAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB),
   1973 									gValue,
   1974 									linearSample(bAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB),
   1975 									aValue
   1976 								};
   1977 								Interval		dstColor[4];
   1978 
   1979 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   1980 
   1981 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   1982 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   1983 							}
   1984 						}
   1985 						else
   1986 							DE_FATAL("Unknown filter");
   1987 					}
   1988 				}
   1989 				else
   1990 				{
   1991 					// Linear, no chroma subsampling
   1992 					const Interval	srcColor[]	=
   1993 					{
   1994 						lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2(i, j)),
   1995 						gValue,
   1996 						lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2(i, j)),
   1997 						aValue
   1998 					};
   1999 					Interval dstColor[4];
   2000 
   2001 					convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   2002 
   2003 					for (size_t compNdx = 0; compNdx < 4; compNdx++)
   2004 						bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   2005 				}
   2006 			}
   2007 		}
   2008 		else if (filter == vk::VK_FILTER_LINEAR)
   2009 		{
   2010 			const IVec2	iRange	(calculateLinearIJRange(coordFormat, u));
   2011 			const IVec2	jRange	(calculateLinearIJRange(coordFormat, v));
   2012 
   2013 			ijBounds[ndx][0] = iRange[0];
   2014 			ijBounds[ndx][1] = iRange[1];
   2015 
   2016 			ijBounds[ndx][2] = jRange[0];
   2017 			ijBounds[ndx][3] = jRange[1];
   2018 
   2019 			for (int j = jRange.x(); j <= jRange.y(); j++)
   2020 			for (int i = iRange.x(); i <= iRange.y(); i++)
   2021 			{
   2022 				const Interval	lumaA		(calculateAB(subTexelPrecisionBits, u, i));
   2023 				const Interval	lumaB		(calculateAB(subTexelPrecisionBits, v, j));
   2024 
   2025 				const Interval	gValue		(linearSample(gAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(i, j), lumaA, lumaB));
   2026 				const Interval	aValue		(linearSample(aAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(i, j), lumaA, lumaB));
   2027 
   2028 				if (subsampledX || subsampledY)
   2029 				{
   2030 					if (explicitReconstruction)
   2031 					{
   2032 						if (chromaFilter == vk::VK_FILTER_NEAREST)
   2033 						{
   2034 							const Interval	srcColor[]	=
   2035 							{
   2036 								linearInterpolate(filteringFormat, lumaA, lumaB,
   2037 																lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2(i       / (subsampledX ? 2 : 1), j       / (subsampledY ? 2 : 1))),
   2038 																lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), j       / (subsampledY ? 2 : 1))),
   2039 																lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2(i       / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1))),
   2040 																lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1)))),
   2041 								gValue,
   2042 								linearInterpolate(filteringFormat, lumaA, lumaB,
   2043 																lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2(i       / (subsampledX ? 2 : 1), j       / (subsampledY ? 2 : 1))),
   2044 																lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), j       / (subsampledY ? 2 : 1))),
   2045 																lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2(i       / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1))),
   2046 																lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2((i + 1) / (subsampledX ? 2 : 1), (j + 1) / (subsampledY ? 2 : 1)))),
   2047 								aValue
   2048 							};
   2049 							Interval		dstColor[4];
   2050 
   2051 							convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   2052 
   2053 							for (size_t compNdx = 0; compNdx < 4; compNdx++)
   2054 								bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   2055 						}
   2056 						else if (chromaFilter == vk::VK_FILTER_LINEAR)
   2057 						{
   2058 							if (subsampledX && subsampledY)
   2059 							{
   2060 								// Linear, Reconstructed xx chroma samples with explicit linear filtering
   2061 								const Interval	rValue	(linearInterpolate(filteringFormat, lumaA, lumaB,
   2062 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i, j),
   2063 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j),
   2064 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i , j + 1),
   2065 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j + 1)));
   2066 								const Interval	bValue	(linearInterpolate(filteringFormat, lumaA, lumaB,
   2067 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i, j),
   2068 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j),
   2069 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i , j + 1),
   2070 																			reconstructLinearXYChromaSample(filteringFormat, conversionFormat, xChromaOffset, yChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j + 1)));
   2071 								const Interval	srcColor[]	=
   2072 								{
   2073 									rValue,
   2074 									gValue,
   2075 									bValue,
   2076 									aValue
   2077 								};
   2078 								Interval		dstColor[4];
   2079 
   2080 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   2081 
   2082 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   2083 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   2084 
   2085 							}
   2086 							else if (subsampledX)
   2087 							{
   2088 								// Linear, Reconstructed x chroma samples with explicit linear filtering
   2089 								const Interval	rValue	(linearInterpolate(filteringFormat, lumaA, lumaB,
   2090 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, rAccess, i, j),
   2091 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j),
   2092 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, rAccess, i , j + 1),
   2093 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, rAccess, i + 1, j + 1)));
   2094 								const Interval	bValue	(linearInterpolate(filteringFormat, lumaA, lumaB,
   2095 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, bAccess, i, j),
   2096 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j),
   2097 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, bAccess, i , j + 1),
   2098 																			reconstructLinearXChromaSample(filteringFormat, conversionFormat, xChromaOffset, addressModeU, addressModeV, bAccess, i + 1, j + 1)));
   2099 								const Interval	srcColor[]	=
   2100 								{
   2101 									rValue,
   2102 									gValue,
   2103 									bValue,
   2104 									aValue
   2105 								};
   2106 								Interval		dstColor[4];
   2107 
   2108 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   2109 
   2110 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   2111 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   2112 							}
   2113 							else
   2114 								DE_FATAL("Unknown subsampling config");
   2115 						}
   2116 						else
   2117 							DE_FATAL("Unknown chroma filter");
   2118 					}
   2119 					else
   2120 					{
   2121 						const Interval	chromaU	(subsampledX ? calculateImplicitChromaUV(coordFormat, xChromaOffset, u) : u);
   2122 						const Interval	chromaV	(subsampledY ? calculateImplicitChromaUV(coordFormat, yChromaOffset, v) : v);
   2123 
   2124 						if (chromaFilter == vk::VK_FILTER_NEAREST)
   2125 						{
   2126 							const IVec2	chromaIRange	(calculateNearestIJRange(coordFormat, chromaU));
   2127 							const IVec2	chromaJRange	(calculateNearestIJRange(coordFormat, chromaV));
   2128 
   2129 							for (int chromaJ = chromaJRange.x(); chromaJ <= chromaJRange.y(); chromaJ++)
   2130 							for (int chromaI = chromaIRange.x(); chromaI <= chromaIRange.y(); chromaI++)
   2131 							{
   2132 								const Interval	srcColor[]	=
   2133 								{
   2134 									lookupWrapped(rAccess, conversionFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ)),
   2135 									gValue,
   2136 									lookupWrapped(bAccess, conversionFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ)),
   2137 									aValue
   2138 								};
   2139 								Interval	dstColor[4];
   2140 
   2141 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   2142 
   2143 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   2144 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   2145 							}
   2146 						}
   2147 						else if (chromaFilter == vk::VK_FILTER_LINEAR)
   2148 						{
   2149 							const IVec2	chromaIRange	(calculateNearestIJRange(coordFormat, chromaU));
   2150 							const IVec2	chromaJRange	(calculateNearestIJRange(coordFormat, chromaV));
   2151 
   2152 							for (int chromaJ = chromaJRange.x(); chromaJ <= chromaJRange.y(); chromaJ++)
   2153 							for (int chromaI = chromaIRange.x(); chromaI <= chromaIRange.y(); chromaI++)
   2154 							{
   2155 								const Interval	chromaA		(calculateAB(subTexelPrecisionBits, chromaU, chromaI));
   2156 								const Interval	chromaB		(calculateAB(subTexelPrecisionBits, chromaV, chromaJ));
   2157 
   2158 								const Interval	rValue		(linearSample(rAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB));
   2159 								const Interval	bValue		(linearSample(bAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(chromaI, chromaJ), chromaA, chromaB));
   2160 
   2161 								const Interval	srcColor[]	=
   2162 								{
   2163 									rValue,
   2164 									gValue,
   2165 									bValue,
   2166 									aValue
   2167 								};
   2168 								Interval		dstColor[4];
   2169 								convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   2170 
   2171 								for (size_t compNdx = 0; compNdx < 4; compNdx++)
   2172 									bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   2173 							}
   2174 						}
   2175 						else
   2176 							DE_FATAL("Unknown chroma filter");
   2177 					}
   2178 				}
   2179 				else
   2180 				{
   2181 					const Interval	chromaA		(lumaA);
   2182 					const Interval	chromaB		(lumaB);
   2183 					const Interval	rValue		(linearSample(rAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(i, j), chromaA, chromaB));
   2184 					const Interval	bValue		(linearSample(bAccess, conversionFormat, filteringFormat, addressModeU, addressModeV, IVec2(i, j), chromaA, chromaB));
   2185 					const Interval	srcColor[]	=
   2186 					{
   2187 						rValue,
   2188 						gValue,
   2189 						bValue,
   2190 						aValue
   2191 					};
   2192 					Interval dstColor[4];
   2193 
   2194 					convertColor(colorModel, range, conversionFormat, bitDepth, srcColor, dstColor);
   2195 
   2196 					for (size_t compNdx = 0; compNdx < 4; compNdx++)
   2197 						bounds[compNdx] |= highp.roundOut(dstColor[compNdx], false);
   2198 				}
   2199 			}
   2200 		}
   2201 		else
   2202 			DE_FATAL("Unknown filter");
   2203 
   2204 		minBounds[ndx] = Vec4((float)bounds[0].lo(), (float)bounds[1].lo(), (float)bounds[2].lo(), (float)bounds[3].lo());
   2205 		maxBounds[ndx] = Vec4((float)bounds[0].hi(), (float)bounds[1].hi(), (float)bounds[2].hi(), (float)bounds[3].hi());
   2206 	}
   2207 }
   2208 
   2209 
   2210 } // ycbcr
   2211 } // vkt
   2212