docs: fix render loop to use CurrentSurfaceTexture enum per wgpu 29 API
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user