﻿<!--
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 OES_vertex_array_object_bufferData Conformance Tests</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>
<!-- comment in the script tag below to test through JS emualation of the extension. -->
<!--
<script src="../../../demos/google/resources/OESVertexArrayObject.js"></script>
-->
</head>
<body>
<div id="description"></div>
<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec4 a_color;
varying vec4 v_color;
void main(void) {
    gl_Position = a_position;
    v_color = a_color;
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 v_color;
void main(void) {
    gl_FragColor = v_color;
}
</script>
<script>
"use strict";
description("This test verifies drawing results when using gl.bufferData with the OES_vertex_array_object extension.");

debug("");

var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas);
var ext = null;
var vao = null;

if (!gl) {
    testFailed("WebGL context does not exist");
} else {
    testPassed("WebGL context exists");

    // Setup emulated OESVertexArrayObject if it has been included.
    if (window.setupVertexArrayObject) {
        debug("using emuated OES_vertex_array_object");
        setupVertexArrayObject(gl);
    }

    // Query the extension and store globally so shouldBe can access it
    ext = gl.getExtension("OES_vertex_array_object");
    if (!ext) {
        testPassed("No OES_vertex_array_object support -- this is legal");

    } else {
        testPassed("Successfully enabled OES_vertex_array_object extension");

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

/**
 * The OES_vertex_array_object extension seems to work incorrectly on some handheld devices,
 * namely the Nexus 5, and Nexus 7 (in 2014/01) when using bufferData before binding the VAO.
 * The tested OS was Android KitKat 4.4.2, the effects were the same in all tested browsers
 * (Chrome, Chrome Beta, Firefox, Firefox Beta), so it is likely a driver bug.
 * These devices have the similar Adreno 320 and Adreno 330 GPUs respectively.
 *
 * The issuse resulted from this sequence of actions in a requestAnimationFrame loop:
 * 1. upload some vertex buffers with gl.bufferData (eg. colors)
 * 2. bind the VAO
 * 3. clear the canvas
 * 4. draw (some triangles) to the canvas
 * 5. unbind the VAO
 *
 * This caused the drawn triangles to be drawn with black (0) for most of the frames, with some
 * rare frames presenting the correct render results. Interestingly on both devices exactly every
 * 64th frame passed (starting with the very first one), the others failed.
 * (Because of this, we test multiple frames.)
 * When positions were uploaded, seemingly nothing was drawn, that's likely because the
 * position buffer was also all 0s.
 *
 * The issue did not occur:
 * - if step 1. and 2. were swapped
 * - or if step5 was ommited (probably because that makes step 2 a no-op since the VAO is bound)
 */
function runBufferDataTest() {
    debug("Testing draws with bufferData");

    canvas.width = 50; canvas.height = 50;
    gl.viewport(0, 0, canvas.width, canvas.height);

    var testColor = [0, 255, 0, 255];
    var clearColor = [255, 0, 0, 255];

    // Where the issue occures, this is the sequence of success/failure every time:
    // result:           success fail fail fail fail ... success fail fail ...
    // currentTestCount: 0       1    2    3    4    ... 64      65   66   ...
    // So with just 1 test it passes, but 2 tests are enough. Here we use 3.
    var numberOfTests = 3;
    var currentTestCount = 0;

    var positionLoc = 0;
    var colorLoc = 1;
    var gridRes = 1;

    var program = wtu.setupSimpleVertexColorProgram(gl, positionLoc, colorLoc);

    var vao0 = ext.createVertexArrayOES();
    ext.bindVertexArrayOES(vao0);

    var buffers = wtu.setupIndexedQuadWithOptions(gl,
    { gridRes: gridRes,
      positionLocation: positionLoc
    });

    var colorTypedArray = createColorTypedArray();

    var colorBuffer = gl.createBuffer(gl.ARRAY_BUFFER);
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.enableVertexAttribArray(colorLoc);
    gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);

    ext.bindVertexArrayOES(null);

    testDrawing();

    function testDrawing() {
        // this order works fine:
        // ext.bindVertexArrayOES(vao0);
        // uploadColor();

        // this order doesn't:
        uploadColor();
        ext.bindVertexArrayOES(vao0);

        wtu.clearAndDrawIndexedQuad(gl, 1, clearColor);

        ext.bindVertexArrayOES(null);

        //debug("<span>"+currentTestCount+"</span");
        wtu.checkCanvas(gl, testColor, "should be green")

        if (++currentTestCount < numberOfTests) {
            testDrawing();
            // wtu.requestAnimFrame(testDrawing);
        } else {
            // clean up
            ext.deleteVertexArrayOES(vao0);
        }
    }

    function uploadColor() {
        gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, colorTypedArray, gl.STREAM_DRAW);
    }

    function createColorTypedArray() {
        var colors = [];
        var pOffset = 0;
        for (var yy = 0; yy <= gridRes; ++yy) {
          for (var xx = 0; xx <= gridRes; ++xx) {
            colors[pOffset + 0] = testColor[0];
            colors[pOffset + 1] = testColor[1];
            colors[pOffset + 2] = testColor[2];
            colors[pOffset + 3] = testColor[3];
            pOffset += 4;
          }
        }
        return new Float32Array(colors);
    }
}

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

</body>
</html>
