/** * Native WebGPU temporal convergence primitives. * * This module deliberately separates three concerns: * - deterministic camera jitter and history admission; * - explicit invalidation for camera motion and LOD changes; * - resident-motion admission only when matching motion vectors are available; * - motion-reprojected, depth-rejected, neighborhood-clamped WebGPU resolve. */ import type { PixelGrid } from '../../native-render/gpu-verify'; import type { Mat4 } from '../../character-render/skin-math'; import type { DepthGrid, MotionVectorGrid, ReactiveMaskGrid } from './TemporalInputs'; export type TemporalInvalidationReason = 'initial' | 'camera-motion' | 'resident-motion' | 'lod-change' | 'manual'; export interface TemporalConvergenceConfig { /** Number of jittered stable frames required for an admitted convergence window. */ sampleCount: number; /** Maximum history contribution after the convergence window fills. */ feedbackCeiling: number; /** Halton jitter amplitude in pixels. */ jitterScalePixels: number; } export declare const TEMPORAL_CONVERGENCE_PROFILES: { readonly 'browser-balanced': { readonly sampleCount: 8; readonly feedbackCeiling: 0.875; readonly jitterScalePixels: 0.5; }; readonly 'quest-90hz-budget': { readonly sampleCount: 4; readonly feedbackCeiling: 0.75; readonly jitterScalePixels: 0.42; }; }; export type TemporalConvergenceProfile = keyof typeof TEMPORAL_CONVERGENCE_PROFILES; export interface TemporalFrameSignals { /** Stable caller-owned identity for the camera transform used by this frame. */ cameraStateId: string; /** Stable caller-owned identity for pose / resident transform state. */ residentStateId: string; lodLevel: number; /** True only when this frame will provide matching current-to-previous velocity. */ motionVectorsAvailable?: boolean; forceReset?: boolean; } export interface TemporalFramePlan { schemaVersion: 'holoscript.temporal-frame-plan.v1'; frameIndex: number; historyGeneration: number; invalidated: boolean; invalidationReason: TemporalInvalidationReason | null; historyValid: boolean; sampleIndex: number; stableFrameCount: number; converged: boolean; feedback: number; jitterPixels: [number, number]; } export interface TemporalConvergenceReceipt { schemaVersion: 'holoscript.temporal-convergence.v2'; config: TemporalConvergenceConfig; frameCount: number; historyGeneration: number; invalidationCounts: Record; stableFrameCount: number; converged: boolean; motionVectorResidentFramesAdmitted: number; reactiveMaskConsumed: false; requiredHistoryPolicy: 'reproject-resident-motion-invalidate-camera-or-lod-v2'; } /** Deterministic centred base-2/base-3 Halton jitter. */ export declare function temporalHaltonJitter(sampleIndex: number, scalePixels?: number): [number, number]; /** * Return a jittered copy of a column-major projection/view-projection matrix. * The input matrix is never mutated. */ export declare function jitterProjectionMatrix(matrix: Mat4, jitterPixels: readonly [number, number], viewportWidth: number, viewportHeight: number): Mat4; /** * Deterministic history controller. The caller supplies stable state ids rather * than relying on wall-clock thresholds or approximate matrix comparisons. */ export declare class TemporalConvergenceController { private readonly config; private frameCount; private historyGeneration; private stableFrameCount; private motionVectorResidentFramesAdmitted; private previous; private readonly invalidationCounts; constructor(config: TemporalConvergenceConfig); static fromProfile(profile: TemporalConvergenceProfile): TemporalConvergenceController; beginFrame(signals: TemporalFrameSignals): TemporalFramePlan; getReceipt(): TemporalConvergenceReceipt; } export interface TemporalResolveOptions { feedback: number; historyValid: boolean; /** Current-minus-previous motion in pixel units. */ motionVectors?: MotionVectorGrid; currentDepth?: DepthGrid; historyDepth?: DepthGrid; reactiveMask?: ReactiveMaskGrid; /** Absolute NDC-depth delta above which history is rejected. Default 0.01. */ disocclusionDepthThreshold?: number; } export interface TemporalResolveReceipt { schemaVersion: 'holoscript.webgpu-temporal-resolve.v2'; backend: 'webgpu'; deviceExecutionMeasured: true; width: number; height: number; feedback: number; historyValid: boolean; neighborhoodClamping: true; motionVectorsConsumed: boolean; motionVectorSpace: 'current-minus-previous-pixels' | 'none'; reactiveMaskConsumed: boolean; disocclusionInputConsumed: boolean; disocclusionDepthThreshold: number; outOfBoundsHistoryPixelCount: number; disocclusionRejectedPixelCount: number; fullyReactivePixelCount: number; gpuTimestampMeasured: false; timingClassification: 'not-measured'; workgroupSize: [8, 8, 1]; dispatch: [number, number, 1]; } export interface TemporalResolveResult { pixels: PixelGrid; receipt: TemporalResolveReceipt; } /** * GPU-resident inputs for a temporal resolve pass. Every texture stays owned * by the caller; the encoder never maps or reads back an intermediate frame. */ export interface TemporalTextureResolveInputs { currentColor: GPUTexture; historyColor: GPUTexture; motionVectors: GPUTexture; currentDepth: GPUTexture; historyDepth: GPUTexture; reactiveMask: GPUTexture; } export interface TemporalTextureResolveOptions { width: number; height: number; feedback: number; historyValid: boolean; motionVectorsAvailable: boolean; depthHistoryAvailable: boolean; reactiveMaskAvailable: boolean; /** Absolute NDC-depth delta above which history is rejected. Default 0.01. */ disocclusionDepthThreshold?: number; /** Optional pass timestamps supplied by a caller-owned query set. */ timestampWrites?: GPUComputePassTimestampWrites; /** Persistent pipeline supplied by a frame graph to avoid per-frame compilation. */ pipeline?: GPUComputePipeline; /** Optional persistent target. When omitted the caller must destroy the returned target. */ outputTexture?: GPUTexture; } export interface TemporalTextureResolveReceipt { schemaVersion: 'holoscript.webgpu-temporal-texture-resolve.v1'; backend: 'webgpu'; width: number; height: number; feedback: number; historyValid: boolean; neighborhoodClamping: true; motionVectorsConsumed: boolean; reactiveMaskConsumed: boolean; disocclusionInputConsumed: boolean; disocclusionDepthThreshold: number; zeroCopyTextureInputs: true; intermediateCpuReadbackCount: 0; gpuTimestampWritesEncoded: boolean; persistentPipelineConsumed: boolean; timingClassification: 'caller-query-set' | 'not-requested'; workgroupSize: [8, 8, 1]; dispatch: [number, number, 1]; } export interface EncodedTemporalTextureResolve { outputTexture: GPUTexture; outputTextureOwnedByCaller: boolean; receipt: TemporalTextureResolveReceipt; /** Release the uniform buffer and an internally-created output texture. */ destroy(): void; } /** Create the reusable compute pipeline used by texture-native temporal graphs. */ export declare function createTemporalTextureResolvePipelineGPU(device: GPUDevice): GPUComputePipeline; /** * Encode a texture-native temporal resolve into a caller-owned command encoder. * * This is the zero-copy integration seam: current color, history, velocity, * depth, mask, and output remain GPU textures for the entire pass. Timestamp * writes are accepted but resolved by the caller so timing and readback policy * remain explicit at the frame-graph boundary. */ export declare function encodeTemporalTextureResolveGPU(device: GPUDevice, encoder: GPUCommandEncoder, inputs: TemporalTextureResolveInputs, options: TemporalTextureResolveOptions): EncodedTemporalTextureResolve; /** * Execute one neighborhood-clamped temporal resolve on a live GPUDevice. * * Pixel grids are caller-owned so the primitive works in Dawn, browsers, and * capture tooling without coupling to a presentation engine. The readback is * evidence of execution, not a GPU timing result. */ export declare function resolveTemporalFrameGPU(device: GPUDevice, current: PixelGrid, history: PixelGrid | null, options: TemporalResolveOptions): Promise; //# sourceMappingURL=TemporalConvergence.d.ts.map