0
\$\begingroup\$

I have 4 rendertargets R10G10B10A2 each being used to store color and normal in a compact double RGB 555 bits format. When I'm doing the lighting pass I'm obliged to have extra rendertargets because if I can read(extract) the normal/color, I can't write back directly the lit color to these surfaces.

So I moved to a compute shader to achieve this goal (DX11 CS5). I have found that I'm limited to R32_UINT for read/write in DX11 CS5. I have thus adapted my encoding/decoding to have things work fine, although the final colors are a little bit hugly with my encoding.

Unfortunately the final frame rate is lower than the regular pixel shader even if I have to do 4 copyresources from the intermediate rendertargets with my previous method.

I have implemented the CS starting from those found in the legacy DX11 samples. My code is like this:

CPU calls:

 //some CSbufferConstant and CSshaderresourceview settings (e.g. shadowmaps, depth textures
 gpDC11->CSSetShader(pCS, 0, 0);
 //I' mworking on 4 UAV at the same time in the CSshader. With only one it is even slower
 gpDC11->CSSetUnorderedAccessViews(0, 4, gSRDeffered.ppUAV, (UINT*)(&gSRDeffered.ppUAV));
 gpDC11->Dispatch(960, 540, 1);//the size of my screen

GPU CS shader:

[numthreads(1,1,1)]
void CS_PostDeferred( uint3 nGid : SV_GroupID, uint3 nDTid : SV_DispatchThreadID, uint3 nGTid : SV_GroupThreadID )//only nDTid is used in fact here
{
    uint Output;
    float2 Tex = float2(nDTid.x/960.0, nDTid.y/540.0);
    float Depth1 = txDepth1.SampleLevel(samPoint, Tex, 0).r;
    float Depth2 = txDepth2.SampleLevel(samPoint, Tex, 0).r;
    float Depth3 = txDepth3.SampleLevel(samPoint, Tex, 0).r;
    float Depth4 = txDepth4.SampleLevel(samPoint, Tex, 0).r;

    Output = UAVDiffuse0[nDTid.xy];
    if ( Depth1 < 1 ) Output = GetLColorUnPackPack_CS(Depth1, Tex, Output);
    UAVDiffuse0[nDTid.xy]=Output;
    Output = UAVDiffuse1[nDTid.xy];
    if ( Depth2 < 1 ) Output = GetLColorUnPackPack_CS(Depth2, Tex, Output);
    UAVDiffuse1[nDTid.xy]=Output;
    Output = UAVDiffuse2[nDTid.xy];
    if ( Depth3 < 1 ) Output = GetLColorUnPackPack_CS(Depth3, Tex, Output);
    UAVDiffuse2[nDTid.xy]=Output;
    Output = UAVDiffuse3[nDTid.xy];
    if ( Depth4 < 1 ) Output = GetLColorUnPackPack_CS(Depth4, Tex, Output);
    UAVDiffuse3[nDTid.xy]=Output;
}

The uint GetLColorUnPackPack_CS (float Depth, float2 UV, uint Data) is the function extracting the color and normal as 2 float3 from the R32_UINT Data param, calculating as usual lighting and shadows for the pixel and then recompacting the final color to the R32_UINT (the normal is not changed). The Depth and UV params are used to recover the position in view space needed for the point lights I'm using.

Any advice welcome

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

When you call Dispatch(960, 540, 1) with [numthreads(1,1,1)] you are basically asking your GPU to distribute the workload on a single thread per thread group, where each thread group (depending on hardware) has a size of 32 or 64 threads, thus only using 1/32 of or 1/64 of the computational power of the threadgroup, while at the same time requesting 960x540 threadgroups

You should aim to use thread groups with sizes that are multiples of 32 or 64 and as few thread groups in Dispatch() as possible:

gpDC11->Dispatch(30, 18, 1); // (960, 540) / 32

Compute Shader:

[numthreads(32,32,1)]
void CS_PostDeferred( uint3 nGid : SV_GroupID, uint3 nDTid : 
SV_DispatchThreadID, uint3 nGTid : SV_GroupThreadID )
{
    /*...*/

Another thing to note is that texture reads are WAY slower than register operations on modern hardware (a few orders of magnitude). The more data you are able to pack into a single texture, the better:

float Depth1 = txDepth1.SampleLevel(samPoint, Tex, 0).r;
float Depth2 = txDepth2.SampleLevel(samPoint, Tex, 0).r;
float Depth3 = txDepth3.SampleLevel(samPoint, Tex, 0).r;
float Depth4 = txDepth4.SampleLevel(samPoint, Tex, 0).r;

Can be packed as

float4 Depth = txDepth.SampleLevel(samPoint, Tex, 0).rgba;
float Depth1 = Depth.r;
float Depth2 = Depth.g;
float Depth3 = Depth.b;
float Depth4 = Depth.a;
\$\endgroup\$
3
  • \$\begingroup\$ Thanks Sup3rlum. I probably need also to change the way I calculate the UV coordinate as now only 1/4 of the screen is lit. I shoud consider including a decal with the uint3 nGTid : SV_GroupThreadID param right? or is it uint3 nGid : SV_GroupID. Regaring texture load I have other loads in the function and my pipeline is not build to have the depth in the same texture (e.g. I'm limited to RG32FLOAT as render target). Need to think another way this part. \$\endgroup\$
    – philB
    Nov 9, 2022 at 8:33
  • \$\begingroup\$ Check out learn.microsoft.com/en-us/windows/win32/direct3dhlsl/… for how indexing parameters work in compute shaders. I've made a mistake in my post the compute shader should be running [numthreads(32,32,1)] instead of [numthreads(16,16,1)] \$\endgroup\$
    – Sup3rlum
    Nov 9, 2022 at 9:02
  • \$\begingroup\$ works perfect now, a liitle bit more efficient than the pixel shader version . thanks again. The doc is fine but it is always easier to understand when doing real things. \$\endgroup\$
    – philB
    Nov 9, 2022 at 10:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .