Open bitsawer opened 2 years ago
and used platform (WINDOWS, MACOS etc.) were automatically defined for all shaders.
I think the main distinctions we need for shaders are "desktop", "mobile" and "using MoltenVK". There are probably no shaders that work on Windows and not Linux and vice versa. This is not true for macOS and iOS due to the use of MoltenVK, which has several limitations native Vulkan doesn't have.
I'd like to add to the list of suggested preprocessor features:
With #include implemented, it's not unreasonable to imagine two shaders that both reference the same shaderinclude file but with each using a different render mode or shader type. It would be helpful to be able to check through a preprocessor instruction what these settings are set to as preprocessor conditions. Example syntax:
shader1.gdshader:
shader_type spatial;
render_mode unshaded;
#include "shader_main.gdshaderinc"
shader2.gdshader:
shader_type spatial;
#include "shader_main.gdshaderinc"
shader_main.gdshaderinc:
#if rendering(unshaded)
<shader 1 would evaluate here with shader code that accounts for the shader having unshaded as a rendering mode>
#else
<shader 2 would evaluate here with shader code that accounts for the shader not having unshaded as a rendering mode>
#endif
@Shnazzy Unless I understood you wrong, similar functionality is already possible (although not automatically). I would handle that scenario something like this:
#define UNSHADED
shader_type spatial;
render_mode
#ifdef UNSHADED
unshaded,
#endif
cull_disabled;
#include "shader_main.gdshaderinc"
And in shader_main:
#ifdef UNSHADED
//Handle unshaded variant
#else
//Handle shaded variant
#endif
You can define things before including files and those defines will be available in those included files (this works with multiple shaders that all use the same included file, too).
Implementing this in a way you suggest would be a lot trickier, as the C/GLSL/Godot family preprocessors are basically just dumb and simple text processors which don't really understand the shader syntax or settings. Adding shader syntax parsing would make the preprocessor more complex. Especially in this case where the workaround is pretty easy to do.
@bitsawer If I understand the shader stack it would go something like so (Let me know if I missed a step):
My original idea was to just read from the list of render modes in the Godot Shader Preprocessor, but now I realize that's not possible since getting that list is done in step 2. However, it seems that in step 2, the list of render modes is converted to a list of GLSL preprocessor #define instructions that will be further addressed in step 3. For example, the following line would be the one for the unshaded render mode:
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
What I'm thinking is that it should be possible to, in step 1, evaluate something like:
#ifrender unshaded
// Some conditional shader code
#endif
into:
#if defined(MODE_UNSHADED)
// Some conditional shader code
#endif
The second block wouldn't be for the Godot Shader preprocessor to evaluate, but the GLSL shader (step 3). Does something like this sound more feasible or in the right direction?
My motive here was because I was trying to recreate in a Godot shaderinclude the default light() function that is automatically used when one isn't defined in a spatial shader for experimentation. This is a location where many GLSL preprocessor checks on the rendering mode happen, hence why I put in the request, because these checks have no equivalent if done outside of GLSL.
Being able to change custom defines via ShaderMaterial is needed. I'm building a terrain editor and using includes to separate the terrain portion and editor overlays portion of the shader. As it is I will need to manually remove #define EDITOR
to strip out the editor shader branches on export. Instead it should be settable in code and automatically remove itself when running in game.
Describe the project you are working on
Multiple, usually small ports from other sources (demos, tutorials, ShaderToy etc).
Describe the problem or limitation you are having in your project
Preprocessor doesn't always fully match the capabilities of other preprocessors or shader editors.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
Now that https://github.com/godotengine/godot/pull/62513 has been merged, there have been several improvement suggestions during and after adding the shader preprocessor.
The issue template asks to make separate issue for each suggestion, but I feel like these would be good to discuss in one place, at least until we decide if any of these improvements are wanted or not as they can be pretty tightly coupled. I can create new issues for any accepted improvement if wanted. I'll tag some people in this suggestion that have been involved in making this all happen, but feel free to ignore this. And of course everyone else is free to chime in, too. Pressing the thumbs-up reaction button helps this proposal to gain support and visibility. @fire @MagdielM @TheOrangeDay @Chaosus @clayjohn @reduz
[x] 1. #elif directive and #if defined(XYZ) function. These are not absolutely necessary and can be worked around with other directives with some effort, but they are useful and many people coming from other engines will probably expect these to work. I did not implement these in the original preprocessor PR as I wasn't really sure if it would be added to Godot in the first place. If this one is accepted, I can work on this at some point in the future. And if someone else wants to try sooner I can at least provide some PR feedback and testing, they should not be very complex tasks. Implemented in: https://github.com/godotengine/godot/pull/64459 and https://github.com/godotengine/godot/pull/64625
[x] 2. In the shader text editor, dim (or otherwise visibly mark or make semi-transparent) lines that have been skipped by preprocessor. This makes it easier to immediately see what is being actually executed and what conditions are true or false. @TheOrangeDay did some work on this, but I don't think it was finished. There was also some discussion about some implementation issues (adding functions to TextEdit vs SyntaxHighlighter). Someone more familiar with the possible ways to implement this is welcome to chime in. I can probably work on this too at some point if someone else doesn't want to try first. But again, this should not be a huge task. @Paulb23 had some comments about this in one of the older pull requests. Implemented in: https://github.com/godotengine/godot/pull/63872
[ ] 3. Allow setting macros in code during runtime. As a minor confusion there was
Shader.custom_defines
for low-level use, but it seems it was removed in 4.x. We should probably create a new API, maybe something likeShaderMaterial.set_preprocessor_macro(name: string, content: string, arguments: Array[String]=[])
. Or should we add the API directly toShader
resource itself (also see suggestion number 5)? Additionally maybe add some API getters to get a macro by name and another for getting all macros. I haven't actually checked this very deeply yet, so feel free to correct me. Are there any possible issues with cache etc. Also think about any possible resource performance/memory use issues with unique vs shared shaders to avoid making duplicate shaders as much as possible if they use exactly the same macros etc. How does Godot currently do shader caching or compiling during export vs runtime etc. Is there even any need to think about caching this much or does it mostly just work automagically? PR: https://github.com/godotengine/godot/pull/70749[ ] 4. UI for setting global macros in the Project Settings, similar to Shader Globals.
[ ] 5. UI for setting/editing shader macros in the editor inspector. I guess this inspector UI should be added to ShaderMaterial (?) instead of the Shader resource itself so that it would be possible to share the same Shader resource but still use different macros to create material variants.
[x] 6. Automatically defined macros. In the original PR macros EDITOR (boolean, true if shader is running in an editor) and used platform (WINDOWS, MACOS etc.) were automatically defined for all shaders. It might be good to maybe define currently used renderer too, for example GODOT_VULKAN, GODOT_GL3, GODOT_GL2ES etc. Of course it would be good to always write universal shaders that work everywhere, but real life is not always so rosy. PR: https://github.com/godotengine/godot/pull/71919
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
See above.
If this enhancement will not be used often, can it be worked around with a few lines of script?
Preprocessor is core functionality, so not easily.
Is there a reason why this should be core and not an add-on in the asset library?
It's adding features to existing core functionality.