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

Make Godot GLSL shader files interoperable with standard GLSL tools #11274

Open
danderson opened this issue Dec 1, 2024 · 13 comments · May be fixed by godotengine/godot#100264
Open

Make Godot GLSL shader files interoperable with standard GLSL tools #11274

danderson opened this issue Dec 1, 2024 · 13 comments · May be fixed by godotengine/godot#100264

Comments

@danderson
Copy link

danderson commented Dec 1, 2024

Note: I am volunteering to ship this myself, if this proposal is acceptable to Godot devs. I have a working prototype already, and I volunteer to get it merged and update documentation.

Describe the project you are working on

I'm experimenting with procedural generation algorithms. For many of them I use compute shaders to speed up the generation process.

Describe the problem or limitation you are having in your project

Godot uses a non-standard GLSL format. Conceptually, the format stores several independent shaders in a single file, separated by #[shader_stage_name] markers (one of vertex, fragment, tesselation_control, tesselation_evaluation, compute). A special #[versions] section also exists, which allows the user to define "variants" of shader with C preprocessor directives. This format is used internally by Godot renderers for the shader code they need, and it's also the user facing format for compute shaders. In the case of compute shaders, the format must be obeyed but only #[compute] and #[versions] sections can appear in the file.

This format is specific to Godot, which means that standard GLSL tools don't function well on Godot shader files. Take for example the trivial compute shader from the manual:

#[compute]
#version 450

// Invocations in the (x, y, z) dimension
layout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in;

// A binding to the buffer we create in our script
layout(set = 0, binding = 0, std430) restrict buffer MyDataBuffer {
    float data[];
}
my_data_buffer;

// The code we want to execute in each invocation
void main() {
    // gl_GlobalInvocationID.x uniquely identifies this invocation across all work groups
    my_data_buffer.data[gl_GlobalInvocationID.x] *= 2.0;
}

If you look at the GLSL specification, this file is invalid in 2 ways:

  • #[compute] is not a valid GLSL preprocessor directive. Section 3.3 of the spec lists the valid directives, and explicitly says that directives not listed result in a compiler error. Empirically, all GLSL tools (linters, IDE extensions, compilers) I've tried do indeed choke on this directive.
  • #[compute] must come before #version, because the Godot format requires all code to be inside a section. However, section 3.3 of the spec says that the #version directive must appear before anything else, except comments and empty lines.

This only gets worse if you add a #[versions] section, because the contents of the versions section is not valid GLSL syntax at all.

