UNPKG

@babylonjs/viewer

Version:

The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.

2,148 lines 93.5 kB
import { T as ThinWebGPUEngine, a as WebGPUCacheRenderPipeline, b as WebGPUTextureHelper, c as WebGPUPerfCounter, d as WebGPURenderItemBeginOcclusionQuery, e as WebGPURenderItemEndOcclusionQuery, W as WebGPUEngine } from './webgpuEngine.pure-BsxyiCJX.esm.js';
import './clearQuad.vertex-Jpm08Tfg.esm.js';
import './clearQuad.fragment-C9nJq5fW.esm.js';
import { a7 as VertexBuffer, cG as GetTypeByteLength, bk as Buffer, O as Observable, V as Vector3, aD as Quaternion, M as Matrix, a as EngineStore, A as AbstractEngine, C as Constants, w as InternalTexture, y as Logger, cH as GetTypeForDepthTexture, cn as HasStencilAspect } from './index-HyNDfLMI.esm.js';
import './instancesVertex-Bxgat6Ds.esm.js';
import './bakedVertexAnimation-DVILfFlx.esm.js';
import './instancesDeclaration-DRfPI11R.esm.js';
import './helperFunctions-DiIK2X1p.esm.js';
import './fresnelFunction-DLLiAPwB.esm.js';
import './meshUboDeclaration-B0E3KZKP.esm.js';
import './sceneUboDeclaration-Bn4-CjGH.esm.js';
import './decalFragment-CpDzppAv.esm.js';
import { R as RenderTargetWrapper } from './renderTargetWrapper-B34Fh8cU.esm.js';
import { e as _SpatialAudioAttacherComponent, d as _WebAudioParameterComponent, f as _SpatialWebAudioUpdaterComponent, A as AbstractAudioNode } from './spatialWebAudioUpdaterComponent-iX4lHNSn.esm.js';
import './sphericalPolynomial.pure-Tla2dEm0.esm.js';
import './textureLoaderManager-GZAmSsDa.esm.js';

/** This file must only contain pure code and pure imports */
let _Registered$a = false;
/**
 * Register side effects for bufferAlign.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterBufferAlign() {
    if (_Registered$a) {
        return;
    }
    _Registered$a = true;
    // eslint-disable-next-line @typescript-eslint/naming-convention
    const IsLittleEndian = (() => {
        const array = new Uint8Array(4);
        const view = new Uint32Array(array.buffer);
        return !!((view[0] = 1) & array[0]);
    })();
    Object.defineProperty(VertexBuffer.prototype, "effectiveByteStride", {
        get: function () {
            return (this._alignedBuffer && this._alignedBuffer.byteStride) || this.byteStride;
        },
        enumerable: true,
        configurable: true,
    });
    Object.defineProperty(VertexBuffer.prototype, "effectiveByteOffset", {
        get: function () {
            return this._alignedBuffer ? 0 : this.byteOffset;
        },
        enumerable: true,
        configurable: true,
    });
    Object.defineProperty(VertexBuffer.prototype, "effectiveBuffer", {
        get: function () {
            return (this._alignedBuffer && this._alignedBuffer.getBuffer()) || this._buffer.getBuffer();
        },
        enumerable: true,
        configurable: true,
    });
    VertexBuffer.prototype._rebuild = function () {
        this._buffer?._rebuild();
        this._alignedBuffer?._rebuild();
    };
    VertexBuffer.prototype.dispose = function () {
        if (this._ownsBuffer) {
            this._buffer.dispose();
        }
        this._alignedBuffer?.dispose();
        this._alignedBuffer = undefined;
        this._isDisposed = true;
    };
    VertexBuffer.prototype.getWrapperBuffer = function () {
        return this._alignedBuffer || this._buffer;
    };
    VertexBuffer.prototype._alignBuffer = function () {
        const data = this._buffer.getData();
        if (!this.engine._features.forceVertexBufferStrideAndOffsetMultiple4Bytes || (this.byteStride % 4 === 0 && this.byteOffset % 4 === 0) || !data) {
            return;
        }
        const typeByteLength = GetTypeByteLength(this.type);
        const alignedByteStride = (this.byteStride + 3) & -4;
        const alignedSize = alignedByteStride / typeByteLength;
        const totalVertices = this._maxVerticesCount;
        const totalByteLength = totalVertices * alignedByteStride;
        const totalLength = totalByteLength / typeByteLength;
        let sourceData;
        if (Array.isArray(data)) {
            const sourceDataAsFloat = new Float32Array(data);
            sourceData = new DataView(sourceDataAsFloat.buffer, sourceDataAsFloat.byteOffset, sourceDataAsFloat.byteLength);
        }
        else if (ArrayBuffer.isView(data)) {
            sourceData = new DataView(data.buffer, data.byteOffset, data.byteLength);
        }
        else {
            sourceData = new DataView(data, 0, data.byteLength);
        }
        let alignedData;
        if (this.type === VertexBuffer.BYTE) {
            alignedData = new Int8Array(totalLength);
        }
        else if (this.type === VertexBuffer.UNSIGNED_BYTE) {
            alignedData = new Uint8Array(totalLength);
        }
        else if (this.type === VertexBuffer.SHORT) {
            alignedData = new Int16Array(totalLength);
        }
        else if (this.type === VertexBuffer.UNSIGNED_SHORT) {
            alignedData = new Uint16Array(totalLength);
        }
        else if (this.type === VertexBuffer.HALF_FLOAT) {
            alignedData = new Uint16Array(totalLength);
        }
        else if (this.type === VertexBuffer.INT) {
            alignedData = new Int32Array(totalLength);
        }
        else if (this.type === VertexBuffer.UNSIGNED_INT) {
            alignedData = new Uint32Array(totalLength);
        }
        else {
            alignedData = new Float32Array(totalLength);
        }
        const numComponents = this.getSize();
        let sourceOffset = this.byteOffset;
        for (let i = 0; i < totalVertices; ++i) {
            for (let j = 0; j < numComponents; ++j) {
                switch (this.type) {
                    case VertexBuffer.BYTE:
                        alignedData[i * alignedSize + j] = sourceData.getInt8(sourceOffset + j);
                        break;
                    case VertexBuffer.UNSIGNED_BYTE:
                        alignedData[i * alignedSize + j] = sourceData.getUint8(sourceOffset + j);
                        break;
                    case VertexBuffer.SHORT:
                        alignedData[i * alignedSize + j] = sourceData.getInt16(sourceOffset + j * 2, IsLittleEndian);
                        break;
                    case VertexBuffer.UNSIGNED_SHORT:
                        alignedData[i * alignedSize + j] = sourceData.getUint16(sourceOffset + j * 2, IsLittleEndian);
                        break;
                    case VertexBuffer.HALF_FLOAT:
                        alignedData[i * alignedSize + j] = sourceData.getUint16(sourceOffset + j * 2, IsLittleEndian);
                        break;
                    case VertexBuffer.INT:
                        alignedData[i * alignedSize + j] = sourceData.getInt32(sourceOffset + j * 4, IsLittleEndian);
                        break;
                    case VertexBuffer.UNSIGNED_INT:
                        alignedData[i * alignedSize + j] = sourceData.getUint32(sourceOffset + j * 4, IsLittleEndian);
                        break;
                    case VertexBuffer.FLOAT:
                        alignedData[i * alignedSize + j] = sourceData.getFloat32(sourceOffset + j * 4, IsLittleEndian);
                        break;
                }
            }
            sourceOffset += this.byteStride;
        }
        this._alignedBuffer?.dispose();
        this._alignedBuffer = new Buffer(this.engine, alignedData, false, alignedByteStride, false, this.getIsInstanced(), true, this.instanceDivisor, (this._label ?? "VertexBuffer") + "_aligned");
    };
}

RegisterBufferAlign();

const Instances = [];
/**
 * Observable that notifies when a new v2 audio engine instance has been created.
 * - Fires after the engine has been fully constructed and initialized (e.g. from {@link CreateAudioEngineAsync}),
 *   so subclass state (audio context, listener, etc.) is guaranteed to be available to observers.
 */
new Observable();
/**
 * Abstract base class for v2 audio engines.
 *
 * A v2 audio engine based on the WebAudio API can be created with the {@link CreateAudioEngineAsync} function.
 */
