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