This makes it hard to work with compute shaders in Godot (and, I presume, also rendering shaders, but I don't know). Basic syntax highlighting works, but other IDE functionality is degraded: there is no intelligent complete/navigation because GLSL language servers can't parse the file, there is a constant sea of red underlines from the IDE trying and failing to parse things, and so on. It's not a fatal problem, but it makes shader hacking in Godot quite unpleasant.

This also causes an adoption problem: you can't just grab some shader code off the internet, or from a GLSL shader library, and drop it in your Godot project. Again this is not a huge barrier since you usually just have to add #[compute] at the top, but it's a less smooth experience than in other engines. It also forces new developers to learn GLSL without good IDE support, if they want to use Godot.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

The discussion in #10880 sketches a solution to this (which is also detailed below) : make "Godot-style GLSL" compatible with regular GLSL. This means replacing the #[section] marker syntax with a combination of compiler #pragma directives and preprocessor #ifdefs. The result is a shader file that is standard GLSL, with additional Godot-specific hints to support multi-stage and multi-version shaders.

In addition (although this can be separated into a different proposal), add support for specifying the type of simple shaders in the filename, e.g. myshader.compute.glsl gets compiled as a compute shader with no extra work needed.

Obviously, we cannot break compatibility with existing Godot GLSL shaders. Fortunately, this proposal is completely backwards compatible: all existing shader code keeps working exactly the same, the shader loading code can trivially detect the format per-file and adapt.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

I got carried away a little bit while prototyping, and implemented this proposal already 😅 , if you prefer to just read code: godotengine/godot@master...danderson:godot:push-xvurqvqkplyr . I broke down the change into small incremental commits, so I recommend reading one commit at a time, for smaller diffs. Please consider this just a prototype to show that it can be done with low pain, and to try the proposed format interactively. I'm happy to adjust the implementation based on feedback, of course!

The new logic is entirely in RDShaderFile, which implements the current Godot-style GLSL format. Conceptually, RDShaderFile acts as a pre-preprocessor stage that figures out what shader stages and variants are present, assembles the correct source code for each one, and invokes the RD's SPIR-V compiler for each.

I added a second parsing codepath in RDShaderFile, which processes the new format (described below). The top-level parse_versions_from_text method uses a quick heuristic to figure out which of the 2 formats it's looking at, and delegates to either the existing format parser, or the new one. Additionally I factored out a few common helpers that both parsers use, to reduce duplication and improve readability.

Everything else stays the same. Aside from a tiny API expansion to support filename-based stage identification in the editor, the rest of Godot is unaware of the new format. The editor resource for GLSL shaders just hands the source text to RDShaderFile, and gets back a pile of compiled SPIR-V and possibly compiler errors. Similarly the GLSL compiler itself doesn't need any changes, it just receives slightly different source text from RDShaderFile.

New file format

The new format is standard, spec-compliant GLSL. Two compiler pragmas tell Godot the desired shader stages and variants. For each {stage, variant}, the entire file is sent to the GLSL compiler, with two additional preprocessor #defines that specify the stage and variant. The shader code can then use standard #ifdefs to specialize the code as it likes.

Here is the compute shader from the Godot manual in this new format (compare to old one shown earlier):

#version 450

#pragma godot_shader_stages(compute)

// Invocations in the (x, y, z) dimension
layout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in;

// A binding to the buffer we create in our script
layout(set = 0, binding = 0, std430) restrict buffer MyDataBuffer {
    float data[];
}
my_data_buffer;

// The code we want to execute in each invocation
void main() {
    // gl_GlobalInvocationID.x uniquely identifies this invocation across all work groups
    my_data_buffer.data[gl_GlobalInvocationID.x] *= 2.0;
}

This shader is trivial since it only has one stage and no variants, so all that's required is to declare the desired stage with the godot_shader_stages pragma. Alternatively, if the file is saved as blahblah.compute.glsl, the stage is deduced from the file extension, and the #pragma is not required.

Here's a skeleton of a more complex shader file, which has both vertex and fragment shaders as well as 2 versions:

#version 450

// This file contains both vertex and fragment shaders.
// The user also wants a 2nd variant with more expensive raymarch processing
#pragma godot_shader_stages(vertex, fragment)
#pragma godot_shader_versions(standard, raymarching)

// Common parts shared by all stages/variants
#include "common_defs.glsl"

struct Vertex {
    vec3 position;
    vec3 normal;
    vec2 uv;
};

// Vertex shader specific code. Godot defines this macro when compiling the vertex stage only
#ifdef GODOT_STAGE_VERTEX

// Vertex specific layouts/etc. here

void main() {
    do_common();

  // Code for standard variant only
#ifdef GODOT_VERSION_STANDARD
    fast_vertex_process();
#endif
  // Code for raymarching variant only
#ifdef GODOT_VERSION_RAYMARCHING
    cast_lots_of_rays();
    make_super_pretty();
#endif

    cleanup_common();
}

#endif // GODOT_STAGE_VERTEX, end of vertex shader

// Fragment specific code
#ifdef GODOT_STAGE_FRAGMENT

#include "fragment_stuff.h"

void main() {
    calculate_albedo();
#ifdef GODOT_VERSION_RAYMARCHING
    // The raymarching vertex shader gave us extra info we can use
    adjust_occlusion();
#endif
}

#endif // GODOT_STAGE_FRAGMENT

Of course this is more complex, and standard GLSL tools will need some manual configuration to set the right GODOT_{STAGE,VERSION}_* macros. However, this is fairly easy to do, and is only necessary for these more complex "multi-shaders". The one-stage, one-version compute shader above can be processed by standard tools out of the box. This also leaves open the possibility of tools adding explicit Godot support: the GLSL spec says that unknown #pragmas must be ignored, but in future third party tools could learn the Godot pragmas, and do the right thing even for complex shaders. This is not required, just a possibility for the future.

For a more realistic example, I picked a random complex shader from the Godot source tree (lm_blendseams.glsl, a vertex+fragment shader with 2 variants) and ported it to this new format. You can see the result in https://gist.github.com/danderson/f5b518b66c1b7670016b3a817cbefbd5. (Note this was done on an earlier prototype with different pragma names, but the semantics are the same).

Detailed semantics

These details are easy to change, as long as we stick to some requirements:

  • The source file must be standard GLSL.
  • It must be possible to easily disambiguate the old vs. new formats for parsing.

The parser detects the format by looking for a line that starts with #[ (not standard GLSL, use old parser) or #version (this cannot appear before #[ in the old format, use new parser).

The old parser is unchanged, except for some refactoring to extract helper functions.

The new parser scans the source file and handles the following specially:

  • #version: remember for use later when compiling.
  • #include: handled identically to current parser.
  • #pragma: ignore non-Godot pragmas. For Godot pragmas:
    • No duplicates allowed, e.g. only one #pragma godot_shader_stages(...) per file.
    • The only two valid Godot pragmas are godot_shader_stages and godot_shader_versions. Any other godot_* pragma is a compile error (to reserve the namespace for future additions).
    • The valid values for godot_shader_stages are vertex, fragment, tessellation_control, tessellation_evaluation and compute. For backwards compatibility, we also allow tesselation (typo, with one L). Unknown values, or duplicates, are a compile error.
    • The valid values for godot_shader_versions are ASCII identifiers. No duplicates allowed, and duplicate checking is case insensitive: you can declare a version tRiAnGlEs if you like pain, but you can't have another version triangles as well.

Rules for a structurally valid shader file:

  • At least 1 shader stage declared, either with #pragma godot_shader_stages or derived from the filename.
  • Compute shaders cannot coexist with render shaders (I just carried this constraint over from the existing parser).
  • Version/variant declaration is optional. If the file does not request versions, we compile a single default "" version just like the old parser.

If the file is structurally valid, then for each stage+version:

  • Construct adjusted source file: #version header, then Godot #defines, then the rest of the source file (with any #includes inlined).
    • When compiling shader stage blah, we add #define GODOT_STAGE_BLAH.
    • When compiling version foo, we add #define GODOT_VERSION_FOO. If the user didn't ask for custom versions, we don't define GODOT_VERSION_....
  • Hand this source to the RD's GLSL compiler. The GLSL compiler handles all other preprocessor directives (non-godot pragmas, #ifdef, macro expansion, etc.).
  • Record the bytecode and compiler errors, exactly like the old parser.

Implementation roadmap

Assuming this proposal is acceptable and we resolve any open questions, I volunteer to ship this feature. This means:

  • Polish my prototype code, send PRs, incorporate feedback, all the usual software development stuff :)
  • Update the manual's compute shader section to use the new format, including: suggestion that users use an IDE with GLSL support to get nice things when editing their shaders, and leaving a side-note about the current format to say that it still works and isn't going anywhere.
  • Maybe port Godot's internal GLSL files to the new format? It's trivial to do, I just don't know if renderer devs would like me to do this, or leave their shaders alone :)

