import { MaterialEffect } from "../materials/MaterialEffect.js"; import { Sprite2DMaterial, Sprite2DMaterialOptions } from "../materials/Sprite2DMaterial.js"; import { SpriteBatch } from "../pipeline/SpriteBatch.js"; import { SystemSchedule } from "./SystemSchedule.js"; import { BatchRun } from "./traits.js"; import { Sprite2D } from "../sprites/Sprite2D.js"; import { Group, Object3D, Texture } from "three"; import { Entity, Trait, World } from "koota"; //#region src/ecs/batchUtils.d.ts /** Shape of the BatchRegistry trait data, used for parameter typing. */ interface RegistryData { runs: Map; sortedRunKeys: string[]; batchPool: Entity[]; activeBatches: Entity[]; renderOrderDirty: boolean; maxBatchSize: number; /** Tiered batch sizes for the auto-orchestrate path; null = fixed maxBatchSize. */ tierLadder: readonly number[] | null; materialRefs: Map; /** Per-texture default materials, scoped to this world. */ defaultMaterials: WeakMap; /** * World-scoped effect-variant materials: texture → variant key → * material. The variant key is the non-texture fragment of * `Sprite2DMaterial`'s shared-cache key (transparent/lit/colorTransform/ * alphaTest/premultipliedAlpha/effectsKey) — see * `sprite2DMaterialVariantKey`. Counterpart to `defaultMaterials` for * sprites carrying constants-effects (provider effects like * NormalMapProvider): two worlds resolving the same texture+effectsKey * combination get distinct instances instead of sharing one. */ effectVariants: WeakMap>; batchSlots: (SpriteBatch | null)[]; batchSlotFreeList: number[]; /** Flat array of Sprite2D refs indexed by entity SoA index (eid). * Pure array indexing — same O(1) pattern as other SoA stores. */ spriteArr: (Sprite2D | null)[]; /** Cached effect traits across all materials. */ effectTraits: Map; /** Entities whose destruction is deferred to the top of the next frame. */ pendingDestroy: Entity[]; /** The SpriteGroup (parent Group) for scene graph sync. */ parentGroup: Group | null; /** Bound Group.prototype.add bypassing SpriteGroup override. */ parentAdd: ((...objects: Object3D[]) => Group) | null; /** Bound Group.prototype.remove bypassing SpriteGroup override. */ parentRemove: ((...objects: Object3D[]) => Group) | null; /** Whether auto-invalidate transforms is enabled. */ autoInvalidateTransforms: boolean; /** Explicit invalidation latch for transforms and hierarchy visibility. */ transformsDirty: boolean; /** The SystemSchedule for this world. */ schedule: SystemSchedule | null; /** Monotonic counter of completed `schedule.run` invocations — see trait doc. */ scheduleRuns: number; /** Whether any occluder changed since the last shadow generation. */ occludersDirty: boolean; } /** * A batch run key: fixed-width hex `sortLayer(8) | materialId(8) | mask(8)`. * * Lexicographic string order equals sortLayer-major numeric order, so the * sorted run-key array doubles as the render-order source without any * numeric packing. A string key sidesteps Float64 precision: the three * components total 96 bits, far past the 53-bit integer-safe range. */ type RunKey = string; /** * Compute a run key from sortLayer, materialId, and camera layers mask. * Runs are the primary batch grouping dimension: sprites in the same run * share (materialId, sortLayer, layers.mask) and can be in the same batch. * Each component is a real GPU constraint — shader pipeline, render-list * position, camera visibility. * * Every component gets a full 32 bits — no truncation collisions for * monotonic material ids, and negative sortLayers keep their ordering * via an offset encoding (int32 + 2^31, so -1 sorts below 0). */ declare function computeRunKey(sortLayer: number, materialId: number, layersMask: number): RunKey; /** * Binary search for insertion point in a sorted array. * Returns the index where `key` should be inserted to maintain sort order. */ declare function binarySearch(arr: T[], key: T): number; /** * Insert a value into a sorted array at the correct position. * No-op if the value already exists. */ declare function sortedInsert(arr: T[], key: T): void; /** * Remove a value from a sorted array. * No-op if the value doesn't exist. */ declare function sortedRemove(arr: T[], key: T): void; /** * Allocate a batchIdx in the registry's batchSlots array. * Reuses freed indices when available. */ declare function allocateBatchIdx(registry: RegistryData, mesh: SpriteBatch): number; /** * Free a batchIdx, returning it to the free list. */ declare function freeBatchIdx(registry: RegistryData, idx: number): void; /** * Get or create a batch run for a given (sortLayer, materialId, layersMask) combo. */ declare function getOrCreateRun(registry: RegistryData, sortLayer: number, materialId: number, layersMask: number, material: Sprite2DMaterial): { run: BatchRun; created: boolean; }; /** * Auto-batch tier ladder. Each SpriteBatch is born at a fixed tier and * stays that size for life; when it fills, the next batch in the run is * created one tier up (or, for a bulk prime — see `resolveBatchSize` — * straight at the tier sized for the incoming load). A small scene pays * for at most one ~180 KB batch (1024 slots × ~176 B/slot); a large * scene's runs converge on 16384-slot batches, the same steady state a * fixed-size SpriteGroup would reach. */ declare const BATCH_TIER_LADDER: readonly number[]; /** * Resolve the slot count for the next batch in a run. * * `registry.tierLadder` non-null → tiered sizing. By default the tier is * chosen by how many batches the run already has (clamped to the top * tier, so growth only ratchets up). When the caller passes `pendingCount` * — the number of sprites it's about to place in this run in one shot — * the tier is instead sized to the smallest tier that can hold that many, * clamped to the top, but never smaller than the batches-length tier * (growth still ratchets). Null ladder → the registry's fixed * `maxBatchSize` (explicit SpriteGroup opt-in). */ declare function resolveBatchSize(registry: RegistryData, run: BatchRun, pendingCount?: number): number; /** * Find a batch in a run that has free slots, or create a new one. * Tries the batch pool first for reuse. * * `pendingCount`, when passed, is the number of sprites the caller is * about to place in this run during the current pass — see * `resolveBatchSize`. */ declare function findOrCreateBatch(world: World, registry: RegistryData, run: BatchRun, pendingCount?: number): Entity; /** * Recycle a batch entity to the pool if it's empty. * Removes it from its run and from activeBatches. */ declare function recycleBatchIfEmpty(registry: RegistryData, batchEntity: Entity, run: BatchRun): void; /** * Rebuild the sorted order of active batches based on run key ordering. * Assigns renderOrder to each batch entity. */ declare function rebuildBatchOrder(registry: RegistryData): void; /** * Detach every material dispose hook a world installed (world/group * disposal path). */ declare function removeMaterialDisposeHooks(world: World): void; /** * Get (or create) the world-scoped default material for a texture. * * Replaces the static shared-material cache: two worlds (two Flatlands, * two SpriteGroups, two auto-registries) resolving the same texture get * two material instances, so effect registration and dispose stay * isolated. Three's pipeline cache dedupes the compiled shader by * source, so the only cost is a JS instance. */ declare function getWorldDefaultMaterial(world: World, registry: RegistryData, texture: Texture): Sprite2DMaterial; /** * Get (or create) the world-scoped effect-variant material for a * texture + configuration. Counterpart to `getWorldDefaultMaterial` for * sprites carrying constants-effects (provider effects like * `NormalMapProvider`): two worlds resolving the same * (texture, effectsKey, …) combination get distinct material instances, * so effect registration and dispose stay isolated the same way * defaults do. */ declare function getWorldEffectVariant(world: World, registry: RegistryData, texture: Texture, options: Sprite2DMaterialOptions): Sprite2DMaterial; /** * Attach the dispose teardown hook for a material used by this world's * batches (idempotent per world). Fires `handleMaterialDispose` so * batches referencing freed GPU resources are torn down and * default-material sprites resurrect. */ declare function ensureMaterialDisposeHook(world: World, registry: RegistryData, material: Sprite2DMaterial): void; /** * Evict every batched entity using `materialId` from its batch. * * Shared by the tier-upgrade rebuild (material schema changed) and the * dispose teardown (material's GPU resources are gone). */ declare function evictBatchesForMaterial(world: World, registry: RegistryData, materialId: number): void; /** * Dispose teardown: batches using the material are torn down; sprites * holding a world-supplied default resurrect with a fresh default * (auto-rebatching on the next system pass); sprites with user-supplied * custom materials fall back to three's standard "disposed material in * use" semantics — restored to visible, unenrolled, and warned about. */ declare function handleMaterialDispose(world: World, registry: RegistryData, material: Sprite2DMaterial): void; /** * Tag a batch entity with its classification traits, replacing any * stale tags from a previous pool tenancy. Systems still branch on the * material directly — see the trait docs for the query-vs-branch rule. */ declare function classifyBatch(batchEntity: Entity, material: Sprite2DMaterial): void; //#endregion export { BATCH_TIER_LADDER, RegistryData, RunKey, allocateBatchIdx, binarySearch, classifyBatch, computeRunKey, ensureMaterialDisposeHook, evictBatchesForMaterial, findOrCreateBatch, freeBatchIdx, getOrCreateRun, getWorldDefaultMaterial, getWorldEffectVariant, handleMaterialDispose, rebuildBatchOrder, recycleBatchIfEmpty, removeMaterialDisposeHooks, resolveBatchSize, sortedInsert, sortedRemove }; //# sourceMappingURL=batchUtils.d.ts.map