Vulkan: Postpone Resource Destruction

Resources can still be in use inside the vulkan command queue, when
they are destroyed. Especially as Vulkan doesn't allow resetting
bindings to nothing.

This PR will collect the resources that needs to be destroyed inside
the VKDevice and actually destroy the resources when on frame end.

The resources currently include:
* VkBuffer
* VkImage
* VkImageView
* VkFramebuffer
* VkRenderPass

Pull Request: https://projects.blender.org/blender/blender/pulls/112514
This commit is contained in:
Jeroen Bakker
2023-09-18 13:42:17 +02:00
parent 0e47a13e3e
commit 1daecc24d0
8 changed files with 91 additions and 13 deletions

View File

@@ -87,6 +87,11 @@ class VKBackend : public GPUBackend {
return device_;
}
VKDevice &device_get()
{
return device_;
}
static void platform_init(const VKDevice &device);
static void capabilities_init(VKDevice &device);

View File

@@ -143,9 +143,8 @@ bool VKBuffer::free()
unmap();
}
const VKDevice &device = VKBackend::get().device_get();
VmaAllocator allocator = device.mem_allocator_get();
vmaDestroyBuffer(allocator, vk_buffer_, allocation_);
VKDevice &device = VKBackend::get().device_get();
device.discard_buffer(vk_buffer_, allocation_);
allocation_ = VK_NULL_HANDLE;
vk_buffer_ = VK_NULL_HANDLE;
return true;

View File

@@ -112,7 +112,11 @@ void VKContext::deactivate()
void VKContext::begin_frame() {}
void VKContext::end_frame() {}
void VKContext::end_frame()
{
VKDevice &device = VKBackend::get().device_get();
device.destroy_discarded_resources();
}
void VKContext::flush()
{

View File

@@ -28,6 +28,7 @@ void VKDevice::deinit()
dummy_buffer_.free();
sampler_.free();
destroy_discarded_resources();
vmaDestroyAllocator(mem_allocator_);
mem_allocator_ = VK_NULL_HANDLE;
debugging_tools_.deinit(vk_instance_);
@@ -248,6 +249,60 @@ const Vector<std::reference_wrapper<VKContext>> &VKDevice::contexts_get() const
return contexts_;
};
void VKDevice::discard_image(VkImage vk_image, VmaAllocation vma_allocation)
{
discarded_images_.append(std::pair(vk_image, vma_allocation));
}
void VKDevice::discard_image_view(VkImageView vk_image_view)
{
discarded_image_views_.append(vk_image_view);
}
void VKDevice::discard_buffer(VkBuffer vk_buffer, VmaAllocation vma_allocation)
{
discarded_buffers_.append(std::pair(vk_buffer, vma_allocation));
}
void VKDevice::discard_render_pass(VkRenderPass vk_render_pass)
{
discarded_render_passes_.append(vk_render_pass);
}
void VKDevice::discard_frame_buffer(VkFramebuffer vk_frame_buffer)
{
discarded_frame_buffers_.append(vk_frame_buffer);
}
void VKDevice::destroy_discarded_resources()
{
VK_ALLOCATION_CALLBACKS
while (!discarded_image_views_.is_empty()) {
VkImageView vk_image_view = discarded_image_views_.pop_last();
vkDestroyImageView(vk_device_, vk_image_view, vk_allocation_callbacks);
}
while (!discarded_images_.is_empty()) {
std::pair<VkImage, VmaAllocation> image_allocation = discarded_images_.pop_last();
vmaDestroyImage(mem_allocator_get(), image_allocation.first, image_allocation.second);
}
while (!discarded_buffers_.is_empty()) {
std::pair<VkBuffer, VmaAllocation> buffer_allocation = discarded_buffers_.pop_last();
vmaDestroyBuffer(mem_allocator_get(), buffer_allocation.first, buffer_allocation.second);
}
while (!discarded_render_passes_.is_empty()) {
VkRenderPass vk_render_pass = discarded_render_passes_.pop_last();
vkDestroyRenderPass(vk_device_, vk_render_pass, vk_allocation_callbacks);
}
while (!discarded_frame_buffers_.is_empty()) {
VkFramebuffer vk_frame_buffer = discarded_frame_buffers_.pop_last();
vkDestroyFramebuffer(vk_device_, vk_frame_buffer, vk_allocation_callbacks);
}
}
/** \} */
} // namespace blender::gpu

View File

@@ -73,6 +73,12 @@ class VKDevice : public NonCopyable {
/** Buffer to bind to unbound resource locations. */
VKBuffer dummy_buffer_;
Vector<std::pair<VkImage, VmaAllocation>> discarded_images_;
Vector<std::pair<VkBuffer, VmaAllocation>> discarded_buffers_;
Vector<VkRenderPass> discarded_render_passes_;
Vector<VkFramebuffer> discarded_frame_buffers_;
Vector<VkImageView> discarded_image_views_;
public:
VkPhysicalDevice physical_device_get() const
{
@@ -177,6 +183,13 @@ class VKDevice : public NonCopyable {
return dummy_buffer_;
}
void discard_image(VkImage vk_image, VmaAllocation vma_allocation);
void discard_image_view(VkImageView vk_image_view);
void discard_buffer(VkBuffer vk_buffer, VmaAllocation vma_allocation);
void discard_render_pass(VkRenderPass vk_render_pass);
void discard_frame_buffer(VkFramebuffer vk_framebuffer);
void destroy_discarded_resources();
/** \} */
private:

View File

@@ -469,12 +469,11 @@ void VKFrameBuffer::render_pass_free()
if (vk_render_pass_ == VK_NULL_HANDLE) {
return;
}
VK_ALLOCATION_CALLBACKS
const VKDevice &device = VKBackend::get().device_get();
VKDevice &device = VKBackend::get().device_get();
if (device.is_initialized()) {
vkDestroyRenderPass(device.device_get(), vk_render_pass_, vk_allocation_callbacks);
vkDestroyFramebuffer(device.device_get(), vk_framebuffer_, vk_allocation_callbacks);
device.discard_render_pass(vk_render_pass_);
device.discard_frame_buffer(vk_framebuffer_);
}
image_views_.clear();
vk_render_pass_ = VK_NULL_HANDLE;

View File

@@ -56,9 +56,9 @@ VKImageView::VKImageView(VKImageView &&other)
VKImageView::~VKImageView()
{
if (vk_image_view_ != VK_NULL_HANDLE) {
VK_ALLOCATION_CALLBACKS
const VKDevice &device = VKBackend::get().device_get();
vkDestroyImageView(device.device_get(), vk_image_view_, vk_allocation_callbacks);
VKDevice &device = VKBackend::get().device_get();
device.discard_image_view(vk_image_view_);
vk_image_view_ = VK_NULL_HANDLE;
}
}

View File

@@ -26,8 +26,11 @@ namespace blender::gpu {
VKTexture::~VKTexture()
{
if (is_allocated() && !is_texture_view()) {
const VKDevice &device = VKBackend::get().device_get();
vmaDestroyImage(device.mem_allocator_get(), vk_image_, allocation_);
VKDevice &device = VKBackend::get().device_get();
device.discard_image(vk_image_, allocation_);
vk_image_ = VK_NULL_HANDLE;
allocation_ = VK_NULL_HANDLE;
}
}