xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/ycbcr/vktYCbCrStorageImageWriteTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2017 Google Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Testing compute shader writing to separate planes of a multiplanar format
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktYCbCrStorageImageWriteTests.hpp"
25 #include "vktTestCaseUtil.hpp"
26 #include "vktTestGroupUtil.hpp"
27 #include "vktYCbCrUtil.hpp"
28 #include "vkBuilderUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkCmdUtil.hpp"
31 #include "vkBarrierUtil.hpp"
32 #include "vkImageUtil.hpp"
33 #include "tcuTexVerifierUtil.hpp"
34 #include "vkTypeUtil.hpp"
35 #include "vkRefUtil.hpp"
36 #include "vkQueryUtil.hpp"
37 #include "tcuTestLog.hpp"
38 #include <cstdio>
39 
40 namespace vkt
41 {
42 namespace ycbcr
43 {
44 namespace
45 {
46 
47 using namespace vk;
48 
49 struct TestParameters
50 {
51     VkFormat format;
52     tcu::UVec3 size;
53     VkImageCreateFlags flags;
54 
TestParametersvkt::ycbcr::__anonf5c6a4150111::TestParameters55     TestParameters(VkFormat format_, const tcu::UVec3 &size_, VkImageCreateFlags flags_)
56         : format(format_)
57         , size(size_)
58         , flags(flags_)
59     {
60     }
61 
TestParametersvkt::ycbcr::__anonf5c6a4150111::TestParameters62     TestParameters(void) : format(VK_FORMAT_UNDEFINED), flags(0u)
63     {
64     }
65 };
66 
getPlaneCompatibleFormatForWriting(const vk::PlanarFormatDescription & formatInfo,uint32_t planeNdx)67 vk::VkFormat getPlaneCompatibleFormatForWriting(const vk::PlanarFormatDescription &formatInfo, uint32_t planeNdx)
68 {
69     DE_ASSERT(planeNdx < formatInfo.numPlanes);
70     vk::VkFormat result = formatInfo.planes[planeNdx].planeCompatibleFormat;
71 
72     // redirect result for some of the YCbCr image formats
73     static const std::pair<vk::VkFormat, vk::VkFormat> ycbcrFormats[] = {
74         {VK_FORMAT_G8B8G8R8_422_UNORM, VK_FORMAT_R8G8B8A8_UNORM},
75         {VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, VK_FORMAT_R16G16B16A16_UNORM},
76         {VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, VK_FORMAT_R16G16B16A16_UNORM},
77         {VK_FORMAT_G16B16G16R16_422_UNORM, VK_FORMAT_R16G16B16A16_UNORM},
78         {VK_FORMAT_B8G8R8G8_422_UNORM, VK_FORMAT_R8G8B8A8_UNORM},
79         {VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, VK_FORMAT_R16G16B16A16_UNORM},
80         {VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, VK_FORMAT_R16G16B16A16_UNORM},
81         {VK_FORMAT_B16G16R16G16_422_UNORM, VK_FORMAT_R16G16B16A16_UNORM}};
82     auto it = std::find_if(std::begin(ycbcrFormats), std::end(ycbcrFormats),
83                            [result](const std::pair<vk::VkFormat, vk::VkFormat> &p) { return p.first == result; });
84     if (it != std::end(ycbcrFormats))
85         result = it->second;
86     return result;
87 }
88 
checkSupport(Context & context,const TestParameters params)89 void checkSupport(Context &context, const TestParameters params)
90 {
91     const bool disjoint = (params.flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0;
92     const auto &instInt(context.getInstanceInterface());
93     std::vector<std::string> reqExts;
94 
95     if (disjoint)
96     {
97         if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_bind_memory2"))
98             reqExts.push_back("VK_KHR_bind_memory2");
99         if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_get_memory_requirements2"))
100             reqExts.push_back("VK_KHR_get_memory_requirements2");
101     }
102 
103     for (const auto &extIter : reqExts)
104     {
105         if (!context.isDeviceFunctionalitySupported(extIter))
106             TCU_THROW(NotSupportedError, (extIter + " is not supported").c_str());
107     }
108 
109     {
110         const VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {
111             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,        // sType;
112             DE_NULL,                                                      // pNext;
113             params.format,                                                // format;
114             VK_IMAGE_TYPE_2D,                                             // type;
115             VK_IMAGE_TILING_OPTIMAL,                                      // tiling;
116             VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, // usage;
117             (VkImageCreateFlags)0u                                        // flags
118         };
119 
120         VkSamplerYcbcrConversionImageFormatProperties samplerYcbcrConversionImage = {};
121         samplerYcbcrConversionImage.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES;
122         samplerYcbcrConversionImage.pNext = DE_NULL;
123 
124         VkImageFormatProperties2 imageFormatProperties = {};
125         imageFormatProperties.sType                    = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
126         imageFormatProperties.pNext                    = &samplerYcbcrConversionImage;
127 
128         VkResult result = instInt.getPhysicalDeviceImageFormatProperties2(context.getPhysicalDevice(), &imageFormatInfo,
129                                                                           &imageFormatProperties);
130         if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
131             TCU_THROW(NotSupportedError, "Format not supported.");
132         VK_CHECK(result);
133 
134         // Check for plane compatible format support when the disjoint flag is being used
135         if (disjoint)
136         {
137             const PlanarFormatDescription formatDescription = getPlanarFormatDescription(params.format);
138 
139             for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
140             {
141                 if (!formatDescription.hasChannelNdx(channelNdx))
142                     continue;
143                 uint32_t planeNdx                  = formatDescription.channels[channelNdx].planeNdx;
144                 vk::VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
145 
146                 const VkPhysicalDeviceImageFormatInfo2 planeImageFormatInfo = {
147                     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,        // sType;
148                     DE_NULL,                                                      // pNext;
149                     planeCompatibleFormat,                                        // format;
150                     VK_IMAGE_TYPE_2D,                                             // type;
151                     VK_IMAGE_TILING_OPTIMAL,                                      // tiling;
152                     VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, // usage;
153                     (VkImageCreateFlags)0u                                        // flags
154                 };
155 
156                 VkResult planesResult = instInt.getPhysicalDeviceImageFormatProperties2(
157                     context.getPhysicalDevice(), &planeImageFormatInfo, &imageFormatProperties);
158                 if (planesResult == VK_ERROR_FORMAT_NOT_SUPPORTED)
159                     TCU_THROW(NotSupportedError, "Plane compatibile format not supported.");
160                 VK_CHECK(planesResult);
161             }
162         }
163     }
164 
165     {
166         const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(
167             context.getInstanceInterface(), context.getPhysicalDevice(), params.format);
168 
169         const bool transferByViews = disjoint && (getPlanarFormatDescription(params.format).numPlanes > 1);
170 
171         if (!disjoint && (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
172             TCU_THROW(NotSupportedError, "Storage images are not supported for this format");
173 
174         if (disjoint && ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DISJOINT_BIT) == 0))
175             TCU_THROW(NotSupportedError, "Disjoint planes are not supported for this format");
176 
177         if (disjoint && transferByViews)
178         {
179             const PlanarFormatDescription formatDescription = getPlanarFormatDescription(params.format);
180             for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
181             {
182                 const VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
183                 const VkFormatProperties planeCompatibleFormatProperties = getPhysicalDeviceFormatProperties(
184                     context.getInstanceInterface(), context.getPhysicalDevice(), planeCompatibleFormat);
185 
186                 if ((planeCompatibleFormatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
187                     TCU_THROW(NotSupportedError, "Storage images are not supported for the plane compatible format");
188             }
189         }
190     }
191 }
192 
193 template <typename T>
makeVkSharedPtr(vk::Move<T> vkMove)194 inline de::SharedPtr<vk::Unique<T>> makeVkSharedPtr(vk::Move<T> vkMove)
195 {
196     return de::SharedPtr<vk::Unique<T>>(new vk::Unique<T>(vkMove));
197 }
198 
computeWorkGroupSize(const VkExtent3D & planeExtent)199 tcu::UVec3 computeWorkGroupSize(const VkExtent3D &planeExtent)
200 {
201     const uint32_t maxComputeWorkGroupInvocations = 128u;
202     const tcu::UVec3 maxComputeWorkGroupSize      = tcu::UVec3(128u, 128u, 64u);
203 
204     const uint32_t xWorkGroupSize =
205         std::min(std::min(planeExtent.width, maxComputeWorkGroupSize.x()), maxComputeWorkGroupInvocations);
206     const uint32_t yWorkGroupSize = std::min(std::min(planeExtent.height, maxComputeWorkGroupSize.y()),
207                                              maxComputeWorkGroupInvocations / xWorkGroupSize);
208     const uint32_t zWorkGroupSize = std::min(std::min(planeExtent.depth, maxComputeWorkGroupSize.z()),
209                                              maxComputeWorkGroupInvocations / (xWorkGroupSize * yWorkGroupSize));
210 
211     return tcu::UVec3(xWorkGroupSize, yWorkGroupSize, zWorkGroupSize);
212 }
213 
testStorageImageWrite(Context & context,TestParameters params)214 tcu::TestStatus testStorageImageWrite(Context &context, TestParameters params)
215 {
216     const DeviceInterface &vkd                      = context.getDeviceInterface();
217     const VkDevice device                           = context.getDevice();
218     const uint32_t queueFamilyIndex                 = context.getUniversalQueueFamilyIndex();
219     const VkQueue queue                             = context.getUniversalQueue();
220     const PlanarFormatDescription formatDescription = getPlanarFormatDescription(params.format);
221     const bool disjoint                             = (params.flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0;
222     const bool transferByViews                      = disjoint && (formatDescription.numPlanes > 1);
223 
224     VkImageCreateInfo imageCreateInfo = {
225         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
226         DE_NULL,
227         params.flags,
228         VK_IMAGE_TYPE_2D,
229         params.format,
230         makeExtent3D(params.size.x(), params.size.y(), params.size.z()),
231         1u, // mipLevels
232         1u, // arrayLayers
233         VK_SAMPLE_COUNT_1_BIT,
234         VK_IMAGE_TILING_OPTIMAL,
235         VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
236         VK_SHARING_MODE_EXCLUSIVE,
237         0u,
238         (const uint32_t *)DE_NULL,
239         VK_IMAGE_LAYOUT_UNDEFINED,
240     };
241 
242     // check if we need to create VkImageView with different VkFormat than VkImage format
243     VkFormat planeCompatibleFormat0 = getPlaneCompatibleFormatForWriting(formatDescription, 0);
244     if (planeCompatibleFormat0 != params.format)
245     {
246         imageCreateInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
247     }
248 
249     const Unique<VkImage> image(createImage(vkd, device, &imageCreateInfo));
250     // allocate memory for the whole image, or for each separate plane ( if the params.flags include VK_IMAGE_CREATE_DISJOINT_BIT )
251     const std::vector<AllocationSp> allocations(allocateAndBindImageMemory(
252         vkd, device, context.getDefaultAllocator(), *image, params.format, params.flags, MemoryRequirement::Any));
253 
254     // Create descriptor set layout
255     const Unique<VkDescriptorSetLayout> descriptorSetLayout(
256         DescriptorSetLayoutBuilder()
257             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
258             .build(vkd, device));
259     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vkd, device, *descriptorSetLayout));
260 
261     // Create descriptor sets
262     const Unique<VkDescriptorPool> descriptorPool(
263         DescriptorPoolBuilder()
264             .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::PlanarFormatDescription::MAX_PLANES)
265             .build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
266                    vk::PlanarFormatDescription::MAX_PLANES));
267 
268     // Create command buffer for compute and transfer operations
269     const Unique<VkCommandPool> commandPool(makeCommandPool(vkd, device, queueFamilyIndex));
270     const Unique<VkCommandBuffer> commandBuffer(
271         allocateCommandBuffer(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
272 
273     std::vector<de::SharedPtr<vk::Unique<vk::VkShaderModule>>> shaderModules;
274     std::vector<de::SharedPtr<vk::Unique<vk::VkPipeline>>> computePipelines;
275     std::vector<de::SharedPtr<vk::Unique<vk::VkDescriptorSet>>> descriptorSets;
276     std::vector<de::SharedPtr<vk::Unique<vk::VkImageView>>> imageViews;
277 
278     uint32_t imageSizeInBytes = 0;
279     uint32_t planeOffsets[PlanarFormatDescription::MAX_PLANES];
280     uint32_t planeRowPitches[PlanarFormatDescription::MAX_PLANES];
281     void *planePointers[PlanarFormatDescription::MAX_PLANES];
282 
283     {
284         // Start recording commands
285         beginCommandBuffer(vkd, *commandBuffer);
286 
287         for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
288         {
289             const VkImageAspectFlags aspect =
290                 (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
291             const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(aspect, 0u, 1u, 0u, 1u);
292             VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
293             vk::PlanarFormatDescription compatibleFormatDescription =
294                 (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ?
295                     getPlanarFormatDescription(planeCompatibleFormat) :
296                     formatDescription;
297             const tcu::UVec3 compatibleShaderGridSize(params.size.x() / formatDescription.blockWidth,
298                                                       params.size.y() / formatDescription.blockHeight,
299                                                       params.size.z() / 1u);
300             VkExtent3D shaderExtent = getPlaneExtent(
301                 compatibleFormatDescription,
302                 VkExtent3D{compatibleShaderGridSize.x(), compatibleShaderGridSize.y(), compatibleShaderGridSize.z()},
303                 planeNdx, 0u);
304 
305             // Create and bind compute pipeline
306             std::ostringstream shaderName;
307             shaderName << "comp" << planeNdx;
308             auto shaderModule = makeVkSharedPtr(
309                 createShaderModule(vkd, device, context.getBinaryCollection().get(shaderName.str()), DE_NULL));
310             shaderModules.push_back(shaderModule);
311             auto computePipeline = makeVkSharedPtr(
312                 makeComputePipeline(vkd, device, *pipelineLayout, (VkPipelineCreateFlags)0u, nullptr,
313                                     shaderModule->get(), (VkPipelineShaderStageCreateFlags)0u, DE_NULL));
314             computePipelines.push_back(computePipeline);
315             vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline->get());
316 
317             auto descriptorSet = makeVkSharedPtr(makeDescriptorSet(vkd, device, *descriptorPool, *descriptorSetLayout));
318             descriptorSets.push_back(descriptorSet);
319 
320             VkImageViewUsageCreateInfo imageViewUsageCreateInfo = {
321                 VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, //VkStructureType sType;
322                 DE_NULL,                                        //const void* pNext;
323                 VK_IMAGE_USAGE_STORAGE_BIT,                     //VkImageUsageFlags usage;
324             };
325 
326             auto imageView =
327                 makeVkSharedPtr(makeImageView(vkd, device, *image, VK_IMAGE_VIEW_TYPE_2D, planeCompatibleFormat,
328                                               subresourceRange, transferByViews ? &imageViewUsageCreateInfo : DE_NULL));
329             imageViews.push_back(imageView);
330             const VkDescriptorImageInfo imageInfo =
331                 makeDescriptorImageInfo(DE_NULL, imageView->get(), VK_IMAGE_LAYOUT_GENERAL);
332 
333             DescriptorSetUpdateBuilder()
334                 .writeSingle(descriptorSet->get(), DescriptorSetUpdateBuilder::Location::binding(0u),
335                              VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageInfo)
336                 .update(vkd, device);
337 
338             vkd.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
339                                       &descriptorSet->get(), 0u, DE_NULL);
340 
341             {
342                 const VkImageMemoryBarrier imageLayoutChangeBarrier = makeImageMemoryBarrier(
343                     0u, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, *image,
344                     subresourceRange, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED);
345                 vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
346                                        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
347                                        &imageLayoutChangeBarrier);
348             }
349 
350             {
351                 const tcu::UVec3 workGroupSize = computeWorkGroupSize(shaderExtent);
352 
353                 const uint32_t xWorkGroupCount =
354                     shaderExtent.width / workGroupSize.x() + (shaderExtent.width % workGroupSize.x() ? 1u : 0u);
355                 const uint32_t yWorkGroupCount =
356                     shaderExtent.height / workGroupSize.y() + (shaderExtent.height % workGroupSize.y() ? 1u : 0u);
357                 const uint32_t zWorkGroupCount =
358                     shaderExtent.depth / workGroupSize.z() + (shaderExtent.depth % workGroupSize.z() ? 1u : 0u);
359 
360                 const tcu::UVec3 maxComputeWorkGroupCount = tcu::UVec3(65535u, 65535u, 65535u);
361 
362                 if (maxComputeWorkGroupCount.x() < xWorkGroupCount || maxComputeWorkGroupCount.y() < yWorkGroupCount ||
363                     maxComputeWorkGroupCount.z() < zWorkGroupCount)
364                 {
365                     TCU_THROW(NotSupportedError, "Image size is not supported");
366                 }
367 
368                 vkd.cmdDispatch(*commandBuffer, xWorkGroupCount, yWorkGroupCount, zWorkGroupCount);
369             }
370 
371             {
372                 const VkImageMemoryBarrier imageTransferBarrier = makeImageMemoryBarrier(
373                     VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL,
374                     VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *image, subresourceRange);
375                 vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
376                                        VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
377                                        &imageTransferBarrier);
378             }
379         }
380 
381         for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
382         {
383             planeOffsets[planeNdx] = imageSizeInBytes;
384             const uint32_t planeW  = imageCreateInfo.extent.width /
385                                     (formatDescription.blockWidth * formatDescription.planes[planeNdx].widthDivisor);
386             planeRowPitches[planeNdx] = formatDescription.planes[planeNdx].elementSizeBytes * planeW;
387             imageSizeInBytes +=
388                 getPlaneSizeInBytes(formatDescription, makeExtent3D(params.size.x(), params.size.y(), params.size.z()),
389                                     planeNdx, 0u, BUFFER_IMAGE_COPY_OFFSET_GRANULARITY);
390         }
391 
392         const VkBufferCreateInfo outputBufferCreateInfo =
393             makeBufferCreateInfo(imageSizeInBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
394         const Unique<VkBuffer> outputBuffer(createBuffer(vkd, device, &outputBufferCreateInfo));
395         const de::UniquePtr<Allocation> outputBufferAlloc(
396             bindBuffer(vkd, device, context.getDefaultAllocator(), *outputBuffer, MemoryRequirement::HostVisible));
397         std::vector<VkBufferImageCopy> bufferImageCopy(formatDescription.numPlanes);
398 
399         for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
400         {
401             const VkImageAspectFlags aspect =
402                 (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
403 
404             bufferImageCopy[planeNdx] = {
405                 planeOffsets[planeNdx],                         // VkDeviceSize bufferOffset;
406                 0u,                                             // uint32_t bufferRowLength;
407                 0u,                                             // uint32_t bufferImageHeight;
408                 makeImageSubresourceLayers(aspect, 0u, 0u, 1u), // VkImageSubresourceLayers imageSubresource;
409                 makeOffset3D(0, 0, 0),                          // VkOffset3D imageOffset;
410                 getPlaneExtent(formatDescription, makeExtent3D(params.size.x(), params.size.y(), params.size.z()),
411                                planeNdx, 0u) // VkExtent3D imageExtent;
412             };
413         }
414         vkd.cmdCopyImageToBuffer(*commandBuffer, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *outputBuffer,
415                                  static_cast<uint32_t>(bufferImageCopy.size()), bufferImageCopy.data());
416 
417         {
418             const VkBufferMemoryBarrier outputBufferHostReadBarrier = makeBufferMemoryBarrier(
419                 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *outputBuffer, 0u, imageSizeInBytes);
420 
421             vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u,
422                                    DE_NULL, 1u, &outputBufferHostReadBarrier, 0u, DE_NULL);
423         }
424 
425         // End recording commands
426         endCommandBuffer(vkd, *commandBuffer);
427 
428         // Submit commands for execution and wait for completion
429         submitCommandsAndWait(vkd, device, queue, *commandBuffer);
430 
431         // Retrieve data from buffer to host memory
432         invalidateAlloc(vkd, device, *outputBufferAlloc);
433         uint8_t *outputData = static_cast<uint8_t *>(outputBufferAlloc->getHostPtr());
434 
435         for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
436             planePointers[planeNdx] = outputData + static_cast<size_t>(planeOffsets[planeNdx]);
437 
438         // write result images to log file
439         for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
440         {
441             if (!formatDescription.hasChannelNdx(channelNdx))
442                 continue;
443             uint32_t planeNdx                  = formatDescription.channels[channelNdx].planeNdx;
444             vk::VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
445             vk::PlanarFormatDescription compatibleFormatDescription =
446                 (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ?
447                     getPlanarFormatDescription(planeCompatibleFormat) :
448                     formatDescription;
449             const tcu::UVec3 compatibleShaderGridSize(params.size.x() / formatDescription.blockWidth,
450                                                       params.size.y() / formatDescription.blockHeight,
451                                                       params.size.z() / 1u);
452             tcu::ConstPixelBufferAccess pixelBuffer =
453                 vk::getChannelAccess(compatibleFormatDescription, compatibleShaderGridSize, planeRowPitches,
454                                      (const void *const *)planePointers, channelNdx);
455             std::ostringstream str;
456             str << "image" << channelNdx;
457             context.getTestContext().getLog() << tcu::LogImage(str.str(), str.str(), pixelBuffer);
458         }
459 
460         // verify data
461         const float epsilon = 1e-5f;
462         for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
463         {
464             if (!formatDescription.hasChannelNdx(channelNdx))
465                 continue;
466 
467             uint32_t planeNdx                  = formatDescription.channels[channelNdx].planeNdx;
468             vk::VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
469             vk::PlanarFormatDescription compatibleFormatDescription =
470                 (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ?
471                     getPlanarFormatDescription(planeCompatibleFormat) :
472                     formatDescription;
473             const tcu::UVec3 compatibleShaderGridSize(params.size.x() / formatDescription.blockWidth,
474                                                       params.size.y() / formatDescription.blockHeight,
475                                                       params.size.z() / 1u);
476             VkExtent3D compatibleImageSize{imageCreateInfo.extent.width / formatDescription.blockWidth,
477                                            imageCreateInfo.extent.height / formatDescription.blockHeight,
478                                            imageCreateInfo.extent.depth / 1u};
479             tcu::ConstPixelBufferAccess pixelBuffer =
480                 vk::getChannelAccess(compatibleFormatDescription, compatibleShaderGridSize, planeRowPitches,
481                                      (const void *const *)planePointers, channelNdx);
482             VkExtent3D planeExtent  = getPlaneExtent(compatibleFormatDescription, compatibleImageSize, planeNdx, 0u);
483             tcu::IVec3 pixelDivider = pixelBuffer.getDivider();
484 
485             for (uint32_t offsetZ = 0u; offsetZ < planeExtent.depth; ++offsetZ)
486                 for (uint32_t offsetY = 0u; offsetY < planeExtent.height; ++offsetY)
487                     for (uint32_t offsetX = 0u; offsetX < planeExtent.width; ++offsetX)
488                     {
489                         uint32_t iReferenceValue;
490                         float fReferenceValue;
491                         switch (channelNdx)
492                         {
493                         case 0:
494                             iReferenceValue = offsetX % 127u;
495                             fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
496                             break;
497                         case 1:
498                             iReferenceValue = offsetY % 127u;
499                             fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
500                             break;
501                         case 2:
502                             iReferenceValue = offsetZ % 127u;
503                             fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
504                             break;
505                         case 3:
506                             iReferenceValue = 1u;
507                             fReferenceValue = 1.f;
508                             break;
509                         default:
510                             DE_FATAL("Unexpected channel index");
511                             break;
512                         }
513                         float acceptableError = epsilon;
514 
515                         switch (formatDescription.channels[channelNdx].type)
516                         {
517                         case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
518                         case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
519                         {
520                             tcu::UVec4 outputValue =
521                                 pixelBuffer.getPixelUint(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
522 
523                             if (outputValue.x() != iReferenceValue)
524                                 return tcu::TestStatus::fail("Failed");
525 
526                             break;
527                         }
528                         case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
529                         case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
530                         {
531                             float fixedPointError = tcu::TexVerifierUtil::computeFixedPointError(
532                                 formatDescription.channels[channelNdx].sizeBits);
533                             acceptableError += fixedPointError;
534                             tcu::Vec4 outputValue =
535                                 pixelBuffer.getPixel(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
536 
537                             if (deAbs(outputValue.x() - fReferenceValue) > acceptableError)
538                                 return tcu::TestStatus::fail("Failed");
539 
540                             break;
541                         }
542                         case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
543                         {
544                             const tcu::Vec4 outputValue =
545                                 pixelBuffer.getPixel(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
546 
547                             if (deAbs(outputValue.x() - fReferenceValue) > acceptableError)
548                                 return tcu::TestStatus::fail("Failed");
549 
550                             break;
551                         }
552                         default:
553                             DE_FATAL("Unexpected channel type");
554                             break;
555                         }
556                     }
557         }
558     }
559     return tcu::TestStatus::pass("Passed");
560 }
561 
getShaderImageType(const vk::PlanarFormatDescription & description)562 std::string getShaderImageType(const vk::PlanarFormatDescription &description)
563 {
564     std::string formatPart;
565 
566     // all PlanarFormatDescription types have at least one channel ( 0 ) and all channel types are the same :
567     switch (description.channels[0].type)
568     {
569     case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
570         formatPart = "i";
571         break;
572     case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
573         formatPart = "u";
574         break;
575     case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
576     case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
577     case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
578         break;
579 
580     default:
581         DE_FATAL("Unexpected channel type");
582     }
583 
584     return formatPart + "image2D";
585 }
586 
getShaderImageDataType(const vk::PlanarFormatDescription & description)587 std::string getShaderImageDataType(const vk::PlanarFormatDescription &description)
588 {
589     switch (description.channels[0].type)
590     {
591     case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
592         return "uvec4";
593     case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
594         return "ivec4";
595     case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
596     case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
597     case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
598         return "vec4";
599     default:
600         DE_FATAL("Unexpected channel type");
601         return "";
602     }
603 }
604 
getFormatValueString(const std::vector<std::pair<uint32_t,uint32_t>> & channelsOnPlane,const std::vector<std::string> & formatValueStrings)605 std::string getFormatValueString(const std::vector<std::pair<uint32_t, uint32_t>> &channelsOnPlane,
606                                  const std::vector<std::string> &formatValueStrings)
607 {
608     std::string result = "( ";
609     uint32_t i;
610     for (i = 0; i < channelsOnPlane.size(); ++i)
611     {
612         result += formatValueStrings[channelsOnPlane[i].first];
613         if (i < 3)
614             result += ", ";
615     }
616     for (; i < 4; ++i)
617     {
618         result += "0";
619         if (i < 3)
620             result += ", ";
621     }
622     result += " )";
623     return result;
624 }
625 
getShaderImageFormatQualifier(VkFormat format)626 std::string getShaderImageFormatQualifier(VkFormat format)
627 {
628     switch (format)
629     {
630     case VK_FORMAT_R8_SINT:
631         return "r8i";
632     case VK_FORMAT_R16_SINT:
633         return "r16i";
634     case VK_FORMAT_R32_SINT:
635         return "r32i";
636     case VK_FORMAT_R8_UINT:
637         return "r8ui";
638     case VK_FORMAT_R16_UINT:
639         return "r16ui";
640     case VK_FORMAT_R32_UINT:
641         return "r32ui";
642     case VK_FORMAT_R8_SNORM:
643         return "r8_snorm";
644     case VK_FORMAT_R16_SNORM:
645         return "r16_snorm";
646     case VK_FORMAT_R8_UNORM:
647         return "r8";
648     case VK_FORMAT_R16_UNORM:
649         return "r16";
650 
651     case VK_FORMAT_R8G8_SINT:
652         return "rg8i";
653     case VK_FORMAT_R16G16_SINT:
654         return "rg16i";
655     case VK_FORMAT_R32G32_SINT:
656         return "rg32i";
657     case VK_FORMAT_R8G8_UINT:
658         return "rg8ui";
659     case VK_FORMAT_R16G16_UINT:
660         return "rg16ui";
661     case VK_FORMAT_R32G32_UINT:
662         return "rg32ui";
663     case VK_FORMAT_R8G8_SNORM:
664         return "rg8_snorm";
665     case VK_FORMAT_R16G16_SNORM:
666         return "rg16_snorm";
667     case VK_FORMAT_R8G8_UNORM:
668         return "rg8";
669     case VK_FORMAT_R16G16_UNORM:
670         return "rg16";
671 
672     case VK_FORMAT_R8G8B8A8_SINT:
673         return "rgba8i";
674     case VK_FORMAT_R16G16B16A16_SINT:
675         return "rgba16i";
676     case VK_FORMAT_R32G32B32A32_SINT:
677         return "rgba32i";
678     case VK_FORMAT_R8G8B8A8_UINT:
679         return "rgba8ui";
680     case VK_FORMAT_R16G16B16A16_UINT:
681         return "rgba16ui";
682     case VK_FORMAT_R32G32B32A32_UINT:
683         return "rgba32ui";
684     case VK_FORMAT_R8G8B8A8_SNORM:
685         return "rgba8_snorm";
686     case VK_FORMAT_R16G16B16A16_SNORM:
687         return "rgba16_snorm";
688     case VK_FORMAT_R8G8B8A8_UNORM:
689         return "rgba8";
690     case VK_FORMAT_R16G16B16A16_UNORM:
691         return "rgba16";
692 
693     case VK_FORMAT_G8B8G8R8_422_UNORM:
694         return "rgba8";
695     case VK_FORMAT_B8G8R8G8_422_UNORM:
696         return "rgba8";
697     case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
698         return "rgba8";
699     case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
700         return "rgba8";
701     case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
702         return "rgba8";
703     case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
704         return "rgba8";
705     case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
706         return "rgba8";
707     case VK_FORMAT_R10X6_UNORM_PACK16:
708         return "r16";
709     case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
710         return "rg16";
711     case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
712         return "rgba16";
713     case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
714         return "rgba16";
715     case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
716         return "rgba16";
717     case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
718         return "rgba16";
719     case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
720         return "rgba16";
721     case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
722         return "rgba16";
723     case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
724         return "rgba16";
725     case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
726         return "rgba16";
727     case VK_FORMAT_R12X4_UNORM_PACK16:
728         return "r16";
729     case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
730         return "rg16";
731     case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
732         return "rgba16";
733     case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
734         return "rgba16";
735     case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
736         return "rgba16";
737     case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
738         return "rgba16";
739     case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
740         return "rgba16";
741     case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
742         return "rgba16";
743     case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
744         return "rgba16";
745     case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
746         return "rgba16";
747     case VK_FORMAT_G16B16G16R16_422_UNORM:
748         return "rgba16";
749     case VK_FORMAT_B16G16R16G16_422_UNORM:
750         return "rgba16";
751     case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
752         return "rgba16";
753     case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
754         return "rgba16";
755     case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
756         return "rgba16";
757     case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
758         return "rgba16";
759     case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
760         return "rgba16";
761     case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT:
762         return "rgba8";
763     case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT:
764         return "rgba16";
765     case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT:
766         return "rgba16";
767     case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT:
768         return "rgba16";
769 
770     default:
771         DE_FATAL("Unexpected texture format");
772         return "error";
773     }
774 }
775 
initPrograms(SourceCollections & sourceCollections,TestParameters params)776 void initPrograms(SourceCollections &sourceCollections, TestParameters params)
777 {
778     // Create compute program
779     const char *const versionDecl                   = glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440);
780     const PlanarFormatDescription formatDescription = getPlanarFormatDescription(params.format);
781     const std::string imageTypeStr                  = getShaderImageType(formatDescription);
782     const std::string formatDataStr                 = getShaderImageDataType(formatDescription);
783     const tcu::UVec3 shaderGridSize(params.size.x(), params.size.y(), params.size.z());
784 
785     std::vector<std::string> formatValueStrings;
786     switch (formatDescription.channels[0].type)
787     {
788     case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
789     case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
790         formatValueStrings = {"int(gl_GlobalInvocationID.x) % 127", "int(gl_GlobalInvocationID.y) % 127",
791                               "int(gl_GlobalInvocationID.z) % 127", "1"};
792         break;
793     case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
794     case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
795     case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
796         formatValueStrings = {"float(int(gl_GlobalInvocationID.x) % 127) / 127.0",
797                               "float(int(gl_GlobalInvocationID.y) % 127) / 127.0",
798                               "float(int(gl_GlobalInvocationID.z) % 127) / 127.0", "1.0"};
799         break;
800     default:
801         DE_ASSERT(false);
802         break;
803     }
804 
805     for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
806     {
807         VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
808         vk::PlanarFormatDescription compatibleFormatDescription =
809             (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ?
810                 getPlanarFormatDescription(planeCompatibleFormat) :
811                 formatDescription;
812         VkExtent3D compatibleShaderGridSize{shaderGridSize.x() / formatDescription.blockWidth,
813                                             shaderGridSize.y() / formatDescription.blockHeight,
814                                             shaderGridSize.z() / 1u};
815 
816         std::vector<std::pair<uint32_t, uint32_t>> channelsOnPlane;
817         for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
818         {
819             if (!formatDescription.hasChannelNdx(channelNdx))
820                 continue;
821             if (formatDescription.channels[channelNdx].planeNdx != planeNdx)
822                 continue;
823             channelsOnPlane.push_back({channelNdx, formatDescription.channels[channelNdx].offsetBits});
824         }
825         // reorder channels for multi-planar images
826         if (formatDescription.numPlanes > 1)
827             std::sort(begin(channelsOnPlane), end(channelsOnPlane),
828                       [](const std::pair<uint32_t, uint32_t> &lhs, const std::pair<uint32_t, uint32_t> &rhs)
829                       { return lhs.second < rhs.second; });
830         std::string formatValueStr = getFormatValueString(channelsOnPlane, formatValueStrings);
831         VkExtent3D shaderExtent    = getPlaneExtent(compatibleFormatDescription, compatibleShaderGridSize, planeNdx, 0);
832         const std::string formatQualifierStr =
833             getShaderImageFormatQualifier(formatDescription.planes[planeNdx].planeCompatibleFormat);
834         const tcu::UVec3 workGroupSize = computeWorkGroupSize(shaderExtent);
835 
836         std::ostringstream src;
837         src << versionDecl << "\n"
838             << "layout (local_size_x = " << workGroupSize.x() << ", local_size_y = " << workGroupSize.y()
839             << ", local_size_z = " << workGroupSize.z() << ") in; \n"
840             << "layout (binding = 0, " << formatQualifierStr << ") writeonly uniform highp " << imageTypeStr
841             << " u_image;\n"
842             << "void main (void)\n"
843             << "{\n"
844             << "    if( gl_GlobalInvocationID.x < " << shaderExtent.width << " ) \n"
845             << "    if( gl_GlobalInvocationID.y < " << shaderExtent.height << " ) \n"
846             << "    if( gl_GlobalInvocationID.z < " << shaderExtent.depth << " ) \n"
847             << "    {\n"
848             << "        imageStore(u_image, ivec2( gl_GlobalInvocationID.x, gl_GlobalInvocationID.y ) ,"
849             << formatDataStr << formatValueStr << ");\n"
850             << "    }\n"
851             << "}\n";
852         std::ostringstream shaderName;
853         shaderName << "comp" << planeNdx;
854         sourceCollections.glslSources.add(shaderName.str()) << glu::ComputeSource(src.str());
855     }
856 }
857 
populateStorageImageWriteFormatGroup(tcu::TestContext & testCtx,de::MovePtr<tcu::TestCaseGroup> testGroup)858 tcu::TestCaseGroup *populateStorageImageWriteFormatGroup(tcu::TestContext &testCtx,
859                                                          de::MovePtr<tcu::TestCaseGroup> testGroup)
860 {
861     const std::vector<tcu::UVec3> availableSizes{tcu::UVec3(512u, 512u, 1u), tcu::UVec3(1024u, 128u, 1u),
862                                                  tcu::UVec3(66u, 32u, 1u)};
863 
864     auto addTests = [&](int formatNdx)
865     {
866         const VkFormat format         = (VkFormat)formatNdx;
867         tcu::UVec3 imageSizeAlignment = getImageSizeAlignment(format);
868         std::string formatName        = de::toLower(de::toString(format).substr(10));
869         de::MovePtr<tcu::TestCaseGroup> formatGroup(new tcu::TestCaseGroup(testCtx, formatName.c_str()));
870 
871         for (size_t sizeNdx = 0; sizeNdx < availableSizes.size(); sizeNdx++)
872         {
873             const tcu::UVec3 imageSize = availableSizes[sizeNdx];
874 
875             // skip test for images with odd sizes for some YCbCr formats
876             if ((imageSize.x() % imageSizeAlignment.x()) != 0)
877                 continue;
878             if ((imageSize.y() % imageSizeAlignment.y()) != 0)
879                 continue;
880 
881             std::ostringstream stream;
882             stream << imageSize.x() << "_" << imageSize.y() << "_" << imageSize.z();
883             de::MovePtr<tcu::TestCaseGroup> sizeGroup(new tcu::TestCaseGroup(testCtx, stream.str().c_str()));
884 
885             addFunctionCaseWithPrograms(sizeGroup.get(), "joint", checkSupport, initPrograms, testStorageImageWrite,
886                                         TestParameters(format, imageSize, 0u));
887             addFunctionCaseWithPrograms(sizeGroup.get(), "disjoint", checkSupport, initPrograms, testStorageImageWrite,
888                                         TestParameters(format, imageSize,
889                                                        (VkImageCreateFlags)(VK_IMAGE_CREATE_DISJOINT_BIT |
890                                                                             VK_IMAGE_CREATE_EXTENDED_USAGE_BIT)));
891 
892             formatGroup->addChild(sizeGroup.release());
893         }
894         testGroup->addChild(formatGroup.release());
895     };
896 
897     for (int formatNdx = VK_YCBCR_FORMAT_FIRST; formatNdx < VK_YCBCR_FORMAT_LAST; formatNdx++)
898     {
899         addTests(formatNdx);
900     }
901 
902     for (int formatNdx = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT; formatNdx <= VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT;
903          formatNdx++)
904     {
905         addTests(formatNdx);
906     }
907 
908     return testGroup.release();
909 }
910 
911 } // namespace
912 
createStorageImageWriteTests(tcu::TestContext & testCtx)913 tcu::TestCaseGroup *createStorageImageWriteTests(tcu::TestContext &testCtx)
914 {
915     de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "storage_image_write"));
916     return populateStorageImageWriteFormatGroup(testCtx, testGroup);
917 }
918 
919 } // namespace ycbcr
920 } // namespace vkt
921