shader-uniform-packing-restrictions.html (11827B)
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>WebGL uniform packing restrctions Conformance Test</title> 12 <link rel="stylesheet" href="../../../resources/js-test-style.css"/> 13 <link rel="stylesheet" href="../../../resources/glsl-feature-tests.css"/> 14 <script src="../../../js/js-test-pre.js"></script> 15 <script src="../../../js/webgl-test-utils.js"></script> 16 <script src="../../../js/glsl-conformance-test.js"></script> 17 </head> 18 <body> 19 <div id="description"></div> 20 <div id="console"></div> 21 <canvas id="example" width="2" height="2"> </canvas> 22 <script id="vshader" type="x-shader/x-vertex"> 23 attribute vec4 a_position; 24 void main() 25 { 26 gl_Position = a_position; 27 } 28 </script> 29 <script id="fshader" type="x-shader/x-vertex"> 30 precision mediump float; 31 varying vec4 v_varying; 32 void main() 33 { 34 gl_FragColor = v_varying; 35 } 36 </script> 37 <script id="vshaderArrayTest" type="x-shader/x-vertex"> 38 attribute vec4 a_position; 39 varying vec4 v_varying; 40 uniform $(type) u_uniform[$(numTestType)]; 41 void main() 42 { 43 v_varying = $(result); 44 gl_Position = a_position; 45 } 46 </script> 47 <script id="fshaderArrayTest" type="x-shader/x-fragment"> 48 precision mediump float; 49 uniform $(type) u_uniform[$(numTestType)]; 50 void main() 51 { 52 gl_FragColor = $(result); 53 } 54 </script> 55 <script id="vshaderUniformTest" type="x-shader/x-fragment"> 56 attribute vec4 a_position; 57 varying vec4 v_varying; 58 $(uniforms) 59 void main() 60 { 61 $(code) 62 v_varying = $(result); 63 gl_Position = a_position; 64 } 65 </script> 66 <script id="fshaderUniformTest" type="x-shader/x-fragment"> 67 precision mediump float; 68 $(uniforms) 69 void main() 70 { 71 $(code) 72 gl_FragColor = $(result); 73 } 74 </script> 75 <script> 76 "use strict"; 77 description(); 78 debug(""); 79 var wtu = WebGLTestUtils; 80 var gl = wtu.create3DContext("example"); 81 82 var uniformTypes = [ 83 { type: "bool", componentsPerRow: 1, rows: 1, fType: "float", uniToF: "float(u_uniform$(id)$(index))", fToVec4: "vec4($(f), 0, 0, 0)"}, 84 { type: "float", componentsPerRow: 1, rows: 1, fType: "float", uniToF: "u_uniform$(id)$(index)", fToVec4: "vec4($(f), 0, 0, 0)"}, 85 { type: "int", componentsPerRow: 1, rows: 1, fType: "float", uniToF: "float(u_uniform$(id)$(index))", fToVec4: "vec4($(f), 0, 0, 0)"}, 86 { type: "vec2", componentsPerRow: 2, rows: 1, fType: "vec2", uniToF: "u_uniform$(id)$(index)", fToVec4: "vec4($(f), 0, 0)"}, 87 { type: "ivec2", componentsPerRow: 2, rows: 1, fType: "vec2", uniToF: "vec2(u_uniform$(id)$(index))", fToVec4: "vec4($(f), 0, 0)"}, 88 { type: "bvec2", componentsPerRow: 2, rows: 1, fType: "vec2", uniToF: "vec2(u_uniform$(id)$(index))", fToVec4: "vec4($(f), 0, 0)"}, 89 { type: "vec3", componentsPerRow: 3, rows: 1, fType: "vec3", uniToF: "u_uniform$(id)$(index)", fToVec4: "vec4($(f), 0)"}, 90 { type: "ivec3", componentsPerRow: 3, rows: 1, fType: "vec3", uniToF: "vec3(u_uniform$(id)$(index))", fToVec4: "vec4($(f), 0)"}, 91 { type: "bvec3", componentsPerRow: 3, rows: 1, fType: "vec3", uniToF: "vec3(u_uniform$(id)$(index))", fToVec4: "vec4($(f), 0)"}, 92 { type: "vec4", componentsPerRow: 4, rows: 1, fType: "vec4", uniToF: "u_uniform$(id)$(index)", fToVec4: "$(f)"}, 93 { type: "ivec4", componentsPerRow: 4, rows: 1, fType: "vec4", uniToF: "vec4(u_uniform$(id)$(index))", fToVec4: "$(f)"}, 94 { type: "bvec4", componentsPerRow: 4, rows: 1, fType: "vec4", uniToF: "vec4(u_uniform$(id)$(index))", fToVec4: "$(f)"}, 95 // Yes, the spec says mat2 takes 4 columns, 2 rows. 96 { type: "mat2", componentsPerRow: 4, rows: 2, fType: "vec2", uniToF: "vec2(u_uniform$(id)$(index)[0])", fToVec4: "vec4($(f), 0, 0)"}, 97 { type: "mat3", componentsPerRow: 3, rows: 3, fType: "vec3", uniToF: "vec3(u_uniform$(id)$(index)[0])", fToVec4: "vec4($(f), 0)"}, 98 { type: "mat4", componentsPerRow: 4, rows: 4, fType: "vec4", uniToF: "vec4(u_uniform$(id)$(index)[0])", fToVec4: "$(f)"}, 99 // Samplers generally have more restrictive limits. 100 // { type: "sampler2D", componentsPerRow: 1, rows: 1, code: "vec4(texture2D(u_uniform[$(index)], vec2(0, 0)))", }, 101 // { type: "samplerCube", componentsPerRow: 1, rows: 1, code: "vec4(textureCube(u_uniform[$(index)], vec3(0, 0, 0)))", }, 102 ]; 103 104 var vBaseSource = wtu.getScript("vshader"); 105 var fBaseSource = wtu.getScript("fshader"); 106 var vArrayTestSource = wtu.getScript("vshaderArrayTest"); 107 var fArrayTestSource = wtu.getScript("fshaderArrayTest"); 108 var vUniformTestSource = wtu.getScript("vshaderUniformTest"); 109 var fUniformTestSource = wtu.getScript("fshaderUniformTest"); 110 111 var tests = []; 112 var shaderTypes = [ 113 { type: "vertex", 114 // For tests that expect failure which shader might fail. 115 vertExpectation: false, 116 fragExpectation: true, 117 vertArrayTest: vArrayTestSource, 118 fragArrayTest: fBaseSource, 119 vertUniformTest: vUniformTestSource, 120 fragUniformTest: fBaseSource, 121 maxVectors: gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS), 122 minVectors: 127, // GLSL ES 1.0.17 Appendix A.7 and A.8. Reserve one row for constants in the code, hence 128 - 1. 123 }, 124 { type: "fragment", 125 // For tests that expect failure which shader might fail. 126 vertExpectation: true, 127 fragExpectation: false, 128 vertArrayTest: vBaseSource, 129 fragArrayTest: fArrayTestSource, 130 vertUniformTest: vBaseSource, 131 fragUniformTest: fUniformTestSource, 132 maxVectors: gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS), 133 minVectors: 15, // GLSL ES 1.0.17 Appendix A.8 - minimum value of gl_maxFragmentUniformVectors is 16. Again, reserve a row for constants. 134 }, 135 ]; 136 for (var ss = 0; ss < shaderTypes.length; ++ss) { 137 var shaderType = shaderTypes[ss]; 138 debug("max " + shaderType.type + ": " + shaderType.maxVectors); 139 for (var ii = 0; ii < uniformTypes.length; ++ii) { 140 var info = uniformTypes[ii]; 141 wtu.log("checking: " + info.type); 142 // Compute the maximum amount of this type allowed in a single array. 143 var maxInArray = Math.floor(shaderType.maxVectors / info.rows); 144 // Compute the minimum required to work in a single array. 145 var minVars = Math.floor(shaderType.minVectors / info.rows); 146 // Compute the maximum allowed as single elements 147 var maxPerRow = Math.floor(4 / info.componentsPerRow); 148 var maxPacked = Math.floor(shaderType.maxVectors * maxPerRow / info.rows); 149 150 // Test array[1] of the type 151 var uniToF = wtu.replaceParams(info.uniToF, {id: "", index: "[0]"}); 152 var vec4 = wtu.replaceParams(info.fToVec4, {f: uniToF}); 153 tests.push({ 154 vShaderSource: wtu.replaceParams(shaderType.vertArrayTest, {numTestType: 1, result: vec4}, info), 155 vShaderSuccess: true, 156 fShaderSource: wtu.replaceParams(shaderType.fragArrayTest, {numTestType: 1, result: vec4}, info), 157 fShaderSuccess: true, 158 linkSuccess: true, 159 passMsg: shaderType.type + " shader with uniform array of " + info.type + " with 1 element should succeed", 160 }); 161 162 // Note: We can't test an array filling all uniform space as actual GL drivers are 163 // only required to be able to do the minimum number. After that it can fail for 164 // multiple reasons, including uniform registers being reserved for the implementation's 165 // own use. Constants also take up uniform registers. 166 167 // Test required number of uniforms 168 var uniToF = wtu.replaceParams(info.uniToF, {id: "", index: "[" + (minVars - 1) + "]"}); 169 var vec4 = wtu.replaceParams(info.fToVec4, {f: uniToF}); 170 tests.push({ 171 vShaderSource: wtu.replaceParams(shaderType.vertArrayTest, {numTestType: minVars, result: vec4}, info), 172 vShaderSuccess: true, 173 fShaderSource: wtu.replaceParams(shaderType.fragArrayTest, {numTestType: minVars, result: vec4}, info), 174 fShaderSuccess: true, 175 linkSuccess: true, 176 passMsg: shaderType.type + " shader with uniform array of " + info.type + " with " + minVars + " elements (the minimum required) should succeed", 177 }); 178 179 // Test array[max + 1] accessing last element. WebGL requires this to fail. 180 var uniToF = wtu.replaceParams(info.uniToF, {id: "", index: "[" + maxInArray + "]"}); 181 var vec4 = wtu.replaceParams(info.fToVec4, {f: uniToF}); 182 tests.push({ 183 vShaderSource: wtu.replaceParams(shaderType.vertArrayTest, {numTestType: maxInArray + 1, result: vec4}, info), 184 vShaderSuccess: shaderType.vertExpectation, 185 fShaderSource: wtu.replaceParams(shaderType.fragArrayTest, {numTestType: maxInArray + 1, result: vec4}, info), 186 fShaderSuccess: shaderType.fragExpectation, 187 linkSuccess: false, 188 passMsg: shaderType.type + " shader with uniform array of " + info.type + " with " + (maxInArray + 1) + " elements (one past maximum) accessing last element should fail", 189 }); 190 191 // Test array[max + 1] accessing first element. WebGL requires this to fail but ES allows truncating array. 192 var uniToF = wtu.replaceParams(info.uniToF, {id: "", index: "[0]"}); 193 var vec4 = wtu.replaceParams(info.fToVec4, {f: uniToF}); 194 tests.push({ 195 vShaderSource: wtu.replaceParams(shaderType.vertArrayTest, {numTestType: maxInArray + 1, result: vec4}, info), 196 vShaderSuccess: shaderType.vertExpectation, 197 fShaderSource: wtu.replaceParams(shaderType.fragArrayTest, {numTestType: maxInArray + 1, result: vec4}, info), 198 fShaderSuccess: shaderType.fragExpectation, 199 linkSuccess: false, 200 passMsg: shaderType.type + " shader with uniform array of " + info.type + " with " + (maxInArray + 1) + " elements (one past maximum) accessing first element should fail", 201 }); 202 203 // Note: We can't test max uniforms as actual GL drivers are only required to be able 204 // to do the minimum number. After that it can fail for multiple reasons, including 205 // uniform registers being reserved for the implementation's own use or also instruction 206 // space limitations. Strictly speaking, guaranteed supported length of a shader 207 // executable is defined by the GLES2 conformance tests according to GLSL ES 1.0.17 208 // Appendix A.2. This does not give us an exact limit: this test only aims to fit within 209 // instruction space limits imposed by existing GLES2 compliant hardware. 210 211 var generateCode = function(numVars) { 212 var uniforms = []; 213 var sumTerms = []; 214 for (var uu = 0; uu < numVars; ++uu) { 215 uniforms.push(" uniform " + info.type + " u_uniform" + uu + ";"); 216 sumTerms.push(wtu.replaceParams(info.uniToF, {id: uu, index: ""})); 217 } 218 return { 219 uniforms: uniforms.join("\n"), 220 code: info.fType + " sum = " + sumTerms.join(" + \n ") + ";", 221 result: wtu.replaceParams(info.fToVec4, {f: 'sum'}) 222 }; 223 }; 224 225 // Test max+1 uniforms of type. 226 tests.push({ 227 vShaderSource: wtu.replaceParams(shaderType.vertUniformTest, generateCode(maxPacked + 1), info), 228 vShaderSuccess: shaderType.vertExpectation, 229 fShaderSource: wtu.replaceParams(shaderType.fragUniformTest, generateCode(maxPacked + 1), info), 230 fShaderSuccess: shaderType.fragExpectation, 231 linkSuccess: false, 232 passMsg: shaderType.type + " shader with " + (maxPacked + 1) + " uniforms of " + info.type + " (one past maximum) should fail", 233 }); 234 235 // Test required uniforms of type. 236 tests.push({ 237 vShaderSource: wtu.replaceParams(shaderType.vertUniformTest, generateCode(minVars), info), 238 vShaderSuccess: true, 239 fShaderSource: wtu.replaceParams(shaderType.fragUniformTest, generateCode(minVars), info), 240 fShaderSuccess: true, 241 linkSuccess: true, 242 passMsg: shaderType.type + " shader with " + minVars + " uniforms of " + info.type + " (the minimum required) should succeed", 243 }); 244 } 245 } 246 GLSLConformanceTester.runTests(tests); 247 var successfullyParsed = true; 248 </script> 249 </body> 250 </html>