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, ®ion);
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