Open questions/syntax war 😂

(assuming the proposal in general sounds good)

  • What names do we want for the pragmas?
    • The prototype uses godot_shader_stages and godot_shader_versions to reserve a Godot-specific prefix, and match the names from the existing format. This also matches the prefix for the macro namespace in C#, for consistency.
  • What names do we want for the preprocessor macros?
    • The prototype uses GODOT_STAGE_<uppercase stage name> and GODOT_VERSION_<uppercase version name>. Again reserving a prefix for Godot macros, and and matching the names to the names of the pragmas.
  • If we keep the stage-from-filename logic, what file extensions to use?
    • For prototyping I made it very explicit: .vertex.glsl, .fragment.glsl, .tessellation_control.glsl, .tessellation_evaluation.glsl, .compute.glsl. I decided to keep the .glsl suffix so that the shader language is immediately obvious, and so that in future if Godot adds HLSL/WGSL/SPIR-V/whatever support, RDShaderFile doesn't have to start using horrible heuristics to guess which compiler to use. I used the same stage names that RDShaderFile uses internally, just to avoid defining yet another set of names for everything, but that means they are quite verbose, especially the tessellation shaders.
  • Should we allow GLSL files that don't declare any shader stage? Currently #includes are painful because Godot tries to import them and reports errors. We could say that a .glsl file with no declared stages is "valid", we just don't invoke the SPIR-V compiler and the editor says there are no shader versions/stages available (or maybe it has UI to say "this looks like an include file, click one of these buttons to turn it into a top-level shader of type X" ?).
  • Should we allow compute shaders to coexist with render shaders? The current parser doesn't allow it, but I couldn't find a reason. Maybe it would be useful as the Godot renderers get more advanced, to have a file that includes compute+vertex+fragment stages all in one? I don't know.

If this enhancement will not be used often, can it be worked around with a few lines of script?

