|
@@ -39,6 +39,9 @@ RendererVK::~RendererVK()
|
|
|
{
|
|
|
vkDeviceWaitIdle(mDevice);
|
|
|
|
|
|
+ // Trace allocation stats
|
|
|
+ Trace("VK: Max allocations: %u, max size: %u MB", mMaxNumAllocations, uint32(mMaxTotalAllocated >> 20));
|
|
|
+
|
|
|
// Destroy the shadow map
|
|
|
mShadowMap = nullptr;
|
|
|
vkDestroyFramebuffer(mDevice, mShadowFrameBuffer, nullptr);
|
|
@@ -50,16 +53,22 @@ RendererVK::~RendererVK()
|
|
|
cb = nullptr;
|
|
|
for (unique_ptr<ConstantBufferVK> &cb : mPixelShaderConstantBuffer)
|
|
|
cb = nullptr;
|
|
|
-
|
|
|
+
|
|
|
// Free all buffers
|
|
|
for (BufferCache &bc : mFreedBuffers)
|
|
|
for (BufferCache::value_type &vt : bc)
|
|
|
for (BufferVK &bvk : vt.second)
|
|
|
- bvk.Free(mDevice);
|
|
|
+ FreeBufferInternal(bvk);
|
|
|
for (BufferCache::value_type &vt : mBufferCache)
|
|
|
for (BufferVK &bvk : vt.second)
|
|
|
- bvk.Free(mDevice);
|
|
|
+ FreeBufferInternal(bvk);
|
|
|
|
|
|
+ // Free all blocks in the memory cache
|
|
|
+ for (MemoryCache::value_type &mc : mMemoryCache)
|
|
|
+ for (Memory &m : mc.second)
|
|
|
+ if (m.mOffset == 0)
|
|
|
+ vkFreeMemory(mDevice, m.mMemory, nullptr); // Don't care about memory tracking anymore
|
|
|
+
|
|
|
for (VkFence fence : mInFlightFences)
|
|
|
vkDestroyFence(mDevice, fence, nullptr);
|
|
|
|
|
@@ -735,8 +744,8 @@ void RendererVK::DestroySwapChain()
|
|
|
if (mDepthImageView != VK_NULL_HANDLE)
|
|
|
{
|
|
|
vkDestroyImageView(mDevice, mDepthImageView, nullptr);
|
|
|
- vkDestroyImage(mDevice, mDepthImage, nullptr);
|
|
|
- vkFreeMemory(mDevice, mDepthImageMemory, nullptr);
|
|
|
+
|
|
|
+ DestroyImage(mDepthImage, mDepthImageMemory);
|
|
|
}
|
|
|
|
|
|
for (VkFramebuffer frame_buffer : mSwapChainFramebuffers)
|
|
@@ -794,7 +803,7 @@ void RendererVK::BeginFrame(const CameraState &inCamera, float inWorldScale)
|
|
|
// Free buffers that weren't used this frame
|
|
|
for (BufferCache::value_type &vt : mBufferCache)
|
|
|
for (BufferVK &bvk : vt.second)
|
|
|
- bvk.Free(mDevice);
|
|
|
+ FreeBufferInternal(bvk);
|
|
|
mBufferCache.clear();
|
|
|
|
|
|
// Recycle the buffers that were freed
|
|
@@ -986,6 +995,32 @@ uint32 RendererVK::FindMemoryType(uint32 inTypeFilter, VkMemoryPropertyFlags inP
|
|
|
FatalError("Failed to find memory type!");
|
|
|
}
|
|
|
|
|
|
+void RendererVK::AllocateMemory(VkDeviceSize inSize, uint32 inMemoryTypeBits, VkMemoryPropertyFlags inProperties, VkDeviceMemory &outMemory)
|
|
|
+{
|
|
|
+ VkMemoryAllocateInfo alloc_info = {};
|
|
|
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
|
+ alloc_info.allocationSize = inSize;
|
|
|
+ alloc_info.memoryTypeIndex = FindMemoryType(inMemoryTypeBits, inProperties);
|
|
|
+ FatalErrorIfFailed(vkAllocateMemory(mDevice, &alloc_info, nullptr, &outMemory));
|
|
|
+
|
|
|
+ // Track allocation
|
|
|
+ ++mNumAllocations;
|
|
|
+ mTotalAllocated += inSize;
|
|
|
+
|
|
|
+ // Track max usage
|
|
|
+ mMaxTotalAllocated = max(mMaxTotalAllocated, mTotalAllocated);
|
|
|
+ mMaxNumAllocations = max(mMaxNumAllocations, mNumAllocations);
|
|
|
+}
|
|
|
+
|
|
|
+void RendererVK::FreeMemory(VkDeviceMemory inMemory, VkDeviceSize inSize)
|
|
|
+{
|
|
|
+ vkFreeMemory(mDevice, inMemory, nullptr);
|
|
|
+
|
|
|
+ // Track free
|
|
|
+ --mNumAllocations;
|
|
|
+ mTotalAllocated -= inSize;
|
|
|
+}
|
|
|
+
|
|
|
void RendererVK::CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, VkMemoryPropertyFlags inProperties, BufferVK &outBuffer)
|
|
|
{
|
|
|
// Check the cache
|
|
@@ -1012,14 +1047,40 @@ void RendererVK::CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, V
|
|
|
VkMemoryRequirements mem_requirements;
|
|
|
vkGetBufferMemoryRequirements(mDevice, outBuffer.mBuffer, &mem_requirements);
|
|
|
|
|
|
- VkMemoryAllocateInfo alloc_info = {};
|
|
|
- alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
|
- alloc_info.allocationSize = mem_requirements.size;
|
|
|
- alloc_info.memoryTypeIndex = FindMemoryType(mem_requirements.memoryTypeBits, inProperties);
|
|
|
+ if (mem_requirements.size > cMaxAllocSize)
|
|
|
+ {
|
|
|
+ // Allocate block directly
|
|
|
+ AllocateMemory(mem_requirements.size, mem_requirements.memoryTypeBits, inProperties, outBuffer.mMemory);
|
|
|
+ outBuffer.mAllocatedSize = mem_requirements.size;
|
|
|
+ outBuffer.mOffset = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Round allocation to the next power of 2 so that we can use a simple block based allocator
|
|
|
+ outBuffer.mAllocatedSize = max(VkDeviceSize(GetNextPowerOf2(uint32(mem_requirements.size))), cMinAllocSize);
|
|
|
+
|
|
|
+ // Ensure that we have memory available from the right pool
|
|
|
+ Array<Memory> &mem_array = mMemoryCache[{ outBuffer.mAllocatedSize, outBuffer.mUsage, outBuffer.mProperties }];
|
|
|
+ if (mem_array.empty())
|
|
|
+ {
|
|
|
+ // Allocate a bigger block
|
|
|
+ VkDeviceMemory device_memory;
|
|
|
+ AllocateMemory(cBlockSize, mem_requirements.memoryTypeBits, inProperties, device_memory);
|
|
|
|
|
|
- FatalErrorIfFailed(vkAllocateMemory(mDevice, &alloc_info, nullptr, &outBuffer.mMemory));
|
|
|
+ // Divide into sub blocks
|
|
|
+ for (VkDeviceSize offset = 0; offset < cBlockSize; offset += outBuffer.mAllocatedSize)
|
|
|
+ mem_array.push_back({ device_memory, offset });
|
|
|
+ }
|
|
|
|
|
|
- vkBindBufferMemory(mDevice, outBuffer.mBuffer, outBuffer.mMemory, 0);
|
|
|
+ // Claim memory from the pool
|
|
|
+ Memory &memory = mem_array.back();
|
|
|
+ outBuffer.mMemory = memory.mMemory;
|
|
|
+ outBuffer.mOffset = memory.mOffset;
|
|
|
+ mem_array.pop_back();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Bind the memory to the buffer
|
|
|
+ vkBindBufferMemory(mDevice, outBuffer.mBuffer, outBuffer.mMemory, outBuffer.mOffset);
|
|
|
}
|
|
|
|
|
|
VkCommandBuffer RendererVK::StartTempCommandBuffer()
|
|
@@ -1073,7 +1134,7 @@ void RendererVK::CreateDeviceLocalBuffer(const void *inData, VkDeviceSize inSize
|
|
|
CreateBuffer(inSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer);
|
|
|
|
|
|
void *data;
|
|
|
- vkMapMemory(mDevice, staging_buffer.mMemory, 0, inSize, 0, &data);
|
|
|
+ vkMapMemory(mDevice, staging_buffer.mMemory, staging_buffer.mOffset, inSize, 0, &data);
|
|
|
memcpy(data, inData, (size_t)inSize);
|
|
|
vkUnmapMemory(mDevice, staging_buffer.mMemory);
|
|
|
|
|
@@ -1093,6 +1154,19 @@ void RendererVK::FreeBuffer(BufferVK &ioBuffer)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void RendererVK::FreeBufferInternal(BufferVK &ioBuffer)
|
|
|
+{
|
|
|
+ // Destroy the buffer
|
|
|
+ vkDestroyBuffer(mDevice, ioBuffer.mBuffer, nullptr);
|
|
|
+ ioBuffer.mBuffer = VK_NULL_HANDLE;
|
|
|
+
|
|
|
+ if (ioBuffer.mAllocatedSize > cMaxAllocSize)
|
|
|
+ FreeMemory(ioBuffer.mMemory, ioBuffer.mAllocatedSize);
|
|
|
+ else
|
|
|
+ mMemoryCache[{ ioBuffer.mAllocatedSize, ioBuffer.mUsage, ioBuffer.mProperties }].push_back({ ioBuffer.mMemory, ioBuffer.mOffset });
|
|
|
+ ioBuffer.mMemory = VK_NULL_HANDLE;
|
|
|
+}
|
|
|
+
|
|
|
unique_ptr<ConstantBufferVK> RendererVK::CreateConstantBuffer(VkDeviceSize inBufferSize)
|
|
|
{
|
|
|
return make_unique<ConstantBufferVK>(this, inBufferSize);
|
|
@@ -1136,15 +1210,21 @@ void RendererVK::CreateImage(uint32 inWidth, uint32 inHeight, VkFormat inFormat,
|
|
|
VkMemoryRequirements mem_requirements;
|
|
|
vkGetImageMemoryRequirements(mDevice, outImage, &mem_requirements);
|
|
|
|
|
|
- VkMemoryAllocateInfo alloc_info = {};
|
|
|
- alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
|
- alloc_info.allocationSize = mem_requirements.size;
|
|
|
- alloc_info.memoryTypeIndex = FindMemoryType(mem_requirements.memoryTypeBits, inProperties);
|
|
|
- FatalErrorIfFailed(vkAllocateMemory(mDevice, &alloc_info, nullptr, &outMemory));
|
|
|
+ AllocateMemory(mem_requirements.size, mem_requirements.memoryTypeBits, inProperties, outMemory);
|
|
|
|
|
|
vkBindImageMemory(mDevice, outImage, outMemory, 0);
|
|
|
}
|
|
|
|
|
|
+void RendererVK::DestroyImage(VkImage inImage, VkDeviceMemory inMemory)
|
|
|
+{
|
|
|
+ VkMemoryRequirements mem_requirements;
|
|
|
+ vkGetImageMemoryRequirements(mDevice, inImage, &mem_requirements);
|
|
|
+
|
|
|
+ vkDestroyImage(mDevice, inImage, nullptr);
|
|
|
+
|
|
|
+ FreeMemory(inMemory, mem_requirements.size);
|
|
|
+}
|
|
|
+
|
|
|
void RendererVK::UpdateViewPortAndScissorRect(uint32 inWidth, uint32 inHeight)
|
|
|
{
|
|
|
VkCommandBuffer command_buffer = GetCommandBuffer();
|