Repository navigation
Add an opt-in reversed depth buffer - #7860
dustinspace217 wants to merge 2 commits into
Conversation
Distant surfaces flicker against each other because a 24-bit depth buffer with a 1 m near plane and a 1000 km far plane has almost no precision left at long range. With the new Reversed Depth option (or the -reversed_depth flag, which also works where in-game options are off), the near plane maps to depth 1 and the far plane to 0, closer surfaces pass with GREATER, and the scene depth buffers are 32-bit float, which keeps precision nearly constant out to the far plane. The reversed perspective terms are written out directly, because flipping the normal ones loses enough float precision to move the far plane by about 5%. Vulkan's depth range is already [0, 1]. OpenGL needs glClipControl to use [0, 1], so it is turned on only with OpenGL 4.5 or ARB_clip_control and otherwise the option is ignored. Shadow maps are reversed too, since they share the depth test, depth bias changes sign, depth clears use 0, and the shaders that read depth (fog, volumetrics, decals, soft particles, lightshafts, fog distance, shadow lookup) follow a new REVERSED_Z define, so with the option off every shader is unchanged.
BMagnu
left a comment
There was a problem hiding this comment.
This conflates multiple depth buffers, some of which do not behave like typical buffers.
We have the position G-buffer, which feeds some of the shaders like effects-f.
We also have the actual depth buffer, used for depth testing in shadows and rendering.
Reverse depth is useful for the actual depth test. It is decidely not useful for the G-buffer, because we already have purpose built Z-compression active there, which would in fact get substantially worse with flipped Z.
So this PR would need to ensure that the G-buffer is not touched and stays identical.
Similarly, I would really like for the PR to not do multiple things at once for one flag.
Why does activating reverse-Z increase the precision of the depth buffer? That should be a seperate flag, if at all, and only if we figure out that that actually substantially benefits us for the performance it costs.
Also, as a general question:
Does this actually fix an observed issue? Otherwise this is a fair bit of maintenance burden, especially with both passes active, that I'd like to not have if not necessary.
glad is regenerated at its existing settings (0.1.36, core 4.3, no loader) with GL_ARB_clip_control added, so reversed depth checks GLAD_GL_ARB_clip_control like the other extensions instead of scanning the extension list by hand. The current generator also makes the extension loop index unsigned.







What?
Adds an opt-in reversed depth buffer: a "Reversed Depth" graphics option, plus a
-reversed_depthlauncher flag that works where in-game options are off. It's off by default.Why?
Space scenes put a cockpit a meter away and capital ships kilometers away in the same depth buffer. With the default 1 m near plane, 1000 km far plane and a 24-bit buffer, the depth step at 5 km is about 1.5 m, so overlapping hull plates and details on distant ships flicker against each other. Reversed depth with a floating-point buffer keeps the step at a tiny fraction of the distance all the way out.
How?
glClipControlto get the same range, which is core in 4.5 or available as ARB_clip_control. The bundled loader doesn't include it, so it is fetched by name at startup. Without it, as on macOS or GLES, the option is ignored and depth works as before. On OpenGL the scene, cockpit, back and MSAA depth buffers and render-target renderbuffers become 32-bit float with stencil.REVERSED_Zdefine: fog, nebula volumetrics, decals, soft particles, lightshafts, the per-vertex fog distance and the shadow lookup. With the option off every shader is unchanged.glClipControlcomes from glad, regenerated at the existing settings withGL_ARB_clip_controladded.Screenshots?
SM1-01 with a transport moved in front of the player, and SM1-05 in the full nebula, option off and on, on OpenGL and Vulkan (attached). They look the same, which is the point.
Testing?
-msaa 4.-no_deferred, render-to-texture, and a Vulkan device that only offers a 24-bit depth format. On a driver without clip control the option is ignored, so it behaves as today. The flicker reduction itself is shown by the arithmetic and the unit test; I didn't find a way to measure it in a headless capture.Anything Else?
While checking every depth reader I noticed three existing issues that aren't part of this PR: Vulkan fog uses the OpenGL depth formula, Vulkan's depth bias maps OpenGL's two parameters the other way round and with the opposite sign, and the soft particle depth formula skips the
* 2 - 1step. Happy to send those separately. I used Claude Code to inventory the depth-dependent code, write the change and run the comparisons.