framebuffer-to-texture.html (7614B)
1 <!-- 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. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 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> 15 </head> 16 <body> 17 <canvas id="canvas"></canvas> 18 <div id="description"></div> 19 <div id="console"></div> 20 <script> 21 "use strict"; 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>'); 24 25 // Reproduces two behaviors: 26 // 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. 31 // 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. 36 37 const size = 64; 38 const halfSize = size / 2; 39 const green = [ 0, 255, 0, 255 ]; 40 const transparent = [ 0, 0, 0, 0 ]; 41 42 let wtu = WebGLTestUtils; 43 let canvas = document.getElementById("canvas"); 44 canvas.width = size; 45 canvas.height = size; 46 47 let gl = wtu.create3DContext("canvas", { 48 // Antialiasing is crucial for reproducing the bug. 49 antialias: true, 50 // Depth testing is not. 51 depth: false, 52 }, 2); 53 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); 63 return texture; 64 } 65 66 // Allocate destination texture 67 let destTexture = allocateTexture(halfSize); 68 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 }); 73 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); 81 82 gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); 83 gl.activeTexture(gl.TEXTURE0); // To match quadTexLoc=0 84 85 function runTest() { 86 gl.clearColor(0, 0, 0, 0); 87 gl.clear(gl.COLOR_BUFFER_BIT); 88 gl.disable(gl.BLEND); 89 gl.bindVertexArray(colorQuadVAO); 90 gl.useProgram(colorQuadProgram); 91 wtu.drawUByteColorQuad(gl, [ 0, 255, 0, 255 ]); 92 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); 98 gl.enable(gl.BLEND); 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); 103 } 104 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 ]); 114 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 ]); 119 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 ]); 128 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); 133 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); 138 139 gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo1); 140 141 gl.useProgram(quadProgram); 142 gl.enable(gl.BLEND); 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); 147 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); 152 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. 156 } 157 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, 163 green, 164 prefix + ": center quad should be green"); 165 166 // Overlapping lower-left quad should be green as well. 167 wtu.checkCanvasRect(gl, 168 1, 1, 169 halfSize - 2, halfSize - 2, 170 green, 171 prefix + ": lower left quad should be green"); 172 173 // Leftmost area above the lower-left quad should be transparent. 174 wtu.checkCanvasRect(gl, 175 1, halfSize + 1, 176 halfSize / 2 - 2, halfSize / 2 - 2, 177 transparent, 178 prefix + ": leftmost area above lower left quad should be transparent"); 179 180 // Bottommost area to the right of the lower-left quad should be transparent. 181 wtu.checkCanvasRect(gl, 182 halfSize + 1, 1, 183 halfSize / 2 - 2, halfSize / 2 - 2, 184 transparent, 185 prefix + ": bottommost area to the right of lower left quad should be transparent"); 186 } 187 188 runTest(); 189 checkRenderingResults("default back buffer"); 190 191 runUserDefinedFBOTest(); 192 checkRenderingResults("user-defined framebuffer"); 193 194 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors at the end of the test."); 195 196 finishTest(); 197 198 var successfullyParsed = true; 199 </script> 200 </body> 201 </html>