Search code examples
javascriptglslwebgl2

WebGL2 tells me, that while loops are not allowned


Most of the code is from here, I just loaded webgl2 instead of webgl , changed the fragment shader to include a while loop, and logged a few things,

source/script.js:

const canvas = document.getElementById('screen');
const gl = canvas.getContext('webgl2');

if (!gl) {
    throw new Error('WebGL not supported');
}

console.log(gl instanceof WebGL2RenderingContext);

// vertexData = [...]

// create buffer
// load vertexData into buffer

// create vertex shader
// create fragment shader
// create program
// attach shaders to program

// enable vertex attributes

// draw

const vertexData = [
    0, 1, 0,
    1, -1, 0,
    -1, -1, 0,
];

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
attribute vec3 position;
void main() {
    gl_Position = vec4(position, 1);
}
`);
gl.compileShader(vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
void main() {
    int i = 0;
    while (i < 2) {
        i++;
    }
    gl_FragColor = vec4(1, 0, 0, 1);
}
`);
gl.compileShader(fragmentShader);

var compiled = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS);
console.log('Shader compiled successfully: ' + compiled);
var compilationLog = gl.getShaderInfoLog(fragmentShader);
console.log('Shader compiler log: ' + compilationLog);


const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

const positionLocation = gl.getAttribLocation(program, `position`);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);

gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);

the console output is:

true
script.js:56 Shader compiled successfully: false
script.js:58 Shader compiler log: ERROR: 0:4: 'while' : This type of loop is not allowed

I am hosting this using python -m http.server 8000 if that is of any help.

index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>WebGL2 Test</title>
    <link href="style.css" rel="stylesheet" type="text/css">
  </head>
  <body>
    <canvas id="screen"></canvas>
    <script type="module" src="./source/script.js"></script>
  </body>
</html>

style.css:

body{
    margin: 0;
    width: 100vw;
    height: 100vh;
}

canvas {
    display: block;
    width: 100%;
    height: 100%;
}

I really have no clue as to what this could be. I hope someone can figure it out.


Solution

  • while loops are not guaranteed to be supported. Try using a for loop instead.

    From the link below:

    The while and do-while loops are optional. The only loop construct you are guaranteed to have is the for loop. In addition, there are many restrictions on the looping constructs. In general “control flow is limited to loops where the maximum number of iterations can easily be determined at compile time.”

    The link also explains some limitations on loops.

    WebGL Iteration

    Try this instead.

    void main() {
        for (int i = 0; i < 2; i++) {
          // do something
        }
        gl_FragColor = vec4(1, 0, 0, 1);
    }
    

    Another approach is to use a high enough hard limit for your loop and then break when your condition is met. This allows the compiler to know the max iterations at compile time (required) and you can break the loop when your condition is met. Do not set the loop limit higher than you need as it affects compilation time and higher limits may not work on all devices (see additional notes below.)

    void main() {
        for (int i = 0; i < 1000; i++) {
          // do something
           if (myCondition) break;
        }
        gl_FragColor = vec4(1, 0, 0, 1);
    }
    

    One of my favorite tools for testing the validity of, and understanding shaders is GPU ShaderAnalyzer by AMD. You can paste in your shader and select compile and it shows you the generated assembly.

    For example:

    void main() {
        for (int i = 0; i < 10; i++) {
          // do something
           
        }
        gl_FragColor = vec4(1, 0, 0, 1);
    }
    

    Is compiled to:

    il_ps_2_0
    dcl_output_generic o0
    dcl_literal l0, 0x3F800000, 0x00000000, 0x00000000, 0x3F800000
    dcl_literal l1, 0x00000001, 0x00000000, 0x00000000, 0x00000000
    mov r1.x___, l0.y
    mov r2.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r3.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r4.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r5.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r6.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r7.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r8.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r9.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r10.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r11.x___, r1.x
    iadd r1.x___, r1.x, l1.x
    mov r12, l0
    mov o0, r12
    endmain
    

    See how it doesn't create an assembly loop and instead creates instructions for each iteration? It helps give a better idea of what is going on under the hood.