I expect this to be used by anyone writing compute shaders for Godot projects, as well as people writing custom render shaders for advanced uses (or Godot devs hacking on the renderers).

The only workaround is what some people do today: put the actual shader code in a separate include file, and include it from the non-standard top level GLSL file. This works, but isn't very ergonomic because every time you edit the included shader the editor throws compile errors complaining that there is no #[blah] in the include file, and you end up with 2 GLSL files for even a trivial shader.

The other "status quo" workaround of course is to ignore the IDE sadness when editing your shaders, and live without autocomplete/linting/etc. Obviously this "works", but it's not a good user experience IMHO.

Is there a reason why this should be core and not an add-on in the asset library?

Using compute shaders in Godot should be a pleasant default experience without requiring external plugins/add-ons, IMHO. Enabling the use of standard GLSL source code and tools is a big enough usability/quality of life improvement to justify being in core.

The code burden is also quite low: +255 lines of code net for my prototype, and I aimed for readability/maintainability rather than "clever" compact code. The change is isolated to the internals of a single class, and the +255LoC includes full backwards compatibility with existing shaders. This seems like a small price to pay in exchange for the ability to use standard compliant GLSL source code.

Proposal changelog

@danderson
Copy link
Author

From RocketChat, I learned that render pipeline shaders include several other godot-specific preprocessor-ish statements that aren't valid GLSL, used for injecting code from user visual shaders in the right places. Looking through the Godot source tree, I found the following additional statements:

#CODE : FOG
#CODE : FRAGMENT
#CODE : LIGHT
#CODE : PROCESS
#CODE : SKY
#CODE : START
#CODE : VERTEX
#GLOBALS
#MATERIAL_UNIFORMS

Their purpose is pretty self-explanatory. They are processed separately from the other macros, in servers/rendering/rendered_rd/shader_rd.cpp.

These have the same issue as the macros in the original proposal: GLSL preprocessor macros are a closed set, and compliant parsers will reject files containing unknown macros. So, these also break tooling like #[blah] and #VERSION_DEFINES.

We can fix these in the same way, by turning them into compiler pragmas. Unlike macros names, pragmas are an open set and compilers ignore pragmas they don't understand. A possible syntax: #pragma godot_user_code(light), #pragma godot_user_globals, #pragma godot_user_material_uniforms. The syntax is the easiest part to change, so again don't get too attached to the specific random names I made up, I'm happy to change those according to dev preferences :)

We can also break up this proposal into two smaller pieces, for ease of reviewing the code: first, focus only on compute shader ergonomics, which means only addressing the original #[foo] and #VERSION_DEFINES macros. Then in a second step, we can tackle the remaining macros that affect renderer shaders. I'm also happy to implement both at the same time, as long as there is consensus on the general direction I can divide it any way that makes it easier to handle the PRs.

@stuartcarnie
Copy link

I did notice is that an empty uniform struct is not valid, such as the following:

#ifdef MATERIAL_UNIFORMS_USED
/* clang-format off */
layout(set = 1, binding = 0, std140) uniform MaterialUniforms {
#pragma godot_user_material_uniforms
} material;
/* clang-format on */
#endif

For example, in CLion, with the glsl plugin:

I think that is a lint, vs the other being invalid code.

@danderson
Copy link
Author

Hmm, indeed, empty structs are forbidden in GLSL. That's annoying :)

That pattern also breaks with the current file format, right? That inner section would be #MATERIAL_UNIFORMS there. So, at least it's not making things worse, but that's not much comfort...

Do you happen to know what compiler/linter CLion is using? I wonder if it has clang-format-style markers to disable a lint check.

Aside from silencing the linter in this instance, the only thing I can think of is either horrible or very clever 😂 . We could teach the preprocessor an additional "placeholder struct member" syntax, with the same effect as the pragma (replace entire line with appropriate value), but in a syntax that's also valid GLSL in the template. Something like:

layout(set=1, binding=0, std140) uniform MaterialUniforms {
    int __GODOT_MATERIAL_UNIFORMS___;
} material;

Maybe that's taking things too far, I don't know 😅

@danderson
Copy link
Author

We discussed this in the rendering meeting. TL;DR 👍 to implement the pragma/ifdef part of the proposal; drop the file extension stuff for now. I'll tidy up commits and send a PR as my free time permits. More detailed notes follow.

Pragmas/ifdef instead of custom macros

No objections heard, a couple of echoes of "oh yeah that annoys me in vscode too" 🎉 . I heard reinforcement that we must not break compatibility for existing code. I agree and confirmed that my implementation conforms with this.

