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 <title>WebGL
2 Uninitialized GL Resources Tests
</title>
11 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
12 <script src=
"../../js/js-test-pre.js"></script>
13 <script src=
"../../js/webgl-test-utils.js"></script>
16 <div id=
"description"></div>
17 <div id=
"console"></div>
18 <canvas id=
"canvas" width=
"2" height=
"2"> </canvas>
21 description("Tests to check user code cannot access uninitialized data from GL resources.");
23 var wtu
= WebGLTestUtils
;
24 var gl
= wtu
.create3DContext("canvas", undefined, 2);
26 testFailed("Context created.");
28 testPassed("Context created.");
30 // This is the maximum size that will end up being allocated with the tests
31 // currently written as they are. It could need to be increased later.
32 var scratchBuffer
= new ArrayBuffer(1024 * 1024 * 8 * 4);
33 function zeroArrayBuffer(arr
) {
34 for (var i
= 0; i
< arr
.length
; ++i
) {
38 function getUint32Array(length
) {
39 var arr
= new Uint32Array(scratchBuffer
, 0, length
);
43 function getInt32Array(length
) {
44 var arr
= new Int32Array(scratchBuffer
, 0, length
);
48 function getUint8Array(length
) {
49 var arr
= new Uint8Array(scratchBuffer
, 0, length
);
53 function getInt8Array(length
) {
54 var arr
= new Int8Array(scratchBuffer
, 0, length
);
59 function setupTexture(target
, texWidth
, texHeight
, texDepth
) {
60 var is3d
= (target
== gl
.TEXTURE_3D
|| target
== gl
.TEXTURE_2D_ARRAY
);
61 var texture
= gl
.createTexture();
62 gl
.bindTexture(target
, texture
);
64 gl
.texImage3D(target
, 0, gl
.RGBA8
, texWidth
, texHeight
, texDepth
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
65 } else if (target
== gl
.TEXTURE_2D
) {
66 gl
.texImage2D(target
, 0, gl
.RGBA8
, texWidth
, texHeight
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
68 gl
.texImage2D(gl
.TEXTURE_CUBE_MAP_POSITIVE_X
, 0, gl
.RGBA8
, texWidth
, texHeight
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
69 gl
.texImage2D(gl
.TEXTURE_CUBE_MAP_NEGATIVE_X
, 0, gl
.RGBA8
, texWidth
, texHeight
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
70 gl
.texImage2D(gl
.TEXTURE_CUBE_MAP_POSITIVE_Y
, 0, gl
.RGBA8
, texWidth
, texHeight
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
71 gl
.texImage2D(gl
.TEXTURE_CUBE_MAP_NEGATIVE_Y
, 0, gl
.RGBA8
, texWidth
, texHeight
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
72 gl
.texImage2D(gl
.TEXTURE_CUBE_MAP_POSITIVE_Z
, 0, gl
.RGBA8
, texWidth
, texHeight
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
73 gl
.texImage2D(gl
.TEXTURE_CUBE_MAP_NEGATIVE_Z
, 0, gl
.RGBA8
, texWidth
, texHeight
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
76 // this can be quite undeterministic so to improve odds of seeing uninitialized data write bits
77 // into tex then delete texture then re-create one with same characteristics (driver will likely reuse mem)
78 // with this trick on r59046 WebKit/OSX I get FAIL 100% of the time instead of ~15% of the time.
80 var badData
= getUint8Array(texWidth
* texHeight
* texDepth
* 4);
81 for (var i
= 0; i
< badData
.length
; ++i
)
85 gl
.texSubImage3D(target
, 0, 0, 0, 0, texWidth
, texHeight
, texDepth
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
86 } else if (target
== gl
.TEXTURE_2D
) {
87 gl
.texSubImage2D(target
, 0, 0, 0, texWidth
, texHeight
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
89 gl
.texSubImage2D(gl
.TEXTURE_CUBE_MAP_POSITIVE_X
, 0, 0, 0, texWidth
, texHeight
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
90 gl
.texSubImage2D(gl
.TEXTURE_CUBE_MAP_NEGATIVE_X
, 0, 0, 0, texWidth
, texHeight
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
91 gl
.texSubImage2D(gl
.TEXTURE_CUBE_MAP_POSITIVE_Y
, 0, 0, 0, texWidth
, texHeight
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
92 gl
.texSubImage2D(gl
.TEXTURE_CUBE_MAP_NEGATIVE_Y
, 0, 0, 0, texWidth
, texHeight
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
93 gl
.texSubImage2D(gl
.TEXTURE_CUBE_MAP_POSITIVE_Z
, 0, 0, 0, texWidth
, texHeight
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
94 gl
.texSubImage2D(gl
.TEXTURE_CUBE_MAP_NEGATIVE_Z
, 0, 0, 0, texWidth
, texHeight
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, badData
);
96 gl
.finish(); // make sure it has been uploaded
98 gl
.deleteTexture(texture
);
99 gl
.finish(); // make sure it has been deleted
101 var texture
= gl
.createTexture();
102 gl
.bindTexture(target
, texture
);
106 function checkNonZeroPixels(texture
, target
, format
, type
, texWidth
, texHeight
, level
, layer
, exceptions
) {
108 var is3d
= (target
== gl
.TEXTURE_3D
|| target
== gl
.TEXTURE_2D_ARRAY
);
110 case gl
.TEXTURE_CUBE_MAP_POSITIVE_X
:
111 case gl
.TEXTURE_CUBE_MAP_NEGATIVE_X
:
112 case gl
.TEXTURE_CUBE_MAP_POSITIVE_Y
:
113 case gl
.TEXTURE_CUBE_MAP_NEGATIVE_Y
:
114 case gl
.TEXTURE_CUBE_MAP_POSITIVE_Z
:
115 case gl
.TEXTURE_CUBE_MAP_NEGATIVE_Z
:
116 gl
.bindTexture(gl
.TEXTURE_CUBE_MAP
, null);
119 gl
.bindTexture(target
, null);
122 var fb
= gl
.createFramebuffer();
123 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
125 gl
.framebufferTextureLayer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, texture
, level
, layer
);
127 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, target
, texture
, level
);
129 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
133 case gl
.UNSIGNED_INT
:
134 data
= getUint32Array(texWidth
* texHeight
* 4);
137 data
= getInt32Array(texWidth
* texHeight
* 4);
139 case gl
.UNSIGNED_BYTE
:
141 data
= getUint8Array(texWidth
* texHeight
* 4);
144 gl
.readPixels(0, 0, texWidth
, texHeight
, format
, type
, data
);
147 var failed_exceptions
= 0;
148 for (var y
= 0; y
< texHeight
; ++y
) {
149 for (var x
= 0; x
< texWidth
; ++x
) {
150 var index
= (y
* texWidth
+ x
) * 4;
151 var is_exception
= false;
152 for (var ii
= 0; ii
< exceptions
.length
; ++ii
) {
153 if (exceptions
[ii
].x
== x
&& exceptions
[ii
].y
== y
) {
155 if (Math
.abs(data
[index
] - exceptions
[ii
].r
) > tol
||
156 Math
.abs(data
[index
+ 1] - exceptions
[ii
].g
) > tol
||
157 Math
.abs(data
[index
+ 2] - exceptions
[ii
].b
) > tol
||
158 Math
.abs(data
[index
+ 3] - exceptions
[ii
].a
) > tol
) {
165 for (var i
= 0; i
< 4; ++i
) {
166 if (data
[index
+ i
] != 0) {
172 var info
= "Level = " + level
;
174 info
+= ", layer = " + layer
;
177 testFailed(info
+ "found " + k
+ " non-zero elements");
179 testPassed(info
+ "all data initialized");
181 if (exceptions
.length
> 0) {
182 if (failed_exceptions
) {
183 testFailed(info
+ "found " + failed_exceptions
+ " elements incorrectly overwritten");
185 testPassed(info
+ "all initialized elements stay untouched");
190 function testTexImage3D() {
192 var max_3d_texture_size
= Math
.min(gl
.getParameter(gl
.MAX_3D_TEXTURE_SIZE
), 1024);
195 // TEXTURE_3D + RGBA8
197 target
: "TEXTURE_3D",
198 internal_format
: "RGBA8",
200 type
: gl
.UNSIGNED_BYTE
,
201 read_type
: gl
.UNSIGNED_BYTE
,
202 width
: 256, // minimum MAX_3D_TEXTURE_SIZE is 256
205 exceptions
: [ { x
: 0, y
: 0, r
: 108, g
: 72, b
: 36, a
: 9 } ],
208 target
: "TEXTURE_3D",
209 internal_format
: "RGBA8",
211 type
: gl
.UNSIGNED_BYTE
,
212 read_type
: gl
.UNSIGNED_BYTE
,
213 width
: 256, // minimum MAX_3D_TEXTURE_SIZE is 256
219 target
: "TEXTURE_3D",
220 internal_format
: "RGBA8",
222 type
: gl
.UNSIGNED_BYTE
,
223 read_type
: gl
.UNSIGNED_BYTE
,
224 width
: max_3d_texture_size
,
225 height
: max_3d_texture_size
,
227 exceptions
: [ { x
: 0, y
: 128, r
: 108, g
: 72, b
: 36, a
: 9 } ],
230 target
: "TEXTURE_3D",
231 internal_format
: "RGBA8",
233 type
: gl
.UNSIGNED_BYTE
,
234 read_type
: gl
.UNSIGNED_BYTE
,
235 width
: max_3d_texture_size
,
236 height
: max_3d_texture_size
,
241 // TEXTURE_3D + RGBA8UI
243 target
: "TEXTURE_3D",
244 internal_format
: "RGBA8UI",
245 format
: gl
.RGBA_INTEGER
,
246 type
: gl
.UNSIGNED_BYTE
,
247 read_type
: gl
.UNSIGNED_INT
,
248 width
: 256, // minimum MAX_3D_TEXTURE_SIZE is 256
251 exceptions
: [ { x
: 0, y
: 255, r
: 108, g
: 72, b
: 36, a
: 9 } ],
254 target
: "TEXTURE_3D",
255 internal_format
: "RGBA8UI",
256 format
: gl
.RGBA_INTEGER
,
257 type
: gl
.UNSIGNED_BYTE
,
258 read_type
: gl
.UNSIGNED_INT
,
259 width
: 256, // minimum MAX_3D_TEXTURE_SIZE is 256
265 target
: "TEXTURE_3D",
266 internal_format
: "RGBA8UI",
267 format
: gl
.RGBA_INTEGER
,
268 type
: gl
.UNSIGNED_BYTE
,
269 read_type
: gl
.UNSIGNED_INT
,
270 width
: max_3d_texture_size
,
271 height
: max_3d_texture_size
,
273 exceptions
: [ { x
: 128, y
: 0, r
: 108, g
: 72, b
: 36, a
: 9 } ],
276 target
: "TEXTURE_3D",
277 internal_format
: "RGBA8UI",
278 format
: gl
.RGBA_INTEGER
,
279 type
: gl
.UNSIGNED_BYTE
,
280 read_type
: gl
.UNSIGNED_INT
,
281 width
: max_3d_texture_size
,
282 height
: max_3d_texture_size
,
287 // TEXTURE_3D + RGBA8I
289 target
: "TEXTURE_3D",
290 internal_format
: "RGBA8I",
291 format
: gl
.RGBA_INTEGER
,
294 width
: 256, // minimum MAX_3D_TEXTURE_SIZE is 256
297 exceptions
: [ { x
: 128, y
: 255, r
: 108, g
: 72, b
: 36, a
: 9 } ],
300 target
: "TEXTURE_3D",
301 internal_format
: "RGBA8I",
302 format
: gl
.RGBA_INTEGER
,
305 width
: 256, // minimum MAX_3D_TEXTURE_SIZE is 256
311 target
: "TEXTURE_3D",
312 internal_format
: "RGBA8I",
313 format
: gl
.RGBA_INTEGER
,
316 width
: max_3d_texture_size
,
317 height
: max_3d_texture_size
,
319 exceptions
: [ { x
: 128, y
: 128, r
: 108, g
: 72, b
: 36, a
: 9 } ],
322 target
: "TEXTURE_3D",
323 internal_format
: "RGBA8I",
324 format
: gl
.RGBA_INTEGER
,
327 width
: max_3d_texture_size
,
328 height
: max_3d_texture_size
,
333 // TEXTURE_2D_ARRAY + RGBA8
335 target
: "TEXTURE_2D_ARRAY",
336 internal_format
: "RGBA8",
338 type
: gl
.UNSIGNED_BYTE
,
339 read_type
: gl
.UNSIGNED_BYTE
,
343 exceptions
: [ { x
: 1023, y
: 0, r
: 108, g
: 72, b
: 36, a
: 9 } ],
346 target
: "TEXTURE_2D_ARRAY",
347 internal_format
: "RGBA8",
349 type
: gl
.UNSIGNED_BYTE
,
350 read_type
: gl
.UNSIGNED_BYTE
,
357 target
: "TEXTURE_2D_ARRAY",
358 internal_format
: "RGBA8",
360 type
: gl
.UNSIGNED_BYTE
,
361 read_type
: gl
.UNSIGNED_BYTE
,
364 depth
: 256, // minimum MAX_ARRAY_TEXTURE_LAYERS is 256
365 exceptions
: [ { x
: 63, y
: 32, r
: 108, g
: 72, b
: 36, a
: 9 } ],
368 target
: "TEXTURE_2D_ARRAY",
369 internal_format
: "RGBA8",
371 type
: gl
.UNSIGNED_BYTE
,
372 read_type
: gl
.UNSIGNED_BYTE
,
375 depth
: 256, // minimum MAX_ARRAY_TEXTURE_LAYERS is 256
379 // TEXTURE_2D_ARRAY + RGBA8UI
381 target
: "TEXTURE_2D_ARRAY",
382 internal_format
: "RGBA8UI",
383 format
: gl
.RGBA_INTEGER
,
384 type
: gl
.UNSIGNED_BYTE
,
385 read_type
: gl
.UNSIGNED_INT
,
389 exceptions
: [ { x
: 1023, y
: 1023, r
: 108, g
: 72, b
: 36, a
: 9 } ],
392 target
: "TEXTURE_2D_ARRAY",
393 internal_format
: "RGBA8UI",
394 format
: gl
.RGBA_INTEGER
,
395 type
: gl
.UNSIGNED_BYTE
,
396 read_type
: gl
.UNSIGNED_INT
,
403 target
: "TEXTURE_2D_ARRAY",
404 internal_format
: "RGBA8UI",
405 format
: gl
.RGBA_INTEGER
,
406 type
: gl
.UNSIGNED_BYTE
,
407 read_type
: gl
.UNSIGNED_INT
,
410 depth
: 256, // minimum MAX_ARRAY_TEXTURE_LAYERS is 256
411 exceptions
: [ { x
: 0, y
: 0, r
: 108, g
: 72, b
: 36, a
: 9 } ],
414 target
: "TEXTURE_2D_ARRAY",
415 internal_format
: "RGBA8UI",
416 format
: gl
.RGBA_INTEGER
,
417 type
: gl
.UNSIGNED_BYTE
,
418 read_type
: gl
.UNSIGNED_INT
,
421 depth
: 256, // minimum MAX_ARRAY_TEXTURE_LAYERS is 256
425 // TEXTURE_2D_ARRAY + RGBA8I
427 target
: "TEXTURE_2D_ARRAY",
428 internal_format
: "RGBA8I",
429 format
: gl
.RGBA_INTEGER
,
435 exceptions
: [ { x
: 512, y
: 1023, r
: 108, g
: 72, b
: 36, a
: 9 } ],
438 target
: "TEXTURE_2D_ARRAY",
439 internal_format
: "RGBA8I",
440 format
: gl
.RGBA_INTEGER
,
449 target
: "TEXTURE_2D_ARRAY",
450 internal_format
: "RGBA8I",
451 format
: gl
.RGBA_INTEGER
,
456 depth
: 256, // minimum MAX_ARRAY_TEXTURE_LAYERS is 256
457 exceptions
: [ { x
: 63, y
: 32, r
: 108, g
: 72, b
: 36, a
: 9 } ],
460 target
: "TEXTURE_2D_ARRAY",
461 internal_format
: "RGBA8I",
462 format
: gl
.RGBA_INTEGER
,
467 depth
: 256, // minimum MAX_ARRAY_TEXTURE_LAYERS is 256
471 // If more tests are added here, make sure to increase the size of
472 // scratchBuffer above, if needed.
475 for (var ii
= 0; ii
< test_cases
.length
; ++ii
) {
477 var test
= test_cases
[ii
];
478 debug("TexImage3D with target = " + test
.target
+ ", internal_format = " + test
.internal_format
+
479 ", width = " + test
.width
+ ", height = " + test
.height
+ ", depth = " + test
.depth
);
480 var tex
= setupTexture(gl
[test
.target
], test
.width
, test
.height
, test
.depth
);
481 gl
.texImage3D(gl
[test
.target
], 0, gl
[test
.internal_format
], test
.width
, test
.height
, test
.depth
, 0, test
.format
, test
.type
, null);
482 for (var jj
= 0; jj
< test
.exceptions
.length
; ++jj
) {
483 var exception
= test
.exceptions
[jj
];
487 data
= getInt8Array(4 * test
.depth
);
489 case gl
.UNSIGNED_BYTE
:
490 data
= getUint8Array(4 * test
.depth
);
495 for (var pixel
= 0; pixel
< test
.depth
; ++pixel
) {
496 data
[pixel
* 4] = exception
.r
;
497 data
[pixel
* 4 + 1] = exception
.g
;
498 data
[pixel
* 4 + 2] = exception
.b
;
499 data
[pixel
* 4 + 3] = exception
.a
;
501 gl
.texSubImage3D(gl
[test
.target
], 0, exception
.x
, exception
.y
, 0, 1, 1, test
.depth
, test
.format
, test
.type
, data
);
503 for (var layer
= 0; layer
< test
.depth
; ++layer
)
504 checkNonZeroPixels(tex
, gl
[test
.target
], test
.format
, test
.read_type
, test
.width
, test
.height
, 0, layer
, test
.exceptions
);
505 gl
.deleteTexture(tex
);
507 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
511 function testTexStorage2D() {
512 var targets
= [ "TEXTURE_2D", "TEXTURE_CUBE_MAP" ];
517 for (var ii
= 0; ii
< targets
.length
; ++ii
) {
519 debug("Reading an uninitialized texture (texStorage2D) should succeed with all bytes set to 0 : target = " + targets
[ii
]);
520 var tex
= setupTexture(gl
[targets
[ii
]], width
, height
, 1);
521 gl
.texStorage2D(gl
[targets
[ii
]], levels
, gl
.RGBA8
, width
, height
);
522 for (var level
= 0; level
< levels
; ++level
) {
523 if (gl
[targets
[ii
]] == gl
.TEXTURE_2D
) {
524 checkNonZeroPixels(tex
, gl
[targets
[ii
]], gl
.RGBA
, gl
.UNSIGNED_BYTE
, width
, height
, level
, 0, []);
526 checkNonZeroPixels(tex
, gl
.TEXTURE_CUBE_MAP_POSITIVE_X
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, width
, height
, level
, 0, []);
527 checkNonZeroPixels(tex
, gl
.TEXTURE_CUBE_MAP_NEGATIVE_X
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, width
, height
, level
, 0, []);
528 checkNonZeroPixels(tex
, gl
.TEXTURE_CUBE_MAP_POSITIVE_Y
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, width
, height
, level
, 0, []);
529 checkNonZeroPixels(tex
, gl
.TEXTURE_CUBE_MAP_NEGATIVE_Y
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, width
, height
, level
, 0, []);
530 checkNonZeroPixels(tex
, gl
.TEXTURE_CUBE_MAP_POSITIVE_Z
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, width
, height
, level
, 0, []);
531 checkNonZeroPixels(tex
, gl
.TEXTURE_CUBE_MAP_NEGATIVE_Z
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, width
, height
, level
, 0, []);
534 gl
.deleteTexture(tex
);
536 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
540 function testTexStorage3D() {
541 var targets
= [ "TEXTURE_3D", "TEXTURE_2D_ARRAY" ];
542 var internal_formats
= [ "RGBA8", "RGBA8UI", "RGBA8I" ];
543 var formats
= [ gl
.RGBA
, gl
.RGBA_INTEGER
, gl
.RGBA_INTEGER
];
544 var read_types
= [ gl
.UNSIGNED_BYTE
, gl
.UNSIGNED_INT
, gl
.INT
];
545 var width
= 256; // minimum MAX_3D_TEXTURE_SIZE is 256
546 var height
= 256; // minimum MAX_3D_TEXTURE_SIZE is 256
550 for (var ii
= 0; ii
< targets
.length
; ++ii
) {
552 debug("Reading an uninitialized texture (texStorage3D) should succeed with all bytes set to 0 : target = " + targets
[ii
]);
553 for (var jj
= 0; jj
< internal_formats
.length
; ++jj
) {
555 debug("Internal format : " + internal_formats
[jj
]);
556 var tex
= setupTexture(gl
[targets
[ii
]], width
, height
, depth
);
557 gl
.texStorage3D(gl
[targets
[ii
]], levels
, gl
[internal_formats
[jj
]], width
, height
, depth
);
558 var level_depth
= depth
;
559 for (var level
= 0; level
< levels
; ++level
) {
560 for (var layer
= 0; layer
< level_depth
; ++layer
) {
561 checkNonZeroPixels(tex
, gl
[targets
[ii
]], formats
[jj
], read_types
[jj
], width
, height
, level
, layer
, []);
563 if (gl
[targets
[ii
]] == gl
.TEXTURE_3D
)
564 level_depth
= Math
.max(1, level_depth
>> 1);
566 gl
.deleteTexture(tex
);
568 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
578 var successfullyParsed
= true;
580 <script src=
"../../js/js-test-post.js"></script>