function Context(canvas, options) {
  //>>includeStart('debug', pragmas.debug);
  Check.defined("canvas", canvas);
  //>>includeEnd('debug');

  const {
    getWebGLStub,
    requestWebgl1,
    webgl: webglOptions = {},
    allowTextureFilterAnisotropic = true,
  } = options ?? {};

  // Override select WebGL defaults
  webglOptions.alpha = webglOptions.alpha ?? false; // WebGL default is true
  webglOptions.stencil = webglOptions.stencil ?? true; // WebGL default is false
  webglOptions.powerPreference =
    webglOptions.powerPreference ?? "high-performance"; // WebGL default is "default"

  const glContext = defined(getWebGLStub)
    ? getWebGLStub(canvas, webglOptions)
    : getWebGLContext(canvas, webglOptions, requestWebgl1);

  // Get context type. instanceof will throw if WebGL2 is not supported
  const webgl2Supported = typeof WebGL2RenderingContext !== "undefined";
  const webgl2 = webgl2Supported && glContext instanceof WebGL2RenderingContext;

  this._canvas = canvas;
  this._originalGLContext = glContext;
  this._gl = glContext;
  this._webgl2 = webgl2;
  this._id = createGuid();

  // Validation and logging disabled by default for speed.
  this.validateFramebuffer = false;
  this.validateShaderProgram = false;
  this.logShaderCompilation = false;

  this._throwOnWebGLError = false;

  this._shaderCache = new ShaderCache(this);
  this._textureCache = new TextureCache();

  const gl = glContext;

  this._stencilBits = gl.getParameter(gl.STENCIL_BITS);

  ContextLimits._maximumCombinedTextureImageUnits = gl.getParameter(
    gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS,
  ); // min: 8
  ContextLimits._maximumCubeMapSize = gl.getParameter(
    gl.MAX_CUBE_MAP_TEXTURE_SIZE,
  ); // min: 16
  ContextLimits._maximumFragmentUniformVectors = gl.getParameter(
    gl.MAX_FRAGMENT_UNIFORM_VECTORS,
  ); // min: 16
  ContextLimits._maximumTextureImageUnits = gl.getParameter(
    gl.MAX_TEXTURE_IMAGE_UNITS,
  ); // min: 8
  ContextLimits._maximumRenderbufferSize = gl.getParameter(
    gl.MAX_RENDERBUFFER_SIZE,
  ); // min: 1
  ContextLimits._maximumTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); // min: 64
  ContextLimits._maximumVaryingVectors = gl.getParameter(
    gl.MAX_VARYING_VECTORS,
  ); // min: 8
  ContextLimits._maximumVertexAttributes = gl.getParameter(
    gl.MAX_VERTEX_ATTRIBS,
  ); // min: 8
  ContextLimits._maximumVertexTextureImageUnits = gl.getParameter(
    gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS,
  ); // min: 0
  ContextLimits._maximumVertexUniformVectors = gl.getParameter(
    gl.MAX_VERTEX_UNIFORM_VECTORS,
  ); // min: 128

  ContextLimits._maximumSamples = this._webgl2
    ? gl.getParameter(gl.MAX_SAMPLES)
    : 0;

  const aliasedLineWidthRange = gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE); // must include 1
  ContextLimits._minimumAliasedLineWidth = aliasedLineWidthRange[0];
  ContextLimits._maximumAliasedLineWidth = aliasedLineWidthRange[1];

  const aliasedPointSizeRange = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE); // must include 1
  ContextLimits._minimumAliasedPointSize = aliasedPointSizeRange[0];
  ContextLimits._maximumAliasedPointSize = aliasedPointSizeRange[1];

  const maximumViewportDimensions = gl.getParameter(gl.MAX_VIEWPORT_DIMS);
  ContextLimits._maximumViewportWidth = maximumViewportDimensions[0];
  ContextLimits._maximumViewportHeight = maximumViewportDimensions[1];

  const highpFloat = gl.getShaderPrecisionFormat(
    gl.FRAGMENT_SHADER,
    gl.HIGH_FLOAT,
  );
  ContextLimits._highpFloatSupported = highpFloat.precision !== 0;
  const highpInt = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT);
  ContextLimits._highpIntSupported = highpInt.rangeMax !== 0;

  this._antialias = gl.getContextAttributes().antialias;

  // Query and initialize extensions
  this._standardDerivatives = !!getExtension(gl, ["OES_standard_derivatives"]);
  this._blendMinmax = !!getExtension(gl, ["EXT_blend_minmax"]);
  this._elementIndexUint = !!getExtension(gl, ["OES_element_index_uint"]);
  this._depthTexture = !!getExtension(gl, [
    "WEBGL_depth_texture",
    "WEBKIT_WEBGL_depth_texture",
  ]);
  this._fragDepth = !!getExtension(gl, ["EXT_frag_depth"]);
  this._debugShaders = getExtension(gl, ["WEBGL_debug_shaders"]);

  this._textureFloat = !!getExtension(gl, ["OES_texture_float"]);
  this._textureHalfFloat = !!getExtension(gl, ["OES_texture_half_float"]);

  this._textureFloatLinear = !!getExtension(gl, ["OES_texture_float_linear"]);
  this._textureHalfFloatLinear = !!getExtension(gl, [
    "OES_texture_half_float_linear",
  ]);

  this._supportsTextureLod = !!getExtension(gl, ["EXT_shader_texture_lod"]);

  this._colorBufferFloat = !!getExtension(gl, [
    "EXT_color_buffer_float",
    "WEBGL_color_buffer_float",
  ]);
  this._floatBlend = !!getExtension(gl, ["EXT_float_blend"]);
  this._colorBufferHalfFloat = !!getExtension(gl, [
    "EXT_color_buffer_half_float",
  ]);

  this._s3tc = !!getExtension(gl, [
    "WEBGL_compressed_texture_s3tc",
    "MOZ_WEBGL_compressed_texture_s3tc",
    "WEBKIT_WEBGL_compressed_texture_s3tc",
  ]);
  this._pvrtc = !!getExtension(gl, [
    "WEBGL_compressed_texture_pvrtc",
    "WEBKIT_WEBGL_compressed_texture_pvrtc",
  ]);
  this._astc = !!getExtension(gl, ["WEBGL_compressed_texture_astc"]);
  this._etc = !!getExtension(gl, ["WEBG_compressed_texture_etc"]);
  this._etc1 = !!getExtension(gl, ["WEBGL_compressed_texture_etc1"]);
  this._bc7 = !!getExtension(gl, ["EXT_texture_compression_bptc"]);

  // It is necessary to pass supported formats to loadKTX2
  // because imagery layers don't have access to the context.
  loadKTX2.setKTX2SupportedFormats(
    this._s3tc,
    this._pvrtc,
    this._astc,
    this._etc,
    this._etc1,
    this._bc7,
  );

  const textureFilterAnisotropic = allowTextureFilterAnisotropic
    ? getExtension(gl, [
        "EXT_texture_filter_anisotropic",
        "WEBKIT_EXT_texture_filter_anisotropic",
      ])
    : undefined;
  this._textureFilterAnisotropic = textureFilterAnisotropic;
  ContextLimits._maximumTextureFilterAnisotropy = defined(
    textureFilterAnisotropic,
  )
    ? gl.getParameter(textureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT)
    : 1.0;

  let glCreateVertexArray;
  let glBindVertexArray;
  let glDeleteVertexArray;

  let glDrawElementsInstanced;
  let glDrawArraysInstanced;
  let glVertexAttribDivisor;

  let glDrawBuffers;

  let vertexArrayObject;
  let instancedArrays;
  let drawBuffers;

  if (webgl2) {
    const that = this;

    glCreateVertexArray = function () {
      return that._gl.createVertexArray();
    };
    glBindVertexArray = function (vao) {
      that._gl.bindVertexArray(vao);
    };
    glDeleteVertexArray = function (vao) {
      that._gl.deleteVertexArray(vao);
    };

    glDrawElementsInstanced = function (
      mode,
      count,
      type,
      offset,
      instanceCount,
    ) {
      gl.drawElementsInstanced(mode, count, type, offset, instanceCount);
    };
    glDrawArraysInstanced = function (mode, first, count, instanceCount) {
      gl.drawArraysInstanced(mode, first, count, instanceCount);
    };
    glVertexAttribDivisor = function (index, divisor) {
      gl.vertexAttribDivisor(index, divisor);
    };

    glDrawBuffers = function (buffers) {
      gl.drawBuffers(buffers);
    };
  } else {
    vertexArrayObject = getExtension(gl, ["OES_vertex_array_object"]);
    if (defined(vertexArrayObject)) {
      glCreateVertexArray = function () {
        return vertexArrayObject.createVertexArrayOES();
      };
      glBindVertexArray = function (vertexArray) {
        vertexArrayObject.bindVertexArrayOES(vertexArray);
      };
      glDeleteVertexArray = function (vertexArray) {
        vertexArrayObject.deleteVertexArrayOES(vertexArray);
      };
    }

    instancedArrays = getExtension(gl, ["ANGLE_instanced_arrays"]);
    if (defined(instancedArrays)) {
      glDrawElementsInstanced = function (
        mode,
        count,
        type,
        offset,
        instanceCount,
      ) {
        instancedArrays.drawElementsInstancedANGLE(
          mode,
          count,
          type,
          offset,
          instanceCount,
        );
      };
      glDrawArraysInstanced = function (mode, first, count, instanceCount) {
        instancedArrays.drawArraysInstancedANGLE(
          mode,
          first,
          count,
          instanceCount,
        );
      };
      glVertexAttribDivisor = function (index, divisor) {
        instancedArrays.vertexAttribDivisorANGLE(index, divisor);
      };
    }

    drawBuffers = getExtension(gl, ["WEBGL_draw_buffers"]);
    if (defined(drawBuffers)) {
      glDrawBuffers = function (buffers) {
        drawBuffers.drawBuffersWEBGL(buffers);
      };
    }
  }

  this.glCreateVertexArray = glCreateVertexArray;
  this.glBindVertexArray = glBindVertexArray;
  this.glDeleteVertexArray = glDeleteVertexArray;

  this.glDrawElementsInstanced = glDrawElementsInstanced;
  this.glDrawArraysInstanced = glDrawArraysInstanced;
  this.glVertexAttribDivisor = glVertexAttribDivisor;

  this.glDrawBuffers = glDrawBuffers;

  this._vertexArrayObject = !!vertexArrayObject;
  this._instancedArrays = !!instancedArrays;
  this._drawBuffers = !!drawBuffers;

  ContextLimits._maximumDrawBuffers = this.drawBuffers
    ? gl.getParameter(WebGLConstants.MAX_DRAW_BUFFERS)
    : 1;
  ContextLimits._maximumColorAttachments = this.drawBuffers
    ? gl.getParameter(WebGLConstants.MAX_COLOR_ATTACHMENTS)
    : 1;

  this._clearColor = new Color(0.0, 0.0, 0.0, 0.0);
  this._clearDepth = 1.0;
  this._clearStencil = 0;

  const us = new UniformState();
  const ps = new PassState(this);
  const rs = RenderState.fromCache();

  this._defaultPassState = ps;
  this._defaultRenderState = rs;
  // default texture has a value of (1, 1, 1)
  // default emissive texture has a value of (0, 0, 0)
  // default normal texture is +z which is encoded as (0.5, 0.5, 1)
  this._defaultTexture = undefined;
  this._defaultEmissiveTexture = undefined;
  this._defaultNormalTexture = undefined;
  this._defaultCubeMap = undefined;

  this._us = us;
  this._currentRenderState = rs;
  this._currentPassState = ps;
  this._currentFramebuffer = undefined;
  this._maxFrameTextureUnitIndex = 0;

  // Vertex attribute divisor state cache. Workaround for ANGLE (also look at VertexArray.setVertexAttribDivisor)
  this._vertexAttribDivisors = [];
  this._previousDrawInstanced = false;
  for (let i = 0; i < ContextLimits._maximumVertexAttributes; i++) {
    this._vertexAttribDivisors.push(0);
  }

  this._pickObjects = {};
  this._nextPickColor = new Uint32Array(1);

  /**
   * The options used to construct this context
   *
   * @type {ContextOptions}
   */
  this.options = {
    getWebGLStub: getWebGLStub,
    requestWebgl1: requestWebgl1,
    webgl: webglOptions,
    allowTextureFilterAnisotropic: allowTextureFilterAnisotropic,
  };

  /**
   * A cache of objects tied to this context.  Just before the Context is destroyed,
   * <code>destroy</code> will be invoked on each object in this object literal that has
   * such a method.  This is useful for caching any objects that might otherwise
   * be stored globally, except they're tied to a particular context, and to manage
   * their lifetime.
   *
   * @type {object}
   */
  this.cache = {};

  RenderState.apply(gl, rs, ps);
}
@private
@constructor

@param {HTMLCanvasElement} canvas The canvas element to which the context will be associated
@param {ContextOptions} [options] Options to control WebGL settings for the context