<!--
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 Non-Power of 2 texture 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="4" height="4" style="width: 40px; height: 30px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 texCoord0;
varying vec2 texCoord;
void main()
{
    gl_Position = vPosition;
    texCoord = texCoord0;
}
</script>

<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform samplerCube tex;
varying vec2 texCoord;
void main()
{
    gl_FragColor = textureCube(tex, normalize(vec3(texCoord, 1)));
}
</script>
<script>
"use strict";
description(document.title);
var wtu = WebGLTestUtils;
var gl = wtu.create3DContext("example");
var program = wtu.setupTexturedQuad(gl);

wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");

var tests = [
  { format: gl.RGBA,
    type: gl.UNSIGNED_BYTE,
    color: [192, 0, 128, 64],
    expected: [192, 0, 128, 64],
    tolerance: 0,
  },
  { format: gl.RGB,
    type: gl.UNSIGNED_BYTE,
    color: [192, 0, 128],
    expected: [192, 0, 128, 255],
    tolerance: 0,
  },
  { format: gl.LUMINANCE,
    type: gl.UNSIGNED_BYTE,
    color: [192],
    expected: [192, 192, 192, 255],
    tolerance: 0,
  },
  { format: gl.ALPHA,
    type: gl.UNSIGNED_BYTE,
    color: [64],
    expected: [0, 0, 0, 64],
    tolerance: 0,
  },
  { format: gl.LUMINANCE_ALPHA,
    type: gl.UNSIGNED_BYTE,
    color: [192, 64],
    expected: [192, 192, 192, 64],
    tolerance: 0,
  },
  // { format: gl.RGBA,
  //   type: gl.UNSIGNED_SHORT_4_4_4_4,
  //   color: [0x48FC],
  //   expected: [0x44, 0x88, 0xFF, 0xCC],
  //   tolerance: 16,
  // },
  // { format: gl.RGBA,
  //   type: gl.UNSIGNED_SHORT_5_5_5_1,
  //   color: [0x8309],  // 10000 01000 00100 1
  //   expected: [128, 64, 32, 255],
  //   tolerance: 16,
  // },
  // { format: gl.RGB,
  //   type: gl.UNSIGNED_SHORT_5_6_5,
  //   color: [0x8404],  // 10000 010000 00100
  //   expected: [128, 64, 32],
  //   tolerance: 16,
  // },
];

tests.forEach(function(test) {
  debug("");
  debug("test " + wtu.glEnumToString(gl, test.format) + "/" + wtu.glEnumToString(gl, test.type));
  var tex = gl.createTexture();

  // Check that an NPOT texture not on level 0 generates INVALID_VALUE
  wtu.fillTexture(gl, tex, 5, 3, test.color, 1, test.format, test.type);
  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
      "gl.texImage2D with NPOT texture with level > 0 should return INVALID_VALUE");

  // Check that an NPOT texture on level 0 succeeds
  wtu.fillTexture(gl, tex, 5, 3, test.color, 0, test.format, test.type);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
      "gl.texImage2D with NPOT texture at level 0 should succeed");

  // Check that generateMipmap fails on NPOT
  gl.generateMipmap(gl.TEXTURE_2D);
  wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
      "gl.generateMipmap with NPOT texture should return INVALID_OPERATION");

  // Check that nothing is drawn if filtering is not correct for NPOT
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, [0, 0, 0, 255],
      "NPOT texture with TEXTURE_WRAP set to REPEAT should draw with 0,0,0,255");
  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, [0, 0, 0, 255],
      "NPOT texture with TEXTURE_MIN_FILTER not NEAREST or LINEAR should draw with 0,0,0,255");
  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, test.expected,
      "NPOT texture with TEXTURE_MIN_FILTER set to LINEAR should draw.");

  gl.copyTexImage2D(gl.TEXTURE_2D, 1, test.format, 0, 0, 5, 3, 0);
  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
      "copyTexImage2D with NPOT texture with level > 0 should return INVALID_VALUE.");

  // Check that generateMipmap for an POT texture succeeds
  wtu.fillTexture(gl, tex, 4, 4, test.color, 0, test.format);
  gl.generateMipmap(gl.TEXTURE_2D);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
      "gl.texImage2D and gl.generateMipmap with POT texture at level 0 should succeed");

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, test.expected,
      "POT texture with TEXTURE_MIN_FILTER set to LINEAR_MIPMAP_LINEAR should draw.");
  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
});