class AudioEngineV2 {
    /**
     * The list of v2 audio engines that have been created and not yet disposed.
     * - Engines are added on construction and removed on {@link AudioEngineV2.dispose}.
     */
    static get Instances() {
        return Instances;
    }
    /**
     * Observable that notifies when a top-level audio node (sound, sound source, bus, or main bus) is added to this engine.
     */
    get onNodeAddedObservable() {
        return this._onNodeAddedObservable;
    }
    /**
     * Observable that notifies when a top-level audio node (sound, sound source, bus, or main bus) is removed from this engine.
     */
    get onNodeRemovedObservable() {
        return this._onNodeRemovedObservable;
    }
    /**
     * Observable that notifies when this engine is disposed.
     * - Fires from {@link AudioEngineV2.dispose} after the engine has been removed from {@link AudioEngineV2.Instances}.
     */
    get onDisposeObservable() {
        return this._onDisposeObservable;
    }
    constructor(options) {
        /** Not owned, but all items should be in `_nodes` container, too, which is owned. */
        this._mainBuses = new Set();
        this._sounds = new Set();
        this._soundsArray = null;
        /** Owned top-level sound and bus nodes. */
        this._nodes = new Set();
        this._defaultMainBus = null;
        this._parameterRampDuration = 0.01;
        this._onNodeAddedObservable = new Observable();
        this._onNodeRemovedObservable = new Observable();
        this._onDisposeObservable = new Observable();
        Instances.push(this);
        if (typeof options.parameterRampDuration === "number") {
            this.parameterRampDuration = options.parameterRampDuration;
        }
        // Intentionally do NOT notify {@link OnAudioEngineV2CreatedObservable} here:
        // - This base constructor runs before subclass fields (audio context, listener, ...) are initialized
        //   and before any async {@link CreateAudioEngineAsync}-style setup completes, so observers would
        //   see a partially constructed engine.
        // - Engine factory functions (e.g. {@link CreateAudioEngineAsync}) call `notifyObservers` themselves
        //   once the engine is fully constructed and initialized.
    }
    /**
     * The default main bus that will be used for audio buses and sounds if their `outBus` option is not set.
     * @see {@link IAudioBusOptions.outBus}
     * @see {@link IAbstractSoundOptions.outBus}
     */
    get defaultMainBus() {
        if (this._mainBuses.size === 0) {
            return null;
        }
        if (!this._defaultMainBus) {
            this._defaultMainBus = Array.from(this._mainBuses)[0];
        }
        return this._defaultMainBus;
    }
    /**
     * The smoothing duration to use when changing audio parameters, in seconds. Defaults to `0.01` (10 milliseconds).
     */
    get parameterRampDuration() {
        return this._parameterRampDuration;
    }
    set parameterRampDuration(value) {
        this._parameterRampDuration = Math.max(0, value);
    }
    /**
     * The list of static and streaming sounds created by the audio engine.
     */
    get sounds() {
        if (!this._soundsArray) {
            this._soundsArray = Array.from(this._sounds);
        }
        return this._soundsArray;
    }
    /**
     * The list of top-level audio nodes (sounds, sound sources, buses, main buses) owned by the audio engine.
     */
    get nodes() {
        return this._nodes;
    }
    /**
     * Releases associated resources.
     */
    dispose() {
        if (Instances.includes(this)) {
            Instances.splice(Instances.indexOf(this), 1);
        }
        const nodeIt = this._nodes.values();
        for (let next = nodeIt.next(); !next.done; next = nodeIt.next()) {
            next.value.dispose();
        }
        this._mainBuses.clear();
        this._nodes.clear();
        this._sounds.clear();
        this._disposeSoundsArray();
        this._defaultMainBus = null;
        this._onDisposeObservable.notifyObservers(this);
        this._onDisposeObservable.clear();
        this._onNodeAddedObservable.clear();
        this._onNodeRemovedObservable.clear();
    }
    /**
     * Unlocks the audio engine if it is locked.
     * - Note that the returned promise may already be resolved if the audio engine is already unlocked.
     * @returns A promise that is resolved when the audio engine is unlocked.
     */
    // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
    unlockAsync() {
        return this.resumeAsync();
    }
    _addMainBus(mainBus) {
        this._mainBuses.add(mainBus);
        this._addNode(mainBus);
    }
    _removeMainBus(mainBus) {
        this._mainBuses.delete(mainBus);
        this._defaultMainBus = null;
        this._removeNode(mainBus);
    }
    _addNode(node) {
        this._nodes.add(node);
        this._onNodeAddedObservable.notifyObservers(node);
    }
    _removeNode(node) {
        this._nodes.delete(node);
        this._onNodeRemovedObservable.notifyObservers(node);
    }
    _addSound(sound) {
        this._disposeSoundsArray();
        this._sounds.add(sound);
        this._addNode(sound);
    }
    _removeSound(sound) {
        this._disposeSoundsArray();
        this._sounds.delete(sound);
        this._removeNode(sound);
    }
    /**
     * Called when any sound's playback state changes (started, stopped, paused, resumed).
     * Override in platform-specific implementations to react to sound playback state changes.
     * @internal
     */
    _onSoundPlaybackStateChanged() {
        // No-op base implementation.
    }
    _disposeSoundsArray() {
        if (this._soundsArray) {
            this._soundsArray.length = 0;
            this._soundsArray = null;
        }
    }
}

const _SpatialAudioListenerDefaults = {
    position: Vector3.Zero(),
    rotation: Vector3.Zero(),
    rotationQuaternion: new Quaternion(),
};
/**
 * @param options The spatial audio listener options to check.
 * @returns `true` if spatial audio listener options are defined, otherwise `false`.
 */
function _HasSpatialAudioListenerOptions(options) {
    return (options.listenerEnabled ||
        options.listenerMinUpdateTime !== undefined ||
        options.listenerPosition !== undefined ||
        options.listenerRotation !== undefined ||
        options.listenerRotationQuaternion !== undefined);
}
/**
 * Abstract class representing the spatial audio `listener` property on an audio engine.
 *
 * @see {@link AudioEngineV2.listener}
 */
class AbstractSpatialAudioListener {
}

/** @internal */
class _SpatialAudioListener extends AbstractSpatialAudioListener {
    constructor() {
        super();
        this._attacherComponent = null;
        this._attacherComponent = new _SpatialAudioAttacherComponent(this);
    }
    /** @internal */
    get isAttached() {
        return this._attacherComponent !== null && this._attacherComponent.isAttached;
    }
    /** @internal */
    get attachedNode() {
        return this._attacherComponent?.sceneNode ?? null;
    }
    /**
     * Attaches to a scene node.
     *
     * Detaches automatically before attaching to the given scene node.
     * If `sceneNode` is `null` it is the same as calling `detach()`.
     *
     * @param sceneNode The scene node to attach to, or `null` to detach.
     * @param useBoundingBox Whether to use the bounding box of the node for positioning. Defaults to `false`.
     * @param attachmentType Whether to attach to the node's position and/or rotation. Defaults to `PositionAndRotation`.
     */
    attach(sceneNode, useBoundingBox = false, attachmentType = 3 /* SpatialAudioAttachmentType.PositionAndRotation */) {
        if (!this._attacherComponent) {
            this._attacherComponent = new _SpatialAudioAttacherComponent(this);
        }
        this._attacherComponent.attach(sceneNode, useBoundingBox, attachmentType);
    }
    /**
     * Detaches from the scene node if attached.
     */
    detach() {
        this._attacherComponent?.detach();
    }
    /** @internal */
    dispose() {
        this._attacherComponent?.dispose();
        this._attacherComponent = null;
    }
    /** @internal */
    setOptions(options) {
        if (options.listenerMinUpdateTime !== undefined) {
            this.minUpdateTime = options.listenerMinUpdateTime;
        }
        if (options.listenerPosition) {
            this.position = options.listenerPosition.clone();
        }
        if (options.listenerRotationQuaternion) {
            this.rotationQuaternion = options.listenerRotationQuaternion.clone();
        }
        else if (options.listenerRotation) {
            this.rotation = options.listenerRotation.clone();
        }
        else {
            this.rotationQuaternion = _SpatialAudioListenerDefaults.rotationQuaternion.clone();
        }
        this.update();
    }
}

const TmpMatrix = Matrix.Zero();
const TmpQuaternion = new Quaternion();
const TmpVector1 = Vector3.Zero();
const TmpVector2 = Vector3.Zero();
/** @internal */
function _CreateSpatialAudioListener(engine, autoUpdate, minUpdateTime) {
    const listener = engine._audioContext.listener;
    if (listener.forwardX &&
        listener.forwardY &&
        listener.forwardZ &&
        listener.positionX &&
        listener.positionY &&
        listener.positionZ &&
        listener.upX &&
        listener.upY &&
        listener.upZ) {
        return new _SpatialWebAudioListener(engine, autoUpdate, minUpdateTime);
    }
    else {
        return new _SpatialWebAudioListenerFallback(engine, autoUpdate, minUpdateTime);
    }
}
class _AbstractSpatialWebAudioListener extends _SpatialAudioListener {
    /** @internal */
    constructor(engine, autoUpdate, minUpdateTime) {
        super();
        this._lastPosition = Vector3.Zero();
        this._lastRotation = Vector3.Zero();
        this._lastRotationQuaternion = new Quaternion();
        /** @internal */
        this.position = Vector3.Zero();
        /** @internal */
        this.rotation = Vector3.Zero();
        /** @internal */
        this.rotationQuaternion = new Quaternion();
        this._listener = engine._audioContext.listener;
        this.engine = engine;
        this._updaterComponent = new _SpatialWebAudioUpdaterComponent(this, autoUpdate, minUpdateTime);
    }
    /** @internal */
    dispose() {
        super.dispose();
        this._updaterComponent.dispose();
        this._updaterComponent = null;
    }
    /** @internal */
    get minUpdateTime() {
        return this._updaterComponent.minUpdateTime;
    }
    /** @internal */
    set minUpdateTime(value) {
        this._updaterComponent.minUpdateTime = value;
    }
    /** @internal */
    update() {
        if (this.isAttached) {
            this._attacherComponent?.update();
        }
        else {
            this._updatePosition();
            this._updateRotation();
        }
    }
    _updatePosition() {
        if (this._lastPosition.equalsWithEpsilon(this.position)) {
            return;
        }
        this._setWebAudioPosition(this.position);
        this._lastPosition.copyFrom(this.position);
    }
    _updateRotation() {
        if (!this._lastRotationQuaternion.equalsWithEpsilon(this.rotationQuaternion)) {
            TmpQuaternion.copyFrom(this.rotationQuaternion);
            this._lastRotationQuaternion.copyFrom(this.rotationQuaternion);
        }
        else if (!this._lastRotation.equalsWithEpsilon(this.rotation)) {
            Quaternion.FromEulerAnglesToRef(this.rotation.x, this.rotation.y, this.rotation.z, TmpQuaternion);
            this._lastRotation.copyFrom(this.rotation);
        }
        else {
            return;
        }
        Matrix.FromQuaternionToRef(TmpQuaternion, TmpMatrix);
        // NB: The WebAudio API is right-handed.
        Vector3.TransformNormalToRef(Vector3.RightHandedForwardReadOnly, TmpMatrix, TmpVector1);
        Vector3.TransformNormalToRef(Vector3.Up(), TmpMatrix, TmpVector2);
        this._setWebAudioOrientation(TmpVector1, TmpVector2);
    }
}
/**
 * Full-featured spatial audio listener for the Web Audio API.
 *
 * Used in browsers that support the `forwardX/Y/Z`, `positionX/Y/Z`, and `upX/Y/Z` properties on the AudioContext listener.
 *
 * NB: Firefox falls back to using this implementation.
 *
 * @see _SpatialWebAudioListenerFallback for the implementation used if only `setPosition` and `setOrientation` are available.
 *
 * NB: This sub property is not backed by a sub node and all properties are set directly on the audio context listener.
 *
 * @internal
 */
