Add support for #extension directives in shaders

Issue #873 wontfix
Konrad Lewandowski
created an issue

Now when you try to add a GLSL extension directive to your shader code it's generating an following error:

Error: Cannot compile pixel shader code: 0:1(1): error: syntax error, unexpected EXTENSION, expecting $end

(I was trying to use #extension GL_ARB_arrays_of_arrays : enable)

Note that #version directive also doesn't work:

Error: Cannot compile pixel shader code: 0:0(1): preprocessor error: #version must appear on the first line

(Even if directive is on the first line)

Finally, it would be nice to have #include (even if such does not exists in the GLSL specification) xD

Comments (16)

  1. Alex Szpakowski

    The #extension directive in general works for me, but the GL_ARB_arrays_of_arrays extension specification does not add GL_ARB_arrays_of_arrays as an #extension toggle, or as a #define either.

    LÖVE uses GLSL 1.20 by default. If you want to use a different version you can overwrite the love.graphics._shaderCodeToGLSL function (which converts your code to raw GLSL), but if you wanted to use GLSL 1.30+, I really recommend using #extension GL_EXT_GPU_shader4 : enable instead - it unlocks most shader model 4-level features, and will work on more drivers (although still requires DX10/GL3-capable hardware.)

  2. Konrad Lewandowski reporter

    My shader code is:

    #extension GL_EXT_GPU_shader4 : enable
    #extension GL_ARB_arrays_of_arrays : enable
    
    extern number time = 1;
    
    vec4 effect(vec4 _color, Image texture, vec2 texture_coords, vec2
    screen_coords) {
      vec2 p = texture_coords - 0.5;
      float sx = 0.03 * (p.x + 0.5) * 4*sin( 24.0 * p.x - 10. * time);
      float dy = 1./ ( 50. * abs(p.y - sx));
      dy += 1./ (20. * length(p - vec2(p.x, 0.)));
      vec4 newcolor = vec4(0.1 * (p.x + 0.1) * dy, 0.1 * dy, dy * 2.0,
         clamp(0.2 * dy, 0, 1) - smoothstep(0.5, 1, texture_coords.x)
        );
      return newcolor;
    }
    

    Without any #extension directives it compiles and works fine. If there are both, or single one of them is's causing allways the same error:

    0:1(1): error: syntax error, unexpected EXTENSION, expecting $end

    (And it doesn't matters which #extension is in the first line)

    Even adding few empty lines after extensions is not solving the situation.

    I'm loading this shader using love.graphics.newShader(filename) and my love2d version is 0.9.1 (newest from archlinux repo)

  3. Alex Szpakowski

    Apparently, the GLSL specification disallows #extension directives from being below any line which contains code that isn't a preprocessor statement. However, every desktop driver except for Mesa seems to disregard that part of the specification.

    When LÖVE converts the arguments of newShader into GLSL, it adds a few lines of code before the code in the arguments, which would cause #extension to fail on Mesa drivers. One workaround in LÖVE's source code might be to put the #extension lines at the top of the converted GLSL code during conversion.

  4. Alex Szpakowski

    One workaround in LÖVE's source code might be to put the #extension lines at the top of the converted GLSL code during conversion.

    It's non-trivial to make that work in a robust manner. For example:

    #ifdef GL_EXT_gpu_shader4
    #extension GL_EXT_gpu_shader4 : require
    #else
    /*
    #extension GL_ARB_gpu_shader5 : enable
    */
    vec4 foo;
    #endif
    
    vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord)
    {
        // ...
    }
    
  5. Jason McKesson

    Apparently, the GLSL specification disallows #extension directives from being below any line which contains code that isn't a preprocessor statement.

    One small point: this is not true in GLSL 3.30 or 4.50. I checked both of these, and while there's language about the location of #version, there is none about #extension.

    I haven't checked earlier versions.

  6. Alex Szpakowski

    The end of section 3.3 (page 16) on the GLSL 4.50 specification says this:

    Each extension can define its allowed granularity of scope. If nothing is said, the granularity is a shader (that is, a single compilation unit), and the extension directives must occur before any non-preprocessor tokens. If necessary, the linker can enforce granularities larger than a single compilation unit, in which case each involved shader will have to contain the necessary extension directive.

    Older versions of GLSL have the same paragraph.

  7. Bart van Strien

    The "cleanest" solution here might be a #pragma love startprefix and a #pragma love endprefix (name suggestions welcome), where anything within it gets placed at the top (after #version). Since we won't have a proper preprocessor it still won't work with #if surrounding it, but it will at least allow having #if within that prefix.

  8. Alex Szpakowski

    I think that's a pretty good solution given the constraints of what we use right now. I do eventually want to use something like glslang or another library to convert input shader code for different backends and potentially do an optimization pass, which would hopefully let us have more options about what happens with extensions... but none of that exists yet in love. ;)

  9. Raidho

    How come you haven't considered simply letting user pass their own header?

    local pixelcodes = {
        headercode or GLSL_VERSION,
        GLSL_SYNTAX, GLSL_PIXEL.HEADER, GLSL_UNIFORMS,  
        "#line 0",
        pixelcode,
        is_multicanvas and GLSL_PIXEL.FOOTER_MULTI_CANVAS or GLSL_PIXEL.FOOTER,
    }
    return table_concat(pixelcodes, "\n")
    
  10. Alex Szpakowski

    I don't think it's a good idea to expose the ability to choose an arbitrary GLSL version as a feature - love's internal code relies on features from specific GLSL versions. I have some other plans for versions in particular as well.

  11. Alex Szpakowski

    Due to the way glslang is used to validate shader code in newShader, it's no longer feasible to use arbitrary GLSL extensions in shaders (since glslang may not support the extension even if the current system's driver does).

    LÖVE 0.11 does now support GLSL 3.30 though, via #pragma language glsl3 at the top of a shader.

  12. Log in to comment