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 ¶meters)
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 ®ion, 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, ©Info);
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 ®ion = 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, ©Info);
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 ®ion : regions)
435 {
436 vk.cmdCopyBufferToImage(*cmdBuffer, **srcBuffer, **image, imageLayout, 1u, ®ion);
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, ®ion);
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 ¶meters)
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