WebGPU error: non-filtering sampler binding

Hello. I tried to render a few textures in rgba8unorm and rgba32float formats on WebGPU, and encountered the error:

Filtering sampler [Sampler] is incompatible with non-filtering sampler binding.
 - While validating entries[4] as a Sampler.
Expected entry layout: { binding: 5, visibility: ShaderStage::Fragment, sampler: { type: SamplerBindingType::NonFiltering } }
 - While validating [BindGroupDescriptor] against [BindGroupLayout]
 - While calling [Device].CreateBindGroup([BindGroupDescriptor]).

My browser doesn’t have “float32-filterable” feature enabled, so BJS detects it and automatically uses nearest sampler with “non-filtering” binding. It works well by itself, but when the scene has rgba8unorm and rgba32float formats mixed together, it produces the error above and renders nothing. I suspect some internal states are incorrectly shared.

Demo: https://playground.babylonjs.com/?webgpu#3DM8LJ. It renders nothing on WebGPU, and the expected result can be seen in WebGL2 mode.

Testing environment:

  • Windows 10 Home 21H2 19044.2728
  • Chrome 113.0.5672.93 (64-bit)
  • Babylon.js 6.3.1 and 6.2.0 (WebGPU)

" 32 bits float textures are not linear-filterable (see List of texture formats), meaning you can’t use the bi/tri linear filtering with them, for eg. You will need to use 16 bits float textures instead, until the spec (or an extension) adds support for it."

your 32 bit texture is using trilinear sampling

This isn’t an issue. As I mentioned, BJS detects it and automatically uses nearest sampler:

The bug is still reproduceable when TEXTURE_NEAREST_SAMPLINGMODE is used. I should have used it instead to avoid the confusion.

commenting these lines still broken for you?

No, it doesn’t break after commenting the lines in the box.

However, I do want to use float32 texture on webgpu, and babylonjs indeed handles it. The float32 texture actually works, if you comment out either “sphere1” or “sphere3” part below. In other words:

  • a scene with a unorm8 texture, a float32 texture: works
  • a scene with a float32 texture, a unorm8 texture: works
  • a scene with a unorm8 texture, a float32 texture, a unorm8 texture: error

The specific order “filterable, non-filterable, filterable” causes the error, which is clearly a bug. I digged into the source code but could not figure out what went wrong.

ah i see
working .https://playground.babylonjs.com/?webgpu#3DM8LJ#1
broken .https://playground.babylonjs.com/?webgpu#3DM8LJ#2

i think in order for it to autofix itself, it has to be be using float32 + a linear filter sampling mode on the SAME material, because its not looking through all materials. i think the fix would be to add a global flag and set it to true once a linear filter sample is specified, and check the global flag instead of the constructor arguments to determine if it needs to use the nearest sampling mode. make sense? im pretty tired lol. ah shit, it’d have to go back through and fix any previously created materials too…hmm

actually, i think it just needs more detailed checks.

Currently, if you use float texture and a tri/bi linear filter sampler, the conditional check will likely change your sampler mode to nearest sampling mode.

for example:
→ it fails test and you end up with:

then in a second material, you use:
BABYLON.Constants.TEXTURETYPE_UNSIGNED_BYTE (or anything except float here)

it passes check.so you get it.

now you have one material with a linear filter sampler and a float texture in another. KABOOM.

I think it would be better to FIRST change flat to half float.

if (!this._engine._caps.textureFloatLinearFiltering && type === Constants.TEXTURETYPE_FLOAT) {
            samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
if (!this._engine._caps.textureHalfFloatLinearFiltering && type === Constants.TEXTURETYPE_HALF_FLOAT) {
            samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;


//check all linear sampling modes here
if ( (samplingMode === Constants.TEXTURE_TRILINEAR_SAMPLINGMODE) || (samplingMode === BABYLON.Constants.TEXTURE_BILINEAR_SAMPLINGMODE) ) {
  // compare user defined precison to hardware caps

  //handle user asking for float (unlikely to be supported as of May 2023)
  if(type === Constants.TEXTURETYPE_FLOAT) {
    //unlikely, but its what they asked for so check it first.
    if(this._engine._caps.textureFloatLinearFiltering) { void(0) /*give'em what they want*/ };
    //very likely user device supports half float, even though they asked for float. just downgrade precision to half float
    if(this._engine._caps.textureHalfFloatLinearFiltering) {type = Constants.TEXTURETYPE_HALF_FLOAT};

    //this was the previous implementation's default behavior. bad apples and balmer's too sweaty. change sampling mode and keep float
    else samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;


  //handle user asking for half float (likely to be supported as of May 2023)
  if(type === Constants.TEXTURETYPE_HALF_FLOAT) {
    //very likely
    if(this._engine._caps.textureHalfFloatLinearFiltering) {void(0) /*give'em what they want*/ };

    //change sampling mode and keep half float
    else samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;


//feel so dirty with no assertions here

I believe the issue is deeper than just samplingMode downgrade. When I change all occurrences of TEXTURE_TRILINEAR_SAMPLINGMODE into TEXTURE_NEAREST_SAMPLINGMODE in the demo, it still fails, however with a different error:

Bind group layout [BindGroupLayout] of pipeline layout [PipelineLayout] does not match layout [BindGroupLayout] of bind group [BindGroup] set at group index 1.
1 Like

It seems there’s a problem with pipeline caching, if you create mat2 first it does work…

I will have to dig a bit more to find the problem.

Here’s the fix:

1 Like