summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/out-of-bounds-index-buffers-after-copying.html
blob: 39bbb5e348b344a9ac7588040e32f2fface17eb5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<!--
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>
<title>WebGL Out-of-Bounds Index Buffer Caused by CopyBufferSubData Conformance Test</title>
</head>
<body>
<canvas id="canvas" width="8" height="8" style="width: 100px; height: 100px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vsCheckOutOfBounds" type="x-shader/x-vertex">
    #define TEST_CASE_IN_BOUND 1
    #define TEST_CASE_OUT_OF_BOUND 2

    precision mediump float;
    attribute vec2 position;
    attribute vec4 vecRandom;
    varying vec4 v_color;
    uniform int u_testCase;

    bool testFloatComponentAccurate(float component) {
        return component == 0.2;
    }
    // Per the spec, each component can either contain existing contents
    // of the buffer or 0.
    bool testFloatComponent(float component) {
        return (component == 0.2 || component == 0.0);
    }
    // The last component is additionally allowed to be 1.0.
    bool testLastFloatComponent(float component) {
        return testFloatComponent(component) || component == 1.0;
    }

    bool testData(vec4 data) {
        if (u_testCase == TEST_CASE_IN_BOUND) {
            return (testFloatComponentAccurate(data.x) &&
                    testFloatComponentAccurate(data.y) &&
                    testFloatComponentAccurate(data.z) &&
                    testFloatComponentAccurate(data.w));
        } else if (u_testCase == TEST_CASE_OUT_OF_BOUND) {
            return (testFloatComponent(data.x) &&
                    testFloatComponent(data.y) &&
                    testFloatComponent(data.z) &&
                    testLastFloatComponent(data.w));
        }
        return false;
    }

    void main() {
        if (testData(vecRandom)) {
            v_color = vec4(0.0, 1.0, 0.0, 1.0); // green -- We're good
        } else {
            v_color = vec4(1.0, 0.0, 0.0, 1.0); // red -- Unexpected value
        }
        gl_Position = vec4(position, 0.0, 1.0);
    }
</script>
<script>
"use strict";
description("This test verifies that out-of-bounds index buffers caused by CopyBufferSubData behave according to spec.");

// Ensure that drawElements flags either no error or INVALID_OPERATION. In the case of INVALID_OPERATION,
// no canvas pixels can be touched.  In the case of NO_ERROR, all written values must either be the
// zero vertex or a value in the vertex buffer.  See vsCheckOutOfBounds shader.
function verifyOutOfBoundsIndex(gl) {
    var error = gl.getError();
    if (error === gl.INVALID_OPERATION) {
        testPassed("drawElements flagged INVALID_OPERATION, which is valid so long as all canvas pixels were not touched.");
        wtu.checkCanvas(gl, [0, 0, 255, 255]);
    } else if (error === gl.NO_ERROR) {
        testPassed("drawElements flagged NO_ERROR, which is valid so long as all canvas pixels are green.");
        wtu.checkCanvas(gl, [0, 255, 0, 255]);
    } else {
        testFailed("Invalid error flagged by drawElements. Should be INVALID_OPERATION or NO_ERROR");
    }
}

// Create an element array buffer with a tri-strip that starts at startIndex and make
// it the active element array buffer.
function prepareElementArrayBuffer(gl, startIndex) {
    var glElementArrayBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, glElementArrayBuffer);
    var quadIndices = new Uint16Array(4);
    for (var i = 0; i < quadIndices.length; i++) {
        quadIndices[i] = startIndex + i;
    }
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, quadIndices, gl.STATIC_DRAW);
    return glElementArrayBuffer;
}


var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas, {antialias: false}, 2);

var numberOfQuads = 200;

// Create a vertex buffer with 200 properly formed tri-strip quads. These quads will cover the canvas texture
// such that every single pixel is touched by the fragment shader.
var quadBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
var quadPositions = new Float32Array(numberOfQuads * /*ComponentsPerQuad*/2 * /*VerticesPerQuad*/4);
for (var i = 0; i < quadPositions.length; i += /*ComponentsPerQuad*/2 * /*VerticesPerQuad*/4) {
    quadPositions[i+0] = -1.0;  // upper left
    quadPositions[i+1] =  1.0;
    quadPositions[i+2] =  1.0;  // upper right
    quadPositions[i+3] =  1.0;
    quadPositions[i+4] = -1.0;  // lower left
    quadPositions[i+5] = -1.0;
    quadPositions[i+6] =  1.0;  // lower right
    quadPositions[i+7] = -1.0;
}
gl.bufferData(gl.ARRAY_BUFFER, quadPositions, gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);

// Create a small vertex buffer with determined-ahead-of-time "random" values (0.2). This buffer will be
// the one indexed off the end.
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER,
              new Float32Array([0.2, 0.2, 0.2, 0.2,
                                0.2, 0.2, 0.2, 0.2,
                                0.2, 0.2, 0.2, 0.2,
                                0.2, 0.2, 0.2, 0.2]),
              gl.STATIC_DRAW);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);

// Setup the verification program.
var program = wtu.setupProgram(gl, ["vsCheckOutOfBounds", wtu.simpleVertexColorFragmentShader], ["position", "vecRandom"]);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Shader and buffer setup should generate no errors");
var loc = gl.getUniformLocation(program, "u_testCase");
shouldBeNonNull(loc);

debug("");
debug("Test -- Vertex indices are in bounds.");
gl.uniform1i(loc, 1);  // TEST_CASE_IN_BOUND == 1
gl.clearColor(0.0, 0.0, 1.0, 1.0);  // Start with blue to indicate no pixels touched.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var elementArrayBuffer = prepareElementArrayBuffer(gl, /*StartIndex*/0);
gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, /*offset*/0);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Draw call should generate no errors");
wtu.checkCanvas(gl, [0, 255, 0, 255]);

debug("");
debug("Test -- Index off the end of the vertex buffer near the beginning of the out of bounds area.");
gl.uniform1i(loc, 2);  // TEST_CASE_OUT_OF_BOUND == 2
gl.clearColor(0.0, 0.0, 1.0, 1.0);  // Start with blue to indicate no pixels touched.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var outOfBoundsElementArrayBuffer = prepareElementArrayBuffer(gl, /*StartIndex*/4);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, outOfBoundsElementArrayBuffer);
gl.bindBuffer(gl.COPY_WRITE_BUFFER, elementArrayBuffer);
gl.copyBufferSubData(gl.ELEMENT_ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 4 * Uint16Array.BYTES_PER_ELEMENT);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "copyBufferSubData should generate no errors");
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementArrayBuffer);
gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, /*offset*/0);
verifyOutOfBoundsIndex(gl);

debug("");
debug("Test -- Index off the end of the vertex buffer near the end of the out of bounds area.")
gl.uniform1i(loc, 2);  // TEST_CASE_OUT_OF_BOUND == 2
gl.clearColor(0.0, 0.0, 1.0, 1.0);  // Start with blue to indicate no pixels touched.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
outOfBoundsElementArrayBuffer = prepareElementArrayBuffer(gl, /*StartIndex*/numberOfQuads - 4);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementArrayBuffer);
gl.bindBuffer(gl.COPY_READ_BUFFER, outOfBoundsElementArrayBuffer);
gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.ELEMENT_ARRAY_BUFFER, 0, 0, 4 * Uint16Array.BYTES_PER_ELEMENT);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "copyBufferSubData should generate no errors");
gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, /*offset*/0);
verifyOutOfBoundsIndex(gl);

debug("");
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Running tests should generate no errors");
var successfullyParsed = true;
</script>
<script src="../../js/js-test-post.js"></script>
</body>
</html>