Lets say an application needs to draws objects by issuing draw commands (vkCmdDraw*) but between them it needs to set new values for various uniform constants
(things like alpha-test ref, matrices, etc.). Virtually for each draw command there is some uniforms that need to be changed.
What is the preferred way to do this in vulkan?
The naive approach would be to use vkCmdCopyBuffer or vkCmdUpdateBuffer before each draw, but this is very problematic:
a) pipeline barriers would be required between each buffer copy/update and the draw command, which could be expensive.
b) neither of those commands (copy and update) can be used inside render pass, so the application would need to enter/leave render pass for basically each draw command.
c) since the vulkan API doesn't guarantee that the draw cmds from different passes will access/update the framebuffer in API order (this is guaranteed only inside single subpass),
explicit barriers will be needed also for all framebuffer attachments between the passes.
All this sounds like brutal overkill to me. Or is it OK to do it this way?
Another approach i thought of would be to write the uniforms needed by each draw cmd in (one or more) sliding buffer(s) - multiple copies of the same logical uniform would be added
in the buffer, one for each draw cmd that uses it. This way, between the draw cmds only vkCmdBindDescriptorSets would be necessary (which can be used inside pass).
This sounds better, but there is a problem when the pipeline uses uniform array(s). Those could be very big. Imagine a skeleton-animated object with 100 bones - that
would need 3*sizeof(vec4)*100 = 4800 bytes. It is probably not good idea to replicate this entire matrix palette each time we want to draw skeletal-animated objects (they may be many).
What would you guys suggest to do in such cases? What would be best for your hardware/drivers.
Can give more cross-vendor info about this, what uniform update strategy is generally preferred at all. Or how the vulkan designers had in mind such task to be handled in this API?
A few advices off-the-cuff:
1) Use push constants to hold small pieces of data which change the most often.
2) For skinning, you are very likely going to be fine with uniform buffers. You could pre-cache matrix data and then use dynamic uniform buffers to quickly switch between portions of data you need to use for your draw call.
3) Feed data you need to be able to access from the shaders in background from another thread, preferably using a transfer queue. The more data you upload at once, the better.
4) If your swapchain consists of more than just one swapchain image, consider allocating memory regions holding "state data" separately for each swapchain image. This way, your application needs not to wait for the buffer contents to be consumed before it can be replaced with newer data.
5) Talking about buffers, make sure your application reuses memory allocations, for instance by allocating one large memory block and "carving out" memory regions from that block. There are platform-specific limitations as to how many memory objects can be active at once.
Thank you for the tips!
What I am trying to do is simulator of d3d9 and opengl (old version) that maps those APIs to vulkan.
I have no idea what the application is trying to do, so I don't have much freedom to organize uniform buffers/push constants in advance.
I have to mix and match at the moment. I'm trying to figure out what is the best that can be done given the limitations.
I was wondering, for example, what the AMD's d3d9 driver does about the vertex/pixel constants