KhronosGroup / GLSL

GLSL Shading Language Issue Tracker
324 stars 96 forks source link

Operator overloading #107

Open Cazadorro opened 4 years ago

Cazadorro commented 4 years ago

Right now it is extremely tedious to create abstracted custom types in GLSL. To do so we must resort to add() mul() etc.. functions, and we can't even create additional min() max() and normalize() functions for our types with out compilation errors. Some pretty stark common cases are dual and quad floats/doubles, complex numbers, and quaternions. I can sort of create "libraries" for others to use via shaderc with include directives, but any types I make will always be inconsistent and appear second class citizens to arithmetic primitives in the language, often resulting in people just choosing to use vec2's for complex numbers since it is easier in many cases.

One way to alleviate this issue would be to introduce syntax to overload operators. These would include:

Some operators and functions may need to be excluded, but if most arithmetic operators could be overloaded it would start to make large scale GLSL programs a whole heck of a lot more manageable.

I'm going to strawman the syntax but essentially I would imagine it could look like this:

//conversion overload
struct Quaternion{
    float x;
    float y;
    float z;
    float w;
};

@operator Quaternion(const in vec4 value){
    return Quaternion(value.x, value.y, value.z, value.w);
}

@operator vec4(const in Quaternion quat){
    return vec4(quat.x, quat.y, quat.z, quat.w);
}
//explicit conversion
Quaternion quat = Quaternion(vec4 (1.0, 0.0, 0.0, 0.0));
vec4 vector = vec4(quat);

//builtin overload

Quaternion @operator normalize(const in Quaternion quat){
//converted because of aforementioned conversion operator
    return Quaternion(normalize(vec4(quat)));
}

Quaternion quat1 = normalize(quat);

// arithmetic overload
//note ideally would not be restricted to same type
Quaternion @operator*(Quaternion a, Quaternion b) {
    return Quaternion(
        a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,  
        a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, 
        a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,  
        a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w  
    );
}

Quaternion quat2 = quat * quat1;

Note I'm not sure if implicit conversions are desirable or feasible. Implicit conversions are not really important to the proposal

Doing this would make GLSL code a whole lot easier to manage, code for complex numbers and multi floats that is the same as normal scalar code could simply be dropped right in, only type signatures need to be changed, and reduce cognitive load on developers. The fact that overloading at all exists in GLSL is itself precedent IMO.

This should be backward compatible with currently working code, if appropriate operator overload markers are chosen, currently working functions should all be fine, the only thing that changes is new code that couldn't exist before. There shouldn't be parsing ambiguity on the operators themselves, for example, with the strawman syntax, the @ symbol shouldn't conflict with any other declaration in the language AFAIK, and could match for @operator entirely (so it just becomes a keyword), then know that an identifier, bultin, or symbolic operator must follow next, semantic checking later identifiers by a check that it matches a predefined struct or primitive name. Currently I have the conversion operator as a special case (no return), since I was basing my strawman off of C++'s syntax, but if it helps parsing, I see no reason why we can't just have the return value for conversions as well, and it may help in identifying them.

example:

Quaternion @operator Quaternion(const in vec4 value){
    return Quaternion(value.x, value.y, value.z, value.w);
}

I am unsure how GLSL internally manages builtins, theoretically custom ones should just be handled like functions at some point during compilation, so if there is going to be any issues, I would expect it to happen there.

fynv commented 4 years ago

Also in need of this feature trying to define a sampled-spectrum representation of color. (see Chapter 5, Physically Based Rendering)

devshgraphicsprogramming commented 3 years ago

I'd very much appreciate namespaces and being able to define at least "static struct methods" because as it stands right now I'm having to mangle my function and struct names into ridiculously long strings, like

irr_glsl_bxdf_brdf_ggx_ndf or irr_glsl_namespace_Struct_Method

which is slowly getting to be annoying with a GLSL header only library that is 4k+ LoC.

fknfilewalker commented 1 year ago

Push! Some things are just not doable without it, like implementing a spectral renderer...

Makogan commented 1 year ago

I want to mention that with the popularisation of geometric algebras and other mathematical concepts that define new math types this feature will probably see more and more demand. Being able to overload operators means the community can explore new math types and publish their own libraries for niche cases.

Yilmaz4 commented 1 month ago

Also needing this, I'm working with complex numbers represented with vec2s and I have to use a function whenever I want to multiply or divide complex numbers; an operator overload would be much more convenient and clean-looking