<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants.js"></script>

<script>
let immersiveTestName = "Ensure that the framebuffer given by the WebGL layer" +
  " is opaque for immersive";
let nonImmersiveTestName = "Ensure that the framebuffer given by the WebGL layer" +
  " is opaque for non-immersive";

let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;

let testFunction =
  (session, fakeDeviceController, t, sessionObjects) => new Promise((resolve, reject) => {
  let gl = sessionObjects.gl;
  let webglLayer = sessionObjects.glLayer;
  let xrFramebuffer = webglLayer.framebuffer;

  // Make sure we're starting with a clean error slate.
  assert_equals(gl.getError(), gl.NO_ERROR);

  if (session.mode == 'inline') {
    // Creating a layer with an inline session should return a framebuffer of
    // null, and as such most of these tests won't apply.
    assert_equals(xrFramebuffer, null);
    resolve();
    return;
  }

  assert_not_equals(xrFramebuffer, null);

  // The XR framebuffer is not bound to the GL context by default.
  assert_not_equals(xrFramebuffer, gl.getParameter(gl.FRAMEBUFFER_BINDING));

  assert_greater_than(webglLayer.framebufferWidth, 0);
  assert_greater_than(webglLayer.framebufferHeight, 0);

  gl.bindFramebuffer(gl.FRAMEBUFFER, xrFramebuffer);
  assert_equals(gl.getError(), gl.NO_ERROR);

  gl.deleteFramebuffer(xrFramebuffer);
  assert_equals(gl.getError(), gl.INVALID_OPERATION);

  // Make sure the framebuffer is still bound after failed attempt to delete.
  let boundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
  assert_equals(xrFramebuffer, boundFramebuffer);
  assert_equals(gl.getError(), gl.NO_ERROR);

  // WebGL 2 does not throw an error and instead returns 0 when there are no
  // attachments
  if (gl.getParameter(gl.VERSION).includes("WebGL 1.0")) {
    // Ensure the framebuffer attachment properties cannot be inspected.
    let attachments = [
      gl.COLOR_ATTACHMENT0,
      gl.DEPTH_ATTACHMENT,
      gl.STENCIL_ATTACHMENT,
    ];

    let parameters = [
      gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
      gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
      gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
      gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE,
    ];

    for (let attachment of attachments) {
      for (let parameter of parameters) {
        let value = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachment, parameter);
        assert_equals(value, null);
        assert_equals(gl.getError(), gl.INVALID_OPERATION);
      }
    }
  }

  let width = 64;
  let height = 64;

  // Ensure the framebuffer texture 2D attachmentments cannot be changed.
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  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.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
  assert_equals(gl.getError(), gl.INVALID_OPERATION);

  // Ensure the framebuffer renderbuffer attachmentments cannot be changed.
  let renderbuffer = gl.createRenderbuffer();
  gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
  gl.framebufferRenderbuffer(
    gl.FRAMEBUFFER,
    gl.DEPTH_ATTACHMENT,
    gl.RENDERBUFFER,
    renderbuffer);
  assert_equals(gl.getError(), gl.INVALID_OPERATION);

  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
  assert_equals(gl.getError(), gl.INVALID_OPERATION);

  // Framebuffer status must be unsupported outside of a XR frame callback.
  assert_equals(gl.checkFramebufferStatus(gl.FRAMEBUFFER), gl.FRAMEBUFFER_UNSUPPORTED);

  session.requestAnimationFrame((time, xrFrame) => {
    // Framebuffer status must be complete inside of a XR frame callback.
    assert_equals(gl.checkFramebufferStatus(gl.FRAMEBUFFER), gl.FRAMEBUFFER_COMPLETE);
    // Finished.
    resolve();
  });
});

xr_session_promise_test(
  immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr');

xr_session_promise_test(
  nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline');

</script>
