diff --git a/docs/01-rainbow-triangle.md b/docs/01-rainbow-triangle.md index b82aefa..a37cc12 100644 --- a/docs/01-rainbow-triangle.md +++ b/docs/01-rainbow-triangle.md @@ -237,7 +237,7 @@ struct State { [present mode](concepts/GLOSSARY.md#present-mode). When the window is resized, we reconfigure the surface with updated dimensions. - **`window`** — shared reference to the winit window. Stored as an `Arc` so - the `resize()` method and the `SurfaceError::Outdated` recovery handler can + the `resize()` method and the `CurrentSurfaceTexture::Outdated` recovery handler can access the window's current dimensions. When the surface becomes outdated (e.g., after a compositor restart or display hotplug), recovery requires reconfiguring the swapchain with the window's live size — and that requires @@ -938,52 +938,54 @@ into for this frame. In a triple-buffered swapchain (`PresentMode::Mailbox`), there are up to two spare back buffers waiting for you. `get_current_texture()` hands you the next available one. -In wgpu 29, this method returns `Result`. On -success you receive a `SurfaceTexture` containing the back-buffer texture. On -failure you receive a `SurfaceError` describing what went wrong: +In wgpu 29, this method returns `CurrentSurfaceTexture`, a standalone enum with +7 variants describing the state of the swapchain's next back buffer: -> **Key insight #5 — 4 surface error variants you must handle:** `Ok(texture)` — -> render normally. `SurfaceError::Timeout` — skip frame (GPU late). -> `SurfaceError::Outdated` — surface changed, reconfigure when possible. -> `SurfaceError::Lost` — surface destroyed, cannot recover without re-init. -> `SurfaceError::OutOfMemory` — GPU memory exhausted, fatal. +> **Key insight #5 — 7 surface texture variants you must handle:** +> `CurrentSurfaceTexture::Success(frame)` — render normally. +> `CurrentSurfaceTexture::Suboptimal(frame)` — render (buffer available but +> not ideal, e.g., format mismatch). `CurrentSurfaceTexture::Timeout` — skip +> frame (GPU late). `CurrentSurfaceTexture::Occluded` — skip frame (window +> fully covered). `CurrentSurfaceTexture::Outdated` — surface changed, +> reconfigure. `CurrentSurfaceTexture::Lost` — surface destroyed, cannot +> recover without re-init. +> `CurrentSurfaceTexture::Validation { source, description }` — API +> validation caught an error, skip frame and log. -WHY `match` on the Result: `get_current_texture()` returns a `Result`, so you -use standard Rust error handling. The happy path (`Ok`) extracts the -`SurfaceTexture`, and the error path (`Err`) matches on the specific -`SurfaceError` variant. The Rust compiler enforces exhaustive matching. +WHY `match` on the enum: `get_current_texture()` returns a +`CurrentSurfaceTexture` enum, not a `Result`. You match on the variant +directly. `Success` and `Suboptimal` both carry a `SurfaceTexture` you can +render into — the only difference is that `Suboptimal` signals the buffer may +not be ideal (e.g., a format downgrade). The Rust compiler enforces exhaustive +matching across all 7 variants. ### The Complete `render` Implementation ```rust fn render(&mut self) { let frame = match self.surface.get_current_texture() { - Ok(frame) => frame, - Err(e) => { - match &e { - wgpu::SurfaceError::Timeout => { - log::warn!("Surface error: Timeout — skipping frame"); - } - wgpu::SurfaceError::Outdated => { - log::warn!("Surface error: Outdated — resizing"); - let size = wgpu::dpi::PhysicalSize { - width: self.config.width, - height: self.config.height, - }; - self.resize(size); - } - // The window field stored in State enables Outdated recovery: - // when the compositor or display server invalidates the surface, - // we can reconfigure the swapchain using the window's current - // dimensions (self.window.inner_size()) without needing a - // separate reference to the window from the event loop. - wgpu::SurfaceError::Lost => { - log::error!("Surface error: Lost — cannot recover without re-creating State"); - } - wgpu::SurfaceError::OutOfMemory => { - log::error!("Surface error: OutOfMemory — GPU memory exhausted"); - } - } + wgpu::CurrentSurfaceTexture::Success(frame) + | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame, + wgpu::CurrentSurfaceTexture::Timeout => { + log::warn!("Surface timeout — skipping frame"); + return; + } + wgpu::CurrentSurfaceTexture::Occluded => { + log::warn!("Surface occluded — skipping frame"); + return; + } + wgpu::CurrentSurfaceTexture::Outdated => { + log::warn!("Surface outdated — resizing"); + let size = self.window.inner_size(); + self.resize(size); + return; + } + wgpu::CurrentSurfaceTexture::Lost => { + log::error!("Surface lost — cannot recover without re-creating State"); + return; + } + wgpu::CurrentSurfaceTexture::Validation { source, description } => { + log::error!("Surface validation error: {:?} — {}", source, description); return; } }; @@ -1169,19 +1171,26 @@ buffer from "render target" to "front buffer" on the next vsync. ### Why the Match Arms Differ -- **`Ok(frame)`** — the swapchain delivered a `SurfaceTexture` you can render - into. Extract `frame.texture` to create a view, render, then call - `frame.present()`. -- **`SurfaceError::Timeout`** — the GPU exceeded the wait threshold for a back - buffer. Skip the frame. The GPU will catch up. -- **`SurfaceError::Outdated`** — the swapchain was created for a resolution - that no longer matches the window. Reconfigure the surface to match the - current dimensions. -- **`SurfaceError::Lost`** — the GPU or display server has been reset. Without - re-creating the device and surface, you cannot recover. In a real - application, you'd trigger a full re-initialization. -- **`SurfaceError::OutOfMemory`** — GPU memory is exhausted. This is a fatal - condition requiring process-level recovery. +- **`CurrentSurfaceTexture::Success(frame)` / `Suboptimal(frame)`** — the + swapchain delivered a `SurfaceTexture` you can render into. `Success` means + the buffer is ideal. `Suboptimal` means the buffer is available but may not + be ideal (e.g., format mismatch, downgraded resolution). Both carry the + same `SurfaceTexture`. Extract `frame.texture` to create a view, render, + then call `frame.present()`. +- **`CurrentSurfaceTexture::Timeout`** — the GPU exceeded the wait threshold + for a back buffer. Skip the frame. The GPU will catch up. +- **`CurrentSurfaceTexture::Occluded`** — the window is fully covered by + another window. Skip the frame; there's no point rendering to an invisible + surface. +- **`CurrentSurfaceTexture::Outdated`** — the swapchain was created for a + resolution that no longer matches the window. Reconfigure the surface + using `self.window.inner_size()` to match the current dimensions. +- **`CurrentSurfaceTexture::Lost`** — the GPU or display server has been + reset. Without re-creating the device and surface, you cannot recover. In + a real application, you'd trigger a full re-initialization. +- **`CurrentSurfaceTexture::Validation { source, description }`** — the wgpu + validation layer caught an API misuse. Log the diagnostic and skip the + frame. ## S8: Handling Window Resize