shader-precision-format.html (11756B)
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 <!DOCTYPE html> 7 <html> 8 <head> 9 <meta charset="utf-8"> 10 <title>WebGL shader precision format test.</title> 11 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 12 <script src="../../js/js-test-pre.js"></script> 13 <script src="../../js/webgl-test-utils.js"> </script> 14 </head> 15 <body> 16 <canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas> 17 <div id="description"></div> 18 <div id="console"></div> 19 <script> 20 "use strict"; 21 var wtu = WebGLTestUtils; 22 description(document.title); 23 debug("Tests that WebGLShaderPrecisionFormat class and getShaderPrecisionFormat work."); 24 debug(""); 25 var gl = wtu.create3DContext("canvas"); 26 27 function verifyShaderPrecisionFormat(shadertype, precisiontype) { 28 shouldBeTrue('gl.getShaderPrecisionFormat(' + shadertype + ', ' + 29 precisiontype + ') instanceof WebGLShaderPrecisionFormat'); 30 } 31 32 debug(""); 33 debug("Test that getShaderPrecisionFormat returns a WebGLShaderPrecisionFormat object."); 34 debug(""); 35 36 verifyShaderPrecisionFormat('gl.VERTEX_SHADER', 'gl.LOW_FLOAT'); 37 verifyShaderPrecisionFormat('gl.VERTEX_SHADER', 'gl.MEDIUM_FLOAT'); 38 verifyShaderPrecisionFormat('gl.VERTEX_SHADER', 'gl.HIGH_FLOAT'); 39 verifyShaderPrecisionFormat('gl.VERTEX_SHADER', 'gl.LOW_INT'); 40 verifyShaderPrecisionFormat('gl.VERTEX_SHADER', 'gl.MEDIUM_INT'); 41 verifyShaderPrecisionFormat('gl.VERTEX_SHADER', 'gl.HIGH_INT'); 42 verifyShaderPrecisionFormat('gl.FRAGMENT_SHADER', 'gl.LOW_FLOAT'); 43 verifyShaderPrecisionFormat('gl.FRAGMENT_SHADER', 'gl.MEDIUM_FLOAT'); 44 verifyShaderPrecisionFormat('gl.FRAGMENT_SHADER', 'gl.HIGH_FLOAT'); 45 verifyShaderPrecisionFormat('gl.FRAGMENT_SHADER', 'gl.LOW_INT'); 46 verifyShaderPrecisionFormat('gl.FRAGMENT_SHADER', 'gl.MEDIUM_INT'); 47 verifyShaderPrecisionFormat('gl.FRAGMENT_SHADER', 'gl.HIGH_INT'); 48 49 debug(""); 50 debug("Test that getShaderPrecisionFormat throws an error with invalid parameters."); 51 debug(""); 52 53 wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, 'gl.getShaderPrecisionFormat(gl.HIGH_INT, gl.VERTEX_SHADER)'); 54 55 debug(""); 56 debug("Test that WebGLShaderPrecisionFormat values are sensible."); 57 debug(""); 58 59 // The minimum values are from OpenGL ES Shading Language spec, section 4.5. 60 61 var shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT); 62 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 1'); 63 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 1'); 64 shouldBeTrue('shaderPrecisionFormat.precision >= 8'); 65 66 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT); 67 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 14'); 68 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 14'); 69 shouldBeTrue('shaderPrecisionFormat.precision >= 10'); 70 71 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT); 72 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 62'); 73 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 62'); 74 shouldBeTrue('shaderPrecisionFormat.precision >= 16'); 75 76 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT); 77 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 8'); 78 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 8'); 79 shouldBeTrue('shaderPrecisionFormat.precision == 0'); 80 81 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT); 82 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 10'); 83 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 10'); 84 shouldBeTrue('shaderPrecisionFormat.precision == 0'); 85 86 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT); 87 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 16'); 88 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 16'); 89 shouldBeTrue('shaderPrecisionFormat.precision == 0'); 90 91 var shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT); 92 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 1'); 93 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 1'); 94 shouldBeTrue('shaderPrecisionFormat.precision >= 8'); 95 96 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT); 97 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 14'); 98 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 14'); 99 shouldBeTrue('shaderPrecisionFormat.precision >= 10'); 100 101 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT); 102 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 8'); 103 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 8'); 104 shouldBeTrue('shaderPrecisionFormat.precision == 0'); 105 106 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT); 107 shouldBeTrue('shaderPrecisionFormat.rangeMin >= 10'); 108 shouldBeTrue('shaderPrecisionFormat.rangeMax >= 10'); 109 shouldBeTrue('shaderPrecisionFormat.precision == 0'); 110 111 debug(""); 112 debug("Test optional highp support in fragment shaders."); 113 debug(""); 114 115 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT); 116 shouldBeTrue('(shaderPrecisionFormat.rangeMin == 0 && shaderPrecisionFormat.rangeMax == 0 && shaderPrecisionFormat.precision == 0) || (shaderPrecisionFormat.rangeMin >= 62 && shaderPrecisionFormat.rangeMax >= 62 && shaderPrecisionFormat.precision >= 16)'); 117 118 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT); 119 shouldBeTrue('(shaderPrecisionFormat.rangeMin == 0 && shaderPrecisionFormat.rangeMax == 0 && shaderPrecisionFormat.precision == 0) || (shaderPrecisionFormat.rangeMin >= 16 && shaderPrecisionFormat.rangeMax >= 16 && shaderPrecisionFormat.precision == 0)'); 120 121 debug(""); 122 debug("Test that getShaderPrecisionFormat returns the same thing every call."); 123 debug(""); 124 125 shaderPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT); 126 var shaderPrecisionFormat2 = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT); 127 shouldBeTrue('shaderPrecisionFormat.rangeMin == shaderPrecisionFormat2.rangeMin'); 128 shouldBeTrue('shaderPrecisionFormat.rangeMax == shaderPrecisionFormat2.rangeMax'); 129 shouldBeTrue('shaderPrecisionFormat.precision == shaderPrecisionFormat2.precision'); 130 131 debug(""); 132 debug("Test that specified precision matches rendering results"); 133 debug(""); 134 135 function testRenderPrecisionSetup(gl, shaderPair) { 136 const program = wtu.setupProgram(gl, shaderPair); 137 138 // Create a buffer and setup an attribute. 139 // We wouldn't need this except for a bug in Safari and arguably 140 // this should be removed from the test but we can't test the test itself 141 // without until the bug is fixed. 142 // see https://bugs.webkit.org/show_bug.cgi?id=197592 143 { 144 gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 145 gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW); 146 const loc = gl.getAttribLocation(program, 'position'); 147 gl.enableVertexAttribArray(loc); 148 gl.vertexAttribPointer(loc, 1, gl.UNSIGNED_BYTE, false, 0, 0); 149 } 150 151 gl.useProgram(program); 152 153 return program; 154 } 155 156 function testRenderPrecision(gl, shaderType, type, precision, expected, msg) { 157 gl.clear(gl.COLOR_BUFFER_BIT); 158 gl.drawArrays(gl.POINTS, 0, 1); 159 160 const pixel = new Uint8Array(4); 161 gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); 162 163 wtu.checkCanvasRect(gl, 0, 0, 1, 1, expected, msg, 5); 164 } 165 166 function testRenderPrecisionFloat(gl, precisionEnum, precision) { 167 function test(gl, shaderPair, shaderType) { 168 const format = gl.getShaderPrecisionFormat(shaderType, precisionEnum); 169 const value = 2 ** format.precision - 1; 170 171 const length = v => Math.sqrt(v.reduce((sum, v) => sum + v * v, 0)); 172 const normalize = (v) => { 173 const l = length(v); 174 return v.map(v => v / l); 175 }; 176 177 const input = [Math.sqrt(value), Math.sqrt(value), Math.sqrt(value)]; 178 const expected = [...normalize(input).map(v => (v * 0.5 + 0.5) * 255 | 0), 255]; 179 180 const msg = `${wtu.glEnumToString(gl, shaderType)}: ${precision} float precision: ${format.precision}, rangeMin: ${format.rangeMin}, rangeMax: ${format.rangeMax}`; 181 const program = testRenderPrecisionSetup(gl, shaderPair); 182 const vLocation = gl.getUniformLocation(program, 'v'); 183 gl.uniform3fv(vLocation, input); 184 testRenderPrecision(gl, shaderType, 'float', precision, expected, msg); 185 } 186 187 { 188 const vs = ` 189 attribute vec4 position; 190 uniform ${precision} vec3 v; 191 varying ${precision} vec4 v_result; 192 void main() { 193 gl_Position = position; 194 gl_PointSize = 1.0; 195 v_result = vec4(normalize(v) * 0.5 + 0.5, 1); 196 } 197 `; 198 199 const fs = ` 200 precision ${precision} float; 201 varying ${precision} vec4 v_result; 202 void main() { 203 gl_FragColor = v_result; 204 } 205 `; 206 207 test(gl, [vs, fs], gl.VERTEX_SHADER); 208 } 209 210 { 211 const vs = ` 212 attribute vec4 position; 213 void main() { 214 gl_Position = position; 215 gl_PointSize = 1.0; 216 } 217 `; 218 219 const fs = ` 220 precision ${precision} float; 221 uniform ${precision} vec3 v; 222 void main() { 223 gl_FragColor = vec4(normalize(v) * 0.5 + 0.5, 1); 224 } 225 `; 226 227 test(gl, [vs, fs], gl.FRAGMENT_SHADER); 228 } 229 } 230 231 function testRenderPrecisionInt(gl, precisionEnum, precision) { 232 function test(gl, shaderPair, shaderType) { 233 const format = gl.getShaderPrecisionFormat(shaderType, precisionEnum); 234 const value = 1 << (format.rangeMax - 1); 235 236 const input = [value, value, value, value]; 237 const expected = [255, 255, 255, 255]; 238 239 const msg = `${wtu.glEnumToString(gl, shaderType)}: ${precision} int precision: ${format.precision}, rangeMin: ${format.rangeMin}, rangeMax: ${format.rangeMax}`; 240 const program = testRenderPrecisionSetup(gl, shaderPair); 241 gl.uniform1i(gl.getUniformLocation(program, 'v'), value); 242 gl.uniform1f(gl.getUniformLocation(program, 'f'), value); 243 testRenderPrecision(gl, shaderType, 'int', precision, expected, msg); 244 } 245 246 { 247 const vs = ` 248 attribute vec4 position; 249 uniform ${precision} int v; 250 uniform highp float f; 251 varying vec4 v_result; 252 253 void main() { 254 gl_Position = position; 255 gl_PointSize = 1.0; 256 float diff = abs(float(v) - f); 257 bool pass = diff < 1.0; 258 v_result = vec4(pass); 259 } 260 `; 261 262 const fs = ` 263 precision mediump float; 264 varying vec4 v_result; 265 void main() { 266 gl_FragColor = v_result; 267 } 268 `; 269 test(gl, [vs, fs], gl.VERTEX_SHADER); 270 } 271 272 { 273 const vs = ` 274 attribute vec4 position; 275 void main() { 276 gl_Position = position; 277 gl_PointSize = 1.0; 278 } 279 `; 280 281 const fs = ` 282 precision ${precision} float; 283 uniform ${precision} int v; 284 uniform mediump float f; 285 286 void main() { 287 mediump float diff = abs(float(v) - f); 288 bool pass = diff < 1.0; 289 gl_FragColor = vec4(pass); 290 } 291 `; 292 293 test(gl, [vs, fs], gl.FRAGMENT_SHADER); 294 } 295 } 296 297 // because the canvas can be 16 bit IIRC 298 const fb = gl.createFramebuffer(gl.FRAMEBUFFER); 299 gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 300 301 const tex = gl.createTexture(); 302 gl.bindTexture(gl.TEXTURE_2D, tex); 303 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 304 305 gl.framebufferTexture2D( 306 gl.FRAMEBUFFER, 307 gl.COLOR_ATTACHMENT0, 308 gl.TEXTURE_2D, 309 tex, 310 0); 311 312 gl.viewport(0, 0, 1, 1); 313 314 { 315 testRenderPrecisionFloat(gl, gl.LOW_FLOAT, 'lowp'); 316 testRenderPrecisionFloat(gl, gl.MEDIUM_FLOAT, 'mediump'); 317 318 const format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT); 319 if (format.precision !== 0) { 320 testRenderPrecisionFloat(gl, gl.HIGH_FLOAT, 'highp'); 321 } 322 } 323 324 { 325 testRenderPrecisionInt(gl, gl.LOW_INT, 'lowp'); 326 testRenderPrecisionInt(gl, gl.MEDIUM_INT, 'mediump'); 327 328 const format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT); 329 if (format.rangeMax !== 0) { 330 testRenderPrecisionInt(gl, gl.HIGH_INT, 'highp'); 331 } 332 } 333 334 finishTest(); 335 </script> 336 337 </body> 338 </html>