class _SpatialWebAudioListener extends _AbstractSpatialWebAudioListener {
    constructor(engine, autoUpdate, minUpdateTime) {
        super(engine, autoUpdate, minUpdateTime);
        const listener = engine._audioContext.listener;
        this._forwardX = new _WebAudioParameterComponent(engine, listener.forwardX);
        this._forwardY = new _WebAudioParameterComponent(engine, listener.forwardY);
        this._forwardZ = new _WebAudioParameterComponent(engine, listener.forwardZ);
        this._positionX = new _WebAudioParameterComponent(engine, listener.positionX);
        this._positionY = new _WebAudioParameterComponent(engine, listener.positionY);
        this._positionZ = new _WebAudioParameterComponent(engine, listener.positionZ);
        this._upX = new _WebAudioParameterComponent(engine, listener.upX);
        this._upY = new _WebAudioParameterComponent(engine, listener.upY);
        this._upZ = new _WebAudioParameterComponent(engine, listener.upZ);
    }
    _setWebAudioPosition(position) {
        // If attached and there is a ramp in progress, we assume another update is coming soon that we can wait for.
        // We don't do this for unattached nodes because there may not be another update coming.
        if (this.isAttached && (this._positionX.isRamping || this._positionY.isRamping || this._positionZ.isRamping)) {
            return;
        }
        this._positionX.targetValue = position.x;
        this._positionY.targetValue = position.y;
        this._positionZ.targetValue = position.z;
    }
    _setWebAudioOrientation(forward, up) {
        // If attached and there is a ramp in progress, we assume another update is coming soon that we can wait for.
        // We don't do this for unattached nodes because there may not be another update coming.
        if (this.isAttached &&
            (this._forwardX.isRamping || this._forwardY.isRamping || this._forwardZ.isRamping || this._upX.isRamping || this._upY.isRamping || this._upZ.isRamping)) {
            return;
        }
        this._forwardX.targetValue = forward.x;
        this._forwardY.targetValue = forward.y;
        this._forwardZ.targetValue = forward.z;
        this._upX.targetValue = up.x;
        this._upY.targetValue = up.y;
        this._upZ.targetValue = up.z;
    }
}
/**
 * Fallback spatial audio listener for the Web Audio API.
 *
 * Used in browsers that do not support the `forwardX/Y/Z`, `positionX/Y/Z`, and `upX/Y/Z` properties on the
 * AudioContext listener.
 *
 * @see _SpatialWebAudioListener for the implementation used if the `forwardX/Y/Z`, `positionX/Y/Z`, and `upX/Y/Z`
 * properties are available.
 *
 * NB: This sub property is not backed by a sub node and all properties are set directly on the audio context listener.
 *
 * @internal
 */
class _SpatialWebAudioListenerFallback extends _AbstractSpatialWebAudioListener {
    _setWebAudioPosition(position) {
        this._listener.setPosition(position.x, position.y, position.z);
    }
    _setWebAudioOrientation(forward, up) {
        this._listener.setOrientation(forward.x, forward.y, forward.z, up.x, up.y, up.z);
    }
}

/**
 * Abstract class for the main audio output node.
 *
 * A main audio output is the last audio node in the audio graph before the audio is sent to the speakers.
 *
 * @see {@link AudioEngineV2.mainOut}
 * @internal
 */
class _MainAudioOut extends AbstractAudioNode {
    constructor(engine) {
        super(engine, 1 /* AudioNodeType.HAS_INPUTS */);
    }
}

/** @internal */
class _WebAudioMainOut extends _MainAudioOut {
    /** @internal */
    constructor(engine) {
        super(engine);
        this._setGainNode(new GainNode(engine._audioContext));
    }
    /** @internal */
    dispose() {
        super.dispose();
        this._volume.dispose();
        this._gainNode.disconnect();
        this._destinationNode.disconnect();
    }
    /** @internal */
    get _inNode() {
        return this._gainNode;
    }
    set _inNode(value) {
        if (this._gainNode === value) {
            return;
        }
        this._setGainNode(value);
    }
    /** @internal */
    get volume() {
        return this._volume.targetValue;
    }
    /** @internal */
    set volume(value) {
        this._volume.targetValue = value;
    }
    get _destinationNode() {
        return this.engine._audioDestination;
    }
    /** @internal */
    getClassName() {
        return "_WebAudioMainOut";
    }
    /** @internal */
    setVolume(value, options = null) {
        this._volume.setTargetValue(value, options);
    }
    _setGainNode(gainNode) {
        if (this._gainNode === gainNode) {
            return;
        }
        this._gainNode?.disconnect();
        gainNode.connect(this._destinationNode);
        this._volume = new _WebAudioParameterComponent(this.engine, gainNode.gain);
        this._gainNode = gainNode;
    }
}

/**
 * Adds a UI button that starts the audio engine's underlying audio context when the user presses it.
 * @internal
 */
class _WebAudioUnmuteUI {
    /** @internal */
    constructor(engine, parentElement) {
        this._button = null;
        this._enabled = true;
        this._style = null;
        this._onStateChanged = () => {
            if (!this._button) {
                return;
            }
            if (this._engine.state === "running") {
                this._hide();
            }
            else {
                this._show();
            }
        };
        this._engine = engine;
        const parent = parentElement || EngineStore.LastCreatedEngine?.getInputElement()?.parentElement || document.body;
        const top = (parent?.offsetTop || 0) + 20;
        this._style = document.createElement("style");
        this._style.appendChild(document.createTextNode(`.babylonUnmute{position:absolute;top:${top}px;margin-left:20px;height:40px;width:60px;background-color:rgba(51,51,51,0.7);background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2239%22%20height%3D%2232%22%20viewBox%3D%220%200%2039%2032%22%3E%3Cpath%20fill%3D%22white%22%20d%3D%22M9.625%2018.938l-0.031%200.016h-4.953q-0.016%200-0.031-0.016v-12.453q0-0.016%200.031-0.016h4.953q0.031%200%200.031%200.016v12.453zM12.125%207.688l8.719-8.703v27.453l-8.719-8.719-0.016-0.047v-9.938zM23.359%207.875l1.406-1.406%204.219%204.203%204.203-4.203%201.422%201.406-4.219%204.219%204.219%204.203-1.484%201.359-4.141-4.156-4.219%204.219-1.406-1.422%204.219-4.203z%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E");background-size:80%;background-repeat:no-repeat;background-position:center;background-position-y:4px;border:none;outline:none;transition:transform 0.125s ease-out;cursor:pointer;z-index:9999;}.babylonUnmute:hover{transform:scale(1.05)}`));
        document.head.appendChild(this._style);
        this._button = document.createElement("button");
        this._button.className = "babylonUnmute";
        this._button.id = "babylonUnmuteButton";
        this._button.addEventListener("click", () => {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            this._engine.unlockAsync();
        });
        parent.appendChild(this._button);
        this._engine.stateChangedObservable.add(this._onStateChanged);
    }
    /** @internal */
    dispose() {
        this._button?.remove();
        this._button = null;
        this._style?.remove();
        this._style = null;
        this._engine.stateChangedObservable.removeCallback(this._onStateChanged);
    }
    /** @internal */
    get enabled() {
        return this._enabled;
    }
    set enabled(value) {
        this._enabled = value;
        if (value) {
            if (this._engine.state !== "running") {
                this._show();
            }
        }
        else {
            this._hide();
        }
    }
    _show() {
        if (!this._button || !this._enabled) {
            return;
        }
        this._button.style.display = "block";
    }
    _hide() {
        if (!this._button) {
            return;
        }
        this._button.style.display = "none";
    }
}

