2 Copyright (c) 2022 The Khronos Group Inc.
3 Use of this source code is governed by an MIT-style license that can be
4 found in the LICENSE.txt file.
10 <meta charset=
"utf-8">
11 <title>WebGL framebuffer to texture conformance test.
</title>
12 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
13 <script src=
"../../js/js-test-pre.js"></script>
14 <script src=
"../../js/webgl-test-utils.js"> </script>
17 <canvas id=
"canvas"></canvas>
18 <div id=
"description"></div>
19 <div id=
"console"></div>
22 description("Test resolving and copying the framebuffer to a texture, and drawing the result.");
23 debug('Reduced test case for <a href="http://anglebug.com/6972">http://anglebug.com/6972</a>');
25 // Reproduces two behaviors:
27 // 1) The initial draw disappearing entirely from the default back
28 // buffer. The current test case does not show this behavior
29 // independently from the other, but a previous iteration, with the
30 // textured quad scaled to half size and translated (-0.5, -0.5), did.
32 // 2) With Metal debug layers and load/store validation turned on on
33 // Intel Macs, the transparent area of the texture prior to the bug
34 // fix was magenta = undefined. Similar behavior would presumably
35 // reproduce on M1 hardware without debug layers or validation.
38 const halfSize
= size
/ 2;
39 const green
= [ 0, 255, 0, 255 ];
40 const transparent
= [ 0, 0, 0, 0 ];
42 let wtu
= WebGLTestUtils
;
43 let canvas
= document
.getElementById("canvas");
47 let gl
= wtu
.create3DContext("canvas", {
48 // Antialiasing is crucial for reproducing the bug.
50 // Depth testing is not.
54 function allocateTexture(sz
) {
55 let texture
= gl
.createTexture();
56 gl
.bindTexture(gl
.TEXTURE_2D
, texture
);
57 gl
.texStorage2D(gl
.TEXTURE_2D
, 1, gl
.RGBA8
, sz
, sz
);
58 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.NEAREST
);
59 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.NEAREST
);
60 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_S
, gl
.CLAMP_TO_EDGE
);
61 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_T
, gl
.CLAMP_TO_EDGE
);
62 gl
.bindTexture(gl
.TEXTURE_2D
, null);
66 // Allocate destination texture
67 let destTexture
= allocateTexture(halfSize
);
69 // Set up half-size solid color quad in center
70 let colorQuadVAO
= gl
.createVertexArray();
71 gl
.bindVertexArray(colorQuadVAO
);
72 let colorQuadProgram
= wtu
.setupColorQuad(gl
, 0, { scale
: 0.5 });
74 // Setup textured quad covering the entire renderable area
75 let quadVAO
= gl
.createVertexArray();
76 gl
.bindVertexArray(quadVAO
);
77 let quadProgram
= wtu
.setupTexturedQuad(gl
, 0, 1);
78 gl
.useProgram(quadProgram
);
79 let quadTexLoc
= gl
.getUniformLocation(quadProgram
, "tex");
80 gl
.uniform1i(quadTexLoc
, 0);
82 gl
.blendFunc(gl
.ONE
, gl
.ONE_MINUS_SRC_ALPHA
);
83 gl
.activeTexture(gl
.TEXTURE0
); // To match quadTexLoc=0
86 gl
.clearColor(0, 0, 0, 0);
87 gl
.clear(gl
.COLOR_BUFFER_BIT
);
89 gl
.bindVertexArray(colorQuadVAO
);
90 gl
.useProgram(colorQuadProgram
);
91 wtu
.drawUByteColorQuad(gl
, [ 0, 255, 0, 255 ]);
93 gl
.bindTexture(gl
.TEXTURE_2D
, destTexture
);
94 // Copy the upper right corner of the framebuffer to the texture.
95 gl
.copyTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, halfSize
, halfSize
, halfSize
, halfSize
);
96 gl
.bindTexture(gl
.TEXTURE_2D
, null);
97 gl
.useProgram(quadProgram
);
99 gl
.bindVertexArray(quadVAO
);
100 gl
.bindTexture(gl
.TEXTURE_2D
, destTexture
);
101 // Magnify and blend this texture over the current framebuffer.
102 wtu
.drawUnitQuad(gl
);
105 function runUserDefinedFBOTest() {
106 let fbo1
= gl
.createFramebuffer();
107 let fbo2
= gl
.createFramebuffer();
108 let rb
= gl
.createRenderbuffer();
109 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo1
);
110 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, rb
);
111 gl
.renderbufferStorageMultisample(gl
.RENDERBUFFER
, 4, gl
.RGBA8
, size
, size
);
112 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, rb
);
113 wtu
.framebufferStatusShouldBe(gl
, gl
.FRAMEBUFFER
, [ gl
.FRAMEBUFFER_COMPLETE
]);
115 let tex
= allocateTexture(size
, size
);
116 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo2
);
117 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.TEXTURE_2D
, tex
, 0);
118 wtu
.framebufferStatusShouldBe(gl
, gl
.FRAMEBUFFER
, [ gl
.FRAMEBUFFER_COMPLETE
]);
120 // Same rendering steps as in the default-framebuffer test, with appropriate framebuffer blits interspersed.
121 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo1
);
122 gl
.clearColor(0, 0, 0, 0);
123 gl
.clear(gl
.COLOR_BUFFER_BIT
);
124 gl
.disable(gl
.BLEND
);
125 gl
.bindVertexArray(colorQuadVAO
);
126 gl
.useProgram(colorQuadProgram
);
127 wtu
.drawUByteColorQuad(gl
, [ 0, 255, 0, 255 ]);
129 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, fbo1
);
130 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo2
);
131 gl
.blitFramebuffer(0, 0, size
, size
, 0, 0, size
, size
, gl
.COLOR_BUFFER_BIT
, gl
.NEAREST
);
132 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo2
);
134 gl
.bindTexture(gl
.TEXTURE_2D
, destTexture
);
135 // Copy the upper right corner of the framebuffer to the texture.
136 gl
.copyTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, halfSize
, halfSize
, halfSize
, halfSize
);
137 gl
.bindTexture(gl
.TEXTURE_2D
, null);
139 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo1
);
141 gl
.useProgram(quadProgram
);
143 gl
.bindVertexArray(quadVAO
);
144 gl
.bindTexture(gl
.TEXTURE_2D
, destTexture
);
145 // Magnify and blend this texture over the current framebuffer.
146 wtu
.drawUnitQuad(gl
);
148 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, fbo1
);
149 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo2
);
150 gl
.blitFramebuffer(0, 0, size
, size
, 0, 0, size
, size
, gl
.COLOR_BUFFER_BIT
, gl
.NEAREST
);
151 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo2
);
153 // No longer easy to put these results on the canvas, because it's
154 // antialiased and we can't blitFramebuffer to it. Let's assume
155 // that if failures occur, they'll be straightforward to debug.
158 function checkRenderingResults(prefix
) {
159 // Center quad should be rendered correctly.
160 wtu
.checkCanvasRect(gl
,
161 halfSize
/ 2 + 1, halfSize
/ 2 + 1,
162 halfSize
- 2, halfSize
- 2,
164 prefix
+ ": center quad should be green");
166 // Overlapping lower-left quad should be green as well.
167 wtu
.checkCanvasRect(gl
,
169 halfSize
- 2, halfSize
- 2,
171 prefix
+ ": lower left quad should be green");
173 // Leftmost area above the lower-left quad should be transparent.
174 wtu
.checkCanvasRect(gl
,
176 halfSize
/ 2 - 2, halfSize
/ 2 - 2,
178 prefix
+ ": leftmost area above lower left quad should be transparent");
180 // Bottommost area to the right of the lower-left quad should be transparent.
181 wtu
.checkCanvasRect(gl
,
183 halfSize
/ 2 - 2, halfSize
/ 2 - 2,
185 prefix
+ ": bottommost area to the right of lower left quad should be transparent");
189 checkRenderingResults("default back buffer");
191 runUserDefinedFBOTest();
192 checkRenderingResults("user-defined framebuffer");
194 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Should be no errors at the end of the test.");
198 var successfullyParsed
= true;