Skip to content

Cannot reliably specify depth biases with depth24plus #6350

Open

Description

@magcius

https://gpuweb.github.io/gpuweb/#ref-for-dom-gputextureformat-depth24plus says:

The depth component of the "depth24plus" and "depth24plus-stencil8" formats may be implemented as either a 24-bit depth value or a "depth32float" value.

https://gpuweb.github.io/gpuweb/#abstract-opdef-biased-fragment-depth says:

If format is a unorm format:

Let bias be (float)state.depthBias * r + state.depthBiasSlopeScale * maxDepthSlope.

Otherwise, if format is a float format:

Let bias be (float)state.depthBias * 2^(exp(max depth in primitive) - r) + state.depthBiasSlopeScale * maxDepthSlope.

Since it is unclear whether depth24plus is a unorm format or a float format, it is impossible to reliably specify depth biases that work. It is also not possible to query whether depth24plus is backed by a unorm format or a float format, as far as I'm aware.

Activity

  1. added this to the Milestone 2 milestone on Aug 11, 2026
  2. magcius commented on Aug 11, 2026

    @magcius
    Author
  3. added
    apiWebGPU API
    and removed
    wgslWebGPU Shading Language Issues
    on Aug 11, 2026
  4. jimblandy commented on Aug 11, 2026

    @jimblandy
  5. kainino0x commented on Aug 11, 2026

    @kainino0x
    Contributor

    TBH I don't understand depth bias super well, but my understanding is that depthBias (Vulkan depthBiasConstantFactor ) is basically supposed to be in units of "ULPs" (units in last place) for both float and unorm, and thus the same value should work approximately the same regardless of the format.

    That 2^(exp(max depth in primitive) - r) seems wrong in several ways, but I think it intends essentially 1 ULP at (max depth in primitive). Vulkan computes this as follows as follows:

    Otherwise for floating-point depth attachment, there is no single minimum resolvable difference. In this case, the minimum resolvable difference for a given polygon is dependent on the maximum exponent, e, in the range of z values spanned by the primitive. If n is the number of bits in the floating-point mantissa, the minimum resolvable difference, r, for the given primitive is defined as

    r = 2e−n

    (Note our algorithm for unorm seems wrong as well: it assumes Vulkan's depthBiasExact is true.)


    Anyway I assume since you filed this issue you ran into some trouble with the assumption that this would work for both formats. Would love to know more.

  6. magcius commented on Aug 12, 2026

    @magcius
    Author

    The D3D11 specification has something similar. I imagine this is where the WebGPU spec text originated from:

    When UNORM Depth Buffer is at Output Merger (or no Depth Buffer):
        Bias = (float)DepthBias * r + SlopeScaledDepthBias
    
        Where r is the minimum representable value > 0 in the depth buffer
        format, converted to float32.
    
    When Floating Point Depth Buffer at Output Merger:
        Bias = (float)DepthBias * 2^(exponent(max abs(z) in primitive) - r) +
                SlopeScaledDepthBias
    
        Where r is the # of mantissa bits in the floating point representation
        (excluding the hidden bit), e.g. 23 for float32.
    

    Anyway I assume since you filed this issue you ran into some trouble with the assumption that this would work for both formats. Would love to know more.

    I don't have any data on this in WebGPU itself (all my test devices have 24-bit support), but I have past experience with this on different engines, and found that I needed to drastically change my depth bias constants when changing depth buffer precision. Even if it's 1ULP, that amount would change between pretty drastically between a 24-bit unorm format and an imagined 32-bit unorm format.

  7. Kangz commented on Aug 18, 2026

    @Kangz
    Contributor
    GPU Web WG 2026-08-12 Atlantic-time
    • KN: Did research on this, Jasper says that there’s … <missed this>
    • Easiest solution would be to adjust what depth24plus is, make format explicit
    • Could also make depth24unorm optional and force developers to choose between that and depth32float.
    • CW: What are the possible formats?
    • KN: 16unorm, 24plus, 32float
    • CW: depth24unormstencil8 is optional
    • JB: Sounds like we don’t know what people are seeing in the field? Would it change our decision if we knew what they were seeing?
    • KN: As long as we trust that that’s true, no.
    • CW: Is there a world where we can say there’s multiple extensions and depth24unorm comes with the stencil8 version? Similar to compression we guarantee that at least one is exposed. Then once it’s in all stable channels developers can rely on at least one. Can’t guarantee because Vulkan doesn’t mandate.
    • KN: It makes sense, is consistent with everything else. But in what situation would you use depth24unorm?
    • CW: It’s a smaller format. Depth32float tends to have 4 bytes for stencil
    • KN: We have depth24plus for people worried about memory usage
    • JB: Unless we tell people what they’re getting they can’t get reliable biasing behavior.
    • CW: Even if this is only useful for depth bias, it would be good to have explicit formats rather than inferring. Nice part is that if they don’t use extensions they are required to use portable formats (depth24plus) even if depth bias is unpredictable.
    • KN: I think so. Are we convinced that this is enough of an issue to make a change?
    • MW: Share Kai’s concern that we may not need to do anything here. Not clear what format supports these formats. 24 bit depth buffers are deprecated in Metal.
    • CW: Is our suggestion to tell people to rely on Depth32_ to have consistent behavior?
    • KN: Wouldn’t hurt to expose which one it is. (depth24plus internal format)
    • CW: We have a number of options here. Let’s consult with Jasper about how big of a deal this is.
    • 3 options:
      • Do nothing
      • Expose depth24plus internal format in adapter info
      • Depth24unorm extension
  8. magcius commented on Aug 19, 2026

    @magcius
    Author

    Exposing depth24plus format information is fine by me. I agree this is not a major issue, just brought it up as a potential compat hazard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    apiWebGPU API

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions