function VertexArray(options) {
  options = options ?? Frozen.EMPTY_OBJECT;

  //>>includeStart('debug', pragmas.debug);
  Check.defined("options.context", options.context);
  Check.defined("options.attributes", options.attributes);
  //>>includeEnd('debug');

  const context = options.context;
  const gl = context._gl;
  const attributes = options.attributes;
  const indexBuffer = options.indexBuffer;

  let i;
  const vaAttributes = [];
  let numberOfVertices = 1; // if every attribute is backed by a single value
  let hasInstancedAttributes = false;
  let hasConstantAttributes = false;

  let length = attributes.length;
  for (i = 0; i < length; ++i) {
    addAttribute(vaAttributes, attributes[i], i, context);
  }

  length = vaAttributes.length;
  for (i = 0; i < length; ++i) {
    const attribute = vaAttributes[i];

    if (defined(attribute.vertexBuffer) && attribute.instanceDivisor === 0) {
      // This assumes that each vertex buffer in the vertex array has the same number of vertices.
      const bytes =
        attribute.strideInBytes ||
        attribute.componentsPerAttribute *
          ComponentDatatype.getSizeInBytes(attribute.componentDatatype);
      numberOfVertices = attribute.vertexBuffer.sizeInBytes / bytes;
      break;
    }
  }

  for (i = 0; i < length; ++i) {
    if (vaAttributes[i].instanceDivisor > 0) {
      hasInstancedAttributes = true;
    }
    if (defined(vaAttributes[i].value)) {
      hasConstantAttributes = true;
    }
  }

  //>>includeStart('debug', pragmas.debug);
  // Verify all attribute names are unique
  const uniqueIndices = {};
  for (i = 0; i < length; ++i) {
    const index = vaAttributes[i].index;
    if (uniqueIndices[index]) {
      throw new DeveloperError(
        `Index ${index} is used by more than one attribute.`,
      );
    }
    uniqueIndices[index] = true;
  }
  //>>includeEnd('debug');

  let vao;

  // Setup VAO if supported
  if (context.vertexArrayObject) {
    vao = context.glCreateVertexArray();
    context.glBindVertexArray(vao);
    bind(gl, vaAttributes, indexBuffer);
    context.glBindVertexArray(null);
  }

  this._numberOfVertices = numberOfVertices;
  this._hasInstancedAttributes = hasInstancedAttributes;
  this._hasConstantAttributes = hasConstantAttributes;
  this._context = context;
  this._gl = gl;
  this._vao = vao;
  this._attributes = vaAttributes;
  this._indexBuffer = indexBuffer;
}
Creates a vertex array, which defines the attributes making up a vertex, and contains an optional index buffer
to select vertices for rendering.  Attributes are defined using object literals as shown in Example 1 below.

@param {object} options Object with the following properties:
@param {Context} options.context The context in which the VertexArray gets created.
@param {Object[]} options.attributes An array of attributes.
@param {IndexBuffer} [options.indexBuffer] An optional index buffer.

@returns {VertexArray} The vertex array, ready for use with drawing.

@exception {DeveloperError} Attribute must have a <code>vertexBuffer</code>.
@exception {DeveloperError} Attribute must have a <code>componentsPerAttribute</code>.
@exception {DeveloperError} Attribute must have a valid <code>componentDatatype</code> or not specify it.
@exception {DeveloperError} Attribute must have a <code>strideInBytes</code> less than or equal to 255 or not specify it.
@exception {DeveloperError} Index n is used by more than one attribute.


@example
// Example 1. Create a vertex array with vertices made up of three floating point
// values, e.g., a position, from a single vertex buffer.  No index buffer is used.
const positionBuffer = Buffer.createVertexBuffer({
    context : context,
    sizeInBytes : 12,
    usage : BufferUsage.STATIC_DRAW
});
const attributes = [
    {
        index                  : 0,
        enabled                : true,
        vertexBuffer           : positionBuffer,
        componentsPerAttribute : 3,
        componentDatatype      : ComponentDatatype.FLOAT,
        normalize              : false,
        offsetInBytes          : 0,
        strideInBytes          : 0 // tightly packed
        instanceDivisor        : 0 // not instanced
    }
];
const va = new VertexArray({
    context : context,
    attributes : attributes
});

@example
// Example 2. Create a vertex array with vertices from two different vertex buffers.
// Each vertex has a three-component position and three-component normal.
const positionBuffer = Buffer.createVertexBuffer({
    context : context,
    sizeInBytes : 12,
    usage : BufferUsage.STATIC_DRAW
});
const normalBuffer = Buffer.createVertexBuffer({
    context : context,
    sizeInBytes : 12,
    usage : BufferUsage.STATIC_DRAW
});
const attributes = [
    {
        index                  : 0,
        vertexBuffer           : positionBuffer,
        componentsPerAttribute : 3,
        componentDatatype      : ComponentDatatype.FLOAT
    },
    {
        index                  : 1,
        vertexBuffer           : normalBuffer,
        componentsPerAttribute : 3,
        componentDatatype      : ComponentDatatype.FLOAT
    }
];
const va = new VertexArray({
    context : context,
    attributes : attributes
});

@example
// Example 3. Creates the same vertex layout as Example 2 using a single
// vertex buffer, instead of two.
const buffer = Buffer.createVertexBuffer({
    context : context,
    sizeInBytes : 24,
    usage : BufferUsage.STATIC_DRAW
});
const attributes = [
    {
        vertexBuffer           : buffer,
        componentsPerAttribute : 3,
        componentDatatype      : ComponentDatatype.FLOAT,
        offsetInBytes          : 0,
        strideInBytes          : 24
    },
    {
        vertexBuffer           : buffer,
        componentsPerAttribute : 3,
        componentDatatype      : ComponentDatatype.FLOAT,
        normalize              : true,
        offsetInBytes          : 12,
        strideInBytes          : 24
    }
];
const va = new VertexArray({
    context : context,
    attributes : attributes
});

@see Buffer#createVertexBuffer
@see Buffer#createIndexBuffer
@see Context#draw

@private