Skip to main content

dsfb_computer_graphics/
scaling.rs

1use serde::Serialize;
2
3use crate::config::{DemoConfig, SceneConfig};
4use crate::cost::{build_cost_report, CostMode};
5use crate::dsfb::run_profiled_taa;
6use crate::error::{Error, Result};
7use crate::host::{default_host_realistic_profile, motion_augmented_profile};
8use crate::metrics::{analyze_demo_a_suite, RunAnalysisInput, ScenarioReport};
9use crate::scene::{
10    generate_sequence_for_definition, scenario_by_id, ScenarioExpectation, ScenarioId,
11    ScenarioSupportCategory, SceneSequence,
12};
13use crate::taa::{run_fixed_alpha_baseline, run_strong_heuristic_baseline};
14
15const RESOLUTION_TIERS: &[(&str, usize, usize, bool)] = &[
16    ("default_full_suite", 160, 96, false),
17    ("intermediate_selected_suite", 640, 360, false),
18    ("high_resolution_proxy_selected_suite", 960, 540, true),
19];
20
21const SELECTED_SCENARIOS: &[ScenarioId] = &[
22    ScenarioId::ThinReveal,
23    ScenarioId::RevealBand,
24    ScenarioId::MotionBiasBand,
25    ScenarioId::ContrastPulse,
26];
27
28#[derive(Clone, Debug, Serialize)]
29pub struct ResolutionScenarioMetrics {
30    pub tier_id: String,
31    pub width: usize,
32    pub height: usize,
33    pub selected_high_resolution_mode: bool,
34    pub scenario_id: String,
35    pub scenario_title: String,
36    pub expectation: ScenarioExpectation,
37    pub support_category: ScenarioSupportCategory,
38    pub target_pixels: usize,
39    pub target_area_fraction: f32,
40    pub fixed_alpha_cumulative_roi_mae: f32,
41    pub strong_heuristic_cumulative_roi_mae: f32,
42    pub host_realistic_cumulative_roi_mae: f32,
43    pub motion_augmented_cumulative_roi_mae: f32,
44    pub host_realistic_vs_fixed_alpha_gain: f32,
45    pub motion_augmented_vs_host_realistic_gain: f32,
46    pub host_realistic_non_roi_mae: f32,
47    pub buffer_memory_megabytes: f32,
48    pub roi_note: String,
49}
50
51#[derive(Clone, Debug, Serialize)]
52pub struct ResolutionScalingMetrics {
53    pub entries: Vec<ResolutionScenarioMetrics>,
54    pub notes: Vec<String>,
55}
56
57pub fn run_resolution_scaling_study(config: &DemoConfig) -> Result<ResolutionScalingMetrics> {
58    let mut entries = Vec::new();
59    let host_cost = build_cost_report(CostMode::HostRealistic);
60    let bytes_per_pixel = host_cost
61        .buffers
62        .iter()
63        .map(|buffer| buffer.bytes_per_pixel)
64        .sum::<usize>();
65
66    for (tier_id, width, height, selected_high_resolution_mode) in RESOLUTION_TIERS {
67        let scaled_scene = scaled_scene_config(&config.scene, *width, *height);
68        let tier_config = DemoConfig {
69            scene: scaled_scene,
70            ..config.clone()
71        };
72        for scenario_id in SELECTED_SCENARIOS {
73            if *width > config.scene.width && matches!(scenario_id, ScenarioId::ThinReveal) {
74                continue;
75            }
76            let definition = scenario_by_id(&tier_config.scene, *scenario_id).ok_or_else(|| {
77                Error::Message(format!(
78                    "resolution scaling scenario {} was unavailable",
79                    scenario_id.as_str()
80                ))
81            })?;
82            let sequence = generate_sequence_for_definition(&definition);
83            let scenario_report = run_resolution_scenario(&tier_config, &sequence)?;
84            let fixed = find_run(&scenario_report, "fixed_alpha")?;
85            let strong = find_run(&scenario_report, "strong_heuristic")?;
86            let host = find_run(&scenario_report, "dsfb_host_realistic")?;
87            let motion = find_run(&scenario_report, "dsfb_motion_augmented")?;
88            let total_pixels = width * height;
89            entries.push(ResolutionScenarioMetrics {
90                tier_id: (*tier_id).to_string(),
91                width: *width,
92                height: *height,
93                selected_high_resolution_mode: *selected_high_resolution_mode,
94                scenario_id: scenario_report.scenario_id.clone(),
95                scenario_title: scenario_report.scenario_title.clone(),
96                expectation: scenario_report.expectation,
97                support_category: scenario_report.support_category,
98                target_pixels: scenario_report.target_pixels,
99                target_area_fraction: scenario_report.target_area_fraction,
100                fixed_alpha_cumulative_roi_mae: fixed.summary.cumulative_roi_mae,
101                strong_heuristic_cumulative_roi_mae: strong.summary.cumulative_roi_mae,
102                host_realistic_cumulative_roi_mae: host.summary.cumulative_roi_mae,
103                motion_augmented_cumulative_roi_mae: motion.summary.cumulative_roi_mae,
104                host_realistic_vs_fixed_alpha_gain: scenario_report
105                    .host_realistic_vs_fixed_alpha_cumulative_roi_gain,
106                motion_augmented_vs_host_realistic_gain: host.summary.cumulative_roi_mae
107                    - motion.summary.cumulative_roi_mae,
108                host_realistic_non_roi_mae: host.summary.average_non_roi_mae,
109                buffer_memory_megabytes: bytes_per_pixel as f32 * total_pixels as f32
110                    / (1024.0 * 1024.0),
111                roi_note: scenario_report.roi_note.clone(),
112            });
113        }
114    }
115
116    Ok(ResolutionScalingMetrics {
117        entries,
118        notes: vec![
119            "The high-resolution tier is a selected-scenario scalable proxy rather than a full 1080p sweep. It is intended to demonstrate structural persistence beyond the toy default resolution without pretending to be a shipping-engine benchmark.".to_string(),
120            "The canonical thin_reveal point-ROI case is intentionally kept at the default resolution only. At higher resolutions its exact one-pixel disocclusion geometry becomes path-dependent and is not a stable scaling metric.".to_string(),
121            "Memory footprint numbers are analytical host-realistic buffer estimates from the crate cost model.".to_string(),
122        ],
123    })
124}
125
126pub fn scaled_scene_config(base: &SceneConfig, width: usize, height: usize) -> SceneConfig {
127    let scale_x = width as f32 / base.width.max(1) as f32;
128    let scale_y = height as f32 / base.height.max(1) as f32;
129    let scale_len = scale_x.min(scale_y);
130    let clamp_x = |value: i32| ((value as f32 * scale_x).round() as i32).clamp(0, width as i32);
131    let clamp_y = |value: i32| ((value as f32 * scale_y).round() as i32).clamp(0, height as i32);
132
133    SceneConfig {
134        width,
135        height,
136        frame_count: base.frame_count,
137        object_width: ((base.object_width as f32 * scale_len).round() as usize).max(8),
138        object_height: ((base.object_height as f32 * scale_len).round() as usize).max(8),
139        object_start_x: clamp_x(base.object_start_x),
140        object_stop_x: clamp_x(base.object_stop_x),
141        object_top_y: clamp_y(base.object_top_y),
142        move_frames: base.move_frames,
143        thin_vertical_x: clamp_x(base.thin_vertical_x),
144    }
145}
146
147fn run_resolution_scenario(
148    config: &DemoConfig,
149    sequence: &SceneSequence,
150) -> Result<ScenarioReport> {
151    let fixed = run_fixed_alpha_baseline(sequence, config.baseline.fixed_alpha);
152    let strong = run_strong_heuristic_baseline(sequence, &config.baseline);
153    let host = run_profiled_taa(
154        sequence,
155        &default_host_realistic_profile(config.dsfb_alpha_range.min, config.dsfb_alpha_range.max),
156    );
157    let motion = run_profiled_taa(
158        sequence,
159        &motion_augmented_profile(config.dsfb_alpha_range.min, config.dsfb_alpha_range.max),
160    );
161    let host_alpha = host
162        .supervision_frames
163        .iter()
164        .map(|frame| frame.alpha.clone())
165        .collect::<Vec<_>>();
166    let host_response = host
167        .supervision_frames
168        .iter()
169        .map(|frame| frame.intervention.clone())
170        .collect::<Vec<_>>();
171    let host_trust = host
172        .supervision_frames
173        .iter()
174        .map(|frame| frame.trust.clone())
175        .collect::<Vec<_>>();
176    let motion_alpha = motion
177        .supervision_frames
178        .iter()
179        .map(|frame| frame.alpha.clone())
180        .collect::<Vec<_>>();
181    let motion_response = motion
182        .supervision_frames
183        .iter()
184        .map(|frame| frame.intervention.clone())
185        .collect::<Vec<_>>();
186    let motion_trust = motion
187        .supervision_frames
188        .iter()
189        .map(|frame| frame.trust.clone())
190        .collect::<Vec<_>>();
191
192    let analysis = analyze_demo_a_suite(&[(
193        sequence.clone(),
194        vec![
195            RunAnalysisInput {
196                id: &fixed.id,
197                label: &fixed.label,
198                category: "baseline",
199                resolved_frames: &fixed.taa.resolved_frames,
200                reprojected_history_frames: &fixed.taa.reprojected_history_frames,
201                alpha_frames: &fixed.alpha_frames,
202                response_frames: &fixed.response_frames,
203                trust_frames: None,
204            },
205            RunAnalysisInput {
206                id: &strong.id,
207                label: &strong.label,
208                category: "baseline",
209                resolved_frames: &strong.taa.resolved_frames,
210                reprojected_history_frames: &strong.taa.reprojected_history_frames,
211                alpha_frames: &strong.alpha_frames,
212                response_frames: &strong.response_frames,
213                trust_frames: None,
214            },
215            RunAnalysisInput {
216                id: &host.profile.id,
217                label: &host.profile.label,
218                category: "dsfb",
219                resolved_frames: &host.resolved_frames,
220                reprojected_history_frames: &host.reprojected_history_frames,
221                alpha_frames: &host_alpha,
222                response_frames: &host_response,
223                trust_frames: Some(&host_trust),
224            },
225            RunAnalysisInput {
226                id: &motion.profile.id,
227                label: &motion.profile.label,
228                category: "dsfb",
229                resolved_frames: &motion.resolved_frames,
230                reprojected_history_frames: &motion.reprojected_history_frames,
231                alpha_frames: &motion_alpha,
232                response_frames: &motion_response,
233                trust_frames: Some(&motion_trust),
234            },
235        ],
236    )])?;
237    analysis.scenarios.into_iter().next().ok_or_else(|| {
238        Error::Message("resolution scaling analysis produced no scenario".to_string())
239    })
240}
241
242fn find_run<'a>(
243    scenario: &'a ScenarioReport,
244    run_id: &str,
245) -> Result<&'a crate::metrics::ScenarioRunReport> {
246    scenario
247        .runs
248        .iter()
249        .find(|run| run.summary.run_id == run_id)
250        .ok_or_else(|| Error::Message(format!("resolution scaling run {run_id} missing")))
251}