1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
3 * ------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 goog
.provide('modules.shared.glsShaderLibrary');
23 goog
.require('framework.common.tcuTestCase');
24 goog
.require('framework.opengl.gluShaderUtil');
25 goog
.require('modules.shared.glsShaderLibraryCase');
27 goog
.scope(function() {
29 var glsShaderLibrary
= modules
.shared
.glsShaderLibrary
;
30 var tcuTestCase
= framework
.common
.tcuTestCase
;
31 var glsShaderLibraryCase
= modules
.shared
.glsShaderLibraryCase
;
32 var gluShaderUtil
= framework
.opengl
.gluShaderUtil
;
34 glsShaderLibrary
.generateTestCases = function() {
35 /** @type {glsShaderLibrary.Parser} */ var parser
= new glsShaderLibrary
.Parser();
37 /** @type {Object} */ var state
= tcuTestCase
.runner
;
38 var tree
= parser
.parse(state
.testFile
);
39 var rootTest
= tcuTestCase
.newTest(state
.testName
, 'Top level');
40 rootTest
.setChildren(tree
);
41 state
.setRoot(rootTest
);
44 bufferedLogToConsole(err
);
45 testFailed('Failed to parse shader test case file');
51 glsShaderLibrary
.processTestFile = function() {
52 if (glsShaderLibrary
.generateTestCases()) {
53 tcuTestCase
.runner
.runCallback(glsShaderLibraryCase
.runTestCases
);
55 tcuTestCase
.runner
.terminate();
59 glsShaderLibrary
.isWhitespace = function(value
) {
60 return /^[ \t\r\n]+$/.test(value
);
62 glsShaderLibrary
.isEOL = function(value
) {
63 return /^[\r\n]+$/.test(value
);
65 glsShaderLibrary
.isAlpha = function(value
) {
66 return /^[a-zA-Z]$/.test(value
);
68 glsShaderLibrary
.isNumeric = function(value
) {
69 return /^[0-9]$/.test(value
);
71 glsShaderLibrary
.isCaseNameChar = function(value
) {
72 return /^[a-zA-Z0-9_\-\.]$/.test(value
);
76 * Removes however many indents there are on the first line from all lines.
78 * @return {string} output
80 glsShaderLibrary
.removeExtraIndentation = function(str
) {
81 return glsShaderLibrary
.removeExtraIndentationArray(
82 str
.split(/\r\n|\r|\n/)
87 * Returns an array of strings without indentation.
88 * @param {Array<string>} arr
89 * @return {Array<string>} output
91 glsShaderLibrary
.removeExtraIndentationArray = function(arr
) {
92 /** @type {Array<string>} */ var output
= [];
96 /** @type {number} */ var numIndentChars
= 0;
97 for (var i
= 0; i
< arr
[0].length
&& glsShaderLibrary
.isWhitespace(arr
[0].charAt(i
)); ++i
) {
98 numIndentChars
+= arr
[0].charAt(i
) === '\t' ? 4 : 1;
101 for (var i
= 0; i
< arr
.length
; ++i
) {
102 /** @type {number} */ var removed
= 0;
103 /** @type {number} */ var j
;
104 // Some tests are indented inconsistently, so we have to check for non-whitespace characters here.
105 for (j
= 0; removed
< numIndentChars
&& j
< arr
[i
].length
&& glsShaderLibrary
.isWhitespace(arr
[i
].charAt(j
)); ++j
) {
106 removed
+= (arr
[i
].charAt(j
) === '\t' ? 4 : 1);
109 output
.push(arr
[i
].substr(j
, arr
[i
].length
- j
));
117 glsShaderLibrary
.de_assert = function(condition
) {
124 * @param {string} str
125 * @param {string} endstr end of string character
126 * @param {boolean=} trimFront trim leading whitespace
127 * @return {string} str
130 glsShaderLibrary
.parseStringLiteralHelper = function(str
, endstr
, trimFront
) {
131 trimFront
= trimFront
|| false;
133 /** @type {number} */ var index_end
= 0;
134 // isolate the string
136 index_end
= str
.indexOf(endstr
, index_end
+ 1);
137 } while (index_end
>= 0 && str
.charAt(index_end
- 1) === '\\');
139 if (index_end
<= 0) {
140 index_end
= str
.length
;
143 // strip quotes, replace \n and \t with nl and tabs respectively
144 str
= str
.substr(endstr
.length
, index_end
- endstr
.length
);
146 str
= str
.replace(/^\s*\n/, '');
149 while (str
[i
] != undefined) {
150 if (str
[i
] == '\\') {
151 switch (str
[i
+ 1]) {
161 result
+= str
[i
+ 1];
175 * glsShaderLibrary.Parser class
178 glsShaderLibrary
.Parser = function() {
183 * The Token constants
190 TOKEN_SHADER_SOURCE
: 3,
192 TOKEN_INT_LITERAL
: 4,
193 TOKEN_FLOAT_LITERAL
: 5,
212 TOKEN_FLOAT_VEC2
: 22,
213 TOKEN_FLOAT_VEC3
: 23,
214 TOKEN_FLOAT_VEC4
: 24,
215 TOKEN_FLOAT_MAT2
: 25,
216 TOKEN_FLOAT_MAT2X3
: 26,
217 TOKEN_FLOAT_MAT2X4
: 27,
218 TOKEN_FLOAT_MAT3X2
: 28,
219 TOKEN_FLOAT_MAT3
: 29,
220 TOKEN_FLOAT_MAT3X4
: 30,
221 TOKEN_FLOAT_MAT4X2
: 31,
222 TOKEN_FLOAT_MAT4X3
: 32,
223 TOKEN_FLOAT_MAT4
: 33,
243 TOKEN_VERTICAL_BAR
: 51,
244 TOKEN_SEMI_COLON
: 52,
245 TOKEN_LEFT_PAREN
: 53,
246 TOKEN_RIGHT_PAREN
: 54,
247 TOKEN_LEFT_BRACKET
: 55,
248 TOKEN_RIGHT_BRACKET
: 56,
249 TOKEN_LEFT_BRACE
: 57,
250 TOKEN_RIGHT_BRACE
: 58,
255 /** @type {string} */ var m_input
= '';
256 /** @type {number} */ var m_curPtr
= 0;
257 /** @type {number} */ var m_curToken
;// = Token.TOKEN_INVALID;
258 /** @type {string} */ var m_curTokenStr
= '';
260 /* function members */
261 this.parse = function(input
) {
266 m_curToken
= Token
.TOKEN_INVALID
;
270 /** @type {Array<tcuTestCase.DeqpTest>} */ var nodeList
= [];
274 if (m_curToken
=== Token
.TOKEN_CASE
) {
275 parseShaderCase(nodeList
);
276 } else if (m_curToken
=== Token
.TOKEN_GROUP
) {
277 parseShaderGroup(nodeList
);
278 } else if (m_curToken
=== Token
.TOKEN_EOF
) {
281 // throw Error("invalid token encountered at main level: '" + m_curTokenStr + "'");
282 testFailed("invalid token encountered at main level: '" + m_curTokenStr
+ "'");
283 tcuTestCase
.runner
.terminate();
293 * ensures that the token exists
294 * otherwise it returns the corresponding token's name depending on enum number value
296 * @return {string} name
298 var resolveTokenName = function(id
) {
299 for (var name
in Token
) {
300 if (Token
[name
] === id
) return name
;
302 return 'TOKEN_UNKNOWN';
306 * Throws an error which contains the passed string
307 * @param {string} errorStr that contains an error to notify
308 * @return {string} error
310 var parseError = function(errorStr
) {
312 throw 'glsShaderLibrary.Parser error: ' + errorStr
+ ' near ' + m_input
.substr(m_curPtr
, m_curPtr
+ 80);
316 * Converts string into float
317 * @param {string} str
320 var parseFloatLiteral = function(str
) {
321 return parseFloat(str
);
325 * Converts string into integer
326 * @param {string} str
329 var parseIntLiteral = function(str
) {
330 return parseInt(str
, 10);
332 var parseStringLiteral = function(str
) {
336 */ var endchar
= str
.substr(0, 1);
337 return glsShaderLibrary
.parseStringLiteralHelper(str
, endchar
);
339 var parseShaderSource = function(str
) {
340 // similar to parse literal, delimitors are two double quotes ("")
341 return glsShaderLibrary
.removeExtraIndentation(
342 glsShaderLibrary
.parseStringLiteralHelper(str
, '""', true)
346 var advanceTokenWorker = function() {
349 m_curPtr
+= m_curTokenStr
.length
;
351 // Reset token (for safety).
352 m_curToken
= Token
.TOKEN_INVALID
;
355 // Eat whitespace & comments while they last.
358 while (glsShaderLibrary
.isWhitespace(m_input
.charAt(m_curPtr
))) ++m_curPtr
;
360 // check for EOL comment
361 if (m_input
.charAt(m_curPtr
) === '#') {
362 // if m_input is to be an array of lines then this probably wont work very well
364 m_curPtr
< m_input
.length
&&
365 !glsShaderLibrary
.isEOL(m_input
.charAt(m_curPtr
))
373 if (m_curPtr
>= m_input
.length
) {
375 m_curToken
= Token
.TOKEN_EOF
;
376 m_curTokenStr
= '<EOF>';
378 } else if (glsShaderLibrary
.isAlpha(m_input
.charAt(m_curPtr
))) {
380 /** @type {number} */ var end
= m_curPtr
+ 1;
381 while (glsShaderLibrary
.isCaseNameChar(m_input
.charAt(end
))) ++end
;
383 m_curTokenStr
= m_input
.substr(m_curPtr
, end
- m_curPtr
);
385 m_curToken
= (function() {
386 // consider reimplementing with a binary search
387 switch (m_curTokenStr
) {
388 case 'true': return Token
.TOKEN_TRUE
;
389 case 'false': return Token
.TOKEN_FALSE
;
390 case 'desc': return Token
.TOKEN_DESC
;
391 case 'expect': return Token
.TOKEN_EXPECT
;
392 case 'group': return Token
.TOKEN_GROUP
;
393 case 'case': return Token
.TOKEN_CASE
;
394 case 'end': return Token
.TOKEN_END
;
395 case 'values': return Token
.TOKEN_VALUES
;
396 case 'both': return Token
.TOKEN_BOTH
;
397 case 'vertex': return Token
.TOKEN_VERTEX
;
398 case 'fragment': return Token
.TOKEN_FRAGMENT
;
399 case 'uniform': return Token
.TOKEN_UNIFORM
;
400 case 'input': return Token
.TOKEN_INPUT
;
401 case 'output': return Token
.TOKEN_OUTPUT
;
402 case 'float': return Token
.TOKEN_FLOAT
;
403 case 'vec2': return Token
.TOKEN_FLOAT_VEC2
;
404 case 'vec3': return Token
.TOKEN_FLOAT_VEC3
;
405 case 'vec4': return Token
.TOKEN_FLOAT_VEC4
;
406 case 'mat2': return Token
.TOKEN_FLOAT_MAT2
;
407 case 'mat2x3': return Token
.TOKEN_FLOAT_MAT2X3
;
408 case 'mat2x4': return Token
.TOKEN_FLOAT_MAT2X4
;
409 case 'mat3x2': return Token
.TOKEN_FLOAT_MAT3X2
;
410 case 'mat3': return Token
.TOKEN_FLOAT_MAT3
;
411 case 'mat3x4': return Token
.TOKEN_FLOAT_MAT3X4
;
412 case 'mat4x2': return Token
.TOKEN_FLOAT_MAT4X2
;
413 case 'mat4x3': return Token
.TOKEN_FLOAT_MAT4X3
;
414 case 'mat4': return Token
.TOKEN_FLOAT_MAT4
;
415 case 'int': return Token
.TOKEN_INT
;
416 case 'ivec2': return Token
.TOKEN_INT_VEC2
;
417 case 'ivec3': return Token
.TOKEN_INT_VEC3
;
418 case 'ivec4': return Token
.TOKEN_INT_VEC4
;
419 case 'uint': return Token
.TOKEN_UINT
;
420 case 'uvec2': return Token
.TOKEN_UINT_VEC2
;
421 case 'uvec3': return Token
.TOKEN_UINT_VEC3
;
422 case 'uvec4': return Token
.TOKEN_UINT_VEC4
;
423 case 'bool': return Token
.TOKEN_BOOL
;
424 case 'bvec2': return Token
.TOKEN_BOOL_VEC2
;
425 case 'bvec3': return Token
.TOKEN_BOOL_VEC3
;
426 case 'bvec4': return Token
.TOKEN_BOOL_VEC4
;
427 case 'version': return Token
.TOKEN_VERSION
;
428 default: return Token
.TOKEN_IDENTIFIER
;
432 } else if (glsShaderLibrary
.isNumeric(m_input
.charAt(m_curPtr
))) {
434 /** @type {number} */ var p
= m_curPtr
;
435 while (glsShaderLibrary
.isNumeric(m_input
.charAt(p
))) ++p
;
437 if (m_input
.charAt(p
) === '.') { // float
440 while (glsShaderLibrary
.isNumeric(m_input
.charAt(p
))) ++p
;
442 if (m_input
.charAt(p
) === 'e' || m_input
.charAt(p
) === 'E') {
445 if (m_input
.charAt(p
) === '+' || m_input
.charAt(p
) === '-') ++p
;
447 glsShaderLibrary
.de_assert(p
< m_input
.length
&& glsShaderLibrary
.isNumeric(m_input
.charAt(p
)));
448 while (glsShaderLibrary
.isNumeric(m_input
.charAt(p
))) ++p
;
452 m_curToken
= Token
.TOKEN_FLOAT_LITERAL
;
453 m_curTokenStr
= m_input
.substr(m_curPtr
, p
- m_curPtr
);
457 m_curToken
= Token
.TOKEN_INT_LITERAL
;
458 m_curTokenStr
= m_input
.substr(m_curPtr
, p
- m_curPtr
);
462 } else if (m_input
.charAt(m_curPtr
) === '"' && m_input
.charAt(m_curPtr
+ 1) === '"') { // shader source
464 var p
= m_curPtr
+ 2;
466 while (m_input
.charAt(p
) != '"' || m_input
.charAt(p
+ 1) != '"') {
467 glsShaderLibrary
.de_assert(p
< m_input
.length
);
468 if (m_input
.charAt(p
) === '\\') {
469 glsShaderLibrary
.de_assert(p
+ 1 < m_input
.length
);
477 m_curToken
= Token
.TOKEN_SHADER_SOURCE
;
478 m_curTokenStr
= m_input
.substr(m_curPtr
, p
- m_curPtr
);
480 } else if (m_input
.charAt(m_curPtr
) === '"' || m_input
.charAt(m_curPtr
) === "'") {
482 /** @type {string} */ var delimitor
= m_input
.charAt(m_curPtr
);
483 var p
= m_curPtr
+ 1;
485 while (m_input
.charAt(p
) != delimitor
) {
487 glsShaderLibrary
.de_assert(p
< m_input
.length
);
488 if (m_input
.charAt(p
) === '\\') {
489 glsShaderLibrary
.de_assert(p
+ 1 < m_input
.length
);
498 m_curToken
= Token
.TOKEN_STRING
;
499 m_curTokenStr
= m_input
.substr(m_curPtr
, p
- m_curPtr
);
503 m_curTokenStr
= m_input
.charAt(m_curPtr
);
504 m_curToken
= (function() {
505 // consider reimplementing with a binary search
506 switch (m_curTokenStr
) {
507 case '=': return Token
.TOKEN_ASSIGN
;
508 case '+': return Token
.TOKEN_PLUS
;
509 case '-': return Token
.TOKEN_MINUS
;
510 case ',': return Token
.TOKEN_COMMA
;
511 case '|': return Token
.TOKEN_VERTICAL_BAR
;
512 case ';': return Token
.TOKEN_SEMI_COLON
;
513 case '(': return Token
.TOKEN_LEFT_PAREN
;
514 case ')': return Token
.TOKEN_RIGHT_PAREN
;
515 case '[': return Token
.TOKEN_LEFT_BRACKET
;
516 case ']': return Token
.TOKEN_RIGHT_BRACKET
;
517 case '{': return Token
.TOKEN_LEFT_BRACE
;
518 case '}': return Token
.TOKEN_RIGHT_BRACE
;
520 default: return Token
.TOKEN_INVALID
;
529 * @return {Object.<number, string, string>}
531 var advanceTokenTester = function(input
, current_index
) {
533 m_curPtr
= current_index
;
535 advanceTokenWorker();
537 /** @type {number} */ idType
: m_curToken
,
538 /** @type {string} */ name
: resolveTokenName(m_curToken
),
539 /** @type {string} */ value
: m_curTokenStr
544 * @param {Token=} tokenAssumed
546 var advanceToken = function(tokenAssumed
) {
547 if (typeof(tokenAssumed
) !== 'undefined') {
548 assumeToken(tokenAssumed
);
550 advanceTokenWorker();
552 var assumeToken = function(token
) {
554 if (m_curToken
!= token
) {
556 /** @type {string} */ var msg
= "unexpected token '" + m_curTokenStr
+ "', expecting '" + getTokenName(token
) + "'";
557 throw Error('Parse Error. ' + msg
+ ' near ' + m_curPtr
+ ' ...');
560 var mapDataTypeToken = function(token
) {
562 case Token
.TOKEN_FLOAT
: return gluShaderUtil
.DataType
.FLOAT
;
563 case Token
.TOKEN_FLOAT_VEC2
: return gluShaderUtil
.DataType
.FLOAT_VEC2
;
564 case Token
.TOKEN_FLOAT_VEC3
: return gluShaderUtil
.DataType
.FLOAT_VEC3
;
565 case Token
.TOKEN_FLOAT_VEC4
: return gluShaderUtil
.DataType
.FLOAT_VEC4
;
566 case Token
.TOKEN_FLOAT_MAT2
: return gluShaderUtil
.DataType
.FLOAT_MAT2
;
567 case Token
.TOKEN_FLOAT_MAT2X3
: return gluShaderUtil
.DataType
.FLOAT_MAT2X3
;
568 case Token
.TOKEN_FLOAT_MAT2X4
: return gluShaderUtil
.DataType
.FLOAT_MAT2X4
;
569 case Token
.TOKEN_FLOAT_MAT3X2
: return gluShaderUtil
.DataType
.FLOAT_MAT3X2
;
570 case Token
.TOKEN_FLOAT_MAT3
: return gluShaderUtil
.DataType
.FLOAT_MAT3
;
571 case Token
.TOKEN_FLOAT_MAT3X4
: return gluShaderUtil
.DataType
.FLOAT_MAT3X4
;
572 case Token
.TOKEN_FLOAT_MAT4X2
: return gluShaderUtil
.DataType
.FLOAT_MAT4X2
;
573 case Token
.TOKEN_FLOAT_MAT4X3
: return gluShaderUtil
.DataType
.FLOAT_MAT4X3
;
574 case Token
.TOKEN_FLOAT_MAT4
: return gluShaderUtil
.DataType
.FLOAT_MAT4
;
575 case Token
.TOKEN_INT
: return gluShaderUtil
.DataType
.INT
;
576 case Token
.TOKEN_INT_VEC2
: return gluShaderUtil
.DataType
.INT_VEC2
;
577 case Token
.TOKEN_INT_VEC3
: return gluShaderUtil
.DataType
.INT_VEC3
;
578 case Token
.TOKEN_INT_VEC4
: return gluShaderUtil
.DataType
.INT_VEC4
;
579 case Token
.TOKEN_UINT
: return gluShaderUtil
.DataType
.UINT
;
580 case Token
.TOKEN_UINT_VEC2
: return gluShaderUtil
.DataType
.UINT_VEC2
;
581 case Token
.TOKEN_UINT_VEC3
: return gluShaderUtil
.DataType
.UINT_VEC3
;
582 case Token
.TOKEN_UINT_VEC4
: return gluShaderUtil
.DataType
.UINT_VEC4
;
583 case Token
.TOKEN_BOOL
: return gluShaderUtil
.DataType
.BOOL
;
584 case Token
.TOKEN_BOOL_VEC2
: return gluShaderUtil
.DataType
.BOOL_VEC2
;
585 case Token
.TOKEN_BOOL_VEC3
: return gluShaderUtil
.DataType
.BOOL_VEC3
;
586 case Token
.TOKEN_BOOL_VEC4
: return gluShaderUtil
.DataType
.BOOL_VEC4
;
587 default: return gluShaderUtil
.DataType
.INVALID
;
592 * Returns the corresponding token's name depending on enum number value
593 * @param {number} token
596 var getTokenName = function(token
) {
598 case Token
.TOKEN_INVALID
: return '<invalid>';
599 case Token
.TOKEN_EOF
: return '<eof>';
600 case Token
.TOKEN_STRING
: return '<string>';
601 case Token
.TOKEN_SHADER_SOURCE
: return 'source';
603 case Token
.TOKEN_INT_LITERAL
: return '<int>';
604 case Token
.TOKEN_FLOAT_LITERAL
: return '<float>';
607 case Token
.TOKEN_IDENTIFIER
: return '<identifier>';
608 case Token
.TOKEN_TRUE
: return 'true';
609 case Token
.TOKEN_FALSE
: return 'false';
610 case Token
.TOKEN_DESC
: return 'desc';
611 case Token
.TOKEN_EXPECT
: return 'expect';
612 case Token
.TOKEN_GROUP
: return 'group';
613 case Token
.TOKEN_CASE
: return 'case';
614 case Token
.TOKEN_END
: return 'end';
615 case Token
.TOKEN_VALUES
: return 'values';
616 case Token
.TOKEN_BOTH
: return 'both';
617 case Token
.TOKEN_VERTEX
: return 'vertex';
618 case Token
.TOKEN_FRAGMENT
: return 'fragment';
619 case Token
.TOKEN_UNIFORM
: return 'uniform';
620 case Token
.TOKEN_INPUT
: return 'input';
621 case Token
.TOKEN_OUTPUT
: return 'output';
622 case Token
.TOKEN_FLOAT
: return 'float';
623 case Token
.TOKEN_FLOAT_VEC2
: return 'vec2';
624 case Token
.TOKEN_FLOAT_VEC3
: return 'vec3';
625 case Token
.TOKEN_FLOAT_VEC4
: return 'vec4';
626 case Token
.TOKEN_FLOAT_MAT2
: return 'mat2';
627 case Token
.TOKEN_FLOAT_MAT2X3
: return 'mat2x3';
628 case Token
.TOKEN_FLOAT_MAT2X4
: return 'mat2x4';
629 case Token
.TOKEN_FLOAT_MAT3X2
: return 'mat3x2';
630 case Token
.TOKEN_FLOAT_MAT3
: return 'mat3';
631 case Token
.TOKEN_FLOAT_MAT3X4
: return 'mat3x4';
632 case Token
.TOKEN_FLOAT_MAT4X2
: return 'mat4x2';
633 case Token
.TOKEN_FLOAT_MAT4X3
: return 'mat4x3';
634 case Token
.TOKEN_FLOAT_MAT4
: return 'mat4';
635 case Token
.TOKEN_INT
: return 'int';
636 case Token
.TOKEN_INT_VEC2
: return 'ivec2';
637 case Token
.TOKEN_INT_VEC3
: return 'ivec3';
638 case Token
.TOKEN_INT_VEC4
: return 'ivec4';
639 case Token
.TOKEN_UINT
: return 'uint';
640 case Token
.TOKEN_UINT_VEC2
: return 'uvec2';
641 case Token
.TOKEN_UINT_VEC3
: return 'uvec3';
642 case Token
.TOKEN_UINT_VEC4
: return 'uvec4';
643 case Token
.TOKEN_BOOL
: return 'bool';
644 case Token
.TOKEN_BOOL_VEC2
: return 'bvec2';
645 case Token
.TOKEN_BOOL_VEC3
: return 'bvec3';
646 case Token
.TOKEN_BOOL_VEC4
: return 'bvec4';
648 case Token
.TOKEN_ASSIGN
: return '=';
649 case Token
.TOKEN_PLUS
: return '+';
650 case Token
.TOKEN_MINUS
: return '-';
651 case Token
.TOKEN_COMMA
: return ',';
652 case Token
.TOKEN_VERTICAL_BAR
: return '|';
653 case Token
.TOKEN_SEMI_COLON
: return ';';
654 case Token
.TOKEN_LEFT_PAREN
: return '(';
655 case Token
.TOKEN_RIGHT_PAREN
: return ')';
656 case Token
.TOKEN_LEFT_BRACKET
: return '[';
657 case Token
.TOKEN_RIGHT_BRACKET
: return ']';
658 case Token
.TOKEN_LEFT_BRACE
: return ' {';
659 case Token
.TOKEN_RIGHT_BRACE
: return '}';
661 default: return '<unknown>';
666 * @param {?gluShaderUtil.DataType} expectedDataType
667 * @param {Object} result
669 var parseValueElement = function(expectedDataType
, result
) {
670 /** @type {?string} */ var scalarType
= null;
671 /** @type {number} */ var scalarSize
= 0;
672 if (expectedDataType
) {
673 scalarType
= gluShaderUtil
.getDataTypeScalarType(expectedDataType
);
674 scalarSize
= gluShaderUtil
.getDataTypeScalarSize(expectedDataType
);
677 /** @type {Array<number>} */ var elems
= [];
679 if (scalarSize
> 1) {
680 glsShaderLibrary
.de_assert(mapDataTypeToken(m_curToken
) === expectedDataType
);
681 advanceToken(); // data type(float, vec2, etc.)
682 advanceToken(Token
.TOKEN_LEFT_PAREN
);
685 for (var i
= 0; i
< scalarSize
; ++i
) {
686 if (scalarType
=== 'float') {
688 /** @type {number} */ var signMult
= 1.0;
689 if (m_curToken
=== Token
.TOKEN_MINUS
) {
694 assumeToken(Token
.TOKEN_FLOAT_LITERAL
);
695 elems
.push(signMult
* parseFloatLiteral(m_curTokenStr
));
696 advanceToken(Token
.TOKEN_FLOAT_LITERAL
);
698 } else if (scalarType
=== 'int' || scalarType
=== 'uint') {
701 if (m_curToken
=== Token
.TOKEN_MINUS
) {
706 assumeToken(Token
.TOKEN_INT_LITERAL
);
707 elems
.push(signMult
* parseIntLiteral(m_curTokenStr
));
708 advanceToken(Token
.TOKEN_INT_LITERAL
);
712 glsShaderLibrary
.de_assert(scalarType
=== 'bool');
713 elems
.push(m_curToken
=== Token
.TOKEN_TRUE
);
714 if (m_curToken
!= Token
.TOKEN_TRUE
&& m_curToken
!= Token
.TOKEN_FALSE
) {
715 throw Error('unexpected token, expecting bool: ' + m_curTokenStr
);
717 advanceToken(); // true/false
721 if (i
!= (scalarSize
- 1)) {
722 advanceToken(Token
.TOKEN_COMMA
);
726 if (scalarSize
> 1) {
727 advanceToken(Token
.TOKEN_RIGHT_PAREN
);
730 for (var i
= 0; i
< elems
.length
; i
++)
731 result
.elements
.push(elems
[i
]);
736 * @param {Object.<Array, number>} valueBlock
738 var parseValue = function(valueBlock
) {
744 /** @type {?gluShaderUtil.DataType} */ dataType
: null,
745 /** @type {?glsShaderLibraryCase.shaderCase} */ storageType
: null,
746 /** @type {?string} */ valueName
: null,
747 /** @type {Array} */ elements
: []
751 switch (m_curToken
) {
752 case Token
.TOKEN_UNIFORM
:
753 result
.storageType
= glsShaderLibraryCase
.shaderCase
.STORAGE_UNIFORM
;
755 case Token
.TOKEN_INPUT
:
756 result
.storageType
= glsShaderLibraryCase
.shaderCase
.STORAGE_INPUT
;
758 case Token
.TOKEN_OUTPUT
:
759 result
.storageType
= glsShaderLibraryCase
.shaderCase
.STORAGE_OUTPUT
;
762 throw Error('unexpected token encountered when parsing value classifier');
768 result
.dataType
= mapDataTypeToken(m_curToken
);
769 if (result
.dataType
=== gluShaderUtil
.DataType
.INVALID
) {
770 throw Error('unexpected token when parsing value data type: ' + m_curTokenStr
);
775 if (m_curToken
=== Token
.TOKEN_IDENTIFIER
) {
776 result
.valueName
= m_curTokenStr
;
777 } else if (m_curToken
=== Token
.TOKEN_STRING
) {
778 result
.valueName
= parseStringLiteral(m_curTokenStr
);
780 throw Error('unexpected token when parsing value name: ' + m_curTokenStr
);
784 // parse assignment operator.
785 advanceToken(Token
.TOKEN_ASSIGN
);
787 // parse actual value
788 if (m_curToken
=== Token
.TOKEN_LEFT_BRACKET
) { // value list
789 advanceToken(Token
.TOKEN_LEFT_BRACKET
);
790 result
.arrayLength
= 0;
793 parseValueElement(result
.dataType
, result
);
794 result
.arrayLength
+= 1;
796 if (m_curToken
=== Token
.TOKEN_RIGHT_BRACKET
) {
798 } else if (m_curToken
=== Token
.TOKEN_VERTICAL_BAR
) { // pipe?
802 throw Error('unexpected token in value element array: ' + m_curTokenStr
);
806 advanceToken(Token
.TOKEN_RIGHT_BRACKET
);
808 } else { // arrays, single elements
809 parseValueElement(result
.dataType
, result
);
810 result
.arrayLength
= 1;
813 advanceToken(Token
.TOKEN_SEMI_COLON
);
815 valueBlock
.values
.push(result
);
820 * @param {Object.<Array, number>} valueBlock
822 var parseValueBlock = function(valueBlock
) {
824 advanceToken(Token
.TOKEN_VALUES
);
825 advanceToken(Token
.TOKEN_LEFT_BRACE
);
829 m_curToken
=== Token
.TOKEN_UNIFORM
||
830 m_curToken
=== Token
.TOKEN_INPUT
||
831 m_curToken
=== Token
.TOKEN_OUTPUT
833 parseValue(valueBlock
);
834 } else if (m_curToken
=== Token
.TOKEN_RIGHT_BRACE
) {
837 throw Error('unexpected( token when parsing a value block: ' + m_curTokenStr
);
841 advanceToken(Token
.TOKEN_RIGHT_BRACE
);
843 /** @type {number} */ var arrayLength
= 1;
844 // compute combined array length of value block.
845 for (var i
= 0; i
< valueBlock
.values
.length
; ++i
) {
846 if (valueBlock
.values
[i
].arrayLength
> 1) {
847 glsShaderLibrary
.de_assert(arrayLength
=== 1 || arrayLength
=== valueBlock
.values
[i
].arrayLength
);
848 arrayLength
= valueBlock
.values
[i
].arrayLength
;
852 valueBlock
.arrayLength
= arrayLength
;
857 * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList
859 var parseShaderCase = function(shaderNodeList
) {
862 advanceToken(Token
.TOKEN_CASE
);
868 var caseName
= m_curTokenStr
;
869 advanceToken(); // \note [pyry] All token types are allowed here.
872 * @type {Array<Object>}
875 var valueBlockList
= [];
877 /** TODO: Should the default version be defined elsewhere? */
878 /** @type {string} */ var version
= '100';
879 /** @type {number} */ var expectResult
= glsShaderLibraryCase
.expectResult
.EXPECT_PASS
;
880 /** @type {string} */ var description
;
881 /** @type {string} */ var bothSource
= '';
882 /** @type {string} */ var vertexSource
= '';
883 /** @type {string} */ var fragmentSource
= '';
887 if (m_curToken
=== Token
.TOKEN_END
) {
891 } else if (m_curToken
=== Token
.TOKEN_DESC
) {
894 assumeToken(Token
.TOKEN_STRING
);
896 description
= parseStringLiteral(m_curTokenStr
);
899 } else if (m_curToken
=== Token
.TOKEN_EXPECT
) {
902 assumeToken(Token
.TOKEN_IDENTIFIER
);
904 expectResult
= (function(token
) {
906 case 'pass': return glsShaderLibraryCase
.expectResult
.EXPECT_PASS
;
907 case 'compile_fail': return glsShaderLibraryCase
.expectResult
.EXPECT_COMPILE_FAIL
;
908 case 'link_fail': return glsShaderLibraryCase
.expectResult
.EXPECT_LINK_FAIL
;
909 case 'compile_or_link_fail': return glsShaderLibraryCase
.expectResult
.EXPECT_COMPILE_LINK_FAIL
;
910 case 'build_successful': return glsShaderLibraryCase
.expectResult
.EXPECT_BUILD_SUCCESSFUL
;
912 throw Error('invalid expected result value: ' + m_curTokenStr
);
918 } else if (m_curToken
=== Token
.TOKEN_VALUES
) {
920 /** @type {Object.<Array, number>} */ var block
= glsShaderLibraryCase
.genValueBlock();
921 parseValueBlock(block
);
922 valueBlockList
.push(block
);
925 m_curToken
=== Token
.TOKEN_BOTH
||
926 m_curToken
=== Token
.TOKEN_VERTEX
||
927 m_curToken
=== Token
.TOKEN_FRAGMENT
930 /** @type {number} */ var token
= m_curToken
;
932 assumeToken(Token
.TOKEN_SHADER_SOURCE
);
933 /** @type {string} */ var source
= parseShaderSource(m_curTokenStr
);
937 case Token
.TOKEN_BOTH
: bothSource
= source
; break;
938 case Token
.TOKEN_VERTEX
: vertexSource
= source
; break;
939 case Token
.TOKEN_FRAGMENT
: fragmentSource
= source
; break;
940 default: glsShaderLibrary
.de_assert(false); break;
943 } else if (m_curToken
=== Token
.TOKEN_VERSION
) {
947 /** @type {number} */ var versionNum
= 0;
948 /** @type {string} */ var postfix
= '';
950 assumeToken(Token
.TOKEN_INT_LITERAL
);
951 versionNum
= parseIntLiteral(m_curTokenStr
);
954 if (m_curToken
=== Token
.TOKEN_IDENTIFIER
) {
955 postfix
= m_curTokenStr
;
959 // TODO: need to fix these constants, we dont have glu
960 if (versionNum
=== 100 && postfix
=== 'es') version
= '100';
961 else if (versionNum
=== 300 && postfix
=== 'es') version
= '300 es';
962 else if (versionNum
=== 310 && postfix
=== 'es') version
= '310 es';
963 else if (versionNum
=== 130) version
= '130';
964 else if (versionNum
=== 140) version
= '140';
965 else if (versionNum
=== 150) version
= '150';
966 else if (versionNum
=== 330) version
= '330';
967 else if (versionNum
=== 400) version
= '400';
968 else if (versionNum
=== 410) version
= '410';
969 else if (versionNum
=== 420) version
= '420';
970 else if (versionNum
=== 430) version
= '430';
971 else if (versionNum
=== 440) version
= '440';
972 else if (versionNum
=== 450) version
= '450';
974 throw Error('Unknown GLSL version');
978 throw Error('unexpected token while parsing shader case: ' + m_curTokenStr
);
983 advanceToken(Token
.TOKEN_END
); // case end
987 * @param {?string} vert
988 * @param {?string} frag
989 * @param {glsShaderLibraryCase.caseType} type
992 var getShaderSpec = function(vert
, frag
, type
) {
994 /** @type {glsShaderLibraryCase.expectResult} */ expectResult
: expectResult
,
995 /** @type {glsShaderLibraryCase.caseType} */ caseType
: type
,
996 /** @type {Array<Object>} */ valueBlockList
: valueBlockList
,
997 /** @type {string} */ targetVersion
: version
,
998 /** @type {?string} */ vertexSource
: vert
,
999 /** @type {?string} */ fragmentSource
: frag
1002 getShaderSpec
.bind(this);
1004 if (bothSource
.length
) {
1006 glsShaderLibrary
.de_assert(!vertexSource
);
1007 glsShaderLibrary
.de_assert(!fragmentSource
);
1009 shaderNodeList
.push(tcuTestCase
.newTest(caseName
+ '_vertex', description
, getShaderSpec(bothSource
, null,
1010 glsShaderLibraryCase
.caseType
.CASETYPE_VERTEX_ONLY
)));
1011 shaderNodeList
.push(tcuTestCase
.newTest(caseName
+ '_fragment', description
, getShaderSpec(null, bothSource
,
1012 glsShaderLibraryCase
.caseType
.CASETYPE_FRAGMENT_ONLY
)));
1015 glsShaderLibrary
.de_assert(vertexSource
);
1016 glsShaderLibrary
.de_assert(fragmentSource
);
1018 shaderNodeList
.push(tcuTestCase
.newTest(caseName
, description
, getShaderSpec(vertexSource
, fragmentSource
,
1019 glsShaderLibraryCase
.caseType
.CASETYPE_COMPLETE
)));
1024 * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList
1026 var parseShaderGroup = function(shaderNodeList
) {
1029 advanceToken(Token
.TOKEN_GROUP
);
1033 */ var name
= m_curTokenStr
;
1034 advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group.
1036 // Parse description.
1037 assumeToken(Token
.TOKEN_STRING
);
1038 /** @type {string} */ var description
= parseStringLiteral(m_curTokenStr
);
1039 advanceToken(Token
.TOKEN_STRING
);
1041 /** @type {Array<tcuTestCase.DeqpTest>} */ var children
= [];
1045 if (m_curToken
=== Token
.TOKEN_END
) {
1047 } else if (m_curToken
=== Token
.TOKEN_GROUP
) {
1048 parseShaderGroup(children
);
1049 } else if (m_curToken
=== Token
.TOKEN_CASE
) {
1050 parseShaderCase(children
);
1052 testFailed('unexpected token while parsing shader group: ' + m_curTokenStr
);
1053 tcuTestCase
.runner
.terminate();
1058 advanceToken(Token
.TOKEN_END
); // group end
1060 /** @type {tcuTestCase.DeqpTest} */ var groupNode
= tcuTestCase
.newTest(name
, description
, null);
1061 groupNode
.setChildren(children
);
1063 shaderNodeList
.push(groupNode
);
1067 // uncomment to expose private functions
1072 parseError
: parseError
,
1073 parseFloatLiteral
: parseFloatLiteral
,
1074 parseIntLiteral
: parseIntLiteral
,
1075 parseStringLiteral
: parseStringLiteral
,
1076 parseShaderSource
: parseShaderSource
,
1077 advanceTokenTester
: advanceTokenTester
,
1078 assumeToken
: assumeToken
,
1079 mapDataTypeToken
: mapDataTypeToken
,
1080 getTokenName
: getTokenName
,
1084 parseValueElement
: parseValueElement
,
1085 parseValue
: parseValue
,
1086 parseValueBlock
: parseValueBlock
,
1087 parseShaderCase
: parseShaderCase
,
1088 parseShaderGroup
: parseShaderGroup
,
1097 * Parse the test file and execute the test cases
1098 * @param {string} testName Name of the test file (without extension)
1099 * @param {string} filter Optional filter. Common substring of the names of the tests that should be glsShaderLibrary.run.
1101 glsShaderLibrary
.run = function(testName
, filter
) {
1102 WebGLTestUtils
.loadTextFileAsync(testName
+ '.test', function(success
, content
) {
1104 tcuTestCase
.runner
.testFile
= content
;
1105 tcuTestCase
.runner
.testName
= testName
;
1106 tcuTestCase
.runner
.runCallback(glsShaderLibrary
.processTestFile
);
1108 testFailed('Failed to load test file: ' + testName
);
1109 tcuTestCase
.runner
.terminate();