KhronosGroup/WebGL

Clarify `invariant gl_FragCoord` for WebGL2

grovesNL opened this issue · 2 comments

Is invariant gl_FragCoord allowed under a fragment shader for both WebGL1 and WebGL2? WebGL1 and WebGL2 seem to disagree currently.

Under

vShaderId: "vertexShaderInvariantGlPosition",
vShaderSuccess: true,
fShaderId: "fragmentShaderInvariantGlFragCoord",
fShaderSuccess: true,
linkSuccess: true,
passMsg: "vertex shader with invariant gl_Position and fragment shader with invariant gl_FragCoord must succeed",
we test that a combination of invariant gl_Position in the vertex shader and invariant gl_FragCoord in the fragment shader under WebGL1 succeeds.

If we port the same test case to WebGL2 it will currently fail under Chrome and Firefox on Windows, e.g. adding a test case with

<script id="vertexShaderInvariantGlPosition" type="text/something-not-javascript">#version 300 es
precision mediump float;
invariant gl_Position;

void main()
{
    gl_Position = vec4(0, 0, 0, 0);
}
</script>
<script id="fragmentShaderInvariantGlFragCoord" type="text/something-not-javascript">#version 300 es
precision mediump float;
invariant gl_FragCoord;
out vec4 my_color;

void main()
{
    my_color = gl_FragCoord;
}
</script>

..results in the following error:

Error compiling FRAGMENT_SHADER '[object WebGLShader]':ERROR: 0:3: 'invariant' : Cannot be qualified as invariant.

(full test case at grovesNL@afe0a53 for convenience)

The ESSL specs both seem to say (under "Invariance" header, ESSL 1.00 10.18 or ESSL 3.00 12.13):

RESOLUTION: Add an invariance qualifier to variables but permit its use only for outputs from the
vertex and fragment shaders. Add a global invariance option for use when complete invariance is
required.

ESSL 3.00 restricts this a little further, which is probably where the shader compiler error originates from:

RESOLUTION: Only allow invariant declarations on outputs.

..but both ESSL 1.00 and ESSL 3.00 also mention gl_FragCoord invariance as a special variable:

How does this rule apply to the built-in special variables.
Option 1: It should be the same as for varyings. But gl_Position is used internally by the rasterizer as
well as for gl_FragCoord so there may be cases where rasterization is required to be invariant but
gl_FragCoord is not.
Option 2: gl_FragCoord and gl_PointCoord can be qualified as invariance if and only if gl_Position and
gl_PointSize are qualified invariant, respectively.

(there's no resolution for these options unfortunately)

gl_FragCoord is an input so it's restricted based on Only allow invariant declarations on outputs. However the line about gl_FragCoord and gl_PointCoord can be qualified as invariance implies that it should be allowed - just restricted so that it can only be specified when gl_Position is also invariant.

The essl300 spec is missing the text, but the essl310 spec has it:

How does this rule apply to the built-in special variables?

Option 1: It should be the same as for varyings. But gl_Position is used internally by the rasterizer as
well as for gl_FragCoord so there may be cases where rasterization is required to be invariant but
gl_FragCoord is not.

Option 2: gl_FragCoord and gl_PointCoord can be qualified as invariance if and only if gl_Position and
gl_PointSize are qualified invariant, respectively.

RESOLUTION: Option 1.

I believe that the intent is actually:

How does this rule apply to the built-in special variables?

Option 1: It should be the same as for varyings.

But [because] gl_Position is used internally by the rasterizer as
well as for gl_FragCoord so there may be cases where rasterization is required to be invariant but
gl_FragCoord is not [, consider:]

Option 2: gl_FragCoord and gl_PointCoord can be qualified as invariance if and only if gl_Position and
gl_PointSize are qualified invariant, respectively.

RESOLUTION: Option 1. [but we acknowledge here the gap in flexibility that this leaves]

That's how I'm going to rule this.
Thanks for bringing this to our attention!

Sounds great, thank you!