Source: graphics/index-buffer.js

Object.assign(pc, function () {
    'use strict';

    /**
     * @constructor
     * @name pc.IndexBuffer
     * @classdesc An index buffer is the mechanism via which the application specifies primitive
     * index data to the graphics hardware.
     * @description Creates a new index buffer.
     * @example
     * // Create an index buffer holding 3 16-bit indices
     * // The buffer is marked as static, hinting that the buffer will never be modified
     * var indexBuffer = new pc.IndexBuffer(graphicsDevice, pc.INDEXFORMAT_UINT16, 3, pc.BUFFER_STATIC);
     * @param {pc.GraphicsDevice} graphicsDevice The graphics device used to manage this index buffer.
     * @param {Number} format The type of each index to be stored in the index buffer (see pc.INDEXFORMAT_*).
     * @param {Number} numIndices The number of indices to be stored in the index buffer.
     * @param {Number} [usage] The usage type of the vertex buffer (see pc.BUFFER_*).
     * @param {ArrayBuffer} [initialData] Initial data.
     */
    var IndexBuffer = function (graphicsDevice, format, numIndices, usage, initialData) {
        // By default, index buffers are static (better for performance since buffer data can be cached in VRAM)
        this.usage = usage || pc.BUFFER_STATIC;
        this.format = format;
        this.numIndices = numIndices;
        this.device = graphicsDevice;

        var gl = this.device.gl;

        // Allocate the storage
        var bytesPerIndex;
        if (format === pc.INDEXFORMAT_UINT8) {
            bytesPerIndex = 1;
            this.glFormat = gl.UNSIGNED_BYTE;
        } else if (format === pc.INDEXFORMAT_UINT16) {
            bytesPerIndex = 2;
            this.glFormat = gl.UNSIGNED_SHORT;
        } else if (format === pc.INDEXFORMAT_UINT32) {
            bytesPerIndex = 4;
            this.glFormat = gl.UNSIGNED_INT;
        }
        this.bytesPerIndex = bytesPerIndex;

        this.numBytes = this.numIndices * bytesPerIndex;

        if (initialData) {
            this.setData(initialData);
        } else {
            this.storage = new ArrayBuffer(this.numBytes);
        }

        graphicsDevice._vram.ib += this.numBytes;

        this.device.buffers.push(this);
    };

    Object.assign(IndexBuffer.prototype, {
        /**
         * @function
         * @name pc.IndexBuffer#destroy
         * @description Frees resources associated with this index buffer.
         */
        destroy: function () {
            var device = this.device;
            var idx = device.buffers.indexOf(this);
            if (idx !== -1) {
                device.buffers.splice(idx, 1);
            }

            if (this.bufferId) {
                var gl = this.device.gl;
                gl.deleteBuffer(this.bufferId);
                this.device._vram.ib -= this.storage.byteLength;
                this.bufferId = null;

                if (this.device.indexBuffer === this) {
                    this.device.indexBuffer = null;
                }
            }
        },

        /**
         * @function
         * @name pc.IndexBuffer#getFormat
         * @description Returns the data format of the specified index buffer.
         * @returns {Number} The data format of the specified index buffer (see pc.INDEXFORMAT_*).
         */
        getFormat: function () {
            return this.format;
        },

        /**
         * @function
         * @name pc.IndexBuffer#getNumIndices
         * @description Returns the number of indices stored in the specified index buffer.
         * @returns {Number} The number of indices stored in the specified index buffer.
         */
        getNumIndices: function () {
            return this.numIndices;
        },

        /**
         * @function
         * @name pc.IndexBuffer#lock
         * @description Gives access to the block of memory that stores the buffer's indices.
         * @returns {ArrayBuffer} A contiguous block of memory where index data can be written to.
         */
        lock: function () {
            return this.storage;
        },

        /**
         * @function
         * @name pc.IndexBuffer#unlock
         * @description Signals that the block of memory returned by a call to the lock function is
         * ready to be given to the graphics hardware. Only unlocked index buffers can be set on the
         * currently active device.
         */
        unlock: function () {
            // Upload the new index data
            var gl = this.device.gl;

            if (!this.bufferId) {
                this.bufferId = gl.createBuffer();
            }

            var glUsage;
            switch (this.usage) {
                case pc.BUFFER_STATIC:
                    glUsage = gl.STATIC_DRAW;
                    break;
                case pc.BUFFER_DYNAMIC:
                    glUsage = gl.DYNAMIC_DRAW;
                    break;
                case pc.BUFFER_STREAM:
                    glUsage = gl.STREAM_DRAW;
                    break;
                case pc.BUFFER_GPUDYNAMIC:
                    if (this.device.webgl2) {
                        glUsage = gl.DYNAMIC_COPY;
                    } else {
                        glUsage = gl.STATIC_DRAW;
                    }
                    break;
            }

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.bufferId);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.storage, glUsage);
        },

        setData: function (data) {
            if (data.byteLength !== this.numBytes) {
                console.error("IndexBuffer: wrong initial data size: expected " + this.numBytes + ", got " + data.byteLength);
                return false;
            }
            this.storage = data;
            this.unlock();
            return true;
        }
    });

    return {
        IndexBuffer: IndexBuffer
    };
}());