const FormatMimeTypes = {
    aac: "audio/aac",
    ac3: "audio/ac3",
    flac: "audio/flac",
    m4a: "audio/mp4",
    mp3: 'audio/mpeg; codecs="mp3"',
    mp4: "audio/mp4",
    ogg: 'audio/ogg; codecs="vorbis"',
    wav: "audio/wav",
    webm: 'audio/webm; codecs="vorbis"',
};
/** @internal */
class _WebAudioEngine extends AudioEngineV2 {
    /** @internal */
    constructor(options = {}) {
        super(options);
        this._audioContextStarted = false;
        this._destinationNode = null;
        this._invalidFormats = new Set();
        this._isUpdating = false;
        this._listener = null;
        this._listenerAutoUpdate = true;
        this._listenerMinUpdateTime = 0;
        this._pauseCalled = false;
        this._resumeOnInteraction = true;
        this._resumeOnPause = true;
        this._resumeOnPauseRetryInterval = 1000;
        this._resumeOnPauseTimerId = null;
        this._resumePromise = null;
        this._silentHtmlAudio = null;
        this._unmuteUI = null;
        this._updateObservable = null;
        this._validFormats = new Set();
        this._volume = 1;
        /** @internal */
        this._isUsingOfflineAudioContext = false;
        /** @internal */
        this.isReadyPromise = new Promise((resolve) => {
            this._resolveIsReadyPromise = resolve;
        });
        /** @internal */
        this.stateChangedObservable = new Observable();
        /** @internal */
        this.userGestureObservable = new Observable();
        this._initAudioContextAsync = async () => {
            this._audioContext.addEventListener("statechange", this._onAudioContextStateChange);
            this._mainOut = new _WebAudioMainOut(this);
            this._mainOut.volume = this._volume;
            await this.createMainBusAsync("default");
        };
        this._onAudioContextStateChange = () => {
            if (this.state === "running") {
                clearInterval(this._resumeOnPauseTimerId);
                this._audioContextStarted = true;
                this._resumePromise = null;
            }
            if (this.state === "suspended" || this.state === "interrupted") {
                if (this._audioContextStarted && this._resumeOnPause && !this._pauseCalled) {
                    clearInterval(this._resumeOnPauseTimerId);
                    this._resumeOnPauseTimerId = setInterval(() => {
                        // eslint-disable-next-line @typescript-eslint/no-floating-promises
                        this.resumeAsync();
                    }, this._resumeOnPauseRetryInterval);
                }
            }
            this.stateChangedObservable.notifyObservers(this.state);
        };
        this._onUserGestureAsync = async () => {
            if (this._resumeOnInteraction) {
                await this._audioContext.resume();
            }
            // On iOS the ringer switch must be turned on for WebAudio to play.
            // This gets WebAudio to play with the ringer switch turned off by playing an HTMLAudioElement.
            // The element is activated during a user gesture so it can be played/paused programmatically
            // later. It is immediately paused to avoid triggering iOS Safari's "now playing" detection,
            // which throttles the page to 30 FPS.
            if (!this._silentHtmlAudio) {
                this._silentHtmlAudio = document.createElement("audio");
                const audio = this._silentHtmlAudio;
                audio.controls = false;
                audio.preload = "auto";
                audio.loop = true;
                // Wave data for 0.0001 seconds of silence.
                audio.src = "data:audio/wav;base64,UklGRjAAAABXQVZFZm10IBAAAAABAAEAgLsAAAB3AQACABAAZGF0YQwAAAAAAAEA/v8CAP//AQA=";
                // Play briefly to activate the element, then immediately pause.
                // The rejection handler is intentionally empty — play() can reject if the
                // browser requires a more specific user gesture; this is non-fatal since
                // the audio context unlock is the primary goal, and the silent element is
                // only a supplementary iOS ringer-switch workaround.
                // eslint-disable-next-line github/no-then
                audio.play().then(() => audio.pause(), () => { });
            }
            this.userGestureObservable.notifyObservers();
        };
        this._startUpdating = () => {
            if (this._isUpdating) {
                return;
            }
            this._isUpdating = true;
            if (this.state === "running") {
                this._update();
            }
            else {
                const callback = () => {
                    if (this.state === "running") {
                        this._update();
                        this.stateChangedObservable.removeCallback(callback);
                    }
                };
                this.stateChangedObservable.add(callback);
            }
        };
        this._update = () => {
            if (this._updateObservable?.hasObservers()) {
                this._updateObservable.notifyObservers();
                requestAnimationFrame(this._update);
            }
            else {
                this._isUpdating = false;
            }
        };
        if (typeof options.listenerAutoUpdate === "boolean") {
            this._listenerAutoUpdate = options.listenerAutoUpdate;
        }
        if (typeof options.listenerMinUpdateTime === "number") {
            this._listenerMinUpdateTime = options.listenerMinUpdateTime;
        }
        this._volume = options.volume ?? 1;
        if (options.audioContext) {
            this._isUsingOfflineAudioContext = options.audioContext instanceof OfflineAudioContext;
            this._audioContext = options.audioContext;
        }
        else {
            this._audioContext = new AudioContext();
        }
        if (!options.disableDefaultUI) {
            this._unmuteUI = new _WebAudioUnmuteUI(this, options.defaultUIParentElement);
        }
    }
    /** @internal */
    async _initAsync(options) {
        this._resumeOnInteraction = typeof options.resumeOnInteraction === "boolean" ? options.resumeOnInteraction : true;
        this._resumeOnPause = typeof options.resumeOnPause === "boolean" ? options.resumeOnPause : true;
        this._resumeOnPauseRetryInterval = options.resumeOnPauseRetryInterval ?? 1000;
        document.addEventListener("click", this._onUserGestureAsync);
        await this._initAudioContextAsync();
        if (_HasSpatialAudioListenerOptions(options)) {
            this._listener = _CreateSpatialAudioListener(this, this._listenerAutoUpdate, this._listenerMinUpdateTime);
            this._listener.setOptions(options);
        }
        this._resolveIsReadyPromise();
    }
    /** @internal */
    get currentTime() {
        return this._audioContext.currentTime ?? 0;
    }
    /** @internal */
    get _inNode() {
        return this._audioContext.destination;
    }
    /** @internal */
    get mainOut() {
        return this._mainOut;
    }
    /** @internal */
    get listener() {
        return this._listener ?? (this._listener = _CreateSpatialAudioListener(this, this._listenerAutoUpdate, this._listenerMinUpdateTime));
    }
    /** @internal */
    get state() {
        // Always return "running" for OfflineAudioContext so sound `play` calls work while the context is suspended.
        return this._isUsingOfflineAudioContext ? "running" : this._audioContext.state;
    }
    /** @internal */
    get volume() {
        return this._volume;
    }
    /** @internal */
    set volume(value) {
        if (this._volume === value) {
            return;
        }
        this._volume = value;
        if (this._mainOut) {
            this._mainOut.volume = value;
        }
    }
    /**
     * This property should only be used by the legacy audio engine.
     * @internal
     * */
    get _audioDestination() {
        return this._destinationNode ? this._destinationNode : (this._destinationNode = this._audioContext.destination);
    }
    set _audioDestination(value) {
        this._destinationNode = value;
    }
    /**
     * This property should only be used by the legacy audio engine.
     * @internal
     */
    get _unmuteUIEnabled() {
        return this._unmuteUI ? this._unmuteUI.enabled : false;
    }
    set _unmuteUIEnabled(value) {
        if (this._unmuteUI) {
            this._unmuteUI.enabled = value;
        }
    }
    /** @internal */
    async createBusAsync(name, options = {}) {
        const module = await import('./webAudioBus-CGXJjzt8.esm.js');
        const bus = new module._WebAudioBus(name, this, options);
        await bus._initAsync(options);
        return bus;
    }
    /** @internal */
    async createMainBusAsync(name, options = {}) {
        const module = await import('./webAudioMainBus-CSoazZ91.esm.js');
        const bus = new module._WebAudioMainBus(name, this);
        await bus._initAsync(options);
        return bus;
    }
    /** @internal */
    async createMicrophoneSoundSourceAsync(name, options) {
        let mediaStream;
        try {
            mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
        }
        catch (e) {
            throw new Error("Unable to access microphone: " + e, { cause: e });
        }
        return await this.createSoundSourceAsync(name, new MediaStreamAudioSourceNode(this._audioContext, { mediaStream }), {
            outBusAutoDefault: false,
            mediaStreamSinkEnabled: false,
            stopMediaStreamTracksOnDispose: true,
            ...options,
        });
    }
    /** @internal */
    async createSoundAsync(name, source, options = {}) {
        const module = await import('./webAudioStaticSound-DZ0Kphdt.esm.js');
        const sound = new module._WebAudioStaticSound(name, this, options);
        await sound._initAsync(source, options);
        return sound;
    }
    /** @internal */
    async createSoundBufferAsync(source, options = {}) {
        const module = await import('./webAudioStaticSound-DZ0Kphdt.esm.js');
        const soundBuffer = new module._WebAudioStaticSoundBuffer(this);
        await soundBuffer._initAsync(source, options);
        return soundBuffer;
    }
    /** @internal */
    async createSoundSourceAsync(name, source, options = {}) {
        const module = await import('./webAudioSoundSource-B667AvD0.esm.js');
        const soundSource = new module._WebAudioSoundSource(name, source, this, options);
        await soundSource._initAsync(options);
        return soundSource;
    }
    /** @internal */
    async createStreamingSoundAsync(name, source, options = {}) {
        const module = await import('./webAudioStreamingSound-B487JQML.esm.js');
        const sound = new module._WebAudioStreamingSound(name, this, options);
        await sound._initAsync(source, options);
        return sound;
    }
    /** @internal */
    dispose() {
        super.dispose();
        this._listener?.dispose();
        this._listener = null;
        // Note that OfflineAudioContext does not have a `close` method.
        if (this._audioContext.state !== "closed" && !this._isUsingOfflineAudioContext) {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            this._audioContext.close();
        }
        document.removeEventListener("click", this._onUserGestureAsync);
        this._audioContext.removeEventListener("statechange", this._onAudioContextStateChange);
        this._silentHtmlAudio?.remove();
        this._updateObservable?.clear();
        this._updateObservable = null;
        this._unmuteUI?.dispose();
        this._unmuteUI = null;
        this.stateChangedObservable.clear();
    }
    /** @internal */
    flagInvalidFormat(format) {
        this._invalidFormats.add(format);
    }
    /** @internal */
    isFormatValid(format) {
        if (this._validFormats.has(format)) {
            return true;
        }
        if (this._invalidFormats.has(format)) {
            return false;
        }
        const mimeType = FormatMimeTypes[format];
        if (mimeType === undefined) {
            return false;
        }
        const audio = new Audio();
        if (audio.canPlayType(mimeType) === "") {
            this._invalidFormats.add(format);
            return false;
        }
        this._validFormats.add(format);
        return true;
    }
    /** @internal */
    async pauseAsync() {
        await this._audioContext.suspend();
        this._pauseCalled = true;
    }
    /** @internal */
    // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
    resumeAsync() {
        this._pauseCalled = false;
        if (this._resumePromise) {
            return this._resumePromise;
        }
        this._resumePromise = this._audioContext.resume();
        this.stateChangedObservable.notifyObservers(this.state);
        return this._resumePromise;
    }
    /** @internal */
    setVolume(value, options = null) {
        if (this._mainOut) {
            this._mainOut.setVolume(value, options);
        }
        else {
            throw new Error("Main output not initialized yet.");
        }
    }
    /** @internal */
    _addMainBus(mainBus) {
        super._addMainBus(mainBus);
    }
    /** @internal */
    _removeMainBus(mainBus) {
        super._removeMainBus(mainBus);
    }
    /** @internal */
    _addNode(node) {
        super._addNode(node);
    }
    /** @internal */
    _removeNode(node) {
        super._removeNode(node);
    }
    /** @internal */
    _addSound(sound) {
        super._addSound(sound);
    }
    /** @internal */
    _removeSound(sound) {
        super._removeSound(sound);
    }
    /** @internal */
    _onSoundPlaybackStateChanged() {
        if (!this._silentHtmlAudio) {
            return;
        }
        const hasActiveSounds = this.sounds.some((s) => s.state === 3 /* SoundState.Started */ || s.state === 2 /* SoundState.Starting */ || s.state === 0 /* SoundState.Stopping */);
        if (hasActiveSounds && this._silentHtmlAudio.paused) {
            // Resume silent audio for iOS ringer switch workaround while sounds are playing.
            // Errors are safe to ignore — the silent audio element is a workaround, not a
            // user-facing sound, so a rejected play (e.g. missing user gesture) is harmless.
            // eslint-disable-next-line github/no-then
            void this._silentHtmlAudio.play().catch(() => { });
        }
        else if (!hasActiveSounds && !this._silentHtmlAudio.paused) {
            // Pause silent audio when no sounds are playing to avoid triggering iOS Safari's
            // audio playback detection, which causes FPS throttling and shows a blue audio icon.
            this._silentHtmlAudio.pause();
        }
    }
    /** @internal */
    _addUpdateObserver(callback) {
        if (!this._updateObservable) {
            this._updateObservable = new Observable();
        }
        this._updateObservable.add(callback);
        this._startUpdating();
    }
    _removeUpdateObserver(callback) {
        if (this._updateObservable) {
            this._updateObservable.removeCallback(callback);
        }
    }
}

/** This file must only contain pure code and pure imports */
/**
 * This represents the default audio engine used in babylon.
 * It is responsible to play, synchronize and analyse sounds throughout the  application.
 * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic
 */
