xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/ycbcr/vktYCbCrFilteringTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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