blob: 26a1bfb63a42070654f681d2298f92faad881fc7 [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;
15
16void SaveAllocatorStatsToFile(const wchar_t* filePath);
17
18////////////////////////////////////////////////////////////////////////////////
19// Class definitions
20
21class BaseImage
22{
23public:
24 virtual VkResult Init(RandomNumberGenerator& rand) = 0;
25 virtual ~BaseImage();
26
27protected:
28 VkImage m_Image = VK_NULL_HANDLE;
29
30 void FillImageCreateInfo(VkImageCreateInfo& outInfo, RandomNumberGenerator& rand);
31};
32
33class TraditionalImage : public BaseImage
34{
35public:
36 virtual VkResult Init(RandomNumberGenerator& rand);
37 virtual ~TraditionalImage();
38
39private:
40 VmaAllocation m_Allocation = VK_NULL_HANDLE;
41};
42
43class SparseBindingImage : public BaseImage
44{
45public:
46 virtual VkResult Init(RandomNumberGenerator& rand);
47 virtual ~SparseBindingImage();
48
49private:
50 std::vector<VmaAllocation> m_Allocations;
51};
52
53////////////////////////////////////////////////////////////////////////////////
54// class BaseImage
55
56BaseImage::~BaseImage()
57{
58 if(m_Image)
59 {
60 vkDestroyImage(g_hDevice, m_Image, nullptr);
61 }
62}
63
64void BaseImage::FillImageCreateInfo(VkImageCreateInfo& outInfo, RandomNumberGenerator& rand)
65{
66 constexpr uint32_t imageSizeMin = 8;
67 constexpr uint32_t imageSizeMax = 2048;
68
69 ZeroMemory(&outInfo, sizeof(outInfo));
70 outInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
71 outInfo.imageType = VK_IMAGE_TYPE_2D;
72 outInfo.extent.width = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
73 outInfo.extent.height = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
74 outInfo.extent.depth = 1;
75 outInfo.mipLevels = 1; // TODO ?
76 outInfo.arrayLayers = 1; // TODO ?
77 outInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
78 outInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
79 outInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
80 outInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
81 outInfo.samples = VK_SAMPLE_COUNT_1_BIT;
82 outInfo.flags = 0;
83}
84
85////////////////////////////////////////////////////////////////////////////////
86// class TraditionalImage
87
88VkResult TraditionalImage::Init(RandomNumberGenerator& rand)
89{
90 VkImageCreateInfo imageCreateInfo;
91 FillImageCreateInfo(imageCreateInfo, rand);
92
93 VmaAllocationCreateInfo allocCreateInfo = {};
94 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
95 // Default BEST_FIT is clearly better.
96 //allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
97
98 const VkResult res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo,
99 &m_Image, &m_Allocation, nullptr);
100
101 return res;
102}
103
104TraditionalImage::~TraditionalImage()
105{
106 if(m_Allocation)
107 {
108 vmaFreeMemory(g_hAllocator, m_Allocation);
109 }
110}
111
112////////////////////////////////////////////////////////////////////////////////
113// class SparseBindingImage
114
115VkResult SparseBindingImage::Init(RandomNumberGenerator& rand)
116{
117 assert(g_SparseBindingEnabled && g_hSparseBindingQueue);
118
119 // Create image.
120 VkImageCreateInfo imageCreateInfo;
121 FillImageCreateInfo(imageCreateInfo, rand);
122 imageCreateInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
123 VkResult res = vkCreateImage(g_hDevice, &imageCreateInfo, nullptr, &m_Image);
124 if(res != VK_SUCCESS)
125 {
126 return res;
127 }
128
129 // Get memory requirements.
130 VkMemoryRequirements imageMemReq;
131 vkGetImageMemoryRequirements(g_hDevice, m_Image, &imageMemReq);
132
133 // This is just to silence validation layer warning.
134 // But it doesn't help. Looks like a bug in Vulkan validation layers.
135 uint32_t sparseMemReqCount = 0;
136 vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, nullptr);
137 assert(sparseMemReqCount <= 8);
138 VkSparseImageMemoryRequirements sparseMemReq[8];
139 vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, sparseMemReq);
140
141 // According to Vulkan specification, for sparse resources memReq.alignment is also page size.
142 const VkDeviceSize pageSize = imageMemReq.alignment;
143 const uint32_t pageCount = (uint32_t)ceil_div<VkDeviceSize>(imageMemReq.size, pageSize);
144
145 VmaAllocationCreateInfo allocCreateInfo = {};
146 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
147
148 VkMemoryRequirements pageMemReq = imageMemReq;
149 pageMemReq.size = pageSize;
150
151 // Allocate and bind memory pages.
152 m_Allocations.resize(pageCount);
153 std::fill(m_Allocations.begin(), m_Allocations.end(), nullptr);
154 std::vector<VkSparseMemoryBind> binds{pageCount};
155 VmaAllocationInfo allocInfo;
156 for(uint32_t i = 0; i < pageCount; ++i)
157 {
158 res = vmaAllocateMemory(g_hAllocator, &pageMemReq, &allocCreateInfo, &m_Allocations[i], &allocInfo);
159 if(res != VK_SUCCESS)
160 {
161 return res;
162 }
163
164 binds[i] = {};
165 binds[i].resourceOffset = pageSize * i;
166 binds[i].size = pageSize;
167 binds[i].memory = allocInfo.deviceMemory;
168 binds[i].memoryOffset = allocInfo.offset;
169 }
170
171 VkSparseImageOpaqueMemoryBindInfo imageBindInfo;
172 imageBindInfo.image = m_Image;
173 imageBindInfo.bindCount = pageCount;
174 imageBindInfo.pBinds = binds.data();
175
176 VkBindSparseInfo bindSparseInfo = { VK_STRUCTURE_TYPE_BIND_SPARSE_INFO };
177 bindSparseInfo.pImageOpaqueBinds = &imageBindInfo;
178 bindSparseInfo.imageOpaqueBindCount = 1;
179
180 ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &g_ImmediateFence) );
181 ERR_GUARD_VULKAN( vkQueueBindSparse(g_hSparseBindingQueue, 1, &bindSparseInfo, g_ImmediateFence) );
182 ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &g_ImmediateFence, VK_TRUE, UINT64_MAX) );
183
184 return VK_SUCCESS;
185}
186
187SparseBindingImage::~SparseBindingImage()
188{
189 for(size_t i = m_Allocations.size(); i--; )
190 {
191 vmaFreeMemory(g_hAllocator, m_Allocations[i]);
192 }
193}
194
195////////////////////////////////////////////////////////////////////////////////
196// Private functions
197
198////////////////////////////////////////////////////////////////////////////////
199// Public functions
200
201void TestSparseBinding()
202{
203 struct ImageInfo
204 {
205 std::unique_ptr<BaseImage> image;
206 uint32_t endFrame;
207 };
208 std::vector<ImageInfo> images;
209
210 constexpr uint32_t frameCount = 2000;
211 constexpr uint32_t imageLifeFramesMin = 1;
212 constexpr uint32_t imageLifeFramesMax = 400;
213
214 RandomNumberGenerator rand(4652467);
215
216 for(uint32_t i = 0; i < frameCount; ++i)
217 {
218 // Bump frame index.
219 ++g_FrameIndex;
220 vmaSetCurrentFrameIndex(g_hAllocator, g_FrameIndex);
221
222 // Create one new, random image.
223 ImageInfo imageInfo;
224 //imageInfo.image = std::make_unique<TraditionalImage>();
225 imageInfo.image = std::make_unique<SparseBindingImage>();
226 if(imageInfo.image->Init(rand) == VK_SUCCESS)
227 {
228 imageInfo.endFrame = g_FrameIndex + rand.Generate() % (imageLifeFramesMax - imageLifeFramesMin) + imageLifeFramesMin;
229 images.push_back(std::move(imageInfo));
230 }
231
232 // Delete all images that expired.
233 for(size_t i = images.size(); i--; )
234 {
235 if(g_FrameIndex >= images[i].endFrame)
236 {
237 images.erase(images.begin() + i);
238 }
239 }
240 }
241
242 SaveAllocatorStatsToFile(L"SparseBindingTest.json");
243
244 // Free remaining images.
245 images.clear();
246}
247
248#endif // #ifdef _WIN32