<!--
Copyright (c) 2021 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>WebGL GLSL Conformance Tests - Swizzle as lvalue</title>
<link rel="stylesheet" href="../../../../webgl_test_files/resources/js-test-style.css"/>
<link rel="stylesheet" href="../../../../webgl_test_files/resources/glsl-feature-tests.css"/>
<script src="../../../../webgl_test_files/js/js-test-pre.js"></script>
<script src="../../../../webgl_test_files/js/webgl-test-utils.js"></script>
<script src="../../../../webgl_test_files/js/glsl-conformance-test.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<!--
Tests swizzle lvalues with out and inout function parameters.
6.1.1 Function Calling Conventions
Evaluation of an inout parameter results in both a value and an l-value; the value is copied to
the formal parameter at call time and the l- value is used to copy out a value when the function
returns.
-->
<script id="fs0" type="text/something-not-javascript">
precision mediump float;
void f(out $dim v) {
    v = $ref;
}
void main()
{
    vec4 i = vec4(10., 11., 12., 13.);
    f(i.$components);
    bool correct = false;
    if (i == $result)
        correct = true;
    gl_FragColor = vec4(0, correct ? 1. : 0., 0., 1.);
}
</script>
<script id="fs1" type="text/something-not-javascript">
precision mediump float;
void f(inout $dim v) {
    v += $ref;
}
void main()
{
    vec4 i = vec4(10., 11., 12., 13.);
    f(i.$components);
    bool correct = false;
    if (i == $resultInOut)
        correct = true;
    gl_FragColor = vec4(0, correct ? 1. : 0., 0., 1.);
}
</script>
<script>
"use strict";

description();

// GLSL ES 3.00.6 5.5 Vector Components
// To form an l-value, swizzling must be applied to an l-value of vector type, contain no duplicate
// components, and it results in an l-value of scalar or vector type, depending on number of
// components specified.
function computeLValueSwizzles() {
    let results = [];
    function f(components, swizzle) {
        for (let c of components) {
            results.push(swizzle.concat([c]));
            let rest = components.filter((e) => { return e != c; });
            if (rest)
                f(rest, swizzle.concat([c]));
        }
    }
    f([0, 1, 2, 3], []);
    return results;
}

var swizzles = computeLValueSwizzles();

// Sanity check that computing the swizzles produces something to test.
shouldBe("swizzles.length", "64");
shouldBe("components(swizzles[0])", "'r'");
shouldBe("components(swizzles[swizzles.length - 1])", "'abgr'");

var refValue = [ "1.", "2.", "3.", "4." ];

var init = [ "10.", "11.", "12.", "13." ];

function dim(swizzle) {
  if (swizzle.length == 1)
    return 'float'
  return `vec${swizzle.length}`;
}

function result(swizzle) {
  let a = init.map((e, c) => {
      if (swizzle.includes(c))
        return refValue[c];
      return e;
  });
  return `vec4(${a.join(', ')})`;
}

function resultInOut(swizzle) {
  let a = init.map((e, c) => {
      if (swizzle.includes(c))
        return `${e} + ${refValue[c]}`;
      return e;
  });
  return `vec4(${a.join(', ')})`;
}

function ref(swizzle) {
  let a = swizzle.map((c) => { return refValue[c]; });
  return `${dim(swizzle)}(${a.join(', ')})`;
}

function components(swizzle) {
  return swizzle.map((e) => { return 'rgba'[e]; }).join('');
}

var tests = [];
for (var swizzle of swizzles) {
  for (var ii = 0; ii < 2; ++ii) {
    var fsrc = document.getElementById("fs" + ii).text;
    fsrc = fsrc.replace(/\$dim/g, dim(swizzle))
               .replace(/\$resultInOut/g, resultInOut(swizzle))
               .replace(/\$result/g, result(swizzle))
               .replace(/\$ref/g, ref(swizzle))
               .replace(/\$components/g, components(swizzle));
    tests.push({
        fShaderSource: fsrc,
        fShaderSuccess: true,
        linkSuccess: true,
        passMsg: `Swizzle as lvalue works, func: ${ii}, swizzle: ${components(swizzle)}`
    });
  }
}

GLSLConformanceTester.runRenderTests(tests);
</script>
</body>
</html>
