1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2017 The Khronos Group Inc.
6  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Protected memory YCbCr image conversion tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktProtectedMemYCbCrConversionTests.hpp"
26 
27 #include "tcuImageCompare.hpp"
28 #include "tcuStringTemplate.hpp"
29 #include "tcuTestLog.hpp"
30 
31 #include "vkBuilderUtil.hpp"
32 #include "vkImageUtil.hpp"
33 #include "vkPrograms.hpp"
34 #include "vkTypeUtil.hpp"
35 #include "vkYCbCrImageWithMemory.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkObjUtil.hpp"
38 
39 #include "vktProtectedMemContext.hpp"
40 #include "vktProtectedMemUtils.hpp"
41 #include "vktTestCaseUtil.hpp"
42 #include "vktYCbCrUtil.hpp"
43 
44 namespace vkt
45 {
46 namespace ProtectedMem
47 {
48 
49 namespace
50 {
51 static const vk::VkFormat s_colorFormat = vk::VK_FORMAT_R8G8B8A8_UNORM;
52 
53 enum
54 {
55     CHECK_SIZE = 50,
56 };
57 
58 struct YCbCrValidationData
59 {
60     tcu::Vec4 coord;
61     tcu::Vec4 minBound;
62     tcu::Vec4 maxBound;
63 };
64 
computeVertexPositions(int numValues,const tcu::IVec2 & renderSize)65 std::vector<tcu::Vec2> computeVertexPositions(int numValues, const tcu::IVec2 &renderSize)
66 {
67     std::vector<tcu::Vec2> positions(numValues);
68     for (int valNdx = 0; valNdx < numValues; valNdx++)
69     {
70         const int ix   = valNdx % renderSize.x();
71         const int iy   = valNdx / renderSize.x();
72         const float fx = -1.0f + 2.0f * ((float(ix) + 0.5f) / float(renderSize.x()));
73         const float fy = -1.0f + 2.0f * ((float(iy) + 0.5f) / float(renderSize.y()));
74 
75         positions[valNdx] = tcu::Vec2(fx, fy);
76     }
77 
78     return positions;
79 }
80 
genTexCoords(std::vector<tcu::Vec2> & coords,const tcu::UVec2 & size)81 void genTexCoords(std::vector<tcu::Vec2> &coords, const tcu::UVec2 &size)
82 {
83     for (uint32_t y = 0; y < size.y(); y++)
84         for (uint32_t x = 0; x < size.x(); x++)
85         {
86             const float fx = (float)x;
87             const float fy = (float)y;
88 
89             const float fw = (float)size.x();
90             const float fh = (float)size.y();
91 
92             const float s = 1.5f * ((fx * 1.5f * fw + fx) / (1.5f * fw * 1.5f * fw)) - 0.25f;
93             const float t = 1.5f * ((fy * 1.5f * fh + fy) / (1.5f * fh * 1.5f * fh)) - 0.25f;
94 
95             coords.push_back(tcu::Vec2(s, t));
96         }
97 }
98 
99 struct TestConfig
100 {
TestConfigvkt::ProtectedMem::__anondc3facf00111::TestConfig101     TestConfig(glu::ShaderType shaderType_, vk::VkFormat format_, vk::VkImageTiling imageTiling_,
102                vk::VkFilter textureFilter_, vk::VkSamplerAddressMode addressModeU_,
103                vk::VkSamplerAddressMode addressModeV_,
104 
105                vk::VkFilter chromaFilter_, vk::VkChromaLocation xChromaOffset_, vk::VkChromaLocation yChromaOffset_,
106                bool explicitReconstruction_, bool disjoint_,
107 
108                vk::VkSamplerYcbcrRange colorRange_, vk::VkSamplerYcbcrModelConversion colorModel_,
109                vk::VkComponentMapping componentMapping_)
110         : shaderType(shaderType_)
111         , format(format_)
112         , imageTiling(imageTiling_)
113         , textureFilter(textureFilter_)
114         , addressModeU(addressModeU_)
115         , addressModeV(addressModeV_)
116 
117         , chromaFilter(chromaFilter_)
118         , xChromaOffset(xChromaOffset_)
119         , yChromaOffset(yChromaOffset_)
120         , explicitReconstruction(explicitReconstruction_)
121         , disjoint(disjoint_)
122 
123         , colorRange(colorRange_)
124         , colorModel(colorModel_)
125         , componentMapping(componentMapping_)
126     {
127     }
128 
129     glu::ShaderType shaderType;
130     vk::VkFormat format;
131     vk::VkImageTiling imageTiling;
132     vk::VkFilter textureFilter;
133     vk::VkSamplerAddressMode addressModeU;
134     vk::VkSamplerAddressMode addressModeV;
135 
136     vk::VkFilter chromaFilter;
137     vk::VkChromaLocation xChromaOffset;
138     vk::VkChromaLocation yChromaOffset;
139     bool explicitReconstruction;
140     bool disjoint;
141 
142     vk::VkSamplerYcbcrRange colorRange;
143     vk::VkSamplerYcbcrModelConversion colorModel;
144     vk::VkComponentMapping componentMapping;
145 };
146 
checkSupport(Context & context,const TestConfig)147 void checkSupport(Context &context, const TestConfig)
148 {
149     checkProtectedQueueSupport(context);
150 }
151 
validateFormatSupport(ProtectedContext & context,TestConfig & config)152 void validateFormatSupport(ProtectedContext &context, TestConfig &config)
153 {
154     tcu::TestLog &log(context.getTestContext().getLog());
155 
156     try
157     {
158         const vk::VkFormatProperties properties(vk::getPhysicalDeviceFormatProperties(
159             context.getInstanceDriver(), context.getPhysicalDevice(), config.format));
160         const vk::VkFormatFeatureFlags features(config.imageTiling == vk::VK_IMAGE_TILING_OPTIMAL ?
161                                                     properties.optimalTilingFeatures :
162                                                     properties.linearTilingFeatures);
163 
164         if ((features & (vk::VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT |
165                          vk::VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT)) == 0)
166             TCU_THROW(NotSupportedError, "Format doesn't support YCbCr conversions");
167 
168         if ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) == 0)
169             TCU_THROW(NotSupportedError, "Format doesn't support sampling");
170 
171         if (config.textureFilter == vk::VK_FILTER_LINEAR &&
172             ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT) == 0))
173             TCU_THROW(NotSupportedError, "Format doesn't support YCbCr linear chroma reconstruction");
174 
175         if (config.chromaFilter == vk::VK_FILTER_LINEAR &&
176             ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT) == 0))
177             TCU_THROW(NotSupportedError, "Format doesn't support YCbCr linear chroma reconstruction");
178 
179         if (config.chromaFilter != config.textureFilter &&
180             ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT) == 0))
181             TCU_THROW(NotSupportedError, "Format doesn't support different chroma and texture filters");
182 
183         if (config.explicitReconstruction &&
184             ((features &
185               vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT) == 0))
186             TCU_THROW(NotSupportedError, "Format doesn't support explicit chroma reconstruction");
187 
188         if (config.disjoint && ((features & vk::VK_FORMAT_FEATURE_DISJOINT_BIT) == 0))
189             TCU_THROW(NotSupportedError, "Format doesn't disjoint planes");
190 
191         if (ycbcr::isXChromaSubsampled(config.format) &&
192             (config.xChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN) &&
193             ((features & vk::VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT) == 0))
194             TCU_THROW(NotSupportedError, "Format doesn't support cosited chroma samples");
195 
196         if (ycbcr::isXChromaSubsampled(config.format) && (config.xChromaOffset == vk::VK_CHROMA_LOCATION_MIDPOINT) &&
197             ((features & vk::VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT) == 0))
198             TCU_THROW(NotSupportedError, "Format doesn't support midpoint chroma samples");
199 
200         if (ycbcr::isYChromaSubsampled(config.format) &&
201             (config.yChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN) &&
202             ((features & vk::VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT) == 0))
203             TCU_THROW(NotSupportedError, "Format doesn't support cosited chroma samples");
204 
205         if (ycbcr::isYChromaSubsampled(config.format) && (config.yChromaOffset == vk::VK_CHROMA_LOCATION_MIDPOINT) &&
206             ((features & vk::VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT) == 0))
207             TCU_THROW(NotSupportedError, "Format doesn't support midpoint chroma samples");
208 
209         if ((features & vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT) != 0)
210             config.explicitReconstruction = true;
211 
212         log << tcu::TestLog::Message << "FormatFeatures: " << vk::getFormatFeatureFlagsStr(features)
213             << tcu::TestLog::EndMessage;
214     }
215     catch (const vk::Error &err)
216     {
217         if (err.getError() == vk::VK_ERROR_FORMAT_NOT_SUPPORTED)
218             TCU_THROW(NotSupportedError, "Format not supported");
219 
220         throw;
221     }
222 }
223 
createSampler(const vk::DeviceInterface & vkd,const vk::VkDevice device,const vk::VkFilter textureFilter,const vk::VkSamplerAddressMode addressModeU,const vk::VkSamplerAddressMode addressModeV,const vk::VkSamplerYcbcrConversion conversion)224 vk::Move<vk::VkSampler> createSampler(const vk::DeviceInterface &vkd, const vk::VkDevice device,
225                                       const vk::VkFilter textureFilter, const vk::VkSamplerAddressMode addressModeU,
226                                       const vk::VkSamplerAddressMode addressModeV,
227                                       const vk::VkSamplerYcbcrConversion conversion)
228 {
229     const vk::VkSamplerYcbcrConversionInfo samplerConversionInfo = {vk::VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
230                                                                     DE_NULL, conversion};
231 
232     const vk::VkSamplerCreateInfo createInfo = {
233         vk::VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
234         &samplerConversionInfo,
235         0u,
236         textureFilter,
237         textureFilter,
238         vk::VK_SAMPLER_MIPMAP_MODE_NEAREST,
239         addressModeU,
240         addressModeV,
241         vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
242         0.0f,
243         VK_FALSE,
244         1.0f,
245         VK_FALSE,
246         vk::VK_COMPARE_OP_ALWAYS,
247         0.0f,
248         0.0f,
249         vk::VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
250         VK_FALSE,
251     };
252 
253     return createSampler(vkd, device, &createInfo);
254 }
255 
createImageView(const vk::DeviceInterface & vkd,const vk::VkDevice device,const vk::VkImage image,const vk::VkFormat format,const vk::VkSamplerYcbcrConversion conversion)256 vk::Move<vk::VkImageView> createImageView(const vk::DeviceInterface &vkd, const vk::VkDevice device,
257                                           const vk::VkImage image, const vk::VkFormat format,
258                                           const vk::VkSamplerYcbcrConversion conversion)
259 {
260     const vk::VkSamplerYcbcrConversionInfo conversionInfo = {vk::VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
261                                                              DE_NULL, conversion};
262 
263     const vk::VkImageViewCreateInfo viewInfo = {
264         vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
265         &conversionInfo,
266         (vk::VkImageViewCreateFlags)0,
267         image,
268         vk::VK_IMAGE_VIEW_TYPE_2D,
269         format,
270         {
271             vk::VK_COMPONENT_SWIZZLE_IDENTITY,
272             vk::VK_COMPONENT_SWIZZLE_IDENTITY,
273             vk::VK_COMPONENT_SWIZZLE_IDENTITY,
274             vk::VK_COMPONENT_SWIZZLE_IDENTITY,
275         },
276         {vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u},
277     };
278 
279     return vk::createImageView(vkd, device, &viewInfo);
280 }
281 
createConversion(const vk::DeviceInterface & vkd,const vk::VkDevice device,const vk::VkFormat format,const vk::VkSamplerYcbcrModelConversion colorModel,const vk::VkSamplerYcbcrRange colorRange,const vk::VkChromaLocation xChromaOffset,const vk::VkChromaLocation yChromaOffset,const vk::VkFilter chromaFilter,const vk::VkComponentMapping & componentMapping,const bool explicitReconstruction)282 vk::Move<vk::VkSamplerYcbcrConversion> createConversion(
283     const vk::DeviceInterface &vkd, const vk::VkDevice device, const vk::VkFormat format,
284     const vk::VkSamplerYcbcrModelConversion colorModel, const vk::VkSamplerYcbcrRange colorRange,
285     const vk::VkChromaLocation xChromaOffset, const vk::VkChromaLocation yChromaOffset, const vk::VkFilter chromaFilter,
286     const vk::VkComponentMapping &componentMapping, const bool explicitReconstruction)
287 {
288     const vk::VkSamplerYcbcrConversionCreateInfo conversionInfo = {
289         vk::VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
290         DE_NULL,
291 
292         format,
293         colorModel,
294         colorRange,
295         componentMapping,
296         xChromaOffset,
297         yChromaOffset,
298         chromaFilter,
299         explicitReconstruction ? VK_TRUE : VK_FALSE};
300 
301     return vk::createSamplerYcbcrConversion(vkd, device, &conversionInfo);
302 }
303 
uploadYCbCrImage(ProtectedContext & ctx,const vk::VkImage image,const ycbcr::MultiPlaneImageData & imageData,const vk::VkAccessFlags nextAccess,const vk::VkImageLayout finalLayout)304 void uploadYCbCrImage(ProtectedContext &ctx, const vk::VkImage image, const ycbcr::MultiPlaneImageData &imageData,
305                       const vk::VkAccessFlags nextAccess, const vk::VkImageLayout finalLayout)
306 {
307     const vk::DeviceInterface &vk   = ctx.getDeviceInterface();
308     const vk::VkDevice device       = ctx.getDevice();
309     const vk::VkQueue queue         = ctx.getQueue();
310     const uint32_t queueFamilyIndex = ctx.getQueueFamilyIndex();
311 
312     const vk::Unique<vk::VkCommandPool> cmdPool(makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
313     const vk::Unique<vk::VkCommandBuffer> cmdBuffer(
314         vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
315 
316     const vk::PlanarFormatDescription &formatDesc = imageData.getDescription();
317 
318     std::vector<de::SharedPtr<de::MovePtr<vk::BufferWithMemory>>> stagingBuffers;
319     std::vector<vk::VkBufferMemoryBarrier> bufferBarriers;
320 
321     for (uint32_t planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
322     {
323         de::MovePtr<vk::BufferWithMemory> buffer(
324             makeBuffer(ctx, PROTECTION_DISABLED, queueFamilyIndex, (uint32_t)imageData.getPlaneSize(planeNdx),
325                        vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT | vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
326                        vk::MemoryRequirement::HostVisible));
327 
328         const vk::VkBufferMemoryBarrier bufferBarrier = {vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
329                                                          DE_NULL,
330                                                          (vk::VkAccessFlags)0,
331                                                          vk::VK_ACCESS_TRANSFER_READ_BIT,
332                                                          queueFamilyIndex,
333                                                          queueFamilyIndex,
334                                                          **buffer,
335                                                          0,
336                                                          (uint32_t)imageData.getPlaneSize(planeNdx)};
337         bufferBarriers.push_back(bufferBarrier);
338 
339         deMemcpy(buffer->getAllocation().getHostPtr(), imageData.getPlanePtr(planeNdx),
340                  imageData.getPlaneSize(planeNdx));
341         flushAlloc(vk, device, buffer->getAllocation());
342         stagingBuffers.push_back(
343             de::SharedPtr<de::MovePtr<vk::BufferWithMemory>>(new de::MovePtr<vk::BufferWithMemory>(buffer.release())));
344     }
345 
346     beginCommandBuffer(vk, *cmdBuffer);
347 
348     for (uint32_t planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
349     {
350         const vk::VkImageAspectFlags aspect =
351             formatDesc.numPlanes > 1 ? vk::getPlaneAspect(planeNdx) : vk::VK_IMAGE_ASPECT_COLOR_BIT;
352 
353         const vk::VkImageMemoryBarrier preCopyBarrier = {vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
354                                                          DE_NULL,
355                                                          (vk::VkAccessFlags)0,
356                                                          vk::VK_ACCESS_TRANSFER_WRITE_BIT,
357                                                          vk::VK_IMAGE_LAYOUT_UNDEFINED,
358                                                          vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
359                                                          queueFamilyIndex,
360                                                          queueFamilyIndex,
361                                                          image,
362                                                          {aspect, 0u, 1u, 0u, 1u}};
363 
364         vk.cmdPipelineBarrier(*cmdBuffer, (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_HOST_BIT,
365                               (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TRANSFER_BIT, (vk::VkDependencyFlags)0u,
366                               0u, (const vk::VkMemoryBarrier *)DE_NULL, (uint32_t)bufferBarriers.size(),
367                               &bufferBarriers[0], 1u, &preCopyBarrier);
368     }
369 
370     for (uint32_t planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
371     {
372         const vk::VkImageAspectFlagBits aspect =
373             (formatDesc.numPlanes > 1) ? vk::getPlaneAspect(planeNdx) : vk::VK_IMAGE_ASPECT_COLOR_BIT;
374         const uint32_t planeW            = (formatDesc.numPlanes > 1) ?
375                                                imageData.getSize().x() / formatDesc.planes[planeNdx].widthDivisor :
376                                                imageData.getSize().x();
377         const uint32_t planeH            = (formatDesc.numPlanes > 1) ?
378                                                imageData.getSize().y() / formatDesc.planes[planeNdx].heightDivisor :
379                                                imageData.getSize().y();
380         const vk::VkBufferImageCopy copy = {
381             0u, // bufferOffset
382             0u, // bufferRowLength
383             0u, // bufferImageHeight
384             {(vk::VkImageAspectFlags)aspect, 0u, 0u, 1u},
385             vk::makeOffset3D(0u, 0u, 0u),
386             vk::makeExtent3D(planeW, planeH, 1u),
387         };
388 
389         vk.cmdCopyBufferToImage(*cmdBuffer, ***stagingBuffers[planeNdx], image,
390                                 vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, &copy);
391     }
392 
393     for (uint32_t planeNdx = 0; planeNdx < imageData.getDescription().numPlanes; ++planeNdx)
394     {
395         const vk::VkImageAspectFlags aspect =
396             formatDesc.numPlanes > 1 ? vk::getPlaneAspect(planeNdx) : vk::VK_IMAGE_ASPECT_COLOR_BIT;
397 
398         const vk::VkImageMemoryBarrier postCopyBarrier = {vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
399                                                           DE_NULL,
400                                                           vk::VK_ACCESS_TRANSFER_WRITE_BIT,
401                                                           nextAccess,
402                                                           vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
403                                                           finalLayout,
404                                                           VK_QUEUE_FAMILY_IGNORED,
405                                                           VK_QUEUE_FAMILY_IGNORED,
406                                                           image,
407                                                           {aspect, 0u, 1u, 0u, 1u}};
408 
409         vk.cmdPipelineBarrier(*cmdBuffer, (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
410                               (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
411                               (vk::VkDependencyFlags)0u, 0u, (const vk::VkMemoryBarrier *)DE_NULL, 0u,
412                               (const vk::VkBufferMemoryBarrier *)DE_NULL, 1u, &postCopyBarrier);
413     }
414 
415     endCommandBuffer(vk, *cmdBuffer);
416 
417     {
418         const vk::Unique<vk::VkFence> fence(createFence(vk, device));
419         VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
420     }
421 }
422 
logTestCaseInfo(tcu::TestLog & log,const TestConfig & config)423 void logTestCaseInfo(tcu::TestLog &log, const TestConfig &config)
424 {
425     log << tcu::TestLog::Message << "ShaderType: " << config.shaderType << tcu::TestLog::EndMessage;
426     log << tcu::TestLog::Message << "Format: " << config.format << tcu::TestLog::EndMessage;
427     log << tcu::TestLog::Message << "ImageTiling: " << config.imageTiling << tcu::TestLog::EndMessage;
428     log << tcu::TestLog::Message << "TextureFilter: " << config.textureFilter << tcu::TestLog::EndMessage;
429     log << tcu::TestLog::Message << "AddressModeU: " << config.addressModeU << tcu::TestLog::EndMessage;
430     log << tcu::TestLog::Message << "AddressModeV: " << config.addressModeV << tcu::TestLog::EndMessage;
431     log << tcu::TestLog::Message << "ChromaFilter: " << config.chromaFilter << tcu::TestLog::EndMessage;
432     log << tcu::TestLog::Message << "XChromaOffset: " << config.xChromaOffset << tcu::TestLog::EndMessage;
433     log << tcu::TestLog::Message << "YChromaOffset: " << config.yChromaOffset << tcu::TestLog::EndMessage;
434     log << tcu::TestLog::Message << "ExplicitReconstruction: " << (config.explicitReconstruction ? "true" : "false")
435         << tcu::TestLog::EndMessage;
436     log << tcu::TestLog::Message << "Disjoint: " << (config.disjoint ? "true" : "false") << tcu::TestLog::EndMessage;
437     log << tcu::TestLog::Message << "ColorRange: " << config.colorRange << tcu::TestLog::EndMessage;
438     log << tcu::TestLog::Message << "ColorModel: " << config.colorModel << tcu::TestLog::EndMessage;
439     log << tcu::TestLog::Message << "ComponentMapping: " << config.componentMapping << tcu::TestLog::EndMessage;
440 }
441 
logBoundImages(tcu::TestLog & log,const tcu::UVec2 size,const std::vector<tcu::Vec4> & minBounds,const std::vector<tcu::Vec4> & maxBounds)442 void logBoundImages(tcu::TestLog &log, const tcu::UVec2 size, const std::vector<tcu::Vec4> &minBounds,
443                     const std::vector<tcu::Vec4> &maxBounds)
444 {
445     tcu::TextureLevel minImage(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), size.x(),
446                                size.y());
447     tcu::TextureLevel maxImage(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), size.x(),
448                                size.y());
449 
450     for (int y = 0; y < (int)(size.y()); y++)
451         for (int x = 0; x < (int)(size.x()); x++)
452         {
453             const int ndx = x + y * (int)(size.x());
454             minImage.getAccess().setPixel(minBounds[ndx], x, y);
455             maxImage.getAccess().setPixel(maxBounds[ndx], x, y);
456         }
457 
458     const tcu::Vec4 scale(1.0f);
459     const tcu::Vec4 bias(0.0f);
460 
461     log << tcu::TestLog::Image("MinBoundImage", "MinBoundImage", minImage.getAccess(), scale, bias);
462     log << tcu::TestLog::Image("MaxBoundImage", "MaxBoundImage", maxImage.getAccess(), scale, bias);
463 }
464 
validateImage(ProtectedContext & ctx,const std::vector<YCbCrValidationData> & refData,const vk::VkSampler sampler,const vk::VkImageView imageView,const uint32_t combinedSamplerDescriptorCount)465 bool validateImage(ProtectedContext &ctx, const std::vector<YCbCrValidationData> &refData, const vk::VkSampler sampler,
466                    const vk::VkImageView imageView, const uint32_t combinedSamplerDescriptorCount)
467 {
468     {
469         tcu::TestLog &log(ctx.getTestContext().getLog());
470 
471         log << tcu::TestLog::Message << "Reference values:" << tcu::TestLog::EndMessage;
472         for (uint32_t ndx = 0; ndx < refData.size(); ndx++)
473         {
474             log << tcu::TestLog::Message << (ndx + 1) << refData[ndx].coord << ": [" << refData[ndx].minBound << ", "
475                 << refData[ndx].maxBound << "]" << tcu::TestLog::EndMessage;
476         }
477     }
478 
479     const uint64_t oneSec = 1000 * 1000 * 1000;
480 
481     const vk::DeviceInterface &vk   = ctx.getDeviceInterface();
482     const vk::VkDevice device       = ctx.getDevice();
483     const vk::VkQueue queue         = ctx.getQueue();
484     const uint32_t queueFamilyIndex = ctx.getQueueFamilyIndex();
485 
486     DE_ASSERT(refData.size() >= CHECK_SIZE && CHECK_SIZE > 0);
487     const uint32_t refUniformSize = (uint32_t)(sizeof(YCbCrValidationData) * refData.size());
488     const de::UniquePtr<vk::BufferWithMemory> refUniform(
489         makeBuffer(ctx, PROTECTION_DISABLED, queueFamilyIndex, refUniformSize, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
490                    vk::MemoryRequirement::HostVisible));
491 
492     // Set the reference uniform data
493     {
494         deMemcpy(refUniform->getAllocation().getHostPtr(), &refData[0], refUniformSize);
495         flushAlloc(vk, device, refUniform->getAllocation());
496     }
497 
498     const uint32_t helperBufferSize = (uint32_t)(2 * sizeof(uint32_t));
499     const de::MovePtr<vk::BufferWithMemory> helperBuffer(
500         makeBuffer(ctx, PROTECTION_ENABLED, queueFamilyIndex, helperBufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
501                    vk::MemoryRequirement::Protected));
502     const vk::Unique<vk::VkShaderModule> resetSSBOShader(
503         vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("ResetSSBO"), 0));
504     const vk::Unique<vk::VkShaderModule> validatorShader(
505         vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("ImageValidator"), 0));
506 
507     // Create descriptors
508     const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(
509         vk::DescriptorSetLayoutBuilder()
510             .addSingleSamplerBinding(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, vk::VK_SHADER_STAGE_COMPUTE_BIT,
511                                      &sampler)
512             .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
513             .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
514             .build(vk, device));
515     const vk::Unique<vk::VkDescriptorPool> descriptorPool(
516         vk::DescriptorPoolBuilder()
517             .addType(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, combinedSamplerDescriptorCount)
518             .addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u)
519             .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
520             .build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
521     const vk::Unique<vk::VkDescriptorSet> descriptorSet(
522         makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
523 
524     // Update descriptor set infirmation
525     {
526         vk::VkDescriptorBufferInfo descRefUniform = makeDescriptorBufferInfo(**refUniform, 0, refUniformSize);
527         vk::VkDescriptorBufferInfo descBuffer     = makeDescriptorBufferInfo(**helperBuffer, 0, helperBufferSize);
528         vk::VkDescriptorImageInfo descSampledImg =
529             makeDescriptorImageInfo(sampler, imageView, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
530 
531         vk::DescriptorSetUpdateBuilder()
532             .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u),
533                          vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &descSampledImg)
534             .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u),
535                          vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descRefUniform)
536             .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u),
537                          vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descBuffer)
538             .update(vk, device);
539     }
540 
541     const vk::Unique<vk::VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
542     const vk::Unique<vk::VkCommandPool> cmdPool(makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
543 
544     // Reset helper SSBO
545     {
546         const vk::Unique<vk::VkFence> fence(vk::createFence(vk, device));
547         const vk::Unique<vk::VkPipeline> resetSSBOPipeline(
548             makeComputePipeline(vk, device, *pipelineLayout, *resetSSBOShader));
549         const vk::Unique<vk::VkCommandBuffer> resetCmdBuffer(
550             vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
551         beginCommandBuffer(vk, *resetCmdBuffer);
552 
553         vk.cmdBindPipeline(*resetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *resetSSBOPipeline);
554         vk.cmdBindDescriptorSets(*resetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
555                                  &*descriptorSet, 0u, DE_NULL);
556         vk.cmdDispatch(*resetCmdBuffer, 1u, 1u, 1u);
557 
558         endCommandBuffer(vk, *resetCmdBuffer);
559         VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *resetCmdBuffer, *fence, ~0ull));
560     }
561 
562     // Create validation compute commands & submit
563     vk::VkResult queueSubmitResult;
564     {
565         const vk::Unique<vk::VkFence> fence(vk::createFence(vk, device));
566         const vk::Unique<vk::VkPipeline> validationPipeline(
567             makeComputePipeline(vk, device, *pipelineLayout, *validatorShader));
568         const vk::Unique<vk::VkCommandBuffer> cmdBuffer(
569             vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
570 
571         beginCommandBuffer(vk, *cmdBuffer);
572 
573         vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *validationPipeline);
574         vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
575                                  &*descriptorSet, 0u, DE_NULL);
576         vk.cmdDispatch(*cmdBuffer, CHECK_SIZE, 1u, 1u);
577 
578         endCommandBuffer(vk, *cmdBuffer);
579 
580         queueSubmitResult = queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, oneSec * 5);
581     }
582 
583     // \todo do we need to check the fence status?
584     if (queueSubmitResult == vk::VK_TIMEOUT)
585         return false;
586 
587     // at this point the submit result should be VK_TRUE
588     VK_CHECK(queueSubmitResult);
589     return true;
590 }
591 
testShaders(vk::SourceCollections & dst,const TestConfig config)592 void testShaders(vk::SourceCollections &dst, const TestConfig config)
593 {
594     const char *const shaderHeader = "layout(constant_id = 1) const float threshold = 0.01f;\n"
595                                      "layout(set = 0, binding = 0) uniform highp sampler2D protectedImage;\n"
596                                      "\n"
597                                      "struct validationData {\n"
598                                      "    highp vec4 imageCoord;\n"
599                                      "    highp vec4 imageRefMinBound;\n"
600                                      "    highp vec4 imageRefMaxBound;\n"
601                                      "};\n"
602                                      "layout(std140, set = 0, binding = 1) uniform Data\n"
603                                      "{\n"
604                                      "    validationData ref[250];\n"
605                                      "};\n";
606 
607     const char *const compareFunction = "bool compare(highp vec4 value, highp vec4 minValue, highp vec4 maxValue)\n"
608                                         "{\n"
609                                         "    return all(greaterThanEqual(value, minValue - threshold)) && "
610                                         "all(lessThanEqual(value, maxValue + threshold));\n"
611                                         "}\n";
612 
613     std::map<std::string, std::string> validatorSpec;
614     validatorSpec["CHECK_SIZE"]       = de::toString((uint32_t)CHECK_SIZE);
615     validatorSpec["SHADER_HEADER"]    = shaderHeader;
616     validatorSpec["COMPARE_FUNCTION"] = compareFunction;
617 
618     const char *const validatorShader =
619         "#version 450\n"
620         "\n"
621         "${SHADER_HEADER}"
622         "\n"
623         "layout(std140, set = 0, binding = 2) buffer ProtectedHelper\n"
624         "{\n"
625         "    highp uint zero;\n"
626         "    highp uint unusedOut;\n"
627         "} helper;\n"
628         "\n"
629         "void error()\n"
630         "{\n"
631         "    for (uint x = 0u; x < 10u; x += helper.zero)\n"
632         "        atomicAdd(helper.unusedOut, 1u);\n"
633         "}\n"
634         "\n"
635         "${COMPARE_FUNCTION}"
636         "\n"
637         "void main(void)\n"
638         "{\n"
639         "    int idx = int(gl_GlobalInvocationID.x);\n"
640         "    vec4 currentValue = texture(protectedImage, ref[idx].imageCoord.xy);\n"
641         "    if (!compare(currentValue, ref[idx].imageRefMinBound, ref[idx].imageRefMaxBound))\n"
642         "    {\n"
643         "      error();\n"
644         "    }\n"
645         "}\n";
646 
647     const char *const resetSSBOShader = "#version 450\n"
648                                         "layout(local_size_x = 1) in;\n"
649                                         "\n"
650                                         "layout(std140, set=0, binding=2) buffer ProtectedHelper\n"
651                                         "{\n"
652                                         "    highp uint zero; // set to 0\n"
653                                         "    highp uint unusedOut;\n"
654                                         "} helper;\n"
655                                         "\n"
656                                         "void main (void)\n"
657                                         "{\n"
658                                         "    helper.zero = 0;\n"
659                                         "    helper.unusedOut = 0;\n"
660                                         "}\n";
661 
662     dst.glslSources.add("ResetSSBO") << glu::ComputeSource(resetSSBOShader);
663     dst.glslSources.add("ImageValidator")
664         << glu::ComputeSource(tcu::StringTemplate(validatorShader).specialize(validatorSpec));
665 
666     if (config.shaderType == glu::SHADERTYPE_COMPUTE)
667         return; // Bail early as the YCbCr image validator already have the test programs set for compute tests
668 
669     const char *const compareOperation =
670         "    highp vec4 currentValue = texture(protectedImage, ref[v_idx].imageCoord.xy);\n"
671         "    if (compare(currentValue, ref[v_idx].imageRefMinBound, ref[v_idx].imageRefMaxBound))\n"
672         "    {\n"
673         "        o_color = vec4(0.0f, 1.0f, 0.0f, 1.0f);\n" // everything is ok, green
674         "    }\n"
675         "    else"
676         "    {\n"
677         "        o_color = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
678         "    }\n";
679 
680     std::map<std::string, std::string> shaderSpec;
681     shaderSpec["SHADER_HEADER"]     = shaderHeader;
682     shaderSpec["COMPARE_FUNCTION"]  = compareFunction;
683     shaderSpec["COMPARE_OPERATION"] = compareOperation;
684 
685     if (config.shaderType == glu::SHADERTYPE_VERTEX)
686     {
687         const char *const vertexShader = "#version 450\n"
688                                          "${SHADER_HEADER}\n"
689                                          "\n"
690                                          "layout(location = 0) in highp vec2 a_position;\n"
691                                          "layout(location = 0) flat out highp vec4 o_color;\n"
692                                          "\n"
693                                          "${COMPARE_FUNCTION}"
694                                          "\n"
695                                          "void main(void)\n"
696                                          "{\n"
697                                          "    gl_Position = vec4(a_position, 0.0f, 1.0f);\n"
698                                          "    gl_PointSize = 1.0f;\n"
699                                          "    int v_idx = gl_VertexIndex;\n"
700                                          "${COMPARE_OPERATION}"
701                                          "}\n";
702 
703         const char *const fragmentShader = "#version 450\n"
704                                            "\n"
705                                            "layout(location = 0) flat in highp vec4 v_color;\n"
706                                            "layout(location = 0) out highp vec4 o_color;\n"
707                                            "\n"
708                                            "void main(void)\n"
709                                            "{\n"
710                                            "    o_color = v_color;\n"
711                                            "}\n";
712 
713         dst.glslSources.add("vert") << glu::VertexSource(tcu::StringTemplate(vertexShader).specialize(shaderSpec));
714         dst.glslSources.add("frag") << glu::FragmentSource(fragmentShader);
715     }
716     else if (config.shaderType == glu::SHADERTYPE_FRAGMENT)
717     {
718         const char *const vertexShader = "#version 450\n"
719                                          "layout(location = 0) in highp vec2 a_position;\n"
720                                          "layout(location = 0) flat out highp int o_idx;\n"
721                                          "\n"
722                                          "void main(void)\n"
723                                          "{\n"
724                                          "    gl_Position = vec4(a_position, 0.0f, 1.0f);\n"
725                                          "    gl_PointSize = 1.0f;\n"
726                                          "    o_idx = gl_VertexIndex;\n"
727                                          "}\n";
728 
729         const char *const fragmentShader = "#version 450\n"
730                                            "${SHADER_HEADER}\n"
731                                            "\n"
732                                            "layout(location = 0) flat in highp int v_idx;\n"
733                                            "layout(location = 0) out highp vec4 o_color;\n"
734                                            "\n"
735                                            "${COMPARE_FUNCTION}"
736                                            "\n"
737                                            "void main(void)\n"
738                                            "{\n"
739                                            "${COMPARE_OPERATION}"
740                                            "}\n";
741 
742         dst.glslSources.add("vert") << glu::VertexSource(vertexShader);
743         dst.glslSources.add("frag") << glu::FragmentSource(tcu::StringTemplate(fragmentShader).specialize(shaderSpec));
744     }
745 }
746 
createYcbcrImage2D(ProtectedContext & context,const ProtectionMode protectionMode,const uint32_t width,const uint32_t height,const vk::VkFormat format,const vk::VkImageCreateFlags createFlags,const vk::VkImageUsageFlags usageFlags)747 de::MovePtr<vk::YCbCrImageWithMemory> createYcbcrImage2D(ProtectedContext &context, const ProtectionMode protectionMode,
748                                                          const uint32_t width, const uint32_t height,
749                                                          const vk::VkFormat format,
750                                                          const vk::VkImageCreateFlags createFlags,
751                                                          const vk::VkImageUsageFlags usageFlags)
752 {
753     const vk::DeviceInterface &vk = context.getDeviceInterface();
754     const vk::VkDevice &device    = context.getDevice();
755     vk::Allocator &allocator      = context.getDefaultAllocator();
756     const uint32_t queueIdx       = context.getQueueFamilyIndex();
757 #ifndef NOT_PROTECTED
758     const uint32_t flags = (protectionMode == PROTECTION_ENABLED) ? vk::VK_IMAGE_CREATE_PROTECTED_BIT : 0x0;
759     const vk::MemoryRequirement memReq =
760         (protectionMode == PROTECTION_ENABLED) ? vk::MemoryRequirement::Protected : vk::MemoryRequirement::Any;
761 #else
762     const uint32_t flags               = 0x0;
763     const vk::MemoryRequirement memReq = vk::MemoryRequirement::Any;
764     DE_UNREF(protectionMode);
765 #endif
766 
767     const vk::VkImageCreateInfo params = {
768         vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,       // VkStructureType            stype
769         DE_NULL,                                       // const void*                pNext
770         (vk::VkImageCreateFlags)(flags | createFlags), // VkImageCreateFlags        flags
771         vk::VK_IMAGE_TYPE_2D,                          // VkImageType                imageType
772         format,                                        // VkFormat                    format
773         {width, height, 1},                            // VkExtent3D                extent
774         1u,                                            // uint32_t                    mipLevels
775         1u,                                            // uint32_t                    arrayLayers
776         vk::VK_SAMPLE_COUNT_1_BIT,                     // VkSampleCountFlagBits    samples
777         vk::VK_IMAGE_TILING_OPTIMAL,                   // VkImageTiling            tiling
778         usageFlags,                                    // VkImageUsageFlags        usage
779         vk::VK_SHARING_MODE_EXCLUSIVE,                 // VkSharingMode            sharingMode
780         1u,                                            // uint32_t                    queueFamilyIndexCount
781         &queueIdx,                                     // const uint32_t*            pQueueFamilyIndices
782         vk::VK_IMAGE_LAYOUT_UNDEFINED,                 // VkImageLayout            initialLayout
783     };
784 
785     return de::MovePtr<vk::YCbCrImageWithMemory>(new vk::YCbCrImageWithMemory(vk, device, allocator, params, memReq));
786 }
787 
renderYCbCrToColor(ProtectedContext & ctx,const tcu::UVec2 size,const vk::VkSampler ycbcrSampler,const vk::VkImageView ycbcrImageView,const vk::VkImage colorImage,const vk::VkImageView colorImageView,const std::vector<YCbCrValidationData> & referenceData,const std::vector<tcu::Vec2> & posCoords,const uint32_t combinedSamplerDescriptorCount)788 void renderYCbCrToColor(ProtectedContext &ctx, const tcu::UVec2 size, const vk::VkSampler ycbcrSampler,
789                         const vk::VkImageView ycbcrImageView, const vk::VkImage colorImage,
790                         const vk::VkImageView colorImageView, const std::vector<YCbCrValidationData> &referenceData,
791                         const std::vector<tcu::Vec2> &posCoords, const uint32_t combinedSamplerDescriptorCount)
792 {
793     const vk::DeviceInterface &vk   = ctx.getDeviceInterface();
794     const vk::VkDevice device       = ctx.getDevice();
795     const vk::VkQueue queue         = ctx.getQueue();
796     const uint32_t queueFamilyIndex = ctx.getQueueFamilyIndex();
797 
798     const vk::Unique<vk::VkRenderPass> renderPass(createRenderPass(ctx, s_colorFormat));
799     const vk::Unique<vk::VkFramebuffer> framebuffer(
800         createFramebuffer(ctx, size.x(), size.y(), *renderPass, colorImageView));
801     const vk::Unique<vk::VkShaderModule> vertexShader(
802         createShaderModule(vk, device, ctx.getBinaryCollection().get("vert"), 0));
803     const vk::Unique<vk::VkShaderModule> fragmentShader(
804         createShaderModule(vk, device, ctx.getBinaryCollection().get("frag"), 0));
805     const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(
806         vk::DescriptorSetLayoutBuilder()
807             .addSingleSamplerBinding(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, vk::VK_SHADER_STAGE_ALL,
808                                      &ycbcrSampler)
809             .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_ALL)
810             .build(vk, device));
811     const vk::Unique<vk::VkDescriptorPool> descriptorPool(
812         vk::DescriptorPoolBuilder()
813             .addType(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, combinedSamplerDescriptorCount)
814             .addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u)
815             .build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
816     const vk::Unique<vk::VkDescriptorSet> descriptorSet(
817         makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
818     const vk::Unique<vk::VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
819 
820     const uint32_t refUniformSize = (uint32_t)(sizeof(YCbCrValidationData) * referenceData.size());
821     const de::UniquePtr<vk::BufferWithMemory> refUniform(
822         makeBuffer(ctx, PROTECTION_DISABLED, queueFamilyIndex, refUniformSize, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
823                    vk::MemoryRequirement::HostVisible));
824 
825     // Set the reference uniform data
826     {
827         deMemcpy(refUniform->getAllocation().getHostPtr(), &referenceData[0], refUniformSize);
828         flushAlloc(vk, device, refUniform->getAllocation());
829     }
830 
831     // Update descriptor set
832     {
833         vk::VkDescriptorImageInfo ycbcrSampled(
834             makeDescriptorImageInfo(ycbcrSampler, ycbcrImageView, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL));
835         vk::VkDescriptorBufferInfo descRefUniform = makeDescriptorBufferInfo(**refUniform, 0, refUniformSize);
836         vk::DescriptorSetUpdateBuilder()
837             .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u),
838                          vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &ycbcrSampled)
839             .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u),
840                          vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descRefUniform)
841             .update(vk, device);
842     }
843 
844     VertexBindings vertexBindings;
845     VertexAttribs vertexAttribs;
846     de::MovePtr<vk::BufferWithMemory> vertexBuffer;
847     {
848         const uint32_t bufferSize = (uint32_t)(sizeof(tcu::Vec2) * posCoords.size());
849         {
850             const vk::VkVertexInputBindingDescription inputBinding = {
851                 0u,                             // uint32_t binding;
852                 sizeof(tcu::Vec2),              // uint32_t strideInBytes;
853                 vk::VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate;
854             };
855             const vk::VkVertexInputAttributeDescription inputAttribute = {
856                 0u,                          // uint32_t location;
857                 0u,                          // uint32_t binding;
858                 vk::VK_FORMAT_R32G32_SFLOAT, // VkFormat format;
859                 0u                           // uint32_t offsetInBytes;
860             };
861 
862             vertexBindings.push_back(inputBinding);
863             vertexAttribs.push_back(inputAttribute);
864         }
865 
866         vertexBuffer = makeBuffer(ctx, PROTECTION_DISABLED, queueFamilyIndex, bufferSize,
867                                   vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, vk::MemoryRequirement::HostVisible);
868 
869         deMemcpy(vertexBuffer->getAllocation().getHostPtr(), &posCoords[0], bufferSize);
870         flushAlloc(vk, device, vertexBuffer->getAllocation());
871     }
872 
873     const vk::Unique<vk::VkPipeline> pipeline(
874         makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexShader, *fragmentShader, vertexBindings,
875                              vertexAttribs, size, vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST));
876     const vk::Unique<vk::VkCommandPool> cmdPool(makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
877     const vk::Unique<vk::VkCommandBuffer> cmdBuffer(
878         vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
879 
880     beginCommandBuffer(vk, *cmdBuffer);
881     {
882         const vk::VkImageMemoryBarrier attachmentStartBarrier = {vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
883                                                                  DE_NULL,
884                                                                  0u,
885                                                                  vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
886                                                                  vk::VK_IMAGE_LAYOUT_UNDEFINED,
887                                                                  vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
888                                                                  queueFamilyIndex,
889                                                                  queueFamilyIndex,
890                                                                  colorImage,
891                                                                  {vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u}};
892 
893         vk.cmdPipelineBarrier(*cmdBuffer, (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
894                               (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
895                               (vk::VkDependencyFlags)0u, 0u, (const vk::VkMemoryBarrier *)DE_NULL, 0u,
896                               (const vk::VkBufferMemoryBarrier *)DE_NULL, 1u, &attachmentStartBarrier);
897     }
898 
899     beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, vk::makeRect2D(0, 0, size.x(), size.y()),
900                     tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f));
901 
902     vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
903     vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &*descriptorSet,
904                              0u, DE_NULL);
905 
906     {
907         const vk::VkDeviceSize vertexBufferOffset = 0;
908         vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &**vertexBuffer, &vertexBufferOffset);
909     }
910 
911     vk.cmdDraw(*cmdBuffer, /*vertexCount*/ (uint32_t)posCoords.size(), 1u, 0u, 0u);
912 
913     endRenderPass(vk, *cmdBuffer);
914 
915     // color attachment render end barrier
916     {
917         const vk::VkImageMemoryBarrier attachmentEndBarrier = {vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
918                                                                DE_NULL,
919                                                                vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
920                                                                vk::VK_ACCESS_SHADER_READ_BIT,
921                                                                vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
922                                                                vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
923                                                                queueFamilyIndex,
924                                                                queueFamilyIndex,
925                                                                colorImage,
926                                                                {vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u}};
927 
928         vk.cmdPipelineBarrier(*cmdBuffer, (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
929                               (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
930                               (vk::VkDependencyFlags)0u, 0u, (const vk::VkMemoryBarrier *)DE_NULL, 0u,
931                               (const vk::VkBufferMemoryBarrier *)DE_NULL, 1u, &attachmentEndBarrier);
932     }
933 
934     endCommandBuffer(vk, *cmdBuffer);
935 
936     // Submit command buffer
937     {
938         const vk::Unique<vk::VkFence> fence(vk::createFence(vk, device));
939         VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
940     }
941 }
942 
generateYCbCrImage(ProtectedContext & ctx,const TestConfig & config,const tcu::UVec2 size,const std::vector<tcu::Vec2> & texCoords,ycbcr::MultiPlaneImageData & ycbcrSrc,std::vector<tcu::Vec4> & ycbcrMinBounds,std::vector<tcu::Vec4> & ycbcrMaxBounds)943 void generateYCbCrImage(ProtectedContext &ctx, const TestConfig &config, const tcu::UVec2 size,
944                         const std::vector<tcu::Vec2> &texCoords, ycbcr::MultiPlaneImageData &ycbcrSrc,
945                         std::vector<tcu::Vec4> &ycbcrMinBounds, std::vector<tcu::Vec4> &ycbcrMaxBounds)
946 {
947     tcu::TestLog &log(ctx.getTestContext().getLog());
948     const std::vector<tcu::FloatFormat> filteringPrecision(ycbcr::getPrecision(config.format));
949     const std::vector<tcu::FloatFormat> conversionPrecision(ycbcr::getPrecision(config.format));
950     const tcu::UVec4 bitDepth(ycbcr::getYCbCrBitDepth(config.format));
951     bool explicitReconstruction = config.explicitReconstruction;
952     const uint32_t subTexelPrecisionBits(
953         vk::getPhysicalDeviceProperties(ctx.getInstanceDriver(), ctx.getPhysicalDevice()).limits.subTexelPrecisionBits);
954 
955     const vk::PlanarFormatDescription planeInfo(vk::getPlanarFormatDescription(config.format));
956 
957     uint32_t nullAccessData(0u);
958     ycbcr::ChannelAccess nullAccess(tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT, 1u,
959                                     tcu::IVec3(size.x(), size.y(), 1), tcu::IVec3(0, 0, 0), &nullAccessData, 0u);
960     uint32_t nullAccessAlphaData(~0u);
961     ycbcr::ChannelAccess nullAccessAlpha(tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT, 1u,
962                                          tcu::IVec3(size.x(), size.y(), 1), tcu::IVec3(0, 0, 0), &nullAccessAlphaData,
963                                          0u);
964     ycbcr::ChannelAccess rChannelAccess(planeInfo.hasChannelNdx(0) ? getChannelAccess(ycbcrSrc, planeInfo, size, 0) :
965                                                                      nullAccess);
966     ycbcr::ChannelAccess gChannelAccess(planeInfo.hasChannelNdx(1) ? getChannelAccess(ycbcrSrc, planeInfo, size, 1) :
967                                                                      nullAccess);
968     ycbcr::ChannelAccess bChannelAccess(planeInfo.hasChannelNdx(2) ? getChannelAccess(ycbcrSrc, planeInfo, size, 2) :
969                                                                      nullAccess);
970     ycbcr::ChannelAccess aChannelAccess(planeInfo.hasChannelNdx(3) ? getChannelAccess(ycbcrSrc, planeInfo, size, 3) :
971                                                                      nullAccessAlpha);
972     const bool implicitNearestCosited((config.chromaFilter == vk::VK_FILTER_NEAREST && !explicitReconstruction) &&
973                                       (config.xChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN ||
974                                        config.yChromaOffset == vk::VK_CHROMA_LOCATION_COSITED_EVEN));
975 
976     for (uint32_t planeNdx = 0; planeNdx < planeInfo.numPlanes; planeNdx++)
977         deMemset(ycbcrSrc.getPlanePtr(planeNdx), 0u, ycbcrSrc.getPlaneSize(planeNdx));
978 
979     // \todo Limit values to only values that produce defined values using selected colorRange and colorModel? The verification code handles those cases already correctly.
980     if (planeInfo.hasChannelNdx(0))
981     {
982         for (int y = 0; y < rChannelAccess.getSize().y(); y++)
983             for (int x = 0; x < rChannelAccess.getSize().x(); x++)
984                 rChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)x / (float)rChannelAccess.getSize().x());
985     }
986 
987     if (planeInfo.hasChannelNdx(1))
988     {
989         for (int y = 0; y < gChannelAccess.getSize().y(); y++)
990             for (int x = 0; x < gChannelAccess.getSize().x(); x++)
991                 gChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)y / (float)gChannelAccess.getSize().y());
992     }
993 
994     if (planeInfo.hasChannelNdx(2))
995     {
996         for (int y = 0; y < bChannelAccess.getSize().y(); y++)
997             for (int x = 0; x < bChannelAccess.getSize().x(); x++)
998                 bChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)(x + y) / (float)(bChannelAccess.getSize().x() +
999                                                                                         bChannelAccess.getSize().y()));
1000     }
1001 
1002     if (planeInfo.hasChannelNdx(3))
1003     {
1004         for (int y = 0; y < aChannelAccess.getSize().y(); y++)
1005             for (int x = 0; x < aChannelAccess.getSize().x(); x++)
1006                 aChannelAccess.setChannel(tcu::IVec3(x, y, 0), (float)(x * y) / (float)(aChannelAccess.getSize().x() *
1007                                                                                         aChannelAccess.getSize().y()));
1008     }
1009 
1010     std::vector<tcu::Vec4> uvBounds;
1011     std::vector<tcu::IVec4> ijBounds;
1012     ycbcr::calculateBounds(rChannelAccess, gChannelAccess, bChannelAccess, aChannelAccess, bitDepth, texCoords,
1013                            filteringPrecision, conversionPrecision, subTexelPrecisionBits, config.textureFilter,
1014                            config.colorModel, config.colorRange, config.chromaFilter, config.xChromaOffset,
1015                            config.yChromaOffset, config.componentMapping, explicitReconstruction, config.addressModeU,
1016                            config.addressModeV, ycbcrMinBounds, ycbcrMaxBounds, uvBounds, ijBounds);
1017 
1018     // Handle case: If implicit reconstruction and chromaFilter == NEAREST, an implementation may behave as if both chroma offsets are MIDPOINT.
1019     if (implicitNearestCosited)
1020     {
1021         std::vector<tcu::Vec4> relaxedYcbcrMinBounds;
1022         std::vector<tcu::Vec4> relaxedYcbcrMaxBounds;
1023 
1024         ycbcr::calculateBounds(rChannelAccess, gChannelAccess, bChannelAccess, aChannelAccess, bitDepth, texCoords,
1025                                filteringPrecision, conversionPrecision, subTexelPrecisionBits, config.textureFilter,
1026                                config.colorModel, config.colorRange, config.chromaFilter,
1027                                vk::VK_CHROMA_LOCATION_MIDPOINT, vk::VK_CHROMA_LOCATION_MIDPOINT,
1028                                config.componentMapping, explicitReconstruction, config.addressModeU,
1029                                config.addressModeV, relaxedYcbcrMinBounds, relaxedYcbcrMaxBounds, uvBounds, ijBounds);
1030 
1031         DE_ASSERT(relaxedYcbcrMinBounds.size() == ycbcrMinBounds.size());
1032         DE_ASSERT(relaxedYcbcrMaxBounds.size() == ycbcrMaxBounds.size());
1033 
1034         for (size_t i = 0; i < ycbcrMinBounds.size(); i++)
1035         {
1036             ycbcrMinBounds[i] = tcu::Vec4(de::min<float>(ycbcrMinBounds[i].x(), relaxedYcbcrMinBounds[i].x()),
1037                                           de::min<float>(ycbcrMinBounds[i].y(), relaxedYcbcrMinBounds[i].y()),
1038                                           de::min<float>(ycbcrMinBounds[i].z(), relaxedYcbcrMinBounds[i].z()),
1039                                           de::min<float>(ycbcrMinBounds[i].w(), relaxedYcbcrMinBounds[i].w()));
1040 
1041             ycbcrMaxBounds[i] = tcu::Vec4(de::max<float>(ycbcrMaxBounds[i].x(), relaxedYcbcrMaxBounds[i].x()),
1042                                           de::max<float>(ycbcrMaxBounds[i].y(), relaxedYcbcrMaxBounds[i].y()),
1043                                           de::max<float>(ycbcrMaxBounds[i].z(), relaxedYcbcrMaxBounds[i].z()),
1044                                           de::max<float>(ycbcrMaxBounds[i].w(), relaxedYcbcrMaxBounds[i].w()));
1045         }
1046     }
1047 
1048     if (vk::isYCbCrFormat(config.format))
1049     {
1050         tcu::TextureLevel rImage(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT),
1051                                  rChannelAccess.getSize().x(), rChannelAccess.getSize().y());
1052         tcu::TextureLevel gImage(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT),
1053                                  gChannelAccess.getSize().x(), gChannelAccess.getSize().y());
1054         tcu::TextureLevel bImage(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT),
1055                                  bChannelAccess.getSize().x(), bChannelAccess.getSize().y());
1056         tcu::TextureLevel aImage(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT),
1057                                  aChannelAccess.getSize().x(), aChannelAccess.getSize().y());
1058 
1059         for (int y = 0; y < (int)rChannelAccess.getSize().y(); y++)
1060             for (int x = 0; x < (int)rChannelAccess.getSize().x(); x++)
1061                 rImage.getAccess().setPixel(tcu::Vec4(rChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
1062 
1063         for (int y = 0; y < (int)gChannelAccess.getSize().y(); y++)
1064             for (int x = 0; x < (int)gChannelAccess.getSize().x(); x++)
1065                 gImage.getAccess().setPixel(tcu::Vec4(gChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
1066 
1067         for (int y = 0; y < (int)bChannelAccess.getSize().y(); y++)
1068             for (int x = 0; x < (int)bChannelAccess.getSize().x(); x++)
1069                 bImage.getAccess().setPixel(tcu::Vec4(bChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
1070 
1071         for (int y = 0; y < (int)aChannelAccess.getSize().y(); y++)
1072             for (int x = 0; x < (int)aChannelAccess.getSize().x(); x++)
1073                 aImage.getAccess().setPixel(tcu::Vec4(aChannelAccess.getChannel(tcu::IVec3(x, y, 0))), x, y);
1074 
1075         {
1076             const tcu::Vec4 scale(1.0f);
1077             const tcu::Vec4 bias(0.0f);
1078 
1079             log << tcu::TestLog::Image("SourceImageR", "SourceImageR", rImage.getAccess(), scale, bias);
1080             log << tcu::TestLog::Image("SourceImageG", "SourceImageG", gImage.getAccess(), scale, bias);
1081             log << tcu::TestLog::Image("SourceImageB", "SourceImageB", bImage.getAccess(), scale, bias);
1082             log << tcu::TestLog::Image("SourceImageA", "SourceImageA", aImage.getAccess(), scale, bias);
1083         }
1084     }
1085     else
1086     {
1087         tcu::TextureLevel ycbcrSrcImage(vk::mapVkFormat(config.format), size.x(), size.y());
1088 
1089         for (int y = 0; y < (int)size.y(); y++)
1090             for (int x = 0; x < (int)size.x(); x++)
1091             {
1092                 const tcu::IVec3 pos(x, y, 0);
1093                 ycbcrSrcImage.getAccess().setPixel(
1094                     tcu::Vec4(rChannelAccess.getChannel(pos), gChannelAccess.getChannel(pos),
1095                               bChannelAccess.getChannel(pos), aChannelAccess.getChannel(pos)),
1096                     x, y);
1097             }
1098 
1099         log << tcu::TestLog::Image("SourceImage", "SourceImage", ycbcrSrcImage.getAccess());
1100     }
1101 }
1102 
conversionTest(Context & context,TestConfig config)1103 tcu::TestStatus conversionTest(Context &context, TestConfig config)
1104 {
1105     std::vector<std::string> requiredDevExt;
1106     requiredDevExt.push_back("VK_KHR_sampler_ycbcr_conversion");
1107     requiredDevExt.push_back("VK_KHR_get_memory_requirements2");
1108     requiredDevExt.push_back("VK_KHR_bind_memory2");
1109     requiredDevExt.push_back("VK_KHR_maintenance1");
1110 
1111     const tcu::UVec2 size(ycbcr::isXChromaSubsampled(config.format) ? 12 : 7,
1112                           ycbcr::isYChromaSubsampled(config.format) ? 8 : 13);
1113 
1114     ProtectedContext ctx(context, std::vector<std::string>(), requiredDevExt);
1115     const vk::DeviceInterface &vk   = ctx.getDeviceInterface();
1116     const vk::VkDevice device       = ctx.getDevice();
1117     const uint32_t queueFamilyIndex = ctx.getQueueFamilyIndex();
1118 
1119     tcu::TestLog &log(context.getTestContext().getLog());
1120 
1121     validateFormatSupport(ctx, config);
1122     logTestCaseInfo(log, config);
1123 
1124     const vk::VkImageCreateFlagBits ycbcrImageFlags =
1125         config.disjoint ? vk::VK_IMAGE_CREATE_DISJOINT_BIT : (vk::VkImageCreateFlagBits)0u;
1126     const de::MovePtr<vk::YCbCrImageWithMemory> ycbcrImage(
1127         createYcbcrImage2D(ctx, PROTECTION_ENABLED, size.x(), size.y(), config.format, ycbcrImageFlags,
1128                            vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT | vk::VK_IMAGE_USAGE_SAMPLED_BIT));
1129     const vk::Unique<vk::VkSamplerYcbcrConversion> conversion(createConversion(
1130         vk, device, config.format, config.colorModel, config.colorRange, config.xChromaOffset, config.yChromaOffset,
1131         config.chromaFilter, config.componentMapping, config.explicitReconstruction));
1132     const vk::Unique<vk::VkSampler> ycbcrSampler(
1133         createSampler(vk, device, config.textureFilter, config.addressModeU, config.addressModeV, *conversion));
1134     const vk::Unique<vk::VkImageView> ycbcrImageView(
1135         createImageView(vk, device, **ycbcrImage, config.format, *conversion));
1136 
1137     uint32_t combinedSamplerDescriptorCount = 1;
1138     {
1139         const vk::VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {
1140             vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,            // sType
1141             DE_NULL,                                                              // pNext
1142             config.format,                                                        // format
1143             vk::VK_IMAGE_TYPE_2D,                                                 // type
1144             vk::VK_IMAGE_TILING_OPTIMAL,                                          // tiling
1145             vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT | vk::VK_IMAGE_USAGE_SAMPLED_BIT, // usage
1146             (vk::VkImageCreateFlags)ycbcrImageFlags                               // flags
1147         };
1148 
1149         vk::VkSamplerYcbcrConversionImageFormatProperties samplerYcbcrConversionImage = {};
1150         samplerYcbcrConversionImage.sType = vk::VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES;
1151         samplerYcbcrConversionImage.pNext = DE_NULL;
1152 
1153         vk::VkImageFormatProperties2 imageFormatProperties = {};
1154         imageFormatProperties.sType                        = vk::VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
1155         imageFormatProperties.pNext                        = &samplerYcbcrConversionImage;
1156 
1157         VK_CHECK(context.getInstanceInterface().getPhysicalDeviceImageFormatProperties2(
1158             context.getPhysicalDevice(), &imageFormatInfo, &imageFormatProperties));
1159         combinedSamplerDescriptorCount = samplerYcbcrConversionImage.combinedImageSamplerDescriptorCount;
1160     }
1161 
1162     // Input attributes
1163     std::vector<tcu::Vec2> texCoords;
1164     std::vector<tcu::Vec2> posCoords;
1165     genTexCoords(texCoords, size);
1166     posCoords = computeVertexPositions((uint32_t)texCoords.size(), size.cast<int>());
1167 
1168     // Input validation data
1169     std::vector<tcu::Vec4> ycbcrMinBounds;
1170     std::vector<tcu::Vec4> ycbcrMaxBounds;
1171 
1172     // Generate input ycbcr image and conversion reference
1173     {
1174         ycbcr::MultiPlaneImageData ycbcrSrc(config.format, size);
1175 
1176         generateYCbCrImage(ctx, config, size, texCoords, ycbcrSrc, ycbcrMinBounds, ycbcrMaxBounds);
1177         logBoundImages(log, size, ycbcrMinBounds, ycbcrMaxBounds);
1178         uploadYCbCrImage(ctx, **ycbcrImage, ycbcrSrc, vk::VK_ACCESS_SHADER_READ_BIT,
1179                          vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
1180     }
1181 
1182     // Build up the reference data structure
1183     DE_ASSERT(posCoords.size() == ycbcrMinBounds.size());
1184     DE_ASSERT(posCoords.size() == ycbcrMaxBounds.size());
1185     DE_ASSERT(texCoords.size() >= CHECK_SIZE);
1186     std::vector<YCbCrValidationData> referenceData;
1187     std::vector<YCbCrValidationData> colorReferenceData;
1188 
1189     for (uint32_t ndx = 0; ndx < texCoords.size(); ++ndx)
1190     {
1191         YCbCrValidationData data;
1192         data.coord    = texCoords[ndx].toWidth<4>();
1193         data.minBound = ycbcrMinBounds[ndx];
1194         data.maxBound = ycbcrMaxBounds[ndx];
1195 
1196         referenceData.push_back(data);
1197 
1198         YCbCrValidationData colorData;
1199         colorData.coord    = posCoords[ndx].toWidth<4>();
1200         colorData.minBound = tcu::Vec4(0.0f, 0.9f, 0.0f, 1.0f);
1201         colorData.maxBound = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
1202 
1203         colorReferenceData.push_back(colorData);
1204     }
1205 
1206     if (config.shaderType == glu::SHADERTYPE_VERTEX || config.shaderType == glu::SHADERTYPE_FRAGMENT)
1207     {
1208         const de::UniquePtr<vk::ImageWithMemory> colorImage(
1209             createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex, size.x(), size.y(), s_colorFormat,
1210                           vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_SAMPLED_BIT));
1211         const vk::Unique<vk::VkImageView> colorImageView(createImageView(ctx, **colorImage, s_colorFormat));
1212         const vk::Unique<vk::VkSampler> colorSampler(makeSampler(vk, device));
1213 
1214         renderYCbCrToColor(ctx, size, *ycbcrSampler, *ycbcrImageView, **colorImage, *colorImageView, referenceData,
1215                            posCoords, combinedSamplerDescriptorCount);
1216 
1217         if (!validateImage(ctx, colorReferenceData, *colorSampler, *colorImageView, combinedSamplerDescriptorCount))
1218             return tcu::TestStatus::fail("YCbCr image conversion via fragment shader failed");
1219     }
1220     else if (config.shaderType == glu::SHADERTYPE_COMPUTE)
1221     {
1222         if (!validateImage(ctx, referenceData, *ycbcrSampler, *ycbcrImageView, combinedSamplerDescriptorCount))
1223             return tcu::TestStatus::fail("YCbCr image conversion via compute shader failed");
1224     }
1225     else
1226     {
1227         TCU_THROW(NotSupportedError, "Unsupported shader test type");
1228     }
1229 
1230     return tcu::TestStatus::pass("YCbCr image conversion was OK");
1231 }
1232 
1233 } // namespace
1234 
createYCbCrConversionTests(tcu::TestContext & testCtx)1235 tcu::TestCaseGroup *createYCbCrConversionTests(tcu::TestContext &testCtx)
1236 {
1237     de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "ycbcr"));
1238 
1239     struct
1240     {
1241         const char *name;
1242         const glu::ShaderType type;
1243     } shaderTypes[] = {{"fragment", glu::SHADERTYPE_FRAGMENT}, {"compute", glu::SHADERTYPE_COMPUTE}};
1244 
1245     struct RangeNamePair
1246     {
1247         const char *name;
1248         vk::VkSamplerYcbcrRange value;
1249     };
1250     struct ChromaLocationNamePair
1251     {
1252         const char *name;
1253         vk::VkChromaLocation value;
1254     };
1255 
1256     const vk::VkComponentMapping identitySwizzle = {
1257         vk::VK_COMPONENT_SWIZZLE_IDENTITY, vk::VK_COMPONENT_SWIZZLE_IDENTITY, vk::VK_COMPONENT_SWIZZLE_IDENTITY,
1258         vk::VK_COMPONENT_SWIZZLE_IDENTITY};
1259 
1260     const RangeNamePair colorRanges[] = {{"itu_full", vk::VK_SAMPLER_YCBCR_RANGE_ITU_FULL},
1261                                          {"itu_narrow", vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW}};
1262 
1263     const ChromaLocationNamePair chromaLocations[] = {{"cosited", vk::VK_CHROMA_LOCATION_COSITED_EVEN},
1264                                                       {"midpoint", vk::VK_CHROMA_LOCATION_MIDPOINT}};
1265 
1266     const struct
1267     {
1268         const char *const name;
1269         const vk::VkSamplerYcbcrModelConversion value;
1270     } colorModels[] = {{"rgb_identity", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY},
1271                        {"ycbcr_identity", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY},
1272                        {"ycbcr_709", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709},
1273                        {"ycbcr_601", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601},
1274                        {"ycbcr_2020", vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020}};
1275 
1276     const struct
1277     {
1278         const char *name;
1279         vk::VkImageTiling value;
1280     } imageTilings[] = {{"tiling_linear", vk::VK_IMAGE_TILING_LINEAR}, {"tiling_optimal", vk::VK_IMAGE_TILING_OPTIMAL}};
1281 
1282     const uint32_t tilingNdx       = 1;
1283     const vk::VkImageTiling tiling = imageTilings[tilingNdx].value;
1284     const char *tilingName         = imageTilings[tilingNdx].name;
1285 
1286     const vk::VkFormat testFormats[] = {
1287         // noChromaSubsampledFormats
1288         vk::VK_FORMAT_R4G4B4A4_UNORM_PACK16,
1289         vk::VK_FORMAT_B4G4R4A4_UNORM_PACK16,
1290         vk::VK_FORMAT_R5G6B5_UNORM_PACK16,
1291         vk::VK_FORMAT_B5G6R5_UNORM_PACK16,
1292         vk::VK_FORMAT_R5G5B5A1_UNORM_PACK16,
1293         vk::VK_FORMAT_B5G5R5A1_UNORM_PACK16,
1294         vk::VK_FORMAT_A1R5G5B5_UNORM_PACK16,
1295         vk::VK_FORMAT_R8G8B8_UNORM,
1296         vk::VK_FORMAT_B8G8R8_UNORM,
1297         vk::VK_FORMAT_R8G8B8A8_UNORM,
1298         vk::VK_FORMAT_B8G8R8A8_UNORM,
1299         vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32,
1300         vk::VK_FORMAT_A2R10G10B10_UNORM_PACK32,
1301         vk::VK_FORMAT_A2B10G10R10_UNORM_PACK32,
1302         vk::VK_FORMAT_R16G16B16_UNORM,
1303         vk::VK_FORMAT_R16G16B16A16_UNORM,
1304         vk::VK_FORMAT_R10X6_UNORM_PACK16,
1305         vk::VK_FORMAT_R10X6G10X6_UNORM_2PACK16,
1306         vk::VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
1307         vk::VK_FORMAT_R12X4_UNORM_PACK16,
1308         vk::VK_FORMAT_R12X4G12X4_UNORM_2PACK16,
1309         vk::VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16,
1310         vk::VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM,
1311         vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16,
1312         vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16,
1313         vk::VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
1314 
1315         // xChromaSubsampledFormats
1316         vk::VK_FORMAT_G8B8G8R8_422_UNORM,
1317         vk::VK_FORMAT_B8G8R8G8_422_UNORM,
1318         vk::VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM,
1319         vk::VK_FORMAT_G8_B8R8_2PLANE_422_UNORM,
1320 
1321         vk::VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16,
1322         vk::VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16,
1323         vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16,
1324         vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16,
1325         vk::VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16,
1326         vk::VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16,
1327         vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16,
1328         vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16,
1329         vk::VK_FORMAT_G16B16G16R16_422_UNORM,
1330         vk::VK_FORMAT_B16G16R16G16_422_UNORM,
1331         vk::VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
1332         vk::VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
1333 
1334         // xyChromaSubsampledFormats
1335         vk::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
1336         vk::VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
1337         vk::VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16,
1338         vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
1339         vk::VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16,
1340         vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16,
1341         vk::VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM,
1342         vk::VK_FORMAT_G16_B16R16_2PLANE_420_UNORM,
1343 
1344         // Extended YCbCr formats
1345         vk::VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT,
1346         vk::VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT,
1347         vk::VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT,
1348         vk::VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT,
1349     };
1350 
1351     for (size_t formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(testFormats); formatNdx++)
1352     {
1353         const vk::VkFormat format(testFormats[formatNdx]);
1354         const std::string formatName(de::toLower(std::string(getFormatName(format)).substr(10)));
1355         de::MovePtr<tcu::TestCaseGroup> formatGroup(new tcu::TestCaseGroup(testCtx, formatName.c_str()));
1356 
1357         for (size_t shaderNdx = 0; shaderNdx < DE_LENGTH_OF_ARRAY(shaderTypes); shaderNdx++)
1358         {
1359             const char *shaderTypeName = shaderTypes[shaderNdx].name;
1360             // YCbCr conversion tests
1361             de::MovePtr<tcu::TestCaseGroup> shaderGroup(new tcu::TestCaseGroup(testCtx, shaderTypeName));
1362 
1363             for (size_t modelNdx = 0; modelNdx < DE_LENGTH_OF_ARRAY(colorModels); modelNdx++)
1364             {
1365                 const char *const colorModelName(colorModels[modelNdx].name);
1366                 const vk::VkSamplerYcbcrModelConversion colorModel(colorModels[modelNdx].value);
1367 
1368                 if (colorModel != vk::VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY &&
1369                     ycbcr::getYCbCrFormatChannelCount(format) < 3)
1370                     continue;
1371 
1372                 // YCbCr conversion tests
1373                 de::MovePtr<tcu::TestCaseGroup> colorModelGroup(new tcu::TestCaseGroup(testCtx, colorModelName));
1374 
1375                 for (size_t rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(colorRanges); rangeNdx++)
1376                 {
1377                     const char *const colorRangeName(colorRanges[rangeNdx].name);
1378                     const vk::VkSamplerYcbcrRange colorRange(colorRanges[rangeNdx].value);
1379 
1380                     // Narrow range doesn't really work with formats that have less than 8 bits
1381                     if (colorRange == vk::VK_SAMPLER_YCBCR_RANGE_ITU_NARROW)
1382                     {
1383                         const tcu::UVec4 bitDepth(ycbcr::getYCbCrBitDepth(format));
1384                         if (bitDepth[0] < 8 || bitDepth[1] < 8 || bitDepth[2] < 8)
1385                             continue;
1386                     }
1387 
1388                     de::MovePtr<tcu::TestCaseGroup> colorRangeGroup(new tcu::TestCaseGroup(testCtx, colorRangeName));
1389 
1390                     for (size_t chromaOffsetNdx = 0; chromaOffsetNdx < DE_LENGTH_OF_ARRAY(chromaLocations);
1391                          chromaOffsetNdx++)
1392                     {
1393                         const char *const chromaOffsetName(chromaLocations[chromaOffsetNdx].name);
1394                         const vk::VkChromaLocation chromaOffset(chromaLocations[chromaOffsetNdx].value);
1395 
1396                         for (uint32_t disjointNdx = 0; disjointNdx < 2; ++disjointNdx)
1397                         {
1398                             bool disjoint = (disjointNdx == 1);
1399                             const TestConfig config(shaderTypes[shaderNdx].type, format, tiling, vk::VK_FILTER_NEAREST,
1400                                                     vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1401                                                     vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, vk::VK_FILTER_NEAREST,
1402                                                     chromaOffset, chromaOffset, false, disjoint, colorRange, colorModel,
1403                                                     identitySwizzle);
1404 
1405                             addFunctionCaseWithPrograms(colorRangeGroup.get(),
1406                                                         std::string(tilingName) + "_" + chromaOffsetName +
1407                                                             (disjoint ? "_disjoint" : ""),
1408                                                         checkSupport, testShaders, conversionTest, config);
1409                         }
1410                     }
1411 
1412                     colorModelGroup->addChild(colorRangeGroup.release());
1413                 }
1414 
1415                 shaderGroup->addChild(colorModelGroup.release());
1416             }
1417 
1418             formatGroup->addChild(shaderGroup.release());
1419         }
1420         testGroup->addChild(formatGroup.release());
1421     }
1422 
1423     return testGroup.release();
1424 }
1425 
1426 } // namespace ProtectedMem
1427 } // namespace vkt
1428