Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to share multiple uniform blocks between shaders? #46

Open
Maeiky opened this issue Jan 2, 2021 · 7 comments
Open

How to share multiple uniform blocks between shaders? #46

Maeiky opened this issue Jan 2, 2021 · 7 comments

Comments

@Maeiky
Copy link

Maeiky commented Jan 2, 2021

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
...
@Maeiky Maeiky closed this as completed Jan 2, 2021
@Maeiky Maeiky reopened this Jan 2, 2021
@floooh
Copy link
Owner

floooh commented Jan 7, 2021

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:

/* this is a clone of SpvTools.cpp/SpirvToolsLegalize with better control over
what optimization passes are run (some passes may generate shader code
which translates to valid GLSL, but invalid WebGL GLSL - e.g. simple
bounded for-loops are converted to what looks like an unbounded loop
("for (;;) { }") to WebGL
*/
static void spirv_optimize(slang_t::type_t slang, std::vector<uint32_t>& spirv) {
spv_target_env target_env;
if (slang == slang_t::WGPU) {
target_env = SPV_ENV_WEBGPU_0;
}
else {
target_env = SPV_ENV_UNIVERSAL_1_2;
}
spvtools::Optimizer optimizer(target_env);
optimizer.SetMessageConsumer(
[](spv_message_level_t level, const char *source, const spv_position_t &position, const char *message) {
// FIXME
});
optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
/*
optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
*/
optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass());
optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
optimizer.RegisterPass(spvtools::CreateSimplificationPass());
optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
// NOTE: it's the BlockMergePass which moves the init statement of a for-loop
// out of the for-statement, which makes it invalid for WebGL
// optimizer.RegisterPass(spvtools::CreateBlockMergePass());
// NOTE: this is the pass which may create invalid WebGL code
// optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
optimizer.RegisterPass(spvtools::CreateIfConversionPass());
optimizer.RegisterPass(spvtools::CreateSimplificationPass());
optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
spvtools::OptimizerOptions spvOptOptions;
spvOptOptions.set_run_validator(false); // The validator may run as a seperate step later on
optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
}
), 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
Copy link

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
Copy link

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

@logankaser
Copy link

logankaser commented Jun 10, 2024

I was reading: KhronosGroup/SPIRV-Tools#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
Copy link

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

@floooh
Copy link
Owner

floooh commented Jun 11, 2024

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
Copy link
Owner

floooh commented Jun 11, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants