Reduce Overdraw on Particles using RWTexture2D

Started by
7 comments, last by Adam Miles 6 years, 6 months ago

Hey,

I just had the idea to use a RWTexture2D within the pixel shader of my particles to try to reduce overdraw/fillrate. I have created a R32_Float texture with UAV and bound it together with the RenderTarget. In my pixel shader I just add a contstant value to pixel of the current fragment while I am checking for a maximum at the beginning. However it does not work. It seams that the texture is not getting written. What I am doing wrong? Or is it not possible to read/write at the same time in PixelShader?

Thx,
Thomas

Advertisement

The same subresource(s) cannot be simultaneously bound to the pipeline for write at multiple bind points (the actual restriction is even stronger: if a resource is bound for write it cannot be bound for read either).

See:

https://msdn.microsoft.com/en-us/library/windows/desktop/ff476517(v=vs.85).aspx

"The runtime read+write conflict prevention logic (which stops a resource from being bound as an SRV and RTV or UAV at the same time) treats views of different parts of the same video surface as conflicting for simplicity"

If you're running on D3D10 or 11 you should get an error/warning reported by the debug runtime.  With D3D12 and bindless UAVs the conflict is slightly harder to detect.

Yes sure you can't use an SRV of the same ressource where you have bound the RTV.. but in my case I have a second texture with an UAV which is bound parallel to the main RTV. Using an UAV it should be possible to read/write a texture within the PS. I am binding the UAV togehter with the current RTV and DSV using ID3D11DeviceContext::OMSetRenderTargetsAndUnorderedAccessViews

and i don't get any error message..

 

  1. Do you have the Debug Layer turned on?
  2. What does your call to OMSetRenderTargetsAndUnorderedAccessViews look like?

And 3, I fail to see how this is going to produce desirable results. The execution order of reads/writes to the UAV is inherently 'Unordered' (it's in the name) and so trying to do any sort of "Read-Modify-Write" on a floating-point UAV Texture in the pixel shader every time the it gets invoked is going to be non-deterministic.

How does this reduce overdraw? It sounds like you're still binding a Render Target and writing something to it?

Could you go into a little more detail how it is you think this technique will a) Be deterministic and b) Perform better than what you were doing before?

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

19 hours ago, ajmiles said:
  1. Do you have the Debug Layer turned on?
  2. What does your call to OMSetRenderTargetsAndUnorderedAccessViews look like?

And 3, I fail to see how this is going to produce desirable results. The execution order of reads/writes to the UAV is inherently 'Unordered' (it's in the name) and so trying to do any sort of "Read-Modify-Write" on a floating-point UAV Texture in the pixel shader every time the it gets invoked is going to be non-deterministic.

How does this reduce overdraw? It sounds like you're still binding a Render Target and writing something to it?

Could you go into a little more detail how it is you think this technique will a) Be deterministic and b) Perform better than what you were doing before?

1. Yes of course Debug Layer is on.. No Warnings or Errors.
2. I am using SlimDX so I don't call that method nativly, but it looks like this:


   Dim UAVs() As UnorderedAccessView = {Overdraw.UAV}
   Dim RTVs() As RenderTargetView = {Path.HDR_Buffer.RTV}
   C.OutputMerger.SetTargets(View.DepthStencilView, 1, UAVs, RTVs)

 

About my idea:
I thought about to have a "coverage texture" parallel to the RenderTarget to check how much alpha was already drawn to the current fragment in order to discard any further draws if a specific value is reached.

In order to simply test if a UAV can be written and read in PS i just tried something like that:


RWTexture2D<uint> Overdraw;  // only R32_UINT are supported..
float4 main(PixelShaderInput input, float4 coord : SV_POSITION) : SV_TARGET
{
  ...
    uint2 uv = (uint2) coord.xy;
     
   if (Overdraw[uv] > 0) discard;
   ...  
   Overdraw[uv] = 3;    
   ...
}

But nothing gets discarded.

About "unordered".. yes you are right, but I thought also the execution order for all fragements in PS is also "unordered"
 

Binding UAVs to the Pixel Shader stage has a slight quirk in that the UAV indices don't start at 0, but rather at an index 1 if you have 1 render target (or 2 if you have 2 RTV outputs).

I've not looked at the internals of SlimDX, but it needs to make sure that it starts binding UAVs from N (where N = NumRTVs) rather than from 0.

Pixel Shader output is strictly ordered by triangle index, so no, it's not unordered. Also worth noting is that killing/discarding pixels is unlikely to save you anything on fill-rate as the waves have already been launched. At least on hardware I'm familiar with, fill-rate is not saved by killing pixels.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

Yea in the HLSL I posted I forgot the : register(u1).. so I was aware about the indices.

In my case the shading of each particle is very complex (lots of lighting) so I was hoping this could be an easy way of reducing the calculation for already covered particles.

 

Finally he also did something equal I think:

 

Nothing about what you're doing (Read + Write to a UAV from a Pixel Shader) is inherently not going to work (it should), it's just your expectation around the ordering.

At the very least you'll need to use an Atomic operation (with return) to keep the per-pixel count deterministic. If you don't you'll likely end up with a non-deterministic number of particles affecting each pixel.

If you're still having problems you might try something like RenderDoc, VSGD, NSight, GPUPerfStudio, Intel GPA to debug it.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

This topic is closed to new replies.

Advertisement