YoYoGames / GameMaker-Bugs

Public tracking for GameMaker bugs
26 stars 8 forks source link

Improved support for shader #include directives #3655

Open iampremo opened 1 year ago

iampremo commented 1 year ago

Customer's description:

PROBLEM: Currently when creating an HLSL11 shader, using the #include directive only works if specifying an absolute drive-relative path such as #include "C:\\ShaderCommon.h"

This is because the actual shader compilation happens in the GMSTemp directory. This can be verified by putting a shader header file in the temp directory then including it from the shader like #include "ShaderCommon.h". But this is obviously not a usable solution since the temp directory is deleted after each run.

CURRENT WORKAROUND

PROPOSED SOLUTIONS:

 

Gamer-XP commented 1 year ago

I think I'd prefer if we could just create some shared shader-specific functions that can be used across any shaders, similar to how scripts work. Include is cool and stuff, but not so convenient to use.

FoxyOfJungle commented 1 year ago

I second that!

Regarding Gamer-XP's suggestion, despite seeming to be a good solution, I don't think it's viable, as there is an obvious possibility of having "name-clash" if there are already shaders with such functions... Mainly if such functions are in a library.

Using #include allows the shader to only import another shader into it, and the scope is limited, which is perfect. Something like using event_inherited().

So this turns out to be the most ideal solution:

sh_common:

#define M_PI 3.141592653589
#define M_PHI 1.61803398875

float sqr(float num) {
    return num * num;
}

Some other shader:

#include "sh_common"

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

#define M_TAU 2.0 * M_PI

void main() {
    // < I can use sqr(), M_PI and others here >
}

This is more or less how other game engines and other languages do it.

kburkhart84 commented 1 year ago

I don't know what the best way to do this is, but the suggestion deserves the upvote regardless so that they implement some kind of solution for this. All too often shaders share code, and there are external solutions that work for shaders outside of GM but nothing for GM itself.

Gamer-XP commented 1 year ago

Regarding Gamer-XP's suggestion, despite seeming to be a good solution, I don't think it's viable, as there is an obvious possibility of having "name-clash" if there are already shaders with such functions... Mainly if such functions are in a library.

I think it's not critical. Can just make it so compiler allow you to override global scope functions with local definitions. So if you define same-name function locally or include it - it should use that one. Though, having easier way to use #include may be good as well.

bawat commented 1 year ago

+1

FoxyOfJungle commented 10 months ago

I would also like to make an addition, it would be interesting to be able to include more than one shader (and also that include is recursive), since for example, I might want to include a shader with common functions, and another shader with color conversion functions/matrices, sampling functions... and among others.

This would make it a lot more organized.

I'm really looking forward to this... I have a project where I use a lot of code repetition to render materials, it will improve workflow by 150% ^^

patriciogonzalezvivo commented 4 months ago

+1 for inclusion for multiple files and recursive.

Ex:

#include "lygia/generative/snoise.glsl"
#include "lygia/color/space/rgb2srgb.glsl"
...

That would bring support for LYGIA ( https://github.com/patriciogonzalezvivo/lygia ) shader library. Happy to provide code examples and parsers for different languages (C++ / Python / JS / Rust).

In LYGIA is very granular and name collisions are prevented using #ifndef FNC_<FUNCTION NAME> pattern.

The #include routine main job will be to prevent endless recursive calls. By converting all relative paths to absolute paths, storing them on an array of strings and checking agains it, is possible to prevent loading the same file twice (each file is one function), this both: makes the final code smaller and prevents dependencies that calls the original parent function.