summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-object-attachment.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-object-attachment.html')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-object-attachment.html678
1 files changed, 678 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-object-attachment.html b/dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-object-attachment.html
new file mode 100644
index 0000000000..b15e1386c7
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-object-attachment.html
@@ -0,0 +1,678 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+"use strict";
+var wtu = WebGLTestUtils;
+var gl;
+var fbo;
+var depthBuffer;
+var stencilBuffer;
+var depthStencilBuffer;
+var colorBuffer;
+var width;
+var height;
+
+var ALLOW_COMPLETE = 0x01;
+var ALLOW_UNSUPPORTED = 0x02;
+var ALLOW_INCOMPLETE_ATTACHMENT = 0x04;
+
+function checkFramebufferForAllowedStatuses(allowedStatuses)
+{
+ // If the framebuffer is in an error state for multiple reasons,
+ // we can't guarantee which one will be reported.
+ var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ var statusAllowed = ((allowedStatuses & ALLOW_COMPLETE) && (status == gl.FRAMEBUFFER_COMPLETE)) ||
+ ((allowedStatuses & ALLOW_UNSUPPORTED) && (status == gl.FRAMEBUFFER_UNSUPPORTED)) ||
+ ((allowedStatuses & ALLOW_INCOMPLETE_ATTACHMENT) && (status == gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT));
+ var msg = "gl.checkFramebufferStatus(gl.FRAMEBUFFER) returned " + status;
+ if (statusAllowed)
+ testPassed(msg);
+ else
+ testFailed(msg);
+}
+
+function checkBufferBits(attachment0, attachment1)
+{
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
+ return;
+
+ var haveDepthBuffer = attachment0 == gl.DEPTH_ATTACHMENT ||
+ attachment0 == gl.DEPTH_STENCIL_ATTACHMENT ||
+ attachment1 == gl.DEPTH_ATTACHMENT ||
+ attachment1 == gl.DEPTH_STENCIL_ATTACHMENT;
+ var haveStencilBuffer = attachment0 == gl.STENCIL_ATTACHMENT ||
+ attachment0 == gl.DEPTH_STENCIL_ATTACHMENT ||
+ attachment1 == gl.STENCIL_ATTACHMENT ||
+ attachment1 == gl.DEPTH_STENCIL_ATTACHMENT;
+
+ shouldBeTrue("gl.getParameter(gl.RED_BITS) + gl.getParameter(gl.GREEN_BITS) + gl.getParameter(gl.BLUE_BITS) + gl.getParameter(gl.ALPHA_BITS) >= 16");
+
+ if (haveDepthBuffer)
+ shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) >= 16");
+ else
+ shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) == 0");
+
+ if (haveStencilBuffer)
+ shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) >= 8");
+ else
+ shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) == 0");
+}
+
+function testAttachment(attachment, buffer, allowedStatuses)
+{
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, buffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebufferForAllowedStatuses(allowedStatuses);
+ if ((allowedStatuses & ALLOW_COMPLETE) == 0) {
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION);
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(width * height * 4));
+ wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION);
+ }
+ checkBufferBits(attachment);
+ gl.deleteFramebuffer(fbo);
+}
+
+function testAttachments(attachment0, buffer0, attachment1, buffer1, allowedStatuses)
+{
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment0, gl.RENDERBUFFER, buffer0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment1, gl.RENDERBUFFER, buffer1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebufferForAllowedStatuses(allowedStatuses);
+ checkBufferBits(attachment0, attachment1);
+ gl.deleteFramebuffer(fbo);
+}
+
+function testColorRenderbuffer(internalformat, allowedStatuses)
+{
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, internalformat, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ testAttachment(gl.COLOR_ATTACHMENT0, colorBuffer, allowedStatuses);
+}
+
+function testDepthStencilRenderbuffer(allowedStatuses)
+{
+ shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ // OpenGL itself doesn't seem to guarantee that e.g. a 2 x 0
+ // renderbuffer will report 2 for its width when queried.
+ if (!(height == 0 && width > 0))
+ shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)", "width");
+ if (!(width == 0 && height > 0))
+ shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)", "height");
+ shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_INTERNAL_FORMAT)", "gl.DEPTH_STENCIL");
+ shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_RED_SIZE)", "0");
+ shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_GREEN_SIZE)", "0");
+ shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_BLUE_SIZE)", "0");
+ shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_ALPHA_SIZE)", "0");
+ // Avoid verifying these for zero-sized renderbuffers for the time
+ // being since it appears that even OpenGL doesn't guarantee them.
+ if (width > 0 && height > 0) {
+ shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_DEPTH_SIZE) > 0");
+ shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_STENCIL_SIZE) > 0");
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatuses);
+ testDepthStencilDepthStencil();
+}
+
+function testDepthStencilDepthStencil()
+{
+ if (!width || !height) {
+ return;
+ }
+
+ var tests = [
+ { firstFormat: gl.DEPTH_COMPONENT16,
+ firstAttach: gl.DEPTH_ATTACHMENT,
+ secondFormat: gl.DEPTH_STENCIL,
+ secondAttach: gl.DEPTH_STENCIL_ATTACHMENT
+ },
+ { firstFormat: gl.DEPTH_STENCIL,
+ firstAttach: gl.DEPTH_STENCIL_ATTACHMENT,
+ secondFormat: gl.DEPTH_COMPONENT16,
+ secondAttach: gl.DEPTH_ATTACHMENT
+ }
+ ];
+ for (var ii = 0; ii < tests.length; ++ii) {
+ var test = tests[ii];
+ for (var jj = 0; jj < 2; ++jj) {
+ var fbo = gl.createFramebuffer();
+ var tex = gl.createTexture();
+ var firstRb = gl.createRenderbuffer();
+
+ debug("");
+ debug("test: " + wtu.glEnumToString(gl, test.firstFormat) + " vs " + wtu.glEnumToString(gl, test.secondFormat) + " with " + (jj ? "unbind" : "delete"));
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ // attach texture as color
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+ // attach first
+ gl.bindRenderbuffer(gl.RENDERBUFFER, firstRb);
+ gl.renderbufferStorage(gl.RENDERBUFFER, test.firstFormat, width, height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, test.firstAttach, gl.RENDERBUFFER, firstRb);
+
+ shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE');
+ gl.enable(gl.DEPTH_TEST);
+ var program = wtu.setupColorQuad(gl);
+ // Test it works
+ wtu.drawUByteColorQuad(gl, [0, 255, 0, 255]);
+ wtu.drawUByteColorQuad(gl, [255, 0, 0, 255]); // should not draw since DEPTH_FUNC == LESS
+ wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255], "should be green");
+
+ var secondRb = gl.createRenderbuffer();
+
+ // attach second
+ gl.bindRenderbuffer(gl.RENDERBUFFER, secondRb);
+ gl.renderbufferStorage(gl.RENDERBUFFER, test.secondFormat, width, height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, test.secondAttach, gl.RENDERBUFFER, secondRb);
+
+ if (jj == 0) {
+ // now delete it
+ debug("test deleting second renderbuffer");
+ gl.deleteRenderbuffer(secondRb);
+ } else {
+ // unbind it
+ debug("test unbinding second renderbuffer");
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, test.secondAttach, gl.RENDERBUFFER, null);
+ }
+
+ // If the first attachment is not restored this may fail
+ shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ // If the first attachment is not restored this may fail.
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ wtu.drawUByteColorQuad(gl, [0, 255, 0, 255]);
+ wtu.drawUByteColorQuad(gl, [255, 0, 0, 255]); // should not draw since DEPTH_FUNC == LESS
+ wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255], "should be green");
+ gl.disable(gl.DEPTH_TEST);
+
+ if (jj == 1) {
+ gl.deleteRenderbuffer(secondRb);
+ }
+
+ gl.deleteRenderbuffer(secondRb);
+ gl.deleteFramebuffer(fbo);
+ }
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+description("Test framebuffer object attachment behaviors");
+
+shouldBeNonNull("gl = wtu.create3DContext()");
+
+function testFramebufferRequiredCombinations() {
+ debug("Checking combinations of framebuffer attachments required to be valid");
+
+ // Per discussion with the OpenGL ES working group, the following framebuffer attachment
+ // combinations are required to work in all WebGL implementations:
+ // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
+ // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbuffer
+ // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer
+
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ var width = 64;
+ var height = 64;
+
+ // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
+ var texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+ checkFramebufferForAllowedStatuses(ALLOW_COMPLETE);
+ checkBufferBits();
+
+ // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbuffer
+ var renderbuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
+ checkFramebufferForAllowedStatuses(ALLOW_COMPLETE);
+ checkBufferBits(gl.DEPTH_ATTACHMENT);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null);
+
+ // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
+ checkFramebufferForAllowedStatuses(ALLOW_COMPLETE);
+ checkBufferBits(gl.DEPTH_STENCIL_ATTACHMENT);
+
+ // Clean up
+ gl.deleteRenderbuffer(renderbuffer);
+ gl.deleteTexture(texture);
+ gl.deleteFramebuffer(fbo);
+}
+
+testFramebufferRequiredCombinations();
+
+for (width = 0; width <= 2; width += 2)
+{
+ for (height = 0; height <= 2; height += 2)
+ {
+ debug("");
+ debug("Dimensions " + width + " x " + height);
+
+ debug("Create renderbuffers");
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ shouldBeNonNull("stencilBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, stencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ var allowedStatusForGoodCase
+ = (width == 0 || height == 0) ? ALLOW_INCOMPLETE_ATTACHMENT : ALLOW_COMPLETE;
+
+ // some cases involving stencil seem to be implementation-dependent
+ var allowedStatusForImplDependentCase = allowedStatusForGoodCase | ALLOW_UNSUPPORTED;
+
+ debug("Attach depth using DEPTH_ATTACHMENT");
+ testAttachment(gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForGoodCase);
+ debug("Attach depth using STENCIL_ATTACHMENT");
+ testAttachment(gl.STENCIL_ATTACHMENT, depthBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
+ debug("Attach depth using DEPTH_STENCIL_ATTACHMENT");
+ testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
+ debug("Attach stencil using STENCIL_ATTACHMENT");
+ testAttachment(gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForImplDependentCase);
+ debug("Attach stencil using DEPTH_ATTACHMENT");
+ testAttachment(gl.DEPTH_ATTACHMENT, stencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
+ debug("Attach stencil using DEPTH_STENCIL_ATTACHMENT");
+ testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, stencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
+ debug("Attach depthStencil using DEPTH_STENCIL_ATTACHMENT");
+ testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForGoodCase);
+ debug("Attach depthStencil using DEPTH_ATTACHMENT");
+ testAttachment(gl.DEPTH_ATTACHMENT, depthStencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
+ debug("Attach depthStencil using STENCIL_ATTACHMENT");
+ testAttachment(gl.STENCIL_ATTACHMENT, depthStencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
+
+ var allowedStatusForConflictedAttachment
+ = (width == 0 || height == 0) ? ALLOW_UNSUPPORTED | ALLOW_INCOMPLETE_ATTACHMENT
+ : ALLOW_UNSUPPORTED;
+
+ debug("Attach depth, then stencil, causing conflict");
+ testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForConflictedAttachment);
+ debug("Attach stencil, then depth, causing conflict");
+ testAttachments(gl.STENCIL_ATTACHMENT, stencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment);
+ debug("Attach depth, then depthStencil, causing conflict");
+ testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForConflictedAttachment);
+ debug("Attach depthStencil, then depth, causing conflict");
+ testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment);
+ debug("Attach stencil, then depthStencil, causing conflict");
+ testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForConflictedAttachment);
+ debug("Attach depthStencil, then stencil, causing conflict");
+ testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForConflictedAttachment);
+
+ debug("Attach color renderbuffer with internalformat == RGBA4");
+ testColorRenderbuffer(gl.RGBA4, allowedStatusForGoodCase);
+
+ debug("Attach color renderbuffer with internalformat == RGB5_A1");
+ testColorRenderbuffer(gl.RGB5_A1, allowedStatusForGoodCase);
+
+ debug("Attach color renderbuffer with internalformat == RGB565");
+ testColorRenderbuffer(gl.RGB565, allowedStatusForGoodCase);
+
+ debug("Create and attach depthStencil renderbuffer");
+ testDepthStencilRenderbuffer(allowedStatusForGoodCase);
+ }
+}
+
+// Determine if we can attach both color and depth or color and depth_stencil
+var depthFormat;
+var depthAttachment;
+
+function checkValidColorDepthCombination() {
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+
+ shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+
+ return tryDepth(gl.DEPTH_COMPONENT16, gl.DEPTH_ATTACHMENT) || tryDepth(gl.DEPTH_STENCIL, gl.DEPTH_STENCIL_ATTACHMENT);
+
+ function tryDepth(try_format, try_attachment) {
+ if (depthAttachment) {
+ // If we've tried once unattach the old one.
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, null);
+ }
+ depthFormat = try_format;
+ depthAttachment = try_attachment;
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ return gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
+ }
+}
+
+if (checkValidColorDepthCombination()) {
+ testFramebufferIncompleteDimensions();
+ testFramebufferIncompleteAttachment();
+ testFramebufferIncompleteMissingAttachment();
+ testUsingIncompleteFramebuffer();
+ testReadingFromMissingAttachment();
+ testBindRenderbufferBeforeFramebufferAttach();
+}
+
+function checkFramebuffer(expected) {
+ var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ var msg = "gl.checkFramebufferStatus(gl.FRAMEBUFFER) should be " + wtu.glEnumToString(gl, expected) + " was " + wtu.glEnumToString(gl, actual);
+ if (expected != gl.FRAMEBUFFER_COMPLETE) {
+ msg += " or FRAMEBUFFER_UNSUPPORTED";
+ }
+ if (actual == expected ||
+ (expected != gl.FRAMEBUFFER_COMPLETE &&
+ actual == gl.FRAMBUFFER_UNSUPPORTED)) {
+ testPassed(msg);
+ } else {
+ testFailed(msg);
+ }
+}
+
+function testUsingIncompleteFramebuffer() {
+ debug("");
+ debug("Test drawing or reading from an incomplete framebuffer");
+ var program = wtu.setupTexturedQuad(gl);
+ var tex = gl.createTexture();
+ wtu.fillTexture(gl, tex, 1, 1, [0,255,0,255]);
+
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+
+ shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+ // We pick this combination because it works on desktop OpenGL but should not work on OpenGL ES 2.0
+ gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+ debug("");
+ debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
+ testRenderingAndReading();
+
+ shouldBeNonNull("fbo2 = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
+ debug("");
+ debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
+ testRenderingAndReading();
+
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
+ debug("");
+ debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
+ testRenderingAndReading();
+
+ function testRenderingAndReading() {
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ wtu.clearAndDrawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "drawArrays with incomplete framebuffer");
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
+ wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "readPixels from incomplete framebuffer");
+ // copyTexImage and copyTexSubImage can be either INVALID_FRAMEBUFFER_OPERATION because
+ // the framebuffer is invalid OR INVALID_OPERATION because in the case of no attachments
+ // the framebuffer is not of a compatible type.
+ gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+ wtu.glErrorShouldBe(gl, [gl.INVALID_FRAMEBUFFER_OPERATION, gl.INVALID_OPERATION], "copyTexImage2D from incomplete framebuffer");
+ gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 1, 1, 0);
+ wtu.glErrorShouldBe(gl, [gl.INVALID_FRAMEBUFFER_OPERATION, gl.INVALID_OPERATION], "copyTexSubImage2D from incomplete framebuffer");
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "clear with incomplete framebuffer");
+ }
+}
+
+function testFramebufferIncompleteAttachment() {
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+ debug("");
+ debug("Wrong storage type for type of attachment be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
+ gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+ debug("");
+ debug("0 size attachment should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+function testFramebufferIncompleteMissingAttachment() {
+ debug("");
+ debug("No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
+
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+function testFramebufferIncompleteDimensions() {
+ debug("");
+ debug("Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5)");
+
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+
+ shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+ gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+ gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 32);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ var tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ return;
+ }
+
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+function testReadingFromMissingAttachment() {
+ debug("");
+ debug("Test drawing or reading from a missing framebuffer attachment");
+
+ shouldBeNonNull("fbo = gl.createFramebuffer()");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ var size = 16;
+
+ // The only scenario we can verify is an attempt to read or copy
+ // from a missing color attachment while the framebuffer is still
+ // complete.
+ shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After depth renderbuffer setup");
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Unable to allocate a framebuffer with just a depth attachment; this is legal");
+ // Try just a depth/stencil renderbuffer
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null);
+ shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After depth+stencil renderbuffer setup");
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Unable to allocate a framebuffer with just a depth+stencil attachment; this is legal");
+ return;
+ }
+ }
+
+ // The FBO has no color attachment. ReadPixels, CopyTexImage2D,
+ // and CopyTexSubImage2D should all generate INVALID_OPERATION.
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Before ReadPixels from missing attachment");
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "After ReadPixels from missing attachment");
+
+ var tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Before CopyTexImage2D from missing attachment");
+ gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, size, size, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "After CopyTexImage2D from missing attachment");
+
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Before CopyTexSubImage2D from missing attachment");
+ gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, size, size);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "After CopyTexSubImage2D from missing attachment");
+}
+
+// [OpenGL ES 2.0.25] Section 4.4.3 page 112
+// [OpenGL ES 3.0.2] Section 4.4.2 page 201
+// 'renderbuffer' must be either zero or the name of an existing renderbuffer object of
+// type 'renderbuffertarget', otherwise an INVALID_OPERATION error is generated.
+function testBindRenderbufferBeforeFramebufferAttach() {
+ debug("");
+ debug("Test calling framebufferRenderbuffer before bindRenderbuffer.");
+
+ let fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ let attachmentTypes = [
+ gl.COLOR_ATTACHMENT0,
+ gl.DEPTH_ATTACHMENT,
+ gl.STENCIL_ATTACHMENT,
+ gl.DEPTH_STENCIL_ATTACHMENT
+ ];
+
+ attachmentTypes.forEach(function(attachmentType) {
+ let strAttachmentType = wtu.glEnumToString(gl, attachmentType);
+ debug("");
+ debug("Testing " + strAttachmentType);
+ let rbo = gl.createRenderbuffer();
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachmentType, gl.RENDERBUFFER, rbo);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bindRenderbuffer must be called before attachment to " + strAttachmentType);
+ shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl." + strAttachmentType + ", gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "gl.NONE");
+ gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachmentType, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Only OBJECT_TYPE can be queried when no image is attached");
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachmentType, gl.RENDERBUFFER, null);
+ gl.deleteRenderbuffer(rbo);
+ });
+
+ gl.deleteFramebuffer(fbo);
+}
+
+var successfullyParsed = true;
+</script>
+
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>