oxiui_render_wgpu/
surface.rs1use oxiui_core::UiError;
13use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
14
15#[derive(Clone, Debug)]
19pub struct SurfaceConfig {
20 pub width: u32,
22 pub height: u32,
24 pub present_mode: wgpu::PresentMode,
26 pub alpha_mode: wgpu::CompositeAlphaMode,
28 pub desired_format: Option<wgpu::TextureFormat>,
31}
32
33impl Default for SurfaceConfig {
34 fn default() -> Self {
35 Self {
36 width: 800,
37 height: 600,
38 present_mode: wgpu::PresentMode::Fifo,
39 alpha_mode: wgpu::CompositeAlphaMode::Auto,
40 desired_format: None,
41 }
42 }
43}
44
45pub struct SurfaceContext {
52 pub device: wgpu::Device,
54 pub queue: wgpu::Queue,
56 pub surface: wgpu::Surface<'static>,
58 pub surface_format: wgpu::TextureFormat,
60 config: wgpu::SurfaceConfiguration,
62}
63
64impl SurfaceContext {
65 pub unsafe fn from_raw_handles(
74 window_handle: RawWindowHandle,
75 display_handle: RawDisplayHandle,
76 config: SurfaceConfig,
77 ) -> Result<Self, UiError> {
78 let instance = wgpu::Instance::default();
79
80 let surface_target = wgpu::SurfaceTargetUnsafe::RawHandle {
82 raw_window_handle: window_handle,
83 raw_display_handle: Some(display_handle),
84 };
85
86 let surface = unsafe { instance.create_surface_unsafe(surface_target) }
88 .map_err(|e| UiError::Backend(format!("wgpu surface creation failed: {e}")))?;
89
90 let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
92 power_preference: wgpu::PowerPreference::default(),
93 force_fallback_adapter: false,
94 compatible_surface: Some(&surface),
95 }))
96 .map_err(|e| UiError::Unsupported(format!("no GPU adapter for surface: {e}")))?;
97
98 let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
99 label: Some("oxiui-render-wgpu windowed device"),
100 required_features: wgpu::Features::empty(),
101 required_limits: wgpu::Limits::default(),
102 memory_hints: wgpu::MemoryHints::Performance,
103 experimental_features: wgpu::ExperimentalFeatures::disabled(),
104 trace: wgpu::Trace::Off,
105 }))
106 .map_err(|e| UiError::Backend(format!("GPU device request failed: {e}")))?;
107
108 let surface_format = if let Some(fmt) = config.desired_format {
111 fmt
112 } else {
113 let caps = surface.get_capabilities(&adapter);
114 caps.formats
115 .iter()
116 .copied()
117 .find(|f| f.is_srgb())
118 .unwrap_or_else(|| {
119 *caps
120 .formats
121 .first()
122 .unwrap_or(&wgpu::TextureFormat::Bgra8Unorm)
123 })
124 };
125
126 let sc_config = wgpu::SurfaceConfiguration {
127 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
128 format: surface_format,
129 width: config.width.max(1),
130 height: config.height.max(1),
131 present_mode: config.present_mode,
132 alpha_mode: config.alpha_mode,
133 view_formats: vec![],
134 desired_maximum_frame_latency: 2,
135 };
136
137 surface.configure(&device, &sc_config);
138
139 Ok(Self {
140 device,
141 queue,
142 surface,
143 surface_format,
144 config: sc_config,
145 })
146 }
147
148 pub fn resize(&mut self, width: u32, height: u32) {
152 self.config.width = width.max(1);
153 self.config.height = height.max(1);
154 self.surface.configure(&self.device, &self.config);
155 }
156
157 pub fn acquire_frame(&self) -> Option<wgpu::SurfaceTexture> {
164 match self.surface.get_current_texture() {
165 wgpu::CurrentSurfaceTexture::Success(frame) => Some(frame),
166 wgpu::CurrentSurfaceTexture::Suboptimal(frame) => {
167 Some(frame)
169 }
170 wgpu::CurrentSurfaceTexture::Timeout => None,
171 wgpu::CurrentSurfaceTexture::Occluded => None,
172 other => {
173 eprintln!("[oxiui-render-wgpu] surface error: {other:?} — frame skipped");
174 None
175 }
176 }
177 }
178
179 pub fn present_frame(&self, frame: wgpu::SurfaceTexture) {
181 frame.present();
182 }
183
184 pub fn width(&self) -> u32 {
186 self.config.width
187 }
188
189 pub fn height(&self) -> u32 {
191 self.config.height
192 }
193
194 pub fn format(&self) -> wgpu::TextureFormat {
196 self.surface_format
197 }
198}
199
200#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn surface_config_default_is_sane() {
208 let c = SurfaceConfig::default();
209 assert!(c.width > 0 && c.height > 0);
210 assert_eq!(c.width, 800);
211 assert_eq!(c.height, 600);
212 }
213
214 #[test]
215 fn surface_config_clone_and_debug() {
216 let c = SurfaceConfig::default();
217 let c2 = c.clone();
218 assert_eq!(c2.width, c.width);
219 let _ = format!("{c:?}");
221 }
222}