anvilkit_render/renderer/surface.rs
1//! # 窗口表面和交换链管理
2//!
3//! 提供 wgpu 表面配置、交换链管理和帧缓冲功能。
4
5use std::sync::Arc;
6use wgpu::{
7 Surface, SurfaceConfiguration, TextureFormat, PresentMode, CompositeAlphaMode,
8 SurfaceTexture,
9};
10use winit::window::Window;
11use log::{info, warn, debug};
12
13use crate::renderer::RenderDevice;
14use anvilkit_core::error::{AnvilKitError, Result};
15
16/// 渲染表面
17///
18/// 管理窗口表面、交换链配置和帧缓冲,提供渲染目标管理功能。
19///
20/// # 设计理念
21///
22/// - **自适应配置**: 根据设备能力自动配置表面参数
23/// - **动态调整**: 支持窗口大小变化时的动态重配置
24/// - **格式选择**: 自动选择最佳的纹理格式和呈现模式
25/// - **错误恢复**: 处理表面丢失等异常情况
26///
27/// # 示例
28///
29/// ```rust,no_run
30/// use anvilkit_render::renderer::{RenderDevice, RenderSurface};
31/// use std::sync::Arc;
32/// use winit::window::Window;
33///
34/// # async fn example() -> anvilkit_core::error::Result<()> {
35/// // 创建设备和表面
36/// // let window = Arc::new(window);
37/// // let device = RenderDevice::new(&window).await?;
38/// // let surface = RenderSurface::new(&device, &window)?;
39///
40/// // 获取当前帧
41/// // let frame = surface.get_current_frame()?;
42/// # Ok(())
43/// # }
44/// ```
45pub struct RenderSurface {
46 /// wgpu 表面(持有 Arc<Window>,保证生命周期安全)
47 surface: Surface<'static>,
48 /// 表面配置
49 config: SurfaceConfiguration,
50 /// 当前纹理格式
51 format: TextureFormat,
52 /// 持有窗口引用以保证 surface 生命周期
53 _window: Arc<Window>,
54}
55
56impl RenderSurface {
57 /// 创建新的渲染表面
58 ///
59 /// # 参数
60 ///
61 /// - `device`: 渲染设备
62 /// - `window`: 窗口实例
63 ///
64 /// # 返回
65 ///
66 /// 成功时返回 RenderSurface 实例,失败时返回错误
67 ///
68 /// # 示例
69 ///
70 /// ```rust,no_run
71 /// use anvilkit_render::renderer::{RenderDevice, RenderSurface};
72 /// use std::sync::Arc;
73 /// use winit::window::Window;
74 ///
75 /// # async fn example() -> anvilkit_core::error::Result<()> {
76 /// // let window = Arc::new(window);
77 /// // let device = RenderDevice::new(&window).await?;
78 /// // let surface = RenderSurface::new(&device, &window)?;
79 /// # Ok(())
80 /// # }
81 /// ```
82 pub fn new(device: &RenderDevice, window: &Arc<Window>) -> Result<Self> {
83 Self::new_with_vsync(device, window, false)
84 }
85
86 /// 创建新的渲染表面(指定 vsync 模式)
87 ///
88 /// - `vsync = true`: 使用 `PresentMode::Fifo`(垂直同步)
89 /// - `vsync = false`: 优先使用 `PresentMode::Mailbox`(三重缓冲,低延迟)
90 pub fn new_with_vsync(device: &RenderDevice, window: &Arc<Window>, vsync: bool) -> Result<Self> {
91 info!("创建渲染表面 (vsync={})", vsync);
92
93 // 创建表面
94 let surface = device.instance().create_surface(window.clone())
95 .map_err(|e| AnvilKitError::render(format!("创建表面失败: {}", e)))?;
96
97 // 获取表面能力
98 let capabilities = surface.get_capabilities(device.adapter());
99
100 // 选择纹理格式
101 let format = Self::choose_format(&capabilities.formats);
102
103 // 获取窗口大小
104 let size = window.inner_size();
105
106 // 选择呈现模式
107 let present_mode = if vsync {
108 PresentMode::Fifo
109 } else {
110 Self::choose_present_mode(&capabilities.present_modes)
111 };
112
113 // 创建表面配置
114 let config = SurfaceConfiguration {
115 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
116 format,
117 width: size.width,
118 height: size.height,
119 present_mode,
120 alpha_mode: Self::choose_alpha_mode(&capabilities.alpha_modes),
121 view_formats: vec![],
122 desired_maximum_frame_latency: 2,
123 };
124
125 // 配置表面
126 surface.configure(device.device(), &config);
127
128 info!("渲染表面创建成功");
129 info!("表面格式: {:?}", format);
130 info!("表面大小: {}x{}", config.width, config.height);
131 info!("呈现模式: {:?}", config.present_mode);
132
133 Ok(Self {
134 surface,
135 config,
136 format,
137 _window: window.clone(),
138 })
139 }
140
141 /// 选择纹理格式
142 ///
143 /// # 参数
144 ///
145 /// - `formats`: 支持的格式列表
146 ///
147 /// # 返回
148 ///
149 /// 返回选择的纹理格式
150 fn choose_format(formats: &[TextureFormat]) -> TextureFormat {
151 // 优先选择 sRGB 格式
152 for &format in formats {
153 match format {
154 TextureFormat::Bgra8UnormSrgb | TextureFormat::Rgba8UnormSrgb => {
155 debug!("选择纹理格式: {:?}", format);
156 return format;
157 }
158 _ => {}
159 }
160 }
161
162 // 回退到第一个可用格式
163 let format = formats[0];
164 debug!("回退到纹理格式: {:?}", format);
165 format
166 }
167
168 /// 选择呈现模式
169 ///
170 /// # 参数
171 ///
172 /// - `modes`: 支持的呈现模式列表
173 ///
174 /// # 返回
175 ///
176 /// 返回选择的呈现模式
177 fn choose_present_mode(modes: &[PresentMode]) -> PresentMode {
178 // 优先选择 Mailbox 模式(三重缓冲)
179 if modes.contains(&PresentMode::Mailbox) {
180 debug!("选择呈现模式: Mailbox");
181 return PresentMode::Mailbox;
182 }
183
184 // 回退到 Fifo 模式(垂直同步)
185 debug!("选择呈现模式: Fifo");
186 PresentMode::Fifo
187 }
188
189 /// 选择 Alpha 混合模式
190 ///
191 /// # 参数
192 ///
193 /// - `modes`: 支持的 Alpha 模式列表
194 ///
195 /// # 返回
196 ///
197 /// 返回选择的 Alpha 模式
198 fn choose_alpha_mode(modes: &[CompositeAlphaMode]) -> CompositeAlphaMode {
199 // 优先选择 Auto 模式
200 if modes.contains(&CompositeAlphaMode::Auto) {
201 debug!("选择 Alpha 模式: Auto");
202 return CompositeAlphaMode::Auto;
203 }
204
205 // 回退到 Opaque 模式
206 debug!("选择 Alpha 模式: Opaque");
207 CompositeAlphaMode::Opaque
208 }
209
210 /// 调整表面大小
211 ///
212 /// # 参数
213 ///
214 /// - `device`: 渲染设备
215 /// - `width`: 新的宽度
216 /// - `height`: 新的高度
217 ///
218 /// # 返回
219 ///
220 /// 成功时返回 Ok(()),失败时返回错误
221 ///
222 /// # 示例
223 ///
224 /// ```rust,no_run
225 /// # use anvilkit_render::renderer::{RenderDevice, RenderSurface};
226 /// # async fn example(device: &RenderDevice, surface: &mut RenderSurface) -> anvilkit_core::error::Result<()> {
227 /// surface.resize(device, 1920, 1080)?;
228 /// # Ok(())
229 /// # }
230 /// ```
231 pub fn resize(&mut self, device: &RenderDevice, width: u32, height: u32) -> Result<()> {
232 if width == 0 || height == 0 {
233 warn!("忽略无效的表面大小: {}x{}", width, height);
234 return Ok(());
235 }
236
237 info!("调整表面大小: {}x{}", width, height);
238
239 self.config.width = width;
240 self.config.height = height;
241
242 self.surface.configure(device.device(), &self.config);
243
244 Ok(())
245 }
246
247 /// 获取当前帧纹理
248 ///
249 /// # 返回
250 ///
251 /// 成功时返回 SurfaceTexture,失败时返回错误
252 ///
253 /// # 示例
254 ///
255 /// ```rust,no_run
256 /// # use anvilkit_render::renderer::RenderSurface;
257 /// # async fn example(surface: &RenderSurface) -> anvilkit_core::error::Result<()> {
258 /// let frame = surface.get_current_frame()?;
259 /// let view = frame.texture.create_view(&Default::default());
260 /// // 使用纹理视图进行渲染
261 /// frame.present();
262 /// # Ok(())
263 /// # }
264 /// ```
265 pub fn get_current_frame(&self) -> Result<SurfaceTexture> {
266 self.surface.get_current_texture()
267 .map_err(|e| match e {
268 wgpu::SurfaceError::Lost => {
269 AnvilKitError::render("表面丢失,需要重新配置".to_string())
270 }
271 wgpu::SurfaceError::OutOfMemory => {
272 AnvilKitError::render("GPU 内存不足".to_string())
273 }
274 wgpu::SurfaceError::Timeout => {
275 AnvilKitError::render("获取表面纹理超时".to_string())
276 }
277 wgpu::SurfaceError::Outdated => {
278 AnvilKitError::render("表面配置过时,需要重新配置".to_string())
279 }
280 })
281 }
282
283 /// 重新配置表面(用于 Lost/Outdated 恢复)
284 pub fn reconfigure(&self, device: &RenderDevice) {
285 info!("重新配置渲染表面: {}x{}", self.config.width, self.config.height);
286 self.surface.configure(device.device(), &self.config);
287 }
288
289 /// 获取当前帧,自动恢复 Lost/Outdated 错误
290 ///
291 /// 如果首次获取失败(Lost 或 Outdated),自动 reconfigure 后重试一次。
292 pub fn get_current_frame_with_recovery(&self, device: &RenderDevice) -> Result<SurfaceTexture> {
293 match self.surface.get_current_texture() {
294 Ok(frame) => Ok(frame),
295 Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
296 warn!("表面需要重新配置,正在恢复...");
297 self.reconfigure(device);
298 // 重试一次
299 self.surface.get_current_texture()
300 .map_err(|e| AnvilKitError::render(format!("表面恢复后仍失败: {}", e)))
301 }
302 Err(wgpu::SurfaceError::OutOfMemory) => {
303 Err(AnvilKitError::render("GPU 内存不足".to_string()))
304 }
305 Err(wgpu::SurfaceError::Timeout) => {
306 Err(AnvilKitError::render("获取表面纹理超时".to_string()))
307 }
308 }
309 }
310
311 /// 获取表面配置
312 ///
313 /// # 返回
314 ///
315 /// 返回当前的表面配置
316 ///
317 /// # 示例
318 ///
319 /// ```rust,no_run
320 /// # use anvilkit_render::renderer::RenderSurface;
321 /// # async fn example(surface: &RenderSurface) {
322 /// let config = surface.config();
323 /// println!("表面大小: {}x{}", config.width, config.height);
324 /// # }
325 /// ```
326 pub fn config(&self) -> &SurfaceConfiguration {
327 &self.config
328 }
329
330 /// 获取纹理格式
331 ///
332 /// # 返回
333 ///
334 /// 返回当前的纹理格式
335 ///
336 /// # 示例
337 ///
338 /// ```rust,no_run
339 /// # use anvilkit_render::renderer::RenderSurface;
340 /// # async fn example(surface: &RenderSurface) {
341 /// let format = surface.format();
342 /// println!("纹理格式: {:?}", format);
343 /// # }
344 /// ```
345 pub fn format(&self) -> TextureFormat {
346 self.format
347 }
348
349 /// 获取表面大小
350 ///
351 /// # 返回
352 ///
353 /// 返回 (宽度, 高度) 元组
354 ///
355 /// # 示例
356 ///
357 /// ```rust,no_run
358 /// # use anvilkit_render::renderer::RenderSurface;
359 /// # async fn example(surface: &RenderSurface) {
360 /// let (width, height) = surface.size();
361 /// println!("表面大小: {}x{}", width, height);
362 /// # }
363 /// ```
364 pub fn size(&self) -> (u32, u32) {
365 (self.config.width, self.config.height)
366 }
367
368 /// 获取表面引用
369 ///
370 /// # 返回
371 ///
372 /// 返回 wgpu 表面的引用
373 ///
374 /// # 示例
375 ///
376 /// ```rust,no_run
377 /// # use anvilkit_render::renderer::RenderSurface;
378 /// # async fn example(surface: &RenderSurface) {
379 /// let wgpu_surface = surface.surface();
380 /// // 使用原始表面进行高级操作
381 /// # }
382 /// ```
383 pub fn surface(&self) -> &Surface<'static> {
384 &self.surface
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use wgpu::{TextureFormat, PresentMode, CompositeAlphaMode};
392
393 #[test]
394 fn test_format_selection() {
395 let formats = vec![
396 TextureFormat::Rgba8Unorm,
397 TextureFormat::Bgra8UnormSrgb,
398 TextureFormat::Rgba8UnormSrgb,
399 ];
400
401 let chosen = RenderSurface::choose_format(&formats);
402 assert_eq!(chosen, TextureFormat::Bgra8UnormSrgb);
403 }
404
405 #[test]
406 fn test_present_mode_selection() {
407 let modes = vec![
408 PresentMode::Fifo,
409 PresentMode::Mailbox,
410 PresentMode::Immediate,
411 ];
412
413 let chosen = RenderSurface::choose_present_mode(&modes);
414 assert_eq!(chosen, PresentMode::Mailbox);
415 }
416
417 #[test]
418 fn test_alpha_mode_selection() {
419 let modes = vec![
420 CompositeAlphaMode::Opaque,
421 CompositeAlphaMode::Auto,
422 CompositeAlphaMode::PreMultiplied,
423 ];
424
425 let chosen = RenderSurface::choose_alpha_mode(&modes);
426 assert_eq!(chosen, CompositeAlphaMode::Auto);
427 }
428
429 #[test]
430 fn test_format_srgb_preference() {
431 // sRGB formats should be preferred
432 let formats = vec![
433 TextureFormat::Rgba8Unorm,
434 TextureFormat::Bgra8UnormSrgb,
435 TextureFormat::Rgba8UnormSrgb,
436 ];
437
438 let srgb = formats.iter().find(|f| {
439 matches!(f,
440 TextureFormat::Bgra8UnormSrgb |
441 TextureFormat::Rgba8UnormSrgb
442 )
443 });
444 assert!(srgb.is_some());
445 }
446
447 #[test]
448 fn test_present_mode_vsync() {
449 // Fifo mode is VSync on
450 let mode = PresentMode::Fifo;
451 assert_eq!(mode, PresentMode::Fifo);
452
453 // Immediate mode is VSync off
454 let mode = PresentMode::Immediate;
455 assert_eq!(mode, PresentMode::Immediate);
456 }
457}