• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2020 The Khronos Group 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 filtering tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuVectorUtil.hpp"
25 #include "tcuTexVerifierUtil.hpp"
26 #include "tcuImageCompare.hpp"
27 #include "vkImageUtil.hpp"
28 #include "vkMemUtil.hpp"
29 #include "vkPrograms.hpp"
30 #include "vkRefUtil.hpp"
31 #include "vkCmdUtil.hpp"
32 #include "vkBarrierUtil.hpp"
33 #include "vktTestCase.hpp"
34 #include "vktTestGroupUtil.hpp"
35 #include "vktYCbCrFilteringTests.hpp"
36 #include "vktDrawUtil.hpp"
37 #include "vktYCbCrUtil.hpp"
38 #include "gluTextureTestUtil.hpp"
39 #include <string>
40 #include <vector>
41 
42 using namespace vk;
43 using namespace vkt::drawutil;
44 
45 namespace vkt
46 {
47 namespace ycbcr
48 {
49 namespace
50 {
51 
52 using std::vector;
53 using std::string;
54 using tcu::TestLog;
55 using tcu::Sampler;
56 using namespace glu::TextureTestUtil;
57 
58 class LinearFilteringTestInstance: public TestInstance
59 {
60 public:
61 	LinearFilteringTestInstance(Context& context, VkFormat format, VkFilter chromaFiltering);
62 	~LinearFilteringTestInstance() = default;
63 
64 protected:
65 
66 	VkSamplerCreateInfo				getSamplerInfo				(const VkSamplerYcbcrConversionInfo*	samplerConversionInfo);
67 	Move<VkDescriptorSetLayout>		createDescriptorSetLayout	(VkSampler sampler);
68 	Move<VkDescriptorPool>			createDescriptorPool		(const deUint32 combinedSamplerDescriptorCount);
69 	Move<VkDescriptorSet>			createDescriptorSet			(VkDescriptorPool		descPool,
70 																 VkDescriptorSetLayout	descLayout);
71 	Move<VkSamplerYcbcrConversion>	createYCbCrConversion		(void);
72 	Move<VkImage>					createImage					(deUint32 width, deUint32 height);
73 	Move<VkImageView>				createImageView				(const VkSamplerYcbcrConversionInfo& samplerConversionInfo, VkImage image);
74 	void							bindImage					(VkDescriptorSet		descriptorSet,
75 																 VkImageView			imageView,
76 																 VkSampler				sampler);
77 	tcu::TestStatus					iterate						(void);
78 
79 private:
80 
81 	struct FilterCase
82 	{
83 		const tcu::UVec2 imageSize;
84 		const tcu::UVec2 renderSize;
85 	};
86 
87 	const VkFormat				m_format;
88 	const VkFilter				m_chromaFiltering;
89 	const DeviceInterface&		m_vkd;
90 	const VkDevice				m_device;
91 	int							m_caseIndex;
92 	const vector<FilterCase>	m_cases;
93 };
94 
LinearFilteringTestInstance(Context & context,VkFormat format,VkFilter chromaFiltering)95 LinearFilteringTestInstance::LinearFilteringTestInstance(Context& context, VkFormat format, VkFilter chromaFiltering)
96 	: TestInstance		(context)
97 	, m_format			(format)
98 	, m_chromaFiltering	(chromaFiltering)
99 	, m_vkd				(m_context.getDeviceInterface())
100 	, m_device			(m_context.getDevice())
101 	, m_caseIndex		(0)
102 	, m_cases			{
103 		{ { 8,  8}, {64, 64} },
104 		{ {64, 32}, {32, 64} }
105 	}
106 {
107 }
108 
getSamplerInfo(const VkSamplerYcbcrConversionInfo * samplerConversionInfo)109 VkSamplerCreateInfo LinearFilteringTestInstance::getSamplerInfo(const VkSamplerYcbcrConversionInfo* samplerConversionInfo)
110 {
111 	return
112 	{
113 		VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
114 		samplerConversionInfo,
115 		0u,
116 		VK_FILTER_LINEAR,							// magFilter
117 		VK_FILTER_LINEAR,							// minFilter
118 		VK_SAMPLER_MIPMAP_MODE_NEAREST,				// mipmapMode
119 		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,		// addressModeU
120 		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,		// addressModeV
121 		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,		// addressModeW
122 		0.0f,										// mipLodBias
123 		VK_FALSE,									// anisotropyEnable
124 		1.0f,										// maxAnisotropy
125 		VK_FALSE,									// compareEnable
126 		VK_COMPARE_OP_ALWAYS,						// compareOp
127 		0.0f,										// minLod
128 		0.0f,										// maxLod
129 		VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,	// borderColor
130 		VK_FALSE,									// unnormalizedCoords
131 	};
132 }
133 
createDescriptorSetLayout(VkSampler sampler)134 Move<VkDescriptorSetLayout> LinearFilteringTestInstance::createDescriptorSetLayout(VkSampler sampler)
135 {
136 	const VkDescriptorSetLayoutBinding binding =
137 	{
138 		0u,												// binding
139 		VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
140 		1u,												// descriptorCount
141 		VK_SHADER_STAGE_ALL,
142 		&sampler
143 	};
144 	const VkDescriptorSetLayoutCreateInfo layoutInfo =
145 	{
146 		VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
147 		DE_NULL,
148 		(VkDescriptorSetLayoutCreateFlags)0u,
149 		1u,
150 		&binding,
151 	};
152 
153 	return ::createDescriptorSetLayout(m_vkd, m_device, &layoutInfo);
154 }
155 
createDescriptorPool(const deUint32 combinedSamplerDescriptorCount)156 Move<VkDescriptorPool> LinearFilteringTestInstance::createDescriptorPool(const deUint32 combinedSamplerDescriptorCount)
157 {
158 	const VkDescriptorPoolSize poolSizes[] =
159 	{
160 		{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,	combinedSamplerDescriptorCount	},
161 	};
162 	const VkDescriptorPoolCreateInfo poolInfo =
163 	{
164 		VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
165 		DE_NULL,
166 		(VkDescriptorPoolCreateFlags)VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
167 		1u,		// maxSets
168 		DE_LENGTH_OF_ARRAY(poolSizes),
169 		poolSizes,
170 	};
171 
172 	return ::createDescriptorPool(m_vkd, m_device, &poolInfo);
173 }
174 
createDescriptorSet(VkDescriptorPool descPool,VkDescriptorSetLayout descLayout)175 Move<VkDescriptorSet> LinearFilteringTestInstance::createDescriptorSet(VkDescriptorPool			descPool,
176 																	   VkDescriptorSetLayout	descLayout)
177 {
178 	const VkDescriptorSetAllocateInfo allocInfo =
179 	{
180 		VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
181 		DE_NULL,
182 		descPool,
183 		1u,
184 		&descLayout,
185 	};
186 
187 	return allocateDescriptorSet(m_vkd, m_device, &allocInfo);
188 }
189 
createYCbCrConversion()190 Move<VkSamplerYcbcrConversion> LinearFilteringTestInstance::createYCbCrConversion()
191 {
192 	const VkSamplerYcbcrConversionCreateInfo conversionInfo =
193 	{
194 		VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
195 		DE_NULL,
196 		m_format,
197 		VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY,
198 		VK_SAMPLER_YCBCR_RANGE_ITU_FULL,
199 		{
200 			VK_COMPONENT_SWIZZLE_IDENTITY,
201 			VK_COMPONENT_SWIZZLE_IDENTITY,
202 			VK_COMPONENT_SWIZZLE_IDENTITY,
203 			VK_COMPONENT_SWIZZLE_IDENTITY,
204 		},
205 		VK_CHROMA_LOCATION_MIDPOINT,
206 		VK_CHROMA_LOCATION_MIDPOINT,
207 		m_chromaFiltering,							// chromaFilter
208 		VK_FALSE,									// forceExplicitReconstruction
209 	};
210 
211 	return createSamplerYcbcrConversion(m_vkd, m_device, &conversionInfo);
212 }
213 
createImage(deUint32 width,deUint32 height)214 Move<VkImage> LinearFilteringTestInstance::createImage(deUint32 width, deUint32 height)
215 {
216 	VkImageCreateFlags			createFlags = 0u;
217 	const VkImageCreateInfo		createInfo =
218 	{
219 		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
220 		DE_NULL,
221 		createFlags,
222 		VK_IMAGE_TYPE_2D,
223 		m_format,
224 		makeExtent3D(width, height, 1u),
225 		1u,		// mipLevels
226 		1u,		// arrayLayers
227 		VK_SAMPLE_COUNT_1_BIT,
228 		VK_IMAGE_TILING_OPTIMAL,
229 		VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
230 		VK_SHARING_MODE_EXCLUSIVE,
231 		0u,
232 		(const deUint32*)DE_NULL,
233 		VK_IMAGE_LAYOUT_UNDEFINED,
234 	};
235 
236 	return ::createImage(m_vkd, m_device, &createInfo);
237 }
238 
createImageView(const VkSamplerYcbcrConversionInfo & samplerConversionInfo,VkImage image)239 Move<VkImageView> LinearFilteringTestInstance::createImageView(const VkSamplerYcbcrConversionInfo& samplerConversionInfo, VkImage image)
240 {
241 	const VkImageViewCreateInfo	viewInfo =
242 	{
243 		VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
244 		&samplerConversionInfo,
245 		(VkImageViewCreateFlags)0,
246 		image,
247 		VK_IMAGE_VIEW_TYPE_2D,
248 		m_format,
249 		{
250 			VK_COMPONENT_SWIZZLE_IDENTITY,
251 			VK_COMPONENT_SWIZZLE_IDENTITY,
252 			VK_COMPONENT_SWIZZLE_IDENTITY,
253 			VK_COMPONENT_SWIZZLE_IDENTITY,
254 		},
255 		{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u },
256 	};
257 
258 	return ::createImageView(m_vkd, m_device, &viewInfo);
259 }
260 
bindImage(VkDescriptorSet descriptorSet,VkImageView imageView,VkSampler sampler)261 void LinearFilteringTestInstance::bindImage(VkDescriptorSet	descriptorSet,
262 											VkImageView		imageView,
263 											VkSampler		sampler)
264 {
265 	const VkDescriptorImageInfo imageInfo =
266 	{
267 		sampler,
268 		imageView,
269 		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
270 	};
271 	const VkWriteDescriptorSet descriptorWrite =
272 	{
273 		VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
274 		DE_NULL,
275 		descriptorSet,
276 		0u,		// dstBinding
277 		0u,		// dstArrayElement
278 		1u,		// descriptorCount
279 		VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
280 		&imageInfo,
281 		(const VkDescriptorBufferInfo*)DE_NULL,
282 		(const VkBufferView*)DE_NULL,
283 	};
284 
285 	m_vkd.updateDescriptorSets(m_device, 1u, &descriptorWrite, 0u, DE_NULL);
286 }
287 
iterate(void)288 tcu::TestStatus LinearFilteringTestInstance::iterate(void)
289 {
290 	const tcu::UVec2						imageSize			(m_cases[m_caseIndex].imageSize);
291 	const tcu::UVec2						renderSize			(m_cases[m_caseIndex].renderSize);
292 	const auto&								instInt				(m_context.getInstanceInterface());
293 	auto									physicalDevice		(m_context.getPhysicalDevice());
294 	const Unique<VkSamplerYcbcrConversion>	conversion			(createYCbCrConversion());
295 	const VkSamplerYcbcrConversionInfo		samplerConvInfo		{ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, DE_NULL, *conversion };
296 	const VkSamplerCreateInfo				samplerCreateInfo	(getSamplerInfo(&samplerConvInfo));
297 	const Unique<VkSampler>					sampler				(createSampler(m_vkd, m_device, &samplerCreateInfo));
298 
299 	deUint32								combinedSamplerDescriptorCount = 1;
300 	{
301 		const VkPhysicalDeviceImageFormatInfo2			imageFormatInfo				=
302 		{
303 			VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,	// sType
304 			DE_NULL,												// pNext
305 			m_format,												// format
306 			VK_IMAGE_TYPE_2D,										// type
307 			VK_IMAGE_TILING_OPTIMAL,								// tiling
308 			VK_IMAGE_USAGE_TRANSFER_DST_BIT |
309 			VK_IMAGE_USAGE_SAMPLED_BIT,								// usage
310 			(VkImageCreateFlags)0u									// flags
311 		};
312 
313 		VkSamplerYcbcrConversionImageFormatProperties	samplerYcbcrConversionImage = {};
314 		samplerYcbcrConversionImage.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES;
315 		samplerYcbcrConversionImage.pNext = DE_NULL;
316 
317 		VkImageFormatProperties2						imageFormatProperties		= {};
318 		imageFormatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
319 		imageFormatProperties.pNext = &samplerYcbcrConversionImage;
320 
321 		VK_CHECK(instInt.getPhysicalDeviceImageFormatProperties2(physicalDevice, &imageFormatInfo, &imageFormatProperties));
322 		combinedSamplerDescriptorCount = samplerYcbcrConversionImage.combinedImageSamplerDescriptorCount;
323 	}
324 
325 	const Unique<VkDescriptorSetLayout>		descLayout			(createDescriptorSetLayout(*sampler));
326 	const Unique<VkDescriptorPool>			descPool			(createDescriptorPool(combinedSamplerDescriptorCount));
327 	const Unique<VkDescriptorSet>			descSet				(createDescriptorSet(*descPool, *descLayout));
328 	const Unique<VkImage>					testImage			(createImage(imageSize.x(), imageSize.y()));
329 	const vector<AllocationSp>				allocations			(allocateAndBindImageMemory(m_vkd, m_device, m_context.getDefaultAllocator(), *testImage, m_format, 0u));
330 	const Unique<VkImageView>				imageView			(createImageView(samplerConvInfo, *testImage));
331 
332 	// create and bind image with test data
333 	MultiPlaneImageData imageData(m_format, imageSize);
334 	fillGradient(&imageData, tcu::Vec4(0.0f), tcu::Vec4(1.0f));
335 	uploadImage(m_vkd,
336 				m_device,
337 				m_context.getUniversalQueueFamilyIndex(),
338 				m_context.getDefaultAllocator(),
339 				*testImage,
340 				imageData,
341 				(VkAccessFlags)VK_ACCESS_SHADER_READ_BIT,
342 				VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
343 				0);
344 	bindImage(*descSet, *imageView, *sampler);
345 
346 	const vector<tcu::Vec4> vertices =
347 	{
348 		{ -1.0f, -1.0f, 0.0f, 1.0f },
349 		{ +1.0f, -1.0f, 0.0f, 1.0f },
350 		{ -1.0f, +1.0f, 0.0f, 1.0f },
351 		{ +1.0f, +1.0f, 0.0f, 1.0f }
352 	};
353 	VulkanProgram program({
354 		VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,	m_context.getBinaryCollection().get("vert")),
355 		VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,	m_context.getBinaryCollection().get("frag"))
356 	});
357 	program.descriptorSet		= *descSet;
358 	program.descriptorSetLayout = *descLayout;
359 
360 	PipelineState		pipelineState		(m_context.getDeviceProperties().limits.subPixelPrecisionBits);
361 	const DrawCallData	drawCallData		(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, vertices);
362 	FrameBufferState	frameBufferState	(renderSize.x(), renderSize.y());
363 	VulkanDrawContext	renderer			(m_context, frameBufferState);
364 
365 	// render full screen quad
366 	renderer.registerDrawObject(pipelineState, program, drawCallData);
367 	renderer.draw();
368 
369 	// get rendered image
370 	tcu::ConstPixelBufferAccess resImage(renderer.getColorPixels());
371 
372 	// construct ChannelAccess objects required to create reference results
373 	const vk::PlanarFormatDescription	planeInfo				= imageData.getDescription();
374 	deUint32							nullAccessData			(0u);
375 	ChannelAccess						nullAccess				(tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT, 1u, tcu::IVec3(imageSize.x(), imageSize.y(), 1), tcu::IVec3(0, 0, 0), &nullAccessData, 0u);
376 	deUint32							nullAccessAlphaData		(~0u);
377 	ChannelAccess						nullAccessAlpha			(tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT, 1u, tcu::IVec3(imageSize.x(), imageSize.y(), 1), tcu::IVec3(0, 0, 0), &nullAccessAlphaData, 0u);
378 	ChannelAccess						rChannelAccess			(planeInfo.hasChannelNdx(0) ? getChannelAccess(imageData, planeInfo, imageSize, 0) : nullAccess);
379 	ChannelAccess						gChannelAccess			(planeInfo.hasChannelNdx(1) ? getChannelAccess(imageData, planeInfo, imageSize, 1) : nullAccess);
380 	ChannelAccess						bChannelAccess			(planeInfo.hasChannelNdx(2) ? getChannelAccess(imageData, planeInfo, imageSize, 2) : nullAccess);
381 	ChannelAccess						aChannelAccess			(planeInfo.hasChannelNdx(3) ? getChannelAccess(imageData, planeInfo, imageSize, 3) : nullAccessAlpha);
382 	const VkFormatProperties			formatProperties		(getPhysicalDeviceFormatProperties(instInt, physicalDevice, m_format));
383 	const VkFormatFeatureFlags			featureFlags			(formatProperties.optimalTilingFeatures);
384 	const bool							explicitReconstruction	(featureFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT);
385 
386 	// calulate texture coordinates used by fragment shader
387 	vector<tcu::Vec2>					sts;
388 	for (deUint32 y = 0; y < renderSize.y(); y++)
389 	for (deUint32 x = 0; x < renderSize.x(); x++)
390 	{
391 		const float s = ((float)x + 0.5f) / (float)renderSize.x();
392 		const float t = ((float)y + 0.5f) / (float)renderSize.y();
393 
394 		sts.push_back(tcu::Vec2(s, t));
395 	}
396 
397 	// calculate minimum and maximum values between which the results should be placed
398 	const tcu::UVec4					bitDepth				(getYCbCrBitDepth(m_format));
399 	const std::vector<tcu::FloatFormat>	filteringPrecision		(getPrecision(m_format));
400 	const std::vector<tcu::FloatFormat>	conversionPrecision		(getPrecision(m_format));
401 	const deUint32						subTexelPrecisionBits	(vk::getPhysicalDeviceProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()).limits.subTexelPrecisionBits);
402 	const vk::VkComponentMapping		componentMapping		= { vk::VK_COMPONENT_SWIZZLE_IDENTITY, vk::VK_COMPONENT_SWIZZLE_IDENTITY, vk::VK_COMPONENT_SWIZZLE_IDENTITY, vk::VK_COMPONENT_SWIZZLE_IDENTITY };
403 
404 	std::vector<tcu::Vec4>				minBound;
405 	std::vector<tcu::Vec4>				maxBound;
406 	std::vector<tcu::Vec4>				uvBound;
407 	std::vector<tcu::IVec4>				ijBound;
408 	calculateBounds(rChannelAccess, gChannelAccess, bChannelAccess, aChannelAccess, bitDepth, sts, filteringPrecision, conversionPrecision, subTexelPrecisionBits, VK_FILTER_LINEAR, VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY, VK_SAMPLER_YCBCR_RANGE_ITU_FULL, m_chromaFiltering, VK_CHROMA_LOCATION_MIDPOINT, VK_CHROMA_LOCATION_MIDPOINT, componentMapping, explicitReconstruction, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, minBound, maxBound, uvBound, ijBound);
409 
410 	// log result and reference images
411 	TestLog&							log						(m_context.getTestContext().getLog());
412 	{
413 		const tcu::Vec4					scale					(1.0f);
414 		const tcu::Vec4					bias					(0.0f);
415 		vector<deUint8>					minData					(renderSize.x() * renderSize.y() * sizeof(tcu::Vec4), 255);
416 		vector<deUint8>					maxData					(renderSize.x() * renderSize.y() * sizeof(tcu::Vec4), 255);
417 		tcu::TextureFormat				refFormat				(vk::mapVkFormat(frameBufferState.colorFormat));
418 		tcu::PixelBufferAccess			minImage				(refFormat, renderSize.x(), renderSize.y(), 1, minData.data());
419 		tcu::PixelBufferAccess			maxImage				(refFormat, renderSize.x(), renderSize.y(), 1, maxData.data());
420 		{
421 			deUint32					ndx						= 0;
422 			for (deUint32 y = 0; y < renderSize.y(); y++)
423 			for (deUint32 x = 0; x < renderSize.x(); x++)
424 			{
425 				minImage.setPixel(minBound[ndx], x, y);
426 				maxImage.setPixel(maxBound[ndx], x, y);
427 				ndx++;
428 			}
429 		}
430 
431 		log << TestLog::Image("MinBoundImage", "MinBoundImage", minImage, scale, bias);
432 		log << TestLog::Image("MaxBoundImage", "MaxBoundImage", maxImage, scale, bias);
433 		log << TestLog::Image("ResImage", "ResImage", resImage, scale, bias);
434 	}
435 
436 	bool								isOk					= true;
437 	{
438 		deUint32						ndx						= 0;
439 		VkFilter						textureFilter			= VK_FILTER_LINEAR;
440 		size_t							errorCount				= 0;
441 
442 		for (deUint32 y = 0; y < renderSize.y(); y++)
443 		for (deUint32 x = 0; x < renderSize.x(); x++)
444 		{
445 			tcu::Vec4 resValue = resImage.getPixel(x, y);
446 			bool fail = tcu::boolAny(tcu::lessThan(resValue, minBound[ndx])) || tcu::boolAny(tcu::greaterThan(resValue, maxBound[ndx]));
447 
448 			if (fail)
449 			{
450 				log << TestLog::Message << "Fail: " << sts[ndx] << " " << resValue << TestLog::EndMessage;
451 				log << TestLog::Message << "  Min : " << minBound[ndx] << TestLog::EndMessage;
452 				log << TestLog::Message << "  Max : " << maxBound[ndx] << TestLog::EndMessage;
453 				log << TestLog::Message << "  Threshold: " << (maxBound[ndx] - minBound[ndx]) << TestLog::EndMessage;
454 				log << TestLog::Message << "  UMin : " << uvBound[ndx][0] << TestLog::EndMessage;
455 				log << TestLog::Message << "  UMax : " << uvBound[ndx][1] << TestLog::EndMessage;
456 				log << TestLog::Message << "  VMin : " << uvBound[ndx][2] << TestLog::EndMessage;
457 				log << TestLog::Message << "  VMax : " << uvBound[ndx][3] << TestLog::EndMessage;
458 				log << TestLog::Message << "  IMin : " << ijBound[ndx][0] << TestLog::EndMessage;
459 				log << TestLog::Message << "  IMax : " << ijBound[ndx][1] << TestLog::EndMessage;
460 				log << TestLog::Message << "  JMin : " << ijBound[ndx][2] << TestLog::EndMessage;
461 				log << TestLog::Message << "  JMax : " << ijBound[ndx][3] << TestLog::EndMessage;
462 
463 				if (isXChromaSubsampled(m_format))
464 				{
465 					log << TestLog::Message << "  LumaAlphaValues : " << TestLog::EndMessage;
466 					log << TestLog::Message << "    Offset : (" << ijBound[ndx][0] << ", " << ijBound[ndx][2] << ")" << TestLog::EndMessage;
467 
468 					for (deInt32 k = ijBound[ndx][2]; k <= ijBound[ndx][3] + (textureFilter == vk::VK_FILTER_LINEAR ? 1 : 0); k++)
469 					{
470 						const deInt32		wrappedK = wrap(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, k, gChannelAccess.getSize().y());
471 						bool				first = true;
472 						std::ostringstream	line;
473 
474 						for (deInt32 j = ijBound[ndx][0]; j <= ijBound[ndx][1] + (textureFilter == vk::VK_FILTER_LINEAR ? 1 : 0); j++)
475 						{
476 							const deInt32	wrappedJ = wrap(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, j, gChannelAccess.getSize().x());
477 
478 							if (!first)
479 							{
480 								line << ", ";
481 								first = false;
482 							}
483 
484 							line << "(" << std::setfill(' ') << std::setw(5) << gChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0))
485 								<< ", " << std::setfill(' ') << std::setw(5) << aChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0)) << ")";
486 						}
487 						log << TestLog::Message << "    " << line.str() << TestLog::EndMessage;
488 					}
489 
490 					{
491 						const tcu::IVec2 chromaJRange(divFloor(ijBound[ndx][0], 2) - 1, divFloor(ijBound[ndx][1] + (textureFilter == vk::VK_FILTER_LINEAR ? 1 : 0), 2) + 1);
492 						const tcu::IVec2 chromaKRange(isYChromaSubsampled(m_format)
493 							? tcu::IVec2(divFloor(ijBound[ndx][2], 2) - 1, divFloor(ijBound[ndx][3] + (textureFilter == vk::VK_FILTER_LINEAR ? 1 : 0), 2) + 1)
494 							: tcu::IVec2(ijBound[ndx][2], ijBound[ndx][3] + (textureFilter == vk::VK_FILTER_LINEAR ? 1 : 0)));
495 
496 						log << TestLog::Message << "  ChromaValues : " << TestLog::EndMessage;
497 						log << TestLog::Message << "    Offset : (" << chromaJRange[0] << ", " << chromaKRange[0] << ")" << TestLog::EndMessage;
498 
499 						for (deInt32 k = chromaKRange[0]; k <= chromaKRange[1]; k++)
500 						{
501 							const deInt32		wrappedK = wrap(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, k, rChannelAccess.getSize().y());
502 							bool				first = true;
503 							std::ostringstream	line;
504 
505 							for (deInt32 j = chromaJRange[0]; j <= chromaJRange[1]; j++)
506 							{
507 								const deInt32	wrappedJ = wrap(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, j, rChannelAccess.getSize().x());
508 
509 								if (!first)
510 								{
511 									line << ", ";
512 									first = false;
513 								}
514 
515 								line << "(" << std::setfill(' ') << std::setw(5) << rChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0))
516 									<< ", " << std::setfill(' ') << std::setw(5) << bChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0)) << ")";
517 							}
518 							log << TestLog::Message << "    " << line.str() << TestLog::EndMessage;
519 						}
520 					}
521 				}
522 				else
523 				{
524 					log << TestLog::Message << "  Values : " << TestLog::EndMessage;
525 					log << TestLog::Message << "    Offset : (" << ijBound[ndx][0] << ", " << ijBound[ndx][2] << ")" << TestLog::EndMessage;
526 
527 					for (deInt32 k = ijBound[ndx][2]; k <= ijBound[ndx][3] + (textureFilter == vk::VK_FILTER_LINEAR ? 1 : 0); k++)
528 					{
529 						const deInt32		wrappedK = wrap(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, k, rChannelAccess.getSize().y());
530 						bool				first = true;
531 						std::ostringstream	line;
532 
533 						for (deInt32 j = ijBound[ndx][0]; j <= ijBound[ndx][1] + (textureFilter == vk::VK_FILTER_LINEAR ? 1 : 0); j++)
534 						{
535 							const deInt32	wrappedJ = wrap(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, j, rChannelAccess.getSize().x());
536 
537 							if (!first)
538 							{
539 								line << ", ";
540 								first = false;
541 							}
542 
543 							line << "(" << std::setfill(' ') << std::setw(5) << rChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0))
544 								<< ", " << std::setfill(' ') << std::setw(5) << gChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0))
545 								<< ", " << std::setfill(' ') << std::setw(5) << bChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0))
546 								<< ", " << std::setfill(' ') << std::setw(5) << aChannelAccess.getChannelUint(tcu::IVec3(wrappedJ, wrappedK, 0)) << ")";
547 						}
548 						log << TestLog::Message << "    " << line.str() << TestLog::EndMessage;
549 					}
550 				}
551 
552 				errorCount++;
553 				isOk = false;
554 
555 				if (errorCount > 30)
556 				{
557 					log << TestLog::Message << "Encountered " << errorCount << " errors. Omitting rest of the per result logs." << TestLog::EndMessage;
558 					break;
559 				}
560 			}
561 			ndx++;
562 		}
563 	}
564 
565 	if (!isOk)
566 		return tcu::TestStatus::fail("Result comparison failed");
567 	if (++m_caseIndex < (int)m_cases.size())
568 		return tcu::TestStatus::incomplete();
569 	return tcu::TestStatus::pass("Pass");
570 }
571 
572 class LinearFilteringTestCase : public vkt::TestCase
573 {
574 public:
575 	LinearFilteringTestCase(tcu::TestContext &context, const char* name, const char* description, VkFormat format, VkFilter chromaFiltering);
576 
577 protected:
578 	void				checkSupport(Context& context) const;
579 	vkt::TestInstance*	createInstance(vkt::Context& context) const;
580 	void				initPrograms(SourceCollections& programCollection) const;
581 
582 private:
583 	VkFormat			m_format;
584 	VkFilter			m_chromaFiltering;
585 };
586 
LinearFilteringTestCase(tcu::TestContext & context,const char * name,const char * description,VkFormat format,VkFilter chromaFiltering)587 LinearFilteringTestCase::LinearFilteringTestCase(tcu::TestContext &context, const char* name, const char* description, VkFormat format, VkFilter chromaFiltering)
588 	: TestCase(context, name, description)
589 	, m_format(format)
590 	, m_chromaFiltering(chromaFiltering)
591 {
592 }
593 
checkSupport(Context & context) const594 void LinearFilteringTestCase::checkSupport(Context& context) const
595 {
596 	context.requireDeviceFunctionality("VK_KHR_sampler_ycbcr_conversion");
597 
598 	const auto&					instInt				= context.getInstanceInterface();
599 	auto						physicalDevice		= context.getPhysicalDevice();
600 	const VkFormatProperties	formatProperties	= getPhysicalDeviceFormatProperties(instInt, physicalDevice, m_format);
601 	const VkFormatFeatureFlags	featureFlags		= formatProperties.optimalTilingFeatures;
602 
603 	if ((featureFlags & VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT) == 0)
604 		TCU_THROW(NotSupportedError, "YCbCr conversion is not supported for format");
605 
606 	if ((featureFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) == 0)
607 		TCU_THROW(NotSupportedError, "Linear filtering not supported for format");
608 
609 	if (m_chromaFiltering != VK_FILTER_LINEAR &&
610 		(featureFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT) == 0)
611 		TCU_THROW(NotSupportedError, "Different chroma, min, and mag filters not supported for format");
612 }
613 
createInstance(vkt::Context & context) const614 vkt::TestInstance* LinearFilteringTestCase::createInstance(vkt::Context& context) const
615 {
616 	return new LinearFilteringTestInstance(context, m_format, m_chromaFiltering);
617 }
618 
initPrograms(SourceCollections & programCollection) const619 void LinearFilteringTestCase::initPrograms(SourceCollections& programCollection) const
620 {
621 	static const char* vertShader =
622 		"#version 450\n"
623 		"precision mediump int; precision highp float;\n"
624 		"layout(location = 0) in vec4 a_position;\n"
625 		"layout(location = 0) out vec2 v_texCoord;\n"
626 		"out gl_PerVertex { vec4 gl_Position; };\n"
627 		"\n"
628 		"void main (void)\n"
629 		"{\n"
630 		"  v_texCoord = a_position.xy * 0.5 + 0.5;\n"
631 		"  gl_Position = a_position;\n"
632 		"}\n";
633 
634 	static const char* fragShader =
635 		"#version 450\n"
636 		"precision mediump int; precision highp float;\n"
637 		"layout(location = 0) in vec2 v_texCoord;\n"
638 		"layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
639 		"layout (set=0, binding=0) uniform sampler2D u_sampler;\n"
640 		"void main (void)\n"
641 		"{\n"
642 		"  dEQP_FragColor = vec4(texture(u_sampler, v_texCoord));\n"
643 		"}\n";
644 
645 	programCollection.glslSources.add("vert") << glu::VertexSource(vertShader);
646 	programCollection.glslSources.add("frag") << glu::FragmentSource(fragShader);
647 }
648 
649 } // anonymous
650 
createFilteringTests(tcu::TestContext & testCtx)651 tcu::TestCaseGroup* createFilteringTests (tcu::TestContext& testCtx)
652 {
653 	struct YCbCrFormatData
654 	{
655 		const char* const	name;
656 		const VkFormat		format;
657 	};
658 
659 	static const std::vector<YCbCrFormatData> ycbcrFormats =
660 	{
661 		{ "g8_b8_r8_3plane_420_unorm",	VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM		},
662 		{ "g8_b8r8_2plane_420_unorm",	VK_FORMAT_G8_B8R8_2PLANE_420_UNORM		},
663 	};
664 
665 	de::MovePtr<tcu::TestCaseGroup> filteringTests(new tcu::TestCaseGroup(testCtx, "filtering",	"YCbCr filtering tests"));
666 
667 	for (const auto& ycbcrFormat : ycbcrFormats)
668 	{
669 		{
670 			const std::string name = std::string("linear_sampler_") + ycbcrFormat.name;
671 			filteringTests->addChild(new LinearFilteringTestCase(filteringTests->getTestContext(), name.c_str(), "", ycbcrFormat.format, VK_FILTER_NEAREST));
672 		}
673 
674 		{
675 			const std::string name = std::string("linear_sampler_with_chroma_linear_filtering_") + ycbcrFormat.name;
676 			filteringTests->addChild(new LinearFilteringTestCase(filteringTests->getTestContext(), name.c_str(), "", ycbcrFormat.format, VK_FILTER_LINEAR));
677 		}
678 	}
679 
680 	return filteringTests.release();
681 }
682 
683 } // ycbcr
684 
685 } // vkt
686