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,
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<SurfaceTexture, SurfaceError>`. 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