class AudioEngine {
    /**
     * The master gain node defines the global audio volume of your audio engine.
     */
    get masterGain() {
        return this._masterGain;
    }
    set masterGain(value) {
        this._masterGain = this._v2.mainOut._inNode = value;
    }
    /**
     * Defines if the audio engine relies on a custom unlocked button.
     * In this case, the embedded button will not be displayed.
     */
    get useCustomUnlockedButton() {
        return this._useCustomUnlockedButton;
    }
    set useCustomUnlockedButton(value) {
        this._useCustomUnlockedButton = value;
        this._v2._unmuteUIEnabled = !value;
    }
    /**
     * Gets the current AudioContext if available.
     */
    get audioContext() {
        if (this._v2.state === "running") {
            // Do not wait for the promise to unlock.
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            this._triggerRunningStateAsync();
        }
        return this._v2._audioContext;
    }
    /**
     * Instantiates a new audio engine.
     *
     * @param hostElement defines the host element where to display the mute icon if necessary
     * @param audioContext defines the audio context to be used by the audio engine
     * @param audioDestination defines the audio destination node to be used by audio engine
     */
    constructor(hostElement = null, audioContext = null, audioDestination = null) {
        this._tryToRun = false;
        this._useCustomUnlockedButton = false;
        /**
         * Gets whether the current host supports Web Audio and thus could create AudioContexts.
         */
        this.canUseWebAudio = true;
        /**
         * Defines if Babylon should emit a warning if WebAudio is not supported.
         */
        // eslint-disable-next-line @typescript-eslint/naming-convention
        this.WarnedWebAudioUnsupported = false;
        /**
         * Gets whether or not mp3 are supported by your browser.
         */
        this.isMP3supported = false;
        /**
         * Gets whether or not ogg are supported by your browser.
         */
        this.isOGGsupported = false;
        /**
         * Gets whether audio has been unlocked on the device.
         * Some Browsers have strong restrictions about Audio and won't autoplay unless
         * a user interaction has happened.
         */
        this.unlocked = false;
        /**
         * Event raised when audio has been unlocked on the browser.
         */
        this.onAudioUnlockedObservable = new Observable();
        /**
         * Event raised when audio has been locked on the browser.
         */
        this.onAudioLockedObservable = new Observable();
        const v2 = new _WebAudioEngine({
            audioContext: audioContext ? audioContext : undefined,
            defaultUIParentElement: hostElement?.parentElement ? hostElement.parentElement : undefined,
        });
        // Historically the unmute button is disabled until a sound tries to play and can't, which results in a call
        // to `AudioEngine.lock()`, which is where the unmute button is enabled if no custom UI is requested.
        v2._unmuteUIEnabled = false;
        this._masterGain = new GainNode(v2._audioContext);
        v2._audioDestination = audioDestination;
        v2.stateChangedObservable.add((state) => {
            if (state === "running") {
                this.unlocked = true;
                this.onAudioUnlockedObservable.notifyObservers(this);
            }
            else {
                this.unlocked = false;
                this.onAudioLockedObservable.notifyObservers(this);
            }
        });
        // eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
        v2._initAsync({ resumeOnInteraction: false }).then(() => {
            const mainBusOutNode = v2.defaultMainBus._outNode;
            if (mainBusOutNode) {
                mainBusOutNode.disconnect(v2.mainOut._inNode);
                mainBusOutNode.connect(this._masterGain);
            }
            v2.mainOut._inNode = this._masterGain;
            v2.stateChangedObservable.notifyObservers(v2.state);
        });
        this.isMP3supported = v2.isFormatValid("mp3");
        this.isOGGsupported = v2.isFormatValid("ogg");
        this._v2 = v2;
    }
    /**
     * Flags the audio engine in Locked state.
     * This happens due to new browser policies preventing audio to autoplay.
     */
    lock() {
        // eslint-disable-next-line @typescript-eslint/no-floating-promises
        this._v2._audioContext.suspend();
        if (!this._useCustomUnlockedButton) {
            this._v2._unmuteUIEnabled = true;
        }
    }
    /**
     * Unlocks the audio engine once a user action has been done on the dom.
     * This is helpful to resume play once browser policies have been satisfied.
     */
    unlock() {
        if (this._v2._audioContext?.state === "running") {
            if (!this.unlocked) {
                // Notify users that the audio stack is unlocked/unmuted
                this.unlocked = true;
                this.onAudioUnlockedObservable.notifyObservers(this);
            }
            return;
        }
        // eslint-disable-next-line @typescript-eslint/no-floating-promises
        this._triggerRunningStateAsync();
    }
    /** @internal */
    _resumeAudioContextOnStateChange() {
        this._v2._audioContext?.addEventListener("statechange", () => {
            if (this.unlocked && this._v2._audioContext?.state !== "running") {
                // eslint-disable-next-line @typescript-eslint/no-floating-promises
                this._resumeAudioContextAsync();
            }
        }, {
            once: true,
            passive: true,
            signal: AbortSignal.timeout(3000),
        });
    }
    // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
    _resumeAudioContextAsync() {
        if (this._v2._isUsingOfflineAudioContext) {
            return Promise.resolve();
        }
        if (this._v2._audioContext.state === "suspended" && !this._useCustomUnlockedButton) {
            this._v2._unmuteUIEnabled = true;
        }
        return this._v2._audioContext.resume();
    }
    /**
     * Destroy and release the resources associated with the audio context.
     */
    dispose() {
        this._v2.dispose();
        this.onAudioUnlockedObservable.clear();
        this.onAudioLockedObservable.clear();
    }
    /**
     * Gets the global volume sets on the master gain.
     * @returns the global volume if set or -1 otherwise
     */
    getGlobalVolume() {
        return this.masterGain.gain.value;
    }
    /**
     * Sets the global volume of your experience (sets on the master gain).
     * @param newVolume Defines the new global volume of the application
     */
    setGlobalVolume(newVolume) {
        this.masterGain.gain.value = newVolume;
    }
    /**
     * Connect the audio engine to an audio analyser allowing some amazing
     * synchronization between the sounds/music and your visualization (VuMeter for instance).
     * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic#using-the-analyser
     * @param analyser The analyser to connect to the engine
     */
    connectToAnalyser(analyser) {
        if (this._connectedAnalyser) {
            this._connectedAnalyser.stopDebugCanvas();
        }
        this._connectedAnalyser = analyser;
        this.masterGain.disconnect();
        this._connectedAnalyser.connectAudioNodes(this.masterGain, this._v2._audioContext.destination);
    }
    async _triggerRunningStateAsync() {
        if (this._tryToRun) {
            void this._v2._audioContext.resume();
            return;
        }
        this._tryToRun = true;
        await this._resumeAudioContextAsync();
        this._tryToRun = false;
        this.unlocked = true;
        this.onAudioUnlockedObservable.notifyObservers(this);
    }
}
let _Registered$9 = false;
/**
 * Register side effects for audioEngine.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterAudioEngine() {
    if (_Registered$9) {
        return;
    }
    _Registered$9 = true;
    // Sets the default audio engine to Babylon.js
    AbstractEngine.AudioEngineFactory = (hostElement, audioContext, audioDestination) => {
        return new AudioEngine(hostElement, audioContext, audioDestination);
    };
}

/**
 * Re-exports pure implementation and applies runtime side effects.
 * Import audioEngine.pure for tree-shakeable, side-effect-free usage.
 */
RegisterAudioEngine();

/** This file must only contain pure code and pure imports */
let _Registered$8 = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineAlpha.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineAlpha() {
    if (_Registered$8) {
        return;
    }
    _Registered$8 = true;
    ThinWebGPUEngine.prototype.setAlphaMode = function (mode, noDepthWriteChange = false, targetIndex = 0) {
        const alphaBlend = this._alphaState._alphaBlend[targetIndex];
        if (this._alphaMode[targetIndex] === mode && ((mode === Constants.ALPHA_DISABLE && !alphaBlend) || (mode !== Constants.ALPHA_DISABLE && alphaBlend))) {
            if (!noDepthWriteChange) {
                // Make sure we still have the correct depth mask according to the alpha mode (a transparent material could have forced writting to the depth buffer, for instance)
                const depthMask = mode === Constants.ALPHA_DISABLE;
                if (this.depthCullingState.depthMask !== depthMask) {
                    this.setDepthWrite(depthMask);
                    this._cacheRenderPipeline.setDepthWriteEnabled(depthMask);
                }
            }
            return;
        }
        const alphaBlendDisabled = mode === Constants.ALPHA_DISABLE;
        this._alphaState.setAlphaBlend(!alphaBlendDisabled, targetIndex);
        this._alphaState.setAlphaMode(mode, targetIndex);
        if (!noDepthWriteChange) {
            this.setDepthWrite(alphaBlendDisabled);
            this._cacheRenderPipeline.setDepthWriteEnabled(alphaBlendDisabled);
        }
        this._alphaMode[targetIndex] = mode;
        this._cacheRenderPipeline.setAlphaBlendEnabled(this._alphaState._alphaBlend, this._alphaState._numTargetEnabled);
        this._cacheRenderPipeline.setAlphaBlendFactors(this._alphaState._blendFunctionParameters, this._alphaState._blendEquationParameters);
    };
    ThinWebGPUEngine.prototype.setAlphaEquation = function (equation, targetIndex = 0) {
        AbstractEngine.prototype.setAlphaEquation.call(this, equation, targetIndex);
        this._cacheRenderPipeline.setAlphaBlendFactors(this._alphaState._blendFunctionParameters, this._alphaState._blendEquationParameters);
    };
}

RegisterEnginesWebGPUExtensionsEngineAlpha();

/** This file must only contain pure code and pure imports */
let _Registered$7 = false;
/**
 * Registers alpha-to-coverage support for WebGPU engines.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineAlphaToCoverage() {
    if (_Registered$7) {
        return;
    }
    _Registered$7 = true;
    const alphaToCoverageState = new WeakMap();
    ThinWebGPUEngine.prototype.getAlphaToCoverage = function () {
        return alphaToCoverageState.get(this) ?? false;
    };
    ThinWebGPUEngine.prototype.setAlphaToCoverage = function (enable) {
        const pipelineCache = this._cacheRenderPipeline;
        if ((alphaToCoverageState.get(this) ?? false) === enable && pipelineCache._alphaToCoverageEnabled === enable) {
            return;
        }
        alphaToCoverageState.set(this, enable);
        this._cacheRenderPipeline.setAlphaToCoverage(enable);
    };
    const pipelinePrototype = WebGPUCacheRenderPipeline.prototype;
    const buildRenderPipelineDescriptor = pipelinePrototype._buildRenderPipelineDescriptor;
    pipelinePrototype._buildRenderPipelineDescriptor = function (effect, topology, sampleCount) {
        const descriptor = buildRenderPipelineDescriptor.call(this, effect, topology, sampleCount);
        descriptor.multisample.alphaToCoverageEnabled = this._alphaToCoverageEnabled && sampleCount > 1;
        return descriptor;
    };
}

RegisterEnginesWebGPUExtensionsEngineAlphaToCoverage();

/** This file must only contain pure code and pure imports */
/**
 * @internal
 */
