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 <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>
21 var wtu
= WebGLTestUtils
;
26 var depthStencilBuffer
;
31 var ALLOW_COMPLETE
= 0x01;
32 var ALLOW_UNSUPPORTED
= 0x02;
33 var ALLOW_INCOMPLETE_ATTACHMENT
= 0x04;
35 function checkFramebufferForAllowedStatuses(allowedStatuses
)
37 // If the framebuffer is in an error state for multiple reasons,
38 // we can't guarantee which one will be reported.
39 var status
= gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
);
40 var statusAllowed
= ((allowedStatuses
& ALLOW_COMPLETE
) && (status
== gl
.FRAMEBUFFER_COMPLETE
)) ||
41 ((allowedStatuses
& ALLOW_UNSUPPORTED
) && (status
== gl
.FRAMEBUFFER_UNSUPPORTED
)) ||
42 ((allowedStatuses
& ALLOW_INCOMPLETE_ATTACHMENT
) && (status
== gl
.FRAMEBUFFER_INCOMPLETE_ATTACHMENT
));
43 var msg
= "gl.checkFramebufferStatus(gl.FRAMEBUFFER) returned " + status
;
50 function checkBufferBits(attachment0
, attachment1
)
52 if (gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
) != gl
.FRAMEBUFFER_COMPLETE
)
55 var haveDepthBuffer
= attachment0
== gl
.DEPTH_ATTACHMENT
||
56 attachment0
== gl
.DEPTH_STENCIL_ATTACHMENT
||
57 attachment1
== gl
.DEPTH_ATTACHMENT
||
58 attachment1
== gl
.DEPTH_STENCIL_ATTACHMENT
;
59 var haveStencilBuffer
= attachment0
== gl
.STENCIL_ATTACHMENT
||
60 attachment0
== gl
.DEPTH_STENCIL_ATTACHMENT
||
61 attachment1
== gl
.STENCIL_ATTACHMENT
||
62 attachment1
== gl
.DEPTH_STENCIL_ATTACHMENT
;
64 shouldBeTrue("gl.getParameter(gl.RED_BITS) + gl.getParameter(gl.GREEN_BITS) + gl.getParameter(gl.BLUE_BITS) + gl.getParameter(gl.ALPHA_BITS) >= 16");
67 shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) >= 16");
69 shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) == 0");
71 if (haveStencilBuffer
)
72 shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) >= 8");
74 shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) == 0");
77 function testAttachment(attachment
, buffer
, allowedStatuses
)
79 shouldBeNonNull("fbo = gl.createFramebuffer()");
80 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
81 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
82 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, attachment
, gl
.RENDERBUFFER
, buffer
);
83 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
84 checkFramebufferForAllowedStatuses(allowedStatuses
);
85 if ((allowedStatuses
& ALLOW_COMPLETE
) == 0) {
86 gl
.clear(gl
.COLOR_BUFFER_BIT
);
87 wtu
.glErrorShouldBe(gl
, gl
.INVALID_FRAMEBUFFER_OPERATION
);
88 gl
.readPixels(0, 0, width
, height
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, new Uint8Array(width
* height
* 4));
89 wtu
.glErrorShouldBe(gl
, gl
.INVALID_FRAMEBUFFER_OPERATION
);
91 checkBufferBits(attachment
);
92 gl
.deleteFramebuffer(fbo
);
95 function testAttachments(attachment0
, buffer0
, attachment1
, buffer1
, allowedStatuses
)
97 shouldBeNonNull("fbo = gl.createFramebuffer()");
98 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
99 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
100 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, attachment0
, gl
.RENDERBUFFER
, buffer0
);
101 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
102 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, attachment1
, gl
.RENDERBUFFER
, buffer1
);
103 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
104 checkFramebufferForAllowedStatuses(allowedStatuses
);
105 checkBufferBits(attachment0
, attachment1
);
106 gl
.deleteFramebuffer(fbo
);
109 function testColorRenderbuffer(internalformat
, allowedStatuses
)
111 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
112 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
113 gl
.renderbufferStorage(gl
.RENDERBUFFER
, internalformat
, width
, height
);
114 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
115 testAttachment(gl
.COLOR_ATTACHMENT0
, colorBuffer
, allowedStatuses
);
118 function testDepthStencilRenderbuffer(allowedStatuses
)
120 shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
121 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthStencilBuffer
);
122 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.DEPTH_STENCIL
, width
, height
);
123 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
125 // OpenGL itself doesn't seem to guarantee that e.g. a 2 x 0
126 // renderbuffer will report 2 for its width when queried.
127 if (!(height
== 0 && width
> 0))
128 shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)", "width");
129 if (!(width
== 0 && height
> 0))
130 shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)", "height");
131 shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_INTERNAL_FORMAT)", "gl.DEPTH_STENCIL");
132 shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_RED_SIZE)", "0");
133 shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_GREEN_SIZE)", "0");
134 shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_BLUE_SIZE)", "0");
135 shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_ALPHA_SIZE)", "0");
136 // Avoid verifying these for zero-sized renderbuffers for the time
137 // being since it appears that even OpenGL doesn't guarantee them.
138 if (width
> 0 && height
> 0) {
139 shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_DEPTH_SIZE) > 0");
140 shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_STENCIL_SIZE) > 0");
142 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
143 testAttachment(gl
.DEPTH_STENCIL_ATTACHMENT
, depthStencilBuffer
, allowedStatuses
);
144 testDepthStencilDepthStencil();
147 function testDepthStencilDepthStencil()
149 if (!width
|| !height
) {
154 { firstFormat
: gl
.DEPTH_COMPONENT16
,
155 firstAttach
: gl
.DEPTH_ATTACHMENT
,
156 secondFormat
: gl
.DEPTH_STENCIL
,
157 secondAttach
: gl
.DEPTH_STENCIL_ATTACHMENT
159 { firstFormat
: gl
.DEPTH_STENCIL
,
160 firstAttach
: gl
.DEPTH_STENCIL_ATTACHMENT
,
161 secondFormat
: gl
.DEPTH_COMPONENT16
,
162 secondAttach
: gl
.DEPTH_ATTACHMENT
165 for (var ii
= 0; ii
< tests
.length
; ++ii
) {
166 var test
= tests
[ii
];
167 for (var jj
= 0; jj
< 2; ++jj
) {
168 var fbo
= gl
.createFramebuffer();
169 var tex
= gl
.createTexture();
170 var firstRb
= gl
.createRenderbuffer();
173 debug("test: " + wtu
.glEnumToString(gl
, test
.firstFormat
) + " vs " + wtu
.glEnumToString(gl
, test
.secondFormat
) + " with " + (jj
? "unbind" : "delete"));
175 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
176 // attach texture as color
177 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
178 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, width
, height
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
179 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.TEXTURE_2D
, tex
, 0);
182 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, firstRb
);
183 gl
.renderbufferStorage(gl
.RENDERBUFFER
, test
.firstFormat
, width
, height
);
184 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, test
.firstAttach
, gl
.RENDERBUFFER
, firstRb
);
186 shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE');
187 gl
.enable(gl
.DEPTH_TEST
);
188 var program
= wtu
.setupColorQuad(gl
);
190 wtu
.drawUByteColorQuad(gl
, [0, 255, 0, 255]);
191 wtu
.drawUByteColorQuad(gl
, [255, 0, 0, 255]); // should not draw since DEPTH_FUNC == LESS
192 wtu
.checkCanvasRect(gl
, 0, 0, width
, height
, [0, 255, 0, 255], "should be green");
194 var secondRb
= gl
.createRenderbuffer();
197 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, secondRb
);
198 gl
.renderbufferStorage(gl
.RENDERBUFFER
, test
.secondFormat
, width
, height
);
199 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, test
.secondAttach
, gl
.RENDERBUFFER
, secondRb
);
203 debug("test deleting second renderbuffer");
204 gl
.deleteRenderbuffer(secondRb
);
207 debug("test unbinding second renderbuffer");
208 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, test
.secondAttach
, gl
.RENDERBUFFER
, null);
211 // If the first attachment is not restored this may fail
212 shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE');
213 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
215 // If the first attachment is not restored this may fail.
216 gl
.clear(gl
.DEPTH_BUFFER_BIT
);
217 wtu
.drawUByteColorQuad(gl
, [0, 255, 0, 255]);
218 wtu
.drawUByteColorQuad(gl
, [255, 0, 0, 255]); // should not draw since DEPTH_FUNC == LESS
219 wtu
.checkCanvasRect(gl
, 0, 0, width
, height
, [0, 255, 0, 255], "should be green");
220 gl
.disable(gl
.DEPTH_TEST
);
223 gl
.deleteRenderbuffer(secondRb
);
226 gl
.deleteRenderbuffer(secondRb
);
227 gl
.deleteFramebuffer(fbo
);
230 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
233 description("Test framebuffer object attachment behaviors");
235 shouldBeNonNull("gl = wtu.create3DContext()");
237 function testFramebufferRequiredCombinations() {
238 debug("Checking combinations of framebuffer attachments required to be valid");
240 // Per discussion with the OpenGL ES working group, the following framebuffer attachment
241 // combinations are required to work in all WebGL implementations:
242 // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
243 // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbuffer
244 // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer
246 var fbo
= gl
.createFramebuffer();
247 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
252 // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
253 var texture
= gl
.createTexture();
254 gl
.bindTexture(gl
.TEXTURE_2D
, texture
);
255 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.NEAREST
);
256 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.NEAREST
);
257 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_S
, gl
.CLAMP_TO_EDGE
);
258 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_T
, gl
.CLAMP_TO_EDGE
);
259 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, width
, height
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
260 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.TEXTURE_2D
, texture
, 0);
261 checkFramebufferForAllowedStatuses(ALLOW_COMPLETE
);
264 // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbuffer
265 var renderbuffer
= gl
.createRenderbuffer();
266 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, renderbuffer
);
267 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.DEPTH_COMPONENT16
, width
, height
);
268 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.DEPTH_ATTACHMENT
, gl
.RENDERBUFFER
, renderbuffer
);
269 checkFramebufferForAllowedStatuses(ALLOW_COMPLETE
);
270 checkBufferBits(gl
.DEPTH_ATTACHMENT
);
271 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.DEPTH_ATTACHMENT
, gl
.RENDERBUFFER
, null);
273 // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer
274 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.DEPTH_STENCIL
, width
, height
);
275 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.DEPTH_STENCIL_ATTACHMENT
, gl
.RENDERBUFFER
, renderbuffer
);
276 checkFramebufferForAllowedStatuses(ALLOW_COMPLETE
);
277 checkBufferBits(gl
.DEPTH_STENCIL_ATTACHMENT
);
280 gl
.deleteRenderbuffer(renderbuffer
);
281 gl
.deleteTexture(texture
);
282 gl
.deleteFramebuffer(fbo
);
285 testFramebufferRequiredCombinations();
287 for (width
= 0; width
<= 2; width
+= 2)
289 for (height
= 0; height
<= 2; height
+= 2)
292 debug("Dimensions " + width
+ " x " + height
);
294 debug("Create renderbuffers");
295 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
296 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
297 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, width
, height
);
298 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
299 shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
300 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthBuffer
);
301 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.DEPTH_COMPONENT16
, width
, height
);
302 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
303 shouldBeNonNull("stencilBuffer = gl.createRenderbuffer()");
304 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, stencilBuffer
);
305 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.STENCIL_INDEX8
, width
, height
);
306 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
307 shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
308 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthStencilBuffer
);
309 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.DEPTH_STENCIL
, width
, height
);
310 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
312 var allowedStatusForGoodCase
313 = (width
== 0 || height
== 0) ? ALLOW_INCOMPLETE_ATTACHMENT
: ALLOW_COMPLETE
;
315 // some cases involving stencil seem to be implementation-dependent
316 var allowedStatusForImplDependentCase
= allowedStatusForGoodCase
| ALLOW_UNSUPPORTED
;
318 debug("Attach depth using DEPTH_ATTACHMENT");
319 testAttachment(gl
.DEPTH_ATTACHMENT
, depthBuffer
, allowedStatusForGoodCase
);
320 debug("Attach depth using STENCIL_ATTACHMENT");
321 testAttachment(gl
.STENCIL_ATTACHMENT
, depthBuffer
, ALLOW_INCOMPLETE_ATTACHMENT
);
322 debug("Attach depth using DEPTH_STENCIL_ATTACHMENT");
323 testAttachment(gl
.DEPTH_STENCIL_ATTACHMENT
, depthBuffer
, ALLOW_INCOMPLETE_ATTACHMENT
);
324 debug("Attach stencil using STENCIL_ATTACHMENT");
325 testAttachment(gl
.STENCIL_ATTACHMENT
, stencilBuffer
, allowedStatusForImplDependentCase
);
326 debug("Attach stencil using DEPTH_ATTACHMENT");
327 testAttachment(gl
.DEPTH_ATTACHMENT
, stencilBuffer
, ALLOW_INCOMPLETE_ATTACHMENT
);
328 debug("Attach stencil using DEPTH_STENCIL_ATTACHMENT");
329 testAttachment(gl
.DEPTH_STENCIL_ATTACHMENT
, stencilBuffer
, ALLOW_INCOMPLETE_ATTACHMENT
);
330 debug("Attach depthStencil using DEPTH_STENCIL_ATTACHMENT");
331 testAttachment(gl
.DEPTH_STENCIL_ATTACHMENT
, depthStencilBuffer
, allowedStatusForGoodCase
);
332 debug("Attach depthStencil using DEPTH_ATTACHMENT");
333 testAttachment(gl
.DEPTH_ATTACHMENT
, depthStencilBuffer
, ALLOW_INCOMPLETE_ATTACHMENT
);
334 debug("Attach depthStencil using STENCIL_ATTACHMENT");
335 testAttachment(gl
.STENCIL_ATTACHMENT
, depthStencilBuffer
, ALLOW_INCOMPLETE_ATTACHMENT
);
337 var allowedStatusForConflictedAttachment
338 = (width
== 0 || height
== 0) ? ALLOW_UNSUPPORTED
| ALLOW_INCOMPLETE_ATTACHMENT
341 debug("Attach depth, then stencil, causing conflict");
342 testAttachments(gl
.DEPTH_ATTACHMENT
, depthBuffer
, gl
.STENCIL_ATTACHMENT
, stencilBuffer
, allowedStatusForConflictedAttachment
);
343 debug("Attach stencil, then depth, causing conflict");
344 testAttachments(gl
.STENCIL_ATTACHMENT
, stencilBuffer
, gl
.DEPTH_ATTACHMENT
, depthBuffer
, allowedStatusForConflictedAttachment
);
345 debug("Attach depth, then depthStencil, causing conflict");
346 testAttachments(gl
.DEPTH_ATTACHMENT
, depthBuffer
, gl
.DEPTH_STENCIL_ATTACHMENT
, depthStencilBuffer
, allowedStatusForConflictedAttachment
);
347 debug("Attach depthStencil, then depth, causing conflict");
348 testAttachments(gl
.DEPTH_STENCIL_ATTACHMENT
, depthStencilBuffer
, gl
.DEPTH_ATTACHMENT
, depthBuffer
, allowedStatusForConflictedAttachment
);
349 debug("Attach stencil, then depthStencil, causing conflict");
350 testAttachments(gl
.DEPTH_ATTACHMENT
, depthBuffer
, gl
.DEPTH_STENCIL_ATTACHMENT
, depthStencilBuffer
, allowedStatusForConflictedAttachment
);
351 debug("Attach depthStencil, then stencil, causing conflict");
352 testAttachments(gl
.DEPTH_STENCIL_ATTACHMENT
, depthStencilBuffer
, gl
.STENCIL_ATTACHMENT
, stencilBuffer
, allowedStatusForConflictedAttachment
);
354 debug("Attach color renderbuffer with internalformat == RGBA4");
355 testColorRenderbuffer(gl
.RGBA4
, allowedStatusForGoodCase
);
357 debug("Attach color renderbuffer with internalformat == RGB5_A1");
358 testColorRenderbuffer(gl
.RGB5_A1
, allowedStatusForGoodCase
);
360 debug("Attach color renderbuffer with internalformat == RGB565");
361 testColorRenderbuffer(gl
.RGB565
, allowedStatusForGoodCase
);
363 debug("Create and attach depthStencil renderbuffer");
364 testDepthStencilRenderbuffer(allowedStatusForGoodCase
);
368 // Determine if we can attach both color and depth or color and depth_stencil
372 function checkValidColorDepthCombination() {
373 shouldBeNonNull("fbo = gl.createFramebuffer()");
374 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
375 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
376 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
377 gl
.framebufferRenderbuffer(
378 gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
379 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 16);
381 shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
382 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthBuffer
);
384 return tryDepth(gl
.DEPTH_COMPONENT16
, gl
.DEPTH_ATTACHMENT
) || tryDepth(gl
.DEPTH_STENCIL
, gl
.DEPTH_STENCIL_ATTACHMENT
);
386 function tryDepth(try_format
, try_attachment
) {
387 if (depthAttachment
) {
388 // If we've tried once unattach the old one.
389 gl
.framebufferRenderbuffer(
390 gl
.FRAMEBUFFER
, depthAttachment
, gl
.RENDERBUFFER
, null);
392 depthFormat
= try_format
;
393 depthAttachment
= try_attachment
;
394 gl
.framebufferRenderbuffer(
395 gl
.FRAMEBUFFER
, depthAttachment
, gl
.RENDERBUFFER
, depthBuffer
);
396 gl
.renderbufferStorage(gl
.RENDERBUFFER
, depthFormat
, 16, 16);
397 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
398 return gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
) == gl
.FRAMEBUFFER_COMPLETE
;
402 if (checkValidColorDepthCombination()) {
403 testFramebufferIncompleteDimensions();
404 testFramebufferIncompleteAttachment();
405 testFramebufferIncompleteMissingAttachment();
406 testUsingIncompleteFramebuffer();
407 testReadingFromMissingAttachment();
408 testBindRenderbufferBeforeFramebufferAttach();
411 function checkFramebuffer(expected
) {
412 var actual
= gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
);
413 var msg
= "gl.checkFramebufferStatus(gl.FRAMEBUFFER) should be " + wtu
.glEnumToString(gl
, expected
) + " was " + wtu
.glEnumToString(gl
, actual
);
414 if (expected
!= gl
.FRAMEBUFFER_COMPLETE
) {
415 msg
+= " or FRAMEBUFFER_UNSUPPORTED";
417 if (actual
== expected
||
418 (expected
!= gl
.FRAMEBUFFER_COMPLETE
&&
419 actual
== gl
.FRAMBUFFER_UNSUPPORTED
)) {
426 function testUsingIncompleteFramebuffer() {
428 debug("Test drawing or reading from an incomplete framebuffer");
429 var program
= wtu
.setupTexturedQuad(gl
);
430 var tex
= gl
.createTexture();
431 wtu
.fillTexture(gl
, tex
, 1, 1, [0,255,0,255]);
433 shouldBeNonNull("fbo = gl.createFramebuffer()");
434 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
435 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
436 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
437 gl
.framebufferRenderbuffer(
438 gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
439 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 16);
441 shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
442 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthBuffer
);
443 gl
.framebufferRenderbuffer(
444 gl
.FRAMEBUFFER
, depthAttachment
, gl
.RENDERBUFFER
, depthBuffer
);
445 gl
.renderbufferStorage(gl
.RENDERBUFFER
, depthFormat
, 16, 16);
446 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
447 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
449 // We pick this combination because it works on desktop OpenGL but should not work on OpenGL ES 2.0
450 gl
.renderbufferStorage(gl
.RENDERBUFFER
, depthFormat
, 32, 16);
451 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_DIMENSIONS
);
453 debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
454 testRenderingAndReading();
456 shouldBeNonNull("fbo2 = gl.createFramebuffer()");
457 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo2
);
458 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
);
460 debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
461 testRenderingAndReading();
463 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
464 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
465 gl
.framebufferRenderbuffer(
466 gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
467 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 0, 0);
469 debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
470 testRenderingAndReading();
472 function testRenderingAndReading() {
473 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
474 wtu
.clearAndDrawUnitQuad(gl
);
475 wtu
.glErrorShouldBe(gl
, gl
.INVALID_FRAMEBUFFER_OPERATION
, "drawArrays with incomplete framebuffer");
476 gl
.readPixels(0, 0, 1, 1, gl
.RGBA
, gl
.UNSIGNED_BYTE
, new Uint8Array(4));
477 wtu
.glErrorShouldBe(gl
, gl
.INVALID_FRAMEBUFFER_OPERATION
, "readPixels from incomplete framebuffer");
478 // copyTexImage and copyTexSubImage can be either INVALID_FRAMEBUFFER_OPERATION because
479 // the framebuffer is invalid OR INVALID_OPERATION because in the case of no attachments
480 // the framebuffer is not of a compatible type.
481 gl
.copyTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, 0, 0, 1, 1);
482 wtu
.glErrorShouldBe(gl
, [gl
.INVALID_FRAMEBUFFER_OPERATION
, gl
.INVALID_OPERATION
], "copyTexImage2D from incomplete framebuffer");
483 gl
.copyTexImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, 0, 0, 1, 1, 0);
484 wtu
.glErrorShouldBe(gl
, [gl
.INVALID_FRAMEBUFFER_OPERATION
, gl
.INVALID_OPERATION
], "copyTexSubImage2D from incomplete framebuffer");
485 gl
.clear(gl
.COLOR_BUFFER_BIT
| gl
.DEPTH_BUFFER_BIT
);
486 wtu
.glErrorShouldBe(gl
, gl
.INVALID_FRAMEBUFFER_OPERATION
, "clear with incomplete framebuffer");
490 function testFramebufferIncompleteAttachment() {
491 shouldBeNonNull("fbo = gl.createFramebuffer()");
492 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
493 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
494 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
495 gl
.framebufferRenderbuffer(
496 gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
497 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 16);
498 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
501 debug("Wrong storage type for type of attachment be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
502 gl
.renderbufferStorage(gl
.RENDERBUFFER
, depthFormat
, 16, 16);
503 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_ATTACHMENT
);
505 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 16);
506 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
509 debug("0 size attachment should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
510 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 0, 0);
511 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_ATTACHMENT
);
513 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
516 function testFramebufferIncompleteMissingAttachment() {
518 debug("No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
519 shouldBeNonNull("fbo = gl.createFramebuffer()");
520 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
521 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
);
523 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
524 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
525 gl
.framebufferRenderbuffer(
526 gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
527 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 16);
528 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
530 gl
.framebufferRenderbuffer(
531 gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, null);
532 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
);
534 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
537 function testFramebufferIncompleteDimensions() {
539 debug("Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5)");
541 shouldBeNonNull("fbo = gl.createFramebuffer()");
542 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
543 shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
544 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
545 gl
.framebufferRenderbuffer(
546 gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, colorBuffer
);
547 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 16);
549 shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
550 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthBuffer
);
551 gl
.framebufferRenderbuffer(
552 gl
.FRAMEBUFFER
, depthAttachment
, gl
.RENDERBUFFER
, depthBuffer
);
553 gl
.renderbufferStorage(gl
.RENDERBUFFER
, depthFormat
, 16, 16);
554 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
555 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
557 gl
.renderbufferStorage(gl
.RENDERBUFFER
, depthFormat
, 32, 16);
558 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_DIMENSIONS
);
559 gl
.renderbufferStorage(gl
.RENDERBUFFER
, depthFormat
, 16, 16);
560 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
561 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, colorBuffer
);
562 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 32);
563 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_DIMENSIONS
);
564 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.RGBA4
, 16, 16);
565 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
566 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
568 var tex
= gl
.createTexture();
569 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
570 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, 16, 16, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
571 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.TEXTURE_2D
, tex
, 0);
572 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
573 if (gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
) != gl
.FRAMEBUFFER_COMPLETE
) {
577 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, 32, 16, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
578 checkFramebuffer(gl
.FRAMEBUFFER_INCOMPLETE_DIMENSIONS
);
579 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, 16, 16, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
580 checkFramebuffer(gl
.FRAMEBUFFER_COMPLETE
);
582 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
);
585 function testReadingFromMissingAttachment() {
587 debug("Test drawing or reading from a missing framebuffer attachment");
589 shouldBeNonNull("fbo = gl.createFramebuffer()");
590 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
594 // The only scenario we can verify is an attempt to read or copy
595 // from a missing color attachment while the framebuffer is still
597 shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
598 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthBuffer
);
599 gl
.framebufferRenderbuffer(
600 gl
.FRAMEBUFFER
, gl
.DEPTH_ATTACHMENT
, gl
.RENDERBUFFER
, depthBuffer
);
601 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.DEPTH_COMPONENT16
, size
, size
);
602 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "After depth renderbuffer setup");
603 if (gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
) != gl
.FRAMEBUFFER_COMPLETE
) {
604 debug("Unable to allocate a framebuffer with just a depth attachment; this is legal");
605 // Try just a depth/stencil renderbuffer
606 gl
.framebufferRenderbuffer(
607 gl
.FRAMEBUFFER
, gl
.DEPTH_ATTACHMENT
, gl
.RENDERBUFFER
, null);
608 shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
609 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, depthStencilBuffer
);
610 gl
.framebufferRenderbuffer(
611 gl
.FRAMEBUFFER
, gl
.DEPTH_STENCIL_ATTACHMENT
, gl
.RENDERBUFFER
, depthStencilBuffer
);
612 gl
.renderbufferStorage(gl
.RENDERBUFFER
, gl
.DEPTH_STENCIL
, size
, size
);
613 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "After depth+stencil renderbuffer setup");
614 if (gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
) != gl
.FRAMEBUFFER_COMPLETE
) {
615 debug("Unable to allocate a framebuffer with just a depth+stencil attachment; this is legal");
620 // The FBO has no color attachment. ReadPixels, CopyTexImage2D,
621 // and CopyTexSubImage2D should all generate INVALID_OPERATION.
622 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Before ReadPixels from missing attachment");
623 gl
.readPixels(0, 0, 1, 1, gl
.RGBA
, gl
.UNSIGNED_BYTE
, new Uint8Array(4));
624 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "After ReadPixels from missing attachment");
626 var tex
= gl
.createTexture();
627 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
628 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Before CopyTexImage2D from missing attachment");
629 gl
.copyTexImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, 0, 0, size
, size
, 0);
630 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "After CopyTexImage2D from missing attachment");
632 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, size
, size
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
633 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Before CopyTexSubImage2D from missing attachment");
634 gl
.copyTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, 0, 0, size
, size
);
635 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "After CopyTexSubImage2D from missing attachment");
638 // [OpenGL ES 2.0.25] Section 4.4.3 page 112
639 // [OpenGL ES 3.0.2] Section 4.4.2 page 201
640 // 'renderbuffer' must be either zero or the name of an existing renderbuffer object of
641 // type 'renderbuffertarget', otherwise an INVALID_OPERATION error is generated.
642 function testBindRenderbufferBeforeFramebufferAttach() {
644 debug("Test calling framebufferRenderbuffer before bindRenderbuffer.");
646 let fbo
= gl
.createFramebuffer();
647 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
649 let attachmentTypes
= [
650 gl
.COLOR_ATTACHMENT0
,
652 gl
.STENCIL_ATTACHMENT
,
653 gl
.DEPTH_STENCIL_ATTACHMENT
656 attachmentTypes
.forEach(function(attachmentType
) {
657 let strAttachmentType
= wtu
.glEnumToString(gl
, attachmentType
);
659 debug("Testing " + strAttachmentType
);
660 let rbo
= gl
.createRenderbuffer();
661 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, attachmentType
, gl
.RENDERBUFFER
, rbo
);
662 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "bindRenderbuffer must be called before attachment to " + strAttachmentType
);
663 shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl." + strAttachmentType
+ ", gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "gl.NONE");
664 gl
.getFramebufferAttachmentParameter(gl
.FRAMEBUFFER
, attachmentType
, gl
.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
);
665 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "Only OBJECT_TYPE can be queried when no image is attached");
666 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, attachmentType
, gl
.RENDERBUFFER
, null);
667 gl
.deleteRenderbuffer(rbo
);
670 gl
.deleteFramebuffer(fbo
);
673 var successfullyParsed
= true;
676 <script src=
"../../js/js-test-post.js"></script>