import { Logger } from "../Misc/logger.js";
import { Tools } from "../Misc/tools.js";
/**
 * Uniform buffer objects.
 *
 * Handles blocks of uniform on the GPU.
 *
 * If WebGL 2 is not available, this class falls back on traditional setUniformXXX calls.
 *
 * For more information, please refer to :
 * https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object
 */
export class UniformBuffer {
    /**
     * Instantiates a new Uniform buffer objects.
     *
     * Handles blocks of uniform on the GPU.
     *
     * If WebGL 2 is not available, this class falls back on traditional setUniformXXX calls.
     *
     * For more information, please refer to :
     * @see https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object
     * @param engine Define the engine the buffer is associated with
     * @param data Define the data contained in the buffer
     * @param dynamic Define if the buffer is updatable
     * @param name to assign to the buffer (debugging purpose)
     * @param forceNoUniformBuffer define that this object must not rely on UBO objects
     * @param trackUBOsInFrame define if the UBOs should be tracked in the frame (default: undefined - will use the value from Engine._features.trackUbosInFrame)
     */
    constructor(engine, data, dynamic = false, name, forceNoUniformBuffer = false, trackUBOsInFrame) {
        // Matrix cache
        this._valueCache = {};
        this._engine = engine;
        this._noUBO = !engine.supportsUniformBuffers || forceNoUniformBuffer;
        this._dynamic = dynamic;
        this._name = name ?? "no-name";
        this._data = data || [];
        this._uniformLocations = {};
        this._uniformSizes = {};
        this._uniformArraySizes = {};
        this._uniformLocationPointer = 0;
        this._needSync = false;
        this._trackUBOsInFrame = false;
        if ((trackUBOsInFrame === undefined && this._engine._features.trackUbosInFrame) || trackUBOsInFrame === true) {
            this._buffers = [];
            this._bufferIndex = -1;
            this._createBufferOnWrite = false;
            this._currentFrameId = 0;
            this._trackUBOsInFrame = true;
        }
        if (this._noUBO) {
            this.updateMatrix3x3 = this._updateMatrix3x3ForEffect;
            this.updateMatrix2x2 = this._updateMatrix2x2ForEffect;
            this.updateFloat = this._updateFloatForEffect;
            this.updateFloat2 = this._updateFloat2ForEffect;
            this.updateFloat3 = this._updateFloat3ForEffect;
            this.updateFloat4 = this._updateFloat4ForEffect;
            this.updateFloatArray = this._updateFloatArrayForEffect;
            this.updateArray = this._updateArrayForEffect;
            this.updateIntArray = this._updateIntArrayForEffect;
            this.updateUIntArray = this._updateUIntArrayForEffect;
            this.updateMatrix = this._updateMatrixForEffect;
            this.updateMatrices = this._updateMatricesForEffect;
            this.updateVector3 = this._updateVector3ForEffect;
            this.updateVector4 = this._updateVector4ForEffect;
            this.updateColor3 = this._updateColor3ForEffect;
            this.updateColor4 = this._updateColor4ForEffect;
            this.updateDirectColor4 = this._updateDirectColor4ForEffect;
            this.updateInt = this._updateIntForEffect;
            this.updateInt2 = this._updateInt2ForEffect;
            this.updateInt3 = this._updateInt3ForEffect;
            this.updateInt4 = this._updateInt4ForEffect;
            this.updateUInt = this._updateUIntForEffect;
            this.updateUInt2 = this._updateUInt2ForEffect;
            this.updateUInt3 = this._updateUInt3ForEffect;
            this.updateUInt4 = this._updateUInt4ForEffect;
        }
        else {
            this._engine._uniformBuffers.push(this);
            this.updateMatrix3x3 = this._updateMatrix3x3ForUniform;
            this.updateMatrix2x2 = this._updateMatrix2x2ForUniform;
            this.updateFloat = this._updateFloatForUniform;
            this.updateFloat2 = this._updateFloat2ForUniform;
            this.updateFloat3 = this._updateFloat3ForUniform;
            this.updateFloat4 = this._updateFloat4ForUniform;
            this.updateFloatArray = this._updateFloatArrayForUniform;
            this.updateArray = this._updateArrayForUniform;
            this.updateIntArray = this._updateIntArrayForUniform;
            this.updateUIntArray = this._updateUIntArrayForUniform;
            this.updateMatrix = this._updateMatrixForUniform;
            this.updateMatrices = this._updateMatricesForUniform;
            this.updateVector3 = this._updateVector3ForUniform;
            this.updateVector4 = this._updateVector4ForUniform;
            this.updateColor3 = this._updateColor3ForUniform;
            this.updateColor4 = this._updateColor4ForUniform;
            this.updateDirectColor4 = this._updateDirectColor4ForUniform;
            this.updateInt = this._updateIntForUniform;
            this.updateInt2 = this._updateInt2ForUniform;
            this.updateInt3 = this._updateInt3ForUniform;
            this.updateInt4 = this._updateInt4ForUniform;
            this.updateUInt = this._updateUIntForUniform;
            this.updateUInt2 = this._updateUInt2ForUniform;
            this.updateUInt3 = this._updateUInt3ForUniform;
            this.updateUInt4 = this._updateUInt4ForUniform;
        }
    }
    /**
     * Indicates if the buffer is using the WebGL2 UBO implementation,
     * or just falling back on setUniformXXX calls.
     */
    get useUbo() {
        return !this._noUBO;
    }
    /**
     * Indicates if the WebGL underlying uniform buffer is in sync
     * with the javascript cache data.
     */
    get isSync() {
        return !this._needSync;
    }
    /**
     * Indicates if the WebGL underlying uniform buffer is dynamic.
     * Also, a dynamic UniformBuffer will disable cache verification and always
     * update the underlying WebGL uniform buffer to the GPU.
     * @returns if Dynamic, otherwise false
     */
    isDynamic() {
        return this._dynamic;
    }
    /**
     * The data cache on JS side.
     * @returns the underlying data as a float array
     */
    getData() {
        return this._bufferData;
    }
    /**
     * The underlying WebGL Uniform buffer.
     * @returns the webgl buffer
     */
    getBuffer() {
        return this._buffer;
    }
    /**
     * std140 layout specifies how to align data within an UBO structure.
     * See https://khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159
     * for specs.
     * @param size
     */
    _fillAlignment(size) {
        // This code has been simplified because we only use floats, vectors of 1, 2, 3, 4 components
        // and 4x4 matrices
        // TODO : change if other types are used
        let alignment;
        if (size <= 2) {
            alignment = size;
        }
        else {
            alignment = 4;
        }
        if (this._uniformLocationPointer % alignment !== 0) {
            const oldPointer = this._uniformLocationPointer;
            this._uniformLocationPointer += alignment - (this._uniformLocationPointer % alignment);
            const diff = this._uniformLocationPointer - oldPointer;
            for (let i = 0; i < diff; i++) {
                this._data.push(0);
            }
        }
    }
    /**
     * Adds an uniform in the buffer.
     * Warning : the subsequents calls of this function must be in the same order as declared in the shader
     * for the layout to be correct ! The addUniform function only handles types like float, vec2, vec3, vec4, mat4,
     * meaning size=1,2,3,4 or 16. It does not handle struct types.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     * @param size Data size, or data directly.
     * @param arraySize The number of elements in the array, 0 if not an array.
     */
    addUniform(name, size, arraySize = 0) {
        if (arraySize > 0 && typeof size === "number") {
            // Keep track of stride for `updateFloatArray`
            this._uniformArraySizes[name] = { strideSize: size, arraySize };
        }
        if (this._noUBO) {
            return;
        }
        if (this._uniformLocations[name] !== undefined) {
            // Already existing uniform
            return;
        }
        // This function must be called in the order of the shader layout !
        // size can be the size of the uniform, or data directly
        let data;
        // std140 FTW...
        if (arraySize > 0) {
            if (size instanceof Array) {
                // eslint-disable-next-line no-throw-literal
                throw "addUniform should not be use with Array in UBO: " + name;
            }
            this._fillAlignment(4);
            if (size == 16) {
                size = size * arraySize;
            }
            else {
                const perElementPadding = 4 - size;
                const totalPadding = perElementPadding * arraySize;
                size = size * arraySize + totalPadding;
            }
            data = [];
            // Fill with zeros
            for (let i = 0; i < size; i++) {
                data.push(0);
            }
        }
        else {
            if (size instanceof Array) {
                data = size;
                size = data.length;
            }
            else {
                data = [];
                // Fill with zeros
                for (let i = 0; i < size; i++) {
                    data.push(0);
                }
            }
            this._fillAlignment(size);
        }
        this._uniformSizes[name] = size;
        this._uniformLocations[name] = this._uniformLocationPointer;
        this._uniformLocationPointer += size;
        for (let i = 0; i < size; i++) {
            this._data.push(data[i]);
        }
        this._needSync = true;
    }
    /**
     * Adds a Matrix 4x4 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     * @param mat A 4x4 matrix.
     */
    addMatrix(name, mat) {
        this.addUniform(name, Array.prototype.slice.call(mat.asArray()));
    }
    /**
     * Adds a vec2 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     * @param x Define the x component value of the vec2
     * @param y Define the y component value of the vec2
     */
    addFloat2(name, x, y) {
        const temp = [x, y];
        this.addUniform(name, temp);
    }
    /**
     * Adds a vec3 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     * @param x Define the x component value of the vec3
     * @param y Define the y component value of the vec3
     * @param z Define the z component value of the vec3
     */
    addFloat3(name, x, y, z) {
        const temp = [x, y, z];
        this.addUniform(name, temp);
    }
    /**
     * Adds a vec3 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     * @param color Define the vec3 from a Color
     */
    addColor3(name, color) {
        const temp = [color.r, color.g, color.b];
        this.addUniform(name, temp);
    }
    /**
     * Adds a vec4 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     * @param color Define the rgb components from a Color
     * @param alpha Define the a component of the vec4
     */
    addColor4(name, color, alpha) {
        const temp = [color.r, color.g, color.b, alpha];
        this.addUniform(name, temp);
    }
    /**
     * Adds a vec3 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     * @param vector Define the vec3 components from a Vector
     */
    addVector3(name, vector) {
        const temp = [vector.x, vector.y, vector.z];
        this.addUniform(name, temp);
    }
    /**
     * Adds a Matrix 3x3 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     */
    addMatrix3x3(name) {
        this.addUniform(name, 12);
    }
    /**
     * Adds a Matrix 2x2 to the uniform buffer.
     * @param name Name of the uniform, as used in the uniform block in the shader.
     */
    addMatrix2x2(name) {
        this.addUniform(name, 8);
    }
    /**
     * Effectively creates the WebGL Uniform Buffer, once layout is completed with `addUniform`.
     */
    create() {
        if (this._noUBO) {
            return;
        }
        if (this._buffer) {
            return; // nothing to do
        }
        // See spec, alignment must be filled as a vec4
        this._fillAlignment(4);
        this._bufferData = new Float32Array(this._data);
        this._rebuild();
        this._needSync = true;
    }
    // The result of this method is used for debugging purpose, as part of the buffer name
    // It is meant to more easily know what this buffer is about when debugging
    // Some buffers can have a lot of uniforms (several dozens), so the method only returns the first 10 of them
    // (should be enough to understand what the buffer is for)
    _getNames() {
        const names = [];
        let i = 0;
        for (const name in this._uniformLocations) {
            names.push(name);
            if (++i === 10) {
                break;
            }
        }
        return names.join(",");
    }
    /** @internal */
    _rebuild() {
        if (this._noUBO || !this._bufferData) {
            return;
        }
        if (this._dynamic) {
            this._buffer = this._engine.createDynamicUniformBuffer(this._bufferData, this._name + "_UniformList:" + this._getNames());
        }
        else {
            this._buffer = this._engine.createUniformBuffer(this._bufferData, this._name + "_UniformList:" + this._getNames());
        }
        if (this._trackUBOsInFrame) {
            this._buffers.push([this._buffer, this._engine._features.checkUbosContentBeforeUpload ? this._bufferData.slice() : undefined]);
            this._bufferIndex = this._buffers.length - 1;
            this._createBufferOnWrite = false;
        }
    }
    /** @internal */
    _rebuildAfterContextLost() {
        if (this._trackUBOsInFrame) {
            this._buffers = [];
            this._currentFrameId = 0;
        }
        this._rebuild();
    }
    /** @internal */
    get _numBuffers() {
        return this._buffers.length;
    }
    /** @internal */
    get _indexBuffer() {
        return this._bufferIndex;
    }
    /** Gets or sets the name of this buffer */
    get name() {
        return this._name;
    }
    set name(value) {
        this._name = value;
    }
    /** Gets the current effect */
    get currentEffect() {
        return this._currentEffect;
    }
    _buffersEqual(buf1, buf2) {
        for (let i = 0; i < buf1.length; ++i) {
            if (buf1[i] !== buf2[i]) {
                return false;
            }
        }
        return true;
    }
    _copyBuffer(src, dst) {
        for (let i = 0; i < src.length; ++i) {
            dst[i] = src[i];
        }
    }
    /**
     * Updates the WebGL Uniform Buffer on the GPU.
     * If the `dynamic` flag is set to true, no cache comparison is done.
     * Otherwise, the buffer will be updated only if the cache differs.
     */
    update() {
        if (this._noUBO) {
            return;
        }
        this.bindUniformBuffer();
        if (!this._buffer) {
            this.create();
            return;
        }
        if (!this._dynamic && !this._needSync) {
            this._createBufferOnWrite = this._trackUBOsInFrame;
            return;
        }
        if (this._buffers && this._buffers.length > 1 && this._buffers[this._bufferIndex][1]) {
            if (this._buffersEqual(this._bufferData, this._buffers[this._bufferIndex][1])) {
                this._needSync = false;
                this._createBufferOnWrite = this._trackUBOsInFrame;
                return;
            }
            else {
                this._copyBuffer(this._bufferData, this._buffers[this._bufferIndex][1]);
            }
        }
        this._engine.updateUniformBuffer(this._buffer, this._bufferData);
        if (this._engine._features._collectUbosUpdatedInFrame) {
            if (!UniformBuffer._UpdatedUbosInFrame[this._name]) {
                UniformBuffer._UpdatedUbosInFrame[this._name] = 0;
            }
            UniformBuffer._UpdatedUbosInFrame[this._name]++;
        }
        this._needSync = false;
        this._createBufferOnWrite = this._trackUBOsInFrame;
    }
    _createNewBuffer() {
        if (this._bufferIndex + 1 < this._buffers.length) {
            this._bufferIndex++;
            this._buffer = this._buffers[this._bufferIndex][0];
            this._createBufferOnWrite = false;
            this._needSync = true;
        }
        else {
            this._rebuild();
        }
    }
    _checkNewFrame() {
        if (this._trackUBOsInFrame && this._currentFrameId !== this._engine.frameId) {
            this._currentFrameId = this._engine.frameId;
            this._createBufferOnWrite = false;
            if (this._buffers && this._buffers.length > 0) {
                this._needSync = this._bufferIndex !== 0;
                this._bufferIndex = 0;
                this._buffer = this._buffers[this._bufferIndex][0];
            }
            else {
                this._bufferIndex = -1;
            }
        }
    }
    /**
     * Updates the value of an uniform. The `update` method must be called afterwards to make it effective in the GPU.
     * @param uniformName Define the name of the uniform, as used in the uniform block in the shader.
     * @param data Define the flattened data
     * @param size Define the size of the data.
     */
    updateUniform(uniformName, data, size) {
        this._checkNewFrame();
        let location = this._uniformLocations[uniformName];
        if (location === undefined) {
            if (this._buffer) {
                // Cannot add an uniform if the buffer is already created
                Logger.Error("Cannot add an uniform after UBO has been created. uniformName=" + uniformName);
                return;
            }
            this.addUniform(uniformName, size);
            location = this._uniformLocations[uniformName];
        }
        if (!this._buffer) {
            this.create();
        }
        if (!this._dynamic) {
            // Cache for static uniform buffers
            let changed = false;
            for (let i = 0; i < size; i++) {
                // We are checking the matrix cache before calling updateUniform so we do not need to check it here
                // Hence the test for size === 16 to simply commit the matrix values
                if ((size === 16 && !this._engine._features.uniformBufferHardCheckMatrix) || this._bufferData[location + i] !== Math.fround(data[i])) {
                    changed = true;
                    if (this._createBufferOnWrite) {
                        this._createNewBuffer();
                    }
                    this._bufferData[location + i] = data[i];
                }
            }
            this._needSync = this._needSync || changed;
        }
        else {
            // No cache for dynamic
            for (let i = 0; i < size; i++) {
                this._bufferData[location + i] = data[i];
            }
        }
    }
    /**
     * Updates the value of an uniform. The `update` method must be called afterwards to make it effective in the GPU.
     * @param uniformName Define the name of the uniform, as used in the uniform block in the shader.
     * @param data Define the flattened data
     * @param size Define the size of the data.
     */
    updateUniformArray(uniformName, data, size) {
        this._checkNewFrame();
        const location = this._uniformLocations[uniformName];
        if (location === undefined) {
            Logger.Error("Cannot add an uniform Array dynamically. Please, add it using addUniform and make sure that uniform buffers are supported by the current engine.");
            return;
        }
        if (!this._buffer) {
            this.create();
        }
        const arraySizes = this._uniformArraySizes[uniformName];
        if (!this._dynamic) {
            // Cache for static uniform buffers
            let changed = false;
            let countToFour = 0;
            let baseStride = 0;
            for (let i = 0; i < size; i++) {
                if (this._bufferData[location + baseStride * 4 + countToFour] !== Tools.FloatRound(data[i])) {
                    changed = true;
                    if (this._createBufferOnWrite) {
                        this._createNewBuffer();
                    }
                    this._bufferData[location + baseStride * 4 + countToFour] = data[i];
                }
                countToFour++;
                if (countToFour === arraySizes.strideSize) {
                    for (; countToFour < 4; countToFour++) {
                        this._bufferData[location + baseStride * 4 + countToFour] = 0;
                    }
                    countToFour = 0;
                    baseStride++;
                }
            }
            this._needSync = this._needSync || changed;
        }
        else {
            // No cache for dynamic
            for (let i = 0; i < size; i++) {
                this._bufferData[location + i] = data[i];
            }
        }
    }
    _cacheMatrix(name, matrix) {
        this._checkNewFrame();
        const cache = this._valueCache[name];
        const flag = matrix.updateFlag;
        if (cache !== undefined && cache === flag) {
            return false;
        }
        this._valueCache[name] = flag;
        return true;
    }
    // Update methods
    _updateMatrix3x3ForUniform(name, matrix) {
        // To match std140, matrix must be realigned
        for (let i = 0; i < 3; i++) {
            UniformBuffer._TempBuffer[i * 4] = matrix[i * 3];
            UniformBuffer._TempBuffer[i * 4 + 1] = matrix[i * 3 + 1];
            UniformBuffer._TempBuffer[i * 4 + 2] = matrix[i * 3 + 2];
            UniformBuffer._TempBuffer[i * 4 + 3] = 0.0;
        }
        this.updateUniform(name, UniformBuffer._TempBuffer, 12);
    }
    _updateMatrix3x3ForEffect(name, matrix) {
        this._currentEffect.setMatrix3x3(name, matrix);
    }
    _updateMatrix2x2ForEffect(name, matrix) {
        this._currentEffect.setMatrix2x2(name, matrix);
    }
    _updateMatrix2x2ForUniform(name, matrix) {
        // To match std140, matrix must be realigned
        for (let i = 0; i < 2; i++) {
            UniformBuffer._TempBuffer[i * 4] = matrix[i * 2];
            UniformBuffer._TempBuffer[i * 4 + 1] = matrix[i * 2 + 1];
            UniformBuffer._TempBuffer[i * 4 + 2] = 0.0;
            UniformBuffer._TempBuffer[i * 4 + 3] = 0.0;
        }
        this.updateUniform(name, UniformBuffer._TempBuffer, 8);
    }
    _updateFloatForEffect(name, x, suffix = "") {
        this._currentEffect.setFloat(name + suffix, x);
    }
    _updateFloatForUniform(name, x) {
        UniformBuffer._TempBuffer[0] = x;
        this.updateUniform(name, UniformBuffer._TempBuffer, 1);
    }
    _updateFloat2ForEffect(name, x, y, suffix = "") {
        this._currentEffect.setFloat2(name + suffix, x, y);
    }
    _updateFloat2ForUniform(name, x, y) {
        UniformBuffer._TempBuffer[0] = x;
        UniformBuffer._TempBuffer[1] = y;
        this.updateUniform(name, UniformBuffer._TempBuffer, 2);
    }
    _updateFloat3ForEffect(name, x, y, z, suffix = "") {
        this._currentEffect.setFloat3(name + suffix, x, y, z);
    }
    _updateFloat3ForUniform(name, x, y, z) {
        UniformBuffer._TempBuffer[0] = x;
        UniformBuffer._TempBuffer[1] = y;
        UniformBuffer._TempBuffer[2] = z;
        this.updateUniform(name, UniformBuffer._TempBuffer, 3);
    }
    _updateFloat4ForEffect(name, x, y, z, w, suffix = "") {
        this._currentEffect.setFloat4(name + suffix, x, y, z, w);
    }
    _updateFloat4ForUniform(name, x, y, z, w) {
        UniformBuffer._TempBuffer[0] = x;
        UniformBuffer._TempBuffer[1] = y;
        UniformBuffer._TempBuffer[2] = z;
        UniformBuffer._TempBuffer[3] = w;
        this.updateUniform(name, UniformBuffer._TempBuffer, 4);
    }
    _updateFloatArrayForEffect(name, array, suffix = "") {
        switch (this._uniformArraySizes[name]?.strideSize) {
            case 2:
                this._currentEffect.setFloatArray2(name + suffix, array);
                break;
            case 3:
                this._currentEffect.setFloatArray3(name + suffix, array);
                break;
            case 4:
                this._currentEffect.setFloatArray4(name + suffix, array);
                break;
            default:
                this._currentEffect.setFloatArray(name + suffix, array);
                break;
        }
    }
    _updateFloatArrayForUniform(name, array) {
        this.updateUniformArray(name, array, array.length);
    }
    _updateArrayForEffect(name, array) {
        this._currentEffect.setArray(name, array);
    }
    _updateArrayForUniform(name, array) {
        this.updateUniformArray(name, array, array.length);
    }
    _updateIntArrayForEffect(name, array) {
        this._currentEffect.setIntArray(name, array);
    }
    _updateIntArrayForUniform(name, array) {
        UniformBuffer._TempBufferInt32View.set(array);
        this.updateUniformArray(name, UniformBuffer._TempBuffer, array.length);
    }
    _updateUIntArrayForEffect(name, array) {
        this._currentEffect.setUIntArray(name, array);
    }
    _updateUIntArrayForUniform(name, array) {
        UniformBuffer._TempBufferUInt32View.set(array);
        this.updateUniformArray(name, UniformBuffer._TempBuffer, array.length);
    }
    _updateMatrixForEffect(name, mat) {
        this._currentEffect.setMatrix(name, mat);
    }
    _updateMatrixForUniform(name, mat) {
        if (this._cacheMatrix(name, mat)) {
            this.updateUniform(name, mat.asArray(), 16);
        }
    }
    _updateMatricesForEffect(name, mat) {
        this._currentEffect.setMatrices(name, mat);
    }
    _updateMatricesForUniform(name, mat) {
        this.updateUniform(name, mat, mat.length);
    }
    _updateVector3ForEffect(name, vector) {
        this._currentEffect.setVector3(name, vector);
    }
    _updateVector3ForUniform(name, vector) {
        UniformBuffer._TempBuffer[0] = vector.x;
        UniformBuffer._TempBuffer[1] = vector.y;
        UniformBuffer._TempBuffer[2] = vector.z;
        this.updateUniform(name, UniformBuffer._TempBuffer, 3);
    }
    _updateVector4ForEffect(name, vector) {
        this._currentEffect.setVector4(name, vector);
    }
    _updateVector4ForUniform(name, vector) {
        UniformBuffer._TempBuffer[0] = vector.x;
        UniformBuffer._TempBuffer[1] = vector.y;
        UniformBuffer._TempBuffer[2] = vector.z;
        UniformBuffer._TempBuffer[3] = vector.w;
        this.updateUniform(name, UniformBuffer._TempBuffer, 4);
    }
    _updateColor3ForEffect(name, color, suffix = "") {
        this._currentEffect.setColor3(name + suffix, color);
    }
    _updateColor3ForUniform(name, color) {
        UniformBuffer._TempBuffer[0] = color.r;
        UniformBuffer._TempBuffer[1] = color.g;
        UniformBuffer._TempBuffer[2] = color.b;
        this.updateUniform(name, UniformBuffer._TempBuffer, 3);
    }
    _updateColor4ForEffect(name, color, alpha, suffix = "") {
        this._currentEffect.setColor4(name + suffix, color, alpha);
    }
    _updateDirectColor4ForEffect(name, color, suffix = "") {
        this._currentEffect.setDirectColor4(name + suffix, color);
    }
    _updateColor4ForUniform(name, color, alpha) {
        UniformBuffer._TempBuffer[0] = color.r;
        UniformBuffer._TempBuffer[1] = color.g;
        UniformBuffer._TempBuffer[2] = color.b;
        UniformBuffer._TempBuffer[3] = alpha;
        this.updateUniform(name, UniformBuffer._TempBuffer, 4);
    }
    _updateDirectColor4ForUniform(name, color) {
        UniformBuffer._TempBuffer[0] = color.r;
        UniformBuffer._TempBuffer[1] = color.g;
        UniformBuffer._TempBuffer[2] = color.b;
        UniformBuffer._TempBuffer[3] = color.a;
        this.updateUniform(name, UniformBuffer._TempBuffer, 4);
    }
    _updateIntForEffect(name, x, suffix = "") {
        this._currentEffect.setInt(name + suffix, x);
    }
    _updateIntForUniform(name, x) {
        UniformBuffer._TempBufferInt32View[0] = x;
        this.updateUniform(name, UniformBuffer._TempBuffer, 1);
    }
    _updateInt2ForEffect(name, x, y, suffix = "") {
        this._currentEffect.setInt2(name + suffix, x, y);
    }
    _updateInt2ForUniform(name, x, y) {
        UniformBuffer._TempBufferInt32View[0] = x;
        UniformBuffer._TempBufferInt32View[1] = y;
        this.updateUniform(name, UniformBuffer._TempBuffer, 2);
    }
    _updateInt3ForEffect(name, x, y, z, suffix = "") {
        this._currentEffect.setInt3(name + suffix, x, y, z);
    }
    _updateInt3ForUniform(name, x, y, z) {
        UniformBuffer._TempBufferInt32View[0] = x;
        UniformBuffer._TempBufferInt32View[1] = y;
        UniformBuffer._TempBufferInt32View[2] = z;
        this.updateUniform(name, UniformBuffer._TempBuffer, 3);
    }
    _updateInt4ForEffect(name, x, y, z, w, suffix = "") {
        this._currentEffect.setInt4(name + suffix, x, y, z, w);
    }
    _updateInt4ForUniform(name, x, y, z, w) {
        UniformBuffer._TempBufferInt32View[0] = x;
        UniformBuffer._TempBufferInt32View[1] = y;
        UniformBuffer._TempBufferInt32View[2] = z;
        UniformBuffer._TempBufferInt32View[3] = w;
        this.updateUniform(name, UniformBuffer._TempBuffer, 4);
    }
    _updateUIntForEffect(name, x, suffix = "") {
        this._currentEffect.setUInt(name + suffix, x);
    }
    _updateUIntForUniform(name, x) {
        UniformBuffer._TempBufferUInt32View[0] = x;
        this.updateUniform(name, UniformBuffer._TempBuffer, 1);
    }
    _updateUInt2ForEffect(name, x, y, suffix = "") {
        this._currentEffect.setUInt2(name + suffix, x, y);
    }
    _updateUInt2ForUniform(name, x, y) {
        UniformBuffer._TempBufferUInt32View[0] = x;
        UniformBuffer._TempBufferUInt32View[1] = y;
        this.updateUniform(name, UniformBuffer._TempBuffer, 2);
    }
    _updateUInt3ForEffect(name, x, y, z, suffix = "") {
        this._currentEffect.setUInt3(name + suffix, x, y, z);
    }
    _updateUInt3ForUniform(name, x, y, z) {
        UniformBuffer._TempBufferUInt32View[0] = x;
        UniformBuffer._TempBufferUInt32View[1] = y;
        UniformBuffer._TempBufferUInt32View[2] = z;
        this.updateUniform(name, UniformBuffer._TempBuffer, 3);
    }
    _updateUInt4ForEffect(name, x, y, z, w, suffix = "") {
        this._currentEffect.setUInt4(name + suffix, x, y, z, w);
    }
    _updateUInt4ForUniform(name, x, y, z, w) {
        UniformBuffer._TempBufferUInt32View[0] = x;
        UniformBuffer._TempBufferUInt32View[1] = y;
        UniformBuffer._TempBufferUInt32View[2] = z;
        UniformBuffer._TempBufferUInt32View[3] = w;
        this.updateUniform(name, UniformBuffer._TempBuffer, 4);
    }
    /**
     * Sets a sampler uniform on the effect.
     * @param name Define the name of the sampler.
     * @param texture Define the texture to set in the sampler
     */
    setTexture(name, texture) {
        this._currentEffect.setTexture(name, texture);
    }
    /**
     * Sets an array of sampler uniforms on the effect.
     * @param name Define the name of uniform.
     * @param textures Define the textures to set in the array of samplers
     */
    setTextureArray(name, textures) {
        this._currentEffect.setTextureArray(name, textures);
    }
    /**
     * Sets a sampler uniform on the effect.
     * @param name Define the name of the sampler.
     * @param texture Define the (internal) texture to set in the sampler
     */
    bindTexture(name, texture) {
        this._currentEffect._bindTexture(name, texture);
    }
    /**
     * Directly updates the value of the uniform in the cache AND on the GPU.
     * @param uniformName Define the name of the uniform, as used in the uniform block in the shader.
     * @param data Define the flattened data
     */
    updateUniformDirectly(uniformName, data) {
        this.updateUniform(uniformName, data, data.length);
        this.update();
    }
    /**
     * Associates an effect to this uniform buffer
     * @param effect Define the effect to associate the buffer to
     * @param name Name of the uniform block in the shader.
     */
    bindToEffect(effect, name) {
        this._currentEffect = effect;
        this._currentEffectName = name;
    }
    /**
     * Binds the current (GPU) buffer to the effect
     */
    bindUniformBuffer() {
        if (!this._noUBO && this._buffer && this._currentEffect) {
            this._currentEffect.bindUniformBuffer(this._buffer, this._currentEffectName);
        }
    }
    /**
     * Dissociates the current effect from this uniform buffer
     */
    unbindEffect() {
        this._currentEffect = undefined;
        this._currentEffectName = undefined;
    }
    /**
     * Sets the current state of the class (_bufferIndex, _buffer) to point to the data buffer passed in parameter if this buffer is one of the buffers handled by the class (meaning if it can be found in the _buffers array)
     * This method is meant to be able to update a buffer at any time: just call setDataBuffer to set the class in the right state, call some updateXXX methods and then call udpate() => that will update the GPU buffer on the graphic card
     * @param dataBuffer buffer to look for
     * @returns true if the buffer has been found and the class internal state points to it, else false
     */
    setDataBuffer(dataBuffer) {
        if (!this._buffers) {
            return this._buffer === dataBuffer;
        }
        for (let b = 0; b < this._buffers.length; ++b) {
            const buffer = this._buffers[b];
            if (buffer[0] === dataBuffer) {
                this._bufferIndex = b;
                this._buffer = dataBuffer;
                this._createBufferOnWrite = false;
                this._currentEffect = undefined;
                // Note that if _buffers.length == 1, we don't copy _bufferData into _buffers[_bufferIndex][1] (see the update() method), and _bufferData already contains the right data
                if (this._buffers.length > 1 && this._buffers[b][1]) {
                    this._bufferData.set(this._buffers[b][1]);
                }
                this._valueCache = {};
                // The following line prevents the current buffer (_buffer / _bufferIndex) from being updated during subsequent calls to updateXXX() due to a call to _checkNewFrame()
                // If we called setDataBuffer, it means that we want to update the buffer we just defined and not another one (_checkNewFrame() can modify the current buffer).
                this._currentFrameId = this._engine.frameId;
                return true;
            }
        }
        return false;
    }
    /**
     * Checks if the uniform buffer has a uniform with the given name.
     * @param name Name of the uniform to check
     * @returns True if the uniform exists, false otherwise.
     */
    has(name) {
        return this._uniformLocations[name] !== undefined;
    }
    /**
     * Disposes the uniform buffer.
     */
    dispose() {
        if (this._noUBO) {
            return;
        }
        const uniformBuffers = this._engine._uniformBuffers;
        const index = uniformBuffers.indexOf(this);
        if (index !== -1) {
            uniformBuffers[index] = uniformBuffers[uniformBuffers.length - 1];
            uniformBuffers.pop();
        }
        if (this._trackUBOsInFrame && this._buffers) {
            for (let i = 0; i < this._buffers.length; ++i) {
                const buffer = this._buffers[i][0];
                this._engine._releaseBuffer(buffer);
            }
        }
        else if (this._buffer && this._engine._releaseBuffer(this._buffer)) {
            this._buffer = null;
        }
    }
}
/** @internal */
UniformBuffer._UpdatedUbosInFrame = {};
// Pool for avoiding memory leaks
UniformBuffer._MAX_UNIFORM_SIZE = 256;
UniformBuffer._TempBuffer = new Float32Array(UniformBuffer._MAX_UNIFORM_SIZE);
UniformBuffer._TempBufferInt32View = new Int32Array(UniformBuffer._TempBuffer.buffer);
UniformBuffer._TempBufferUInt32View = new Uint32Array(UniformBuffer._TempBuffer.buffer);
//# sourceMappingURL=uniformBuffer.js.map