Shader stages based on file extension

There is disagreement on user ergonomics: this lets you share shader code from other tools/engines... But it also means that if you miss "oh btw save this code as .compute.glsl or it won't work" in the instructions, the shader mysteriously doesn't work and you end up with sad users.

It might be a nice workflow for advanced users, but a lot of people just paste a shader they like from godotshaders.com, and that should "just work", always. For that workflow, the shader type(s) needs to stay inside the source code.

We also can't take shortcuts like "assume a file with no stage declaration is a compute shader". Right now compute is the only "accessible" GLSL stage, but the WIP shader templates means that very soon, it will be more common to also paste vertex/fragment GLSL shaders into projects, and that also needs to just work.

There might be a way to do this that balances the different UX constraints, but it should be a dedicated proposal because it's not trivial or obvious what to do.

Question for future proposal: if we support per-type filenames, does Khronos Group define a convention for this? If yes, a godot proposal to do this should account for that convention.

Code injection macros in render shaders

We didn't talk about this in the meeting. The general guidance I got: break things into smaller standalone parts if possible, to make it easier to review.

So, I'm going to defer dealing with the code injection macros, and focus on the ergonomics of compute shaders for now. Changing the code injection logic might also clash with the WIP templates changes, so I have to go learn about that before I can safely propose a change there.

@geekley
Copy link

geekley commented Dec 13, 2024

  • What names do we want for the pragmas? [...]
    • The prototype uses godot_shader_stages and godot_shader_versions to reserve a Godot-specific prefix, and match the names from the existing format. This also matches the prefix for the macro namespace in C#, for consistency.
  • What names do we want for the preprocessor macros?
    • The prototype uses GODOT_STAGE_<uppercase stage name> and GODOT_VERSION_<uppercase version name>.

Can we replace "version(s)" with "variation(s)" (just on the new format)?

I feel like if we're defining a macro like GODOT_VERSION_*, then it should be used for actual Godot versions. See also:

I'd expect that to be used like this, as Unity does something similar in C#:

#ifdef GODOT_VERSION_4_3_OR_LATER
// and/or even better:
#if GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR >= 3

IMO it would be better to change the pragma to godot_shader_variations and its macros to GODOT_SHADER_VARIATION_*. A bit verbose, yes, but it's very self-explanatory, and it's important to be so on something difficult to understand like shaders. Plus it avoids confusion with GLSL #version and Godot versions.

#version 450
#pragma godot_stages(vertex, fragment)
#pragma godot_shader_variations(STANDARD, RAY_MARCHING)

#if GODOT_STAGE_VERTEX
void main() {
    do_common();
#if GODOT_SHADER_VARIATION_STANDARD
    fast_vertex_process();
#elif GODOT_SHADER_VARIATION_RAY_MARCHING
    cast_lots_of_rays();
    make_super_pretty();
#endif
    cleanup_common();
}
#endif // GODOT_STAGE_VERTEX

@geekley
Copy link

geekley commented Dec 13, 2024

The valid values for godot_shader_stages are vertex, fragment, tessellation_control, tessellation_evaluation and compute. For backwards compatibility, we also allow tesselation (typo, with one L).

Is it possible to disallow the typo only on the new parser? Compatibility is needed on the old parser of course, but on the new one this would just cause inconsistency between shader files IMHO. I think it's not really necessary to allow it, unless there's a reason to?

@geekley
Copy link

geekley commented Dec 13, 2024

It's also worth considering how those macros should be defined.

Instead of defining them or not, it's better to always define the ones declared in the pragmas as either 0 or 1.
Advantages:

  • They can be used in #if ... #elif more succinctly without requiring #ifdef ... #elif defined(...). Since all possibilities are declared in the pragmas, it shouldn't cause any issues.
  • It also gives you the additional semantic, e.g. on an included file (where we might not know which stages are declared) #if GODOT_STAGE_VERTEX means we're in the vertex stage, whereas #ifdef GODOT_STAGE_VERTEX means a vertex stage is declared in the pragma (but we might not be in it). I assume this could be potentially useful in libraries. Same for the variations.

@geekley
Copy link

geekley commented Dec 13, 2024

We could teach the preprocessor an additional "placeholder struct member" syntax, with the same effect as the pragma (replace entire line with appropriate value), but in a syntax that's also valid GLSL in the template.

If you're going that route, would this make more sense, since the macro can expand to arbitrary fields of arbitrary types?

