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