<!--
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>WebGL texture size conformance test.</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>
</head>
<body>
<canvas id="example" width="32" height="32" style="width: 40px; height: 40px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec3 texCoord0;
varying vec3 texCoord;
void main()
{
    gl_Position = vPosition;
    texCoord = texCoord0;
}
</script>

<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform samplerCube tex;
varying vec3 texCoord;
void main()
{
    gl_FragColor = textureCube(tex, normalize(texCoord));
}
</script>
<script>
"use strict";
description("Checks that various sizes of textures render")

var wtu = WebGLTestUtils;
var gl = wtu.create3DContext("example");
var program2D = wtu.setupTexturedQuad(gl);
var programCubeMap = wtu.setupProgram(
    gl, ['vshader', 'fshader'], ['vPosition', 'texCoord0'], [0, 1]);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
var tex = gl.createTexture();
var max2DSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
var maxCubeMapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE);
debug("MAX_TEXTURE_SIZE:" + max2DSize);
debug("MAX_CUBE_MAP_TEXTURE_SIZE:" + maxCubeMapSize);
// Assuming 2048x2048xRGBA (22meg with mips) will run on all WebGL platforms
var max2DSquareSize = Math.min(max2DSize, 2048);
// I'd prefer this to be 2048 but that's 16meg x 6 faces or 128meg (with mips)
// 1024 is 33.5 meg (with mips)
var maxCubeMapSize = Math.min(maxCubeMapSize, 1024);

var colors = [
  { name: "green", rgba: [0, 0, 255, 255] },
  { name: "red", rgba: [255, 0, 0, 255] },
  { name: "blue", rgba: [0, 255, 0, 255] },
  { name: "yellow", rgba: [255, 255, 0, 255] },
  { name: "magenta", rgba: [255, 0, 255, 255] },
  { name: "cyan", rgba: [0, 255, 255, 255] }
];

var count = 0;
var power = 0;
runTest();

function runTest() {
  function doTest() {
    var size = Math.pow(2, power);
    if (size > max2DSize) {
      return false;
    }
    gl.useProgram(program2D);
    if (!checkTexture(size, 1, false)) return false;
    if (!checkTexture(1, size, false)) return false;
    if (size <= max2DSquareSize) {
      if (!checkTexture(size, size, false)) {
        return false;
      }
    }
    if (size <= maxCubeMapSize) {
      gl.useProgram(programCubeMap);
      if (!checkTexture(size, size, true)) {
        return false;
      }
    }
    return true;
  }

  if (doTest()) {
    ++power;
    setTimeout(runTest, 0);
  } else {
    finishTest();
  }
}

function checkTexture(width, height, cubeMap) {
  debug("");
  count = (count + 1) % colors.length;
  var color = colors[count];
  var tex = gl.createTexture();
  var target = cubeMap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;
  var type = cubeMap ? "cube map" : "2D texture";
  debug("check " + width + ", " + height + " " + type);
  gl.bindTexture(target, tex);
  gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  fillLevel(0, width, height, color.rgba, cubeMap);
  var err = gl.getError();
  if (err == gl.OUT_OF_MEMORY) {
    debug("out of memory");
    return false;
  }
  if (err != gl.NO_ERROR) {
    testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err));
  }
  wtu.clearAndDrawUnitQuad(gl);
  var tolerance = 3;
  wtu.checkCanvas(gl, color.rgba,
      type + " of size " + width + "x" + height + " with no mips should draw with " + color.name,
      tolerance);
  count = (count + 1) % colors.length;
  color = colors[count];
  fillLevel(0, width, height, color.rgba, cubeMap);
  gl.generateMipmap(target);
  var err = gl.getError();
  if (err == gl.OUT_OF_MEMORY) {
    debug("out of memory");
    return false;
  }
  if (err != gl.NO_ERROR) {
    testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err));
  }
  gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(gl, color.rgba,
      type + " of size " + width + "x" + height + " with mips should draw with " + color.name,
      tolerance);

  count = (count + 1) % colors.length;
  color = colors[count];
  fillLevel(0, width, height, color.rgba, cubeMap, true);
  gl.generateMipmap(target);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(gl, color.rgba,
      type + " of size " + width + "x" + height + " with mips should draw with " + color.name,
      tolerance);

  gl.deleteTexture(tex);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
  return true;
}

function fillLevel(level, width, height, color, opt_cubemap, opt_subTex) {
  var numPixels = width * height;
  var pixels = null;
  var largeDim = Math.max(width, height);
  var smallDim = Math.min(width, height);

  var pixelRow = new Uint8Array(largeDim * 4);
  for (var jj = 0; jj < largeDim; ++jj) {
    var off = jj * 4;
    pixelRow[off + 0] = color[0];
    pixelRow[off + 1] = color[1];
    pixelRow[off + 2] = color[2];
    pixelRow[off + 3] = color[3];
  }

  if (largeDim == numPixels) {
    pixels = pixelRow;
  } else {
    var pixels = new Uint8Array(numPixels * 4);
    for (var jj = 0; jj < smallDim; ++jj) {
      var off = jj * largeDim * 4;
      pixels.set(pixelRow, off);
    }
  }

  var targets = opt_cubemap ? [
    gl.TEXTURE_CUBE_MAP_POSITIVE_X,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
    gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
    gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_Z] :
    [gl.TEXTURE_2D];

  for (var ii = 0; ii < targets.length; ++ii) {
    // debug(wtu.glEnumToString(gl, targets[ii]));
    var index = (ii + power) % targets.length;
    var target = targets[index];
    if (opt_subTex) {
      gl.texSubImage2D(
          target, level, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE,
          pixels);
    } else {
      gl.texImage2D(
          target, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE,
          pixels);
    }
  }
}
</script>
</body>
</html>

