# Fast fullscreen quad rendering in Direct3D 11?

For the last few weeks, I've been trying to port a DX9 implementation of HDR rendering (tone mapping, bloom, stars, etc.) over to DX11. I believe I've got all features working but I'm not getting good enough performance. I'd like to be able to render the whole effect in under 4ms on a fairly low-powered GPU, but using D3D11 Queries I'm noticing that it takes 0.5ms to just render a fullscreen quad with a solid color, and 1.0ms to render a fullscreen texture! And because tone-mapping is the only part of the effect that uses a fullscreen texture, this makes it the most expensive!

I'm already doing some optimisations with my limited graphics knowledge, I've disabled blending and depth-testing, I make sure that the texture sampler uses sensible filtering settings, and I'm pretty sure that the effects of any state-changes are negligible. I've heard that rendering 1 oversized triangle instead of 2 can yield some improvements, but I'm not sure if that will help me in this situation. Basically, does anyone have any suggestions to speed up rendering of a textured quad?

• What hardware are you running on? What screen resolution? Fullscreen or windowed? Is multisampling enabled? Precisely how are you measuring the timings? – Nathan Reed Jun 14 '14 at 21:44
• The device is an integrated Intel HD Graphics Media Accelerator 4000, fullscreen 1366x768, no multisampling. I'm measuring the timings using one D3D11 Query immediately before the necessary state-changes and one after the draw call. – Richard Copperwaite Jun 14 '14 at 21:55
• What specifically are you doing to render your quads? Are you stalling the pipeline after every draw call with queries? Have you tried using any graphics debugger with performance capabilities like Intel GPA or NVIDIA PerfKit? – Sean Middleditch Jun 15 '14 at 7:36
• I don't think I'm doing anything special to render my quads. I'm absolutely removing the queries once I'm done with them, but for the moment I'm polling them after a 5-frame delay, so nothing should be stalling. I did try switching from using two triangles to one oversized triangle, and that did appear to give the tiniest improvement. Running the frame through Intel GPA doesn't give me much useful information, but sure enough performing the 'Simple Pixel Shader' experiment (outputting a solid colour) brings the tone mapping call to 0.5ms, which seems like a long time to just fill the screen! – Richard Copperwaite Jun 15 '14 at 14:36
• Using texture.Load instead of texture.Sample may also be beneficial as it can bypass sampling altogether (if nothing else it will save you state changes on your samplers). – Maximus Minimus Jul 15 '14 at 20:49

So after experimenting a little more with Intel GPA following Sean Middleditch's comment, I noticed that messing around with state changes, including sampler states, seemed to give me no improvement whatsoever.

But sampler states got me thinking about texture formats, so I had another look at the DirectX documentation for a texture format that might be more optimal. Previously I was using R16G16B16A16 textures for all my render surfaces, but DXGI has the 32-bit packed format R11G11B10, which, according to some forum threads, is particularly well-suited to HDR rendering. I changed both my source and target textures to use this format, and BOOM, tone-mapping now takes about 0.65ms on my integrated gpu with no apparent loss of quality, which seems good enough for me.

Clearly I'd underestimated the importance of a compact texture format - I'd guess it has numerous benefits to caching and simplifying arithmetic, but I'd be interested to hear more detailed theory if anyone would like to comment. Worth trying if anyone else gets stuck on the same issue.

• It sounds like you're memory-bandwidth-limited. Taking the bandwidth to be 25.6 GB/sec for the HD 4000 (from here), and 1366x768x16 bytes, I get 0.66 ms needed just to write all the data. If you are both sampling a texture and writing one, we can assume the required bandwidth is doubled, and switching to R11G11B10 would halve it. So this seems to line up nicely with your results. (BTW, the benefit of the thinner format is simply that there's less data overall, nothing to do with caching or simplifying arithmetic.) – Nathan Reed Jul 16 '14 at 1:40

Maybe you can save some time if you don't bind a vertex buffer and use SV_VERTEXID (which is created automatically by the IA stage) to build your quad in the vertex shader, something like:

struct VSOUT{
float4 pos:SV_POSITION;
float2 tex:TEXCOORD0;
};

VSOUT main(uint vI: SV_VERTEXID)
{
VSOUT Out = VSOUT(0);
Out.tex=float2(vI%2,vI%4/2);
Out.pos=float4((Out.tex.x-0.5f)*2,-(Out.tex.y-0.5f)*2,0,1);
return Out;
}


The vertex shader will output a full screen quad this way if you call a draw for 4 vertices with a TRIANGLESTRIP topology;

Probably you can also save some time if you try out different rendertarget formats and resoulutions.

If you can have depth or stencil testing it can also save you some time in some scenarios.

• Author of SMAA suggests: "Remember to use oversized triangles instead of quads to avoid overshading along the diagonal." – Ondrej Petrzilka Jan 6 '16 at 15:26