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 <meta charset=
"utf-8">
11 <title>WebGL
2 ReadPixels Test.
</title>
12 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
13 <script src=
"../../js/js-test-pre.js"></script>
14 <script src=
"../../js/webgl-test-utils.js"> </script>
17 <div id=
"description"></div>
18 <div id=
"console"></div>
21 description("Checks that ReadPixels from a fbo works as expected.");
23 var wtu
= WebGLTestUtils
;
24 var gl
= wtu
.create3DContext(undefined, undefined, 2);
25 gl
.pixelStorei(gl
.PACK_ALIGNMENT
, 1);
27 function getChannelCount(format
) {
36 case gl
.LUMINANCE_ALPHA
:
49 function getUnpackInfo(type
) {
51 case gl
.UNSIGNED_SHORT_5_6_5
:
52 return {bitsCount
: [5, 6, 5], isReverse
: false};
53 case gl
.UNSIGNED_SHORT_4_4_4_4
:
54 return {bitsCount
: [4, 4, 4, 4], isReverse
: false};
55 case gl
.UNSIGNED_SHORT_5_5_5_1
:
56 return {bitsCount
: [5, 5, 5, 1], isReverse
: false};
57 case gl
.UNSIGNED_INT_2_10_10_10_REV
:
58 return {bitsCount
: [2, 10, 10, 10], isReverse
: true};
59 case gl
.UNSIGNED_INT_10F_11F_11F_REV
:
60 return {bitsCount
: [10, 11, 11], isReverse
: true};
61 case gl
.UNSIGNED_INT_5_9_9_9_REV
:
62 return {bitsCount
: [5, 9, 9, 9], isReverse
: true};
68 // bitsCount is an array contains bit count for each component.
69 function unpack(value
, channelCount
, bitsCount
, isReverse
) {
70 var result
= new Array(channelCount
);
72 var accumBitsCount
= 0;
73 for (var i
= channelCount
- 1; i
>= 0; --i
) {
74 var currentChannel
= isReverse
? (channelCount
- i
- 1) : i
;
75 var mask
= 0xFFFFFFFF >>> (32 - bitsCount
[i
]);
76 result
[currentChannel
] = ((value
>> accumBitsCount
) & mask
);
77 accumBitsCount
+= bitsCount
[i
];
83 function getColor(buf
, index
, readFormat
, readType
) {
84 var channelCount
= getChannelCount(readFormat
);
85 var result
= new Array(channelCount
);
87 var unpackInfo
= getUnpackInfo(readType
);
89 result
= unpack(buf
[index
], channelCount
, unpackInfo
.bitsCount
, unpackInfo
.isReverse
);
91 for (var i
= 0; i
< channelCount
; ++i
) {
92 result
[i
] = buf
[index
+ i
];
99 function convertToSRGB(val
) {
102 } else if (val
< 0.0031308) {
104 } else if (val
< 1) {
105 return 1.055 * Math
.pow(val
, 0.41666) - 0.055;
111 function denormalizeColor(srcInternalFormat
, destType
, color
) {
112 var result
= color
.slice();
115 var srcIsNormalized
= false;
117 switch (srcInternalFormat
) {
123 case gl
.SRGB8_ALPHA8
:
125 srcIsNormalized
= true;
129 // RGB565 needs slightly extra tolerance, at least on Google Pixel. crbug.com/682753
130 srcIsNormalized
= true;
134 srcIsNormalized
= true;
139 if (!srcIsNormalized
) {
140 return { color
: result
, tol
: tol
};
143 if (srcInternalFormat
== gl
.SRGB8_ALPHA8
) {
144 for (var i
= 0; i
< 3; ++i
) {
145 result
[i
] = convertToSRGB(result
[i
]);
150 case gl
.UNSIGNED_BYTE
:
151 result
= result
.map(val
=> { return val
* 0xFF});
153 case gl
.UNSIGNED_SHORT
:
154 // On Linux NVIDIA, tol of 33 is necessary to pass the test.
156 result
= result
.map(val
=> { return val
* 0xFFFF});
158 case gl
.UNSIGNED_INT
:
160 result
= result
.map(val
=> { return val
* 0xFFFFFFFF});
162 case gl
.UNSIGNED_SHORT_4_4_4_4
:
163 result
= result
.map(val
=> { return val
* 0xF});
165 case gl
.UNSIGNED_SHORT_5_5_5_1
:
166 result
[0] = result
[0] * 0x1F;
167 result
[1] = result
[1] * 0x1F;
168 result
[2] = result
[2] * 0x1F;
169 result
[3] = result
[3] * 0x1;
171 case gl
.UNSIGNED_SHORT_5_6_5
:
172 result
[0] = result
[0] * 0x1F;
173 result
[1] = result
[1] * 0x3F;
174 result
[2] = result
[2] * 0x1F;
176 case gl
.UNSIGNED_INT_2_10_10_10_REV
:
178 result
[0] = result
[0] * 0x3FF;
179 result
[1] = result
[1] * 0x3FF;
180 result
[2] = result
[2] * 0x3FF;
181 result
[3] = result
[3] * 0x3;
183 case gl
.UNSIGNED_INT_5_9_9_9_REV
:
184 result
[0] = result
[0] * 0x1FF;
185 result
[1] = result
[1] * 0x1FF;
186 result
[2] = result
[2] * 0x1FF;
187 result
[3] = result
[3] * 0x1F;
191 return { color
: result
, tol
: tol
};
194 function compareColor(buf
, index
, expectedColor
, srcInternalFormat
,
195 srcFormat
, srcType
, readFormat
, readType
) {
196 var srcChannelCount
= getChannelCount(srcFormat
);
197 var readChannelCount
= getChannelCount(readFormat
);
199 var color
= getColor(buf
, index
, readFormat
, readType
);
200 expectedColor
= denormalizeColor(srcInternalFormat
, readType
, expectedColor
);
202 var minChannel
= Math
.min(srcChannelCount
, readChannelCount
);
203 for (var i
= 0; i
< minChannel
; ++i
) {
204 if (Math
.abs(expectedColor
.color
[i
] - color
[i
]) > expectedColor
.tol
) {
205 testFailed("Expected color = " + expectedColor
.color
+ ", was = " + color
);
213 var textureTestCases
= [
215 texInternalFormat
: 'R8', texFormat
: 'RED', texType
: 'UNSIGNED_BYTE',
216 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
217 clearColor
: [0.5, 0.0, 0.0, 0],
220 texInternalFormat
: 'R8UI', texFormat
: 'RED_INTEGER', texType
: 'UNSIGNED_BYTE',
221 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
222 clearColor
: [250, 0, 0, 0],
225 texInternalFormat
: 'R8I', texFormat
: 'RED_INTEGER', texType
: 'BYTE',
226 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
227 clearColor
: [-126, 0, 0, 0],
230 texInternalFormat
: 'R16UI', texFormat
: 'RED_INTEGER', texType
: 'UNSIGNED_SHORT',
231 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
232 clearColor
: [30001, 0, 0, 0],
235 texInternalFormat
: 'R16I', texFormat
: 'RED_INTEGER', texType
: 'SHORT',
236 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
237 clearColor
: [-14189, 0, 0, 0],
240 texInternalFormat
: 'R32UI', texFormat
: 'RED_INTEGER', texType
: 'UNSIGNED_INT',
241 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
242 clearColor
: [126726, 0, 0, 0],
245 texInternalFormat
: 'R32I', texFormat
: 'RED_INTEGER', texType
: 'INT',
246 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
247 clearColor
: [-126726, 0, 0, 0],
251 texInternalFormat
: 'RG8', texFormat
: 'RG', texType
: 'UNSIGNED_BYTE',
252 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
253 clearColor
: [0.5, 0.7, 0.0, 0],
256 texInternalFormat
: 'RG8UI', texFormat
: 'RG_INTEGER', texType
: 'UNSIGNED_BYTE',
257 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
258 clearColor
: [250, 124, 0, 0],
261 texInternalFormat
: 'RG8I', texFormat
: 'RG_INTEGER', texType
: 'BYTE',
262 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
263 clearColor
: [-55, 124, 0, 0],
266 texInternalFormat
: 'RG16UI', texFormat
: 'RG_INTEGER', texType
: 'UNSIGNED_SHORT',
267 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
268 clearColor
: [30001, 18, 0, 0],
271 texInternalFormat
: 'RG16I', texFormat
: 'RG_INTEGER', texType
: 'SHORT',
272 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
273 clearColor
: [-14189, 6735, 0, 0],
276 texInternalFormat
: 'RG32UI', texFormat
: 'RG_INTEGER', texType
: 'UNSIGNED_INT',
277 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
278 clearColor
: [126726, 1976, 0, 0],
281 texInternalFormat
: 'RG32I', texFormat
: 'RG_INTEGER', texType
: 'INT',
282 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
283 clearColor
: [-126726, 126726, 0, 0],
287 texInternalFormat
: 'RGB8', texFormat
: 'RGB', texType
: 'UNSIGNED_BYTE',
288 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
289 clearColor
: [0.5, 1, 0, 0],
292 texInternalFormat
: 'RGB565', texFormat
: 'RGB', texType
: 'UNSIGNED_BYTE',
293 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
294 clearColor
: [0.5, 0.7, 0.2, 0],
297 texInternalFormat
: 'RGB565', texFormat
: 'RGB', texType
: 'UNSIGNED_SHORT_5_6_5',
298 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
299 clearColor
: [0.5, 0.7, 0.2, 0],
303 texInternalFormat
: 'RGBA8', texFormat
: 'RGBA', texType
: 'UNSIGNED_BYTE',
304 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
305 clearColor
: [0.5, 0, 1, 0.7],
308 texInternalFormat
: 'SRGB8_ALPHA8', texFormat
: 'RGBA', texType
: 'UNSIGNED_BYTE',
309 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
310 clearColor
: [0.5, 0, 1, 0.7],
313 texInternalFormat
: 'RGB5_A1', texFormat
: 'RGBA', texType
: 'UNSIGNED_BYTE',
314 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
315 clearColor
: [0.5, 0, 0.7, 1],
318 texInternalFormat
: 'RGB5_A1', texFormat
: 'RGBA', texType
: 'UNSIGNED_SHORT_5_5_5_1',
319 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
320 clearColor
: [0.5, 0.7, 1, 0],
323 texInternalFormat
: 'RGB5_A1', texFormat
: 'RGBA', texType
: 'UNSIGNED_INT_2_10_10_10_REV',
324 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
325 clearColor
: [0.5, 0.7, 0, 1],
328 texInternalFormat
: 'RGBA4', texFormat
: 'RGBA', texType
: 'UNSIGNED_BYTE',
329 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
330 clearColor
: [0.5, 0.7, 1, 0],
333 texInternalFormat
: 'RGBA4', texFormat
: 'RGBA', texType
: 'UNSIGNED_SHORT_4_4_4_4',
334 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
335 clearColor
: [1, 0, 0.5, 0.7],
338 texInternalFormat
: 'RGBA8UI', texFormat
: 'RGBA_INTEGER', texType
: 'UNSIGNED_BYTE',
339 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
340 clearColor
: [127, 0, 255, 178],
343 texInternalFormat
: 'RGBA8I', texFormat
: 'RGBA_INTEGER', texType
: 'BYTE',
344 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
345 clearColor
: [-55, 56, 80, 127],
348 texInternalFormat
: 'RGB10_A2UI', texFormat
: 'RGBA_INTEGER', texType
: 'UNSIGNED_INT_2_10_10_10_REV',
349 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
350 clearColor
: [178, 0, 127, 3],
353 texInternalFormat
: 'RGBA16UI', texFormat
: 'RGBA_INTEGER', texType
: 'UNSIGNED_SHORT',
354 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
355 clearColor
: [14189, 6735, 0, 19],
358 texInternalFormat
: 'RGBA16I', texFormat
: 'RGBA_INTEGER', texType
: 'SHORT',
359 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
360 clearColor
: [14189, -6735, 0, 19],
363 texInternalFormat
: 'RGBA32UI', texFormat
: 'RGBA_INTEGER', texType
: 'UNSIGNED_INT',
364 readFormat
: 'RGBA_INTEGER', readType
: 'UNSIGNED_INT',
365 clearColor
: [126726, 6726, 98765, 2015],
368 texInternalFormat
: 'RGBA32I', texFormat
: 'RGBA_INTEGER', texType
: 'INT',
369 readFormat
: 'RGBA_INTEGER', readType
: 'INT',
370 clearColor
: [126726, -6726, -98765, 2015],
374 texInternalFormat
: 'RGB10_A2', texFormat
: 'RGBA', texType
: 'UNSIGNED_INT_2_10_10_10_REV',
375 readFormat
: 'RGBA', readType
: 'UNSIGNED_BYTE',
376 clearColor
: [0.7, 0, 0.5, 1],
379 // TODO(zmo): add float/half_float test cases with extension supports.
382 function getArrayTypeFromReadPixelsType(gl
, type
) {
384 case gl
.UNSIGNED_BYTE
:
388 case gl
.UNSIGNED_SHORT
:
389 case gl
.UNSIGNED_SHORT_5_6_5
:
390 case gl
.UNSIGNED_SHORT_4_4_4_4
:
391 case gl
.UNSIGNED_SHORT_5_5_5_1
:
395 case gl
.UNSIGNED_INT
:
396 case gl
.UNSIGNED_INT_2_10_10_10_REV
:
397 case gl
.UNSIGNED_INT_10F_11F_11F_REV
:
398 case gl
.UNSIGNED_INT_5_9_9_9_REV
:
411 function getFormatString(gl
, format
) {
416 return 'RED_INTEGER';
424 return 'RGB_INTEGER';
427 case gl
.RGBA_INTEGER
:
428 return 'RGBA_INTEGER';
431 case gl
.LUMINANCE_ALPHA
:
432 return 'LUMINANCE_ALPHA';
440 function getTypeString(gl
, type
) {
442 case gl
.UNSIGNED_BYTE
:
443 return 'UNSIGNED_BYTE';
446 case gl
.UNSIGNED_SHORT
:
447 return 'UNSIGNED_SHORT';
450 case gl
.UNSIGNED_INT
:
451 return 'UNSIGNED_INT';
454 case gl
.UNSIGNED_SHORT_5_6_5
:
455 return 'UNSIGNED_SHORT_5_6_5';
456 case gl
.UNSIGNED_SHORT_5_5_5_1
:
457 return 'UNSIGNED_SHORT_5_5_5_1';
458 case gl
.UNSIGNED_INT_2_10_10_10_REV
:
459 return 'UNSIGNED_INT_2_10_10_10_REV';
460 case gl
.UNSIGNED_SHORT_4_4_4_4
:
461 return 'UNSIGNED_SHORT_4_4_4_4';
462 case gl
.UNSIGNED_INT_10F_11F_11F_REV
:
463 return 'UNSIGNED_INT_10F_11F_11F_REV';
464 case gl
.UNSIGNED_INT_5_9_9_9_REV
:
465 return 'UNSIGNED_INT_5_9_9_9_REV';
471 function elementCountPerPixel(gl
, readFormat
, readType
) {
472 switch (readFormat
) {
480 case gl
.LUMINANCE_ALPHA
:
485 case gl
.UNSIGNED_SHORT_5_6_5
:
491 case gl
.RGBA_INTEGER
:
493 case gl
.UNSIGNED_SHORT_4_4_4_4
:
494 case gl
.UNSIGNED_SHORT_5_5_5_1
:
495 case gl
.UNSIGNED_INT_2_10_10_10_REV
:
496 case gl
.UNSIGNED_INT_10F_11F_11F_REV
:
497 case gl
.UNSIGNED_INT_5_9_9_9_REV
:
503 testFailed("Unexpected read format/type = " + readFormat
+ "/" + readType
);
508 function testReadPixels(gl
, srcInternalFormat
, srcFormat
, srcType
,
509 readFormat
, readType
, expectedColor
) {
510 var arrayType
= getArrayTypeFromReadPixelsType(gl
, readType
);
511 var buf
= new arrayType(width
* height
* 4);
512 gl
.readPixels(0, 0, width
, height
, readFormat
, readType
, buf
);
514 gl
, gl
.NO_ERROR
, "readPixels should generate no error");
515 var diffFound
= false;
516 for (var ii
= 0; ii
< width
* height
; ++ii
) {
517 var offset
= ii
* elementCountPerPixel(gl
, readFormat
, readType
);
518 if (!compareColor(buf
, offset
, expectedColor
, srcInternalFormat
, srcFormat
, srcType
,
519 readFormat
, readType
)) {
525 testPassed("Color read back as expected");
529 function clearBuffer(gl
, texInternalFormat
, clearColor
) {
531 switch (texInternalFormat
) {
542 value
= new Uint32Array(4);
543 for (var ii
= 0; ii
< 4; ++ii
)
544 value
[ii
] = clearColor
[ii
];
545 gl
.clearBufferuiv(gl
.COLOR
, 0, value
);
556 value
= new Int32Array(4);
557 for (var ii
= 0; ii
< 4; ++ii
)
558 value
[ii
] = clearColor
[ii
];
559 gl
.clearBufferiv(gl
.COLOR
, 0, value
);
562 gl
.clearColor(clearColor
[0], clearColor
[1], clearColor
[2], clearColor
[3]);
563 gl
.clear(gl
.COLOR_BUFFER_BIT
);
568 for (var tt
= 0; tt
< textureTestCases
.length
; ++tt
) {
569 var test
= textureTestCases
[tt
];
571 debug("ReadPixels from fbo with texture = (" + test
.texInternalFormat
+
572 ", " + test
.texFormat
+ ", " + test
.texType
+
573 "), format = " + test
.readFormat
+ ", type = " + test
.readType
);
576 var fbo
= gl
.createFramebuffer();
577 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
578 var colorImage
= gl
.createTexture();
579 gl
.bindTexture(gl
.TEXTURE_2D
, colorImage
);
580 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
[test
.texInternalFormat
], width
, height
, 0,
581 gl
[test
.texFormat
], gl
[test
.texType
], null);
582 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
,
583 gl
.TEXTURE_2D
, colorImage
, 0);
585 gl
, gl
.NO_ERROR
, "Setting up fbo should generate no error");
586 if (gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
) != gl
.FRAMEBUFFER_COMPLETE
) {
587 debug("fbo is not complete, skip");
590 clearBuffer(gl
, gl
[test
.texInternalFormat
], test
.clearColor
);
592 gl
, gl
.NO_ERROR
, "Clear color should generate no error");
594 var implFormat
= gl
.getParameter(gl
.IMPLEMENTATION_COLOR_READ_FORMAT
);
595 var implType
= gl
.getParameter(gl
.IMPLEMENTATION_COLOR_READ_TYPE
);
596 var implFormatString
= getFormatString(gl
, implFormat
);
597 var implTypeString
= getTypeString(gl
, implType
);
599 if (gl
[test
.texInternalFormat
] == gl
.RGB10_A2
) {
600 // This is a special case where three read format/type are supported.
601 var readTypes
= [gl
.UNSIGNED_BYTE
, gl
.UNSIGNED_INT_2_10_10_10_REV
];
602 var readTypeStrings
= ['UNSIGNED_BYTE', 'UNSIGNED_INT_2_10_10_10_REV'];
603 if (implFormat
== gl
.RGBA
&& implTypeString
!= '') {
604 readTypes
.push(implType
);
605 readTypeStrings
.push(implTypeString
);
607 testFailed("Unimplemented Implementation dependent color read format/type = " +
608 implFormat
+ "/" + implType
);
610 for (var rr
= 0; rr
< readTypes
.length
; ++rr
) {
611 debug("Special case RGB10_A2, format = RGBA, type = " + readTypeStrings
[rr
]);
612 testReadPixels(gl
, gl
[test
.texInternalFormat
], gl
[test
.texFormat
], gl
[test
.texType
],
613 gl
.RGBA
, readTypes
[rr
], test
.clearColor
);
616 testReadPixels(gl
, gl
[test
.texInternalFormat
], gl
[test
.texFormat
], gl
[test
.texType
],
617 gl
[test
.readFormat
], gl
[test
.readType
], test
.clearColor
);
619 debug("Testing implementation dependent color read format = " + implFormatString
+
620 ", type = " + implTypeString
);
621 if (implFormatString
== '') {
622 testFailed("Invalid IMPLEMENTATION_COLOR_READ_FORMAT = " + implFormat
);
625 if (implTypeString
== '') {
626 testFailed("Invalid IMPLEMENTATION_COLOR_READ_TYPE = " + implType
);
629 testReadPixels(gl
, gl
[test
.texInternalFormat
], gl
[test
.texFormat
], gl
[test
.texType
],
630 implFormat
, implType
, test
.clearColor
);
632 gl
.deleteTexture(colorImage
);
633 gl
.deleteFramebuffer(fbo
);
638 var successfullyParsed
= true;
640 <script src=
"../../js/js-test-post.js"></script>