{"version":3,"file":"PrepareBase.mjs","sources":["../../src/prepare/PrepareBase.ts"],"sourcesContent":["import { Container } from '../scene/container/Container';\nimport { UPDATE_PRIORITY } from '../ticker/const';\nimport { Ticker } from '../ticker/Ticker';\n\nimport type { TextureSource } from '../rendering/renderers/shared/texture/sources/TextureSource';\nimport type { Texture } from '../rendering/renderers/shared/texture/Texture';\nimport type { Renderer } from '../rendering/renderers/types';\nimport type { GraphicsContext } from '../scene/graphics/shared/GraphicsContext';\nimport type { Text } from '../scene/text/Text';\n\n/**\n * The accepted types to pass to the prepare system\n * @category rendering\n * @advanced\n */\nexport type PrepareSourceItem = Container | TextureSource | Texture | GraphicsContext;\n\n/**\n * The valid types resolved to the queue ready for upload\n * @category rendering\n * @advanced\n */\nexport type PrepareQueueItem = TextureSource | Text | GraphicsContext;\n\n/**\n * Part of the prepare system. Responsible for uploading all the items to the GPU.\n * This class provides the base functionality and handles processing the queue asynchronously.\n * @category rendering\n * @advanced\n */\nexport abstract class PrepareBase\n{\n    /** The number of uploads to process per frame */\n    public static uploadsPerFrame = 4;\n\n    /** Reference to the renderer */\n    protected renderer: Renderer;\n\n    /** The queue to process over a async timer */\n    protected queue: PrepareQueueItem[];\n\n    /** Collection of callbacks to call when the uploads are finished */\n    protected resolves: ((value: void | PromiseLike<void>) => void)[];\n\n    /** Timeout id for next processing call */\n    protected timeout?: number;\n\n    private _destroyed: boolean;\n\n    /**\n     * @param {Renderer} renderer - A reference to the current renderer\n     */\n    constructor(renderer: Renderer)\n    {\n        this.renderer = renderer;\n        this.queue = [];\n        this.resolves = [];\n    }\n\n    /** Resolve the given resource type and return an item for the queue */\n    protected abstract resolveQueueItem(source: PrepareSourceItem, queue: PrepareQueueItem[]): void;\n    protected abstract uploadQueueItem(item: PrepareQueueItem): void;\n\n    /**\n     * Return a copy of the queue\n     * @returns {PrepareQueueItem[]} The queue\n     */\n    public getQueue(): PrepareQueueItem[]\n    {\n        return [...this.queue];\n    }\n\n    /**\n     * Add a textures or graphics resource to the queue\n     * @param {PrepareSourceItem | PrepareSourceItem[]} resource\n     */\n    public add(resource: PrepareSourceItem | PrepareSourceItem[]): this\n    {\n        const resourceArray = Array.isArray(resource) ? resource : [resource];\n\n        for (const resourceItem of resourceArray)\n        {\n            // handle containers and their children\n            if (resourceItem instanceof Container)\n            {\n                this._addContainer(resourceItem);\n            }\n            else\n            {\n                this.resolveQueueItem(resourceItem, this.queue);\n            }\n        }\n\n        return this;\n    }\n\n    /**\n     * Recursively add a container and its children to the queue\n     * @param {Container} container - The container to add to the queue\n     */\n    private _addContainer(container: Container): void\n    {\n        this.resolveQueueItem(container, this.queue);\n\n        // recursively add children\n        for (const child of container.children)\n        {\n            this._addContainer(child);\n        }\n    }\n\n    /**\n     * Upload all the textures and graphics to the GPU (optionally add more resources to the queue first)\n     * @param {PrepareSourceItem | PrepareSourceItem[] | undefined} resource\n     */\n    public upload(resource?: PrepareSourceItem | PrepareSourceItem[]): Promise<void>\n    {\n        if (resource)\n        {\n            this.add(resource);\n        }\n\n        return new Promise((resolve) =>\n        {\n            if (this.queue.length)\n            {\n                // add resolve callback to the collection\n                this.resolves.push(resolve);\n\n                // eliminate duplicates first\n                this.dedupeQueue();\n\n                // launch first tick\n                Ticker.system.addOnce(this._tick, this, UPDATE_PRIORITY.UTILITY);\n            }\n            else\n            {\n                // queue is empty, resolve immediately\n                resolve();\n            }\n        });\n    }\n\n    /** eliminate duplicates before processing */\n    public dedupeQueue(): void\n    {\n        const hash = Object.create(null);\n        let nextUnique = 0;\n\n        for (let i = 0; i < this.queue.length; i++)\n        {\n            const current = this.queue[i];\n\n            if (!hash[current.uid])\n            {\n                hash[current.uid] = true;\n                this.queue[nextUnique++] = current;\n            }\n        }\n\n        this.queue.length = nextUnique;\n    }\n\n    public destroy(): void\n    {\n        this._destroyed = true;\n        clearTimeout(this.timeout);\n    }\n\n    /** called per frame by the ticker, defer processing to next tick */\n    private readonly _tick = () =>\n    {\n        if (this._destroyed) return;\n\n        this.timeout = setTimeout(this._processQueue, 0) as unknown as number;\n    };\n\n    /** process the queue up to max item limit per frame */\n    private readonly _processQueue = () =>\n    {\n        if (this._destroyed) return;\n\n        const { queue } = this;\n        let itemsProcessed = 0;\n\n        // process the maximum number of items per frame\n        while (queue.length && itemsProcessed < PrepareBase.uploadsPerFrame)\n        {\n            const queueItem = queue.shift();\n\n            this.uploadQueueItem(queueItem);\n\n            itemsProcessed++;\n        }\n\n        if (queue.length)\n        {\n            // queue is not empty, continue processing on next frame\n            Ticker.system.addOnce(this._tick, this, UPDATE_PRIORITY.UTILITY);\n        }\n        else\n        {\n            // queue is empty, resolve immediately\n            this._resolve();\n        }\n    };\n\n    /** Call all the resolve callbacks */\n    private _resolve(): void\n    {\n        const { resolves } = this;\n\n        // call all resolve callbacks\n        const array = resolves.slice(0);\n\n        resolves.length = 0;\n\n        for (const resolve of array)\n        {\n            resolve();\n        }\n    }\n}\n"],"names":[],"mappings":"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"}