#pragma once #include #include #include #include namespace faset::render { enum class TemporalMode { Off, TAA, Upscale }; struct TemporalCapabilities { bool compute{}; bool formats{}; bool extent{}; }; enum class TemporalFallbackReason { None, ComputeUnavailable, FormatUnavailable, ExtentUnsupported }; TemporalFallbackReason temporal_fallback_reason(TemporalMode requested, TemporalCapabilities available) noexcept; TemporalMode select_effective_temporal_mode(TemporalMode requested, TemporalCapabilities available) noexcept; // Validate the requested mode and scale before allocating scene targets. // Off/TAA are 1:1; Upscale accepts [0.5, 1). Ceil rounding is deterministic // for odd output extents and never produces a zero-sized internal target. std::array temporal_internal_extent(std::uint32_t output_width, std::uint32_t output_height, TemporalMode mode, float render_scale); enum class TemporalResetReason { None, FirstFrame, CameraCut, CameraDiscontinuity, ViewChanged, ViewportChanged, ProjectionChanged, Resize, ModeChanged, ScaleChanged, ShaderReload, Unsupported }; // Snapshot matrices/rect stay unjittered. This is independent of HZB history: // Direct rendering and GPU frustum mode can still accumulate temporal color. // A missing previous key means no completed frame is available to reuse. struct TemporalHistoryKey { std::string view_id; std::uint32_t output_width{}, output_height{}; std::uint32_t internal_width{}, internal_height{}; std::array scene_rect{}; std::array projection{}, view_projection{}; std::array camera_eye{}; TemporalMode mode{TemporalMode::Off}; float render_scale{1}; std::uint64_t shader_generation{}; bool camera_cut{}; }; struct TemporalHistoryDecision { bool valid{}; TemporalResetReason reason{TemporalResetReason::FirstFrame}; }; TemporalHistoryDecision evaluate_temporal_history(const std::optional& previous, const TemporalHistoryKey& current) noexcept; // Sixteen-phase Halton(2,3) offset in clip-space units for scene rasterization. // UI, picking and history keys use unjittered space. Culling must account for // this jitter with a conservative edge; HZB depth must match jittered geometry. // A zero viewport extent throws std::invalid_argument. std::array temporal_jitter(std::uint64_t frame_index, std::uint32_t viewport_width, std::uint32_t viewport_height); // Scene-local normalized UV motion, current minus previous. Both clips use // their own jittered scene VP and the same local vertex. Invalid/behind-eye // clips have no usable history; the shader writes velocity validity zero. std::optional> project_motion(const std::array& current_clip, const std::array& previous_clip) noexcept; } // namespace faset::render