floooh / sokol-tools

Command line tools for use with sokol headers
MIT License
215 stars 52 forks source link

How to share multiple uniform blocks between shaders? #46

Open Maeiky opened 3 years ago

Maeiky commented 3 years ago

I'm able to share my primary uniform bloc between vertex and fragment using the same name:

@vs vs
uniform vs_params {
    float time;
}A;
...
@end

@fs fs

uniform vs_params {
    float time;
}A;
...
@end

So I can use the "time" uniform in Vertex and Fragment.

But when I try to add a second bloc, it's doesn't work:

@fs fs
uniform vs_params {
    float time;
}A;

uniform test_fs_params { //error: conflicting uniform block definitions found for 'vs_params'
    float foo;
}B;
...
@end

I got this error:

error: conflicting uniform block definitions found for 'vs_params'

Maybe I'm misunderstood something ?


Edit: I found my problem, the compiler seems to "randomly" optimize out my uniform block in Vertex and/or Fragment Shader, So I got a mismatch.

This is very annoying, is there a ways to prevent the compiler from removing it?


Edit2: This is worse than I thought, there seems to be a random reordering according to the order of using the variables which also causes a mismatch. (So first use of uniform must be in same order both in Vertex and Fragment)

My 2 cents solution:

//KeepAlive
if(gl_VertexID < 0){gl_Position += 
time + foo; //Order is important
return;
}

Or in little more elegant way:

#define VS_KeepAlive(var) if(gl_VertexID < 0){gl_Position += var;return;}
void main() {
VS_KeepAlive(time + foo) //Order is important
...
floooh commented 3 years ago

This is very annoying, is there a ways to prevent the compiler from removing it?

I agree it's annoying (happens for all sorts of "bind slots", e.g. textures). I think (but am not entirely sure) this is caused by SPIRV-Tools optimizer passes (see here: https://github.com/floooh/sokol-tools/blob/ad016969eedfa3152f44ff7c4d4d460a4a5011d8/src/shdc/spirv.cc#L101-L154), those may do very deep structural changes to the program (some of which also generate code which is illegal in WebGL, thus the commented-out passes).

I'm really not sure what to do about those optimizer-passes. On one hand they are important for OpenGL implementations with bad drivers (similar to what glsl-optimizer does), on the other hand they are entirely unpredictable and cause problems with WebGL.

logankaser commented 1 year ago

From reading https://github.com/KhronosGroup/SPIRV-Tools/blob/master/include/spirv-tools/optimizer.hpp It looks like the optimizers are meant to be run on a full module, with multiple entry points, but it seems as if we are running it on each entry point seperately. Could this be why it thinks it's okay to modify these definitions differently?

logankaser commented 1 year ago

I also wonder if we could use ::SetValidateAfterAll(true); to figure out which pass is causing this.

logankaser commented 2 months ago

I was reading: https://github.com/KhronosGroup/SPIRV-Tools/pull/5524 When I saw .RegisterPass(CreateAggressiveDCEPass(preserve_interface))

https://github.com/s-perron/SPIRV-Tools/blob/ac11724763cec72056a10be759458fef9826bf83/source/opt/optimizer.cpp#L142 Turns out you can pass preserve interface (well, true to the position parameter) to CreateAggressiveDCEPass to force it to stop eliminating the external interface of stages!

logankaser commented 2 months ago

Oh, wait looks like we do that now, maybe this issue is resolved?

floooh commented 2 months ago

Yeah, sokol-shdc is using the DCEPass hints, but this doesn't help with all cases where unused uniforms are removed (I think what it mainly helped with was not removing unused shader inputs/outputs - most importantly vertex attributes).

Other then that there's not much sokol-shdc can do. The same problem can even happen at runtime in OpenGL drivers (e.g. OpenGL drivers are free to remove unused uniforms during shader compilation).

floooh commented 2 months ago

From reading https://github.com/KhronosGroup/SPIRV-Tools/blob/master/include/spirv-tools/optimizer.hpp It looks like the optimizers are meant to be run on a full module, with multiple entry points, but it seems as if we are running it on each entry point seperately. Could this be why it thinks it's okay to modify these definitions differently?

This is actually something we could explore, yeah.