let _Registered$6 = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineRawTexture.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineRawTexture() {
    if (_Registered$6) {
        return;
    }
    _Registered$6 = true;
    // eslint-disable-next-line @typescript-eslint/naming-convention
    function ConvertRGBtoRGBATextureData(rgbData, width, height, textureType) {
        // Create new RGBA data container.
        let rgbaData;
        let val1 = 1;
        if (textureType === Constants.TEXTURETYPE_FLOAT) {
            rgbaData = new Float32Array(width * height * 4);
        }
        else if (textureType === Constants.TEXTURETYPE_HALF_FLOAT) {
            rgbaData = new Uint16Array(width * height * 4);
            val1 = 15360; // 15360 is the encoding of 1 in half float
        }
        else if (textureType === Constants.TEXTURETYPE_UNSIGNED_INTEGER) {
            rgbaData = new Uint32Array(width * height * 4);
        }
        else {
            rgbaData = new Uint8Array(width * height * 4);
        }
        // Convert each pixel.
        for (let x = 0; x < width; x++) {
            for (let y = 0; y < height; y++) {
                const index = (y * width + x) * 3;
                const newIndex = (y * width + x) * 4;
                // Map Old Value to new value.
                rgbaData[newIndex + 0] = rgbData[index + 0];
                rgbaData[newIndex + 1] = rgbData[index + 1];
                rgbaData[newIndex + 2] = rgbData[index + 2];
                // Add fully opaque alpha channel.
                rgbaData[newIndex + 3] = val1;
            }
        }
        return rgbaData;
    }
    ThinWebGPUEngine.prototype.createRawTexture = function (data, width, height, format, generateMipMaps, invertY, samplingMode, compression = null, type = Constants.TEXTURETYPE_UNSIGNED_BYTE, creationFlags = 0, useSRGBBuffer = false, mipLevelCount) {
        const texture = new InternalTexture(this, 3 /* InternalTextureSource.Raw */);
        texture.baseWidth = width;
        texture.baseHeight = height;
        texture.width = width;
        texture.height = height;
        texture.format = format;
        texture.generateMipMaps = generateMipMaps;
        texture.samplingMode = samplingMode;
        texture.invertY = invertY;
        texture._compression = compression;
        texture.type = type;
        texture._creationFlags = creationFlags;
        texture._useSRGBBuffer = useSRGBBuffer;
        if (!this._doNotHandleContextLost) {
            texture._bufferView = data;
        }
        this._textureHelper.updateMipLevelCountForInternalTexture(texture, mipLevelCount);
        this._textureHelper.createGPUTextureForInternalTexture(texture, width, height, undefined, creationFlags);
        this.updateRawTexture(texture, data, format, invertY, compression, type, useSRGBBuffer);
        this._internalTexturesCache.push(texture);
        return texture;
    };
    ThinWebGPUEngine.prototype.updateRawTexture = function (texture, bufferView, format, invertY, compression = null, type = Constants.TEXTURETYPE_UNSIGNED_BYTE, useSRGBBuffer = false, mipLevel) {
        if (!texture) {
            return;
        }
        if (!this._doNotHandleContextLost) {
            texture._bufferView = bufferView;
            texture.invertY = invertY;
            texture._compression = compression;
            texture._useSRGBBuffer = useSRGBBuffer;
            if (mipLevel !== undefined && bufferView) {
                if (!texture._bufferViewArray) {
                    texture._bufferViewArray = new Array(texture.mipLevelCount);
                }
                texture._bufferViewArray[mipLevel] = bufferView;
            }
        }
        if (bufferView) {
            const gpuTextureWrapper = texture._hardwareTexture;
            const needConversion = format === Constants.TEXTUREFORMAT_RGB;
            if (needConversion) {
                bufferView = ConvertRGBtoRGBATextureData(bufferView, texture.width, texture.height, type);
            }
            const data = new Uint8Array(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
            const mipWidth = Math.max(1, texture.width >> (mipLevel ?? 0));
            const mipHeight = Math.max(1, texture.height >> (mipLevel ?? 0));
            this._textureHelper.updateTexture(data, texture, mipWidth, mipHeight, texture.depth, gpuTextureWrapper.format, 0, mipLevel ?? 0, invertY, false, 0, 0);
            if (texture.generateMipMaps && !mipLevel) {
                this._generateMipmaps(texture, this._uploadEncoder);
            }
        }
        texture.isReady = true;
    };
    ThinWebGPUEngine.prototype.createRawCubeTexture = function (data, size, format, type, generateMipMaps, invertY, samplingMode, compression = null) {
        const texture = new InternalTexture(this, 8 /* InternalTextureSource.CubeRaw */);
        if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloatLinearFiltering) {
            generateMipMaps = false;
            samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
            Logger.Warn("Float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively.");
        }
        else if (type === Constants.TEXTURETYPE_HALF_FLOAT && !this._caps.textureHalfFloatLinearFiltering) {
            generateMipMaps = false;
            samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
            Logger.Warn("Half float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively.");
        }
        else if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloatRender) {
            generateMipMaps = false;
            Logger.Warn("Render to float textures is not supported. Mipmap generation forced to false.");
        }
        else if (type === Constants.TEXTURETYPE_HALF_FLOAT && !this._caps.colorBufferFloat) {
            generateMipMaps = false;
            Logger.Warn("Render to half float textures is not supported. Mipmap generation forced to false.");
        }
        texture.isCube = true;
        texture._originalFormat = format;
        texture.format = format === Constants.TEXTUREFORMAT_RGB ? Constants.TEXTUREFORMAT_RGBA : format;
        texture.type = type;
        texture.generateMipMaps = generateMipMaps;
        texture.width = size;
        texture.height = size;
        texture.samplingMode = samplingMode;
        if (!this._doNotHandleContextLost) {
            texture._bufferViewArray = data;
        }
        texture.invertY = invertY;
        texture._compression = compression;
        texture._cachedWrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        texture._cachedWrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        this._textureHelper.createGPUTextureForInternalTexture(texture);
        if (format === Constants.TEXTUREFORMAT_RGB) {
            const gpuTextureWrapper = texture._hardwareTexture;
            gpuTextureWrapper._originalFormatIsRGB = true;
        }
        if (data) {
            this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
        }
        texture.isReady = true;
        return texture;
    };
    ThinWebGPUEngine.prototype.updateRawCubeTexture = function (texture, bufferView, _format, type, invertY, compression = null) {
        texture._bufferViewArray = bufferView;
        texture.invertY = invertY;
        texture._compression = compression;
        const gpuTextureWrapper = texture._hardwareTexture;
        const needConversion = gpuTextureWrapper._originalFormatIsRGB;
        const faces = [0, 2, 4, 1, 3, 5];
        const data = [];
        for (let i = 0; i < bufferView.length; ++i) {
            let faceData = bufferView[faces[i]];
            if (needConversion) {
                faceData = ConvertRGBtoRGBATextureData(faceData, texture.width, texture.height, type);
            }
            data.push(new Uint8Array(faceData.buffer, faceData.byteOffset, faceData.byteLength));
        }
        this._textureHelper.updateCubeTextures(data, texture, texture.width, texture.height, gpuTextureWrapper.format, invertY, false, 0, 0);
        if (texture.generateMipMaps) {
            this._generateMipmaps(texture, this._uploadEncoder);
        }
        texture.isReady = true;
    };
    ThinWebGPUEngine.prototype.createRawCubeTextureFromUrl = function (url, scene, size, format, type, noMipmap, callback, mipmapGenerator, onLoad = null, onError = null, samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, invertY = false) {
        const texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
        scene?.addPendingData(texture);
        texture.url = url;
        texture.isReady = false;
        this._internalTexturesCache.push(texture);
        const onerror = (request, exception) => {
            scene?.removePendingData(texture);
            if (onError && request) {
                onError(request.status + " " + request.statusText, exception);
            }
        };
        const internalCallbackAsync = async (data) => {
            const faceDataArraysResult = callback(data);
            if (!faceDataArraysResult) {
                return;
            }
            const faceDataArrays = faceDataArraysResult instanceof Promise ? await faceDataArraysResult : faceDataArraysResult;
            const width = texture.width;
            if (mipmapGenerator) {
                const needConversion = format === Constants.TEXTUREFORMAT_RGB;
                const mipData = mipmapGenerator(faceDataArrays);
                const gpuTextureWrapper = texture._hardwareTexture;
                const faces = [0, 1, 2, 3, 4, 5];
                for (let level = 0; level < mipData.length; level++) {
                    const mipSize = width >> level;
                    const allFaces = [];
                    for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
                        let mipFaceData = mipData[level][faces[faceIndex]];
                        if (needConversion) {
                            mipFaceData = ConvertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type);
                        }
                        allFaces.push(new Uint8Array(mipFaceData.buffer, mipFaceData.byteOffset, mipFaceData.byteLength));
                    }
                    this._textureHelper.updateCubeTextures(allFaces, texture, mipSize, mipSize, gpuTextureWrapper.format, invertY, false, 0, 0);
                }
            }
            else {
                this.updateRawCubeTexture(texture, faceDataArrays, format, type, invertY);
            }
            texture.isReady = true;
            scene?.removePendingData(texture);
            if (onLoad) {
                onLoad();
            }
        };
        this._loadFile(url, (data) => {
            // eslint-disable-next-line github/no-then
            internalCallbackAsync(data).catch((err) => {
                onerror(undefined, err);
            });
        }, undefined, scene?.offlineProvider, true, onerror);
        return texture;
    };
    ThinWebGPUEngine.prototype.createRawTexture3D = function (data, width, height, depth, format, generateMipMaps, invertY, samplingMode, compression = null, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE, creationFlags = 0) {
        const source = 10 /* InternalTextureSource.Raw3D */;
        const texture = new InternalTexture(this, source);
        texture.baseWidth = width;
        texture.baseHeight = height;
        texture.baseDepth = depth;
        texture.width = width;
        texture.height = height;
        texture.depth = depth;
        texture.format = format;
        texture.type = textureType;
        texture.generateMipMaps = generateMipMaps;
        texture.samplingMode = samplingMode;
        texture.is3D = true;
        texture._creationFlags = creationFlags;
        if (!this._doNotHandleContextLost) {
            texture._bufferView = data;
        }
        this._textureHelper.createGPUTextureForInternalTexture(texture, width, height, undefined, creationFlags);
        this.updateRawTexture3D(texture, data, format, invertY, compression, textureType);
        this._internalTexturesCache.push(texture);
        return texture;
    };
    ThinWebGPUEngine.prototype.updateRawTexture3D = function (texture, bufferView, format, invertY, compression = null, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE) {
        if (!this._doNotHandleContextLost) {
            texture._bufferView = bufferView;
            texture.format = format;
            texture.invertY = invertY;
            texture._compression = compression;
        }
        if (bufferView) {
            const gpuTextureWrapper = texture._hardwareTexture;
            const needConversion = format === Constants.TEXTUREFORMAT_RGB;
            if (needConversion) {
                bufferView = ConvertRGBtoRGBATextureData(bufferView, texture.width, texture.height, textureType);
            }
            const data = new Uint8Array(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
            this._textureHelper.updateTexture(data, texture, texture.width, texture.height, texture.depth, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0);
            if (texture.generateMipMaps) {
                this._generateMipmaps(texture, this._uploadEncoder);
            }
        }
        texture.isReady = true;
    };
    ThinWebGPUEngine.prototype.createRawTexture2DArray = function (data, width, height, depth, format, generateMipMaps, invertY, samplingMode, compression = null, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE, creationFlags = 0, mipLevelCount) {
        const source = 11 /* InternalTextureSource.Raw2DArray */;
        const texture = new InternalTexture(this, source);
        texture.baseWidth = width;
        texture.baseHeight = height;
        texture.baseDepth = depth;
        texture.width = width;
        texture.height = height;
        texture.depth = depth;
        texture.format = format;
        texture.type = textureType;
        texture.generateMipMaps = generateMipMaps;
        texture.samplingMode = samplingMode;
        texture.is2DArray = true;
        texture._creationFlags = creationFlags;
        if (!this._doNotHandleContextLost) {
            texture._bufferView = data;
        }
        this._textureHelper.updateMipLevelCountForInternalTexture(texture, mipLevelCount);
        this._textureHelper.createGPUTextureForInternalTexture(texture, width, height, depth, creationFlags);
        this.updateRawTexture2DArray(texture, data, format, invertY, compression, textureType);
        this._internalTexturesCache.push(texture);
        return texture;
    };
    ThinWebGPUEngine.prototype.updateRawTexture2DArray = function (texture, bufferView, format, invertY, compression = null, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE, mipLevel) {
        if (!this._doNotHandleContextLost) {
            texture._bufferView = bufferView;
            texture.format = format;
            texture.invertY = invertY;
            texture._compression = compression;
            if (mipLevel !== undefined && bufferView) {
                if (!texture._bufferViewArray) {
                    texture._bufferViewArray = new Array(texture.mipLevelCount);
                }
                texture._bufferViewArray[mipLevel] = bufferView;
            }
        }
        if (bufferView) {
            const gpuTextureWrapper = texture._hardwareTexture;
            const needConversion = format === Constants.TEXTUREFORMAT_RGB;
            if (needConversion) {
                bufferView = ConvertRGBtoRGBATextureData(bufferView, texture.width, texture.height, textureType);
            }
            const data = new Uint8Array(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
            const mipWidth = Math.max(1, texture.width >> (mipLevel ?? 0));
            const mipHeight = Math.max(1, texture.height >> (mipLevel ?? 0));
            this._textureHelper.updateTexture(data, texture, mipWidth, mipHeight, texture.depth, gpuTextureWrapper.format, 0, mipLevel ?? 0, invertY, false, 0, 0);
            if (texture.generateMipMaps && !mipLevel) {
                this._generateMipmaps(texture, this._uploadEncoder);
            }
        }
        texture.isReady = true;
    };
}

RegisterEnginesWebGPUExtensionsEngineRawTexture();

/** This file must only contain pure code and pure imports */
let _Registered$5 = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineReadTexture.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineReadTexture() {
    if (_Registered$5) {
        return;
    }
    _Registered$5 = true;
    // eslint-disable-next-line @typescript-eslint/promise-function-async
    ThinWebGPUEngine.prototype._readTexturePixels = function (texture, width, height, faceIndex = -1, level = 0, buffer = null, flushRenderer = true, noDataConversion = false, x = 0, y = 0) {
        const gpuTextureWrapper = texture._hardwareTexture;
        if (flushRenderer) {
            this.flushFramebuffer();
        }
        return this._textureHelper.readPixels(gpuTextureWrapper.underlyingResource, x, y, width, height, gpuTextureWrapper.format, faceIndex, level, buffer, noDataConversion);
    };
    ThinWebGPUEngine.prototype._readTexturePixelsSync = function () {
        // eslint-disable-next-line no-throw-literal
        throw "_readTexturePixelsSync is unsupported in WebGPU!";
    };
}

RegisterEnginesWebGPUExtensionsEngineReadTexture();

/** This file must only contain pure code and pure imports */
let _Registered$4 = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineCubeTexture.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineCubeTexture() {
    if (_Registered$4) {
        return;
    }
    _Registered$4 = true;
    ThinWebGPUEngine.prototype._createDepthStencilCubeTexture = function (size, options) {
        const internalTexture = new InternalTexture(this, options.generateStencil ? 12 /* InternalTextureSource.DepthStencil */ : 14 /* InternalTextureSource.Depth */);
        internalTexture.isCube = true;
        internalTexture.label = options.label;
        const internalOptions = {
            bilinearFiltering: false,
            comparisonFunction: 0,
            samples: 1,
            depthTextureFormat: options.generateStencil ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT,
            ...options,
        };
        internalTexture.format = internalOptions.depthTextureFormat;
        this._setupDepthStencilTexture(internalTexture, size, internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples);
        this._textureHelper.createGPUTextureForInternalTexture(internalTexture);
        // Now that the hardware texture is created, we can retrieve the GPU format and set the right type to the internal texture
        const gpuTextureWrapper = internalTexture._hardwareTexture;
        internalTexture.type = WebGPUTextureHelper.GetTextureTypeFromFormat(gpuTextureWrapper.format);
        this._internalTexturesCache.push(internalTexture);
        return internalTexture;
    };
    ThinWebGPUEngine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad = null, onError = null, format, forcedExtension = null, createPolynomials = false, lodScale = 0, lodOffset = 0, fallback = null, loaderOptions, useSRGBBuffer = false, buffer = null) {
        return this.createCubeTextureBase(rootUrl, scene, files, !!noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback, null, (texture, imgs) => {
            const imageBitmaps = imgs; // we will always get an ImageBitmap array in WebGPU
            const width = imageBitmaps[0].width;
            const height = width;
            this._setCubeMapTextureParams(texture, !noMipmap);
            texture.format = format ?? -1;
            const gpuTextureWrapper = this._textureHelper.createGPUTextureForInternalTexture(texture, width, height);
            this._textureHelper.updateCubeTextures(imageBitmaps, texture, width, height, gpuTextureWrapper.format, false, false, 0, 0);
            if (!noMipmap) {
                this._generateMipmaps(texture, this._uploadEncoder);
            }
            texture.isReady = true;
            texture.onLoadedObservable.notifyObservers(texture);
            texture.onLoadedObservable.clear();
            if (onLoad) {
                onLoad();
            }
        }, !!useSRGBBuffer, buffer);
    };
    ThinWebGPUEngine.prototype._setCubeMapTextureParams = function (texture, loadMipmap, maxLevel) {
        texture.samplingMode = loadMipmap ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : Constants.TEXTURE_BILINEAR_SAMPLINGMODE;
        texture._cachedWrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        texture._cachedWrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        if (maxLevel) {
            texture._maxLodLevel = maxLevel;
        }
    };
    ThinWebGPUEngine.prototype.generateMipMapsForCubemap = function (texture) {
        if (texture.generateMipMaps) {
            const gpuTexture = texture._hardwareTexture?.underlyingResource;
            if (!gpuTexture) {
                this._textureHelper.createGPUTextureForInternalTexture(texture);
            }
            this._generateMipmaps(texture);
        }
    };
}

