Skip to main content

app_surface/
lib.rs

1use core::ops::Deref;
2use wgpu::{Instance, Surface};
3
4mod touch;
5pub use touch::*;
6
7#[cfg_attr(
8    any(target_os = "ios", all(feature = "mac_catalyst", target_os = "macos")),
9    path = "ios.rs"
10)]
11#[cfg_attr(target_os = "android", path = "android.rs")]
12#[cfg_attr(
13    all(target_arch = "wasm32", feature = "web_rwh"),
14    path = "web_rwh/mod.rs"
15)]
16#[cfg_attr(
17    any(
18        all(not(feature = "mac_catalyst"), target_os = "macos"),
19        target_os = "windows",
20        target_os = "linux",
21    ),
22    path = "app_surface_use_winit.rs"
23)]
24#[cfg_attr(
25    all(target_arch = "wasm32", not(feature = "web_rwh")),
26    path = "app_surface_use_winit.rs"
27)]
28mod app_surface;
29pub use app_surface::*;
30
31// #[cfg(all(target_arch = "wasm32", feature = "web_rwh"))]
32// compile_error!("web_rwh feature is enabled for wasm32");
33
34// #[cfg(all(target_arch = "wasm32", not(feature = "web_rwh")))]
35// compile_error!("web_rwh feature is not enabled -");
36
37#[repr(C)]
38#[derive(Debug)]
39pub struct ViewSize {
40    pub width: u32,
41    pub height: u32,
42}
43
44#[cfg(target_arch = "wasm32")]
45use std::rc::Rc as SharedPtr;
46#[cfg(not(target_arch = "wasm32"))]
47use std::sync::Arc as SharedPtr;
48/// wgpu v24 开始,Instance、Adapter、Device 和 Queue 都是可 `Clone` 的
49#[derive(Clone)]
50pub struct IASDQContext {
51    pub instance: wgpu::Instance,
52    pub surface: SharedPtr<wgpu::Surface<'static>>,
53    pub config: wgpu::SurfaceConfiguration,
54    pub adapter: wgpu::Adapter,
55    pub device: wgpu::Device,
56    pub queue: wgpu::Queue,
57}
58
59impl IASDQContext {
60    pub fn update_config_format(&mut self, format: wgpu::TextureFormat) {
61        self.config.format = format;
62        if cfg!(feature = "webgl") {
63            // webgl 后端不支持 view_formats
64        } else if format == format.remove_srgb_suffix() {
65            self.config.view_formats = vec![format.add_srgb_suffix()];
66        } else {
67            self.config.view_formats = vec![format];
68        }
69        self.surface.configure(&self.device, &self.config);
70    }
71}
72
73impl Deref for AppSurface {
74    type Target = IASDQContext;
75    fn deref(&self) -> &Self::Target {
76        &self.ctx
77    }
78}
79
80pub trait SurfaceFrame {
81    fn view_size(&self) -> ViewSize;
82    // After App view's size or orientation changed, need to resize surface.
83    fn resize_surface(&mut self);
84    fn resize_surface_by_size(&mut self, size: (u32, u32));
85    fn pintch(&mut self, _touch: Touch, _scale: f32) {}
86    fn touch(&mut self, _touch: Touch) {}
87    fn normalize_touch_point(&self, _touch_point_x: f32, _touch_point_y: f32) -> (f32, f32) {
88        unimplemented!()
89    }
90    fn enter_frame(&mut self) {}
91    fn get_current_frame_view(
92        &self,
93        _view_format: Option<wgpu::TextureFormat>,
94    ) -> Option<(wgpu::SurfaceTexture, wgpu::TextureView)> {
95        unimplemented!()
96    }
97    fn create_current_frame_view(
98        &self,
99        device: &wgpu::Device,
100        surface: &wgpu::Surface,
101        config: &wgpu::SurfaceConfiguration,
102        view_format: Option<wgpu::TextureFormat>,
103    ) -> Option<(wgpu::SurfaceTexture, wgpu::TextureView)> {
104        let frame = match surface.get_current_texture() {
105            wgpu::CurrentSurfaceTexture::Success(frame)
106            | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
107            wgpu::CurrentSurfaceTexture::Timeout
108            | wgpu::CurrentSurfaceTexture::Outdated
109            | wgpu::CurrentSurfaceTexture::Lost => {
110                surface.configure(device, config);
111                match surface.get_current_texture() {
112                    wgpu::CurrentSurfaceTexture::Success(frame)
113                    | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
114                    _ => panic!("Failed to acquire next swap chain texture!"),
115                }
116            }
117            wgpu::CurrentSurfaceTexture::Occluded => return None,
118            wgpu::CurrentSurfaceTexture::Validation => panic!("Validation error acquiring texture"),
119        };
120        let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
121            label: Some("frame texture view"),
122            format: if view_format.is_none() {
123                // frame buffer's view format prefer to use sRGB.
124                Some(config.format.add_srgb_suffix())
125            } else {
126                view_format
127            },
128            ..Default::default()
129        });
130        Some((frame, view))
131    }
132}
133
134impl SurfaceFrame for AppSurface {
135    fn view_size(&self) -> ViewSize {
136        let size = self.get_view_size();
137        ViewSize {
138            width: size.0,
139            height: size.1,
140        }
141    }
142
143    fn resize_surface(&mut self) {
144        let size = self.get_view_size();
145        self.ctx.config.width = size.0;
146        self.ctx.config.height = size.1;
147        self.surface.configure(&self.device, &self.config);
148    }
149
150    fn resize_surface_by_size(&mut self, size: (u32, u32)) {
151        self.ctx.config.width = size.0;
152        self.ctx.config.height = size.1;
153        self.surface.configure(&self.device, &self.config);
154    }
155
156    fn normalize_touch_point(&self, touch_point_x: f32, touch_point_y: f32) -> (f32, f32) {
157        let size = self.get_view_size();
158        (
159            touch_point_x * self.scale_factor / size.0 as f32,
160            touch_point_y * self.scale_factor / size.1 as f32,
161        )
162    }
163
164    fn get_current_frame_view(
165        &self,
166        view_format: Option<wgpu::TextureFormat>,
167    ) -> Option<(wgpu::SurfaceTexture, wgpu::TextureView)> {
168        self.create_current_frame_view(&self.device, &self.surface, &self.config, view_format)
169    }
170}
171
172async fn create_iasdq_context(
173    instance: Instance,
174    surface: Surface<'static>,
175    physical_size: (u32, u32),
176) -> IASDQContext {
177    let (adapter, device, queue) = crate::request_device(&instance, &surface).await;
178
179    let caps = surface.get_capabilities(&adapter);
180    let prefered = caps.formats[0];
181
182    let format = if cfg!(all(target_arch = "wasm32", not(feature = "webgl"))) {
183        // Chrome WebGPU doesn't support sRGB:
184        // unsupported swap chain format "xxxx8unorm-srgb"
185        prefered.remove_srgb_suffix()
186    } else {
187        prefered
188    };
189    let view_formats = if cfg!(feature = "webgl") {
190        // panicked at 'Error in Surface::configure: Validation Error
191        // Caused by:
192        // Downlevel flags DownlevelFlags(SURFACE_VIEW_FORMATS) are required but not supported on the device.
193        vec![]
194    } else if cfg!(target_os = "android") {
195        // TODO:HarmonyOS 不支持 view_formats 格式
196        // format 的值与 view_formats 的值一致时,configure 内部会自动忽略 view_formats 的值
197        //
198        // Android 不支持 view_formats:
199        // Downlevel flags DownlevelFlags(SURFACE_VIEW_FORMATS) are required but not supported on the device.
200        // This is not an invalid use of WebGPU: the underlying API or device does not support enough features
201        // to be a fully compliant implementation. A subset of the features can still be used.
202        // If you are running this program on native and not in a browser and wish to work around this issue,
203        // call Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current platform supports.
204        vec![format]
205    } else if format.is_srgb() {
206        vec![format, format.remove_srgb_suffix()]
207    } else {
208        vec![format.add_srgb_suffix(), format.remove_srgb_suffix()]
209    };
210
211    let mut config = surface
212        .get_default_config(&adapter, physical_size.0, physical_size.1)
213        .expect("Surface isn't supported by the adapter.");
214
215    config.view_formats = view_formats;
216    config.format = format;
217
218    surface.configure(&device, &config);
219
220    IASDQContext {
221        instance,
222        surface: SharedPtr::new(surface),
223        config,
224        adapter,
225        device,
226        queue,
227    }
228}
229
230async fn request_device(
231    instance: &Instance,
232    surface: &Surface<'static>,
233) -> (wgpu::Adapter, wgpu::Device, wgpu::Queue) {
234    let adapter = instance
235        .request_adapter(&wgpu::RequestAdapterOptions {
236            power_preference: wgpu::PowerPreference::from_env()
237                .unwrap_or(wgpu::PowerPreference::HighPerformance),
238            force_fallback_adapter: false,
239            compatible_surface: Some(surface),
240        })
241        .await
242        .expect("No suitable GPU adapters found on the system!");
243
244    let adapter_info = adapter.get_info();
245    println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
246
247    let base_dir = std::env::var("CARGO_MANIFEST_DIR");
248    let _trace_path = if let Ok(base_dir) = base_dir {
249        Some(std::path::PathBuf::from(&base_dir).join("WGPU_TRACE_ERROR"))
250    } else {
251        None
252    };
253
254    // remove raytracing features from acquired features under unix like os on nvidia discrete cards
255    // this might be related to wgpu issue, need to keep tracing.
256    let mut adp_features = adapter.features();
257    #[cfg(target_family = "unix")]
258    {
259        if adapter_info.name.contains("NVIDIA") {
260            adp_features.remove(wgpu::Features::EXPERIMENTAL_RAY_QUERY);
261        }
262    }
263    // test features
264    // let adp_features = wgpu::Features::from_bits(0b0011111111011100110111111111111111111111110111000000111111001111).unwrap();
265
266    let res = adapter
267        .request_device(&wgpu::DeviceDescriptor {
268            label: None,
269            required_features: adp_features,
270            required_limits: adapter.limits(),
271            experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
272            memory_hints: wgpu::MemoryHints::Performance,
273            trace: wgpu::Trace::Off,
274        })
275        .await;
276
277    match res {
278        Err(err) => {
279            panic!("request_device failed: {err:?}");
280        }
281        Ok(tuple) => (adapter, tuple.0, tuple.1),
282    }
283}