| <!-- |
| Copyright (c) 2019 The Khronos Group Inc. |
| Use of this source code is governed by an MIT-style license that can be |
| found in the LICENSE.txt file. |
| --> |
| |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>GLSL tricky loop conditions and loop expressions</title> |
| <link rel="stylesheet" href="../../resources/js-test-style.css"/> |
| <script src="../../js/js-test-pre.js"></script> |
| <script src="../../js/webgl-test-utils.js"></script> |
| <script src="../../js/glsl-conformance-test.js"></script> |
| </head> |
| <body> |
| <div id="description"></div> |
| <div id="console"></div> |
| <!-- |
| Some tricky shader expressions might be subject to syntax tree transformations that need to create |
| new statements. Ensure that these expressions also work inside loop conditions and loop expressions. |
| --> |
| <script type="application/javascript"> |
| "use strict"; |
| description("Indexing complex array expressions"); |
| debug(""); |
| |
| // All the templates run the given sequence: |
| // 1. loopExpression or loopCondition |
| // 2. loopContents |
| // 3. Break loop if it's done loopIterations iterations, else go back to 1. |
| |
| var forLoopExpressionTemplate = [ |
| '#version 300 es', |
| 'precision mediump float;', |
| 'out vec4 color;', |
| '$(globalScopePrefix)', |
| 'void main() {', |
| '$(mainPrefix)', |
| ' for (int i = 0; true; $(loopExpression))', |
| ' {', |
| ' ++i;', |
| ' if (i > 1) {', |
| ' $(loopContents)', |
| ' if (i > $(loopIterations)) { break; }', |
| ' }', |
| ' }', |
| ' color = ($(passCondition)) ? vec4(0, 1.0, 0, 1.0) : vec4(1.0, 0, 0, 1.0);', |
| '}' |
| ].join('\n'); |
| |
| var forLoopConditionTemplate = [ |
| '#version 300 es', |
| 'precision mediump float;', |
| 'out vec4 color;', |
| '$(globalScopePrefix)', |
| 'void main() {', |
| '$(mainPrefix)', |
| ' for (int i = 1; $(loopCondition); ++i)', |
| ' {', |
| ' $(loopContents)', |
| ' if (i >= $(loopIterations)) { break; }', |
| ' }', |
| ' color = ($(passCondition)) ? vec4(0, 1.0, 0, 1.0) : vec4(1.0, 0, 0, 1.0);', |
| '}' |
| ].join('\n'); |
| |
| var whileLoopConditionTemplate = [ |
| '#version 300 es', |
| 'precision mediump float;', |
| 'out vec4 color;', |
| '$(globalScopePrefix)', |
| 'void main() {', |
| '$(mainPrefix)', |
| ' int i = 0;', |
| ' while ($(loopCondition))', |
| ' {', |
| ' $(loopContents)', |
| ' ++i;', |
| ' if (i >= $(loopIterations)) { break; }', |
| ' }', |
| ' color = ($(passCondition)) ? vec4(0, 1.0, 0, 1.0) : vec4(1.0, 0, 0, 1.0);', |
| '}' |
| ].join('\n'); |
| |
| var doWhileLoopConditionTemplate = [ |
| '#version 300 es', |
| 'precision mediump float;', |
| 'out vec4 color;', |
| '$(globalScopePrefix)', |
| 'void main() {', |
| '$(mainPrefix)', |
| ' int i = 0;', |
| // Run the loop condition one extra time to make the different test types behave the same |
| ' $(loopCondition);', |
| ' do {', |
| ' $(loopContents)', |
| ' ++i;', |
| ' if (i >= $(loopIterations)) { break; }', |
| ' }', |
| ' while ($(loopCondition));', |
| ' color = ($(passCondition)) ? vec4(0, 1.0, 0, 1.0) : vec4(1.0, 0, 0, 1.0);', |
| '}' |
| ].join('\n'); |
| |
| var testDataList = [ |
| { |
| description: 'indexing an array assignment', |
| globalScopePrefix: '', |
| mainPrefix: [ |
| 'float a[2] = float[2](0.0, 0.0);', |
| 'float b[2] = float[2](2.0, 1.0);', |
| 'float c = 0.0;' |
| ].join('\n'), |
| loopExpression: 'c = (a = b)[0]', |
| loopCondition: 'bool((c = (a = b)[0]) + 1.0)', |
| loopContents: 'b[0] += 1.0;', |
| loopIterations: 3, |
| passCondition: 'abs(c - 4.0) < 0.01' |
| }, |
| { |
| description: 'indexing a function returning an array', |
| globalScopePrefix: [ |
| 'int sideEffectCounter = 0;', |
| 'float[2] functionReturnArray() {', |
| ' ++sideEffectCounter;', |
| ' return float[2](float(sideEffectCounter), 1.0);', |
| '}' |
| ].join('\n'), |
| mainPrefix: 'float c = 0.0;', |
| loopExpression: 'c = functionReturnArray()[0]', |
| loopCondition: 'bool(c = functionReturnArray()[0])', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'abs(c - 3.0) < 0.01 && sideEffectCounter == 3' |
| }, |
| { |
| description: 'indexing an array constructor', |
| globalScopePrefix: '', |
| mainPrefix: 'int c = 0;', |
| loopExpression: 'c = (int[2](c + 1, c + 2))[1]', |
| loopCondition: 'bool(c = (int[2](c + 1, c + 2))[1])', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'c == 6' |
| }, |
| { |
| description: 'indexing an array constructor inside a sequence operator', |
| globalScopePrefix: [ |
| 'int sideEffectCounter = 0;', |
| 'int func() {', |
| ' sideEffectCounter++;', |
| ' return sideEffectCounter;', |
| '}' |
| ].join('\n'), |
| mainPrefix: 'int c = 0;', |
| loopExpression: 'c = (func(), (int[2](c + 1, c + 2))[1])', |
| loopCondition: 'bool(c = (func(), (int[2](c + 1, c + 2))[1]))', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'c == 6 && sideEffectCounter == 3' |
| }, |
| { |
| description: 'dynamic indexing of a vector', |
| globalScopePrefix: '', |
| mainPrefix: [ |
| 'vec4 v = vec4(1.0, 2.0, 3.0, 4.0);', |
| 'float c = 0.0;', |
| 'int j = 0;' |
| ].join('\n'), |
| loopExpression: 'c = v[j]', |
| loopCondition: 'bool(c = v[j])', |
| loopContents: '++j;', |
| loopIterations: 3, |
| passCondition: 'abs(c - 3.0) < 0.01' |
| }, |
| { |
| description: 'short-circuiting operator', |
| globalScopePrefix: [ |
| 'int sideEffectCounter = 0;', |
| 'bool func() {', |
| ' sideEffectCounter++;', |
| ' return sideEffectCounter > 0;', |
| '}' |
| ].join('\n'), |
| mainPrefix: '', |
| loopExpression: 'func() && func()', |
| loopCondition: 'func() && func()', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'sideEffectCounter == 6' |
| }, |
| { |
| description: 'short-circuiting operator', |
| globalScopePrefix: [ |
| 'int sideEffectCounter = 0;', |
| 'bool func() {', |
| ' sideEffectCounter++;', |
| ' return sideEffectCounter > 0;', |
| '}' |
| ].join('\n'), |
| mainPrefix: '', |
| loopExpression: 'func() || func()', |
| loopCondition: 'func() || func()', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'sideEffectCounter == 3' |
| }, |
| { |
| description: 'short-circuiting operator', |
| globalScopePrefix: [ |
| 'int sideEffectCounterA = 0;', |
| 'bool funcA() {', |
| ' sideEffectCounterA++;', |
| ' return sideEffectCounterA > 1;', |
| '}', |
| 'int sideEffectCounterB = 0;', |
| 'bool funcB() {', |
| ' sideEffectCounterB++;', |
| ' return sideEffectCounterB > 0;', |
| '}' |
| ].join('\n'), |
| mainPrefix: '', |
| loopExpression: 'funcA() ? true : funcB()', |
| loopCondition: 'funcA() ? true : funcB()', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'sideEffectCounterA == 3 && sideEffectCounterB == 1' |
| }, |
| { |
| description: 'high-precision constant', |
| globalScopePrefix: [ |
| 'const highp float f = 2048.5;', |
| 'uniform mediump float u_zero;' |
| ].join('\n'), |
| mainPrefix: 'float c = 0.0;', |
| loopExpression: 'c = fract(u_zero + f)', |
| loopCondition: 'bool(c = fract(u_zero + f))', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'abs(c - 0.5) < 0.01' |
| }, |
| { |
| description: 'l-value indexing side effects combined with static indexing of a vector', |
| globalScopePrefix: [ |
| 'int sideEffectCounter = 0;', |
| 'int func() {', |
| ' sideEffectCounter++;', |
| ' return sideEffectCounter > 1 ? 1 : 0;', |
| '}' |
| ].join('\n'), |
| mainPrefix: [ |
| 'vec4[2] V;', |
| 'V[0] = vec4(1.0);', |
| 'V[1] = vec4(3.0);' |
| ].join('\n'), |
| loopExpression: 'V[func()][0]++', |
| loopCondition: 'bool(V[func()][0]++)', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'abs(V[0][0] - 2.0) < 0.01 && abs(V[1][0] - 5.0) < 0.01 && sideEffectCounter == 3' |
| }, |
| { |
| description: 'l-value indexing side effects combined with dynamically indexing a vector', |
| globalScopePrefix: [ |
| 'int sideEffectCounter = 0;', |
| 'uniform int u_zero;', |
| 'int func() {', |
| ' sideEffectCounter++;', |
| ' return sideEffectCounter > 1 ? 1 : 0;', |
| '}' |
| ].join('\n'), |
| mainPrefix: [ |
| 'vec4[2] V;', |
| 'V[0] = vec4(1.0);', |
| 'V[1] = vec4(3.0);' |
| ].join('\n'), |
| loopExpression: 'V[func()][u_zero + 1]++', |
| loopCondition: 'bool(V[func()][u_zero + 1]++)', |
| loopContents: '', |
| loopIterations: 3, |
| passCondition: 'abs(V[0][1] - 2.0) < 0.01 && abs(V[1][1] - 5.0) < 0.01 && sideEffectCounter == 3' |
| } |
| ]; |
| |
| var tests = []; |
| |
| var wtu = WebGLTestUtils; |
| |
| for (var i = 0; i < testDataList.length; ++i) { |
| var testData = testDataList[i]; |
| if ('loopCondition' in testData) { |
| tests.push({ |
| fShaderSource: wtu.replaceParams(forLoopConditionTemplate, testData), |
| fShaderSuccess: true, |
| linkSuccess: true, |
| passMsg: 'Test ' + testData.description + ': ' + testData.loopCondition + ' inside a for loop condition' |
| }); |
| tests.push({ |
| fShaderSource: wtu.replaceParams(whileLoopConditionTemplate, testData), |
| fShaderSuccess: true, |
| linkSuccess: true, |
| passMsg: 'Test ' + testData.description + ': ' + testData.loopCondition + ' inside a while loop condition' |
| }); |
| tests.push({ |
| fShaderSource: wtu.replaceParams(doWhileLoopConditionTemplate, testData), |
| fShaderSuccess: true, |
| linkSuccess: true, |
| passMsg: 'Test ' + testData.description + ': ' + testData.loopCondition + ' inside a do-while loop condition' |
| }); |
| } |
| if ('loopExpression' in testData) { |
| tests.push({ |
| fShaderSource: wtu.replaceParams(forLoopExpressionTemplate, testData), |
| fShaderSuccess: true, |
| linkSuccess: true, |
| passMsg: 'Test ' + testData.description + ': ' + testData.loopExpression + ' inside a for loop expression' |
| }); |
| } |
| } |
| |
| GLSLConformanceTester.runRenderTests(tests, 2); |
| |
| </script> |
| </body> |
| </html> |