docs: fix render loop to use CurrentSurfaceTexture enum per wgpu 29 API

This commit is contained in:
2026-05-30 20:12:18 -05:00
parent 667dea3d52
commit d7fc299d5a

View File

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