framebuffer-object-attachment.html (32231B)
1 <!-- 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. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 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> 14 </head> 15 <body> 16 <div id="description"></div> 17 <div id="console"></div> 18 19 <script> 20 "use strict"; 21 var wtu = WebGLTestUtils; 22 var gl; 23 var fbo; 24 var depthBuffer; 25 var stencilBuffer; 26 var depthStencilBuffer; 27 var colorBuffer; 28 var width; 29 var height; 30 31 var ALLOW_COMPLETE = 0x01; 32 var ALLOW_UNSUPPORTED = 0x02; 33 var ALLOW_INCOMPLETE_ATTACHMENT = 0x04; 34 35 function checkFramebufferForAllowedStatuses(allowedStatuses) 36 { 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; 44 if (statusAllowed) 45 testPassed(msg); 46 else 47 testFailed(msg); 48 } 49 50 function checkBufferBits(attachment0, attachment1) 51 { 52 if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) 53 return; 54 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; 63 64 shouldBeTrue("gl.getParameter(gl.RED_BITS) + gl.getParameter(gl.GREEN_BITS) + gl.getParameter(gl.BLUE_BITS) + gl.getParameter(gl.ALPHA_BITS) >= 16"); 65 66 if (haveDepthBuffer) 67 shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) >= 16"); 68 else 69 shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) == 0"); 70 71 if (haveStencilBuffer) 72 shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) >= 8"); 73 else 74 shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) == 0"); 75 } 76 77 function testAttachment(attachment, buffer, allowedStatuses) 78 { 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); 90 } 91 checkBufferBits(attachment); 92 gl.deleteFramebuffer(fbo); 93 } 94 95 function testAttachments(attachment0, buffer0, attachment1, buffer1, allowedStatuses) 96 { 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); 107 } 108 109 function testColorRenderbuffer(internalformat, allowedStatuses) 110 { 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); 116 } 117 118 function testDepthStencilRenderbuffer(allowedStatuses) 119 { 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); 124 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"); 141 } 142 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 143 testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatuses); 144 testDepthStencilDepthStencil(); 145 } 146 147 function testDepthStencilDepthStencil() 148 { 149 if (!width || !height) { 150 return; 151 } 152 153 var tests = [ 154 { firstFormat: gl.DEPTH_COMPONENT16, 155 firstAttach: gl.DEPTH_ATTACHMENT, 156 secondFormat: gl.DEPTH_STENCIL, 157 secondAttach: gl.DEPTH_STENCIL_ATTACHMENT 158 }, 159 { firstFormat: gl.DEPTH_STENCIL, 160 firstAttach: gl.DEPTH_STENCIL_ATTACHMENT, 161 secondFormat: gl.DEPTH_COMPONENT16, 162 secondAttach: gl.DEPTH_ATTACHMENT 163 } 164 ]; 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(); 171 172 debug(""); 173 debug("test: " + wtu.glEnumToString(gl, test.firstFormat) + " vs " + wtu.glEnumToString(gl, test.secondFormat) + " with " + (jj ? "unbind" : "delete")); 174 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); 180 181 // attach first 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); 185 186 shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); 187 gl.enable(gl.DEPTH_TEST); 188 var program = wtu.setupColorQuad(gl); 189 // Test it works 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"); 193 194 var secondRb = gl.createRenderbuffer(); 195 196 // attach second 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); 200 201 if (jj == 0) { 202 // now delete it 203 debug("test deleting second renderbuffer"); 204 gl.deleteRenderbuffer(secondRb); 205 } else { 206 // unbind it 207 debug("test unbinding second renderbuffer"); 208 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, test.secondAttach, gl.RENDERBUFFER, null); 209 } 210 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); 214 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); 221 222 if (jj == 1) { 223 gl.deleteRenderbuffer(secondRb); 224 } 225 226 gl.deleteRenderbuffer(secondRb); 227 gl.deleteFramebuffer(fbo); 228 } 229 } 230 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 231 } 232 233 description("Test framebuffer object attachment behaviors"); 234 235 shouldBeNonNull("gl = wtu.create3DContext()"); 236 237 function testFramebufferRequiredCombinations() { 238 debug("Checking combinations of framebuffer attachments required to be valid"); 239 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 245 246 var fbo = gl.createFramebuffer(); 247 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); 248 249 var width = 64; 250 var height = 64; 251 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); 262 checkBufferBits(); 263 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); 272 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); 278 279 // Clean up 280 gl.deleteRenderbuffer(renderbuffer); 281 gl.deleteTexture(texture); 282 gl.deleteFramebuffer(fbo); 283 } 284 285 testFramebufferRequiredCombinations(); 286 287 for (width = 0; width <= 2; width += 2) 288 { 289 for (height = 0; height <= 2; height += 2) 290 { 291 debug(""); 292 debug("Dimensions " + width + " x " + height); 293 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); 311 312 var allowedStatusForGoodCase 313 = (width == 0 || height == 0) ? ALLOW_INCOMPLETE_ATTACHMENT : ALLOW_COMPLETE; 314 315 // some cases involving stencil seem to be implementation-dependent 316 var allowedStatusForImplDependentCase = allowedStatusForGoodCase | ALLOW_UNSUPPORTED; 317 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); 336 337 var allowedStatusForConflictedAttachment 338 = (width == 0 || height == 0) ? ALLOW_UNSUPPORTED | ALLOW_INCOMPLETE_ATTACHMENT 339 : ALLOW_UNSUPPORTED; 340 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); 353 354 debug("Attach color renderbuffer with internalformat == RGBA4"); 355 testColorRenderbuffer(gl.RGBA4, allowedStatusForGoodCase); 356 357 debug("Attach color renderbuffer with internalformat == RGB5_A1"); 358 testColorRenderbuffer(gl.RGB5_A1, allowedStatusForGoodCase); 359 360 debug("Attach color renderbuffer with internalformat == RGB565"); 361 testColorRenderbuffer(gl.RGB565, allowedStatusForGoodCase); 362 363 debug("Create and attach depthStencil renderbuffer"); 364 testDepthStencilRenderbuffer(allowedStatusForGoodCase); 365 } 366 } 367 368 // Determine if we can attach both color and depth or color and depth_stencil 369 var depthFormat; 370 var depthAttachment; 371 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); 380 381 shouldBeNonNull("depthBuffer = gl.createRenderbuffer()"); 382 gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); 383 384 return tryDepth(gl.DEPTH_COMPONENT16, gl.DEPTH_ATTACHMENT) || tryDepth(gl.DEPTH_STENCIL, gl.DEPTH_STENCIL_ATTACHMENT); 385 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); 391 } 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; 399 } 400 } 401 402 if (checkValidColorDepthCombination()) { 403 testFramebufferIncompleteDimensions(); 404 testFramebufferIncompleteAttachment(); 405 testFramebufferIncompleteMissingAttachment(); 406 testUsingIncompleteFramebuffer(); 407 testReadingFromMissingAttachment(); 408 testBindRenderbufferBeforeFramebufferAttach(); 409 } 410 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"; 416 } 417 if (actual == expected || 418 (expected != gl.FRAMEBUFFER_COMPLETE && 419 actual == gl.FRAMBUFFER_UNSUPPORTED)) { 420 testPassed(msg); 421 } else { 422 testFailed(msg); 423 } 424 } 425 426 function testUsingIncompleteFramebuffer() { 427 debug(""); 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]); 432 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); 440 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); 448 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); 452 debug(""); 453 debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION"); 454 testRenderingAndReading(); 455 456 shouldBeNonNull("fbo2 = gl.createFramebuffer()"); 457 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2); 458 checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); 459 debug(""); 460 debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION"); 461 testRenderingAndReading(); 462 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); 468 debug(""); 469 debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION"); 470 testRenderingAndReading(); 471 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"); 487 } 488 } 489 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); 499 500 debug(""); 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); 504 505 gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16); 506 checkFramebuffer(gl.FRAMEBUFFER_COMPLETE); 507 508 debug(""); 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); 512 513 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 514 } 515 516 function testFramebufferIncompleteMissingAttachment() { 517 debug(""); 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); 522 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); 529 530 gl.framebufferRenderbuffer( 531 gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null); 532 checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); 533 534 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 535 } 536 537 function testFramebufferIncompleteDimensions() { 538 debug(""); 539 debug("Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5)"); 540 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); 548 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); 556 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); 567 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) { 574 return; 575 } 576 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); 581 582 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 583 } 584 585 function testReadingFromMissingAttachment() { 586 debug(""); 587 debug("Test drawing or reading from a missing framebuffer attachment"); 588 589 shouldBeNonNull("fbo = gl.createFramebuffer()"); 590 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); 591 592 var size = 16; 593 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 596 // complete. 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"); 616 return; 617 } 618 } 619 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"); 625 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"); 631 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"); 636 } 637 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() { 643 debug(""); 644 debug("Test calling framebufferRenderbuffer before bindRenderbuffer."); 645 646 let fbo = gl.createFramebuffer(); 647 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); 648 649 let attachmentTypes = [ 650 gl.COLOR_ATTACHMENT0, 651 gl.DEPTH_ATTACHMENT, 652 gl.STENCIL_ATTACHMENT, 653 gl.DEPTH_STENCIL_ATTACHMENT 654 ]; 655 656 attachmentTypes.forEach(function(attachmentType) { 657 let strAttachmentType = wtu.glEnumToString(gl, attachmentType); 658 debug(""); 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); 668 }); 669 670 gl.deleteFramebuffer(fbo); 671 } 672 673 var successfullyParsed = true; 674 </script> 675 676 <script src="../../js/js-test-post.js"></script> 677 </body> 678 </html>