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