import type { StreamingSettings } from "./settings.js"; import type { VisibilityStats } from "./diagnostics.js"; export type AssetLoadState = "queued" | "loading" | "loaded" | "unloaded" | "error"; export interface AssetLoadResult { /** Approximate resident size, used for the memory budget and small-asset resident policy. */ readonly bytes?: number; /** Opaque handle to the loaded resource (texture, mesh, buffer). */ readonly value?: unknown; } export interface CancelSignal { readonly cancelled: boolean; } export interface AssetStreamingOptions { /** Async loader. Honor `signal.cancelled` and always settle; cancelled successful loads are released through `unload`. */ readonly load: (assetId: string, signal: CancelSignal) => Promise; /** Release GPU/CPU resources for an asset. */ readonly unload?: (assetId: string) => void; readonly settings?: Partial; readonly now?: () => number; } export interface AssetRecord { readonly id: string; state: AssetLoadState; bytes: number; priority: number; refCount: number; pinned: boolean; lastActiveMs: number; value: unknown; } /** Current demand/residency gauges and cumulative unload/cancellation counters; clear resets totals. */ export interface StreamingStats { queued: number; loading: number; loaded: number; unloaded: number; errored: number; inFlight: number; cancelled: number; /** Known resident bytes; missing or invalid loader sizes count as zero. */ bytes: number; /** Current tracked records, including inactive history until forget or clear. */ records: number; /** Known bytes protected by pins or active references. */ protectedBytes: number; /** Loaded assets whose resident size is unknown. */ unknownSizeLoaded: number; /** Known resident bytes above the configured target, including protected bytes. */ overBudgetBytes: number; /** Cumulative resources evicted by resident-byte pressure since clear. */ budgetEvicted: number; } export interface AssetStreamingSystem { /** Demand an asset (near/in view). Deduplicated — an already loaded/loading/queued asset is never re-fetched concurrently. */ request(assetId: string, priority?: number): void; /** Shared ownership by an active object. A retained asset (refCount > 0) is never auto-unloaded. */ retain(assetId: string): void; release(assetId: string): void; /** Pin resident regardless of distance/refcount/scene. */ pin(assetId: string): void; unpin(assetId: string): void; /** Refresh the "last needed" timestamp used by the grace-period unload. */ markActive(assetId: string): void; /** Advance one frame: bounded load starts and unloads; resident-byte pressure precedes idle unloading. */ tick(dt: number): void; /** Cancel demand. Unresolved loads still occupy concurrency slots and block same-id retries until settlement. */ cancel(assetId: string): void; /** Merge policy. Zero concurrency pauses starts; resident-byte target/order changes apply on the next tick. */ retune(settings: Partial): void; stateOf(assetId: string): AssetLoadState | undefined; isLoaded(assetId: string): boolean; record(assetId: string): AssetRecord | undefined; /** Drop inactive unloaded/error history. Returns false for missing, pinned, retained, queued, loaded or unresolved assets. Old record handles remain detached; future demand creates a new record. */ forget(assetId: string): boolean; stats(): StreamingStats; /** Merge asset counters into a per-frame VisibilityStats. */ applyTo(stats: VisibilityStats): void; /** Resolve once every in-flight load settles — for deterministic tests. */ settle(): Promise; /** Release loaded resources and cancel demand; outstanding loads remain tracked until settlement. */ clear(): void; } /** Stream shared resources with bounded load concurrency, per-tick resident-byte eviction, and explicit inactive-history cleanup. */ export declare function createAssetStreamingSystem(options: AssetStreamingOptions): AssetStreamingSystem;