<!DOCTYPE html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>WebGL Triangle</title>
<style>
canvas {
    width: 2px;
    height: 2px;
}
</style>
</head>
<script id="vertexShaderSource" type="text/glsl">
attribute vec4 position;
void main() {
  gl_Position = position;
}
</script>
<script id="fragmentShaderSource" type="text/glsl">
#ifdef GL_ES
precision mediump float;
#endif

void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script>

if (window.testRunner) {
    window.testRunner.waitUntilDone();
    window.testRunner.dumpAsText();
}

function logResult(message) {
    var output = document.getElementById("results");
    output.innerHTML += message + "<br>";
}

function drawTriangle(canvas) {

    canvas.width = 2;
    canvas.height = 2;

    var gl = canvas.getContext("webgl", { antialias: true });

    if (!gl) {
        logResult("ERROR: Couldn't create WebGL context.");
        return;
    }

    if (!gl.getContextAttributes().antialias) {
        logResult("ERROR: Antialiasing was not enabled at creation time.");
        return;
    }

    var vertexShader = gl.createShader(gl.VERTEX_SHADER);

    gl.shaderSource(vertexShader, document.getElementById("vertexShaderSource").textContent);

    gl.compileShader(vertexShader);
    if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
        logResult("ERROR: Vertex Shader failed to compile.");
        logResult(gl.getShaderInfoLog(vertexShader));
        return;
    }

    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, document.getElementById("fragmentShaderSource").textContent);
    gl.compileShader(fragmentShader);
    if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
        logResult("ERROR: Fragment Shader failed to compile.");
        logResult(gl.getShaderInfoLog(fragmentShader));
        return;
    }

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

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        logResult("ERROR: Unable to link shaders into program.");
        return;
    }

    gl.useProgram(program);
    var positionAttribute = gl.getAttribLocation(program, "position");
    gl.enableVertexAttribArray(positionAttribute);

    var vertices = new Float32Array([
       -1.0, -1.0,
       1.0, -1.0,
       1.0, 1.0
    ]);
    var triangleBuffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
    gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0);

    gl.drawArrays(gl.TRIANGLES, 0, 3);
}

function pixelAt(cssX, cssY, buffer) {
    // Remember that "top" in CSS coordinates is the last row in WebGL. i.e. things are upside down.
    var col = cssX;
    var row = 1 - cssY;
    return {
        r: buffer[(row * 2 + col) * 4 + 0],
        g: buffer[(row * 2 + col) * 4 + 1],
        b: buffer[(row * 2 + col) * 4 + 2],
        a: buffer[(row * 2 + col) * 4 + 3],
    };
}

function pixelToString(pixel) {
    return "{r: " + pixel.r + ", g: " + pixel.g + ", b: " + pixel.b + ", a: " + pixel.a + "}";
}

function testOutput(canvas) {
    
    var gl = canvas.getContext("webgl");
    
    if (!gl) {
        logResult("FAIL: No WebGL context to examine.");
        return;
    }
    
    var imageBuffer = new Uint8Array(canvas.width * canvas.height * 4);
    gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, imageBuffer);

    // Check that the top left pixel is fully black.
    var topLeft = pixelAt(0, 0, imageBuffer);
    if (topLeft.r != 0 || topLeft.g != 0 || topLeft.b != 0 || topLeft.a != 255) {
        logResult("FAIL: top left corner was not fully black. " + pixelToString(topLeft));
        return;
    }
    logResult("PASS: top left corner was fully black.");
    
    // Check that the top right pixel isn't fully red. If it is, it implies we didn't multisample.
    var topRight = pixelAt(1, 0, imageBuffer);
    if (topRight.r == 255) {
        logResult("FAIL: top right corner had a solid red channel. " + pixelToString(topRight));
        return;
    }
    logResult("PASS: top right corner did not have a completely solid red channel.");
}

window.addEventListener("load", function () {
    var canvas = document.querySelector("canvas");
    drawTriangle(canvas);
    testOutput(canvas);
    if (window.testRunner)
        window.testRunner.notifyDone();
}, false);
</script>
<body>
    <p>To check anti-aliasing, we draw a triangle over half of a 2x2 quad, then check the pixel colors
    in the corners. Note that if you're looking at the rendering results on a high-dpi display
    you will see some artefacts as the canvas is scaled by the page zoom. Don't mistake that for
    anti-aliasing (the test code doesn't).</p>
    <canvas></canvas>
    <p id="results"></p>
</body>