Open geekley opened 2 months ago
I didn't check, but I assume current implementation may be using something like Expression? I believe #93822 may be useful for this, if I understand correctly?
Another issue related to #if
expression evaluation (that would need fixing in the new implementation) is shown here.
The operators &&
and ||
should be short-circuit operators that may not evaluate the next part.
However, macro names that are not defined still generate an error even if trying to precede them with a defined
guard.
bug-should-short-circuit.gdshader
//shader_type spatial;
#if defined(foo) && foo
const int one = 1;
#endif
#if !defined(foo) || foo
const int two = 2;
#endif
cd /tmp
nano a.c # edit the files
cat a.c > a.frag; echo '=== GLSL ==='; glslang -E a.frag; echo '=== C ==='; gcc -E a.c
GLSL (and C of course) allow it, deactivating the first #if
branch, resulting in:
const int two = 2;
GDShader raises "Condition evaluation error" on both #if
directives. It should match GLSL behavior instead.
You can tell the operators have short-circuit semantics because without the preceding defined
guards, GLSL (which unlike C doesn't interpret undefined macros as 0
) complains:
'preprocessor evaluation' : undefined macro in expression not allowed in es profile foo
But note that it seems only parameter-less macros are "syntactically" allowed after expansion. If the right part is foo(x)
but a foo
macro is not defined, C and GLSL both raise a "syntax" error on the expression:
GLSL: '#if' : unexpected tokens following directive
C: error: missing binary operator before token "("
So this is what I think GLSL is doing:
defined(...)
keywords first (e.g. to " 0 " or " 1 ")Just found out about the defined macro_name
syntax (without parentheses).
It's supported (like in C and GLSL), except it's a bit buggy in the current implementation.
//shader_type spatial;
#if !defined apple && defined banana
const int zero = 0;
#elif !defined apple && !defined banana
const int one = 1;
#endif
C and GLSL accept it just fine. GDShader raises "Condition evaluation error".
Tested versions
System information
Godot v4.3.stable (77dcf97d8) - Freedesktop SDK 23.08 (Flatpak runtime) - X11 - Vulkan (Forward+) - integrated Intel(R) HD Graphics 5500 (BDW GT2) - Intel(R) Core(TM) i5-5300U CPU @ 2.30GHz (4 Threads)
Issue description
In GDShader
#if
condition expressions, you can use several operators (more than I expected it to allow) and even functions (e.g. math likesqrt
,sin
), which will be evaluated in the preprocessor. Which ones are undocumented.At first I expected those to at least match the GDShader functions and operator syntax. But I found it's not always the case (e.g. ternary
? :
doesn't work,inversesqrt(...)
doesn't either). After some more tests (see https://github.com/godotengine/godot/issues/96253#issuecomment-2318882070 for background), I found it's accepting functions in@GlobalScope
as well as some constructors likebool
etc. It allows even functions very weird to allow, likerandf
,instance_from_id
andrid_from_int64
. Allowing even stuff likebytes_to_var_with_objects
seems like it could be particularly dangerous (ACE risk?). But I don't know for sure to which extent it allows functions with side-effects and whether it affects the editor.In any case, I'm surprised things like functions work at all. I was expecting that dealing with integers and booleans would be enough for a preprocessor. In fact, if the intention is to match C/GLSL, then GDShader is doing way more than it should in the preprocessor
#if
directives. Not even C deals with float or boolean constants in the preprocessor, let alone math functions. GLSL doesn't either. They do handle integers without theu
suffix (so nouint
support). C does handle arbitrary names, treating them like0
(eventrue
is treated like0
), but GLSL doesn't allow them on#if
. GDShader allows most literals (bool, int_decimal, int_hex, uint_decimal, float), except for uint_hex like0x0u
.Comments from @pirey0:
IMO, GDShader should match GLSL, and be as safe as possible:
0
true
isn't a keyword, it won't expand to0
by default; raise "undefined macro" error instead&&
and||
) and the final#if
expression boolean resultu
suffixdefined(macro_name)
(alsodefined macro_name
) preprocessor keyword is the only "function" allowedA lot of these behaviors are explicitly defined in the GLSL ES 3.00 spec pages 13~14.
Moved from #96253 (sub-issue 7).
Steps to reproduce
bug-global-fn-evaluation.gdshader
Minimal reproduction project (MRP)
N/A