summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/00_test_list.txt10
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html508
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-test.html288
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html144
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/invalidate-framebuffer.html230
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-draws-between-blits.html207
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-with-full-sample-counts.html98
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-depth-renderbuffer-initialization.html180
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-renderbuffer-initialization.html149
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-stencil-renderbuffer-initialization.html167
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/readbuffer.html174
11 files changed, 2155 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/00_test_list.txt
new file mode 100644
index 0000000000..c06bbe3770
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/00_test_list.txt
@@ -0,0 +1,10 @@
+framebuffer-object-attachment.html
+framebuffer-test.html
+framebuffer-texture-layer.html
+invalidate-framebuffer.html
+multisampled-renderbuffer-initialization.html
+--min-version 2.0.1 multisampled-depth-renderbuffer-initialization.html
+--min-version 2.0.1 multisampled-stencil-renderbuffer-initialization.html
+--min-version 2.0.1 multisample-with-full-sample-counts.html
+--min-version 2.0.1 multisample-draws-between-blits.html
+readbuffer.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html
new file mode 100644
index 0000000000..754ff2cc73
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html
@@ -0,0 +1,508 @@
+<!--
+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;
+
+function checkFramebuffer(expected) {
+ var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ if (expected.indexOf(actual) < 0) {
+ var msg = "checkFramebufferStatus expects [";
+ for (var index = 0; index < expected.length; ++index) {
+ msg += wtu.glEnumToString(gl, expected[index]);
+ if (index + 1 < expected.length)
+ msg += ", ";
+ }
+ msg += "], was " + wtu.glEnumToString(gl, actual);
+ testFailed(msg);
+ } else {
+ var msg = "checkFramebufferStatus got " + wtu.glEnumToString(gl, actual) +
+ " as expected";
+ testPassed(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 testFramebufferWebGL1RequiredCombinations() {
+ debug("Checking combinations of framebuffer attachments required to be valid by WebGL 1");
+
+ // Per discussion with the OpenGL ES working group, the following framebuffer attachment
+ // combinations are required to work in all WebGL 1 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);
+ checkFramebuffer([gl.FRAMEBUFFER_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);
+ checkFramebuffer([gl.FRAMEBUFFER_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);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits(gl.DEPTH_STENCIL_ATTACHMENT);
+
+ // Clean up
+ gl.deleteRenderbuffer(renderbuffer);
+ gl.deleteTexture(texture);
+ gl.deleteFramebuffer(fbo);
+}
+
+function testDepthStencilAttachmentBehaviors() {
+ debug("");
+ debug("Checking ES3 DEPTH_STENCIL_ATTACHMENT behaviors are implemented for WebGL 2");
+ // DEPTH_STENCIL_ATTACHMENT is treated as an independent attachment point in WebGL 1;
+ // however, in WebGL 2, it is treated as an alias for DEPTH_ATTACHMENT + STENCIL_ATTACHMENT.
+ var size = 16;
+
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ var colorBuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, size, size);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+
+ var depthBuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
+
+ var stencilBuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, stencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, size, size);
+
+ var depthStencilBuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ debug("");
+ debug("color + depth");
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits(gl.DEPTH_ATTACHMENT);
+
+ debug("");
+ debug("color + depth + stencil: depth != stencil");
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", stencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ checkFramebuffer([gl.FRAMEBUFFER_UNSUPPORTED]);
+
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+
+ debug("");
+ debug("color + depth: DEPTH_STENCIL for DEPTH_ATTACHMENT");
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits(gl.DEPTH_ATTACHMENT);
+
+ debug("");
+ debug("color + depth + stencil: DEPTH_STENCIL for DEPTH_ATTACHMENT and STENCIL_ATTACHMENT");
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits(gl.DEPTH_STENCIL_ATTACHMENT);
+
+ debug("");
+ debug("color + depth_stencil");
+ var texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH24_STENCIL8, size, size, 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, texture, 0);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", texture);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", texture);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", texture);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, null, 0);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits(gl.DEPTH_STENCIL_ATTACHMENT);
+
+ debug("");
+ debug("DEPTH_STENCIL_ATTACHMENT overwrites DEPTH_ATTACHMENT/STENCIL_ATTACHMENT")
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits();
+
+ debug("");
+ debug("STENCIL_ATTACHMENT overwrites stencil set by DEPTH_STENCIL_ATTACHMENT")
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits(gl.DEPTH_ATTACHMENT);
+}
+
+function testFramebufferIncompleteAttachment() {
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ var 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 should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 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");
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT]);
+
+ gl.deleteRenderbuffer(colorBuffer);
+ gl.deleteFramebuffer(fbo);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+function testFramebufferIncompleteMissingAttachment() {
+ debug("");
+ debug("No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT");
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT]);
+
+ var 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]);
+
+ gl.deleteRenderbuffer(colorBuffer);
+ gl.deleteFramebuffer(fbo);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+function testFramebufferWithImagesOfDifferentSizes() {
+ debug("");
+ debug("Attachments of different sizes should NOT be allowed");
+
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ var 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);
+
+ var 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, 16, 16);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 32, 16);
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS]);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 32);
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS]);
+ 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) {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS]);
+ }
+
+ gl.deleteTexture(tex);
+ gl.deleteRenderbuffer(depthBuffer);
+ gl.deleteRenderbuffer(colorBuffer);
+ gl.deleteFramebuffer(fbo);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+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]);
+
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT]);
+ debug("");
+ debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
+ testRenderingAndReading();
+
+ var 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);
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT]);
+ 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");
+ }
+
+ gl.deleteRenderbuffer(colorBuffer);
+ gl.deleteFramebuffer(fbo);
+ gl.deleteTexture(tex);
+ gl.deleteProgram(program);
+}
+
+function testReadingFromMissingAttachment() {
+ debug("");
+ debug("Test drawing or reading from a framebuffer with no color image");
+
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ var object_type = gl.getFramebufferAttachmentParameter(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+ if (object_type != gl.NONE)
+ testFailed("object type from empty attachment point should be NONE");
+ else
+ testPassed("object type from empty attachment point is NONE");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query should not generate error");
+
+ var object_name = gl.getFramebufferAttachmentParameter(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
+ if (object_name)
+ testFailed("object name from empty attachment point should be null");
+ else
+ testPassed("object name from empty attachment point is null");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query should not generate error");
+
+ 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.
+ var 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) {
+ // 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");
+
+ gl.deleteTexture(tex);
+ }
+
+ gl.deleteRenderbuffer(depthBuffer);
+ gl.deleteFramebuffer(fbo);
+}
+
+description("Test framebuffer object attachment behaviors");
+
+shouldBeNonNull("gl = wtu.create3DContext(undefined, undefined, 2)");
+
+testFramebufferWebGL1RequiredCombinations();
+testDepthStencilAttachmentBehaviors();
+testFramebufferIncompleteAttachment();
+testFramebufferIncompleteMissingAttachment();
+testFramebufferWithImagesOfDifferentSizes();
+testUsingIncompleteFramebuffer();
+testReadingFromMissingAttachment();
+
+// -
+
+debug("");
+debug("Test calling framebufferTexture2D with impossible mip levels.");
+
+const fb = gl.createFramebuffer();
+gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+
+const tex = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, tex);
+
+gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 1000);
+wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Mip level attachment impossibly high.");
+gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 10);
+wtu.glErrorShouldBe(gl, 0, "Mip level attachment within acceptable range.");
+
+// Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1636517 :
+gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 1000);
+wtu.glErrorShouldBe(gl, 0, "Mip level detachment can be impossibly high.");
+shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "0");
+
+// -
+
+debug("")
+var successfullyParsed = true;
+</script>
+
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-test.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-test.html
new file mode 100644
index 0000000000..5aa6c91847
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-test.html
@@ -0,0 +1,288 @@
+<!--
+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">
+<title>WebGL Framebuffer Test Against WebGL 2</title>
+<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>
+<canvas id="canvas" width="2" height="2"> </canvas>
+<script>
+"use strict";
+var wtu = WebGLTestUtils;
+var gl;
+
+function testFramebufferRenderbuffer() {
+ debug("");
+ debug("Checking framebuffer/renderbuffer stuff.");
+
+ gl.getFramebufferAttachmentParameter(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM,
+ "getFramebufferAttachmentParameter(COLOR_ATTACHMENT0) on the default framebuffer.");
+ gl.getFramebufferAttachmentParameter(
+ gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "getFramebufferAttachmentParameter(BACK) on the default framebuffer.");
+ gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "checkFramebufferStatus on the default framebuffer.");
+
+ var tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ 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, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "attach a texture to default framebuffer.");
+
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "detach default renderbuffer from default framebuffer.");
+
+ var rb = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, canvas.width, canvas.height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "allocate renderbuffer storage of a newly created renderbuffer.");
+
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "attach a renderbuffer to the default framebuffer.");
+
+ var fbtex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, fbtex);
+ gl.texImage2D(
+ gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ var fb = gl.createFramebuffer();
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bind a newly created framebuffer.");
+
+ var target = gl.READ_FRAMEBUFFER;
+ gl.getFramebufferAttachmentParameter(
+ target, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getFramebufferAttachmentParameter(READ_FRAMEBUFFER).");
+ assertMsg(gl.checkFramebufferStatus(target) != 0,
+ "checkFramebufferStatus(READ_FRAMEBUFFER) should succeed.");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "checkFramebufferStatus(READ_FRAMEBUFFER).");
+ var readFB = gl.createFramebuffer();
+ gl.bindFramebuffer(target, readFB);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bindFramebuffer(READ_FRAMEBUFFER).");
+ assertMsg(readFB == gl.getParameter(gl.READ_FRAMEBUFFER_BINDING),
+ "bindFramebuffer(READ_FRAMEBUFFER) should change READ_FRAMEBUFFER_BINDING.");
+ assertMsg(fb == gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING),
+ "bindFramebuffer(READ_FRAMEBUFFER) should not change DRAW_FRAMEBUFFER_BINDING.");
+ gl.getFramebufferAttachmentParameter(
+ target, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) with no attachment.");
+ gl.framebufferTexture2D(target, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "framebufferTexImage2D(READ_FRAMEBUFFER).");
+ gl.framebufferRenderbuffer(target, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "framebufferRenderbuffer(READ_FRAMEBUFFER).");
+
+ var colorAttachmentsNum = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS);
+ if (colorAttachmentsNum >= 2) {
+ var attachment = gl.COLOR_ATTACHMENT1;
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, fbtex, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "framebufferTexImage2D(COLOR_ATTACHMENT1).");
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, null, 0);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, rb);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "framebufferRenderbuffer(COLOR_ATTACHMENT1).");
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, null);
+ }
+
+ gl.getFramebufferAttachmentParameter(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "with no attachment.");
+
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "attach a texture to a framebuffer.");
+
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "detach a texture from a framebuffer.");
+
+ function numLevelsFromSize(size) {
+ var levels = 0;
+ while ((size >> levels) > 0) {
+ ++levels;
+ }
+ return levels;
+ }
+
+ var maxTexSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
+ var maxLevels = numLevelsFromSize(maxTexSize);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, maxLevels - 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "framebufferTexture2D with an appropriate mipmap level.");
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, maxLevels);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "framebufferTexture2D with a mipmap level out of range.");
+
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "attach a renderbuffer to a framebuffer.");
+
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "detach a renderbuffer from a framebuffer.");
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bind default (null) framebuffer.");
+
+ // attach/detach a 2d texture to one framebuffer binding point,
+ // while no attachment to the other binding point.
+ function attachAndDetachTexture(targetA, targetB) {
+ gl.framebufferTexture2D(targetA, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "attach a texture to read/draw framebuffer binding point.");
+ gl.getFramebufferAttachmentParameter(
+ targetA, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "on read/draw framebuffer.");
+ gl.getFramebufferAttachmentParameter(
+ targetB, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "on read/draw framebuffer with no attachment.");
+ gl.framebufferTexture2D(targetA, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "detach a texture from read/draw framebuffer.");
+ }
+
+ var readFBWithTexture = gl.createFramebuffer();
+ var drawFBWithTexture = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFBWithTexture);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawFBWithTexture);
+ attachAndDetachTexture(gl.READ_FRAMEBUFFER, gl.DRAW_FRAMEBUFFER);
+ attachAndDetachTexture(gl.DRAW_FRAMEBUFFER, gl.READ_FRAMEBUFFER);
+
+ // attach different textures as color attachment to read and draw framebuffer respectively,
+ // then detach these attachments.
+ var fbtex1 = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, fbtex1);
+ gl.texImage2D(
+ gl.TEXTURE_2D, 0, gl.RG8, canvas.width, canvas.height, 0, gl.RG, gl.UNSIGNED_BYTE, null);
+ gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex1, 0);
+ gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, 0);
+ shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.READ_FRAMEBUFFER, ' +
+ 'gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE)');
+ shouldBe('gl.getFramebufferAttachmentParameter(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ' +
+ 'gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE)', '0');
+
+ gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
+ gl.getFramebufferAttachmentParameter(
+ gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "on read framebuffer with no attachment.");
+ gl.getFramebufferAttachmentParameter(
+ gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "on draw framebuffer.");
+ gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
+ gl.getFramebufferAttachmentParameter(
+ gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "on draw framebuffer with no attachment.");
+
+ // attach/detach a renderbuffer to one framebuffer binding point,
+ // while no attachment to the other binding point.
+ function attachAndDetachRenderbuffer(targetA, targetB) {
+ gl.framebufferRenderbuffer(targetA, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "attaching a renderbuffer to a read/draw framebuffer.");
+ gl.getFramebufferAttachmentParameter(
+ targetA, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "on read/draw framebuffer.");
+ gl.getFramebufferAttachmentParameter(
+ targetB, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) " +
+ "on read/draw framebuffer with no attachment.");
+ gl.framebufferRenderbuffer(targetA, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "detach a renderbuffer from a read/draw framebuffer.");
+ }
+
+ var readFBWithRenderbuffer = gl.createFramebuffer();
+ var drawFBWithRenderbuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFBWithRenderbuffer);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawFBWithRenderbuffer);
+ attachAndDetachRenderbuffer(gl.READ_FRAMEBUFFER, gl.DRAW_FRAMEBUFFER);
+ attachAndDetachRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.READ_FRAMEBUFFER);
+
+ // attach different renderbuffers to read and draw framebuffer respectively,
+ // then detach these attachments.
+ var depthRB = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthRB);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, canvas.width, canvas.height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "allocating renderbuffer storage of a newly created renderbuffer.");
+ gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRB);
+ gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
+ shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.READ_FRAMEBUFFER, ' +
+ 'gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_RED_SIZE)');
+ gl.getFramebufferAttachmentParameter(
+ gl.READ_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_DEPTH_SIZE);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE) " +
+ "on read framebuffer without depth attachment.");
+ shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.DRAW_FRAMEBUFFER, ' +
+ 'gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE)');
+ gl.getFramebufferAttachmentParameter(
+ gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_RED_SIZE);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_RED_SIZE) " +
+ "on draw framebuffer without color attachment.");
+
+ gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
+ gl.getFramebufferAttachmentParameter(
+ gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_RED_SIZE);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_RED_SIZE) " +
+ "on read framebuffer with no attachment.");
+ shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.DRAW_FRAMEBUFFER, ' +
+ 'gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE)');
+ gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null);
+ gl.getFramebufferAttachmentParameter(
+ gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_DEPTH_SIZE);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "getFramebufferAttachmentParameter(FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE) " +
+ "on draw framebuffer with no attachment.");
+
+ // binding read/draw framebuffer to default framebuffer
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bind read framebuffer to default (null) framebuffer.");
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bind draw framebuffer to default (null) framebuffer.");
+}
+
+description("This tests framebuffer/renderbuffer-related functions");
+
+var canvas = document.getElementById("canvas");
+shouldBeNonNull("gl = wtu.create3DContext(canvas, undefined, 2)");
+
+testFramebufferRenderbuffer();
+
+debug("");
+var successfullyParsed = true;
+
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html
new file mode 100644
index 0000000000..0e435d6a2e
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html
@@ -0,0 +1,144 @@
+<!--
+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">
+<title>WebGL FramebufferTextureLayer Test</title>
+<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>
+<canvas id="canvas" width="2" height="2"> </canvas>
+
+<script>
+"use strict";
+var wtu = WebGLTestUtils;
+var gl;
+var canvas = document.getElementById("canvas");
+
+function numLevelsFromSize(size) {
+ var levels = 0;
+ while ((size >> levels) > 0) {
+ ++levels;
+ }
+ return levels;
+}
+
+function checkFramebuffer(expected) {
+ var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ if (expected.indexOf(actual) < 0) {
+ var msg = "checkFramebufferStatus expects [";
+ for (var index = 0; index < expected.length; ++index) {
+ msg += wtu.glEnumToString(gl, expected[index]);
+ if (index + 1 < expected.length)
+ msg += ", ";
+ }
+ msg += "], was " + wtu.glEnumToString(gl, actual);
+ testFailed(msg);
+ } else {
+ var msg = "checkFramebufferStatus got " + wtu.glEnumToString(gl, actual) +
+ " as expected";
+ testPassed(msg);
+ }
+}
+
+function testFramebufferTextureLayer() {
+ debug("");
+ debug("Checking FramebufferTextureLayer stuff.");
+
+ var tex3d = gl.createTexture();
+ var fb = gl.createFramebuffer();
+ gl.bindTexture(gl.TEXTURE_3D, tex3d);
+ gl.texImage3D(gl.TEXTURE_3D,
+ 0, // level
+ gl.RGBA, // internalFormat
+ 1, // width
+ 1, // height
+ 1, // depth
+ 0, // border
+ gl.RGBA, // format
+ gl.UNSIGNED_BYTE, // type
+ new Uint8Array([0xff, 0x00, 0x00, 0x00])); // data
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3d, 0, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "attaching a texture to default framebuffer should generate INVALID_OPERATION.");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3d, 0, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "attaching a texture to a framebuffer should succeed.");
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, null, 0, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "detaching a texture from a framebuffer should succeed.");
+
+ var maxTexSize = gl.getParameter(gl.MAX_3D_TEXTURE_SIZE);
+ var maxLevels = numLevelsFromSize(maxTexSize);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3d, maxLevels - 1, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling framebufferTextureLayer with an appropriate mipmap level should succeed.");
+ checkFramebuffer([gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT]);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3d, maxLevels, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
+ "calling framebufferTextureLayer with a mipmap level out of range should generate INVALID_VALUE.");
+
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3d, 0, -1);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
+ "calling framebufferTextureLayer with a negative texture layer should generate INVALID_VALUE.");
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3d, 0, maxTexSize);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
+ "calling framebufferTextureLayer with a texture layer out of range should generate INVALID_VALUE.");
+
+ var tex2d = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex2d);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex2d, 0, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "attaching a 2d texture to a framebuffer should generate INVALID_OPERATION.");
+
+ var texDepthStencil = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, texDepthStencil);
+ var fbDepthStencil = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbDepthStencil);
+ gl.texImage3D(gl.TEXTURE_2D_ARRAY,
+ 0, // level
+ gl.DEPTH24_STENCIL8, // internalFormat
+ 1, // width
+ 1, // height
+ 1, // depth
+ 0, // border
+ gl.DEPTH_STENCIL, // format
+ gl.UNSIGNED_INT_24_8, // type
+ new Uint32Array([0])); // data
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, texDepthStencil, 0, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "attaching a depth_stencil texture to a framebuffer should succeed.");
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+
+ // Clean up
+ gl.deleteTexture(tex3d);
+ gl.deleteTexture(texDepthStencil);
+ gl.deleteTexture(tex2d);
+ gl.deleteFramebuffer(fb);
+ gl.deleteFramebuffer(fbDepthStencil);
+}
+
+description("This tests framebufferTextureLayer.");
+
+shouldBeNonNull("gl = wtu.create3DContext(undefined, undefined, 2)");
+
+testFramebufferTextureLayer();
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/invalidate-framebuffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/invalidate-framebuffer.html
new file mode 100644
index 0000000000..426148cf5e
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/invalidate-framebuffer.html
@@ -0,0 +1,230 @@
+<!--
+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">
+<title>Invalidate Framebuffer Against WebGL 2</title>
+<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>
+<canvas id="canvas" width="20" height="20"> </canvas>
+<script>
+"use strict";
+description("This tests invalidateFramebuffer and invalidateSubFramebuffer");
+
+debug("");
+debug("Canvas.getContext");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, { depth : true, stencil : false }, 2);
+if (!gl) {
+ testFailed("context does not exist");
+} else {
+ testPassed("context exists");
+
+ debug("");
+ debug("invalidate framebuffer.");
+
+ gl.clearColor(0, 0, 0, 0);
+
+ // setup framebuffer with depth attachment and multi-sampled color attachment
+ var fb_m = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb_m);
+
+ var rb_m = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rb_m);
+ var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb_m);
+ // invalidate the framebuffer when the attachment is incomplete: no storage allocated to the attached renderbuffer
+ invalidateIncompleteAttachment(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl.RGBA8, canvas.width, canvas.height);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no errors after attaching a multi-sampled renderbuffer to fbo.");
+
+ var rb = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb);
+ // invalidate the framebuffer when the attachment is incomplete: no storage allocated to the attached renderbuffer
+ invalidateIncompleteAttachment(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl.DEPTH_COMPONENT16, canvas.width, canvas.height);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no errors after attaching a renderbuffer to fbo.");
+
+ // in real world case, after some drawing, we can invalidate the depth attachment of the bound fbo
+ invalidation(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.STENCIL_ATTACHMENT);
+
+ // set up framebuffer to blit to and read back from
+ var fb = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ var buffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, canvas.width, canvas.height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, buffer);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no errors after attaching a renderbuffer to fbo.");
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb_m);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb);
+ gl.blitFramebuffer(0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no errors after bliting framebuffer.");
+
+ // invalidate the multi-sampled color attachment of the bound read framebuffer after blitFramebuffer.
+ invalidation(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.DEPTH_ATTACHMENT);
+
+ var maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS);
+ gl.invalidateSubFramebuffer(gl.READ_FRAMEBUFFER, [gl.COLOR_ATTACHMENT0 + maxColorAttachments], 5, 5, 10, 10);
+ wtu.glErrorShouldBe(gl, [gl.INVALID_OPERATION, gl.INVALID_ENUM],
+ "calling invalidateSubFramebuffer to invalidate a COLOR_ATTACHMENT that exceeds MAX_COLOR_ATTACHMENT should generate INVALID_ENUM or INVALID_OPERATION.");
+ gl.invalidateFramebuffer(gl.READ_FRAMEBUFFER, [gl.COLOR_ATTACHMENT0 + maxColorAttachments]);
+ wtu.glErrorShouldBe(gl, [gl.INVALID_OPERATION, gl.INVALID_ENUM],
+ "calling invalidateFramebuffer to invalidate a COLOR_ATTACHMENT that exceeds MAX_COLOR_ATTACHMENT should generate INVALID_ENUM or INVALID_OPERATION.");
+
+ // invalidate the default framebuffer
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ invalidation(gl.FRAMEBUFFER, gl.DEPTH, gl.STENCIL);
+
+ gl.deleteFramebuffer(fb_m);
+ gl.deleteRenderbuffer(rb_m);
+ gl.deleteRenderbuffer(rb);
+ gl.deleteFramebuffer(fb);
+ gl.deleteRenderbuffer(buffer);
+
+ testInvalidateRGBThenDraw();
+ testInvalidateWithBlend();
+}
+
+function invalidation(target, valid_attachment, invalid_attachment) {
+ gl.invalidateSubFramebuffer(target, [invalid_attachment], 5, 5, 10, 10);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateSubFramebuffer to invalidate a specified attachment that does not exist will be ignored. There should be no errors.");
+ gl.invalidateSubFramebuffer(target, [valid_attachment], 5, 5, 10, 10);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateSubFramebuffer should succeed.");
+
+ gl.invalidateSubFramebuffer(target, [valid_attachment], 5, 5, -5, -5);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
+ "calling invalidateSubFramebuffer should generate INVALID_VALUE if width < 0 or height < 0.");
+
+ gl.invalidateSubFramebuffer(target, [valid_attachment], -5, -5, 10, 10);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateSubFramebuffer should succeed, even the invalidated pixels may be outside of the framebuffer allocated to current context. These pixels are ignored.");
+ gl.invalidateSubFramebuffer(target, [valid_attachment], 5, 5, 20, 20);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateSubFramebuffer should succeed, even the invalidated pixels may be outside of the framebuffer allocated to current context. These pixels are ignored.");
+
+ gl.invalidateFramebuffer(target, [invalid_attachment]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateFramebuffer to invalidate a specified attachment that does not exist will be ignored. There should be no errors.");
+ gl.invalidateFramebuffer(target, [valid_attachment]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateFramebuffer should succeed.");
+}
+
+function invalidateIncompleteAttachment(target, incomplete_attachment) {
+ shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)",
+ "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
+ gl.invalidateSubFramebuffer(target, [incomplete_attachment], 5, 5, 10, 10);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateSubFramebuffer to invalidate an incomplete attachment will be ignored. There should be no errors");
+ gl.invalidateFramebuffer(target, [incomplete_attachment]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateFramebuffer to invalidate an incomplete attachment will be ignored. There should be no errors.");
+}
+
+function testInvalidateRGBThenDraw() {
+ const program = wtu.setupColorQuad(gl);
+
+ const texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Verify that clearing alpha is ineffective on an RGB format.
+ const black = [0, 0, 0, 255];
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, black, `should be black`);
+
+ // Invalidate the framebuffer contents.
+ gl.invalidateFramebuffer(gl.FRAMEBUFFER, [gl.COLOR_ATTACHMENT0]);
+
+ // Without an explicit clear, draw blue and make sure alpha is unaffected. If RGB is emulated
+ // with RGBA, the previous invalidate shouldn't affect the alpha value.
+ wtu.drawFloatColorQuad(gl, [0, 0, 1, 1]);
+ const blue = [0, 0, 255, 255];
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, blue, `should be blue`);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ gl.deleteTexture(texture);
+ gl.deleteFramebuffer(fbo);
+ gl.deleteProgram(program);
+}
+
+function testInvalidateWithBlend() {
+ // Create the framebuffer that will be invalidated
+ const renderTarget = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, renderTarget);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+
+ const drawFBO = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawFBO);
+ gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, renderTarget, 0);
+
+ // Clear the framebuffer and invalidate it.
+ gl.clearColor(1, 1, 1, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ gl.invalidateFramebuffer(gl.DRAW_FRAMEBUFFER, [gl.COLOR_ATTACHMENT0]);
+
+ // Upload data to it
+ const cyan = new Uint8Array([0, 255, 255, 255]);
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, cyan);
+
+ // Blend into the framebuffer, then verify that the framebuffer should have had cyan.
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, drawFBO);
+
+ const program = wtu.setupColorQuad(gl);
+
+ gl.enable(gl.BLEND);
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+ wtu.drawFloatColorQuad(gl, [1, 0, 0, 0.5]);
+ const expected = [127, 127, 127, 191];
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, expected, `should be ${expected}`, 1);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling invalidateFramebuffer and then uploading data to texture in framebuffer. There should be no errors");
+
+ gl.deleteTexture(renderTarget);
+ gl.deleteFramebuffer(drawFBO);
+ gl.deleteProgram(program);
+ gl.disable(gl.BLEND);
+}
+
+debug("");
+var successfullyParsed = true;
+
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-draws-between-blits.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-draws-between-blits.html
new file mode 100644
index 0000000000..bc4ea25a95
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-draws-between-blits.html
@@ -0,0 +1,207 @@
+<!--
+Copyright (c) 2022 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>
+<canvas id="canvas" width="128" height="64" style="width: 32px; height: 32px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+const wtu = WebGLTestUtils;
+description(' Test multisample with blitting between draws');
+
+const gl = wtu.create3DContext("canvas", null, 2);
+const w = 128;
+const h = 64;
+
+if (!gl) {
+ testFailed('canvas.getContext() failed');
+} else {
+ gl.viewport(0, 0, w, h);
+ runTest(gl, 4);
+}
+
+function runTest(gl, sampleCount) {
+ const vs = `#version 300 es
+
+ layout(location = 0) in vec4 position;
+ uniform mat4 mat;
+
+ void main() {
+ gl_Position = mat * position;
+ }
+ `;
+
+ const fs = `#version 300 es
+ precision mediump float;
+ uniform vec4 color;
+ out vec4 outColor;
+ void main() {
+ outColor = color;
+ }
+ `;
+
+ const texVS = `#version 300 es
+
+ layout(location = 0) in vec4 position;
+ out vec2 texcoord;
+ uniform mat4 mat;
+
+ void main() {
+ gl_Position = mat * position;
+ texcoord = position.xy;
+ }
+ `;
+
+ const texFS = `#version 300 es
+ precision mediump float;
+ in vec2 texcoord;
+ uniform sampler2D tex;
+ out vec4 outColor;
+ void main() {
+ outColor = texture(tex, texcoord);
+ }
+ `;
+
+ const msRB = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, msRB);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, w, h);
+
+ const msFB = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, msFB);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, msRB);
+
+ const dTex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, dTex);
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, w, h);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+
+ const dFB = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dFB);
+ gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, dTex, 0);
+
+ const positionLoc = 0; // hard coded in shaders so they match
+ const buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+ 0, 0,
+ 1, 0,
+ 0, 1,
+ 0, 1,
+ 1, 0,
+ 1, 1,
+ ]), gl.STATIC_DRAW);
+ gl.enableVertexAttribArray(positionLoc);
+ gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
+
+ const program = wtu.setupProgram(gl, [vs, fs]);
+ const texProgram = wtu.setupProgram(gl, [texVS, texFS]);
+
+ const colorLoc = gl.getUniformLocation(program, 'color');
+ const matLoc = gl.getUniformLocation(program, 'mat');
+ const texMatLoc = gl.getUniformLocation(texProgram, 'mat');
+
+ gl.useProgram(program);
+
+ const drawAndResolve = (color, mat) => {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, msFB);
+ gl.uniform4fv(colorLoc, color);
+ gl.uniformMatrix4fv(matLoc, false, mat);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dFB);
+ gl.blitFramebuffer(0, 0, w, h, 0, 0, w, h, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+ };
+
+ const check = (x, y, w, h, expected, msg) => {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, dFB);
+ const tolerance = 2; // For multisampling resolution differences between GPUs
+ wtu.checkCanvasRect(gl, x, y, w, h, expected, msg, tolerance);
+ };
+
+ const f32Red = [1, 0, 0, 1];
+ const f32Green = [0, 1, 0, 1];
+ const f32Gray = [0.5, 0.5, 0.5, 1];
+
+ const u8Red = [255, 0, 0, 255];
+ const u8Green = [ 0, 255, 0, 255];
+ const u8LightRed = [255, 128, 128, 255];
+ const u8LightGreen = [128, 255, 128, 255];
+
+ debug('fill with red');
+ drawAndResolve(f32Red, [
+ 2, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 1, 0,
+ -1, -1, 0, 1,
+ ]);
+ check(0, 0, w, h, u8Red, 'whole thing');
+
+ debug('draw right in green');
+ drawAndResolve(f32Green, [
+ 1, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 1, 0,
+ 0, -1, 0, 1,
+ ]);
+ check(0, 0, w / 2, h, u8Red, 'left');
+ check(w / 2, 0, w / 2, h, u8Green, 'right');
+
+ debug('draw middle in gray with blending');
+ gl.enable(gl.BLEND);
+ gl.blendFunc(gl.ONE, gl.ONE);
+ drawAndResolve(f32Gray, [
+ 1, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 1, 0,
+ -0.5, -1, 0, 1,
+ ]);
+ gl.disable(gl.BLEND);
+
+ /*
+ expected
+ +-----+-------+---------+--------+
+ | red | ltRed | ltGreen | green |
+ +-----+-------+---------+--------+
+ 0,0
+ */
+
+ check(0, 0, w / 4, h , u8Red, 'left edge')
+ check(w * 3 / 4, 0, w / 4, h, u8Green, 'right edge');
+ check(w / 4, 0, w / 4, h, u8LightRed, 'left of center');
+ check(w / 2, 0, w / 4, h, u8LightGreen, 'right of center');
+
+ // show it
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.useProgram(texProgram);
+ gl.uniformMatrix4fv(texMatLoc, false, [
+ 2, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 1, 0,
+ -1, -1, 0, 1,
+ ]);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+ gl.deleteRenderbuffer(msRB);
+ gl.deleteTexture(dTex);
+ gl.deleteFramebuffer(msFB);
+ gl.deleteFramebuffer(dFB);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-with-full-sample-counts.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-with-full-sample-counts.html
new file mode 100644
index 0000000000..2e04385f28
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisample-with-full-sample-counts.html
@@ -0,0 +1,98 @@
+<!--
+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>
+<canvas id="canvas" width="32" height="32" style="width: 32px; height: 32px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+var wtu = WebGLTestUtils;
+description(' Test multisample with sample number from 1 to max sample number which also includes the samples that may not be in the supported sample list');
+
+var gl = wtu.create3DContext("canvas", null, 2);
+var size = 32;
+var program;
+
+if (!gl) {
+ testFailed('canvas.getContext() failed');
+} else {
+ program = wtu.setupColorQuad(gl);
+ gl.viewport(0, 0, size, size);
+ var supportedSampleCountArray = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES);
+ var iterationCount = supportedSampleCountArray[0] + 1;
+ for (var i = 1; i < iterationCount; i++)
+ {
+ runTest(gl, i, false);
+ runTest(gl, i, true);
+ }
+}
+
+function runTest(gl, sampleCount, isInverted) {
+ // Setup multi-sample RBO
+ var msColorRbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, msColorRbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGBA8, size, size);
+
+ // Setup multi-sample FBO.
+ var msFbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, msFbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, msColorRbo);
+
+ // Setup resolve color RBO.
+ var resolveColorRbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, resolveColorRbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, size, size);
+ // Setup resolve FBO
+ var resolveFbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, resolveFbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, resolveColorRbo);
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, msFbo);
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ testFailed("Framebuffer incomplete.");
+ return;
+ }
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ gl.enable(gl.SAMPLE_COVERAGE);
+ var coverageValue = isInverted ? 0.0 : 1.0;
+ gl.sampleCoverage(coverageValue, isInverted);
+
+ var quadColor = [1.0, 0.0, 0.0, 1.0];
+ gl.useProgram(program);
+ wtu.drawFloatColorQuad(gl, quadColor);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, resolveFbo);
+ gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, resolveFbo);
+ wtu.checkCanvasRect(gl, 0, 0, size, size, [255, 0, 0, 255],
+ "User buffer has been rendered to red with sample = "
+ + sampleCount + ", coverageValue = " + coverageValue
+ + " and isInverted = " + isInverted, 3);
+
+ gl.disable(gl.SAMPLE_COVERAGE);
+ gl.deleteRenderbuffer(msColorRbo);
+ gl.deleteRenderbuffer(resolveColorRbo);
+ gl.deleteFramebuffer(msFbo);
+ gl.deleteFramebuffer(resolveFbo);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-depth-renderbuffer-initialization.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-depth-renderbuffer-initialization.html
new file mode 100644
index 0000000000..7bd87890e2
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-depth-renderbuffer-initialization.html
@@ -0,0 +1,180 @@
+<!--
+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>
+<canvas id="testbed" width="40" height="40" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+var wtu = WebGLTestUtils;
+description('Verify multisampled depth renderbuffers are initialized to 1.0 before being read in WebGL');
+
+var gl = wtu.create3DContext("testbed", null, 2);
+
+if (!gl) {
+ testFailed('canvas.getContext() failed');
+} else {
+ // Set the clear color to green. It should never show up.
+ gl.clearColor(0, 1, 0, 1);
+
+ debug("Test renderbufferStorageMultisample with webgl1's DEPTH_STENCIL.");
+ {
+ const rb = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
+ wtu.shouldGenerateGLError(gl, 0,
+ "gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 0, gl.DEPTH_STENCIL, 1, 1)");
+ wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION,
+ "gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 1, gl.DEPTH_STENCIL, 1, 1)");
+ wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION,
+ "gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 2, gl.DEPTH_STENCIL, 1, 1)");
+ gl.deleteRenderbuffer(rb);
+ }
+
+ let c = gl.canvas;
+ var maxSamples = gl.getInternalformatParameter(
+ gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES)[0];
+ for (let i = 0; i < 2; ++i) {
+ // Non-multisampled tests
+ runTest(gl, {alloc1: {w: c.width, h: c.height, s: 0}, alloc2: null});
+ runTest(gl, {alloc1: null, alloc2: {w: c.width, h: c.height, s: 0}});
+ // Multisampled tests
+ runTest(gl, {alloc1: {w: c.width, h: c.height, s: maxSamples}, alloc2: null});
+ runTest(gl, {alloc1: null, alloc2: {w: c.width, h: c.height, s: maxSamples}});
+
+ // Tests for initially allocating at the wrong size.
+ // This is caused by a Qualcomm driver bug: http://crbug.com/696126
+ runTest(gl, {alloc1: {w: 5, h: 5, s: maxSamples}, alloc2: {w: c.width, h: c.height, s: maxSamples}});
+ }
+
+ // Testing buffer clearing won't change the clear values.
+ var clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
+ shouldBe("clearColor", "[0, 1, 0, 1]");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+}
+
+function runTest(gl, params) {
+ debug("");
+ debug("Test for depth buffer: " + JSON.stringify(params));
+ let resolve = params.alloc2 ? params.alloc2 : params.alloc1;
+ gl.viewport(0, 0, resolve.w, resolve.h);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 0, 0, 0],
+ "internal buffers have been initialized to 0");
+
+ gl.disable(gl.DEPTH_TEST);
+
+ // fill the back buffer so we know that reading below happens from
+ // the renderbuffer.
+ gl.clearDepth(0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ // Set up (color+depth) non-multisampled buffer to blit to and read back from.
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ var buffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, resolve.w, resolve.h);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, buffer);
+ var depthBuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, resolve.w, resolve.h);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Skip: framebuffer is allowed to be incomplete.");
+ return;
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+ gl.clearDepth(0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 255, 0, 255],
+ "user buffer has been cleared to green");
+
+ // Set up (depth-only) multisampled buffer to test.
+ var fbo_m = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_m);
+ var buffer_m = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer_m);
+
+ if (params.alloc1) {
+ allocStorage(params.alloc1.w, params.alloc1.h, params.alloc1.s);
+ }
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, buffer_m);
+ if (params.alloc2) {
+ if (params.alloc1) {
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Skip: framebuffer is allowed to be incomplete.");
+ return;
+ }
+ // Clear the FBO in order to make sure framebufferRenderbuffer is
+ // committed. (In Firefox, framebufferRenderbuffer is deferred, so this
+ // is needed to trigger the bug.)
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ }
+ allocStorage(params.alloc2.w, params.alloc2.h, params.alloc2.s);
+ }
+
+ function allocStorage(width, height, samples) {
+ gl.renderbufferStorageMultisample(
+ gl.RENDERBUFFER, samples, gl.DEPTH_COMPONENT16, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no error after renderbufferStorageMultisample(DEPTH_COMPONENT16).");
+ }
+
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Skip: framebuffer is allowed to be incomplete.");
+ return;
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+
+ // Blit from multisampled buffer to non-multisampled buffer.
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_m);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.clearDepth(0);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ // Blit depth from fbo_m (should be default value of 1.0) to fbo (should be 0.0).
+ gl.blitFramebuffer(0, 0, resolve.w, resolve.h,
+ 0, 0, resolve.w, resolve.h,
+ gl.DEPTH_BUFFER_BIT, gl.NEAREST);
+ // fbo depth should now be 1.0.
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ // Draw a blue quad (at clip z = 0.0, so depth = 0.5) (should pass the depth test: 0.5 < 1.0)
+ gl.depthFunc(gl.LESS);
+ gl.enable(gl.DEPTH_TEST);
+ wtu.setupColorQuad(gl);
+ wtu.setFloatDrawColor(gl, [0, 0, 1, 1]);
+ wtu.drawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 0, 255, 255]);
+
+ gl.deleteFramebuffer(fbo_m);
+ gl.deleteRenderbuffer(buffer_m);
+ gl.deleteFramebuffer(fbo);
+ gl.deleteRenderbuffer(buffer);
+
+ gl.canvas.width += 1;
+ gl.canvas.height += 1;
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+ debug('');
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-renderbuffer-initialization.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-renderbuffer-initialization.html
new file mode 100644
index 0000000000..0b27da661d
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-renderbuffer-initialization.html
@@ -0,0 +1,149 @@
+<!--
+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>
+<canvas id="testbed" width="40" height="40" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+var wtu = WebGLTestUtils;
+description('Verify multisampled renderbuffers are initialized to 0 before being read in WebGL');
+
+var gl = wtu.create3DContext("testbed", null, 2);
+
+if (!gl) {
+ testFailed('canvas.getContext() failed');
+} else {
+ // Set the clear color to green. It should never show up.
+ gl.clearColor(0, 1, 0, 1);
+
+ let c = gl.canvas;
+ var maxSamples = gl.getInternalformatParameter(
+ gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES)[0];
+ for (let i = 0; i < 2; ++i) {
+ runTest(gl, {alloc1: {w: c.width, h: c.height, s: maxSamples}, alloc2: null});
+ runTest(gl, {alloc1: null, alloc2: {w: c.width, h: c.height, s: maxSamples}});
+
+ // Tests for initially allocating at the wrong size.
+ // This is caused by a Qualcomm driver bug: http://crbug.com/696126
+ runTest(gl, {alloc1: {w: 5, h: 5, s: maxSamples}, alloc2: {w: c.width, h: c.height, s: maxSamples}});
+ runTest(gl, {alloc1: {w: 5, h: 5, s: maxSamples}, alloc2: {w: c.width, h: c.height, s: 0}});
+ runTest(gl, {alloc1: {w: 5, h: 5, s: 0}, alloc2: {w: c.width, h: c.height, s: maxSamples}});
+ }
+
+ // Testing buffer clearing won't change the clear values.
+ var clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
+ shouldBe("clearColor", "[0, 1, 0, 1]");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+}
+
+function runTest(gl, params) {
+ debug("Test for color buffer: " + JSON.stringify(params));
+ let resolve = params.alloc2 ? params.alloc2 : params.alloc1;
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 0, 0, 0],
+ "internal buffers have been initialized to 0");
+
+ // fill the back buffer so we know that reading below happens from
+ // the renderbuffer.
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Set up non-multisampled buffer to blit to and read back from.
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ var buffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, resolve.w, resolve.h);
+ attachBuffer(buffer);
+ shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)",
+ "gl.FRAMEBUFFER_COMPLETE");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 255, 0, 255],
+ "user buffer has been cleared to green");
+
+ // Set up multisampled buffer to test.
+ var fbo_m = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_m);
+ var buffer_m = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer_m);
+
+ if (params.alloc1) {
+ allocStorage(params.alloc1.w, params.alloc1.h, params.alloc1.s);
+ }
+ attachBuffer(buffer_m);
+ if (params.alloc2) {
+ if (params.alloc1) {
+ // Clear the FBO in order to make sure framebufferRenderbuffer is
+ // committed. (In Firefox, framebufferRenderbuffer is deferred, so
+ // this is needed to trigger the bug.)
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+ allocStorage(params.alloc2.w, params.alloc2.h, params.alloc2.s);
+ }
+
+ function allocStorage(width, height, samples) {
+ gl.renderbufferStorageMultisample(
+ gl.RENDERBUFFER, samples, gl.RGBA8, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no error after renderbufferStorageMultisample(RGBA8).");
+ }
+
+ function attachBuffer(buffer) {
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, buffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no error after framebufferRenderbuffer.");
+ }
+
+ shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)",
+ "gl.FRAMEBUFFER_COMPLETE");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+
+ // Blit from multisampled buffer to non-multisampled buffer.
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_m);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ // Blit color from fbo_m (should be black) to fbo (should be green)
+ gl.blitFramebuffer(0, 0, resolve.w, resolve.h,
+ 0, 0, resolve.w, resolve.h,
+ gl.COLOR_BUFFER_BIT, gl.NEAREST);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 0, 0, 0],
+ "user buffer has been initialized to 0");
+
+ gl.deleteFramebuffer(fbo_m);
+ gl.deleteRenderbuffer(buffer_m);
+ gl.deleteFramebuffer(fbo);
+ gl.deleteRenderbuffer(buffer);
+
+ // this clear should not matter we are about to resize
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ gl.canvas.width += 1;
+ gl.canvas.height += 1;
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+ debug('');
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-stencil-renderbuffer-initialization.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-stencil-renderbuffer-initialization.html
new file mode 100644
index 0000000000..cb351454d2
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/multisampled-stencil-renderbuffer-initialization.html
@@ -0,0 +1,167 @@
+<!--
+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>
+<canvas id="testbed" width="40" height="40" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+var wtu = WebGLTestUtils;
+description('Verify multisampled stencil renderbuffers are initialized to 0 before being read in WebGL');
+
+var gl = wtu.create3DContext("testbed", null, 2);
+
+if (!gl) {
+ testFailed('canvas.getContext() failed');
+} else {
+ // Set the clear color to green. It should never show up.
+ gl.clearColor(0, 1, 0, 1);
+ gl.disable(gl.DEPTH_TEST);
+
+ let c = gl.canvas;
+ var maxSamples = gl.getInternalformatParameter(
+ gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES)[0];
+ for (let i = 0; i < 2; ++i) {
+ // Non-multisampled tests
+ runTest(gl, {alloc1: {w: c.width, h: c.height, s: 0}, alloc2: null});
+ runTest(gl, {alloc1: null, alloc2: {w: c.width, h: c.height, s: 0}});
+ // Multisampled tests
+ runTest(gl, {alloc1: {w: c.width, h: c.height, s: maxSamples}, alloc2: null});
+ runTest(gl, {alloc1: null, alloc2: {w: c.width, h: c.height, s: maxSamples}});
+
+ // Tests for initially allocating at the wrong size.
+ // This is caused by a Qualcomm driver bug: http://crbug.com/696126
+ runTest(gl, {alloc1: {w: 5, h: 5, s: maxSamples}, alloc2: {w: c.width, h: c.height, s: maxSamples}});
+ }
+
+ // Testing buffer clearing won't change the clear values.
+ var clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
+ shouldBe("clearColor", "[0, 1, 0, 1]");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+}
+
+function runTest(gl, params) {
+ debug("Test for stencil buffer: " + JSON.stringify(params));
+ let resolve = params.alloc2 ? params.alloc2 : params.alloc1;
+ gl.viewport(0, 0, resolve.w, resolve.h);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 0, 0, 0],
+ "internal buffers have been initialized to 0");
+
+ gl.disable(gl.STENCIL_TEST);
+
+ // fill the back buffer so we know that reading below happens from
+ // the renderbuffer.
+ gl.clearStencil(2);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+
+ // Set up (color+stencil) non-multisampled buffer to blit to and read back from.
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ var buffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, resolve.w, resolve.h);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, buffer);
+ var stencilBuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, stencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, resolve.w, resolve.h);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencilBuffer);
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Skip: framebuffer is allowed to be incomplete.");
+ return;
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+ gl.clearStencil(2);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 255, 0, 255],
+ "user buffer has been cleared to green");
+
+ // Set up (stencil-only) multisampled buffer to test.
+ var fbo_m = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_m);
+ var buffer_m = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer_m);
+
+ if (params.alloc1) {
+ allocStorage(params.alloc1.w, params.alloc1.h, params.alloc1.s);
+ }
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, buffer_m);
+ if (params.alloc2) {
+ if (params.alloc1) {
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Skip: framebuffer is allowed to be incomplete.");
+ return;
+ }
+ // Clear the FBO in order to make sure framebufferRenderbuffer is
+ // committed. (In Firefox, framebufferRenderbuffer is deferred, so this
+ // is needed to trigger the bug.)
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+ }
+ allocStorage(params.alloc2.w, params.alloc2.h, params.alloc2.s);
+ }
+
+ function allocStorage(width, height, samples) {
+ gl.renderbufferStorageMultisample(
+ gl.RENDERBUFFER, samples, gl.STENCIL_INDEX8, width, height);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "should be no error after renderbufferStorageMultisample(STENCIL_INDEX8).");
+ }
+
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+ debug("Skip: framebuffer is allowed to be incomplete.");
+ return;
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+
+ // Blit from multisampled buffer to non-multisampled buffer.
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_m);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.clearStencil(2);
+ gl.clear(gl.STENCIL_BUFFER_BIT);
+ // Blit stencil from fbo_m (should be default value of 0) to fbo (should be 2).
+ gl.blitFramebuffer(0, 0, resolve.w, resolve.h,
+ 0, 0, resolve.w, resolve.h,
+ gl.STENCIL_BUFFER_BIT, gl.NEAREST);
+ // fbo stencil should now be 0.
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ // Draw a blue quad (with stencil = 1) (should pass the stencil test: 1 > 0)
+ gl.stencilFunc(gl.GREATER, 1, 0xffffffff);
+ gl.enable(gl.STENCIL_TEST);
+ wtu.setupColorQuad(gl);
+ wtu.setFloatDrawColor(gl, [0, 0, 1, 1]);
+ wtu.drawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, resolve.w, resolve.h,
+ [0, 0, 255, 255]);
+
+ gl.deleteFramebuffer(fbo_m);
+ gl.deleteRenderbuffer(buffer_m);
+ gl.deleteFramebuffer(fbo);
+ gl.deleteRenderbuffer(buffer);
+
+ gl.canvas.width += 1;
+ gl.canvas.height += 1;
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, 'should be no errors');
+ debug('');
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/readbuffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/readbuffer.html
new file mode 100644
index 0000000000..56bfd656a4
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/readbuffer.html
@@ -0,0 +1,174 @@
+<!--
+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">
+<title>Test readBuffer Against WebGL 2</title>
+<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>
+<canvas id="canvas" width="20" height="20"> </canvas>
+<script>
+"use strict";
+description("This tests reading from fbo");
+
+var clearDrawingbuffer = function(color) {
+ gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+}
+
+var validateReadingFromFramebuffer = function(color, expected, msg) {
+ var pixels = new Uint8Array(1 * 1 * 4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+ wtu.glErrorShouldBe(gl, expected, msg);
+ if (expected == gl.NO_ERROR)
+ wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, color,
+ "the color should be [" + color + "]");
+}
+
+var setupRenderbuffer = function(attachment) {
+ var renderbuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderbuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, canvas.width, canvas.height);
+ return renderbuffer;
+}
+
+var testReadBufferOnDefaultFB = function() {
+ gl.readBuffer(gl.NONE);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling readBuffer with GL_NONE on the default framebuffer should succeed.");
+ var pixels = new Uint8Array(1 * 1 * 4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "should generate INVALID_OPERATION when reading from framebuffer and read buffer is GL_NONE.");
+ gl.readBuffer(gl.BACK);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling readBuffer with GL_BACK on the default framebuffer should succeed.");
+
+ gl.readBuffer(gl.COLOR_ATTACHMENT0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "calling readBuffer with GL_COLOR_ATTACHMENT0 on the default framebuffer should generate INVALID_OPERATION.");
+}
+
+var testReadBufferOnFBO = function() {
+ gl.readBuffer(gl.BACK);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "calling readBuffer with GL_BACK on fbo should generate INVALID_OPERATION.");
+
+ gl.readBuffer(gl.NONE);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling readBuffer with GL_NONE on fbo should succeed.");
+ var pixels = new Uint8Array(1 * 1 * 4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "should generate INVALID_OPERATION when reading from framebuffer and read buffer is GL_NONE.");
+ gl.readBuffer(gl.COLOR_ATTACHMENT0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling readBuffer with GL_COLOR_ATTACHMENT0 on fbo should succeed.");
+
+ var maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS);
+ gl.readBuffer(gl.COLOR_ATTACHMENT0 + maxColorAttachments);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
+ "calling readBuffer with GL_COLOR_ATTACHMENTi that exceeds MAX_COLOR_ATTACHMENT on fbo should generate INVALID_OPERATION.");
+ gl.readBuffer(gl.COLOR_ATTACHMENT1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "calling readBuffer with GL_COLOR_ATTACHMENT1 on the fbo should succeed.");
+ shouldBe('gl.getParameter(gl.READ_BUFFER)', 'gl.COLOR_ATTACHMENT1');
+}
+
+debug("");
+debug("Canvas.getContext");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, undefined, 2);
+if (!gl) {
+ testFailed("context does not exist");
+} else {
+ testPassed("context exists");
+
+ debug("");
+ debug("Checking reading from framebuffer.");
+
+ // Test on the default framebuffer. Read buffer is GL_BACK by default.
+ var backColor = [0, 0, 0, 255];
+ clearDrawingbuffer(backColor);
+ validateReadingFromFramebuffer(backColor, gl.NO_ERROR,
+ "should be no errors when reading from GL_BACK on the default framebuffer.");
+
+ shouldBe('gl.getParameter(gl.READ_BUFFER)', 'gl.BACK');
+ testReadBufferOnDefaultFB();
+
+ // Test on fbo. Read buffer is GL_COLOR_ATTACHMENT0 by default
+ var fb = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ var colorbuffer = setupRenderbuffer(gl.COLOR_ATTACHMENT0);
+ var red = [255, 0, 0, 255];
+ clearDrawingbuffer(red);
+ validateReadingFromFramebuffer(red, gl.NO_ERROR,
+ "should be no errors when reading from GL_COLOR_ATTACHMENT0 on fbo.");
+
+ shouldBe('gl.getParameter(gl.READ_BUFFER)', 'gl.COLOR_ATTACHMENT0');
+ testReadBufferOnFBO();
+
+ // Test on user defined read buffer (GL_COLOR_ATTACHMENT1) with or without corresponding image on fbo.
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ var colorbuffer1 = setupRenderbuffer(gl.COLOR_ATTACHMENT1);
+ var green = [0, 255, 0, 255];
+ gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]);
+ clearDrawingbuffer(green);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error after setup and clear render buffer");
+ gl.readBuffer(gl.COLOR_ATTACHMENT1);
+ validateReadingFromFramebuffer(green, gl.NO_ERROR,
+ "should be no errors when reading from GL_COLOR_ATTACHMENT1 on fbo.");
+ shouldBe('gl.getParameter(gl.READ_BUFFER)', 'gl.COLOR_ATTACHMENT1');
+ // Need to reset draw buffers, otherwise it triggers a mac driver bug.
+ // We add a separate test for that bug: conformance2/rendering/framebuffer-completeness-unaffected.html
+ gl.drawBuffers([gl.COLOR_ATTACHMENT0]);
+
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, null)
+ gl.readBuffer(gl.COLOR_ATTACHMENT1);
+ validateReadingFromFramebuffer(null, gl.INVALID_OPERATION,
+ "should generate INVALID_OPERATION when reading from GL_COLOR_ATTACHMENT1 but this attachment has no image currently.");
+
+ // switch to another fbo, read buffer is GL_COLOR_ATTACHMENT0, not GL_COLOR_ATTACHMENT1
+ var fb1 = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb1);
+ var buffer = setupRenderbuffer(gl.COLOR_ATTACHMENT0);
+ shouldBe('gl.getParameter(gl.READ_BUFFER)', 'gl.COLOR_ATTACHMENT0');
+ var blue = [0, 0, 255, 255];
+ clearDrawingbuffer(blue);
+ validateReadingFromFramebuffer(blue, gl.NO_ERROR,
+ "should be no errors when reading from GL_COLOR_ATTACHMENT0 on another fbo.");
+
+ // switch from fbo to default fb, read buffer will switch to GL_BACK from GL_COLOR_ATTACHMENT0
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ shouldBe('gl.getParameter(gl.READ_BUFFER)', 'gl.BACK');
+ validateReadingFromFramebuffer(backColor, gl.NO_ERROR,
+ "should be no errors when reading from GL_BACK on the default framebuffer.");
+
+ gl.deleteFramebuffer(fb);
+ gl.deleteRenderbuffer(colorbuffer);
+ gl.deleteRenderbuffer(colorbuffer1);
+ gl.deleteFramebuffer(fb1);
+ gl.deleteRenderbuffer(buffer);
+}
+
+debug("");
+var successfullyParsed = true;
+
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>