<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
<script id="vshader" type="x-shader/x-vertex">
attribute vec3 pos;
attribute vec4 colorIn;
varying vec4 color;

void main()
{
    color = colorIn;
    gl_Position = vec4(pos.xyz, 1.0);
}
</script>

<script id="fshader" type="x-shader/x-fragment">
precision mediump float;

varying vec4 color;

void main()
{
    gl_FragColor = color;
}
</script>

<script>
"use strict";

// These four declarations need to be global for "shouldBe" to see them
var wtu = WebGLTestUtils;
var gl;
var contextAttribs = null;
var redChannels = [0, 0, 0];
var correctColor = null;
var framebuffer;
var fbHasColor;
var fbHasDepth;
var fbHasStencil;
var contextVersion = wtu.getDefault3DContextVersion();

function init()
{
    description('Verify WebGLContextAttributes are working as specified, including alpha, depth, stencil, antialias, but not premultipliedAlpha');

    runTest();
}

var vertices = new Float32Array([
    1.0, 1.0, 0.0,
    -1.0, 1.0, 0.0,
    -1.0, -1.0, 0.0,
    1.0, 1.0, 0.0,
    -1.0, -1.0, 0.0,
    1.0, -1.0, 0.0]);

var colors = new Uint8Array([
    255, 0, 0, 255,
    255, 0, 0, 255,
    255, 0, 0, 255,
    255, 0, 0, 255,
    255, 0, 0, 255,
    255, 0, 0, 255]);


function getWebGL(canvasWidth, canvasHeight, contextAttribs, clearColor, clearDepth, clearStencil)
{
    var canvas = document.createElement("canvas");
    if (!canvas)
        return null;
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;

    gl = wtu.create3DContext(canvas, contextAttribs, contextVersion);
    if (!gl)
        return null;

    var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["pos", "colorIn"]);
    if (!program)
        return null;

    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.STENCIL_TEST);

    gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
    gl.clearDepth(clearDepth);
    gl.clearStencil(clearStencil);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);

    framebuffer = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.canvas.width, gl.canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
    fbHasStencil = false;
    fbHasDepth = false;
    fbHasColor = gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
    if (fbHasColor) {
      var depthStencil = gl.createRenderbuffer();
      gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencil);
      gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, gl.canvas.width, gl.canvas.height);
      gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencil);
      fbHasDepth = gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
      if (!fbHasDepth) {
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
        shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE');
      } else {
        fbHasStencil = true;
      }
    }
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    var colorOffset = vertices.byteLength;
    var vbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    gl.bufferData(gl.ARRAY_BUFFER, colorOffset + colors.byteLength, gl.STATIC_DRAW);
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);
    gl.bufferSubData(gl.ARRAY_BUFFER, colorOffset, colors);
    gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(0);
    gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, colorOffset);
    gl.enableVertexAttribArray(1);

    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
    return gl;
}

function draw(gl, verticesCount)
{
    verticesCount = verticesCount || vertices.length / 3;
    gl.drawArrays(gl.TRIANGLES, 0, verticesCount);
}

function checkDraw(hasAlpha, hasStencil, hasDepth, hasAntialias)
{
    let red = [255, 0, 0, 255 ];
    let black = [0, 0, 0, hasAlpha ? 0 : 255 ];
    debug(`Testing that stencil ${ hasStencil ? 'affects': 'does not affect'} the rendering.`);
    gl.stencilFunc(gl.NEVER, 1, 1);
    gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
    draw(gl);
    correctColor = hasStencil ? black : red;
    wtu.checkCanvas(gl, correctColor)
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    wtu.checkCanvas(gl, black);
    gl.stencilFunc(gl.ALWAYS, 1, 1);

    debug(`Testing that depth ${ hasDepth ? 'affects': 'does not affect'} the rendering.`);
    gl.depthFunc(gl.NEVER);
    draw(gl);
    correctColor = hasDepth ? black : red;
    wtu.checkCanvas(gl, correctColor);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    wtu.checkCanvas(gl, black);
    gl.depthFunc(gl.ALWAYS);

    debug(`Testing that rendering is ${hasAntialias ? 'antialiased' : 'aliased'}.`);
    draw(gl, 3);
    let N = 2;
    let buf = new Uint8Array(N * N * 4);
    gl.readPixels(0, 0, N, N, gl.RGBA, gl.UNSIGNED_BYTE, buf);
    redChannels[0] = buf[4 * (N + 1)]; // (1, 1)
    redChannels[1] = buf[4 * N * (N - 1)]; // left top
    redChannels[2] = buf[4 * (N - 1)]; // right bottom
    shouldBe("redChannels[1]", "255");
    shouldBe("redChannels[2]", "0");
    if (hasAntialias) {
        shouldNotBe("redChannels[0]", "255");
        shouldNotBe("redChannels[0]", "0");
    } else {
        shouldBeTrue("redChannels[0] == 255 || redChannels[0] == 0");
    }
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    wtu.checkCanvas(gl, black);

    debug("Testing that rendering works.");
    draw(gl);
    wtu.checkCanvas(gl, red);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    wtu.checkCanvas(gl, black);
}

