<!--
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">
<title>WebGL multisampling fragment shader evaluation</title>
<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">#version 300 es
layout(location=0) in vec4 aPosition;
out vec4 vPosition;
void main()
{
    gl_Position = vec4(aPosition);
    vPosition = aPosition;
}
</script>
<script id="fshader" type="x-shader/x-fragment">#version 300 es
precision highp float;
in vec4 vPosition;
layout(location=0) out vec4 oColor;
void main()
{
    if (vPosition.x < 0.0) {
        oColor = vec4(1, 0, 0, 1);
    } else if (vPosition.y < 0.0) {
        oColor = vec4(0, 1, 0, 1);
    } else {
        oColor = vec4(0, 0, 1, 1);
    }
}
</script>

</head>
<body>
<div id="description"></div>
<div id="console"></div>

<script>
"use strict";

var wtu = WebGLTestUtils;
description("Verify that fragment shader is evaluated only once per framebuffer pixel when multisampling is used.");

// GLES 3.0.5 section 3.6.3. Polygon Multisample Rasterization:
// "Polygon rasterization produces a fragment for each framebuffer pixel with one or more sample points that satisfy
// the point sampling criteria described in section 3.6.1."

debug("Regression test for <a href='http://crbug.com/682815'>http://crbug.com/682815</a>");

function runTest(testParams) {
    let canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = 1;
    let gl = wtu.create3DContext(canvas, {antialias: false}, 2);

    // Find the supported samples for a multisampled renderbuffer of the appropriate internal format.
    let samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl[testParams.internalformat], gl.SAMPLES);
    if (!samples || !samples.length) {
        testFailed("Could not query supported sample counts for required multisampling format " + testParams.internalformat);
        return;
    }

    // Note that supported sample counts are required to be reported in descending order.
    debug('Testing with sample count ' + samples[0]);
    // Create a framebuffer with a multisampled renderbuffer with the maximum supported number of samples.
    let rb = gl.createRenderbuffer();
    gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
    gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl[testParams.internalformat], 1, 1);
    let fb = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
        testFailed("Rendering to a multisampled renderbuffer of format " + testParams.internalformat + " is required.");
        return;
    }

    // Create a program that will choose between one of different possible colors in the fragment shader.
    // It should be evaluated only once per framebuffer pixel, so only one of the colors will end up in the framebuffer.
    // However, if the multisampling mode is incorrectly implemented by supersampling, the samples may have different
    // colors.
    let program = wtu.setupProgram(gl, ["vshader", "fshader"], ["aPosition"]);

    // Render one triangle using the program. The triangle needs to extend far outside the viewport on all sides, so
    // that we can safely assume all samples fall inside the triangle. GLES 3.0.5:
    // "The sample points associated with a pixel may be located inside or outside of the unit square that is considered to bound the pixel."
    // Here we assume that sample points are less than 9999 pixels away from the pixel they are associated with.
    let buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 10000,  30000,
                                                     -30000, -10000,
                                                      10000, -10000]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(0);
    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
    gl.blitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);

    let readBuffer = new Uint8Array(4);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer);

    // Check that the canvas is one of the colors that the fragment shader may generate, and not a blend of them.
    let possibleColors = [
        [255, 0, 0, 255],
        [0, 255, 0, 255],
        [0, 0, 255, 255]
    ];
    let anyColorMatched = false;
    for (let i = 0; i < possibleColors.length; ++i) {
        let colorMatched = true;
        for (let j = 0; j < 4; ++j) {
            if (Math.abs(readBuffer[j] - possibleColors[i][j]) > 2) {
                colorMatched = false;
            }
        }
        if (colorMatched) {
            anyColorMatched = true;
        }
    }
    if (!anyColorMatched) {
        testFailed("Color in framebuffer was not one of the colors generated by the fragment shader: " + readBuffer);
    } else {
        testPassed("Color in framebuffer was one of the colors generated by the fragment shader: " + readBuffer);
    }
}

runTest({internalformat: 'RGBA8'});

var successfullyParsed = true;
</script>
<script src="../../js/js-test-post.js"></script>

</body>
</html>
