Source code
Revision control
Copy as Markdown
Other Tools
<!--
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>