function testDefault()
{
    debug("Testing default attributes: { stencil:false }");
    shouldBeNonNull("gl = getWebGL(1, 1, null, [ 0, 0, 0, 0 ], 1, 0)");
    shouldBeFalse("gl.getContextAttributes().stencil");
    shouldBe("gl.getParameter(gl.STENCIL_BITS)", "0");
}

function testAttributesAffectContext(alpha, stencil, depth, antialias)
{
    shouldBeNonNull(`gl = getWebGL(2, 2, { depth: ${depth}, stencil: ${stencil}, antialias: ${antialias}, alpha: ${alpha} }, [ 0, 0, 0, 0 ], 1, 0)`);
    shouldBeNonNull("contextAttribs = gl.getContextAttributes()");

    shouldBeGreaterThanOrEqual("gl.getParameter(gl.RED_BITS)", "8");
    shouldBeGreaterThanOrEqual("gl.getParameter(gl.GREEN_BITS)", "8");
    shouldBeGreaterThanOrEqual("gl.getParameter(gl.BLUE_BITS)", "8");

    shouldBe("contextAttribs.alpha", "" + alpha);
    if (contextVersion < 2) {
        if (!stencil)
            shouldBeFalse("contextAttribs.stencil");
        else
            stencil = contextAttribs.stencil;
        if (!depth)
            shouldBeFalse("contextAttribs.depth");
        else
            depth = contextAttribs.depth;
        if (!antialias)
            shouldBeFalse("contextAttribs.antialias");
        else
            antialias = contextAttribs.antialias;
    } else {
        shouldBe("contextAttribs.stencil", "" + stencil);
        shouldBe("contextAttribs.depth", "" + depth);
        shouldBe("contextAttribs.antialias", "" + antialias);
    }

    if (alpha)
        shouldBeGreaterThanOrEqual("gl.getParameter(gl.ALPHA_BITS)", "8");
    else
        shouldBe("gl.getParameter(gl.ALPHA_BITS)", "0");
    if (stencil)
        shouldBeGreaterThanOrEqual("gl.getParameter(gl.STENCIL_BITS)", "8");
    else
        shouldBe("gl.getParameter(gl.STENCIL_BITS)", "0");
    if (depth)
        shouldBeGreaterThanOrEqual("gl.getParameter(gl.DEPTH_BITS)", "16");
    else
        shouldBe("gl.getParameter(gl.DEPTH_BITS)", "0");

    var correctColor = alpha ? [0, 0, 0, 0] : [0, 0, 0, 255];
    wtu.checkCanvas(gl, correctColor);

    debug("Testing default framebuffer.");
    checkDraw(alpha, stencil, depth, antialias);

    if (fbHasColor) {
        debug("Testing bound framebuffer object.");
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
        gl.clearColor(0, 0, 0, 0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
        checkDraw(true, fbHasStencil, fbHasDepth, false);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    }
}

function runTest()
{
    testDefault();
    let cases = [false, true];
    for (let alpha of cases) {
        for (let stencil of cases) {
            for (let depth of cases) {
                for (let antialias of cases) {
                    testAttributesAffectContext(alpha, stencil, depth, antialias);
                }
            }
        }
    }
    finishTest();
}
</script>
</head>
<body onload="init()">
<div id="description"></div>
<div id="console"></div>
</body>
</html>