RegisterEnginesWebGPUExtensionsEngineCubeTexture();

/**
 * Specialized class used to store a render target of a WebGPU engine
 */
class WebGPURenderTargetWrapper extends RenderTargetWrapper {
    /**
     * Initializes the render target wrapper
     * @param isMulti true if the wrapper is a multi render target
     * @param isCube true if the wrapper should render to a cube texture
     * @param size size of the render target (width/height/layers)
     * @param engine engine used to create the render target
     * @param label defines the label to use for the wrapper (for debugging purpose only)
     */
    constructor(isMulti, isCube, size, engine, label) {
        super(isMulti, isCube, size, engine, label);
        /**
         * When true, the engine skips its render-target Y-flip when drawing into this target: it binds the
         * non-inverting internals UBO (yFactor = +1) and keeps the main-framebuffer front-face winding, exactly
         * as if rendering to the canvas. This is set for XR projection-layer targets, whose textures are handed
         * directly to the XR compositor (top-left origin, presented as-is, never re-sampled by Babylon) and must
         * therefore be rendered upright. Defaults to false so every other render target keeps the standard flip
         * that keeps a later-sampled RTT consistent with the WebGL texture-space convention.
         * @internal
         */
        this._disableEngineYFlip = false;
        if (engine.enableGPUTimingMeasurements) {
            this.gpuTimeInFrame = new WebGPUPerfCounter();
        }
    }
}

