• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2024 Google LLC.
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 Tests multiple image copies without barriers
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktImageConcurrentCopyTests.hpp"
25 
26 #include "vkRefUtil.hpp"
27 #include "vkCmdUtil.hpp"
28 #include "vkImageUtil.hpp"
29 #include "deRandom.hpp"
30 #include "ycbcr/vktYCbCrUtil.hpp"
31 #include "vkObjUtil.hpp"
32 #include "vkMemUtil.hpp"
33 #include "vkBarrierUtil.hpp"
34 #include "deThread.hpp"
35 #include "deSharedPtr.hpp"
36 #include "vkRef.hpp"
37 
38 #include <set>
39 #include <algorithm>
40 
41 namespace vkt
42 {
43 namespace image
44 {
45 namespace
46 {
47 
48 struct TestParameters
49 {
50     vk::VkFormat format;
51     vk::VkImageTiling tiling;
52     vk::VkImageType type;
53     bool hostCopy;
54     bool read;
55     bool singleCommand;
56     bool randomData;
57 };
58 
59 class ConcurrentCopyTestInstance : public vkt::TestInstance
60 {
61 public:
ConcurrentCopyTestInstance(vkt::Context & context,const TestParameters & parameters)62     ConcurrentCopyTestInstance(vkt::Context &context, const TestParameters &parameters)
63         : vkt::TestInstance(context)
64         , m_parameters(parameters)
65     {
66     }
67 
68 private:
69     tcu::TestStatus iterate(void);
70 
71     const TestParameters m_parameters;
72 };
73 
74 #ifndef CTS_USES_VULKANSC
75 class HostCopyThread : public de::Thread
76 {
77 public:
HostCopyThread(const vk::DeviceInterface & vk,const vk::VkDevice device,const vk::VkImage image,const vk::VkImageLayout imageLayout,const vk::VkMemoryToImageCopyEXT & region,const bool read,const uint32_t pixelSize)78     HostCopyThread(const vk::DeviceInterface &vk, const vk::VkDevice device, const vk::VkImage image,
79                    const vk::VkImageLayout imageLayout, const vk::VkMemoryToImageCopyEXT &region, const bool read,
80                    const uint32_t pixelSize)
81         : de::Thread()
82         , m_vk(vk)
83         , m_device(device)
84         , m_image(image)
85         , m_imageLayout(imageLayout)
86         , m_region(region)
87         , m_read(read)
88         , m_pixelSize(pixelSize)
89         , m_failed(false)
90     {
91     }
~HostCopyThread(void)92     virtual ~HostCopyThread(void)
93     {
94     }
95 
run()96     virtual void run()
97     {
98         vk::VkCopyMemoryToImageInfoEXT copyInfo = {
99             vk::VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT, // VkStructureType sType;
100             nullptr,                                             // const void* pNext;
101             0u,                                                  // VkHostImageCopyFlagsEXT flags;
102             m_image,                                             // VkImage dstImage;
103             m_imageLayout,                                       // VkImageLayout dstImageLayout;
104             1u,                                                  // uint32_t regionCount;
105             &m_region,                                           // const VkMemoryToImageCopyEXT* pRegions;
106         };
107         m_vk.copyMemoryToImage(m_device, &copyInfo);
108 
109         if (m_read)
110         {
111             std::vector<uint8_t> data(m_region.imageExtent.width * m_region.imageExtent.height *
112                                       m_region.imageExtent.depth * m_pixelSize);
113 
114             vk::VkImageToMemoryCopyEXT readRegion = {
115                 vk::VK_STRUCTURE_TYPE_IMAGE_TO_MEMORY_COPY_EXT, // VkStructureType sType;
116                 nullptr,                                        // const void* pNext;
117                 data.data(),                                    // void* pHostPointer;
118                 0u,                                             // uint32_t memoryRowLength;
119                 0u,                                             // uint32_t memoryImageHeight;
120                 m_region.imageSubresource,                      // VkImageSubresourceLayers imageSubresource;
121                 m_region.imageOffset,                           // VkOffset3D imageOffset;
122                 m_region.imageExtent,                           // VkExtent3D imageExtent;
123             };
124             vk::VkCopyImageToMemoryInfoEXT readInfo = {
125                 vk::VK_STRUCTURE_TYPE_COPY_IMAGE_TO_MEMORY_INFO_EXT, // VkStructureType sType;
126                 nullptr,                                             // const void* pNext;
127                 0u,                                                  // VkHostImageCopyFlagsEXT flags;
128                 m_image,                                             // VkImage srcImage;
129                 m_imageLayout,                                       // VkImageLayout srcImageLayout;
130                 1u,                                                  // uint32_t regionCount;
131                 &readRegion,                                         // const VkImageToMemoryCopyEXT* pRegions;
132             };
133 
134             m_vk.copyImageToMemory(m_device, &readInfo);
135 
136             const uint32_t rowLength = m_region.imageExtent.width * m_pixelSize;
137             for (uint32_t k = 0; k < m_region.imageExtent.depth; ++k)
138             {
139                 for (uint32_t j = 0; j < m_region.imageExtent.height; ++j)
140                 {
141                     const uint32_t srcOffset =
142                         (m_region.memoryRowLength * j + m_region.memoryRowLength * m_region.memoryImageHeight * k) *
143                         m_pixelSize;
144                     void *src                = &((uint8_t *)m_region.pHostPointer)[srcOffset];
145                     const uint32_t dstOffset = (m_region.imageExtent.width * j +
146                                                 m_region.imageExtent.width * m_region.imageExtent.height * k) *
147                                                m_pixelSize;
148                     void *dst = &data[dstOffset];
149                     if (memcmp(src, dst, rowLength) != 0)
150                     {
151                         m_failed = true;
152                     }
153                 }
154             }
155         }
156     }
157 
hasFailed() const158     bool hasFailed() const
159     {
160         return m_failed;
161     }
162 
163 private:
164     const vk::DeviceInterface &m_vk;
165     const vk::VkDevice m_device;
166     const vk::VkImage m_image;
167     const vk::VkImageLayout m_imageLayout;
168     const vk::VkMemoryToImageCopyEXT m_region;
169     const bool m_read;
170     const uint32_t m_pixelSize;
171     bool m_failed;
172 };
173 #endif
174 
splitRegion(de::Random & randomGen,uint32_t size,std::vector<uint32_t> & output)175 void splitRegion(de::Random &randomGen, uint32_t size, std::vector<uint32_t> &output)
176 {
177     uint32_t pos = 0u;
178     while (pos < size)
179     {
180         uint32_t current = randomGen.getUint32() % 32u + 1u;
181         if (current + pos > size)
182         {
183             current = size - pos;
184         }
185         output.push_back(current);
186         pos += current;
187     }
188 }
189 
iterate(void)190 tcu::TestStatus ConcurrentCopyTestInstance::iterate(void)
191 {
192     const vk::DeviceInterface &vk   = m_context.getDeviceInterface();
193     const vk::VkDevice device       = m_context.getDevice();
194     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
195     const vk::VkQueue queue         = m_context.getUniversalQueue();
196     auto &alloc                     = m_context.getDefaultAllocator();
197     tcu::TestLog &log               = m_context.getTestContext().getLog();
198 
199     const vk::Move<vk::VkCommandPool> cmdPool(
200         createCommandPool(vk, device, vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
201     const vk::Move<vk::VkCommandBuffer> cmdBuffer(
202         allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
203 
204     const uint32_t width  = 256u;
205     const uint32_t height = 256u;
206     const uint32_t depth  = m_parameters.type == vk::VK_IMAGE_TYPE_3D ? 32u : 1u;
207 
208     const vk::VkImageLayout imageLayout =
209         m_parameters.read ? vk::VK_IMAGE_LAYOUT_GENERAL : vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
210 
211     const vk::VkImageSubresourceRange subresourceRange =
212         makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
213 
214     const uint32_t pixelSize  = tcu::getPixelSize(vk::mapVkFormat(m_parameters.format));
215     const uint32_t bufferSize = width * height * depth * pixelSize;
216     std::vector<uint8_t> testData(bufferSize);
217     de::Random randomGen(deInt32Hash((uint32_t)m_parameters.format) ^ deInt32Hash((uint32_t)bufferSize));
218     if (m_parameters.randomData)
219     {
220         ycbcr::fillRandomNoNaN(&randomGen, testData.data(), bufferSize, m_parameters.format);
221     }
222     else
223     {
224         for (uint32_t i = 0; i < width; ++i)
225         {
226             for (uint32_t j = 0; j < height; ++j)
227             {
228                 for (uint32_t k = 0; k < depth; ++k)
229                 {
230                     uint32_t p = i + j * width + k * width * height;
231                     uint32_t v = de::max(de::max(i, j), k);
232                     if (pixelSize == 1)
233                         testData[p] = uint8_t(v % 256);
234                     else if (pixelSize == 2)
235                         ((uint16_t *)testData.data())[i] = uint16_t(v);
236                     else
237                     {
238                         for (uint32_t l = 0; l < pixelSize / 4; ++l)
239                         {
240                             ((uint32_t *)testData.data())[i * pixelSize / 4 + l] = v + l;
241                         }
242                     }
243                 }
244             }
245         }
246     }
247 
248     de::MovePtr<vk::BufferWithMemory> srcBuffer = de::MovePtr<vk::BufferWithMemory>(new vk::BufferWithMemory(
249         vk, device, alloc, makeBufferCreateInfo(bufferSize, vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT),
250         vk::MemoryRequirement::HostVisible));
251 
252     de::MovePtr<vk::BufferWithMemory> dstBuffer = de::MovePtr<vk::BufferWithMemory>(new vk::BufferWithMemory(
253         vk, device, alloc, makeBufferCreateInfo(bufferSize, vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT),
254         vk::MemoryRequirement::HostVisible));
255 
256     auto &srcBufferAlloc = srcBuffer->getAllocation();
257     memcpy(srcBufferAlloc.getHostPtr(), testData.data(), bufferSize);
258     flushAlloc(vk, device, srcBufferAlloc);
259 
260     vk::VkImageUsageFlags usage = vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT;
261 #ifndef CTS_USES_VULKANSC
262     if (m_parameters.hostCopy)
263         usage |= vk::VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
264 #endif
265 
266     vk::VkImageCreateInfo imageCreateInfo = {
267         vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType          sType
268         nullptr,                                 // const void*              pNext
269         0u,                                      // VkImageCreateFlags       flags
270         m_parameters.type,                       // VkImageType              imageType
271         m_parameters.format,                     // VkFormat                 format
272         {width, height, depth},                  // VkExtent3D               extent
273         1u,                                      // uint32_t                 mipLevels
274         1u,                                      // uint32_t                 arrayLayers
275         vk::VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits    samples
276         m_parameters.tiling,                     // VkImageTiling            tiling
277         usage,                                   // VkImageUsageFlags        usage
278         vk::VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode            sharingMode
279         0,                                       // uint32_t                 queueFamilyIndexCount
280         nullptr,                                 // const uint32_t*          pQueueFamilyIndices
281         vk::VK_IMAGE_LAYOUT_UNDEFINED            // VkImageLayout            initialLayout
282     };
283 
284     de::MovePtr<vk::ImageWithMemory> image = de::MovePtr<vk::ImageWithMemory>(
285         new vk::ImageWithMemory(vk, device, alloc, imageCreateInfo, vk::MemoryRequirement::Any));
286 
287     if (m_parameters.hostCopy)
288     {
289 #ifndef CTS_USES_VULKANSC
290         vk::VkHostImageLayoutTransitionInfoEXT transition = {
291             vk::VK_STRUCTURE_TYPE_HOST_IMAGE_LAYOUT_TRANSITION_INFO_EXT, // VkStructureType sType;
292             nullptr,                                                     // const void* pNext;
293             **image,                                                     // VkImage image;
294             vk::VK_IMAGE_LAYOUT_UNDEFINED,                               // VkImageLayout oldLayout;
295             imageLayout,                                                 // VkImageLayout newLayout;
296             subresourceRange,                                            // VkImageSubresourceRange subresourceRange;
297         };
298         vk.transitionImageLayout(device, 1u, &transition);
299 #endif
300     }
301     else
302     {
303         m_context.resetCommandPoolForVKSC(device, *cmdPool);
304         vk::beginCommandBuffer(vk, *cmdBuffer);
305         auto preImageMemoryBarrier =
306             makeImageMemoryBarrier(0u, vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
307                                    **image, subresourceRange);
308         vk.cmdPipelineBarrier(*cmdBuffer, vk::VK_PIPELINE_STAGE_NONE_KHR, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u,
309                               nullptr, 0u, nullptr, 1, &preImageMemoryBarrier);
310         vk::endCommandBuffer(vk, *cmdBuffer);
311         vk::submitCommandsAndWait(vk, device, queue, *cmdBuffer);
312     }
313 
314     std::vector<uint32_t> widths;
315     splitRegion(randomGen, width, widths);
316     std::vector<uint32_t> heights;
317     splitRegion(randomGen, height, heights);
318     std::vector<uint32_t> depths;
319     if (m_parameters.type == vk::VK_IMAGE_TYPE_2D)
320         depths.push_back(1u);
321     else
322         splitRegion(randomGen, depth, depths);
323 
324     std::vector<vk::VkBufferImageCopy> regions;
325     int posWidth = 0u;
326     for (const auto w : widths)
327     {
328         int posHeight = 0u;
329         for (const auto h : heights)
330         {
331             int posDepth = 0u;
332             for (const auto d : depths)
333             {
334                 vk::VkBufferImageCopy region;
335                 region.bufferOffset       = (width * height * posDepth + width * posHeight + posWidth) * pixelSize;
336                 region.bufferRowLength    = width;
337                 region.bufferImageHeight  = height;
338                 region.imageSubresource   = {vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u};
339                 region.imageOffset        = {posWidth, posHeight, posDepth};
340                 region.imageExtent.width  = w;
341                 region.imageExtent.height = h;
342                 region.imageExtent.depth  = d;
343                 regions.push_back(region);
344 
345                 posDepth += d;
346             }
347             posHeight += h;
348         }
349         posWidth += w;
350     }
351 
352     if (m_parameters.hostCopy)
353     {
354 #ifndef CTS_USES_VULKANSC
355 
356         std::vector<vk::VkMemoryToImageCopyEXT> memoryToImageCopies;
357         for (uint32_t i = 0; i < (uint32_t)regions.size(); ++i)
358         {
359             const auto &region = regions[i];
360             void *hostPointer  = (uint8_t *)srcBufferAlloc.getHostPtr() + region.bufferOffset;
361 
362             vk::VkMemoryToImageCopyEXT regionInfo = {
363                 vk::VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT, // VkStructureType sType;
364                 nullptr,                                        // const void* pNext;
365                 hostPointer,                                    // const void* pHostPointer;
366                 region.bufferRowLength,                         // uint32_t     memoryRowLength;
367                 region.bufferImageHeight,                       // uint32_t     memoryImageHeight;
368                 region.imageSubresource,                        // VkImageSubresourceLayers     imageSubresource;
369                 region.imageOffset,                             // VkOffset3D imageOffset;
370                 region.imageExtent,                             // VkExtent3D imageExtent;
371             };
372 
373             memoryToImageCopies.push_back(regionInfo);
374         }
375 
376         if (m_parameters.singleCommand)
377         {
378             vk::VkCopyMemoryToImageInfoEXT copyInfo = {
379                 vk::VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT, // VkStructureType sType;
380                 nullptr,                                             // const void* pNext;
381                 0u,                                                  // VkHostImageCopyFlagsEXT flags;
382                 **image,                                             // VkImage dstImage;
383                 imageLayout,                                         // VkImageLayout dstImageLayout;
384                 (uint32_t)memoryToImageCopies.size(),                // uint32_t regionCount;
385                 memoryToImageCopies.data(),                          // const VkMemoryToImageCopyEXT* pRegions;
386             };
387             vk.copyMemoryToImage(device, &copyInfo);
388         }
389         else
390         {
391             const uint32_t batch_size  = 256;
392             const uint32_t num_batches = ((uint32_t)(memoryToImageCopies.size()) / batch_size) + 1;
393 
394             for (uint32_t batch = 0; batch < num_batches; ++batch)
395             {
396                 std::vector<de::SharedPtr<HostCopyThread>> threads;
397                 const uint32_t from = batch * batch_size;
398                 const uint32_t to   = std::min((batch + 1) * batch_size, (uint32_t)memoryToImageCopies.size());
399 
400                 for (uint32_t i = from; i < to; ++i)
401                 {
402                     threads.push_back(de::SharedPtr<HostCopyThread>(new HostCopyThread(
403                         vk, device, **image, imageLayout, memoryToImageCopies[i], m_parameters.read, pixelSize)));
404                 }
405 
406                 for (auto &thread : threads)
407                     thread->start();
408 
409                 for (auto &thread : threads)
410                     thread->join();
411 
412                 for (const auto &thread : threads)
413                 {
414                     if (thread->hasFailed())
415                     {
416                         return tcu::TestStatus::fail("Fail");
417                     }
418                 }
419             }
420         }
421 #endif
422     }
423     else
424     {
425         m_context.resetCommandPoolForVKSC(device, *cmdPool);
426         vk::beginCommandBuffer(vk, *cmdBuffer);
427         if (m_parameters.singleCommand)
428         {
429             vk.cmdCopyBufferToImage(*cmdBuffer, **srcBuffer, **image, imageLayout, (uint32_t)regions.size(),
430                                     regions.data());
431         }
432         else
433         {
434             for (const auto &region : regions)
435             {
436                 vk.cmdCopyBufferToImage(*cmdBuffer, **srcBuffer, **image, imageLayout, 1u, &region);
437             }
438         }
439         vk::endCommandBuffer(vk, *cmdBuffer);
440         vk::submitCommandsAndWait(vk, device, queue, *cmdBuffer);
441     }
442 
443     m_context.resetCommandPoolForVKSC(device, *cmdPool);
444     vk::beginCommandBuffer(vk, *cmdBuffer);
445     auto postImageMemoryBarrier =
446         makeImageMemoryBarrier(vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_ACCESS_TRANSFER_READ_BIT, imageLayout,
447                                vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **image, subresourceRange);
448     vk.cmdPipelineBarrier(*cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u,
449                           nullptr, 0u, nullptr, 1, &postImageMemoryBarrier);
450     vk::VkBufferImageCopy region;
451     region.bufferOffset      = 0u;
452     region.bufferRowLength   = 0u;
453     region.bufferImageHeight = 0u;
454     region.imageSubresource  = {vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u};
455     region.imageOffset       = {0, 0, 0};
456     region.imageExtent       = {width, height, depth};
457     vk.cmdCopyImageToBuffer(*cmdBuffer, **image, vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **dstBuffer, 1u, &region);
458     vk::endCommandBuffer(vk, *cmdBuffer);
459     vk::submitCommandsAndWait(vk, device, queue, *cmdBuffer);
460 
461     auto &dstBufferAlloc = dstBuffer->getAllocation();
462     if (memcmp(srcBufferAlloc.getHostPtr(), dstBufferAlloc.getHostPtr(), bufferSize) != 0)
463     {
464         uint8_t *srcPtr = (uint8_t *)srcBufferAlloc.getHostPtr();
465         uint8_t *dstPtr = (uint8_t *)dstBufferAlloc.getHostPtr();
466         for (uint32_t i = 0; i < bufferSize; ++i)
467         {
468             if (srcPtr[i] != dstPtr[i])
469             {
470                 log << tcu::TestLog::Message << "Mismatch at byte " << i << ". Src value: " << srcPtr[i]
471                     << ", dst value: " << dstPtr[i] << "." << tcu::TestLog::EndMessage;
472             }
473         }
474         return tcu::TestStatus::fail("Fail");
475     }
476 
477     return tcu::TestStatus::pass("Pass");
478 }
479 
480 class ConcurrentCopyTestCase : public vkt::TestCase
481 {
482 public:
ConcurrentCopyTestCase(tcu::TestContext & context,const char * name,const TestParameters & parameters)483     ConcurrentCopyTestCase(tcu::TestContext &context, const char *name, const TestParameters &parameters)
484         : TestCase(context, name)
485         , m_parameters(parameters)
486     {
487     }
488 
489 private:
createInstance(vkt::Context & context) const490     vkt::TestInstance *createInstance(vkt::Context &context) const
491     {
492         return new ConcurrentCopyTestInstance(context, m_parameters);
493     }
494     void checkSupport(vkt::Context &context) const;
495 
496     const TestParameters m_parameters;
497 };
498 
checkSupport(vkt::Context & context) const499 void ConcurrentCopyTestCase::checkSupport(vkt::Context &context) const
500 {
501     const auto &vki           = context.getInstanceInterface();
502     const auto physicalDevice = context.getPhysicalDevice();
503 
504     vk::VkImageUsageFlags usage = vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT;
505 #ifndef CTS_USES_VULKANSC
506     if (m_parameters.hostCopy)
507         usage |= vk::VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
508 #endif
509 
510     vk::VkImageFormatProperties imageFormatProperties;
511     const auto result = vki.getPhysicalDeviceImageFormatProperties(
512         physicalDevice, m_parameters.format, m_parameters.type, m_parameters.tiling, usage, 0, &imageFormatProperties);
513 
514     if (result != vk::VK_SUCCESS)
515     {
516         TCU_THROW(NotSupportedError, "Format unsupported");
517     }
518 
519 #ifndef CTS_USES_VULKANSC
520     if (m_parameters.hostCopy)
521     {
522         context.requireDeviceFunctionality("VK_EXT_host_image_copy");
523 
524         const vk::VkImageLayout requiredDstLayout =
525             m_parameters.read ? vk::VK_IMAGE_LAYOUT_GENERAL : vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
526 
527         vk::VkPhysicalDeviceHostImageCopyProperties hostImageCopyProperties = vk::initVulkanStructure();
528         vk::VkPhysicalDeviceProperties2 properties2 = vk::initVulkanStructure(&hostImageCopyProperties);
529         vki.getPhysicalDeviceProperties2(physicalDevice, &properties2);
530         std::vector<vk::VkImageLayout> srcLayouts(hostImageCopyProperties.copySrcLayoutCount);
531         std::vector<vk::VkImageLayout> dstLayouts(hostImageCopyProperties.copyDstLayoutCount);
532         hostImageCopyProperties.pCopySrcLayouts = srcLayouts.data();
533         hostImageCopyProperties.pCopyDstLayouts = dstLayouts.data();
534         vki.getPhysicalDeviceProperties2(physicalDevice, &properties2);
535         bool hasRequiredLayout = false;
536         for (const auto &dstLayout : dstLayouts)
537         {
538             if (dstLayout == requiredDstLayout)
539             {
540                 hasRequiredLayout = true;
541                 break;
542             }
543         }
544         if (!hasRequiredLayout)
545         {
546             TCU_THROW(NotSupportedError, "Required layout not supported in "
547                                          "VkPhysicalDeviceHostImageCopyPropertiesEXT::pCopyDstLayouts");
548         }
549     }
550 #endif
551 }
552 
553 } // namespace
554 
createImageConcurrentCopyTests(tcu::TestContext & testCtx)555 tcu::TestCaseGroup *createImageConcurrentCopyTests(tcu::TestContext &testCtx)
556 {
557     de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "concurrent_copy"));
558 
559     const std::set<vk::VkFormat> formats{
560         vk::VK_FORMAT_R8G8B8A8_UNORM,
561         vk::VK_FORMAT_R8_UNORM,
562         vk::VK_FORMAT_R32G32_SFLOAT,
563     };
564 
565     const std::set<vk::VkImageTiling> tilings{
566         vk::VK_IMAGE_TILING_LINEAR,
567         vk::VK_IMAGE_TILING_OPTIMAL,
568     };
569 
570     const std::set<vk::VkImageType> types{
571         vk::VK_IMAGE_TYPE_2D,
572         vk::VK_IMAGE_TYPE_3D,
573     };
574 
575     constexpr struct CopyType
576     {
577         bool hostCopy;
578         const char *name;
579     } copyTypes[] = {
580         {false, "device"},
581 #ifndef CTS_USES_VULKANSC
582         {true, "host"},
583 #endif
584     };
585 
586     constexpr struct AccessType
587     {
588         bool read;
589         const char *name;
590     } accessTypes[] = {
591         {false, "write"},
592 #ifndef CTS_USES_VULKANSC
593         {true, "read_and_write"},
594 #endif
595     };
596 
597     constexpr struct CommandType
598     {
599         bool singleCommand;
600         const char *name;
601     } commandTypes[] = {
602         {true, "single"},
603         {false, "multiple"},
604     };
605 
606     constexpr struct DataType
607     {
608         bool random;
609         const char *name;
610     } dataTypes[] = {
611         {true, "random"},
612         {false, "gradient"},
613     };
614 
615     for (const auto format : formats)
616     {
617         de::MovePtr<tcu::TestCaseGroup> formatGroup(
618             new tcu::TestCaseGroup(testCtx, de::toLower(vk::getFormatName(format)).c_str()));
619         for (const auto tiling : tilings)
620         {
621             de::MovePtr<tcu::TestCaseGroup> tilingGroup(
622                 new tcu::TestCaseGroup(testCtx, de::toLower(vk::getImageTilingName(tiling)).c_str()));
623             for (const auto type : types)
624             {
625                 de::MovePtr<tcu::TestCaseGroup> typeGroup(
626                     new tcu::TestCaseGroup(testCtx, de::toLower(vk::getImageTypeName(type)).c_str()));
627                 for (const auto commandType : commandTypes)
628                 {
629                     de::MovePtr<tcu::TestCaseGroup> commandTypeGroup(new tcu::TestCaseGroup(testCtx, commandType.name));
630                     for (const auto dataType : dataTypes)
631                     {
632                         de::MovePtr<tcu::TestCaseGroup> dataTypeGroup(new tcu::TestCaseGroup(testCtx, dataType.name));
633                         for (const auto copyType : copyTypes)
634                         {
635                             de::MovePtr<tcu::TestCaseGroup> copyTypeGroup(
636                                 new tcu::TestCaseGroup(testCtx, copyType.name));
637                             for (const auto accessType : accessTypes)
638                             {
639                                 if (accessType.read && !copyType.hostCopy)
640                                     continue;
641 
642                                 TestParameters parameters;
643                                 parameters.format        = format;
644                                 parameters.tiling        = tiling;
645                                 parameters.type          = type;
646                                 parameters.hostCopy      = copyType.hostCopy;
647                                 parameters.read          = accessType.read;
648                                 parameters.singleCommand = commandType.singleCommand;
649                                 parameters.randomData    = dataType.random;
650 
651                                 copyTypeGroup->addChild(
652                                     new ConcurrentCopyTestCase(testCtx, accessType.name, parameters));
653                             }
654                             dataTypeGroup->addChild(copyTypeGroup.release());
655                         }
656                         commandTypeGroup->addChild(dataTypeGroup.release());
657                     }
658                     typeGroup->addChild(commandTypeGroup.release());
659                 }
660                 tilingGroup->addChild(typeGroup.release());
661             }
662             formatGroup->addChild(tilingGroup.release());
663         }
664         testGroup->addChild(formatGroup.release());
665     }
666 
667     return testGroup.release();
668 }
669 
670 } // namespace image
671 } // namespace vkt
672