/** * @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js' * @import { LayerRenderStep } from './layer-render-step.js' * @import { LightingParams } from '../lighting/lighting-params.js' */ /** * A class managing instances of world clusters used by the renderer for layers with * unique sets of clustered lights. * * @ignore */ export class WorldClustersAllocator { /** * Create a new instance. * * @param {GraphicsDevice} graphicsDevice - The graphics device. */ constructor(graphicsDevice: GraphicsDevice); /** * Empty cluster with no lights. * * @type {WorldClusters|null} */ _empty: WorldClusters | null; /** * All allocated clusters * * @type {WorldClusters[]} */ _allocated: WorldClusters[]; /** * Layer render steps with all unique light clusters. The key is the hash of lights on a layer, * the value is a layer render step with unique light clusters. * * @type {Map} */ _clusters: Map; /** * Clusters allocated in a previous frame, available for reuse this frame. Owned by this * allocator (destroyed in {@link WorldClustersAllocator#destroy}) and only transiently non-empty * within a single {@link WorldClustersAllocator#upload} call. * * @type {WorldClusters[]} */ _recycled: WorldClusters[]; /** * Layer render steps that requested a cluster this frame, resolved together in * {@link WorldClustersAllocator#upload} (after culling). * * @type {LayerRenderStep[]} */ _requestedSteps: LayerRenderStep[]; device: GraphicsDevice; destroy(): void; get count(): number; get empty(): WorldClusters; /** * Creates the shared empty (no-lights) cluster if it does not exist yet, and returns it. This * uploads the cluster's texture, so it must run outside a render pass - the clustered update * pass calls it at construction. Reading {@link WorldClustersAllocator#empty} also creates it * lazily, as a fallback. * * @returns {WorldClusters} The empty cluster. */ createEmpty(): WorldClusters; /** * Discards the previous frame's cluster requests. Called once at the start of the frame (before * any {@link WorldClustersAllocator#request}), so a frame that builds but never uploads - e.g. * one interrupted before rendering - does not carry stale steps into the next. */ reset(): void; /** * Records that a layer render step will be rendered this frame and may need a light cluster. * Called during frame graph build (from a render pass's frameUpdate); eligibility, cluster * de-duplication and assignment are all resolved later in {@link WorldClustersAllocator#upload}, * so they observe the final layer state after any culling callbacks. * * @param {LayerRenderStep} step - The layer render step that may need a cluster. */ request(step: LayerRenderStep): void; /** * Resolves and uploads the clusters for the steps requested this frame. For each step whose * layer has clustered lights and meshes it assigns a cluster (steps whose layer shares the same * clustered-light set share one; others are left without a cluster and fall back to * {@link WorldClustersAllocator#empty} at render time), recycling the previous frame's clusters * and destroying any not reused, then uploads each unique cluster's light data. * * Runs from the clustered update pass - after cullComposition and its precull / postcull / * cull:end callbacks, and before the passes that use the clusters execute - so a callback that * adds or removes a layer's meshes or lights is reflected here. The whole assignment (including * the recycle pool) happens within this one synchronous call, so no partially-recycled cluster * is ever visible to {@link WorldClustersAllocator#destroy}. * * @param {LightingParams} lighting - The clustered lighting parameters. */ upload(lighting: LightingParams): void; } import { WorldClusters } from '../lighting/world-clusters.js'; import type { LayerRenderStep } from './layer-render-step.js'; import type { GraphicsDevice } from '../../platform/graphics/graphics-device.js'; import type { LightingParams } from '../lighting/lighting-params.js';