Skip to content

Commit 8cec85a

Browse files
authored
wgpu_context: don't reconfigure while a surface output is alive (#66)
wgpu forbids `Surface::configure` while a `SurfaceOutput` is alive, which panicked on the first acquire after a resize/maximize (reported suboptimal). `get_current_texture` is now stored as `Option<SurfaceTexture>`. A suboptimal texture is still presentable, so it is rendered as-is (size changes are reconfigured in `resize`), avoiding any per-frame reconfigure churn. Only `Outdated`/`Lost` surfaces — which carry no live output — are reconfigured and re-acquired once, via `acquire_reconfiguring_if_stale` on `SurfaceRenderer`.
1 parent 54fa6b6 commit 8cec85a

1 file changed

Lines changed: 33 additions & 22 deletions

File tree

crates/wgpu_context/src/surface_renderer.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub struct SurfaceRenderer<'s> {
8989
pub surface: Surface<'s>,
9090
pub config: SurfaceConfiguration,
9191

92-
current_surface_texture: Option<CurrentSurfaceTexture>,
92+
current_surface_texture: Option<SurfaceTexture>,
9393
intermediate_texture: Option<Box<IntermediateTextureStuff>>,
9494
}
9595

@@ -193,28 +193,43 @@ impl<'s> SurfaceRenderer<'s> {
193193
self.current_surface_texture = None;
194194
}
195195

196+
/// Acquire the next surface frame.
197+
fn acquire(&self) -> CurrentSurfaceTexture {
198+
self.surface.get_current_texture()
199+
}
200+
201+
/// Acquire a usable surface texture. A `Suboptimal` texture is still
202+
/// presentable, so it is rendered as-is — size changes are reconfigured in
203+
/// `resize`, so this path does not churn the swapchain every frame when the
204+
/// surface stays suboptimal. `Outdated`/`Lost` surfaces are genuinely
205+
/// unusable: reconfigure and re-acquire once.
206+
fn acquire_reconfiguring_if_stale(&self) -> Option<SurfaceTexture> {
207+
match self.acquire() {
208+
CurrentSurfaceTexture::Success(surface_texture)
209+
| CurrentSurfaceTexture::Suboptimal(surface_texture) => Some(surface_texture),
210+
CurrentSurfaceTexture::Outdated | CurrentSurfaceTexture::Lost => {
211+
self.configure();
212+
match self.acquire() {
213+
CurrentSurfaceTexture::Success(surface_texture)
214+
| CurrentSurfaceTexture::Suboptimal(surface_texture) => Some(surface_texture),
215+
_ => None,
216+
}
217+
}
218+
// Timeout / occluded / validation error: skip this frame.
219+
_ => None,
220+
}
221+
}
222+
196223
pub fn ensure_current_surface_texture(
197224
&mut self,
198225
) -> Result<&SurfaceTexture, GetCurrentSurfaceTextureErr> {
199226
if self.current_surface_texture.is_none() {
200-
let tex = self.surface.get_current_texture();
201-
match &tex {
202-
CurrentSurfaceTexture::Lost
203-
| CurrentSurfaceTexture::Outdated
204-
| CurrentSurfaceTexture::Suboptimal(_) => {
205-
self.surface
206-
.configure(&self.device_handle.device, &self.config);
207-
}
208-
_ => {}
209-
}
210-
211-
self.current_surface_texture = Some(tex);
227+
self.current_surface_texture = self.acquire_reconfiguring_if_stale();
212228
}
213229

214-
match self.current_surface_texture.as_ref().unwrap() {
215-
CurrentSurfaceTexture::Success(surface_texture) => Ok(surface_texture),
216-
_ => Err(GetCurrentSurfaceTextureErr),
217-
}
230+
self.current_surface_texture
231+
.as_ref()
232+
.ok_or(GetCurrentSurfaceTextureErr)
218233
}
219234

220235
/// Get a target texture view to render to.
@@ -242,11 +257,7 @@ impl<'s> SurfaceRenderer<'s> {
242257
return Err(GetCurrentSurfaceTextureErr);
243258
}
244259

245-
let CurrentSurfaceTexture::Success(surface_texture) =
246-
self.current_surface_texture.take().unwrap()
247-
else {
248-
unreachable!("Surface texture was set in ensure_current_surface_texture above");
249-
};
260+
let surface_texture = self.current_surface_texture.take().unwrap();
250261

251262
if let Some(its) = &self.intermediate_texture {
252263
self.blit_from_intermediate_texture_to_surface(&surface_texture, its);

0 commit comments

Comments
 (0)