docs: fix SurfaceStatus API to match wgpu 29 CurrentSurfaceTexture

This commit is contained in:
2026-05-30 20:01:24 -05:00
parent 98cf438f88
commit 23a7e3b151

View File

@@ -921,7 +921,7 @@ wait for GPU completion.
### Acquiring a Back Buffer from the Swapchain ### Acquiring a Back Buffer from the Swapchain
```rust ```rust
let status = self.surface.get_current_texture(); let frame = self.surface.get_current_texture();
``` ```
`get_current_texture()` is how you acquire a back buffer from the `get_current_texture()` is how you acquire a back buffer from the
@@ -930,37 +930,58 @@ 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 a `CurrentSurfaceTexture` **enum** — not a In wgpu 29, this method returns `Result<SurfaceTexture, SurfaceError>`. On
`Result`. The swapchain can be in seven distinct states, and every state is a success you receive a `SurfaceTexture` containing the back-buffer texture. On
valid, non-error condition: failure you receive a `SurfaceError` describing what went wrong:
> **Key insight #5 — 7 swapchain states you must handle:** `Success(buf)` — > **Key insight #5 — 4 surface error variants you must handle:** `Ok(texture)` —
> render normally. `Suboptimal(buf)` — render but reconfig is advisable. > render normally. `SurfaceError::Timeout` — skip frame (GPU late).
> `Timeout` — skip frame (GPU late). `Occluded` — skip frame (window behind > `SurfaceError::Outdated` — surface changed, reconfigure when possible.
> another). `Outdated` — `self.resize()` to reconfigure. `Lost` — skip frame > `SurfaceError::Lost` — surface destroyed, cannot recover without re-init.
> (display server restarted). `Validation` — skip frame (API misuse; check > `SurfaceError::OutOfMemory` — GPU memory exhausted, fatal.
> logs).
WHY `match` on 7 variants: `get_current_texture()` does not return a `Result`. WHY `match` on the Result: `get_current_texture()` returns a `Result`, so you
All 7 states are valid and the match must be exhaustive. The Rust compiler use standard Rust error handling. The happy path (`Ok`) extracts the
enforces this — you cannot miss a variant. `SurfaceTexture`, and the error path (`Err`) matches on the specific
`SurfaceError` variant. The Rust compiler enforces exhaustive matching.
### The Complete `render` Implementation ### The Complete `render` Implementation
```rust ```rust
fn render(&mut self) { fn render(&mut self) {
let status = self.surface.get_current_texture(); 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);
}
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");
}
}
return;
}
};
match status {
wgpu::SurfaceStatus::Success(surface_texture)
| wgpu::SurfaceStatus::Suboptimal(surface_texture) => {
// Drive GPU work: shader compilation, memory allocation, fence signaling // Drive GPU work: shader compilation, memory allocation, fence signaling
if let Err(e) = self.device.poll(wgpu::PollType::Wait { submission_index: None, timeout: None }) { if let Err(e) = self.device.poll(wgpu::PollType::Wait { submission_index: None, timeout: None }) {
log::error!("Device poll failed: {e}"); log::error!("Device poll failed: {e}");
return; return;
} }
let texture_view = surface_texture.texture.create_view(&Default::default()); let texture_view = frame.texture.create_view(&Default::default());
let mut encoder = self.device.create_command_encoder( let mut encoder = self.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { &wgpu::CommandEncoderDescriptor {
@@ -997,37 +1018,7 @@ fn render(&mut self) {
} // render_pass drops here — render pass ends automatically } // render_pass drops here — render pass ends automatically
self.queue.submit(std::iter::once(encoder.finish())); self.queue.submit(std::iter::once(encoder.finish()));
surface_texture.present(); frame.present();
}
wgpu::SurfaceStatus::Timeout => {
// GPU took too long to finish previous work. Skip this frame.
log::warn!("Surface status: Timeout — skipping frame");
}
wgpu::SurfaceStatus::Occluded => {
// Window is fully occluded by another window. Skip rendering.
log::debug!("Surface status: Occluded — skipping frame");
}
wgpu::SurfaceStatus::Outdated => {
// Swapchain resolution no longer matches window. Reconfigure.
log::warn!("Surface status: Outdated — resizing");
if let Some(window) = &self.window {
self.resize(window.inner_size());
}
}
wgpu::SurfaceStatus::Lost => {
// Display server restarted or GPU lost. Fatal without re-init.
log::error!("Surface status: Lost — cannot recover without re-creating State");
}
wgpu::SurfaceStatus::Validation { source, description } => {
// wgpu validated your descriptor and found it invalid.
log::error!("Surface validation: {source} — {description}");
}
}
} }
``` ```
@@ -1165,22 +1156,19 @@ buffer from "render target" to "front buffer" on the next vsync.
### Why the Match Arms Differ ### Why the Match Arms Differ
- **`Success` / `Suboptimal`** — both deliver a `SurfaceTexture` you can render - **`Ok(frame)`** — the swapchain delivered a `SurfaceTexture` you can render
into. The difference: `Suboptimal` means the current swapchain configuration into. Extract `frame.texture` to create a view, render, then call
is not ideal for the GPU (e.g., format mismatch). You render normally but `frame.present()`.
should consider reconfiguring the surface during idle time. - **`SurfaceError::Timeout`** — the GPU exceeded the wait threshold for a back
- **`Timeout`** — the GPU exceeded the wait threshold for a back buffer. Skip buffer. Skip the frame. The GPU will catch up.
the frame. The GPU will catch up. - **`SurfaceError::Outdated`** — the swapchain was created for a resolution
- **`Occluded`** — another fully covers your window. Skip rendering entirely — that no longer matches the window. Reconfigure the surface to match the
the display server will not show your output. Saves GPU work. current dimensions.
- **`Outdated`** — the swapchain was created for a resolution that no longer - **`SurfaceError::Lost`** — the GPU or display server has been reset. Without
matches the window. Reconfigure the surface to match. re-creating the device and surface, you cannot recover. In a real
- **`Lost`** — the GPU or display server has been reset. Without re-creating application, you'd trigger a full re-initialization.
the device and surface, you cannot recover. In a real application, you'd - **`SurfaceError::OutOfMemory`** — GPU memory is exhausted. This is a fatal
trigger a full re-initialization. condition requiring process-level recovery.
- **`Validation`** — wgpu rejected the surface configuration due to API misuse.
Check logs for the description. This is a programming error, not a runtime
condition.
## S8: Handling Window Resize ## S8: Handling Window Resize