layout(set=1, binding=0, std140) uniform MaterialUniforms {
    __GODOT_MATERIAL_UNIFORMS___
} material;

IDE support could be configured the same way as the stages then, by manually defining it to anything so the linter doesnt complain, e.g.: --define-macro __GODOT_MATERIAL_UNIFORMS___='int __godot_material_uniforms__;'

(Sorry if I'm saying something stupid, as I don't understand anything about this)

@beicause
Copy link

beicause commented Mar 3, 2025

I tried the new format of this proposal, but it still couldn't make the VSCode GLSL lint extension work out of the box, because macros such as GODOT_STAGR_VERTEX are not defined, so I couldn't get compilation hints. Or I can only define one of them, and can't get the compilation hints for multiple stages simultaneously.

To truly get IDE linters support for the existing godot GLSL as well as the format of this proposal, we can achieve it in https://github.com/godotengine/godot-vscode-plugin or separate extension.

If compatibility is not taken into account, one good thing about the new format is that we can skip parsing the GLSL and define the macro and directly compile the same piece of GLSL for different stages. However, when considering compatibility, this proposal increases the complexity of parsing. I'm afraid it's kind of getting more trouble than it's worth.

@danderson
Copy link
Author

The problem with that approach is that there are more IDEs than vscode, and teaching all of them about a bespoke file format is a tall order. On the other hand, giving them standard GLSL and only requiring symbol definition through a standard GLSL compiler mechanism should be a baseline feature for GLSL support (or if it isn't, it would be a useful thing to support for all GLSL, not just Godot).

I think realistically, telling every IDE "please spend time to support custom Godot formats/languages" will result in "no thanks", whereas "please improve your GLSL support" is less effort, and has more universal benefit.

@stuartcarnie
Copy link

stuartcarnie commented Mar 4, 2025

If you're going that route, would this make more sense, since the macro can expand to arbitrary fields of arbitrary types?

layout(set=1, binding=0, std140) uniform MaterialUniforms {
GODOT_MATERIAL_UNIFORMS_
} material;
IDE support could be configured the same way as the stages then, by manually defining it to anything so the linter doesnt complain, e.g.: --define-macro __GODOT_MATERIAL_UNIFORMS___='int __godot_material_uniforms__;'

(Sorry if I'm saying something stupid, as I don't understand anything about this)

Another alternative is

layout(set=1, binding=0, std140) uniform MaterialUniforms {
#ifdef GODOT_COMPILER
    __GODOT_MATERIAL_UNIFORMS___
#else
   int __tmp;
#endif
} material;

And when compiling in Godot, the GODOT_COMPILER (naming 🤷🏻) macro is defined and __GODOT_MATERIAL_UNIFORMS___ replaced.

The original would work, but would fail to parse in IDEs, whereas this would only enable the block if GODOT_COMPILER was defined.

Yet another alternative is at the top of your file

#ifndef __GODOT_MATERIAL_UNIFORMS___
#define __GODOT_MATERIAL_UNIFORMS___ int __tmp;
#endif

Note

For this approach to work, it assumes that the code replacement happens before the preprocessor for Godot's GLSL compiler is run.

@beicause
Copy link

beicause commented Mar 4, 2025

Perhaps I didn't express myself clearly. I mean I cannot get diagnosis within IDE. Many linters use glslangValidator for diagnosis, which needs to specify the shader satge and if the macro is not defined all the content within #ifdef is ignored.

To get the diagnostic, I need to run this from command line:

 glslangValidator -S vert -DGODOT_STAGE_VERTEX -l ./my_shader.glsl
 glslangValidator -S frag -DGODOT_STAGE_FRAGMENT -l ./my_shader.glsl

It still requires further support from IDE plugins to generate diagnosis.

For syntax highlighting or code formatting, some plugins already ignore preprocessor directives and work well.

@geekley
Copy link

geekley commented Mar 4, 2025

@beicause I would suggest you open an issue in their extension repo asking to allow configuring "glsllint.glslangValidatorArgs" per file extension. For example, let's say you could do:

"glsllint.glslangValidatorArgs": {
  "*.vert.glsl": ["-DGODOT_STAGE_VERTEX"]`,
  "*.frag.glsl": ["-DGODOT_STAGE_FRAGMENT"], // etc.
}

That's a simple change to the implementation that would solve the issue without being specific to Godot at all. And what I think he meant by:

"please improve your GLSL support" is less effort, and has more universal benefit

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

Successfully merging a pull request may close this issue.

5 participants