How to update efficiently uniform buffer in shaders?

My team and I are porting few D3D11 custom shaders to RHI.
The shaders make use of constant buffers (cbuffer) which in the RHI world
are called uniform buffers. We got it working using TUniformBuffer but
the performance was not optimal. The reason is that we need to update them
multiple times per frame and we naively went with TUniformBuffer::SetContents
which should update the buffers…except it does not - it destroys them and
recreates a new one with the updated data and this is slow.

I attempted some workarounds:

  1. I cannot store the buffers as FShaderParameter because they are not getting bound correctly (even though I’m calling FooParam.Bind(ShaderMap, TEXT("ShaderMap)).
  2. Using FShaderUniformBufferParameter correctly causes the parameter to be bound, but then I cannot use SetShaderValue because FShaderUniformBufferParameter and FShaderParameter are unrelated classes. I need to use SetUniformBufferParameter.
  3. To use SetUniformBufferParameter I need to create a FRHIUniformBuffer by calling RHICreateUniformBuffer and this is fine…except I just did the same thing TUniformBuffer is doing internally.

The problem is that unlike all other data objects (vertex, index and structured buffers, textures)
there’s neither RHIUpdateUniformBuffer, nor RHILockUniformBuffer.

After some digging around the codebase I found that the engine uses cbuffers in only a few places, none
of which requires frequent updates…except some D3D-related slate magic which actually defines
its own FSlateD3DConstantBuffer for the sole purpose of updating the constant buffer without
recreating it!

So, am I missing something or is there a performant way to update a uniform buffer?

Hi Dimitar, Unreal’s current handling of UB’s is that they are persistent/read-only, hence the flags for multiframe or multidraw so the RHI can decide how to allocate it (it might just reuse an entry from a pool on some RHIs).
We have been discussing other ways to perform update but it’s still a matter of debate.

Hi Rolando,

Thanks for the quick answer.
So we are evaluating our next options here and I wanted to know whether you will have some advice about them.

  • The pooling is not an option for us, because it is not supported by all the RHIs we target.
  • The option to write the update code ourselves for each platform is not very appealing
  • Convert the uniform buffers to plain shader constants and use the FShaderParameter class to update them

So our next plan is to convert the uniform buffers to constants. Do you see any potential issues with that?
Or is there another way that we are missing at the moment?

The Engine itself has a bunch of uniform buffers used (eg View, Primitive) and also per material, so we are creating these over & over (that’s why the creation flag is so important); I wonder what perf issues you see?
You can switch to regular constant params also but depending on the number of them, CPU-wise is more expensive to query/set.