<!--
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>All valid constant expressions should be allowed in the initialization of const variables</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<link rel="stylesheet" href="../../../resources/glsl-feature-tests.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>
<script id="VertexTemplate" type="x-shader/x-vertex">
precision mediump float;

$(init)

void main() {
    const $(constType) c = $(constantExpression);
    gl_Position = vec4(float(c$(constTypeToScalar)));
}
</script>
<script id="FragmentTemplate" type="x-shader/x-fragment">
precision mediump float;

$(init)

void main() {
    const $(constType) c = $(constantExpression);
    gl_FragColor = vec4(float(c$(constTypeToScalar)));
}
</script>
</head>
<body onload="runTest()">
<div id="description"></div>
<div id="console"></div>
<script type="application/javascript">
"use strict";
description();

var wtu = WebGLTestUtils;

// ESSL 1.00 spec section 4.3.2.
// Initializers for const declarations must be a constant expression.

// ESSL 1.00 spec section 5.10.
// A constant expression is one of
// * a literal value (e.g., 5 or true)
// * a global or local variable qualified as const excluding function parameters
// * an expression formed by an operator on operands that are constant expressions, including getting
// an element of a constant vector or a constant matrix, or a field of a constant structure
// * a constructor whose arguments are all constant expressions
// * a built-in function call whose arguments are all constant expressions, with the exception of the
// texture lookup functions.

var binaryOpsGenTypeRValuesToGenType = [
  '+',
  '-',
  '*',
  '/'
];

var binaryOpsScalarsToBool = [
  '<',
  '>',
  '<=',
  '>='
];

var binaryOpsRValuesToBool = [
  '==',
  '!='
];

var binaryOpsBoolsToBool = [
  '&&',
  '^^',
  '||'
];

var builtInsGenTypeToGenType = [
  'radians',
  'degrees',
  'sin',
  'cos',
  'tan',
  'asin',
  'acos',
  'atan',
  'exp',
  'log',
  'exp2',
  'log2',
  'sqrt',
  'inversesqrt',
  'abs',
  'sign',
  'floor',
  'ceil',
  'fract'
];

var builtIns2VecToBvec = [
  'lessThan',
  'lessThanEqual',
  'greaterThan',
  'greaterThanEqual',
  'equal',
  'notEqual'
];

var builtIns2GenTypeToGenType = [
  'atan',
  'pow',
  'mod',
  'min',
  'max',
  'step'
];

