/
githubmirror
/
pixijs
Обзор
Документация
Войти
/
githubmirror
/
pixijs
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
src/prepare/PrepareBase.ts
223 строки
6 KB
Ilia Malanin
fix: destroy of prepared but not rendered text should not throw an error (#11672)
10 сен 2025, 10:18
Не верифицирован
10 сен 2025, 10:18
fbf2049
Код
Авторство
О чём код?
import { Container } from '../scene/container/Container'; import { UPDATE_PRIORITY } from '../ticker/const'; import { Ticker } from '../ticker/Ticker'; import type { TextureSource } from '../rendering/renderers/shared/texture/sources/TextureSource'; import type { Texture } from '../rendering/renderers/shared/texture/Texture'; import type { Renderer } from '../rendering/renderers/types'; import type { GraphicsContext } from '../scene/graphics/shared/GraphicsContext'; import type { Text } from '../scene/text/Text'; /** * The accepted types to pass to the prepare system * @category rendering * @advanced */ export type PrepareSourceItem = Container | TextureSource | Texture | GraphicsContext; /** * The valid types resolved to the queue ready for upload * @category rendering * @advanced */ export type PrepareQueueItem = TextureSource | Text | GraphicsContext; /** * Part of the prepare system. Responsible for uploading all the items to the GPU. * This class provides the base functionality and handles processing the queue asynchronously. * @category rendering * @advanced */ export abstract class PrepareBase { /** The number of uploads to process per frame */ public static uploadsPerFrame = 4; /** Reference to the renderer */ protected renderer: Renderer; /** The queue to process over a async timer */ protected queue: PrepareQueueItem[]; /** Collection of callbacks to call when the uploads are finished */ protected resolves: ((value: void | PromiseLike<void>) => void)[]; /** Timeout id for next processing call */ protected timeout?: number; private _destroyed: boolean; /** * @param {Renderer} renderer - A reference to the current renderer */ constructor(renderer: Renderer) { this.renderer = renderer; this.queue = []; this.resolves = []; } /** Resolve the given resource type and return an item for the queue */ protected abstract resolveQueueItem(source: PrepareSourceItem, queue: PrepareQueueItem[]): void; protected abstract uploadQueueItem(item: PrepareQueueItem): void; /** * Return a copy of the queue * @returns {PrepareQueueItem[]} The queue */ public getQueue(): PrepareQueueItem[] { return [...this.queue]; } /** * Add a textures or graphics resource to the queue * @param {PrepareSourceItem | PrepareSourceItem[]} resource */ public add(resource: PrepareSourceItem | PrepareSourceItem[]): this { const resourceArray = Array.isArray(resource) ? resource : [resource]; for (const resourceItem of resourceArray) { // handle containers and their children if (resourceItem instanceof Container) { this._addContainer(resourceItem); } else { this.resolveQueueItem(resourceItem, this.queue); } } return this; } /** * Recursively add a container and its children to the queue * @param {Container} container - The container to add to the queue */ private _addContainer(container: Container): void { this.resolveQueueItem(container, this.queue); // recursively add children for (const child of container.children) { this._addContainer(child); } } /** * Upload all the textures and graphics to the GPU (optionally add more resources to the queue first) * @param {PrepareSourceItem | PrepareSourceItem[] | undefined} resource */ public upload(resource?: PrepareSourceItem | PrepareSourceItem[]): Promise<void> { if (resource) { this.add(resource); } return new Promise((resolve) => { if (this.queue.length) { // add resolve callback to the collection this.resolves.push(resolve); // eliminate duplicates first this.dedupeQueue(); // launch first tick Ticker.system.addOnce(this._tick, this, UPDATE_PRIORITY.UTILITY); } else { // queue is empty, resolve immediately resolve(); } }); } /** eliminate duplicates before processing */ public dedupeQueue(): void { const hash = Object.create(null); let nextUnique = 0; for (let i = 0; i < this.queue.length; i++) { const current = this.queue[i]; if (!hash[current.uid]) { hash[current.uid] = true; this.queue[nextUnique++] = current; } } this.queue.length = nextUnique; } public destroy(): void { this._destroyed = true; clearTimeout(this.timeout); } /** called per frame by the ticker, defer processing to next tick */ private readonly _tick = () => { if (this._destroyed) return; this.timeout = setTimeout(this._processQueue, 0) as unknown as number; }; /** process the queue up to max item limit per frame */ private readonly _processQueue = () => { if (this._destroyed) return; const { queue } = this; let itemsProcessed = 0; // process the maximum number of items per frame while (queue.length && itemsProcessed < PrepareBase.uploadsPerFrame) { const queueItem = queue.shift(); this.uploadQueueItem(queueItem); itemsProcessed++; } if (queue.length) { // queue is not empty, continue processing on next frame Ticker.system.addOnce(this._tick, this, UPDATE_PRIORITY.UTILITY); } else { // queue is empty, resolve immediately this._resolve(); } }; /** Call all the resolve callbacks */ private _resolve(): void { const { resolves } = this; // call all resolve callbacks const array = resolves.slice(0); resolves.length = 0; for (const resolve of array) { resolve(); } } }