5
\$\begingroup\$

As a DirectX noob i'm trying to wrap my head around depth buffers and specifically how pixel shaders are called for obscured pixels.

From what i understand, the rasterizer calls the pixel shader for each pixel that is covering the primitive beeing drawn, then later in the output merger stage, the output merger checks the depth buffer and either discards, writes or blends the pixel in the back buffer.

This seems wasteful though if i'm rendering a simple opaque object in front of a very complex one, so it seems it would be useful to have the rasterizer check the depthmap Before even calling the pixel shaders for the complex object.

Doing research i've found references to early Z test / conservative Z testing and so on, but there also seems to be very little documentation about it. i looked for a way to configure that on the rasterizer state desc object, but i only found anything like that on the OM state desc.

It also seems like this was possible to set with SetRenderState in DX9 (i have no experience with DX9 either though)

From my research it seems like this is something some hardware just does if i render objects front to back, is that correct? How can i even tell? With all the Control DirectX gives you it seems weird that there is no control over this, as it seems to be a good optimization :)

Any info or references on this is appriciated

\$\endgroup\$
3
\$\begingroup\$

It's completely automatic, so long as you don't do anything that might implicitly disable it.

Early Z and such are hardware optimizations, meaning that they're things hardware might do and are allowed to do or not to do as the result of early Z has no effect on the rendered image other than speed. It's similar to how a compiler or even CPU is allowed to transform or reorder your code so long as it doesn't change the actual output (assuming you don't rely on some specific result from undefined behaviour). This is the so called "as-if" rule, which is a formal part of some language specifications and just an informal/implicit rule of most other languages and APIs. Your role as an API users is simply to avoid doing things that might stop the hardware from using its optimizations rather than explicitly opting in or opting out.

For each pixel (fragment), the rasterizer will have already computed a "default" depth. This depth value is tested before the pixel shader is run. If the depth test fails (the pixel is obscured) then the pixel shader is not run. All automatic.

This is disabled if you do things like modify the depth output semantic in the pixel shader. In this case, the rasterizer doesn't know what depth to use until after it runs the pixel shader, so it can't do early Z culling.

A related topic is hierarchical Z. With this feature, the GPU takes advantage of the fact that most pixels drawn are part of a larger primitive and all other pixels nearby will be part of that same primitive and be at a relatively similar depth. Also, the primitive being drawn typically either fully obscures other primitives drawn or is itself fully obscured. Hierarchical Z breaks the scene into sub-regision, recursively, in an approach reminiscent of mipmaps (might even be the same thing on some hardware). In each lower layer of this hierarchy, the Z buffer stores only the furthest depth value (assuming you're using the standard depth scheme). Before testing a specific pixel's depth, the hierarchy is traversed downward. If any part of the hierarchy fails, the GPU knows that any pixel in that entire chunk would obscure the pixel being processed so it is culled. The hierarchy allows less depth value updates, tighter memory access, and so on, which account for sometimes significant drawing advantages. This feature is again free so long as you don't use weird depth setups or draw in crazy ways that would subvert the assumptions the algorithm is based on.

\$\endgroup\$
  • \$\begingroup\$ So using the depth semantic is how Dx knows i modify the depth? i wonderd how that worked :) \$\endgroup\$ – aL3891 Jul 27 '13 at 17:58
  • 1
    \$\begingroup\$ Using it as an output parameter, yeahy. Again, it's a driver/hardware "hidden" optimization, so it's not really defined, but that's the most obvious and direct way for them to detect it. In OpenGL, the driver generally just checks during shader compilation if you assign to gl_Depth, which is OpenGL's equivalent to using semantics. \$\endgroup\$ – Sean Middleditch Jul 27 '13 at 18:05
  • \$\begingroup\$ Here's a sub-question that's not worth an actual question. GL 4.2+ has a way of forcing early-z by using a specific fragment shader setting. This also turns off the ability to write to gl_Depth (so that the value that gets tested with the depth is the value that gets written if it passes). This is mainly for image load/store operations. Does D3D 11 have something similar? And if so, how does it work? \$\endgroup\$ – Nicol Bolas Jul 27 '13 at 19:11
  • 1
    \$\begingroup\$ @SeanMiddleditch: That sounds more like conservative depth, rather than an enforced depth test. It's a hint you use so that early depth can still happen even if you change the depth, because you promise not to make it smaller or larger than the original. \$\endgroup\$ – Nicol Bolas Jul 27 '13 at 20:54
  • 2
    \$\begingroup\$ [earlydepthstencil] - see msdn.microsoft.com/en-us/library/windows/desktop/… \$\endgroup\$ – Maximus Minimus Jul 27 '13 at 21:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.