{"version":3,"file":"WorkerManager.mjs","sources":["../../../../src/assets/loader/workers/WorkerManager.ts"],"sourcesContent":["import CheckImageBitmapWorker from 'worker:./checkImageBitmap.worker.ts';\nimport LoadImageBitmapWorker from 'worker:./loadImageBitmap.worker.ts';\n\nimport type { TextureSourceOptions } from '../../../rendering/renderers/shared/texture/sources/TextureSource';\nimport type { ResolvedAsset } from '../../types';\n\nlet UUID = 0;\nlet MAX_WORKERS: number;\n\ntype LoadImageBitmapResult = {\n    data?: ImageBitmap,\n    error?: Error,\n    uuid: number,\n    id: string,\n};\n\n/** @internal */\nclass WorkerManagerClass\n{\n    /**\n     * Hash map storing resolve/reject functions for pending worker requests.\n     * Keyed by UUID to match responses with their corresponding promises.\n     */\n    private _resolveHash: {\n        [key: string]: {\n            resolve: (...param: any[]) => void;\n            reject: (...param: any[]) => void;\n        }\n    };\n    /** Pool of available workers ready for use */\n    private readonly _workerPool: Worker[];\n    /** Queue of pending work items waiting for available workers */\n    private readonly _queue: {\n        id: string;\n        arguments: any[];\n        resolve: (...param: any[]) => void;\n        reject: (...param: any[]) => void;\n    }[];\n\n    /** Whether the worker manager has been initialized */\n    private _initialized = false;\n\n    /** Current number of created workers (used to enforce MAX_WORKERS limit) */\n    private _createdWorkers = 0;\n    /** Cached promise for ImageBitmap support check */\n    private _isImageBitmapSupported?: Promise<boolean>;\n\n    constructor()\n    {\n        this._workerPool = [];\n        this._queue = [];\n\n        this._resolveHash = {};\n    }\n\n    /**\n     * Checks if ImageBitmap is supported in the current environment.\n     *\n     * This method uses a dedicated worker to test ImageBitmap support\n     * and caches the result for subsequent calls.\n     * @returns Promise that resolves to true if ImageBitmap is supported, false otherwise\n     */\n    public isImageBitmapSupported(): Promise<boolean>\n    {\n        if (this._isImageBitmapSupported !== undefined) return this._isImageBitmapSupported;\n\n        this._isImageBitmapSupported = new Promise((resolve) =>\n        {\n            const { worker } = new CheckImageBitmapWorker();\n\n            worker.addEventListener('message', (event: MessageEvent<boolean>) =>\n            {\n                worker.terminate();\n                CheckImageBitmapWorker.revokeObjectURL();\n                resolve(event.data);\n            });\n        });\n\n        return this._isImageBitmapSupported;\n    }\n\n    /**\n     * Loads an image as an ImageBitmap using a web worker.\n     * @param src - The source URL or path of the image to load\n     * @param asset - Optional resolved asset containing additional texture source options\n     * @returns Promise that resolves to the loaded ImageBitmap\n     * @example\n     * ```typescript\n     * const bitmap = await WorkerManager.loadImageBitmap('image.png');\n     * const bitmapWithOptions = await WorkerManager.loadImageBitmap('image.png', asset);\n     * ```\n     */\n    public loadImageBitmap(src: string, asset?: ResolvedAsset<TextureSourceOptions<any>>): Promise<ImageBitmap>\n    {\n        return this._run('loadImageBitmap', [src, asset?.data?.alphaMode]) as Promise<ImageBitmap>;\n    }\n\n    /**\n     * Initializes the worker pool if not already initialized.\n     * Currently a no-op but reserved for future initialization logic.\n     */\n    private async _initWorkers()\n    {\n        if (this._initialized) return;\n\n        this._initialized = true;\n    }\n\n    /**\n     * Gets an available worker from the pool or creates a new one if needed.\n     *\n     * Workers are created up to the MAX_WORKERS limit (based on navigator.hardwareConcurrency).\n     * Each worker is configured with a message handler for processing results.\n     * @returns Available worker or undefined if pool is at capacity and no workers are free\n     */\n    private _getWorker(): Worker\n    {\n        if (MAX_WORKERS === undefined)\n        {\n            MAX_WORKERS = navigator.hardwareConcurrency || 4;\n        }\n        let worker = this._workerPool.pop();\n\n        if (!worker && this._createdWorkers < MAX_WORKERS)\n        {\n            // only create as many as MAX_WORKERS allows..\n            this._createdWorkers++;\n            worker = new LoadImageBitmapWorker().worker;\n\n            worker.addEventListener('message', (event: MessageEvent) =>\n            {\n                this._complete(event.data);\n\n                this._returnWorker(event.target as Worker);\n                this._next();\n            });\n        }\n\n        return worker;\n    }\n\n    /**\n     * Returns a worker to the pool after completing a task.\n     * @param worker - The worker to return to the pool\n     */\n    private _returnWorker(worker: Worker)\n    {\n        this._workerPool.push(worker);\n    }\n\n    /**\n     * Handles completion of a worker task by resolving or rejecting the corresponding promise.\n     * @param data - Result data from the worker containing uuid, data, and optional error\n     */\n    private _complete(data: LoadImageBitmapResult): void\n    {\n        if (!this._resolveHash[data.uuid])\n        {\n            // this can happen if the worker manager is reset before a task completes\n            return;\n        }\n\n        if (data.error !== undefined)\n        {\n            this._resolveHash[data.uuid].reject(data.error);\n        }\n        else\n        {\n            this._resolveHash[data.uuid].resolve(data.data);\n        }\n\n        delete this._resolveHash[data.uuid];\n    }\n\n    /**\n     * Executes a task using the worker pool system.\n     *\n     * Queues the task and processes it when a worker becomes available.\n     * @param id - Identifier for the type of task to run\n     * @param args - Arguments to pass to the worker\n     * @returns Promise that resolves with the worker's result\n     */\n    private async _run(id: string, args: any[]): Promise<any>\n    {\n        await this._initWorkers();\n        // push into the queue...\n\n        const promise = new Promise((resolve, reject) =>\n        {\n            this._queue.push({ id, arguments: args, resolve, reject });\n        });\n\n        this._next();\n\n        return promise;\n    }\n\n    /**\n     * Processes the next item in the queue if workers are available.\n     *\n     * This method is called after worker initialization and when workers\n     * complete tasks to continue processing the queue.\n     */\n    private _next(): void\n    {\n        // nothing to do\n        if (!this._queue.length) return;\n\n        const worker = this._getWorker();\n\n        // no workers available...\n        if (!worker)\n        {\n            return;\n        }\n\n        const toDo = this._queue.pop();\n\n        const id = toDo.id;\n\n        this._resolveHash[UUID] = { resolve: toDo.resolve, reject: toDo.reject };\n\n        worker.postMessage({\n            data: toDo.arguments,\n            uuid: UUID++,\n            id,\n        });\n    }\n\n    /**\n     * Resets the worker manager, terminating all workers and clearing the queue.\n     *\n     * This method:\n     * - Terminates all active workers\n     * - Rejects all pending promises with an error\n     * - Clears all internal state\n     * - Resets initialization flags\n     *\n     * This should be called when the worker manager is no longer needed\n     * to prevent memory leaks and ensure proper cleanup.\n     * @example\n     * ```typescript\n     * // Clean up when shutting down\n     * WorkerManager.reset();\n     * ```\n     */\n    public reset(): void\n    {\n        // Terminate all workers\n        this._workerPool.forEach((worker) => worker.terminate());\n        this._workerPool.length = 0;\n\n        // Reject pending promises\n        Object.values(this._resolveHash).forEach(({ reject }) =>\n        {\n            reject?.(new Error('WorkerManager has been reset before completion'));\n        });\n        this._resolveHash = {};\n        this._queue.length = 0;\n\n        this._initialized = false;\n        this._createdWorkers = 0;\n    }\n}\n\n/**\n * Manages a pool of web workers for loading ImageBitmap objects asynchronously.\n *\n * This class provides a thread-safe way to load images using web workers,\n * automatically managing worker creation, pooling, and cleanup. It supports\n * checking ImageBitmap support and queuing multiple load requests.\n *\n * > [!IMPORTANT] You should not need to use this class directly\n * > However, you can call `WorkerManager.reset()` to clean up all workers when they are no longer needed.\n * @category assets\n * @advanced\n */\nconst WorkerManager = new WorkerManagerClass();\n\nexport {\n    WorkerManager,\n};\n"],"names":["CheckImageBitmapWorker","LoadImageBitmapWorker"],"mappings":"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"}