Uniform arrays with non uniform control texture sample (WebGPU)

Hello everyone!

This is a followup question to Texture sampler and non uniform control flow

TLDR: uniform control flow analysis can be disabled in BabylonJS by adding /* disable_uniformity_analysis */ in the shader code.

The story could have ended here, and my code would be compatible with WebGPU (I am almost there I swear) but I finally managed to reproduce a very weird bug that is related to this non uniform analysis.

Here is a PG that can produce the bug: https://www.babylonjs-playground.com/#1PHYB0#329

To make the bug happen, you only need to uncomment line 112, which is a non uniform texture sample.

Normally this would not be an issue as I marked the shader as to ignore these non uniformities, but somehow, because I referenced an array of uniforms (here this is uniform vec3 lightPositions[2];), it creates this weird error of type alias:

WebGPU uncaptured error (1): [object GPUValidationError] - Tint WGSL reader failure: :2:13 error: invalid type alias
alias Arr = @stride(16) array<vec3<f32>, 2u>;

Indeed, when I remove the reference to the uniform array (PG: https://www.babylonjs-playground.com/#1PHYB0#330), the bug goes away :thinking:

I haven’t the faintest idea about why this is happening, if anyone can help, it would be much appreciated :pray:

1 Like

It may be because we are not using the latest version of Tint, because it is too big (two times bigger than the version we use!). We are waiting for the file to come back to a smaller size before we update our TintWASM package (they currently have two code paths and they are going to remove one, that’s why it is so big).

In your case, you can fix the problem by using textureLod instead of texture2D:

Or use textureGrad, if you still want trilinear mipmapping:

However, the default sampling mode for a post process is “nearest”, meaning no mipmaps are generated. If you want mipmaps, you will have to pass BABYLON.Constants.TEXTURE_TRILINEAR_SAMPLINGMODE for samplingMode when creating the post-process.

3 Likes

Thank you for the insights!

The solution with textureGrad does not work when the calls to dFdx and dFdy are inside a non uniform control flow: https://www.babylonjs-playground.com/#1PHYB0#334 It is probably also related to the TintWASM version.

textureLod does work indeed, but the loss of the trilinear mipmapping is a bummer and murders the image quality :confused: (and also the performance)

Is there a way to override the version of TintWASM used by BabylonJS? The file size is not really an issue for me in the development process.

In this case you could also move the derivatives call out of the condition: https://www.babylonjs-playground.com/#1PHYB0#335

Indeed it works, I think it is equivalent to what @Evgeni_Popov proposed earlier, but some of my use cases prevent this solution unfortunately.

For example in my atmosphere shader (https://github.com/BarthPaleologue/volumetric-atmospheric-scattering/blob/main/src/glsl/atmosphericScattering.glsl), I would like to sample a look up texture inside the calculateLight function to make it way faster. If I needed to somehow extract the texture coordinates all the way up the call hierarchy to find myself in a uniform control flow, it would make the code a mess, take a lot of time and could actually be infeasable :thinking:

If the new lighter version of TintWASM releases in a matter of weeks I can totally wait for it, but if it takes longer I would be interested in maybe overriding the version of TintWASM I use in my projects

you can pass both the js and wasm url as an option in the webgpuengineOptions:

Yes, see my PG, it must be done before the if.

Do it with textureGrad, there’s no perf problem by doing the dFdx and dFdy outside the if, these are fast functions.

Also, I don’t see why textureLod would lead to poorer performance… It does a bilinear instead of a trilinear filtering, so it should be faster on the contrary…

You can try to get the project locally and regenerate the .wasm yourself:

oooh nice! I will try that to rebuild the wasm and use the engine option.

I am sorry, my post was not super clear about textureGrad. It does work when outside the if, but in my shaders, putting the dFdx and dFdy outside will either lead to messy refactorings, and in the case of the atmosphere look up texture, it is just not possible as the dFdx initially be inside a for loop inside a function inside an if.

Yeah I am not sure either, but it’s is really noticable, I went from 60fps to 10fps when using 10 texture samples in an evolution of the playground shader (with triplanar normal mapping which increases the number of samples quite fast).

It could be that the textures are mostly sampled with low mips when using trilinear, whereas when using textureLod, it always samples the mip #0, which does not play well with texture cache…

1 Like

I have been trying to build the wasm from source today and encountered an error.

I used a Ubuntu 22.04 VM created only for this purpose.

My error happened at step 5 when running emcmake cmake ../twgsl:

CMake Error at CMakeLists.txt:9 (target_compile_options):
  Cannot specify compile options for target "tint_api" which is not built by
  this project.

I cloned all the repos in a folder called TintWASM. I had no error in previous steps beside not having the python command, which was fixed by installing pyenv.

Here is my path:

/home/barth/Documents/TintWASM/depot_tools:
/home/barth/Documents/TintWASM/emsdk:
/home/barth/Documents/TintWASM/emsdk/upstream/emscripten:
/home/barth/Documents/TintWASM/emsdk/node/16.20.0_64bit/bin:
/home/barth/.pyenv/versions/3.11.7/bin:
/home/barth/.pyenv/libexec:
/home/barth/.pyenv/plugins/python-build/bin:
/home/barth/.pyenv/plugins/pyenv-virtualenv/bin:
/home/barth/.pyenv/plugins/pyenv-update/bin:
/home/barth/.pyenv/plugins/pyenv-doctor/bin:
/home/barth/.pyenv/shims:
/home/barth/.pyenv/bin:
/home/barth/Documents/TintWASM/depot_tools:
/usr/local/sbin:
/usr/local/bin:
/usr/sbin:
/usr/bin:
/sbin:
/bin:
/usr/games:
/usr/local/games:
/snap/bin

Maybe someone got a similar error in the past?

Maybe @Cedric will be able to help here.

1 Like

I’ve just tried to kick a build and got this:

Error: Command 'python3 build/mac_toolchain.py' returned non-zero exit status 1 in /Users/runner/work/twgsl/twgsl/Dependencies/tint/.
Traceback (most recent call last):
  File "/Users/runner/work/twgsl/twgsl/Dependencies/tint/build/mac_toolchain.py", line 23, in <module>
    import pkg_resources
ModuleNotFoundError: No module named 'pkg_resources'

I’ll take a look why it happens.

I’ve fixed the Python package thing and build went well:

2 Likes

I trided again to do a clean install with the new version, and I still got the same error :confused:

Should the twgsl_build folder be at the same level as twgsl ? This is what I understood but maybe I must create it in a subfolder?

This is my file tree for the whole setup:

- ./depot_tools
- ./emsdk
- ./twgsl
- ./twgsl_build

yes, it looks like that.
Is there a ‘tint_api’ cmake target somewhere?

There are only 2 occurences of ‘tint_api’:

grep -r 'tint_api' .

./twgsl/Core/twgsl/CMakeLists.txt:target_link_libraries(twgsl PRIVATE tint_api)
./twgsl/CMakeLists.txt:target_compile_options(tint_api PRIVATE -Oz)

I do not have a deep understanding of CMake unfortunately, I always used it in simpler cases haha
Maybe I should try running CMake somewhere else before doing step 5?

It seems like something is missing
is there some files in Dependencies/tint ?

If it can help, here are the files generated by @Cedric 2 hours ago:

TWGSL.zip (862.0 KB)

It uses the latest version of Tint.

Wow thank you for the wasm! I will test it soon to check if the original issue goes away :ok_hand:

Yes there are quite a few! Even though gclient sync failed the first time because of python, I did a clean install afterward where it succedeed directly so that should not be the issue :thinking:

~/Documents/TintWASM/twgsl/Dependencies/tint$ ls -a
.                   .gclient_previous_sync_commits
..                  .git
AUTHORS             .gitignore
build               .gn
BUILD.gn            include
build_overrides     infra
buildtools          kokoro
.cipd               LICENSE
.clang-format       OWNERS
CMakeLists.txt      PRESUBMIT.py
CMakeSettings.json  README.md
CODE_OF_CONDUCT.md  samples
CONTRIBUTING.md     src
CPPLINT.cfg         standalone.gclient
DEPS                test
docs                testing
Doxyfile            third_party
fuzzers             tint_overrides_with_defaults.gni
.gclient            tools
.gclient_entries    .vscode

I imported the new Tint files in the playground here: https://www.babylonjs-playground.com/#1PHYB0#345

But the bug still remains (uncommenting line 119 performs a non uniform texture sample that crashes the playground only when using a uniform array). Maybe I did something wrong with the glslang options (there is a 3rd field that I did not define as I don’t understand what it does)

Is there a way to check that the engine is indeed using the new version in this playground?