var runTest = function() {
  var vsTemplate = document.getElementById('VertexTemplate').text;
  var fsTemplate = document.getElementById('FragmentTemplate').text;

  var tests = [];
  var i;
  var op;
  var builtIn;

  var pushTest = function(constType, constantExpression, expectSuccess, opt_init) {
    if (opt_init === undefined) {
      opt_init = '';
    }
    var constTypeToScalar = '';
    if (constType.substring(0, 3) == 'vec' || constType.substring(1, 4) == 'vec') {
        constTypeToScalar = '.x';
    } else if (constType.substring(0, 3) == 'mat') {
        constTypeToScalar = '[0].x';
    } else if (constType == 'my_struct') {
        constTypeToScalar = '.field';
    }
    var vs = wtu.replaceParams(vsTemplate, {constType: constType, constantExpression: constantExpression, constTypeToScalar: constTypeToScalar, init: opt_init});
    var fs = wtu.replaceParams(fsTemplate, {constType: constType, constantExpression: constantExpression, constTypeToScalar: constTypeToScalar, init: opt_init});
    tests.push({
      vShaderSource: vs,
      vShaderSuccess: expectSuccess,
      linkSuccess: expectSuccess,
      passMsg: "Assigning " + constantExpression + " to a const in a vertex shader should " + (expectSuccess ? "compile." : "fail compilation.")
    });
    tests.push({
      fShaderSource: fs,
      fShaderSuccess: expectSuccess,
      linkSuccess: expectSuccess,
      passMsg: "Assigning " + constantExpression + " to a const in a fragment shader should " + (expectSuccess ? "compile." : "fail compilation.")
    });
  }

  // Handle some one of a kind cases first
  pushTest('float', 'vec4(0.5).x', true);
  pushTest('float', 'vec4(0.5)[0]', true);
  pushTest('float', 'true ? 0.5 : 0.2', true);
  pushTest('my_struct', 'my_struct(0.5, 0.2)', true, 'struct my_struct { float field; float field2; };');
  pushTest('float', '(0.2, 0.5)', true);

  pushTest('float', 'clamp(0.2, 0.3, 0.4)', true);
  pushTest('float', 'mix(0.2, 0.3, 0.4)', true);
  pushTest('float', 'smoothstep(0.2, 0.3, 0.4)', true);
  pushTest('float', 'length(vec4(0.5))', true);
  pushTest('float', 'distance(vec4(0.5), vec4(0.2))', true);
  pushTest('float', 'dot(vec4(0.5), vec4(0.2))', true);
  pushTest('vec3', 'cross(vec3(0.5), vec3(0.2))', true);
  pushTest('vec4', 'normalize(vec4(0.5))', true);
  pushTest('vec4', 'faceforward(vec4(0.2), vec4(0.3), vec4(0.4))', true);
  pushTest('vec4', 'reflect(vec4(0.2), vec4(0.5))', true);
  pushTest('vec4', 'refract(vec4(0.2), vec4(0.3), 0.4)', true);
  pushTest('mat4', 'matrixCompMult(mat4(0.2), mat4(0.5))', true);

  // Handle built-in constructors
  for (i = 2; i <= 4; ++i) {
    var vecType = 'vec' + i;
    pushTest(vecType, vecType + '(0.5)', true);
    pushTest('i' + vecType, 'i' + vecType + '(1)', true);
    pushTest('b' + vecType, 'b' + vecType + '(true)', true);
    pushTest('mat' + i, 'mat' + i + '(0.5)', true);
  }

  // Handle ops
  for (i = 0; i < binaryOpsGenTypeRValuesToGenType.length; ++i) {
    var op = binaryOpsGenTypeRValuesToGenType[i];
    pushTest('float', '0.2 ' + op + ' 0.5', true);
    pushTest('vec4', 'vec4(0.2) ' + op + ' vec4(0.5)', true);
    pushTest('mat4', 'mat4(0.2) ' + op + ' mat4(0.5)', true);
  }

  for (i = 0; i < binaryOpsScalarsToBool.length; ++i) {
    var op = binaryOpsScalarsToBool[i];
    pushTest('bool', '0.2 ' + op + ' 0.5', true);
  }

  for (i = 0; i < binaryOpsRValuesToBool.length; ++i) {
    var op = binaryOpsRValuesToBool[i];
    pushTest('bool', '0.2 ' + op + ' 0.5', true);
    pushTest('bool', 'vec4(0.2) ' + op + ' vec4(0.5)', true);
  }

  for (i = 0; i < binaryOpsBoolsToBool.length; ++i) {
    var op = binaryOpsBoolsToBool[i];
    pushTest('bool', 'false ' + op + ' true', true);
  }

  // Handle allowed built-ins
  for (i = 0; i < builtInsGenTypeToGenType.length; ++i) {
    builtIn = builtInsGenTypeToGenType[i];
    pushTest('float', builtIn + '(0.5)', true);
    pushTest('vec4', builtIn + '(vec4(0.5))', true);
  }

  for (i = 0; i < builtIns2VecToBvec.length; ++i) {
    builtIn = builtIns2VecToBvec[i];
    pushTest('bvec4', builtIn + '(vec4(0.2), vec4(0.5))', true);
  }

  for (i = 0; i < builtIns2GenTypeToGenType.length; ++i) {
    builtIn = builtIns2GenTypeToGenType[i];
    pushTest('float', builtIn + '(0.2, 0.5)', true);
    pushTest('vec4', builtIn + '(vec4(0.2), vec4(0.5))', true);
  }

  // Include some expressions with a constant variable reference
  pushTest('float', 'cc', true, 'const float cc = 0.5;');
  pushTest('float', 'cc + cc2', true, 'const float cc = 0.5; const float cc2 = 0.2;');
  pushTest('float', 'sqrt(cc)', true, 'const float cc = 0.5;');

  GLSLConformanceTester.runTests(tests);
}

var successfullyParsed = true;
</script>
</body>
</html>