/** This file must only contain pure code and pure imports */
let _Registered$3 = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineRenderTarget.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineRenderTarget() {
    if (_Registered$3) {
        return;
    }
    _Registered$3 = true;
    ThinWebGPUEngine.prototype._createHardwareRenderTargetWrapper = function (isMulti, isCube, size) {
        const rtWrapper = new WebGPURenderTargetWrapper(isMulti, isCube, size, this);
        this._renderTargetWrapperCache.push(rtWrapper);
        return rtWrapper;
    };
    ThinWebGPUEngine.prototype.createRenderTargetTexture = function (size, options) {
        const rtWrapper = this._createHardwareRenderTargetWrapper(false, false, size);
        const fullOptions = {};
        if (options !== undefined && typeof options === "object") {
            fullOptions.generateMipMaps = options.generateMipMaps;
            fullOptions.generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
            fullOptions.generateStencilBuffer = fullOptions.generateDepthBuffer && options.generateStencilBuffer;
            fullOptions.samplingMode = options.samplingMode === undefined ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : options.samplingMode;
            fullOptions.creationFlags = options.creationFlags ?? 0;
            fullOptions.noColorAttachment = !!options.noColorAttachment;
            fullOptions.colorAttachment = options.colorAttachment;
            fullOptions.samples = options.samples;
            fullOptions.label = options.label;
            fullOptions.format = options.format;
            fullOptions.type = options.type;
        }
        else {
            fullOptions.generateMipMaps = options;
            fullOptions.generateDepthBuffer = true;
            fullOptions.generateStencilBuffer = false;
            fullOptions.samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
            fullOptions.creationFlags = 0;
            fullOptions.noColorAttachment = false;
        }
        const texture = fullOptions.colorAttachment || (fullOptions.noColorAttachment ? null : this._createInternalTexture(size, fullOptions, true, 5 /* InternalTextureSource.RenderTarget */));
        rtWrapper.label = fullOptions.label ?? "RenderTargetWrapper";
        rtWrapper._samples = fullOptions.colorAttachment?.samples ?? fullOptions.samples ?? 1;
        rtWrapper._generateDepthBuffer = fullOptions.generateDepthBuffer;
        rtWrapper._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
        rtWrapper.setTextures(texture);
        if (rtWrapper._generateDepthBuffer || rtWrapper._generateStencilBuffer) {
            rtWrapper.createDepthStencilTexture(0, false, // force false as filtering is not supported for depth textures
            rtWrapper._generateStencilBuffer, rtWrapper.samples, fullOptions.generateStencilBuffer ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT, fullOptions.label ? fullOptions.label + "-DepthStencil" : undefined);
        }
        if (texture && !fullOptions.colorAttachment) {
            if (options !== undefined && typeof options === "object" && options.createMipMaps && !fullOptions.generateMipMaps) {
                texture.generateMipMaps = true;
            }
            this._textureHelper.createGPUTextureForInternalTexture(texture, undefined, undefined, undefined, fullOptions.creationFlags);
            if (options !== undefined && typeof options === "object" && options.createMipMaps && !fullOptions.generateMipMaps) {
                texture.generateMipMaps = false;
            }
        }
        return rtWrapper;
    };
    ThinWebGPUEngine.prototype._createDepthStencilTexture = function (size, options, wrapper) {
        const internalOptions = {
            bilinearFiltering: false,
            comparisonFunction: 0,
            samples: 1,
            depthTextureFormat: options.generateStencil ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT,
            ...options,
        };
        const hasStencil = HasStencilAspect(internalOptions.depthTextureFormat);
        wrapper._depthStencilTextureWithStencil = hasStencil;
        const internalTexture = new InternalTexture(this, hasStencil ? 12 /* InternalTextureSource.DepthStencil */ : 14 /* InternalTextureSource.Depth */);
        internalTexture.label = options.label;
        internalTexture.format = internalOptions.depthTextureFormat;
        internalTexture.type = GetTypeForDepthTexture(internalTexture.format);
        this._setupDepthStencilTexture(internalTexture, size, internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples);
        this._textureHelper.createGPUTextureForInternalTexture(internalTexture);
        this._internalTexturesCache.push(internalTexture);
        return internalTexture;
    };
    ThinWebGPUEngine.prototype._setupDepthStencilTexture = function (internalTexture, size, bilinearFiltering, comparisonFunction, samples = 1) {
        const width = size.width ?? size;
        const height = size.height ?? size;
        const layers = size.layers || 0;
        const depth = size.depth || 0;
        internalTexture.baseWidth = width;
        internalTexture.baseHeight = height;
        internalTexture.width = width;
        internalTexture.height = height;
        internalTexture.is2DArray = layers > 0;
        internalTexture.is3D = depth > 0;
        internalTexture.depth = layers || depth;
        internalTexture.isReady = true;
        internalTexture.samples = samples;
        internalTexture.generateMipMaps = false;
        internalTexture.samplingMode = bilinearFiltering ? Constants.TEXTURE_BILINEAR_SAMPLINGMODE : Constants.TEXTURE_NEAREST_SAMPLINGMODE;
        internalTexture.type = Constants.TEXTURETYPE_FLOAT; // the right type will be set later
        internalTexture._comparisonFunction = comparisonFunction;
        internalTexture._cachedWrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        internalTexture._cachedWrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
    };
    ThinWebGPUEngine.prototype.updateRenderTargetTextureSampleCount = function (rtWrapper, samples) {
        if (!rtWrapper || !rtWrapper.texture || rtWrapper.samples === samples) {
            return samples;
        }
        samples = Math.min(samples, this.getCaps().maxMSAASamples);
        // Releases existing MSAA textures. New ones will be created on demand.
        const gpuTexture = rtWrapper.texture._hardwareTexture;
        gpuTexture?.releaseMSAATextures();
        if (rtWrapper._depthStencilTexture) {
            rtWrapper._depthStencilTexture._hardwareTexture?.releaseMSAATextures();
            rtWrapper._depthStencilTexture.samples = samples;
        }
        rtWrapper._samples = samples;
        rtWrapper.texture.samples = samples;
        return samples;
    };
}

RegisterEnginesWebGPUExtensionsEngineRenderTarget();

/** This file must only contain pure code and pure imports */
let _Registered$2 = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineRenderTargetTexture.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineRenderTargetTexture() {
    if (_Registered$2) {
        return;
    }
    _Registered$2 = true;
    ThinWebGPUEngine.prototype.setDepthStencilTexture = function (channel, uniform, texture, name) {
        if (!texture || !texture.depthStencilTexture) {
            this._setTexture(channel, null, undefined, undefined, name);
        }
        else {
            this._setTexture(channel, texture, false, true, name);
        }
    };
}

RegisterEnginesWebGPUExtensionsEngineRenderTargetTexture();

/** This file must only contain pure code and pure imports */
let _Registered$1 = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineRenderTargetCube.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineRenderTargetCube() {
    if (_Registered$1) {
        return;
    }
    _Registered$1 = true;
    ThinWebGPUEngine.prototype.createRenderTargetCubeTexture = function (size, options) {
        const rtWrapper = this._createHardwareRenderTargetWrapper(false, true, size);
        const fullOptions = {
            generateMipMaps: true,
            generateDepthBuffer: true,
            generateStencilBuffer: false,
            type: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            samplingMode: Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
            format: Constants.TEXTUREFORMAT_RGBA,
            samples: 1,
            ...options,
        };
        fullOptions.generateStencilBuffer = fullOptions.generateDepthBuffer && fullOptions.generateStencilBuffer;
        rtWrapper.label = fullOptions.label ?? "RenderTargetWrapper";
        rtWrapper._generateDepthBuffer = fullOptions.generateDepthBuffer;
        rtWrapper._generateStencilBuffer = fullOptions.generateStencilBuffer;
        const texture = new InternalTexture(this, 5 /* InternalTextureSource.RenderTarget */);
        texture.width = size;
        texture.height = size;
        texture.depth = 0;
        texture.isReady = true;
        texture.isCube = true;
        texture.samples = fullOptions.samples;
        texture.generateMipMaps = fullOptions.generateMipMaps;
        texture.samplingMode = fullOptions.samplingMode;
        texture.type = fullOptions.type;
        texture.format = fullOptions.format;
        this._internalTexturesCache.push(texture);
        rtWrapper.setTextures(texture);
        if (rtWrapper._generateDepthBuffer || rtWrapper._generateStencilBuffer) {
            rtWrapper.createDepthStencilTexture(0, fullOptions.samplingMode === undefined ||
                fullOptions.samplingMode === Constants.TEXTURE_BILINEAR_SAMPLINGMODE ||
                fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR ||
                fullOptions.samplingMode === Constants.TEXTURE_TRILINEAR_SAMPLINGMODE ||
                fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR ||
                fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST ||
                fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR ||
                fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR ||
                fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST, rtWrapper._generateStencilBuffer, rtWrapper.samples);
        }
        if (options && options.createMipMaps && !fullOptions.generateMipMaps) {
            texture.generateMipMaps = true;
        }
        this._textureHelper.createGPUTextureForInternalTexture(texture);
        if (options && options.createMipMaps && !fullOptions.generateMipMaps) {
            texture.generateMipMaps = false;
        }
        return rtWrapper;
    };
}

RegisterEnginesWebGPUExtensionsEngineRenderTargetCube();

/** This file must only contain pure code and pure imports */
let _Registered = false;
/**
 * Register side effects for enginesWebGPUExtensionsEngineQuery.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterEnginesWebGPUExtensionsEngineQuery() {
    if (_Registered) {
        return;
    }
    _Registered = true;
    ThinWebGPUEngine.prototype.getGPUFrameTimeCounter = function () {
        return this._timestampQuery.gpuFrameTimeCounter;
    };
    ThinWebGPUEngine.prototype.captureGPUFrameTime = function (value) {
        this._timestampQuery.enable = value && !!this._caps.timerQuery;
    };
    ThinWebGPUEngine.prototype.createQuery = function () {
        return this._occlusionQuery.createQuery();
    };
    ThinWebGPUEngine.prototype.deleteQuery = function (query) {
        this._occlusionQuery.deleteQuery(query);
        return this;
    };
    ThinWebGPUEngine.prototype.isQueryResultAvailable = function (query) {
        return this._occlusionQuery.isQueryResultAvailable(query);
    };
    ThinWebGPUEngine.prototype.getQueryResult = function (query) {
        return this._occlusionQuery.getQueryResult(query);
    };
    ThinWebGPUEngine.prototype.beginOcclusionQuery = function (algorithmType, query) {
        if (this.compatibilityMode) {
            if (this._occlusionQuery.canBeginQuery(query)) {
                this._currentRenderPass?.beginOcclusionQuery(query);
                return true;
            }
        }
        else {
            this._bundleList.addItem(new WebGPURenderItemBeginOcclusionQuery(query));
            return true;
        }
        return false;
    };
    ThinWebGPUEngine.prototype.endOcclusionQuery = function () {
        if (this.compatibilityMode) {
            this._currentRenderPass?.endOcclusionQuery();
        }
        else {
            this._bundleList.addItem(new WebGPURenderItemEndOcclusionQuery());
        }
        return this;
    };
}

/**
 * Re-exports pure implementation and applies runtime side effects.
 * Import engine.query.pure for tree-shakeable, side-effect-free usage.
 */
RegisterEnginesWebGPUExtensionsEngineQuery();

export { WebGPUEngine };
//# sourceMappingURL=webgpuEngine-DcpN7xPo.esm.js.map