par_term_render/cell_renderer/
surface.rs1use super::CellRenderer;
2use crate::wgpu_conversions::VsyncModeWgpu;
3use std::sync::atomic::{AtomicU64, Ordering};
4
5pub fn clamp_surface_extent(width: u32, height: u32, max_dimension: u32) -> (u32, u32) {
15 (
16 width.min(max_dimension).max(1),
17 height.min(max_dimension).max(1),
18 )
19}
20
21pub fn texture_limits(max_texture_dimension_2d: u32) -> wgpu::Limits {
24 wgpu::Limits {
25 max_texture_dimension_2d,
26 ..wgpu::Limits::default()
27 }
28}
29
30pub fn install_nonfatal_error_handler(device: &wgpu::Device) {
38 let seen = AtomicU64::new(0);
39 device.on_uncaptured_error(std::sync::Arc::new(move |err: wgpu::Error| {
40 let count = seen.fetch_add(1, Ordering::Relaxed) + 1;
41 if should_log_uncaptured_error(count) {
42 log::error!("wgpu uncaptured error #{count}: {err}");
43 }
44 }));
45}
46
47fn should_log_uncaptured_error(count: u64) -> bool {
50 count == 1 || count.is_multiple_of(1_000)
51}
52
53impl CellRenderer {
54 pub fn reconfigure_surface(&mut self) {
55 self.surface.configure(&self.device, &self.config);
56 }
57
58 pub fn supported_present_modes(&self) -> &[wgpu::PresentMode] {
60 &self.supported_present_modes
61 }
62
63 pub fn is_vsync_mode_supported(&self, mode: par_term_config::VsyncMode) -> bool {
65 self.supported_present_modes
66 .contains(&mode.to_present_mode())
67 }
68
69 pub fn update_vsync_mode(
72 &mut self,
73 mode: par_term_config::VsyncMode,
74 ) -> (par_term_config::VsyncMode, bool) {
75 let requested = mode.to_present_mode();
76 let current = self.config.present_mode;
77
78 let actual = if self.supported_present_modes.contains(&requested) {
80 requested
81 } else {
82 log::warn!(
83 "Requested present mode {:?} not supported, falling back to Fifo",
84 requested
85 );
86 wgpu::PresentMode::Fifo
87 };
88
89 if actual != current {
91 self.config.present_mode = actual;
92 self.surface.configure(&self.device, &self.config);
93 log::info!("VSync mode changed to {:?}", actual);
94 }
95
96 let actual_vsync = match actual {
98 wgpu::PresentMode::Immediate => par_term_config::VsyncMode::Immediate,
99 wgpu::PresentMode::Mailbox => par_term_config::VsyncMode::Mailbox,
100 wgpu::PresentMode::Fifo | wgpu::PresentMode::FifoRelaxed => {
101 par_term_config::VsyncMode::Fifo
102 }
103 _ => par_term_config::VsyncMode::Fifo,
104 };
105
106 (actual_vsync, actual != current)
107 }
108
109 pub fn current_vsync_mode(&self) -> par_term_config::VsyncMode {
111 match self.config.present_mode {
112 wgpu::PresentMode::Immediate => par_term_config::VsyncMode::Immediate,
113 wgpu::PresentMode::Mailbox => par_term_config::VsyncMode::Mailbox,
114 wgpu::PresentMode::Fifo | wgpu::PresentMode::FifoRelaxed => {
115 par_term_config::VsyncMode::Fifo
116 }
117 _ => par_term_config::VsyncMode::Fifo,
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
129 fn oversize_extent_is_clamped_to_the_device_limit() {
130 assert_eq!(clamp_surface_extent(10240, 2822, 8192), (8192, 2822));
131 }
132
133 #[test]
134 fn extent_within_the_limit_is_unchanged() {
135 assert_eq!(clamp_surface_extent(3840, 2160, 8192), (3840, 2160));
136 }
137
138 #[test]
139 fn extent_at_the_limit_is_unchanged() {
140 assert_eq!(clamp_surface_extent(8192, 8192, 8192), (8192, 8192));
141 }
142
143 #[test]
146 fn zero_extent_floors_to_one() {
147 assert_eq!(clamp_surface_extent(0, 0, 8192), (1, 1));
148 }
149
150 #[test]
151 fn texture_limits_raises_only_the_2d_dimension() {
152 assert_eq!(
153 texture_limits(16384),
154 wgpu::Limits {
155 max_texture_dimension_2d: 16384,
156 ..wgpu::Limits::default()
157 }
158 );
159 }
160
161 #[test]
162 fn uncaptured_errors_log_the_first_then_every_1000th() {
163 assert!(should_log_uncaptured_error(1));
164 assert!(!should_log_uncaptured_error(2));
165 assert!(!should_log_uncaptured_error(999));
166 assert!(should_log_uncaptured_error(1000));
167 assert!(!should_log_uncaptured_error(1001));
168 }
169}