• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2023 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file  vktSparseResourcesRebind.cpp
21  * \brief Sparse image rebind tests
22  *
23  * Summary of the test:
24  *
25  * Creates a sparse buffer and two backing device memory objects.
26  * 1) Binds the first memory fully to the image and fill it with data.
27  * 2) Binds the second memory fully (this unbinds the first memory) to the image and fill it with different data.
28  * 3) Rebinds one block from the first memory back into one layer and at non 0, 0 offset.
29  * 4) Copies data out of sparse image into host accessible buffer.
30  * 5) Verifies if the data in the host accesible buffer is correct.
31  *
32  * For example, 2D image with VK_FORMAT_R16G16B16A16_UNORM, 2 layers, dimensions 512x256, and the block size of 256x128, the final layout will be:
33  *
34  *  Layer 1, 512x256
35  * +-----------------------+
36  * | memory 2   256        |-+
37  * |           +-----------+ |
38  * |       128 | memory 1  | |
39  * +-----------+-----------+ |
40  *   | memory 2              |
41  *   +-----------------------+
42  *    Layer 0
43  *
44  *//*--------------------------------------------------------------------*/
45 
46 #include "vktSparseResourcesImageRebind.hpp"
47 #include "deDefs.h"
48 #include "vktSparseResourcesTestsUtil.hpp"
49 #include "vktSparseResourcesBase.hpp"
50 #include "vktTestCaseUtil.hpp"
51 
52 #include "vkDefs.hpp"
53 #include "vkRef.hpp"
54 #include "vkRefUtil.hpp"
55 #include "vkPlatform.hpp"
56 #include "vkPrograms.hpp"
57 #include "vkRefUtil.hpp"
58 #include "vkMemUtil.hpp"
59 #include "vkBarrierUtil.hpp"
60 #include "vkQueryUtil.hpp"
61 #include "vkBuilderUtil.hpp"
62 #include "vkTypeUtil.hpp"
63 #include "vkCmdUtil.hpp"
64 #include "vkObjUtil.hpp"
65 #include "vkImageUtil.hpp"
66 
67 #include "deStringUtil.hpp"
68 #include "deUniquePtr.hpp"
69 #include "deSharedPtr.hpp"
70 
71 #include "tcuTexture.hpp"
72 #include "tcuTextureUtil.hpp"
73 #include "tcuTexVerifierUtil.hpp"
74 
75 #include <cassert>
76 #include <deMath.h>
77 #include <string>
78 #include <vector>
79 
80 using namespace vk;
81 
82 namespace vkt
83 {
84 namespace sparse
85 {
86 namespace
87 {
88 
89 constexpr int32_t kMemoryObjectCount = 2;
90 
91 class ImageSparseRebindCase : public TestCase
92 {
93 public:
94     ImageSparseRebindCase(tcu::TestContext &testCtx, const std::string &name, const ImageType imageType,
95                           const tcu::UVec3 &imageSize, const VkFormat format, const bool useDeviceGroups);
96 
97     TestInstance *createInstance(Context &context) const;
98     virtual void checkSupport(Context &context) const;
99 
100 private:
101     const bool m_useDeviceGroups;
102     const ImageType m_imageType;
103     const tcu::UVec3 m_imageSize;
104     const VkFormat m_format;
105 };
106 
ImageSparseRebindCase(tcu::TestContext & testCtx,const std::string & name,const ImageType imageType,const tcu::UVec3 & imageSize,const VkFormat format,const bool useDeviceGroups)107 ImageSparseRebindCase::ImageSparseRebindCase(tcu::TestContext &testCtx, const std::string &name,
108                                              const ImageType imageType, const tcu::UVec3 &imageSize,
109                                              const VkFormat format, const bool useDeviceGroups)
110     : TestCase(testCtx, name)
111     , m_useDeviceGroups(useDeviceGroups)
112     , m_imageType(imageType)
113     , m_imageSize(imageSize)
114     , m_format(format)
115 {
116 }
117 
checkSupport(Context & context) const118 void ImageSparseRebindCase::checkSupport(Context &context) const
119 {
120     const InstanceInterface &instance     = context.getInstanceInterface();
121     const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
122 
123     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_RESIDENCY_ALIASED);
124 
125     // Check if image size does not exceed device limits
126     if (!isImageSizeSupported(instance, physicalDevice, m_imageType, m_imageSize))
127         TCU_THROW(NotSupportedError, "Image size not supported for device");
128 
129     // Check if device supports sparse operations for image type
130     if (!checkSparseSupportForImageType(instance, physicalDevice, m_imageType))
131         TCU_THROW(NotSupportedError, "Sparse residency for image type is not supported");
132 }
133 
134 class ImageSparseRebindInstance : public SparseResourcesBaseInstance
135 {
136 public:
137     ImageSparseRebindInstance(Context &context, const ImageType imageType, const tcu::UVec3 &imageSize,
138                               const VkFormat format, const bool useDeviceGroups);
139 
140     tcu::TestStatus iterate(void);
141 
142 private:
143     const bool m_useDeviceGroups;
144     const ImageType m_imageType;
145     const tcu::UVec3 m_imageSize;
146     const VkFormat m_format;
147 
148     VkClearColorValue getColorClearValue(uint32_t memoryIdx);
149 };
150 
ImageSparseRebindInstance(Context & context,const ImageType imageType,const tcu::UVec3 & imageSize,const VkFormat format,const bool useDeviceGroups)151 ImageSparseRebindInstance::ImageSparseRebindInstance(Context &context, const ImageType imageType,
152                                                      const tcu::UVec3 &imageSize, const VkFormat format,
153                                                      const bool useDeviceGroups)
154     : SparseResourcesBaseInstance(context, useDeviceGroups)
155     , m_useDeviceGroups(useDeviceGroups)
156     , m_imageType(imageType)
157     , m_imageSize(imageSize)
158     , m_format(format)
159 {
160 }
161 
getColorClearValue(uint32_t memoryIdx)162 VkClearColorValue ImageSparseRebindInstance::getColorClearValue(uint32_t memoryIdx)
163 {
164     uint32_t startI[kMemoryObjectCount] = {7, 13};
165     uint32_t startU[kMemoryObjectCount] = {53u, 61u};
166     float startF[kMemoryObjectCount]    = {1.0f, 0.5f};
167     VkClearColorValue result            = {};
168 
169     if (isIntFormat(m_format))
170     {
171         for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
172         {
173             result.int32[channelNdx] = startI[memoryIdx] * static_cast<int32_t>(channelNdx + 1) * (-1 * channelNdx % 2);
174         }
175     }
176     else if (isUintFormat(m_format))
177     {
178         for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
179         {
180             result.uint32[channelNdx] = startU[memoryIdx] * (channelNdx + 1);
181         }
182     }
183     else
184     {
185         for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
186         {
187             result.float32[channelNdx] = startF[memoryIdx] - (0.1f * static_cast<float>(channelNdx));
188         }
189     }
190 
191     return result;
192 }
193 
iterate(void)194 tcu::TestStatus ImageSparseRebindInstance::iterate(void)
195 {
196     const float epsilon               = 1e-5f;
197     const InstanceInterface &instance = m_context.getInstanceInterface();
198 
199     {
200         // Create logical device supporting both sparse and transfer queues
201         QueueRequirementsVec queueRequirements;
202         queueRequirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u));
203         queueRequirements.push_back(QueueRequirements(VK_QUEUE_TRANSFER_BIT, 1u));
204 
205         createDeviceSupportingQueues(queueRequirements);
206     }
207 
208     const VkPhysicalDevice physicalDevice = getPhysicalDevice();
209     VkImageCreateInfo imageSparseInfo;
210     std::vector<DeviceMemorySp> deviceMemUniquePtrVec;
211 
212     //vsk getting queues should be outside the loop
213     //see these in all image files
214 
215     const DeviceInterface &deviceInterface          = getDeviceInterface();
216     const Queue &sparseQueue                        = getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0);
217     const Queue &transferQueue                      = getQueue(VK_QUEUE_TRANSFER_BIT, 0);
218     const PlanarFormatDescription formatDescription = getPlanarFormatDescription(m_format);
219 
220     // Go through all physical devices
221     for (uint32_t physDevID = 0; physDevID < m_numPhysicalDevices; physDevID++)
222     {
223         const uint32_t firstDeviceID  = physDevID;
224         const uint32_t secondDeviceID = (firstDeviceID + 1) % m_numPhysicalDevices;
225 
226         imageSparseInfo.sType         = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
227         imageSparseInfo.pNext         = nullptr;
228         imageSparseInfo.flags         = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
229         imageSparseInfo.imageType     = mapImageType(m_imageType);
230         imageSparseInfo.format        = m_format;
231         imageSparseInfo.extent        = makeExtent3D(getLayerSize(m_imageType, m_imageSize));
232         imageSparseInfo.mipLevels     = 1;
233         imageSparseInfo.arrayLayers   = getNumLayers(m_imageType, m_imageSize);
234         imageSparseInfo.samples       = VK_SAMPLE_COUNT_1_BIT;
235         imageSparseInfo.tiling        = VK_IMAGE_TILING_OPTIMAL;
236         imageSparseInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
237         imageSparseInfo.usage         = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
238         imageSparseInfo.sharingMode   = VK_SHARING_MODE_EXCLUSIVE;
239         imageSparseInfo.queueFamilyIndexCount = 0u;
240         imageSparseInfo.pQueueFamilyIndices   = nullptr;
241 
242         if (m_imageType == IMAGE_TYPE_CUBE || m_imageType == IMAGE_TYPE_CUBE_ARRAY)
243             imageSparseInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
244 
245         // Check if device supports sparse operations for image format
246         if (!checkSparseSupportForImageFormat(instance, physicalDevice, imageSparseInfo))
247             TCU_THROW(NotSupportedError, "The image format does not support sparse operations");
248 
249         {
250             VkImageFormatProperties imageFormatProperties;
251             if (instance.getPhysicalDeviceImageFormatProperties(
252                     physicalDevice, imageSparseInfo.format, imageSparseInfo.imageType, imageSparseInfo.tiling,
253                     imageSparseInfo.usage, imageSparseInfo.flags,
254                     &imageFormatProperties) == VK_ERROR_FORMAT_NOT_SUPPORTED)
255             {
256                 TCU_THROW(NotSupportedError, "Image format does not support sparse operations");
257             }
258         }
259 
260         // Create sparse image
261         const Unique<VkImage> image(createImage(deviceInterface, getDevice(), &imageSparseInfo));
262 
263         // Create semaphores to synchronize sparse binding operations with transfer operations on the sparse images
264         const Unique<VkSemaphore> bindSemaphore(createSemaphore(deviceInterface, getDevice()));
265         const Unique<VkSemaphore> transferSemaphore(createSemaphore(deviceInterface, getDevice()));
266 
267         std::vector<VkSparseImageMemoryRequirements> sparseMemoryRequirements;
268 
269         // Get sparse image general memory requirements
270         const VkMemoryRequirements imageMemoryRequirements =
271             getImageMemoryRequirements(deviceInterface, getDevice(), *image);
272 
273         // Check if required image memory size does not exceed device limits
274         if (imageMemoryRequirements.size >
275             getPhysicalDeviceProperties(instance, getPhysicalDevice(secondDeviceID)).limits.sparseAddressSpaceSize)
276             TCU_THROW(NotSupportedError, "Required memory size for sparse resource exceeds device limits");
277 
278         DE_ASSERT((imageMemoryRequirements.size % imageMemoryRequirements.alignment) == 0);
279 
280         const uint32_t memoryType = findMatchingMemoryType(instance, getPhysicalDevice(secondDeviceID),
281                                                            imageMemoryRequirements, MemoryRequirement::Any);
282 
283         if (memoryType == NO_MATCH_FOUND)
284             return tcu::TestStatus::fail("No matching memory type found");
285 
286         if (firstDeviceID != secondDeviceID)
287         {
288             VkPeerMemoryFeatureFlags peerMemoryFeatureFlags = (VkPeerMemoryFeatureFlags)0;
289             const uint32_t heapIndex =
290                 getHeapIndexForMemoryType(instance, getPhysicalDevice(secondDeviceID), memoryType);
291             deviceInterface.getDeviceGroupPeerMemoryFeatures(getDevice(), heapIndex, firstDeviceID, secondDeviceID,
292                                                              &peerMemoryFeatureFlags);
293 
294             if (((peerMemoryFeatureFlags & VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT) == 0) ||
295                 ((peerMemoryFeatureFlags & VK_PEER_MEMORY_FEATURE_COPY_DST_BIT) == 0) ||
296                 ((peerMemoryFeatureFlags & VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT) == 0))
297             {
298                 TCU_THROW(NotSupportedError, "Peer memory does not support COPY_SRC, COPY_DST, and GENERIC_DST");
299             }
300         }
301 
302         // Get sparse image sparse memory requirements
303         sparseMemoryRequirements = getImageSparseMemoryRequirements(deviceInterface, getDevice(), *image);
304         DE_ASSERT(sparseMemoryRequirements.size() != 0);
305 
306         // Select only one layer to partial bind
307         const uint32_t partiallyBoundLayer = imageSparseInfo.arrayLayers - 1;
308 
309         // Prepare the binding structures and calculate the memory size
310         VkDeviceSize allocationSize = 0;
311 
312         std::vector<VkSparseImageMemoryBind> imageFullBinds[kMemoryObjectCount];
313         VkSparseImageMemoryBind imagePartialBind;
314 
315         for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
316         {
317             const VkImageAspectFlags aspect =
318                 (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
319             const uint32_t aspectIndex = getSparseAspectRequirementsIndex(sparseMemoryRequirements, aspect);
320 
321             if (aspectIndex == NO_MATCH_FOUND)
322                 TCU_THROW(NotSupportedError, "Not supported image aspect");
323 
324             VkSparseImageMemoryRequirements aspectRequirements = sparseMemoryRequirements[aspectIndex];
325 
326             VkExtent3D imageGranularity    = aspectRequirements.formatProperties.imageGranularity;
327             const VkExtent3D planeExtent   = getPlaneExtent(formatDescription, imageSparseInfo.extent, planeNdx, 0);
328             const tcu::UVec3 sparseBlocks  = alignedDivide(planeExtent, imageGranularity);
329             const uint32_t numSparseBlocks = sparseBlocks.x() * sparseBlocks.y() * sparseBlocks.z();
330 
331             if (numSparseBlocks < 2)
332                 TCU_THROW(NotSupportedError, "Image size is too small for partial binding");
333 
334             if (aspectRequirements.imageMipTailFirstLod == 0)
335                 TCU_THROW(NotSupportedError, "Image needs mip tail for mip level 0, partial binding is not possible");
336 
337             for (uint32_t layerNdx = 0; layerNdx < imageSparseInfo.arrayLayers; ++layerNdx)
338             {
339                 const VkImageSubresource subresource = {aspect, 0, layerNdx};
340 
341                 const VkSparseImageMemoryBind imageFullBind = {
342                     subresource,                 // VkImageSubresource subresource;
343                     makeOffset3D(0u, 0u, 0u),    // VkOffset3D offset;
344                     planeExtent,                 // VkExtent3D extent;
345                     VK_NULL_HANDLE,              // VkDeviceMemory            memory; // will be patched in later
346                     allocationSize,              // VkDeviceSize memoryOffset;
347                     (VkSparseMemoryBindFlags)0u, // VkSparseMemoryBindFlags flags;
348                 };
349 
350                 for (uint32_t memoryIdx = 0; memoryIdx < kMemoryObjectCount; memoryIdx++)
351                     imageFullBinds[memoryIdx].push_back(imageFullBind);
352 
353                 // Partially bind only one layer
354                 if (layerNdx == partiallyBoundLayer)
355                 {
356                     // Offset by one block in every direction if possible
357                     VkOffset3D partialOffset = makeOffset3D(0, 0, 0);
358                     if (sparseBlocks.x() > 1)
359                         partialOffset.x = imageGranularity.width;
360                     if (sparseBlocks.y() > 1)
361                         partialOffset.y = imageGranularity.height;
362                     if (sparseBlocks.z() > 1)
363                         partialOffset.z = imageGranularity.depth;
364 
365                     // Map only one block and clamp it to the image dimensions
366                     VkExtent3D partialExtent =
367                         makeExtent3D(de::min(imageGranularity.width, planeExtent.width - partialOffset.x),
368                                      de::min(imageGranularity.height, planeExtent.height - partialOffset.y),
369                                      de::min(imageGranularity.depth, planeExtent.depth - partialOffset.z));
370 
371                     imagePartialBind = {
372                         subresource,                 // VkImageSubresource subresource;
373                         partialOffset,               // VkOffset3D offset;
374                         partialExtent,               // VkExtent3D extent;
375                         VK_NULL_HANDLE,              // VkDeviceMemory            memory; // will be patched in later
376                         allocationSize,              // VkDeviceSize memoryOffset;
377                         (VkSparseMemoryBindFlags)0u, // VkSparseMemoryBindFlags flags;
378                     };
379                 }
380 
381                 allocationSize += imageMemoryRequirements.alignment * numSparseBlocks;
382             }
383         }
384 
385         // Alocate device memory
386         const VkMemoryAllocateInfo allocInfo = {
387             VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType;
388             nullptr,                                // const void* pNext;
389             allocationSize,                         // VkDeviceSize allocationSize;
390             memoryType,                             // uint32_t memoryTypeIndex;
391         };
392 
393         std::vector<Move<VkDeviceMemory>> deviceMemories;
394         for (uint32_t memoryIdx = 0; memoryIdx < kMemoryObjectCount; memoryIdx++)
395         {
396             VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
397             VK_CHECK(deviceInterface.allocateMemory(getDevice(), &allocInfo, nullptr, &deviceMemory));
398             deviceMemories.push_back(Move<VkDeviceMemory>(
399                 check<VkDeviceMemory>(deviceMemory), Deleter<VkDeviceMemory>(deviceInterface, getDevice(), nullptr)));
400         }
401 
402         // Patch-in the newly generate memory objects into pre-created binding structures
403         for (uint32_t i = 0; i < imageFullBinds[0].size(); i++)
404         {
405             for (uint32_t memoryIdx = 0; memoryIdx < kMemoryObjectCount; memoryIdx++)
406             {
407                 DE_ASSERT(imageFullBinds[0].size() == imageFullBinds[memoryIdx].size());
408                 imageFullBinds[memoryIdx][i].memory = *deviceMemories[memoryIdx];
409             }
410         }
411 
412         imagePartialBind.memory = *deviceMemories[0];
413 
414         const Unique<VkCommandPool> commandPool(
415             makeCommandPool(deviceInterface, getDevice(), transferQueue.queueFamilyIndex));
416 
417         const VkPipelineStageFlags waitStageBits[] = {VK_PIPELINE_STAGE_TRANSFER_BIT};
418 
419         // Fully bind the memory and fill it with a value
420         for (uint32_t memoryIdx = 0; memoryIdx < kMemoryObjectCount; memoryIdx++)
421         {
422             const VkDeviceGroupBindSparseInfo devGroupBindSparseInfo = {
423                 VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO, // VkStructureType sType;
424                 nullptr,                                         // const void* pNext;
425                 firstDeviceID,                                   // uint32_t resourceDeviceIndex;
426                 secondDeviceID,                                  // uint32_t memoryDeviceIndex;
427             };
428 
429             VkBindSparseInfo bindSparseInfo = {
430                 VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,                    // VkStructureType sType;
431                 m_useDeviceGroups ? &devGroupBindSparseInfo : nullptr, // const void* pNext;
432                 memoryIdx == 0 ? 0u : 1u,                              // uint32_t waitSemaphoreCount;
433                 &transferSemaphore.get(),                              // const VkSemaphore* pWaitSemaphores;
434                 0u,                                                    // uint32_t bufferBindCount;
435                 nullptr,             // const VkSparseBufferMemoryBindInfo* pBufferBinds;
436                 0u,                  // uint32_t imageOpaqueBindCount;
437                 nullptr,             // const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds;
438                 0u,                  // uint32_t imageBindCount;
439                 nullptr,             // const VkSparseImageMemoryBindInfo* pImageBinds;
440                 1u,                  // uint32_t signalSemaphoreCount;
441                 &bindSemaphore.get() // const VkSemaphore* pSignalSemaphores;
442             };
443 
444             VkSparseImageMemoryBindInfo imageBindInfo;
445 
446             if (imageFullBinds[memoryIdx].size() > 0)
447             {
448                 imageBindInfo.image     = *image;
449                 imageBindInfo.bindCount = static_cast<uint32_t>(imageFullBinds[memoryIdx].size());
450                 imageBindInfo.pBinds    = imageFullBinds[memoryIdx].data();
451 
452                 bindSparseInfo.imageBindCount = 1u;
453                 bindSparseInfo.pImageBinds    = &imageBindInfo;
454             }
455 
456             // Submit sparse bind commands
457             VK_CHECK(deviceInterface.queueBindSparse(sparseQueue.queueHandle, 1u, &bindSparseInfo, VK_NULL_HANDLE));
458 
459             const Unique<VkCommandBuffer> commandBuffer(
460                 allocateCommandBuffer(deviceInterface, getDevice(), *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
461 
462             beginCommandBuffer(deviceInterface, *commandBuffer);
463 
464             // Clear everything
465             for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
466             {
467                 const VkImageAspectFlags aspect =
468                     (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
469 
470                 const VkImageSubresourceRange range = {
471                     aspect,                   // VkImageAspectFlags aspectMask;
472                     0,                        // uint32_t baseMipLevel;
473                     VK_REMAINING_MIP_LEVELS,  // uint32_t levelCount;
474                     0,                        // uint32_t baseArrayLayer;
475                     VK_REMAINING_ARRAY_LAYERS // uint32_t layerCount;
476                 };
477 
478                 const VkImageMemoryBarrier imageMemoryBarrierBefore = {
479                     VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
480                     nullptr,                                // const void* pNext;
481                     0u,                                     // VkAccessFlags srcAccessMask;
482                     VK_ACCESS_TRANSFER_WRITE_BIT,           // VkAccessFlags dstAccessMask;
483                     VK_IMAGE_LAYOUT_UNDEFINED,              // VkImageLayout oldLayout;
484                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,   // VkImageLayout newLayout;
485                     VK_QUEUE_FAMILY_IGNORED,                // uint32_t srcQueueFamilyIndex;
486                     VK_QUEUE_FAMILY_IGNORED,                // uint32_t dstQueueFamilyIndex;
487                     *image,                                 // VkImage image;
488                     range                                   // VkImageSubresourceRange subresourceRange;
489                 };
490 
491                 deviceInterface.cmdPipelineBarrier(
492                     *commandBuffer,                     // VkCommandBuffer                    commandBuffer,
493                     VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // VkPipelineStageFlags                srcStageMask,
494                     VK_PIPELINE_STAGE_TRANSFER_BIT,     // VkPipelineStageFlags                dstStageMask,
495                     (VkDependencyFlagBits)0u,           // VkDependencyFlags                dependencyFlags,
496                     0,                                  // uint32_t                            memoryBarrierCount,
497                     nullptr,                            // const VkMemoryBarrier *            pMemoryBarriers,
498                     0,                                  // uint32_t                            bufferMemoryBarrierCount,
499                     nullptr,                            // const VkBufferMemoryBarrier *    pBufferMemoryBarriers,
500                     1,                                  // uint32_t                            imageMemoryBarrierCount,
501                     &imageMemoryBarrierBefore           // const VkImageMemoryBarrier *        pImageMemoryBarriers
502                 );
503                 VkClearColorValue clearValue = getColorClearValue(memoryIdx);
504                 deviceInterface.cmdClearColorImage(
505                     *commandBuffer,                       // VkCommandBuffer                    commandBuffer,
506                     *image,                               // VkImage                            image,
507                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout                    imageLayout,
508                     &clearValue,                          // VkClearColorValue *                pColor,
509                     1u,                                   // uint32_t                            rangeCount,
510                     &range                                // VkImageSubresourceRange *        pRanges
511                 );
512 
513                 const VkImageMemoryBarrier imageMemoryBarrierAfter = {
514                     VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
515                     nullptr,                                // const void* pNext;
516                     VK_ACCESS_TRANSFER_WRITE_BIT,           // VkAccessFlags srcAccessMask;
517                     VK_ACCESS_TRANSFER_READ_BIT,            // VkAccessFlags dstAccessMask;
518                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,   // VkImageLayout oldLayout;
519                     VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,   // VkImageLayout newLayout;
520                     VK_QUEUE_FAMILY_IGNORED,                // uint32_t srcQueueFamilyIndex;
521                     VK_QUEUE_FAMILY_IGNORED,                // uint32_t dstQueueFamilyIndex;
522                     *image,                                 // VkImage image;
523                     range                                   // VkImageSubresourceRange subresourceRange;
524                 };
525 
526                 deviceInterface.cmdPipelineBarrier(
527                     *commandBuffer,                 // VkCommandBuffer                    commandBuffer,
528                     VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags                srcStageMask,
529                     VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags                dstStageMask,
530                     (VkDependencyFlagBits)0u,       // VkDependencyFlags                dependencyFlags,
531                     0,                              // uint32_t                            memoryBarrierCount,
532                     nullptr,                        // const VkMemoryBarrier *            pMemoryBarriers,
533                     0,                              // uint32_t                            bufferMemoryBarrierCount,
534                     nullptr,                        // const VkBufferMemoryBarrier *    pBufferMemoryBarriers,
535                     1,                              // uint32_t                            imageMemoryBarrierCount,
536                     &imageMemoryBarrierAfter        // const VkImageMemoryBarrier *        pImageMemoryBarriers
537                 );
538             }
539 
540             endCommandBuffer(deviceInterface, *commandBuffer);
541 
542             // Wait for the sparse bind operation semaphore, submit and wait on host for the transfer stage.
543             // In case of device groups, submit on the physical device with the resource.
544             submitCommandsAndWait(deviceInterface,           // DeviceInterface&                    vk,
545                                   getDevice(),               // VkDevice                            device,
546                                   transferQueue.queueHandle, // VkQueue                            queue,
547                                   *commandBuffer,            // VkCommandBuffer                    commandBuffer,
548                                   1u,                        // uint32_t                            waitSemaphoreCount,
549                                   &bindSemaphore.get(),      // VkSemaphore*                        pWaitSemaphores,
550                                   waitStageBits,             // VkPipelineStageFlags*            pWaitDstStageMask,
551                                   1u,                       // uint32_t                            signalSemaphoreCount,
552                                   &transferSemaphore.get(), // VkSemaphore*                        pSignalSemaphores)
553                                   m_useDeviceGroups,        // bool                                useDeviceGroups,
554                                   firstDeviceID             // uint32_t                            physicalDeviceID
555 
556             );
557         }
558 
559         // Partially bind memory 1 back to the image
560         {
561             const VkDeviceGroupBindSparseInfo devGroupBindSparseInfo = {
562                 VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO, // VkStructureType sType;
563                 nullptr,                                         // const void* pNext;
564                 firstDeviceID,                                   // uint32_t resourceDeviceIndex;
565                 secondDeviceID,                                  // uint32_t memoryDeviceIndex;
566             };
567 
568             VkSparseImageMemoryBindInfo imageBindInfo = {
569                 *image,           // VkImage image;
570                 1,                // uint32_t bindCount;
571                 &imagePartialBind // const VkSparseImageMemoryBind* pBinds;
572             };
573 
574             VkBindSparseInfo bindSparseInfo = {
575                 VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,                    // VkStructureType sType;
576                 m_useDeviceGroups ? &devGroupBindSparseInfo : nullptr, // const void* pNext;
577                 1u,                                                    // uint32_t waitSemaphoreCount;
578                 &transferSemaphore.get(),                              // const VkSemaphore* pWaitSemaphores;
579                 0u,                                                    // uint32_t bufferBindCount;
580                 nullptr,             // const VkSparseBufferMemoryBindInfo* pBufferBinds;
581                 0u,                  // uint32_t imageOpaqueBindCount;
582                 nullptr,             // const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds;
583                 1u,                  // uint32_t imageBindCount;
584                 &imageBindInfo,      // const VkSparseImageMemoryBindInfo* pImageBinds;
585                 1u,                  // uint32_t signalSemaphoreCount;
586                 &bindSemaphore.get() // const VkSemaphore* pSignalSemaphores;
587             };
588 
589             // Submit sparse bind commands for execution
590             VK_CHECK(deviceInterface.queueBindSparse(sparseQueue.queueHandle, 1u, &bindSparseInfo, VK_NULL_HANDLE));
591         }
592 
593         // Verify the results
594         // Create a big buffer ...
595         uint32_t bufferSize = 0;
596         uint32_t bufferOffsets[PlanarFormatDescription::MAX_PLANES];
597         uint32_t bufferRowPitches[PlanarFormatDescription::MAX_PLANES];
598 
599         for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
600         {
601             const VkExtent3D planeExtent = getPlaneExtent(formatDescription, imageSparseInfo.extent, planeNdx, 0);
602             bufferOffsets[planeNdx]      = bufferSize;
603             bufferRowPitches[planeNdx]   = formatDescription.planes[planeNdx].elementSizeBytes * planeExtent.width;
604             bufferSize += getImageMipLevelSizeInBytes(imageSparseInfo.extent, 1, formatDescription, planeNdx, 0,
605                                                       BUFFER_IMAGE_COPY_OFFSET_GRANULARITY);
606         }
607 
608         const VkBufferCreateInfo outputBufferCreateInfo =
609             makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
610         const Unique<VkBuffer> outputBuffer(createBuffer(deviceInterface, getDevice(), &outputBufferCreateInfo));
611         const de::UniquePtr<Allocation> outputBufferAlloc(
612             bindBuffer(deviceInterface, getDevice(), getAllocator(), *outputBuffer, MemoryRequirement::HostVisible));
613 
614         std::vector<VkBufferImageCopy> bufferImageCopy(formatDescription.numPlanes);
615         {
616             for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
617             {
618                 const VkImageAspectFlags aspect =
619                     (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
620 
621                 bufferImageCopy[planeNdx] = {
622                     bufferOffsets[planeNdx], // VkDeviceSize bufferOffset;
623                     0u,                      // uint32_t bufferRowLength;
624                     0u,                      // uint32_t bufferImageHeight;
625                     makeImageSubresourceLayers(aspect, 0u, partiallyBoundLayer,
626                                                1u), // VkImageSubresourceLayers imageSubresource;
627                     makeOffset3D(0, 0, 0),          // VkOffset3D imageOffset;
628                     vk::getPlaneExtent(formatDescription, imageSparseInfo.extent, planeNdx,
629                                        0u) // VkExtent3D imageExtent;
630                 };
631             }
632         }
633 
634         //... and copy selected layer into it
635         {
636             const Unique<VkCommandBuffer> commandBuffer(
637                 allocateCommandBuffer(deviceInterface, getDevice(), *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
638 
639             beginCommandBuffer(deviceInterface, *commandBuffer);
640 
641             deviceInterface.cmdCopyImageToBuffer(*commandBuffer, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
642                                                  *outputBuffer, static_cast<uint32_t>(bufferImageCopy.size()),
643                                                  bufferImageCopy.data());
644 
645             // Make the changes visible to the host
646             {
647                 const VkBufferMemoryBarrier outputBufferHostBarrier =
648                     makeBufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags            srcAccessMask,
649                                             VK_ACCESS_HOST_READ_BIT,      // VkAccessFlags            dstAccessMask,
650                                             *outputBuffer,                // VkBuffer                    buffer,
651                                             0ull,                         // VkDeviceSize                offset,
652                                             bufferSize); // VkDeviceSize                bufferSizeBytes,
653 
654                 deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
655                                                    VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, nullptr, 1u,
656                                                    &outputBufferHostBarrier, 0u, nullptr);
657             }
658 
659             endCommandBuffer(deviceInterface, *commandBuffer);
660 
661             // Wait for the sparse bind operations, submit and wait on host for the transfer stage.
662             // In case of device groups, submit on the physical device with the resource.
663             submitCommandsAndWait(deviceInterface,           // DeviceInterface&            vk,
664                                   getDevice(),               // VkDevice                    device,
665                                   transferQueue.queueHandle, // VkQueue                    queue,
666                                   *commandBuffer,            // VkCommandBuffer            commandBuffer,
667                                   1u,                        // uint32_t                    waitSemaphoreCount,
668                                   &bindSemaphore.get(),      // VkSemaphore*                pWaitSemaphores,
669                                   waitStageBits,             // VkPipelineStageFlags*    pWaitDstStageMask,
670                                   0,                         // uint32_t                    signalSemaphoreCount,
671                                   nullptr,                   // VkSemaphore*                pSignalSemaphores,
672                                   m_useDeviceGroups,         // bool                        useDeviceGroups,
673                                   firstDeviceID              // uint32_t                    physicalDeviceID
674             );
675         }
676 
677         // Retrieve data from output buffer to host memory
678         invalidateAlloc(deviceInterface, getDevice(), *outputBufferAlloc);
679 
680         uint8_t *outputData = static_cast<uint8_t *>(outputBufferAlloc->getHostPtr());
681 
682         void *bufferPointers[PlanarFormatDescription::MAX_PLANES];
683 
684         for (uint32_t planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
685             bufferPointers[planeNdx] = outputData + static_cast<size_t>(bufferOffsets[planeNdx]);
686 
687         for (uint32_t channelNdx = 0; channelNdx < 4; ++channelNdx)
688         {
689             if (!formatDescription.hasChannelNdx(channelNdx))
690                 continue;
691 
692             uint32_t planeNdx                  = formatDescription.channels[channelNdx].planeNdx;
693             vk::VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
694             vk::PlanarFormatDescription compatibleFormatDescription =
695                 (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ?
696                     getPlanarFormatDescription(planeCompatibleFormat) :
697                     formatDescription;
698 
699             const tcu::UVec3 size(imageSparseInfo.extent.width, imageSparseInfo.extent.height,
700                                   imageSparseInfo.extent.depth);
701             const tcu::ConstPixelBufferAccess pixelBuffer = vk::getChannelAccess(
702                 compatibleFormatDescription, size, bufferRowPitches, (const void *const *)bufferPointers, channelNdx);
703             tcu::IVec3 pixelDivider = pixelBuffer.getDivider();
704 
705             std::ostringstream str;
706             str << "image" << channelNdx;
707             m_context.getTestContext().getLog() << tcu::LogImage(str.str(), str.str(), pixelBuffer);
708 
709             const VkExtent3D extent            = getPlaneExtent(formatDescription, imageSparseInfo.extent, planeNdx, 0);
710             const VkOffset3D partialBindOffset = imagePartialBind.offset;
711             const VkExtent3D partialBindExtent = imagePartialBind.extent;
712 
713             for (uint32_t offsetZ = 0u; offsetZ < extent.depth; ++offsetZ)
714                 for (uint32_t offsetY = 0u; offsetY < extent.height; ++offsetY)
715                     for (uint32_t offsetX = 0u; offsetX < extent.width; ++offsetX)
716                     {
717                         float fReferenceValue    = 0.0f;
718                         uint32_t uReferenceValue = 0;
719                         int32_t iReferenceValue  = 0;
720                         float acceptableError    = epsilon;
721 
722                         if ((offsetX >= static_cast<uint32_t>(partialBindOffset.x) &&
723                              offsetX < partialBindOffset.x + partialBindExtent.width) &&
724                             (offsetY >= static_cast<uint32_t>(partialBindOffset.y) &&
725                              offsetY < partialBindOffset.y + partialBindExtent.height) &&
726                             (offsetZ >= static_cast<uint32_t>(partialBindOffset.z) &&
727                              offsetZ < partialBindOffset.z + partialBindExtent.depth))
728                         {
729                             fReferenceValue = getColorClearValue(0).float32[channelNdx];
730                             uReferenceValue = getColorClearValue(0).uint32[channelNdx];
731                             iReferenceValue = getColorClearValue(0).int32[channelNdx];
732                         }
733                         else
734                         {
735                             fReferenceValue = getColorClearValue(kMemoryObjectCount - 1).float32[channelNdx];
736                             uReferenceValue = getColorClearValue(kMemoryObjectCount - 1).uint32[channelNdx];
737                             iReferenceValue = getColorClearValue(kMemoryObjectCount - 1).uint32[channelNdx];
738                         }
739 
740                         switch (formatDescription.channels[channelNdx].type)
741                         {
742                         case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
743                         {
744                             const tcu::IVec4 outputValue = pixelBuffer.getPixelInt(
745                                 offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), offsetZ * pixelDivider.z());
746 
747                             if (outputValue.x() != iReferenceValue)
748                                 return tcu::TestStatus::fail("Failed");
749 
750                             break;
751                         }
752                         case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
753                         {
754                             const tcu::UVec4 outputValue = pixelBuffer.getPixelUint(
755                                 offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), offsetZ * pixelDivider.z());
756 
757                             if (outputValue.x() != uReferenceValue)
758                                 return tcu::TestStatus::fail("Failed");
759 
760                             break;
761                         }
762                         case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
763                         case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
764                         {
765                             int numAccurateBits = formatDescription.channels[channelNdx].sizeBits;
766                             if (formatDescription.channels[channelNdx].type ==
767                                 tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT)
768                                 numAccurateBits -= 1;
769                             float fixedPointError = tcu::TexVerifierUtil::computeFixedPointError(numAccurateBits);
770                             acceptableError += fixedPointError;
771                             const tcu::Vec4 outputValue = pixelBuffer.getPixel(
772                                 offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), offsetZ * pixelDivider.z());
773 
774                             if (deAbs(outputValue.x() - fReferenceValue) > acceptableError)
775                                 return tcu::TestStatus::fail("Failed");
776 
777                             break;
778                         }
779                         case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
780                         {
781                             const tcu::Vec4 outputValue = pixelBuffer.getPixel(
782                                 offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), offsetZ * pixelDivider.z());
783 
784                             if (deAbs(outputValue.x() - fReferenceValue) > acceptableError)
785                                 return tcu::TestStatus::fail("Failed");
786 
787                             break;
788                         }
789                         default:
790                             DE_FATAL("Unexpected channel type");
791                             break;
792                         }
793                     }
794         }
795     }
796 
797     return tcu::TestStatus::pass("Passed");
798 }
799 
createInstance(Context & context) const800 TestInstance *ImageSparseRebindCase::createInstance(Context &context) const
801 {
802     return new ImageSparseRebindInstance(context, m_imageType, m_imageSize, m_format, m_useDeviceGroups);
803 }
804 
805 } // namespace
806 
createImageSparseRebindTestsCommon(tcu::TestContext & testCtx,de::MovePtr<tcu::TestCaseGroup> testGroup,const bool useDeviceGroup=false)807 tcu::TestCaseGroup *createImageSparseRebindTestsCommon(tcu::TestContext &testCtx,
808                                                        de::MovePtr<tcu::TestCaseGroup> testGroup,
809                                                        const bool useDeviceGroup = false)
810 {
811     const std::vector<TestImageParameters> imageParameters{
812         {IMAGE_TYPE_2D,
813          {tcu::UVec3(512u, 256u, 1u), tcu::UVec3(128u, 128u, 1u), tcu::UVec3(503u, 137u, 1u)},
814          getTestFormats(IMAGE_TYPE_2D)},
815         {IMAGE_TYPE_2D_ARRAY,
816          {tcu::UVec3(512u, 256u, 6u), tcu::UVec3(128u, 128u, 8u), tcu::UVec3(503u, 137u, 3u)},
817          getTestFormats(IMAGE_TYPE_2D_ARRAY)},
818         {IMAGE_TYPE_CUBE,
819          {tcu::UVec3(256u, 256u, 1u), tcu::UVec3(128u, 128u, 1u), tcu::UVec3(137u, 137u, 1u)},
820          getTestFormats(IMAGE_TYPE_CUBE)},
821         {IMAGE_TYPE_CUBE_ARRAY,
822          {tcu::UVec3(256u, 256u, 6u), tcu::UVec3(128u, 128u, 8u), tcu::UVec3(137u, 137u, 3u)},
823          getTestFormats(IMAGE_TYPE_CUBE_ARRAY)},
824         {IMAGE_TYPE_3D,
825          {tcu::UVec3(256u, 256u, 16u), tcu::UVec3(128u, 128u, 8u), tcu::UVec3(503u, 137u, 3u)},
826          getTestFormats(IMAGE_TYPE_3D)}};
827 
828     for (size_t imageTypeNdx = 0; imageTypeNdx < imageParameters.size(); ++imageTypeNdx)
829     {
830         const ImageType imageType = imageParameters[imageTypeNdx].imageType;
831         de::MovePtr<tcu::TestCaseGroup> imageTypeGroup(
832             new tcu::TestCaseGroup(testCtx, getImageTypeName(imageType).c_str()));
833 
834         for (size_t formatNdx = 0; formatNdx < imageParameters[imageTypeNdx].formats.size(); ++formatNdx)
835         {
836             VkFormat format = imageParameters[imageTypeNdx].formats[formatNdx].format;
837 
838             // skip YCbCr formats for simplicity
839             if (isYCbCrFormat(format))
840                 continue;
841 
842             de::MovePtr<tcu::TestCaseGroup> formatGroup(
843                 new tcu::TestCaseGroup(testCtx, getImageFormatID(format).c_str()));
844 
845             for (size_t imageSizeNdx = 0; imageSizeNdx < imageParameters[imageTypeNdx].imageSizes.size();
846                  ++imageSizeNdx)
847             {
848                 const tcu::UVec3 imageSize = imageParameters[imageTypeNdx].imageSizes[imageSizeNdx];
849 
850                 std::ostringstream stream;
851                 stream << imageSize.x() << "_" << imageSize.y() << "_" << imageSize.z();
852 
853                 formatGroup->addChild(
854                     new ImageSparseRebindCase(testCtx, stream.str(), imageType, imageSize, format, useDeviceGroup));
855             }
856             imageTypeGroup->addChild(formatGroup.release());
857         }
858         testGroup->addChild(imageTypeGroup.release());
859     }
860 
861     return testGroup.release();
862 }
863 
createImageSparseRebindTests(tcu::TestContext & testCtx)864 tcu::TestCaseGroup *createImageSparseRebindTests(tcu::TestContext &testCtx)
865 {
866     de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "image_rebind"));
867     return createImageSparseRebindTestsCommon(testCtx, testGroup);
868 }
869 
870 } // namespace sparse
871 } // namespace vkt
872