How do you handle buffer deletion?


#1

What is an effective way to handle the deletion of a vertex buffer when it may still be in use in a command buffer that is rendering? I am trying to find a good clean way to handle this without adding a lot of overhead.


#2

Fence on that command buffer submission, and vkWaitForFences. Or perhaps speculatively garbage collect with vkGetFenceStatus.


#3

Yeah but I am trying to implement this in a general way in an engine, not one specific app where I know every variable. The only two options I can think of are either wait for the device to idle and clear all command buffers, or a complicated system of smart pointers. I can’t think of any simple way to handle this.


#4

What exactly do you mean “in a general way in an engine”?

The way “engine” is typically used, with regard to graphics, is that the engine ought to be handling all aspects of the graphics system. The user decides what to render, but the engine handles how that gets rendered.

So your engine should be able to know what buffers it used in which CBs and for which purposes, and therefore should know what to synchronize with to know when those buffers are no longer in use.

There is no “general way” because Vulkan expects the user to have complete knowledge of what that user has done. Vulkan doesn’t track which objects are being used; if the user needs that information, then that’s the user’s job to keep track of it. Vulkan is a low-level API.


#5

Right, so how would you actually do that? A buffer might be used in eight different command buffers, in three different contexts, and maybe one of those contexts stopped rendering half an hour ago.

I could create a MyBufferClass that wraps the Vulkan buffer, and a MyCommandBufferClass that wraps the command buffer and stores a list of in-use buffers, and use shared pointers to keep track of the whole system, but that’s a lot of code and I am hesitant to start creating wrapper classes yet. I was wondering if people had another idea that was simpler.


#6

That all depends on how your engine and rendering system is structured. The idea with Vulkan is that you use specific knowledge of what and how you’re rendering to inform the decisions your engine makes.

Consider a block-based streaming system. Each block gets loaded into some storage and each streamed block likely has its own VkBuffers. But those blocks also have their own images and such.

So the thing you want to remember is not whether a buffer or image has been used by a submitted batch. What you want to remember is whether anything from a block is currently in use. After all, you can’t stream in a new block until everything from the previous block is done. You don’t need buffer or image granularity; you need block granularity.

A different kind of rendering system will want to track usage differently.

There isn’t supposed to be a general solution; Vulkan gives you the power to build specific solutions, catered to your particular needs. If you’re creating a system where the user of your engine makes all of the rendering system decisions, and you’re just making an abstraction of Vulkan, then you’re basically throwing away many of the advantages Vulkan gives you.

What is a context? Do you mean “thread”?


#7

I mean swap chain and its associated command buffers. Sorry, OpenGL terminology will always be with me. :smiley:

Shared pointers in a wrapper class for buffers and command buffers is probably the only reasonable solution here:

class GPUCommandBuffer
{
VkCommandBuffer cb;
std::vector<shared_ptr > boundbuffers;

void BindBuffer(shared_ptr buf)
{
VkSomethingSomething…
boundbuffers.push_back(buf)
}

void Reset()
{
VkResetCommandBuffer(cb);
boundbuffers.clear()
}
}

class GPUBuffer
{
VkBuffer buffer;
}