var testCubemap = function(switchTextureUnitBeforeDraw) {
  debug("");
  var title = "check using cubemap";
  if (switchTextureUnitBeforeDraw) {
    title += " and switch texture unit before draw to check for Chromium bug";
  }
  debug(title);
  var program = wtu.setupProgram(
      gl, ['vshader', 'fshader'], ['vPosition', 'texCoord0'], [0, 1]);
  var tex = gl.createTexture();

  // Check that an NPOT texture not on level 0 generates INVALID_VALUE
  fillCubeTexture(gl, tex, 5, 3, [0, 192, 128, 255], 1);
  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
      "gl.texImage2D with NPOT texture with level > 0 should return INVALID_VALUE");

  // Check that an NPOT texture on level 0 succeeds
  fillCubeTexture(gl, tex, 5, 5, [0, 192, 128, 255]);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
      "gl.texImage2D with NPOT texture at level 0 should succeed");

  // Check that generateMipmap fails on NPOT
  gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
  wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
      "gl.generateMipmap with NPOT texture should return INVALID_OPERATION");

  var loc = gl.getUniformLocation(program, "tex");
  gl.uniform1i(loc, 0);

  // Check that nothing is drawn if filtering is not correct for NPOT
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.REPEAT);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.REPEAT);

  if (switchTextureUnitBeforeDraw) {
    debug("Switching active texture unit to gl.TEXTURE1");
    // Test for http://crbug.com/390514
    gl.activeTexture(gl.TEXTURE1);
  }

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, [0, 0, 0, 255],
      "NPOT cubemap with TEXTURE_WRAP set to REPEAT should draw with 0,0,0,255");

  if (switchTextureUnitBeforeDraw) {
    var error = gl.getError();
    if (error === gl.NO_ERROR) {
      testPassed("getError was expected value: NO_ERROR : Should be no errors from draw.");
    } else if (error === gl.INVALID_ENUM) {
      testFailed("getError returned INVALID_ENUM. Possibly Chromium bug where texture unit is set to 0 instead of GL_TEXTURE0.");
    } else {
      testFailed("Drawing resulted in error: " + wtu.glEnumToString(gl, error));
    }
    gl.activeTexture(gl.TEXTURE0);
  } else {
    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from draw.");
  }

  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, [0, 0, 0, 255],
      "NPOT cubemap with TEXTURE_MIN_FILTER not NEAREST or LINEAR should draw with 0,0,0,255");
  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");

  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, [0, 192, 128, 255],
      "NPOT cubemap with TEXTURE_MIN_FILTER set to LINEAR should draw.");

  // Check that an POT texture on level 0 succeeds
  fillCubeTexture(gl, tex, 4, 4, [0, 192, 128, 255]);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
      "gl.texImage2D with POT texture at level 0 should succeed");

  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.REPEAT);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.REPEAT);

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, [0, 0, 0, 255],
      "POT cubemap with TEXTURE_MIN_FILTER set to LINEAR_MIPMAP_LINEAR but no mips draw with 0,0,0,255");

  // Check that generateMipmap succeeds on POT
  gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
      "gl.generateMipmap with POT texture should return succeed");

  wtu.clearAndDrawUnitQuad(gl);
  wtu.checkCanvas(
      gl, [0, 192, 128, 255],
      "POT cubemap with TEXTURE_MIN_FILTER set to LINEAR_MIPMAP_LINEAR should draw.");
};

testCubemap(false);
testCubemap(true);

var successfullyParsed = true;

function fillCubeTexture(gl, tex, width, height, color, opt_level) {
  opt_level = opt_level || 0;
  var canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = height;
  var ctx2d = canvas.getContext('2d');
  ctx2d.fillStyle = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ")";
  ctx2d.fillRect(0, 0, width, height);
  gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
  var targets = [
    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];
  for (var tt = 0; tt < targets.length; ++tt) {
    gl.texImage2D(
        targets[tt], opt_level, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
  }
};
</script>
<script src="../../../js/js-test-post.js"></script>

</body>
</html>

