blob: b1fc0cb632ead7adadfda1f6c4dbf7d452f4a3fe [file] [log] [blame]
Adam Sawicki51fa9662018-10-03 13:44:29 +02001#include "Common.h"
2#include "SparseBindingTest.h"
3
4#ifdef _WIN32
5
6////////////////////////////////////////////////////////////////////////////////
7// External imports
8
9extern VkDevice g_hDevice;
10extern VmaAllocator g_hAllocator;
11extern uint32_t g_FrameIndex;
12extern bool g_SparseBindingEnabled;
13extern VkQueue g_hSparseBindingQueue;
14extern VkFence g_ImmediateFence;
Adam Sawickida6c1942018-12-05 17:34:34 +010015extern VkCommandBuffer g_hTemporaryCommandBuffer;
Adam Sawicki51fa9662018-10-03 13:44:29 +020016
Adam Sawickida6c1942018-12-05 17:34:34 +010017void BeginSingleTimeCommands();
18void EndSingleTimeCommands();
Adam Sawicki51fa9662018-10-03 13:44:29 +020019void SaveAllocatorStatsToFile(const wchar_t* filePath);
Adam Sawickida6c1942018-12-05 17:34:34 +010020void LoadShader(std::vector<char>& out, const char* fileName);
Adam Sawicki51fa9662018-10-03 13:44:29 +020021
22////////////////////////////////////////////////////////////////////////////////
23// Class definitions
24
25class BaseImage
26{
27public:
Adam Sawickid062b782018-10-03 15:26:22 +020028 virtual void Init(RandomNumberGenerator& rand) = 0;
Adam Sawicki51fa9662018-10-03 13:44:29 +020029 virtual ~BaseImage();
30
Adam Sawickida6c1942018-12-05 17:34:34 +010031 const VkImageCreateInfo& GetCreateInfo() const { return m_CreateInfo; }
32
33 void TestContent(RandomNumberGenerator& rand);
34
Adam Sawicki51fa9662018-10-03 13:44:29 +020035protected:
Adam Sawickida6c1942018-12-05 17:34:34 +010036 VkImageCreateInfo m_CreateInfo = {};
Adam Sawicki51fa9662018-10-03 13:44:29 +020037 VkImage m_Image = VK_NULL_HANDLE;
38
Adam Sawickida6c1942018-12-05 17:34:34 +010039 void FillImageCreateInfo(RandomNumberGenerator& rand);
40 void UploadContent();
41 void ValidateContent(RandomNumberGenerator& rand);
Adam Sawicki51fa9662018-10-03 13:44:29 +020042};
43
44class TraditionalImage : public BaseImage
45{
46public:
Adam Sawickid062b782018-10-03 15:26:22 +020047 virtual void Init(RandomNumberGenerator& rand);
Adam Sawicki51fa9662018-10-03 13:44:29 +020048 virtual ~TraditionalImage();
49
50private:
51 VmaAllocation m_Allocation = VK_NULL_HANDLE;
52};
53
54class SparseBindingImage : public BaseImage
55{
56public:
Adam Sawickid062b782018-10-03 15:26:22 +020057 virtual void Init(RandomNumberGenerator& rand);
Adam Sawicki51fa9662018-10-03 13:44:29 +020058 virtual ~SparseBindingImage();
59
60private:
61 std::vector<VmaAllocation> m_Allocations;
62};
63
64////////////////////////////////////////////////////////////////////////////////
65// class BaseImage
66
67BaseImage::~BaseImage()
68{
69 if(m_Image)
70 {
71 vkDestroyImage(g_hDevice, m_Image, nullptr);
72 }
73}
74
Adam Sawickida6c1942018-12-05 17:34:34 +010075void BaseImage::TestContent(RandomNumberGenerator& rand)
76{
77 printf("Validating content of %u x %u texture...\n",
78 m_CreateInfo.extent.width, m_CreateInfo.extent.height);
79 UploadContent();
80 ValidateContent(rand);
81}
82
83void BaseImage::FillImageCreateInfo(RandomNumberGenerator& rand)
Adam Sawicki51fa9662018-10-03 13:44:29 +020084{
85 constexpr uint32_t imageSizeMin = 8;
86 constexpr uint32_t imageSizeMax = 2048;
87
Adam Sawickida6c1942018-12-05 17:34:34 +010088 ZeroMemory(&m_CreateInfo, sizeof(m_CreateInfo));
89 m_CreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
90 m_CreateInfo.imageType = VK_IMAGE_TYPE_2D;
91 m_CreateInfo.extent.width = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
92 m_CreateInfo.extent.height = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
93 m_CreateInfo.extent.depth = 1;
94 m_CreateInfo.mipLevels = 1; // TODO ?
95 m_CreateInfo.arrayLayers = 1;
96 m_CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
97 m_CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
98 m_CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
99 m_CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
100 m_CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
101 m_CreateInfo.flags = 0;
102}
103
104void BaseImage::UploadContent()
105{
106 VkBufferCreateInfo srcBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
107 srcBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
108 srcBufCreateInfo.size = 4 * m_CreateInfo.extent.width * m_CreateInfo.extent.height;
109
110 VmaAllocationCreateInfo srcBufAllocCreateInfo = {};
111 srcBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
112 srcBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
113
114 VkBuffer srcBuf = nullptr;
115 VmaAllocation srcBufAlloc = nullptr;
116 VmaAllocationInfo srcAllocInfo = {};
117 TEST( vmaCreateBuffer(g_hAllocator, &srcBufCreateInfo, &srcBufAllocCreateInfo, &srcBuf, &srcBufAlloc, &srcAllocInfo) == VK_SUCCESS );
118
119 // Fill texels with: r = x % 255, g = u % 255, b = 13, a = 25
120 uint32_t* srcBufPtr = (uint32_t*)srcAllocInfo.pMappedData;
121 for(uint32_t y = 0, sizeY = m_CreateInfo.extent.height; y < sizeY; ++y)
122 {
123 for(uint32_t x = 0, sizeX = m_CreateInfo.extent.width; x < sizeX; ++x, ++srcBufPtr)
124 {
125 const uint8_t r = (uint8_t)x;
126 const uint8_t g = (uint8_t)y;
127 const uint8_t b = 13;
128 const uint8_t a = 25;
129 *srcBufPtr = (uint32_t)r << 24 | (uint32_t)g << 16 |
130 (uint32_t)b << 8 | (uint32_t)a;
131 }
132 }
133
134 BeginSingleTimeCommands();
135
136 // Barrier undefined to transfer dst.
137 {
138 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
139 barrier.srcAccessMask = 0;
140 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
141 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
142 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
143 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
144 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
145 barrier.image = m_Image;
146 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
147 barrier.subresourceRange.baseArrayLayer = 0;
148 barrier.subresourceRange.baseMipLevel = 0;
149 barrier.subresourceRange.layerCount = 1;
150 barrier.subresourceRange.levelCount = 1;
151
152 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
153 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
154 VK_PIPELINE_STAGE_TRANSFER_BIT, // dstStageMask
155 0, // dependencyFlags
156 0, nullptr, // memoryBarriers
157 0, nullptr, // bufferMemoryBarriers
158 1, &barrier); // imageMemoryBarriers
159 }
160
161 // CopyBufferToImage
162 {
163 VkBufferImageCopy region = {};
164 region.bufferOffset = 0;
165 region.bufferRowLength = 0; // Zeros mean tightly packed.
166 region.bufferImageHeight = 0; // Zeros mean tightly packed.
167 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
168 region.imageSubresource.mipLevel = 0;
169 region.imageSubresource.baseArrayLayer = 0;
170 region.imageSubresource.layerCount = 1;
171 region.imageOffset = { 0, 0, 0 };
172 region.imageExtent = m_CreateInfo.extent;
173 vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, srcBuf, m_Image,
174 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
175 }
176
177 // Barrier transfer dst to fragment shader read only.
178 {
179 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
180 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
181 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
182 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
183 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
184 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
185 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
186 barrier.image = m_Image;
187 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
188 barrier.subresourceRange.baseArrayLayer = 0;
189 barrier.subresourceRange.baseMipLevel = 0;
190 barrier.subresourceRange.layerCount = 1;
191 barrier.subresourceRange.levelCount = 1;
192
193 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
194 VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask
195 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask
196 0, // dependencyFlags
197 0, nullptr, // memoryBarriers
198 0, nullptr, // bufferMemoryBarriers
199 1, &barrier); // imageMemoryBarriers
200 }
201
202 EndSingleTimeCommands();
203
204 vmaDestroyBuffer(g_hAllocator, srcBuf, srcBufAlloc);
205}
206
207void BaseImage::ValidateContent(RandomNumberGenerator& rand)
208{
209 /*
210 dstBuf has following layout:
211 For each of texels to be sampled, [0..valueCount):
212 struct {
213 in uint32_t pixelX;
214 in uint32_t pixelY;
215 out uint32_t pixelColor;
216 }
217 */
218
219 const uint32_t valueCount = 32;
220
221 VkBufferCreateInfo dstBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
222 dstBufCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
223 dstBufCreateInfo.size = valueCount * sizeof(uint32_t) * 3;
224
225 VmaAllocationCreateInfo dstBufAllocCreateInfo = {};
226 dstBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
227 dstBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
228
229 VkBuffer dstBuf = nullptr;
230 VmaAllocation dstBufAlloc = nullptr;
231 VmaAllocationInfo dstBufAllocInfo = {};
232 TEST( vmaCreateBuffer(g_hAllocator, &dstBufCreateInfo, &dstBufAllocCreateInfo, &dstBuf, &dstBufAlloc, &dstBufAllocInfo) == VK_SUCCESS );
233
234 // Fill dstBuf input data.
235 {
236 uint32_t* dstBufContent = (uint32_t*)dstBufAllocInfo.pMappedData;
237 for(uint32_t i = 0; i < valueCount; ++i)
238 {
239 const uint32_t x = rand.Generate() % m_CreateInfo.extent.width;
240 const uint32_t y = rand.Generate() % m_CreateInfo.extent.height;
241 dstBufContent[i * 3 ] = x;
242 dstBufContent[i * 3 + 1] = y;
243 dstBufContent[i * 3 + 2] = 0;
244 }
245 }
246
247 VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
248 samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
249 samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
250 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
251 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
252 samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
253 samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
254 samplerCreateInfo.unnormalizedCoordinates = VK_TRUE;
255
256 VkSampler sampler = nullptr;
257 TEST( vkCreateSampler( g_hDevice, &samplerCreateInfo, nullptr, &sampler) == VK_SUCCESS );
258
259 VkDescriptorSetLayoutBinding bindings[2] = {};
260 bindings[0].binding = 0;
261 bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
262 bindings[0].descriptorCount = 1;
263 bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
264 bindings[0].pImmutableSamplers = &sampler;
265 bindings[1].binding = 1;
266 bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
267 bindings[1].descriptorCount = 1;
268 bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
269
270 VkDescriptorSetLayoutCreateInfo descSetLayoutCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
271 descSetLayoutCreateInfo.bindingCount = 2;
272 descSetLayoutCreateInfo.pBindings = bindings;
273
274 VkDescriptorSetLayout descSetLayout = nullptr;
275 TEST( vkCreateDescriptorSetLayout(g_hDevice, &descSetLayoutCreateInfo, nullptr, &descSetLayout) == VK_SUCCESS );
276
277 VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
278 pipelineLayoutCreateInfo.setLayoutCount = 1;
279 pipelineLayoutCreateInfo.pSetLayouts = &descSetLayout;
280
281 VkPipelineLayout pipelineLayout = nullptr;
282 TEST( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout) == VK_SUCCESS );
283
284 std::vector<char> shaderCode;
285 LoadShader(shaderCode, "SparseBindingTest.comp.spv");
286
287 VkShaderModuleCreateInfo shaderModuleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
288 shaderModuleCreateInfo.codeSize = shaderCode.size();
289 shaderModuleCreateInfo.pCode = (const uint32_t*)shaderCode.data();
290
291 VkShaderModule shaderModule = nullptr;
292 TEST( vkCreateShaderModule(g_hDevice, &shaderModuleCreateInfo, nullptr, &shaderModule) == VK_SUCCESS );
293
294 VkComputePipelineCreateInfo pipelineCreateInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
295 pipelineCreateInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
296 pipelineCreateInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
297 pipelineCreateInfo.stage.module = shaderModule;
298 pipelineCreateInfo.stage.pName = "main";
299 pipelineCreateInfo.layout = pipelineLayout;
300
301 VkPipeline pipeline = nullptr;
302 TEST( vkCreateComputePipelines(g_hDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline) == VK_SUCCESS );
303
304 VkDescriptorPoolSize poolSizes[2] = {};
305 poolSizes[0].type = bindings[0].descriptorType;
306 poolSizes[0].descriptorCount = bindings[0].descriptorCount;
307 poolSizes[1].type = bindings[1].descriptorType;
308 poolSizes[1].descriptorCount = bindings[1].descriptorCount;
309
310 VkDescriptorPoolCreateInfo descPoolCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
311 descPoolCreateInfo.maxSets = 1;
312 descPoolCreateInfo.poolSizeCount = 2;
313 descPoolCreateInfo.pPoolSizes = poolSizes;
314
315 VkDescriptorPool descPool = nullptr;
316 TEST( vkCreateDescriptorPool(g_hDevice, &descPoolCreateInfo, nullptr, &descPool) == VK_SUCCESS );
317
318 VkDescriptorSetAllocateInfo descSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
319 descSetAllocInfo.descriptorPool = descPool;
320 descSetAllocInfo.descriptorSetCount = 1;
321 descSetAllocInfo.pSetLayouts = &descSetLayout;
322
323 VkDescriptorSet descSet = nullptr;
324 TEST( vkAllocateDescriptorSets(g_hDevice, &descSetAllocInfo, &descSet) == VK_SUCCESS );
325
326 VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
327 imageViewCreateInfo.image = m_Image;
328 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
329 imageViewCreateInfo.format = m_CreateInfo.format;
330 imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
331 imageViewCreateInfo.subresourceRange.layerCount = 1;
332 imageViewCreateInfo.subresourceRange.levelCount = 1;
333
334 VkImageView imageView = nullptr;
335 TEST( vkCreateImageView(g_hDevice, &imageViewCreateInfo, nullptr, &imageView) == VK_SUCCESS );
336
337 VkDescriptorImageInfo descImageInfo = {};
338 descImageInfo.imageView = imageView;
339 descImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
340
341 VkDescriptorBufferInfo descBufferInfo = {};
342 descBufferInfo.buffer = dstBuf;
343 descBufferInfo.offset = 0;
344 descBufferInfo.range = VK_WHOLE_SIZE;
345
346 VkWriteDescriptorSet descWrites[2] = {};
347 descWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
348 descWrites[0].dstSet = descSet;
349 descWrites[0].dstBinding = bindings[0].binding;
350 descWrites[0].dstArrayElement = 0;
351 descWrites[0].descriptorCount = 1;
352 descWrites[0].descriptorType = bindings[0].descriptorType;
353 descWrites[0].pImageInfo = &descImageInfo;
354 descWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
355 descWrites[1].dstSet = descSet;
356 descWrites[1].dstBinding = bindings[1].binding;
357 descWrites[1].dstArrayElement = 0;
358 descWrites[1].descriptorCount = 1;
359 descWrites[1].descriptorType = bindings[1].descriptorType;
360 descWrites[1].pBufferInfo = &descBufferInfo;
361 vkUpdateDescriptorSets(g_hDevice, 2, descWrites, 0, nullptr);
362
363 BeginSingleTimeCommands();
364 vkCmdBindPipeline(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
365 vkCmdBindDescriptorSets(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descSet, 0, nullptr);
366 vkCmdDispatch(g_hTemporaryCommandBuffer, valueCount, 1, 1);
367 EndSingleTimeCommands();
368
369 // Validate dstBuf output data.
370 {
371 const uint32_t* dstBufContent = (const uint32_t*)dstBufAllocInfo.pMappedData;
372 for(uint32_t i = 0; i < valueCount; ++i)
373 {
374 const uint32_t x = dstBufContent[i * 3 ];
375 const uint32_t y = dstBufContent[i * 3 + 1];
376 const uint32_t color = dstBufContent[i * 3 + 2];
377 const uint8_t a = (uint8_t)(color >> 24);
378 const uint8_t b = (uint8_t)(color >> 16);
379 const uint8_t g = (uint8_t)(color >> 8);
380 const uint8_t r = (uint8_t)color;
381 TEST(r == (uint8_t)x && g == (uint8_t)y && b == 13 && a == 25);
382 }
383 }
384
385 vkDestroyImageView(g_hDevice, imageView, nullptr);
386 vkDestroyDescriptorPool(g_hDevice, descPool, nullptr);
387 vmaDestroyBuffer(g_hAllocator, dstBuf, dstBufAlloc);
388 vkDestroyPipeline(g_hDevice, pipeline, nullptr);
389 vkDestroyShaderModule(g_hDevice, shaderModule, nullptr);
390 vkDestroyPipelineLayout(g_hDevice, pipelineLayout, nullptr);
391 vkDestroyDescriptorSetLayout(g_hDevice, descSetLayout, nullptr);
392 vkDestroySampler(g_hDevice, sampler, nullptr);
Adam Sawicki51fa9662018-10-03 13:44:29 +0200393}
394
395////////////////////////////////////////////////////////////////////////////////
396// class TraditionalImage
397
Adam Sawickid062b782018-10-03 15:26:22 +0200398void TraditionalImage::Init(RandomNumberGenerator& rand)
Adam Sawicki51fa9662018-10-03 13:44:29 +0200399{
Adam Sawickida6c1942018-12-05 17:34:34 +0100400 FillImageCreateInfo(rand);
Adam Sawicki51fa9662018-10-03 13:44:29 +0200401
402 VmaAllocationCreateInfo allocCreateInfo = {};
403 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
404 // Default BEST_FIT is clearly better.
405 //allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
406
Adam Sawickida6c1942018-12-05 17:34:34 +0100407 ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &m_CreateInfo, &allocCreateInfo,
Adam Sawickid062b782018-10-03 15:26:22 +0200408 &m_Image, &m_Allocation, nullptr) );
Adam Sawicki51fa9662018-10-03 13:44:29 +0200409}
410
411TraditionalImage::~TraditionalImage()
412{
413 if(m_Allocation)
414 {
415 vmaFreeMemory(g_hAllocator, m_Allocation);
416 }
417}
418
419////////////////////////////////////////////////////////////////////////////////
420// class SparseBindingImage
421
Adam Sawickid062b782018-10-03 15:26:22 +0200422void SparseBindingImage::Init(RandomNumberGenerator& rand)
Adam Sawicki51fa9662018-10-03 13:44:29 +0200423{
424 assert(g_SparseBindingEnabled && g_hSparseBindingQueue);
425
426 // Create image.
Adam Sawickida6c1942018-12-05 17:34:34 +0100427 FillImageCreateInfo(rand);
428 m_CreateInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
429 ERR_GUARD_VULKAN( vkCreateImage(g_hDevice, &m_CreateInfo, nullptr, &m_Image) );
Adam Sawicki51fa9662018-10-03 13:44:29 +0200430
431 // Get memory requirements.
432 VkMemoryRequirements imageMemReq;
433 vkGetImageMemoryRequirements(g_hDevice, m_Image, &imageMemReq);
434
435 // This is just to silence validation layer warning.
436 // But it doesn't help. Looks like a bug in Vulkan validation layers.
Adam Sawickida6c1942018-12-05 17:34:34 +0100437 // See: https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/364
Adam Sawicki51fa9662018-10-03 13:44:29 +0200438 uint32_t sparseMemReqCount = 0;
439 vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, nullptr);
Adam Sawickia7d77692018-10-03 16:15:27 +0200440 TEST(sparseMemReqCount <= 8);
Adam Sawicki51fa9662018-10-03 13:44:29 +0200441 VkSparseImageMemoryRequirements sparseMemReq[8];
442 vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, sparseMemReq);
443
444 // According to Vulkan specification, for sparse resources memReq.alignment is also page size.
445 const VkDeviceSize pageSize = imageMemReq.alignment;
446 const uint32_t pageCount = (uint32_t)ceil_div<VkDeviceSize>(imageMemReq.size, pageSize);
447
448 VmaAllocationCreateInfo allocCreateInfo = {};
449 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
450
451 VkMemoryRequirements pageMemReq = imageMemReq;
452 pageMemReq.size = pageSize;
453
454 // Allocate and bind memory pages.
455 m_Allocations.resize(pageCount);
456 std::fill(m_Allocations.begin(), m_Allocations.end(), nullptr);
457 std::vector<VkSparseMemoryBind> binds{pageCount};
Adam Sawickid062b782018-10-03 15:26:22 +0200458 std::vector<VmaAllocationInfo> allocInfo{pageCount};
459 ERR_GUARD_VULKAN( vmaAllocateMemoryPages(g_hAllocator, &pageMemReq, &allocCreateInfo, pageCount, m_Allocations.data(), allocInfo.data()) );
460
Adam Sawicki51fa9662018-10-03 13:44:29 +0200461 for(uint32_t i = 0; i < pageCount; ++i)
462 {
Adam Sawicki51fa9662018-10-03 13:44:29 +0200463 binds[i] = {};
464 binds[i].resourceOffset = pageSize * i;
465 binds[i].size = pageSize;
Adam Sawickid062b782018-10-03 15:26:22 +0200466 binds[i].memory = allocInfo[i].deviceMemory;
467 binds[i].memoryOffset = allocInfo[i].offset;
Adam Sawicki51fa9662018-10-03 13:44:29 +0200468 }
469
470 VkSparseImageOpaqueMemoryBindInfo imageBindInfo;
471 imageBindInfo.image = m_Image;
472 imageBindInfo.bindCount = pageCount;
473 imageBindInfo.pBinds = binds.data();
474
475 VkBindSparseInfo bindSparseInfo = { VK_STRUCTURE_TYPE_BIND_SPARSE_INFO };
476 bindSparseInfo.pImageOpaqueBinds = &imageBindInfo;
477 bindSparseInfo.imageOpaqueBindCount = 1;
478
479 ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &g_ImmediateFence) );
480 ERR_GUARD_VULKAN( vkQueueBindSparse(g_hSparseBindingQueue, 1, &bindSparseInfo, g_ImmediateFence) );
481 ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &g_ImmediateFence, VK_TRUE, UINT64_MAX) );
Adam Sawicki51fa9662018-10-03 13:44:29 +0200482}
483
484SparseBindingImage::~SparseBindingImage()
485{
Adam Sawickid062b782018-10-03 15:26:22 +0200486 vmaFreeMemoryPages(g_hAllocator, m_Allocations.size(), m_Allocations.data());
Adam Sawicki51fa9662018-10-03 13:44:29 +0200487}
488
489////////////////////////////////////////////////////////////////////////////////
490// Private functions
491
492////////////////////////////////////////////////////////////////////////////////
493// Public functions
494
495void TestSparseBinding()
496{
497 struct ImageInfo
498 {
499 std::unique_ptr<BaseImage> image;
500 uint32_t endFrame;
501 };
502 std::vector<ImageInfo> images;
503
504 constexpr uint32_t frameCount = 2000;
505 constexpr uint32_t imageLifeFramesMin = 1;
506 constexpr uint32_t imageLifeFramesMax = 400;
507
508 RandomNumberGenerator rand(4652467);
509
510 for(uint32_t i = 0; i < frameCount; ++i)
511 {
512 // Bump frame index.
513 ++g_FrameIndex;
514 vmaSetCurrentFrameIndex(g_hAllocator, g_FrameIndex);
515
516 // Create one new, random image.
517 ImageInfo imageInfo;
518 //imageInfo.image = std::make_unique<TraditionalImage>();
519 imageInfo.image = std::make_unique<SparseBindingImage>();
Adam Sawickid062b782018-10-03 15:26:22 +0200520 imageInfo.image->Init(rand);
521 imageInfo.endFrame = g_FrameIndex + rand.Generate() % (imageLifeFramesMax - imageLifeFramesMin) + imageLifeFramesMin;
522 images.push_back(std::move(imageInfo));
Adam Sawicki51fa9662018-10-03 13:44:29 +0200523
524 // Delete all images that expired.
525 for(size_t i = images.size(); i--; )
526 {
527 if(g_FrameIndex >= images[i].endFrame)
528 {
529 images.erase(images.begin() + i);
530 }
531 }
532 }
533
534 SaveAllocatorStatsToFile(L"SparseBindingTest.json");
535
Adam Sawickida6c1942018-12-05 17:34:34 +0100536 // Choose biggest image. Test uploading and sampling.
537 BaseImage* biggestImage = nullptr;
538 for(size_t i = 0, count = images.size(); i < count; ++i)
539 {
540 if(!biggestImage ||
541 images[i].image->GetCreateInfo().extent.width * images[i].image->GetCreateInfo().extent.height >
542 biggestImage->GetCreateInfo().extent.width * biggestImage->GetCreateInfo().extent.height)
543 {
544 biggestImage = images[i].image.get();
545 }
546 }
547 assert(biggestImage);
548
549 biggestImage->TestContent(rand);
550
Adam Sawicki51fa9662018-10-03 13:44:29 +0200551 // Free remaining images.
552 images.clear();
553}
554
555#endif // #ifdef _WIN32