immutable-tex-render-feedback.html (7505B)
1 <!-- 2 Copyright (c) 2019 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 10 <meta charset="utf-8"> 11 <title>Ensure sampling-feedback detection can allow certain immutable texture uses</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 <div id="description"></div> 18 <div id="console"></div> 19 <script> 20 "use strict"; 21 22 const wtu = WebGLTestUtils; 23 description(); 24 25 const gl = wtu.create3DContext(undefined, undefined, 2); 26 27 function* range(a, b) { 28 a.required; 29 if (b === undefined) { 30 b = a; 31 a = 0; 32 } 33 for (let i = a; i < b; i += 1) { 34 yield i; 35 } 36 } 37 38 const VS = `\ 39 void main() { 40 gl_PointSize = 1.0; 41 } 42 `; 43 44 const FS = `\ 45 uniform sampler2D u_tex0; 46 void main() { 47 gl_FragColor = texture2D(u_tex0, vec2(0)); 48 } 49 `; 50 51 const prog = wtu.loadProgram(gl, VS, FS); 52 gl.useProgram(prog); 53 54 (() => { 55 const MIPS = 3; 56 const SIZE = 10; 57 58 const immutTex = gl.createTexture(); 59 immutTex.name = "immutTex"; 60 immutTex.immutable = true; 61 gl.bindTexture(gl.TEXTURE_2D, immutTex); 62 gl.texStorage2D(gl.TEXTURE_2D, MIPS, gl.RGBA8, SIZE, SIZE); 63 64 const mutTex = gl.createTexture(); 65 mutTex.name = "mutTex"; 66 mutTex.immutable = false; 67 gl.bindTexture(gl.TEXTURE_2D, mutTex); 68 for (const mip of range(MIPS)) { 69 const size = SIZE >> mip; 70 gl.texImage2D(gl.TEXTURE_2D, mip, gl.RGBA8, size, size, 0, 71 gl.RGBA, gl.UNSIGNED_BYTE, null); 72 } 73 74 const MAG_FILTERS = [ 75 'LINEAR', 76 'NEAREST', 77 ]; 78 const MIN_FILTERS = [ 79 ['LINEAR',false], 80 ['LINEAR_MIPMAP_LINEAR',true], 81 ['LINEAR_MIPMAP_NEAREST',true], 82 ['NEAREST',false], 83 ['NEAREST_MIPMAP_LINEAR',true], 84 ['NEAREST_MIPMAP_NEAREST',true], 85 ]; 86 87 const fb = gl.createFramebuffer(); 88 gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 89 90 debug(` 91 mips: ${MIPS}: [0,${MIPS-1}] (inclusive) 92 size: ${SIZE}`); 93 const texs = [ 94 immutTex, 95 mutTex, 96 ]; 97 for (const tex of texs) { 98 debug(`\ 99 100 immutable: ${tex.immutable}`); 101 gl.bindTexture(gl.TEXTURE_2D, tex); 102 103 for (const level_prime_base of range(MIPS+1)) { // `level_base` in GLES 104 // ES 3.0.6 p150 105 let _level_base = level_prime_base; 106 if (tex.immutable) { 107 _level_base = Math.min(_level_base, MIPS-1); 108 } 109 const level_base = _level_base; 110 111 for (let _level_prime_max of range(level_prime_base-1, MIPS+2)) { // `q` in GLES 112 if (_level_prime_max < 0) continue; 113 if (_level_prime_max == MIPS+1) { 114 _level_prime_max = 10000; // This is the default, after all! 115 } 116 const level_prime_max = _level_prime_max; 117 118 // ES 3.0.6 p150 119 let _level_max = level_prime_max; 120 if (tex.immutable) { 121 _level_max = Math.min(Math.max(level_base, level_prime_max), MIPS-1); 122 } 123 const level_max = _level_max; 124 125 const p = Math.floor(Math.log2(SIZE)) + level_base; 126 const q = Math.min(p, level_max); 127 128 debug(`\ 129 130 level_prime_base/max: [${level_prime_base}, ${level_prime_max}] (inclusive) 131 level_base/max: [${level_base}, ${level_max}] (inclusive) 132 q: ${q}`); 133 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 134 level_prime_base); 135 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 136 level_prime_max); 137 138 const mipComplete = (q <= MIPS-1); 139 140 for (const [minFilter,useMips] of MIN_FILTERS) { 141 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, 142 gl[minFilter]); 143 144 // ES3.0 p211 145 const srcMaxSampledMip = (useMips ? q : level_base); 146 147 // ES3.0 p160-161 148 const textureComplete = (srcMaxSampledMip <= MIPS-1) && 149 (level_base <= level_max); 150 151 for (const magFilter of MAG_FILTERS) { 152 debug(`\ 153 154 min: ${minFilter}, mag: ${magFilter}`); 155 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, 156 gl[magFilter]); 157 158 for (const dstMip of range(0,MIPS+1)) { 159 debug(` 160 mip: ${dstMip}`); 161 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 162 gl.TEXTURE_2D, tex, dstMip); 163 164 // - 165 166 // ES3.0 p213-214 167 let fbComplete = true; 168 169 // * "The width and height of `image` are non-zero" 170 fbComplete &= (0 <= dstMip && dstMip <= MIPS-1); 171 172 if (!tex.immutable) { // "...does not name an immutable-format texture..." 173 // * "...the value of [level] must be in the range `[level_base, q]`" 174 fbComplete &= (level_base <= dstMip && dstMip <= q); 175 176 // * "...the value of [level] is not `level_base`, then the texture must be mipmap complete" 177 if (dstMip != level_base) { 178 fbComplete &= mipComplete; 179 } 180 } 181 182 // - 183 184 let expectError = 0; 185 let expectStatus = gl.FRAMEBUFFER_COMPLETE; 186 187 // ES3.0 p211 188 let samplingFeedback = (level_base <= dstMip && dstMip <= srcMaxSampledMip); 189 if (!textureComplete) { 190 // Incomplete textures are safe 191 samplingFeedback = false; 192 } 193 if (samplingFeedback) { 194 expectError = gl.INVALID_OPERATION; 195 } 196 if (!fbComplete) { 197 expectStatus = gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT; 198 expectError = gl.INVALID_FRAMEBUFFER_OPERATION; 199 } 200 201 // - 202 203 wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, 204 expectStatus, `{immutable: ${tex.immutable}, level_prime_base/max: [${level_prime_base}, ${level_prime_max}], minFilter: ${minFilter}, dest: ${dstMip}}`); 205 206 gl.drawArrays(gl.POINTS, 0, 1); 207 wtu.glErrorShouldBe(gl, expectError, "after draw with texture"); 208 } 209 } 210 } 211 } 212 } 213 } 214 })(); 215 216 var successfullyParsed = true; 217 </script> 218 <script src="../../../js/js-test-post.js"></script> 219 220 </body> 221 </html>