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.
8 var GLSLConstructorTestsGenerator = (function() {
10 var wtu = WebGLTestUtils;
12 // Shader code templates
13 var constructorVertexTemplate = [
14 "attribute vec4 vPosition;",
16 "precision mediump int;",
17 "precision mediump float;",
19 // Colors used to signal correctness of component values comparison
20 "const vec4 green = vec4(0.0, 1.0, 0.0, 1.0);",
21 "const vec4 red = vec4(1.0, 0.0, 0.0, 1.0);",
23 // Error bound used in comparison of floating point values
26 "varying vec4 vColor;",
31 " $(type) v = $(type)($(argsConstr));",
33 " if ($(checkCompVals))",
38 " gl_Position = vPosition;",
43 var passThroughColorFragmentShader = [
44 "precision mediump float;",
46 "varying vec4 vColor;",
49 " gl_FragColor = vColor;",
54 var constructorFragmentTemplate = [
55 "precision mediump int;",
56 "precision mediump float;",
58 // Colors used to signal correctness of component values comparison
59 "const vec4 green = vec4(0.0, 1.0, 0.0, 1.0); ",
60 "const vec4 red = vec4(1.0, 0.0, 0.0, 1.0); ",
62 // Error bound used in comparison of floating point values
68 " $(type) v = $(type)($(argsConstr));",
70 " if ($(checkCompVals))",
71 " gl_FragColor = green;",
73 " gl_FragColor = red;",
78 // Coding of the different argument types
87 // Returns the dimensions of the type
88 // Count of columns, count of rows
89 function getTypeCodeDimensions(typeCode) {
91 case "s": return [1, 1];
92 case "v2": return [1, 2];
93 case "v3": return [1, 3];
94 case "v4": return [1, 4];
95 case "m2": return [2, 2];
96 case "m3": return [3, 3];
97 case "m4": return [4, 4];
100 wtu.error("GLSLConstructorTestsGenerator.getTypeCodeDimensions(), unknown type code");
106 // Returns the component count for the type code
107 function getTypeCodeComponentCount(typeCode) {
108 var dim = getTypeCodeDimensions(typeCode);
110 return dim[0] * dim[1];
114 // Returns glsl name of type code
115 function getGLSLBaseTypeName(typeCode) {
118 case "v2": return "vec2";
119 case "v3": return "vec3";
120 case "v4": return "vec4";
121 case "m2": return "mat2";
122 case "m3": return "mat3";
123 case "m4": return "mat4";
126 wtu.error("GLSLConstructorTestsGenerator.getGLSLBaseTypeName(), unknown type code");
132 // Returns the scalar glsl type name related to the structured type
133 function getGLSLScalarType(targetType) {
134 switch(targetType[0]) {
135 case 'i': return "int";
136 case 'b': return "bool";
143 wtu.error("GLSLConstructorTestsGenerator.getGLSLScalarType(), unknown target type");
149 // Returns the scalar prefix for the associated scalar type
150 function getGLSLScalarPrefix(targetType) {
151 switch(targetType[0]) {
154 return targetType[0];
161 wtu.error("GLSLConstructorTestsGenerator.getGLSLScalarPrefix(), unknown target type");
167 // Returns the type for a specified target type and argument type code
168 function getGLSLArgumentType(typeCode, targetType) {
169 var baseType = getGLSLBaseTypeName(typeCode);
170 if (baseType !== "") {
171 if (typeCode[0] === "v") {
172 // Vectors come in different flavours
173 return getGLSLScalarPrefix(targetType) + baseType;
179 return getGLSLScalarType(targetType);
183 // Returns the glsl type of the argument components
184 function getGLSLArgumentComponentType(argTypeCode, targetType) {
187 if (argTypeCode[0] === "m") {
188 // Matrices are always floats
189 scalarType = "float";
192 scalarType = getGLSLScalarType(targetType);
198 function getGLSLColumnSize(targetType) {
199 colSize = parseInt(targetType.slice(-1));
204 wtu.error("GLSLConstructorTestsGenerator.getGLSLColumnSize(), invalid target type");
209 // Returns correct string representation of scalar value
210 function getScalarTypeValStr(val, scalarType) {
214 switch (scalarType) {
215 case "float": return val.toFixed(1);
216 case "int": return val;
217 case "bool": return (val === 0) ? "false" : "true";
220 wtu.error("GLSLConstructorTestsGenerator.getScalarTypeValStr(), unknown scalar type");
226 // Returns true if the glsl type name is a matrix
227 function isGLSLTypeMatrix(type) {
228 return (type.indexOf("mat") !== -1);
232 // Returns true if the glsl type name is a vector
233 function isGLSLTypeVector(type) {
234 return (type.indexOf("vec") !== -1);
238 // Returns the count of components
239 function getGLSLTypeComponentCount(type) {
240 var colSize = getGLSLColumnSize(type);
242 if (isGLSLTypeMatrix(type))
243 return colSize * colSize;
249 // Returns the constructor expression with the components set to a sequence of scalar values
250 // Like vec3(1.0, 2.0, 3.0)
251 function getComponentSequenceConstructorExpression(typeCode, firstCompValue, targetType) {
252 var scalarType = getGLSLArgumentComponentType(typeCode, targetType);
254 if (typeCode === "s") {
256 return getScalarTypeValStr(firstCompValue, scalarType) + ";";
259 // Structured typeargTypeCode[0] === "m"
260 compCount = getTypeCodeComponentCount(typeCode);
261 var constrExpParts = new Array(compCount);
262 for (var aa = 0; aa < compCount; ++aa)
263 constrExpParts[aa] = getScalarTypeValStr(firstCompValue + aa, scalarType);
265 return getGLSLArgumentType(typeCode, targetType) + "(" + constrExpParts.join(", ") + ");";
270 // Returns the expression to select a component of the structured type
271 function getComponentSelectorExpStr(targetType, compIx) {
272 if (isGLSLTypeMatrix(targetType)) {
273 var colRowIx = getColRowIndexFromLinearIndex(compIx, getGLSLColumnSize(targetType));
274 return "v[" + colRowIx.colIx + "][" + colRowIx.rowIx + "]";
277 return "v[" + compIx + "]";
281 // Returns expression which validates the components set by the constructor expression
282 function getComponentValidationExpression(refCompVals, targetType) {
283 // Early out for invalid arguments
284 if (refCompVals.length === 0)
287 var scalarType = getGLSLScalarType(targetType);
288 var checkComponentValueParts = new Array(refCompVals.length);
289 for (var cc = 0; cc < refCompVals.length; ++cc) {
290 var val_str = getScalarTypeValStr(refCompVals[cc], scalarType);
291 var comp_sel_exp = getComponentSelectorExpStr(targetType, cc);
292 if (scalarType === "float") {
293 // Comparison of floating point values with error bound
294 checkComponentValueParts[cc] = "abs(" + comp_sel_exp + " - " + val_str + ") <= errorBound";
297 // Simple comparison to expected value
298 checkComponentValueParts[cc] = comp_sel_exp + " == " + val_str;
302 return checkComponentValueParts.join(" && ");
306 // Returns substitution parts to turn the shader template into testable shader code
307 function getTestShaderParts(targetType, argExp, firstCompValue) {
308 // glsl code of declarations of arguments
309 var argsListParts = new Array(argExp.length);
311 // glsl code of constructor expression
312 var argsConstrParts = new Array(argExp.length);
314 // glsl type expression
315 var typeExpParts = new Array(argExp.length);
316 for (var aa = 0; aa < argExp.length; ++aa) {
317 var typeCode = argExp[aa];
318 var argCompCount = getTypeCodeComponentCount(typeCode);
319 var argName = "a" + aa;
320 var argType = getGLSLArgumentType(typeCode, targetType);
321 var argConstrExp = argType + " " + argName + " = " + getComponentSequenceConstructorExpression(typeCode, firstCompValue, targetType);
323 // Add construction of one argument
324 // Indent if not first argument
325 argsListParts[aa] = ((aa > 0) ? " " : "") + argConstrExp;
327 // Add argument name to target type argument list
328 argsConstrParts[aa] = argName;
330 // Add type name to type expression
331 typeExpParts[aa] = argType;
333 // Increment argument component value so all argument component arguments have a unique value
334 firstCompValue += argCompCount;
338 argsList: argsListParts.join("\n") + "\n",
339 argsConstr: argsConstrParts.join(", "),
340 typeExp: targetType + "(" + typeExpParts.join(", ") + ")"
345 // Utility functions to manipulate the array of reference values
347 // Returns array filled with identical values
348 function getArrayWithIdenticalValues(size, val) {
349 var matArray = new Array(size);
350 for (var aa = 0; aa < size; ++aa)
357 // Returns array filled with increasing values from a specified start value
358 function getArrayWithIncreasingValues(size, start) {
359 var matArray = new Array(size);
360 for (var aa = 0; aa < size; ++aa)
361 matArray[aa] = start + aa;
367 // Utility functions to manipulate the array of reference values if the target type is a matrix
369 // Returns an array which is the column order layout of a square matrix where the diagonal is set to a specified value
370 function matCompArraySetDiagonal(matArray, diagVal) {
371 // The entries for the diagonal start at array index 0 and increase
372 // by column size + 1
373 var colSize = Math.round(Math.sqrt(matArray.length));
376 matArray[dIx] = diagVal;
377 dIx += (colSize + 1);
379 while (dIx < colSize * colSize);
385 // Returns an array which contains the values of an identity matrix read out in column order
386 function matCompArrayCreateDiagonalMatrix(colSize, diagVal) {
387 var size = colSize * colSize;
388 var matArray = new Array(size);
389 for (var aa = 0; aa < size; ++aa)
392 return matCompArraySetDiagonal(matArray, diagVal);
396 // Returns the column and row index from the linear index if the components of the matrix are stored in column order in an array
397 // in a one dimensional array in column order
398 function getColRowIndexFromLinearIndex(linIx, colSize) {
400 colIx: Math.floor(linIx / colSize),
401 rowIx: linIx % colSize
406 // Returns the linear index for matrix column and row index for a specified matrix size
407 function getLinearIndexFromColRowIndex(rowColIx, colSize) {
408 return rowColIx.colIx * colSize + rowColIx.rowIx;
412 // Returns a matrix set from another matrix
413 function matCompArraySetMatrixFromMatrix(dstColSize, srcMatArray) {
414 // Overwrite components from destination with the source component values at the same col, row coordinates
415 var dstMatArray = matCompArrayCreateDiagonalMatrix(dstColSize, 1);
417 var srcColSize = Math.round(Math.sqrt(srcMatArray.length));
419 for (var c_ix = 0; c_ix < srcMatArray.length; ++c_ix) {
420 var srcMatIx = getColRowIndexFromLinearIndex(c_ix, srcColSize);
421 if (srcMatIx.colIx < dstColSize && srcMatIx.rowIx < dstColSize) {
422 // Source matrix coordinates are valid destination matrix coordinates
423 dstMatArray[getLinearIndexFromColRowIndex(srcMatIx, dstColSize)] = srcMatArray[c_ix];
431 // Returns the glsl code to verify if the components are set correctly
432 // and the message to display for the test
433 function getConstructorExpressionInfo(targetType, argExp, firstCompValue) {
434 var argCompCountsSum = 0;
435 var argCompCounts = new Array(argExp.length);
436 for (var aa = 0; aa < argExp.length; ++aa) {
437 argCompCounts[aa] = getTypeCodeComponentCount(argExp[aa]);
438 argCompCountsSum += argCompCounts[aa];
441 var targetCompCount = getGLSLTypeComponentCount(targetType);
447 if (argCompCountsSum === 0) {
448 // A constructor needs at least one argument
450 testMsg = "invalid (no arguments)";
454 if (isGLSLTypeVector(targetType)) {
455 if (argCompCountsSum === 1) {
456 // One scalar argument
457 // Vector constructor with one scalar argument set all components to the same value
458 refCompVals = getArrayWithIdenticalValues(targetCompCount, firstCompValue);
459 testMsg = "valid (all components set to the same value)";
463 // Not one scalar argument
464 if (argCompCountsSum < targetCompCount) {
465 // Not all components set
467 testMsg = "invalid (not enough arguments)";
471 // argCompCountsSum >= targetCompCount
472 // All components set
473 var lastArgFirstCompIx = argCompCountsSum - argCompCounts[argCompCounts.length - 1];
475 if (lastArgFirstCompIx < targetCompCount) {
476 // First component of last argument is used
477 refCompVals = getArrayWithIncreasingValues(targetCompCount, firstCompValue);
482 // First component of last argument is not used
484 testMsg = "invalid (unused argument)";
491 // Matrix target type
492 if (argCompCountsSum === 1) {
493 // One scalar argument
494 // Matrix constructors with one scalar set all components on the diagonal to the same value
495 // All other components are set to zero
496 refCompVals = matCompArrayCreateDiagonalMatrix(Math.round(Math.sqrt(targetCompCount)), firstCompValue);
497 testMsg = "valid (diagonal components set to the same value, off-diagonal components set to zero)";
501 // Not one scalar argument
502 if (argExp.length === 1 && argExp[0][0] === "m") {
503 // One single matrix argument
504 var dstColSize = getGLSLColumnSize(targetType);
505 refCompVals = matCompArraySetMatrixFromMatrix(dstColSize, getArrayWithIncreasingValues(getTypeCodeComponentCount(argExp[0]), firstCompValue));
506 testMsg = "valid, components at corresponding col, row indices are set from argument, other components are set from identity matrix";
510 // More than one argument or one argument not of type matrix
511 // Can be treated in the same manner
512 // Arguments can not be of type matrix
513 var matFound = false;
514 for (var aa = 0; aa < argExp.length; ++aa)
515 if (argExp[aa][0] === "m")
520 testMsg = "invalid, argument list greater than one contains matrix type";
524 if (argCompCountsSum < targetCompCount) {
526 testMsg = "invalid (not enough arguments)";
530 // argCompCountsSum >= targetCompCount
531 // All components set
532 var lastArgFirstCompIx = argCompCountsSum - argCompCounts[argCompCounts.length - 1];
534 if (lastArgFirstCompIx < targetCompCount) {
535 // First component of last argument is used
536 refCompVals = getArrayWithIncreasingValues(targetCompCount, firstCompValue);
541 // First component of last argument is not used
543 testMsg = "invalid (unused argument)";
553 // Check if no case is missed
554 if (testMsg == null || valid == null) {
555 wtu.error("GLSLConstructorTestsGenerator.getConstructorExpressionInfo(), info not set");
560 refCompVals: refCompVals,
567 // Returns a vertex shader testcase and a fragment shader testcase
568 function getVertexAndFragmentShaderTestCase(targetType, argExp) {
569 var firstCompValue = 0;
570 if (isGLSLTypeMatrix(targetType)) {
571 // Use value different from 0 and 1
572 // 0 and 1 are values used by matrix constructed from a matrix or a single scalar
576 var argCode = getTestShaderParts (targetType, argExp, firstCompValue);
577 var expInfo = getConstructorExpressionInfo(targetType, argExp, firstCompValue);
579 var substitutions = {
581 errorBound: (getGLSLScalarType(targetType) === "float") ? "const float errorBound = 1.0E-5;" : "",
582 argsList: argCode.argsList,
583 argsConstr: argCode.argsConstr,
584 checkCompVals: getComponentValidationExpression(expInfo.refCompVals, targetType)
588 // Test constructor argument list in vertex shader
589 vShaderSource: wtu.replaceParams(constructorVertexTemplate, substitutions),
590 vShaderSuccess: expInfo.valid,
591 fShaderSource: passThroughColorFragmentShader,
592 fShaderSuccess: true,
593 linkSuccess: expInfo.valid,
594 passMsg: "Vertex shader : " + argCode.typeExp + ", " + expInfo.testMsg,
595 render: expInfo.valid
597 // Test constructor argument list in fragment shader
598 fShaderSource: wtu.replaceParams(constructorFragmentTemplate, substitutions),
599 fShaderSuccess: expInfo.valid,
600 linkSuccess: expInfo.valid,
601 passMsg: "Fragment shader : " + argCode.typeExp + ", " + expInfo.testMsg,
602 render: expInfo.valid
608 // Incrementing the argument expressions
609 // Utility object which defines the order of incrementing the argument types
610 var typeCodeIncrementer = {
611 s: { typeCode: "v2", order: 0 },
612 v2: { typeCode: "v3", order: 1 },
613 v3: { typeCode: "v4", order: 2 },
614 v4: { typeCode: "m2", order: 3 },
615 m2: { typeCode: "m3", order: 4 },
616 m3: { typeCode: "m4", order: 5 },
617 m4: { typeCode: "s", order: 6 },
622 // Returns the next argument sequence
623 function getNextArgumentSequence(inSeq) {
625 if (inSeq.length === 0) {
626 // Current argument sequence is empty, add first argument
627 nextSeq = [typeCodeIncrementer.first];
630 nextSeq = new Array(inSeq.length);
632 for (var aa = 0; aa < inSeq.length; ++aa) {
633 var currArg = inSeq[aa];
635 // Increment the current argument type
636 var nextArg = typeCodeIncrementer[currArg].typeCode;
637 nextSeq[aa] = nextArg;
638 overflow = (nextArg === typeCodeIncrementer.first);
641 // Copy remainder of sequence
642 nextSeq[aa] = currArg;
647 nextSeq.push(typeCodeIncrementer.first);
655 // Returns true if two argument expressions are equal
656 function areArgExpEqual(expA, expB) {
657 if (expA.length !== expB.length)
660 for (var aa = 0; aa < expA.length; ++aa)
661 if (expA[aa] !== expB[aa])
668 // Returns true if first argument expression is smaller
669 // (comes before the second one in iterating order)
670 // compared to the second argument expression
671 function isArgExpSmallerOrEqual(argExpA, argExpB) {
672 var aLen = argExpA.length;
673 var bLen = argExpB.length;
675 return (aLen < bLen);
677 // Argument type expression lengths are equal
678 for (var aa = aLen - 1; aa >= 0; --aa) {
679 var argA = argExpA[aa];
680 var argB = argExpB[aa];
683 var aOrder = typeCodeIncrementer[argA].order;
684 var bOrder = typeCodeIncrementer[argB].order;
685 if (aOrder !== bOrder)
686 return (aOrder < bOrder);
690 // Argument type expressions are equal
695 // Returns the next argument expression from sequence set
696 // Returns null if end is reached
697 function getNextArgumentExpression(testExp, testSet) {
698 var testInterval = testSet[testExp.ix];
700 if (areArgExpEqual(testExp.argExp, testInterval[1])) {
701 // End of current interval reached
702 if (testExp.ix === testSet.length - 1) {
703 // End of set reached
707 // Return first argument expression of next interval
708 var nextIx = testExp.ix + 1;
709 return { ix: nextIx, argExp: testSet[nextIx][0] };
713 // Return next expression in current interval
714 return { ix: testExp.ix, argExp: getNextArgumentSequence(testExp.argExp) };
719 // Returns an array of the parts in the string separated by commas and with the white space trimmed
720 function convertCsvToArray(str) {
721 // Checks type codes in input
722 function checkInput(el, ix, arr) {
723 var typeCode = el.trim();
724 if (!(typeCode in typeCodeIncrementer) && typeCode !== "first") {
725 wtu.error("GLSLConstructorTestsGenerator.convertCsvToArray(), unknown type code" + typeCode);
732 var spArr = str.split(",");
734 // Convert empty string to empty array
735 if (spArr.length === 1 && spArr[0].trim() === "")
738 spArr.forEach(checkInput);
744 // Processes the set of specified test sequences
745 function processInputs(testSequences) {
746 var testSet = new Array(testSequences.length);
747 for (var tt = 0; tt < testSequences.length; ++tt) {
748 var interval = testSequences[tt];
749 var bounds = interval.split("-");
750 var begin = convertCsvToArray(bounds[0]);
751 var end = convertCsvToArray(bounds[bounds.length - 1]);
753 // Check if interval is valid
754 if (!isArgExpSmallerOrEqual(begin, end)) {
755 wtu.error("GLSLConstructorTestsGenerator.processInputs(), interval not valid");
759 testSet[tt] = [ begin, end ];
767 * Returns list of test cases for vector types
768 * All combinations of arguments up to one unused argument of one component are tested
769 * @param {targetType} Name of target type to test the constructor expressions on
770 * @param {testSet} Set of intervals of argument sequences to test
772 function getConstructorTests(targetType, testSequences) {
773 // List of tests to return
776 // List of argument types
777 var testSet = processInputs(testSequences);
778 var testExp = { ix: 0, argExp: testSet[0][0] };
781 // Add one vertex shader test case and one fragment shader test case
782 testInfos = testInfos.concat(getVertexAndFragmentShaderTestCase(targetType, testExp.argExp));
784 // Generate next argument expression
785 testExp = getNextArgumentExpression(testExp, testSet);
787 while (testExp != null);
793 // Returns default test argument expression set
794 // For details on input format : see bottom of file
795 function getDefaultTestSet(targetType) {
801 // No arguments and all single argument expressions
804 // All two argument expressions with a scalar as second argument
807 // All two arguments expressions with a scalar as first argument
808 "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
810 // Three argument expression
818 // No arguments and all single argument expressions
821 // All two argument expressions with a scalar as second argument
824 // All two argument expressions with a scalar as first argument
825 "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
827 // All three argument expressions with two scalars as second and third argument
828 "s, s, s - m4, s, s",
830 // All three argument expressions with two scalars as first and second argument
831 "s, s, v2", "s, s, v3", "s, s, v4", "s, s, m2", "s, s, m3", "s, s, m4",
833 // Four argument expression
842 // No arguments and all single argument expressions
845 // All two argument expressions with a scalar as second argument
848 // All two argument expressions with a scalar as first argument
849 "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
851 // All three argument expressions with two scalars as second and third argument
852 "s, s, s - m4, s, s",
854 // All three argument expressions with two scalars as first and second argument
855 "s, s, v2", "s, s, v3", "s, s, v4", "s, s, m2", "s, s, m3", "s, s, m4",
857 // All four argument expressions with three scalars as second, third and fourth argument
858 "s, s, s, s - m4, s, s, s",
860 // All four argument expressions with three scalars as first, second and third argument
861 "s, s, s, v2", "s, s, s, v3", "s, s, s, v4", "s, s, s, m2", "s, s, s, m3", "s, s, s, m4",
863 // Five argument expression
870 // No arguments and all single argument expressions
873 // All two argument expressions with a scalar as second argument
876 // All two argument expressions with a scalar as first argument
877 "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
879 // Several argument sequences
880 "v4, s, v4", "v4, s, v3, v2", "v4, v4, v3, v2", "v4, v4, v4, v4", "v2, v2, v2, v2, v2", "v2, v2, v2, v2, v2, v2, v2, v2",
881 "v3, v3, v3", "v3, v3, v3, s", "v3, v3, v3, v3, v3, s", "v3, v3, v3, v3, v3, s, s",
889 getConstructorTests: getConstructorTests,
890 getDefaultTestSet: getDefaultTestSet
896 // Input is an array of intervals of argument types
897 // The generated test argument sequences are from (including) the lower interval boundary
898 // until (including) the upper boundary
899 // Coding and order of the different argument types :
908 // One interval is put in one string
909 // Low and high bound are separated by a dash.
910 // If there is no dash it is regarded as an interval of one expression
911 // The individual argument codes are separated by commas
912 // The individual arguments are incremented from left to right
913 // The left most argument is the one which is incremented first
914 // Once the left most arguments wraps the second argument is increased
916 // "s - m4" : All single arguments from scalar up to (including) mat4
917 // "m2, s - m4, s" : All two argument expressions with a matrix argument as first argument and a scalar as second argument
918 // " - m4, m4" : The empty argument, all one arguments and all two argument expressions
919 // "m2, s, v3, m4" : One 4 argument expression : mat2, scalar, vec3, mat4