• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "Common.h"
24 #include "SparseBindingTest.h"
25 
26 #ifdef _WIN32
27 
28 ////////////////////////////////////////////////////////////////////////////////
29 // External imports
30 
31 extern VkDevice g_hDevice;
32 extern VmaAllocator g_hAllocator;
33 extern uint32_t g_FrameIndex;
34 extern bool g_SparseBindingEnabled;
35 extern VkQueue g_hSparseBindingQueue;
36 extern VkFence g_ImmediateFence;
37 extern VkCommandBuffer g_hTemporaryCommandBuffer;
38 
39 void BeginSingleTimeCommands();
40 void EndSingleTimeCommands();
41 void SaveAllocatorStatsToFile(const wchar_t* filePath);
42 void LoadShader(std::vector<char>& out, const char* fileName);
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 // Class definitions
46 
CalculateMipMapCount(uint32_t width,uint32_t height,uint32_t depth)47 static uint32_t CalculateMipMapCount(uint32_t width, uint32_t height, uint32_t depth)
48 {
49     uint32_t mipMapCount = 1;
50     while(width > 1 || height > 1 || depth > 1)
51     {
52         ++mipMapCount;
53         width  /= 2;
54         height /= 2;
55         depth  /= 2;
56     }
57     return mipMapCount;
58 }
59 
60 class BaseImage
61 {
62 public:
63     virtual void Init(RandomNumberGenerator& rand) = 0;
64     virtual ~BaseImage();
65 
GetCreateInfo() const66     const VkImageCreateInfo& GetCreateInfo() const { return m_CreateInfo; }
67 
68     void TestContent(RandomNumberGenerator& rand);
69 
70 protected:
71     VkImageCreateInfo m_CreateInfo = {};
72     VkImage m_Image = VK_NULL_HANDLE;
73 
74     void FillImageCreateInfo(RandomNumberGenerator& rand);
75     void UploadContent();
76     void ValidateContent(RandomNumberGenerator& rand);
77 };
78 
79 class TraditionalImage : public BaseImage
80 {
81 public:
82     virtual void Init(RandomNumberGenerator& rand);
83     virtual ~TraditionalImage();
84 
85 private:
86     VmaAllocation m_Allocation = VK_NULL_HANDLE;
87 };
88 
89 class SparseBindingImage : public BaseImage
90 {
91 public:
92     virtual void Init(RandomNumberGenerator& rand);
93     virtual ~SparseBindingImage();
94 
95 private:
96     std::vector<VmaAllocation> m_Allocations;
97 };
98 
99 ////////////////////////////////////////////////////////////////////////////////
100 // class BaseImage
101 
~BaseImage()102 BaseImage::~BaseImage()
103 {
104     if(m_Image)
105     {
106         vkDestroyImage(g_hDevice, m_Image, nullptr);
107     }
108 }
109 
TestContent(RandomNumberGenerator & rand)110 void BaseImage::TestContent(RandomNumberGenerator& rand)
111 {
112     printf("Validating content of %u x %u texture...\n",
113         m_CreateInfo.extent.width, m_CreateInfo.extent.height);
114     UploadContent();
115     ValidateContent(rand);
116 }
117 
FillImageCreateInfo(RandomNumberGenerator & rand)118 void BaseImage::FillImageCreateInfo(RandomNumberGenerator& rand)
119 {
120     constexpr uint32_t imageSizeMin = 8;
121     constexpr uint32_t imageSizeMax = 2048;
122 
123     const bool useMipMaps = rand.Generate() % 2 != 0;
124 
125     ZeroMemory(&m_CreateInfo, sizeof(m_CreateInfo));
126     m_CreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
127     m_CreateInfo.imageType = VK_IMAGE_TYPE_2D;
128     m_CreateInfo.extent.width = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
129     m_CreateInfo.extent.height = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
130     m_CreateInfo.extent.depth = 1;
131     m_CreateInfo.mipLevels = useMipMaps ?
132         CalculateMipMapCount(m_CreateInfo.extent.width, m_CreateInfo.extent.height, m_CreateInfo.extent.depth) : 1;
133     m_CreateInfo.arrayLayers = 1;
134     m_CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
135     m_CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
136     m_CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
137     m_CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
138     m_CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
139     m_CreateInfo.flags = 0;
140 }
141 
UploadContent()142 void BaseImage::UploadContent()
143 {
144     VkBufferCreateInfo srcBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
145     srcBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
146     srcBufCreateInfo.size = 4 * m_CreateInfo.extent.width * m_CreateInfo.extent.height;
147 
148     VmaAllocationCreateInfo srcBufAllocCreateInfo = {};
149     srcBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
150     srcBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
151 
152     VkBuffer srcBuf = nullptr;
153     VmaAllocation srcBufAlloc = nullptr;
154     VmaAllocationInfo srcAllocInfo = {};
155     TEST( vmaCreateBuffer(g_hAllocator, &srcBufCreateInfo, &srcBufAllocCreateInfo, &srcBuf, &srcBufAlloc, &srcAllocInfo) == VK_SUCCESS );
156 
157     // Fill texels with: r = x % 255, g = u % 255, b = 13, a = 25
158     uint32_t* srcBufPtr = (uint32_t*)srcAllocInfo.pMappedData;
159     for(uint32_t y = 0, sizeY = m_CreateInfo.extent.height; y < sizeY; ++y)
160     {
161         for(uint32_t x = 0, sizeX = m_CreateInfo.extent.width; x < sizeX; ++x, ++srcBufPtr)
162         {
163             const uint8_t r = (uint8_t)x;
164             const uint8_t g = (uint8_t)y;
165             const uint8_t b = 13;
166             const uint8_t a = 25;
167             *srcBufPtr = (uint32_t)r << 24 | (uint32_t)g << 16 |
168                 (uint32_t)b << 8 | (uint32_t)a;
169         }
170     }
171 
172     BeginSingleTimeCommands();
173 
174     // Barrier undefined to transfer dst.
175     {
176         VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
177         barrier.srcAccessMask = 0;
178         barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
179         barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
180         barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
181         barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
182         barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
183         barrier.image = m_Image;
184         barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
185         barrier.subresourceRange.baseArrayLayer = 0;
186         barrier.subresourceRange.baseMipLevel = 0;
187         barrier.subresourceRange.layerCount = 1;
188         barrier.subresourceRange.levelCount = 1;
189 
190         vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
191             VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
192             VK_PIPELINE_STAGE_TRANSFER_BIT, // dstStageMask
193             0, // dependencyFlags
194             0, nullptr, // memoryBarriers
195             0, nullptr, // bufferMemoryBarriers
196             1, &barrier); // imageMemoryBarriers
197     }
198 
199     // CopyBufferToImage
200     {
201         VkBufferImageCopy region = {};
202         region.bufferOffset = 0;
203         region.bufferRowLength = 0; // Zeros mean tightly packed.
204         region.bufferImageHeight = 0; // Zeros mean tightly packed.
205         region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
206         region.imageSubresource.mipLevel = 0;
207         region.imageSubresource.baseArrayLayer = 0;
208         region.imageSubresource.layerCount = 1;
209         region.imageOffset = { 0, 0, 0 };
210         region.imageExtent = m_CreateInfo.extent;
211         vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, srcBuf, m_Image,
212             VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
213     }
214 
215     // Barrier transfer dst to fragment shader read only.
216     {
217         VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
218         barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
219         barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
220         barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
221         barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
222         barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
223         barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
224         barrier.image = m_Image;
225         barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
226         barrier.subresourceRange.baseArrayLayer = 0;
227         barrier.subresourceRange.baseMipLevel = 0;
228         barrier.subresourceRange.layerCount = 1;
229         barrier.subresourceRange.levelCount = 1;
230 
231         vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
232             VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask
233             VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask
234             0, // dependencyFlags
235             0, nullptr, // memoryBarriers
236             0, nullptr, // bufferMemoryBarriers
237             1, &barrier); // imageMemoryBarriers
238     }
239 
240     EndSingleTimeCommands();
241 
242     vmaDestroyBuffer(g_hAllocator, srcBuf, srcBufAlloc);
243 }
244 
ValidateContent(RandomNumberGenerator & rand)245 void BaseImage::ValidateContent(RandomNumberGenerator& rand)
246 {
247     /*
248     dstBuf has following layout:
249     For each of texels to be sampled, [0..valueCount):
250     struct {
251         in uint32_t pixelX;
252         in uint32_t pixelY;
253         out uint32_t pixelColor;
254     }
255     */
256 
257     const uint32_t valueCount = 128;
258 
259     VkBufferCreateInfo dstBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
260     dstBufCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
261     dstBufCreateInfo.size = valueCount * sizeof(uint32_t) * 3;
262 
263     VmaAllocationCreateInfo dstBufAllocCreateInfo = {};
264     dstBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
265     dstBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
266 
267     VkBuffer dstBuf = nullptr;
268     VmaAllocation dstBufAlloc = nullptr;
269     VmaAllocationInfo dstBufAllocInfo = {};
270     TEST( vmaCreateBuffer(g_hAllocator, &dstBufCreateInfo, &dstBufAllocCreateInfo, &dstBuf, &dstBufAlloc, &dstBufAllocInfo) == VK_SUCCESS );
271 
272     // Fill dstBuf input data.
273     {
274         uint32_t* dstBufContent = (uint32_t*)dstBufAllocInfo.pMappedData;
275         for(uint32_t i = 0; i < valueCount; ++i)
276         {
277             const uint32_t x = rand.Generate() % m_CreateInfo.extent.width;
278             const uint32_t y = rand.Generate() % m_CreateInfo.extent.height;
279             dstBufContent[i * 3    ] = x;
280             dstBufContent[i * 3 + 1] = y;
281             dstBufContent[i * 3 + 2] = 0;
282         }
283     }
284 
285     VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
286     samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
287     samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
288     samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
289     samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
290     samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
291     samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
292     samplerCreateInfo.unnormalizedCoordinates = VK_TRUE;
293 
294     VkSampler sampler = nullptr;
295     TEST( vkCreateSampler( g_hDevice, &samplerCreateInfo, nullptr, &sampler) == VK_SUCCESS );
296 
297     VkDescriptorSetLayoutBinding bindings[2] = {};
298     bindings[0].binding = 0;
299     bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
300     bindings[0].descriptorCount = 1;
301     bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
302     bindings[0].pImmutableSamplers = &sampler;
303     bindings[1].binding = 1;
304     bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
305     bindings[1].descriptorCount = 1;
306     bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
307 
308     VkDescriptorSetLayoutCreateInfo descSetLayoutCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
309     descSetLayoutCreateInfo.bindingCount = 2;
310     descSetLayoutCreateInfo.pBindings = bindings;
311 
312     VkDescriptorSetLayout descSetLayout = nullptr;
313     TEST( vkCreateDescriptorSetLayout(g_hDevice, &descSetLayoutCreateInfo, nullptr, &descSetLayout) == VK_SUCCESS );
314 
315     VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
316     pipelineLayoutCreateInfo.setLayoutCount = 1;
317     pipelineLayoutCreateInfo.pSetLayouts = &descSetLayout;
318 
319     VkPipelineLayout pipelineLayout = nullptr;
320     TEST( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout) == VK_SUCCESS );
321 
322     std::vector<char> shaderCode;
323     LoadShader(shaderCode, "SparseBindingTest.comp.spv");
324 
325     VkShaderModuleCreateInfo shaderModuleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
326     shaderModuleCreateInfo.codeSize = shaderCode.size();
327     shaderModuleCreateInfo.pCode = (const uint32_t*)shaderCode.data();
328 
329     VkShaderModule shaderModule = nullptr;
330     TEST( vkCreateShaderModule(g_hDevice, &shaderModuleCreateInfo, nullptr, &shaderModule) == VK_SUCCESS );
331 
332     VkComputePipelineCreateInfo pipelineCreateInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
333     pipelineCreateInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
334     pipelineCreateInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
335     pipelineCreateInfo.stage.module = shaderModule;
336     pipelineCreateInfo.stage.pName = "main";
337     pipelineCreateInfo.layout = pipelineLayout;
338 
339     VkPipeline pipeline = nullptr;
340     TEST( vkCreateComputePipelines(g_hDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline) == VK_SUCCESS );
341 
342     VkDescriptorPoolSize poolSizes[2] = {};
343     poolSizes[0].type = bindings[0].descriptorType;
344     poolSizes[0].descriptorCount = bindings[0].descriptorCount;
345     poolSizes[1].type = bindings[1].descriptorType;
346     poolSizes[1].descriptorCount = bindings[1].descriptorCount;
347 
348     VkDescriptorPoolCreateInfo descPoolCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
349     descPoolCreateInfo.maxSets = 1;
350     descPoolCreateInfo.poolSizeCount = 2;
351     descPoolCreateInfo.pPoolSizes = poolSizes;
352 
353     VkDescriptorPool descPool = nullptr;
354     TEST( vkCreateDescriptorPool(g_hDevice, &descPoolCreateInfo, nullptr, &descPool) == VK_SUCCESS );
355 
356     VkDescriptorSetAllocateInfo descSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
357     descSetAllocInfo.descriptorPool = descPool;
358     descSetAllocInfo.descriptorSetCount = 1;
359     descSetAllocInfo.pSetLayouts = &descSetLayout;
360 
361     VkDescriptorSet descSet = nullptr;
362     TEST( vkAllocateDescriptorSets(g_hDevice, &descSetAllocInfo, &descSet) == VK_SUCCESS );
363 
364     VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
365     imageViewCreateInfo.image = m_Image;
366     imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
367     imageViewCreateInfo.format = m_CreateInfo.format;
368     imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
369     imageViewCreateInfo.subresourceRange.layerCount = 1;
370     imageViewCreateInfo.subresourceRange.levelCount = 1;
371 
372     VkImageView imageView = nullptr;
373     TEST( vkCreateImageView(g_hDevice, &imageViewCreateInfo, nullptr, &imageView) == VK_SUCCESS );
374 
375     VkDescriptorImageInfo descImageInfo = {};
376     descImageInfo.imageView = imageView;
377     descImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
378 
379     VkDescriptorBufferInfo descBufferInfo = {};
380     descBufferInfo.buffer = dstBuf;
381     descBufferInfo.offset = 0;
382     descBufferInfo.range = VK_WHOLE_SIZE;
383 
384     VkWriteDescriptorSet descWrites[2] = {};
385     descWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
386     descWrites[0].dstSet = descSet;
387     descWrites[0].dstBinding = bindings[0].binding;
388     descWrites[0].dstArrayElement = 0;
389     descWrites[0].descriptorCount = 1;
390     descWrites[0].descriptorType = bindings[0].descriptorType;
391     descWrites[0].pImageInfo = &descImageInfo;
392     descWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
393     descWrites[1].dstSet = descSet;
394     descWrites[1].dstBinding = bindings[1].binding;
395     descWrites[1].dstArrayElement = 0;
396     descWrites[1].descriptorCount = 1;
397     descWrites[1].descriptorType = bindings[1].descriptorType;
398     descWrites[1].pBufferInfo = &descBufferInfo;
399     vkUpdateDescriptorSets(g_hDevice, 2, descWrites, 0, nullptr);
400 
401     BeginSingleTimeCommands();
402     vkCmdBindPipeline(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
403     vkCmdBindDescriptorSets(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descSet, 0, nullptr);
404     vkCmdDispatch(g_hTemporaryCommandBuffer, valueCount, 1, 1);
405     EndSingleTimeCommands();
406 
407     // Validate dstBuf output data.
408     {
409         const uint32_t* dstBufContent = (const uint32_t*)dstBufAllocInfo.pMappedData;
410         for(uint32_t i = 0; i < valueCount; ++i)
411         {
412             const uint32_t x     = dstBufContent[i * 3    ];
413             const uint32_t y     = dstBufContent[i * 3 + 1];
414             const uint32_t color = dstBufContent[i * 3 + 2];
415             const uint8_t a = (uint8_t)(color >> 24);
416             const uint8_t b = (uint8_t)(color >> 16);
417             const uint8_t g = (uint8_t)(color >>  8);
418             const uint8_t r = (uint8_t)color;
419             TEST(r == (uint8_t)x && g == (uint8_t)y && b == 13 && a == 25);
420         }
421     }
422 
423     vkDestroyImageView(g_hDevice, imageView, nullptr);
424     vkDestroyDescriptorPool(g_hDevice, descPool, nullptr);
425     vmaDestroyBuffer(g_hAllocator, dstBuf, dstBufAlloc);
426     vkDestroyPipeline(g_hDevice, pipeline, nullptr);
427     vkDestroyShaderModule(g_hDevice, shaderModule, nullptr);
428     vkDestroyPipelineLayout(g_hDevice, pipelineLayout, nullptr);
429     vkDestroyDescriptorSetLayout(g_hDevice, descSetLayout, nullptr);
430     vkDestroySampler(g_hDevice, sampler, nullptr);
431 }
432 
433 ////////////////////////////////////////////////////////////////////////////////
434 // class TraditionalImage
435 
Init(RandomNumberGenerator & rand)436 void TraditionalImage::Init(RandomNumberGenerator& rand)
437 {
438     FillImageCreateInfo(rand);
439 
440     VmaAllocationCreateInfo allocCreateInfo = {};
441     allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
442     // Default BEST_FIT is clearly better.
443     //allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
444 
445     ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &m_CreateInfo, &allocCreateInfo,
446         &m_Image, &m_Allocation, nullptr) );
447 }
448 
~TraditionalImage()449 TraditionalImage::~TraditionalImage()
450 {
451     if(m_Allocation)
452     {
453         vmaFreeMemory(g_hAllocator, m_Allocation);
454     }
455 }
456 
457 ////////////////////////////////////////////////////////////////////////////////
458 // class SparseBindingImage
459 
Init(RandomNumberGenerator & rand)460 void SparseBindingImage::Init(RandomNumberGenerator& rand)
461 {
462     assert(g_SparseBindingEnabled && g_hSparseBindingQueue);
463 
464     // Create image.
465     FillImageCreateInfo(rand);
466     m_CreateInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
467     ERR_GUARD_VULKAN( vkCreateImage(g_hDevice, &m_CreateInfo, nullptr, &m_Image) );
468 
469     // Get memory requirements.
470     VkMemoryRequirements imageMemReq;
471     vkGetImageMemoryRequirements(g_hDevice, m_Image, &imageMemReq);
472 
473     // This is just to silence validation layer warning.
474     // But it doesn't help. Looks like a bug in Vulkan validation layers.
475     // See: https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/364
476     uint32_t sparseMemReqCount = 0;
477     vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, nullptr);
478     TEST(sparseMemReqCount <= 8);
479     VkSparseImageMemoryRequirements sparseMemReq[8];
480     vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, sparseMemReq);
481 
482     // According to Vulkan specification, for sparse resources memReq.alignment is also page size.
483     const VkDeviceSize pageSize = imageMemReq.alignment;
484     const uint32_t pageCount = (uint32_t)ceil_div<VkDeviceSize>(imageMemReq.size, pageSize);
485 
486     VmaAllocationCreateInfo allocCreateInfo = {};
487     allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
488 
489     VkMemoryRequirements pageMemReq = imageMemReq;
490     pageMemReq.size = pageSize;
491 
492     // Allocate and bind memory pages.
493     m_Allocations.resize(pageCount);
494     std::fill(m_Allocations.begin(), m_Allocations.end(), nullptr);
495     std::vector<VkSparseMemoryBind> binds{pageCount};
496     std::vector<VmaAllocationInfo> allocInfo{pageCount};
497     ERR_GUARD_VULKAN( vmaAllocateMemoryPages(g_hAllocator, &pageMemReq, &allocCreateInfo, pageCount, m_Allocations.data(), allocInfo.data()) );
498 
499     for(uint32_t i = 0; i < pageCount; ++i)
500     {
501         binds[i] = {};
502         binds[i].resourceOffset = pageSize * i;
503         binds[i].size = pageSize;
504         binds[i].memory = allocInfo[i].deviceMemory;
505         binds[i].memoryOffset = allocInfo[i].offset;
506     }
507 
508     VkSparseImageOpaqueMemoryBindInfo imageBindInfo;
509     imageBindInfo.image = m_Image;
510     imageBindInfo.bindCount = pageCount;
511     imageBindInfo.pBinds = binds.data();
512 
513     VkBindSparseInfo bindSparseInfo = { VK_STRUCTURE_TYPE_BIND_SPARSE_INFO };
514     bindSparseInfo.pImageOpaqueBinds = &imageBindInfo;
515     bindSparseInfo.imageOpaqueBindCount = 1;
516 
517     ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &g_ImmediateFence) );
518     ERR_GUARD_VULKAN( vkQueueBindSparse(g_hSparseBindingQueue, 1, &bindSparseInfo, g_ImmediateFence) );
519     ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &g_ImmediateFence, VK_TRUE, UINT64_MAX) );
520 }
521 
~SparseBindingImage()522 SparseBindingImage::~SparseBindingImage()
523 {
524     vmaFreeMemoryPages(g_hAllocator, m_Allocations.size(), m_Allocations.data());
525 }
526 
527 ////////////////////////////////////////////////////////////////////////////////
528 // Private functions
529 
530 ////////////////////////////////////////////////////////////////////////////////
531 // Public functions
532 
TestSparseBinding()533 void TestSparseBinding()
534 {
535     struct ImageInfo
536     {
537         std::unique_ptr<BaseImage> image;
538         uint32_t endFrame;
539     };
540     std::vector<ImageInfo> images;
541 
542     constexpr uint32_t frameCount = 1000;
543     constexpr uint32_t imageLifeFramesMin = 1;
544     constexpr uint32_t imageLifeFramesMax = 400;
545 
546     RandomNumberGenerator rand(4652467);
547 
548     for(uint32_t i = 0; i < frameCount; ++i)
549     {
550         // Bump frame index.
551         ++g_FrameIndex;
552         vmaSetCurrentFrameIndex(g_hAllocator, g_FrameIndex);
553 
554         // Create one new, random image.
555         ImageInfo imageInfo;
556         //imageInfo.image = std::make_unique<TraditionalImage>();
557         imageInfo.image = std::make_unique<SparseBindingImage>();
558         imageInfo.image->Init(rand);
559         imageInfo.endFrame = g_FrameIndex + rand.Generate() % (imageLifeFramesMax - imageLifeFramesMin) + imageLifeFramesMin;
560         images.push_back(std::move(imageInfo));
561 
562         // Delete all images that expired.
563         for(size_t i = images.size(); i--; )
564         {
565             if(g_FrameIndex >= images[i].endFrame)
566             {
567                 images.erase(images.begin() + i);
568             }
569         }
570     }
571 
572     SaveAllocatorStatsToFile(L"SparseBindingTest.json");
573 
574     // Choose biggest image. Test uploading and sampling.
575     BaseImage* biggestImage = nullptr;
576     for(size_t i = 0, count = images.size(); i < count; ++i)
577     {
578         if(!biggestImage ||
579             images[i].image->GetCreateInfo().extent.width * images[i].image->GetCreateInfo().extent.height >
580                 biggestImage->GetCreateInfo().extent.width * biggestImage->GetCreateInfo().extent.height)
581         {
582             biggestImage = images[i].image.get();
583         }
584     }
585     assert(biggestImage);
586 
587     biggestImage->TestContent(rand);
588 
589     // Free remaining images.
590     images.clear();
591 }
592 
593 #endif // #ifdef _WIN32
594