2 Copyright (c) 2019 The Khronos Group Inc.
3 Use of this source code is governed by an MIT-style license that can be
4 found in the LICENSE.txt file.
10 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
11 <script src=
"../../js/js-test-pre.js"></script>
12 <script src=
"../../js/webgl-test-utils.js"></script>
13 <title>WebGL Out-of-Bounds Index Buffer Caused by CopyBufferSubData Conformance Test
</title>
16 <canvas id=
"canvas" width=
"8" height=
"8" style=
"width: 100px; height: 100px;"></canvas>
17 <div id=
"description"></div>
18 <div id=
"console"></div>
19 <script id=
"vsCheckOutOfBounds" type=
"x-shader/x-vertex">
20 #define TEST_CASE_IN_BOUND
1
21 #define TEST_CASE_OUT_OF_BOUND
2
23 precision mediump float;
24 attribute vec2 position;
25 attribute vec4 vecRandom;
27 uniform int u_testCase;
29 bool testFloatComponentAccurate(float component) {
30 return component ==
0.2;
32 // Per the spec, each component can either contain existing contents
33 // of the buffer or
0.
34 bool testFloatComponent(float component) {
35 return (component ==
0.2 || component ==
0.0);
37 // The last component is additionally allowed to be
1.0.
38 bool testLastFloatComponent(float component) {
39 return testFloatComponent(component) || component ==
1.0;
42 bool testData(vec4 data) {
43 if (u_testCase == TEST_CASE_IN_BOUND) {
44 return (testFloatComponentAccurate(data.x) &&
45 testFloatComponentAccurate(data.y) &&
46 testFloatComponentAccurate(data.z) &&
47 testFloatComponentAccurate(data.w));
48 } else if (u_testCase == TEST_CASE_OUT_OF_BOUND) {
49 return (testFloatComponent(data.x) &&
50 testFloatComponent(data.y) &&
51 testFloatComponent(data.z) &&
52 testLastFloatComponent(data.w));
58 if (testData(vecRandom)) {
59 v_color = vec4(
0.0,
1.0,
0.0,
1.0); // green -- We're good
61 v_color = vec4(
1.0,
0.0,
0.0,
1.0); // red -- Unexpected value
63 gl_Position = vec4(position,
0.0,
1.0);
68 description("This test verifies that out-of-bounds index buffers caused by CopyBufferSubData behave according to spec.");
70 // Ensure that drawElements flags either no error or INVALID_OPERATION. In the case of INVALID_OPERATION,
71 // no canvas pixels can be touched. In the case of NO_ERROR, all written values must either be the
72 // zero vertex or a value in the vertex buffer. See vsCheckOutOfBounds shader.
73 function verifyOutOfBoundsIndex(gl
) {
74 var error
= gl
.getError();
75 if (error
=== gl
.INVALID_OPERATION
) {
76 testPassed("drawElements flagged INVALID_OPERATION, which is valid so long as all canvas pixels were not touched.");
77 wtu
.checkCanvas(gl
, [0, 0, 255, 255]);
78 } else if (error
=== gl
.NO_ERROR
) {
79 testPassed("drawElements flagged NO_ERROR, which is valid so long as all canvas pixels are green.");
80 wtu
.checkCanvas(gl
, [0, 255, 0, 255]);
82 testFailed("Invalid error flagged by drawElements. Should be INVALID_OPERATION or NO_ERROR");
86 // Create an element array buffer with a tri-strip that starts at startIndex and make
87 // it the active element array buffer.
88 function prepareElementArrayBuffer(gl
, startIndex
) {
89 var glElementArrayBuffer
= gl
.createBuffer();
90 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, glElementArrayBuffer
);
91 var quadIndices
= new Uint16Array(4);
92 for (var i
= 0; i
< quadIndices
.length
; i
++) {
93 quadIndices
[i
] = startIndex
+ i
;
95 gl
.bufferData(gl
.ELEMENT_ARRAY_BUFFER
, quadIndices
, gl
.STATIC_DRAW
);
96 return glElementArrayBuffer
;
100 var wtu
= WebGLTestUtils
;
101 var canvas
= document
.getElementById("canvas");
102 var gl
= wtu
.create3DContext(canvas
, {antialias
: false}, 2);
104 var numberOfQuads
= 200;
106 // Create a vertex buffer with 200 properly formed tri-strip quads. These quads will cover the canvas texture
107 // such that every single pixel is touched by the fragment shader.
108 var quadBuffer
= gl
.createBuffer();
109 gl
.bindBuffer(gl
.ARRAY_BUFFER
, quadBuffer
);
110 var quadPositions
= new Float32Array(numberOfQuads
* /*ComponentsPerQuad*/2 * /*VerticesPerQuad*/4);
111 for (var i
= 0; i
< quadPositions
.length
; i
+= /*ComponentsPerQuad*/2 * /*VerticesPerQuad*/4) {
112 quadPositions
[i
+0] = -1.0; // upper left
113 quadPositions
[i
+1] = 1.0;
114 quadPositions
[i
+2] = 1.0; // upper right
115 quadPositions
[i
+3] = 1.0;
116 quadPositions
[i
+4] = -1.0; // lower left
117 quadPositions
[i
+5] = -1.0;
118 quadPositions
[i
+6] = 1.0; // lower right
119 quadPositions
[i
+7] = -1.0;
121 gl
.bufferData(gl
.ARRAY_BUFFER
, quadPositions
, gl
.STATIC_DRAW
);
122 gl
.enableVertexAttribArray(0);
123 gl
.vertexAttribPointer(0, 2, gl
.FLOAT
, false, 0, 0);
125 // Create a small vertex buffer with determined-ahead-of-time "random" values (0.2). This buffer will be
126 // the one indexed off the end.
127 var vertexBuffer
= gl
.createBuffer();
128 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertexBuffer
);
129 gl
.bufferData(gl
.ARRAY_BUFFER
,
130 new Float32Array([0.2, 0.2, 0.2, 0.2,
133 0.2, 0.2, 0.2, 0.2]),
135 gl
.enableVertexAttribArray(1);
136 gl
.vertexAttribPointer(1, 4, gl
.FLOAT
, false, 0, 0);
138 // Setup the verification program.
139 var program
= wtu
.setupProgram(gl
, ["vsCheckOutOfBounds", wtu
.simpleVertexColorFragmentShader
], ["position", "vecRandom"]);
140 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Shader and buffer setup should generate no errors");
141 var loc
= gl
.getUniformLocation(program
, "u_testCase");
142 shouldBeNonNull(loc
);
145 debug("Test -- Vertex indices are in bounds.");
146 gl
.uniform1i(loc
, 1); // TEST_CASE_IN_BOUND == 1
147 gl
.clearColor(0.0, 0.0, 1.0, 1.0); // Start with blue to indicate no pixels touched.
148 gl
.clear(gl
.COLOR_BUFFER_BIT
| gl
.DEPTH_BUFFER_BIT
);
149 var elementArrayBuffer
= prepareElementArrayBuffer(gl
, /*StartIndex*/0);
150 gl
.drawElements(gl
.TRIANGLE_STRIP
, 4, gl
.UNSIGNED_SHORT
, /*offset*/0);
151 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Draw call should generate no errors");
152 wtu
.checkCanvas(gl
, [0, 255, 0, 255]);
155 debug("Test -- Index off the end of the vertex buffer near the beginning of the out of bounds area.");
156 gl
.uniform1i(loc
, 2); // TEST_CASE_OUT_OF_BOUND == 2
157 gl
.clearColor(0.0, 0.0, 1.0, 1.0); // Start with blue to indicate no pixels touched.
158 gl
.clear(gl
.COLOR_BUFFER_BIT
| gl
.DEPTH_BUFFER_BIT
);
159 var outOfBoundsElementArrayBuffer
= prepareElementArrayBuffer(gl
, /*StartIndex*/4);
160 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, outOfBoundsElementArrayBuffer
);
161 gl
.bindBuffer(gl
.COPY_WRITE_BUFFER
, elementArrayBuffer
);
162 gl
.copyBufferSubData(gl
.ELEMENT_ARRAY_BUFFER
, gl
.COPY_WRITE_BUFFER
, 0, 0, 4 * Uint16Array
.BYTES_PER_ELEMENT
);
163 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "copyBufferSubData should generate no errors");
164 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementArrayBuffer
);
165 gl
.drawElements(gl
.TRIANGLE_STRIP
, 4, gl
.UNSIGNED_SHORT
, /*offset*/0);
166 verifyOutOfBoundsIndex(gl
);
169 debug("Test -- Index off the end of the vertex buffer near the end of the out of bounds area.")
170 gl
.uniform1i(loc
, 2); // TEST_CASE_OUT_OF_BOUND == 2
171 gl
.clearColor(0.0, 0.0, 1.0, 1.0); // Start with blue to indicate no pixels touched.
172 gl
.clear(gl
.COLOR_BUFFER_BIT
| gl
.DEPTH_BUFFER_BIT
);
173 outOfBoundsElementArrayBuffer
= prepareElementArrayBuffer(gl
, /*StartIndex*/numberOfQuads
- 4);
174 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementArrayBuffer
);
175 gl
.bindBuffer(gl
.COPY_READ_BUFFER
, outOfBoundsElementArrayBuffer
);
176 gl
.copyBufferSubData(gl
.COPY_READ_BUFFER
, gl
.ELEMENT_ARRAY_BUFFER
, 0, 0, 4 * Uint16Array
.BYTES_PER_ELEMENT
);
177 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "copyBufferSubData should generate no errors");
178 gl
.drawElements(gl
.TRIANGLE_STRIP
, 4, gl
.UNSIGNED_SHORT
, /*offset*/0);
179 verifyOutOfBoundsIndex(gl
);
182 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Running tests should generate no errors");
183 var successfullyParsed
= true;
185 <script src=
"../../js/js-test-post.js"></script>