1use wgpu::{Instance, Surface};
2
3mod touch;
4pub use touch::*;
5
6#[cfg(any(target_os = "ios", all(feature = "mac_catalyst", target_os = "macos")))]
7#[path = "ios.rs"]
8mod app_surface;
9
10#[cfg(target_os = "android")]
11#[path = "android.rs"]
12mod app_surface;
13
14#[cfg(all(target_arch = "wasm32", feature = "web_rwh"))]
15#[path = "web_rwh/mod.rs"]
16mod app_surface;
17
18#[cfg(all(
19 feature = "winit",
20 any(
21 all(not(feature = "mac_catalyst"), target_os = "macos"),
22 target_os = "windows",
23 target_os = "linux",
24 all(target_arch = "wasm32", not(feature = "web_rwh")),
25 )
26))]
27#[path = "app_surface_use_winit.rs"]
28mod app_surface;
29
30#[cfg(any(
31 all(
32 not(feature = "winit"),
33 any(
34 all(not(feature = "mac_catalyst"), target_os = "macos"),
35 target_os = "windows",
36 target_os = "linux",
37 ),
38 ),
39 all(
40 target_arch = "wasm32",
41 not(feature = "winit"),
42 not(feature = "web_rwh")
43 ),
44))]
45#[path = "unsupported.rs"]
46mod app_surface;
47#[cfg(not(any(
48 all(
49 not(feature = "winit"),
50 any(
51 all(not(feature = "mac_catalyst"), target_os = "macos"),
52 target_os = "windows",
53 target_os = "linux",
54 ),
55 ),
56 all(
57 target_arch = "wasm32",
58 not(feature = "winit"),
59 not(feature = "web_rwh")
60 ),
61)))]
62pub use app_surface::*;
63
64#[repr(C)]
65#[derive(Debug)]
66pub struct ViewSize {
67 pub width: u32,
68 pub height: u32,
69}
70
71pub(crate) fn normalize_view_size(size: (u32, u32)) -> (u32, u32) {
72 (size.0.max(1), size.1.max(1))
73}
74
75pub(crate) fn normalize_scale_factor(scale_factor: f32) -> f32 {
76 if scale_factor.is_finite() && scale_factor > 0.0 {
77 scale_factor
78 } else {
79 1.0
80 }
81}
82
83#[allow(dead_code)]
84pub(crate) fn physical_size_from_logical_size(
85 width: f32,
86 height: f32,
87 scale_factor: f32,
88) -> (u32, u32) {
89 let scale_factor = normalize_scale_factor(scale_factor);
90 let width = if width.is_finite() && width > 0.0 {
91 (width * scale_factor) as u32
92 } else {
93 0
94 };
95 let height = if height.is_finite() && height > 0.0 {
96 (height * scale_factor) as u32
97 } else {
98 0
99 };
100
101 normalize_view_size((width, height))
102}
103
104#[allow(dead_code)]
105pub(crate) fn logical_size_from_physical_size(
106 physical_size: (u32, u32),
107 scale_factor: f32,
108) -> (f32, f32) {
109 let physical_size = normalize_view_size(physical_size);
110 let scale_factor = normalize_scale_factor(scale_factor);
111
112 (
113 physical_size.0 as f32 / scale_factor,
114 physical_size.1 as f32 / scale_factor,
115 )
116}
117
118pub(crate) fn normalize_touch_point(
119 touch_point_x: f32,
120 touch_point_y: f32,
121 physical_size: (u32, u32),
122 scale_factor: f32,
123) -> (f32, f32) {
124 let physical_size = normalize_view_size(physical_size);
125 let scale_factor = normalize_scale_factor(scale_factor);
126
127 (
128 touch_point_x * scale_factor / physical_size.0 as f32,
129 touch_point_y * scale_factor / physical_size.1 as f32,
130 )
131}
132
133pub(crate) fn resize_surface_config(
134 config: &mut wgpu::SurfaceConfiguration,
135 size: (u32, u32),
136) -> bool {
137 let size = normalize_view_size(size);
138 if config.width == size.0 && config.height == size.1 {
139 return false;
140 }
141
142 config.width = size.0;
143 config.height = size.1;
144 true
145}
146
147#[cfg(target_arch = "wasm32")]
148use std::rc::Rc as SharedPtr;
149#[cfg(not(target_arch = "wasm32"))]
150use std::sync::Arc as SharedPtr;
151#[derive(Clone)]
153pub struct IASDQContext {
154 pub instance: wgpu::Instance,
155 pub surface: SharedPtr<wgpu::Surface<'static>>,
156 pub config: wgpu::SurfaceConfiguration,
157 pub adapter: wgpu::Adapter,
158 pub device: wgpu::Device,
159 pub queue: wgpu::Queue,
160}
161
162impl IASDQContext {
163 pub fn update_config_format(&mut self, format: wgpu::TextureFormat) {
164 self.config.format = format;
165 if cfg!(feature = "webgl") {
166 } else if format == format.remove_srgb_suffix() {
168 self.config.view_formats = vec![format.add_srgb_suffix()];
169 } else {
170 self.config.view_formats = vec![format];
171 }
172 self.surface.configure(&self.device, &self.config);
173 }
174}
175
176#[cfg(not(any(
177 all(
178 not(feature = "winit"),
179 any(
180 all(not(feature = "mac_catalyst"), target_os = "macos"),
181 target_os = "windows",
182 target_os = "linux",
183 ),
184 ),
185 all(
186 target_arch = "wasm32",
187 not(feature = "winit"),
188 not(feature = "web_rwh")
189 ),
190)))]
191impl core::ops::Deref for AppSurface {
192 type Target = IASDQContext;
193 fn deref(&self) -> &Self::Target {
194 &self.ctx
195 }
196}
197
198pub trait SurfaceFrame {
199 fn view_size(&self) -> ViewSize;
200 fn resize_surface(&mut self);
202 fn resize_surface_by_size(&mut self, size: (u32, u32));
203 fn pintch(&mut self, _touch: Touch, _scale: f32) {}
204 fn touch(&mut self, _touch: Touch) {}
205 fn normalize_touch_point(&self, _touch_point_x: f32, _touch_point_y: f32) -> (f32, f32) {
206 unimplemented!()
207 }
208 fn enter_frame(&mut self) {}
209 fn get_current_frame_view(
210 &self,
211 _view_format: Option<wgpu::TextureFormat>,
212 ) -> Option<(wgpu::SurfaceTexture, wgpu::TextureView)> {
213 unimplemented!()
214 }
215 fn create_current_frame_view(
216 &self,
217 device: &wgpu::Device,
218 surface: &wgpu::Surface,
219 config: &wgpu::SurfaceConfiguration,
220 view_format: Option<wgpu::TextureFormat>,
221 ) -> Option<(wgpu::SurfaceTexture, wgpu::TextureView)> {
222 let frame = match surface.get_current_texture() {
223 wgpu::CurrentSurfaceTexture::Success(frame)
224 | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
225 wgpu::CurrentSurfaceTexture::Timeout
226 | wgpu::CurrentSurfaceTexture::Outdated
227 | wgpu::CurrentSurfaceTexture::Lost => {
228 surface.configure(device, config);
229 match surface.get_current_texture() {
230 wgpu::CurrentSurfaceTexture::Success(frame)
231 | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
232 _ => panic!("Failed to acquire next swap chain texture!"),
233 }
234 }
235 wgpu::CurrentSurfaceTexture::Occluded => return None,
236 wgpu::CurrentSurfaceTexture::Validation => panic!("Validation error acquiring texture"),
237 };
238 let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
239 label: Some("frame texture view"),
240 format: if view_format.is_none() {
241 Some(config.format.add_srgb_suffix())
243 } else {
244 view_format
245 },
246 ..Default::default()
247 });
248 Some((frame, view))
249 }
250}
251
252#[cfg(not(any(
253 all(
254 not(feature = "winit"),
255 any(
256 all(not(feature = "mac_catalyst"), target_os = "macos"),
257 target_os = "windows",
258 target_os = "linux",
259 ),
260 ),
261 all(
262 target_arch = "wasm32",
263 not(feature = "winit"),
264 not(feature = "web_rwh")
265 ),
266)))]
267impl SurfaceFrame for AppSurface {
268 fn view_size(&self) -> ViewSize {
269 let size = self.get_view_size();
270 ViewSize {
271 width: size.0,
272 height: size.1,
273 }
274 }
275
276 fn resize_surface(&mut self) {
277 let size = self.get_view_size();
278 if resize_surface_config(&mut self.ctx.config, size) {
279 self.surface.configure(&self.device, &self.config);
280 }
281 }
282
283 fn resize_surface_by_size(&mut self, size: (u32, u32)) {
284 if resize_surface_config(&mut self.ctx.config, size) {
285 self.surface.configure(&self.device, &self.config);
286 }
287 }
288
289 fn normalize_touch_point(&self, touch_point_x: f32, touch_point_y: f32) -> (f32, f32) {
290 normalize_touch_point(
291 touch_point_x,
292 touch_point_y,
293 self.get_view_size(),
294 self.scale_factor,
295 )
296 }
297
298 fn get_current_frame_view(
299 &self,
300 view_format: Option<wgpu::TextureFormat>,
301 ) -> Option<(wgpu::SurfaceTexture, wgpu::TextureView)> {
302 self.create_current_frame_view(&self.device, &self.surface, &self.config, view_format)
303 }
304}
305
306async fn create_iasdq_context(
307 instance: Instance,
308 surface: Surface<'static>,
309 physical_size: (u32, u32),
310) -> IASDQContext {
311 let physical_size = normalize_view_size(physical_size);
312 let (adapter, device, queue) = crate::request_device(&instance, &surface).await;
313
314 let caps = surface.get_capabilities(&adapter);
315 let prefered = caps.formats[0];
316
317 let format = if cfg!(all(target_arch = "wasm32", not(feature = "webgl"))) {
318 prefered.remove_srgb_suffix()
321 } else {
322 prefered
323 };
324 let view_formats = if cfg!(feature = "webgl") {
325 vec![]
329 } else if cfg!(target_os = "android") {
330 vec![format]
340 } else if format.is_srgb() {
341 vec![format, format.remove_srgb_suffix()]
342 } else {
343 vec![format.add_srgb_suffix(), format.remove_srgb_suffix()]
344 };
345
346 let mut config = surface
347 .get_default_config(&adapter, physical_size.0, physical_size.1)
348 .expect("Surface isn't supported by the adapter.");
349
350 config.view_formats = view_formats;
351 config.format = format;
352
353 surface.configure(&device, &config);
354
355 IASDQContext {
356 instance,
357 surface: SharedPtr::new(surface),
358 config,
359 adapter,
360 device,
361 queue,
362 }
363}
364
365async fn request_device(
366 instance: &Instance,
367 surface: &Surface<'static>,
368) -> (wgpu::Adapter, wgpu::Device, wgpu::Queue) {
369 let adapter = instance
370 .request_adapter(&wgpu::RequestAdapterOptions {
371 power_preference: wgpu::PowerPreference::from_env()
372 .unwrap_or(wgpu::PowerPreference::HighPerformance),
373 force_fallback_adapter: false,
374 compatible_surface: Some(surface),
375 })
376 .await
377 .expect("No suitable GPU adapters found on the system!");
378
379 let adapter_info = adapter.get_info();
380 println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
381
382 let base_dir = std::env::var("CARGO_MANIFEST_DIR");
383 let _trace_path = if let Ok(base_dir) = base_dir {
384 Some(std::path::PathBuf::from(&base_dir).join("WGPU_TRACE_ERROR"))
385 } else {
386 None
387 };
388
389 let adp_features = adapter.features();
392 #[cfg(target_family = "unix")]
393 let adp_features = {
394 let mut adp_features = adp_features;
395 if adapter_info.name.contains("NVIDIA") {
396 adp_features.remove(wgpu::Features::EXPERIMENTAL_RAY_QUERY);
397 }
398 adp_features
399 };
400 let res = adapter
404 .request_device(&wgpu::DeviceDescriptor {
405 label: None,
406 required_features: adp_features,
407 required_limits: adapter.limits(),
408 experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
409 memory_hints: wgpu::MemoryHints::Performance,
410 trace: wgpu::Trace::Off,
411 })
412 .await;
413
414 match res {
415 Err(err) => {
416 panic!("request_device failed: {err:?}");
417 }
418 Ok(tuple) => (adapter, tuple.0, tuple.1),
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 #[test]
427 fn normalize_view_size_clamps_zero_axes() {
428 assert_eq!(normalize_view_size((0, 720)), (1, 720));
429 assert_eq!(normalize_view_size((1280, 0)), (1280, 1));
430 assert_eq!(normalize_view_size((0, 0)), (1, 1));
431 }
432
433 #[test]
434 fn normalize_scale_factor_falls_back_to_one_for_invalid_values() {
435 assert_eq!(normalize_scale_factor(0.0), 1.0);
436 assert_eq!(normalize_scale_factor(-2.0), 1.0);
437 assert_eq!(normalize_scale_factor(f32::NAN), 1.0);
438 assert_eq!(normalize_scale_factor(f32::INFINITY), 1.0);
439 assert_eq!(normalize_scale_factor(2.0), 2.0);
440 }
441
442 #[test]
443 fn physical_size_from_logical_size_normalizes_scale_and_size() {
444 assert_eq!(
445 physical_size_from_logical_size(100.0, 50.0, 2.0),
446 (200, 100)
447 );
448 assert_eq!(physical_size_from_logical_size(0.0, 0.0, 2.0), (1, 1));
449 assert_eq!(physical_size_from_logical_size(12.0, 8.0, 0.0), (12, 8));
450 }
451
452 #[test]
453 fn logical_size_from_physical_size_normalizes_scale_and_size() {
454 assert_eq!(
455 logical_size_from_physical_size((200, 100), 2.0),
456 (100.0, 50.0)
457 );
458 assert_eq!(logical_size_from_physical_size((0, 0), 0.0), (1.0, 1.0));
459 }
460
461 #[test]
462 fn normalize_touch_point_uses_normalized_size_and_scale() {
463 assert_eq!(
464 normalize_touch_point(50.0, 25.0, (200, 100), 2.0),
465 (0.5, 0.5)
466 );
467 assert_eq!(normalize_touch_point(1.0, 1.0, (0, 0), 0.0), (1.0, 1.0));
468 }
469
470 #[test]
471 fn resize_surface_config_updates_only_when_size_changes() {
472 let mut config = wgpu::SurfaceConfiguration {
473 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
474 format: wgpu::TextureFormat::Bgra8Unorm,
475 width: 640,
476 height: 480,
477 present_mode: wgpu::PresentMode::Fifo,
478 desired_maximum_frame_latency: 2,
479 alpha_mode: wgpu::CompositeAlphaMode::Auto,
480 view_formats: vec![],
481 };
482
483 assert!(!resize_surface_config(&mut config, (640, 480)));
484 assert!(resize_surface_config(&mut config, (0, 720)));
485 assert_eq!((config.width, config.height), (1, 720));
486 }
487}