Skip to content

Commit e4dae05

Browse files
Wumpfcwfitzgerald
andauthored
Change get_current_texture output to a unified enum (gfx-rs#9257)
Co-authored-by: Connor Fitzgerald <[email protected]>
1 parent 41e85e7 commit e4dae05

11 files changed

Lines changed: 334 additions & 211 deletions

File tree

CHANGELOG.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,29 @@ Bottom level categories:
4444

4545
### Major Changes
4646

47+
#### `Surface::get_current_texture` now returns `CurrentSurfaceTexture` enum
48+
49+
`Surface::get_current_texture` no longer returns `Result<SurfaceTexture, SurfaceError>`.
50+
Instead, it returns a single `CurrentSurfaceTexture` enum that represents all possible outcomes as variants.
51+
`SurfaceError` has been removed, and the `suboptimal` field on `SurfaceTexture` has been replaced by a dedicated `Suboptimal` variant.
52+
53+
```rust
54+
match surface.get_current_texture() {
55+
wgpu::CurrentSurfaceTexture::Success(frame) => { /* render */ }
56+
wgpu::CurrentSurfaceTexture::Timeout
57+
| wgpu::CurrentSurfaceTexture::Occluded => { /* skip frame */ }
58+
wgpu::CurrentSurfaceTexture::Outdated
59+
| wgpu::CurrentSurfaceTexture::Suboptimal(frame) => { /* reconfigure surface */ }
60+
wgpu::CurrentSurfaceTexture::Lost => { /* reconfigure surface, or recreate device if device lost */ }
61+
wgpu::CurrentSurfaceTexture::Validation => {
62+
/* Only happens if there is a validation error and you
63+
have registered a error scope or uncaptured error handler. */
64+
}
65+
}
66+
```
67+
68+
By @cwfitzgerald, @Wumpf, and @emilk in [#9141](https://github.com/gfx-rs/wgpu/pull/9141) and [#????](https://github.com/gfx-rs/wgpu/pull/????).
69+
4770
#### `InstanceDescriptor` initialization APIs
4871

4972
`InstanceDescriptor`'s convenience constructors (an implementation of `Default` and the static `from_env_or_default` method) have been removed. In their place are new static methods that force recognition of whether a display handle is used:
@@ -164,7 +187,6 @@ By @kpreid in [#9042](https://github.com/gfx-rs/wgpu/pull/9042).
164187

165188
#### Other Breaking Changes
166189

167-
- `Surface::get_current_texture` can now return `SurfaceError::Occluded`. By @emilk in [#9141](https://github.com/gfx-rs/wgpu/pull/9141).
168190
- Use clearer field names for `StageError::InvalidWorkgroupSize`. By @ErichDonGubler in [#9192](https://github.com/gfx-rs/wgpu/pull/9192).
169191

170192
### New Features

examples/bug-repro/01_texture_atomic_bug/src/main.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct App {
2626
}
2727

2828
struct State {
29+
instance: wgpu::Instance,
2930
window: Arc<Window>,
3031
device: wgpu::Device,
3132
queue: wgpu::Queue,
@@ -250,6 +251,7 @@ impl State {
250251
});
251252

252253
State {
254+
instance,
253255
window,
254256
device,
255257
queue,
@@ -271,12 +273,17 @@ impl State {
271273

272274
fn render_frame(&mut self) {
273275
let frame = match self.surface.get_current_texture() {
274-
Ok(f) => f,
275-
Err(wgpu::SurfaceError::Outdated | wgpu::SurfaceError::Lost) => {
276+
wgpu::CurrentSurfaceTexture::Success(f) => f,
277+
wgpu::CurrentSurfaceTexture::Suboptimal(_) | wgpu::CurrentSurfaceTexture::Outdated => {
276278
self.surface.configure(&self.device, &self.surface_config);
277279
return;
278280
}
279-
Err(_) => return,
281+
wgpu::CurrentSurfaceTexture::Lost => {
282+
self.surface = self.instance.create_surface(self.window.clone()).unwrap();
283+
self.surface.configure(&self.device, &self.surface_config);
284+
return;
285+
}
286+
_ => return,
280287
};
281288
let frame_view = frame.texture.create_view(&Default::default());
282289
let mut enc = self.device.create_command_encoder(&Default::default());

examples/features/src/framework.rs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,25 +183,43 @@ impl SurfaceWrapper {
183183
/// Acquire the next surface texture.
184184
///
185185
/// Returns `None` on failure.
186-
fn acquire(&mut self, context: &ExampleContext) -> Option<wgpu::SurfaceTexture> {
186+
fn acquire(
187+
&mut self,
188+
context: &ExampleContext,
189+
window: Arc<Window>,
190+
) -> Option<wgpu::SurfaceTexture> {
191+
use wgpu::CurrentSurfaceTexture;
192+
187193
let surface = self.surface.as_ref().unwrap();
188194

189195
match surface.get_current_texture() {
190-
Ok(frame) => Some(frame),
196+
CurrentSurfaceTexture::Success(frame) => Some(frame),
191197
// If we timed out or the window is occluded, skip this frame:
192-
Err(wgpu::SurfaceError::Timeout | wgpu::SurfaceError::Occluded) => None,
193-
Err(
194-
// If the surface is outdated, or was lost, reconfigure it.
195-
wgpu::SurfaceError::Outdated
196-
| wgpu::SurfaceError::Lost
197-
| wgpu::SurfaceError::Other
198-
// If OutOfMemory happens, reconfiguring may not help, but we might as well try
199-
| wgpu::SurfaceError::OutOfMemory,
200-
) => {
198+
CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => None,
199+
// If the surface is outdated or suboptimal, reconfigure and retry.
200+
CurrentSurfaceTexture::Suboptimal(_) | CurrentSurfaceTexture::Outdated => {
201201
surface.configure(&context.device, self.config());
202-
Some(surface
203-
.get_current_texture()
204-
.expect("Failed to acquire next surface texture!"))
202+
match surface.get_current_texture() {
203+
CurrentSurfaceTexture::Success(frame)
204+
| CurrentSurfaceTexture::Suboptimal(frame) => Some(frame),
205+
other => panic!("Failed to acquire next surface texture: {other:?}"),
206+
}
207+
}
208+
CurrentSurfaceTexture::Validation => {
209+
unreachable!("No error scope registered, so validation errors will panic")
210+
}
211+
// If the surface is lost, recreate and reconfigure it.
212+
CurrentSurfaceTexture::Lost => {
213+
self.surface = Some(context.instance.create_surface(window).unwrap());
214+
self.surface
215+
.as_ref()
216+
.unwrap()
217+
.configure(&context.device, self.config());
218+
match self.surface.as_ref().unwrap().get_current_texture() {
219+
CurrentSurfaceTexture::Success(frame)
220+
| CurrentSurfaceTexture::Suboptimal(frame) => Some(frame),
221+
other => panic!("Failed to acquire next surface texture: {other:?}"),
222+
}
205223
}
206224
}
207225
}
@@ -518,7 +536,8 @@ impl<E: Example> ApplicationHandler<AppAction> for App<E> {
518536

519537
self.frame_counter.update();
520538

521-
if let Some(frame) = surface.acquire(context) {
539+
let window_arc = self.window.clone().unwrap();
540+
if let Some(frame) = surface.acquire(context, window_arc) {
522541
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
523542
format: Some(surface.config().view_formats[0]),
524543
..wgpu::TextureViewDescriptor::default()

examples/features/src/hello_triangle/mod.rs

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{borrow::Cow, future::Future, sync::Arc};
2-
use wgpu::SurfaceError;
2+
use wgpu::CurrentSurfaceTexture;
33
use winit::{
44
application::ApplicationHandler,
55
event::WindowEvent,
@@ -22,6 +22,8 @@ fn spawn(f: impl Future<Output = ()> + 'static) {
2222
}
2323

2424
struct WgpuState {
25+
instance: wgpu::Instance,
26+
window: Arc<Window>,
2527
device: wgpu::Device,
2628
queue: wgpu::Queue,
2729
surface: wgpu::Surface<'static>,
@@ -174,6 +176,8 @@ impl ApplicationHandler<TriangleAction> for App {
174176
surface.configure(&device, &config);
175177

176178
let _ = proxy.send_event(TriangleAction::Initialized(WgpuState {
179+
instance,
180+
window,
177181
device,
178182
queue,
179183
surface,
@@ -218,53 +222,74 @@ impl ApplicationHandler<TriangleAction> for App {
218222
}
219223
}
220224
WindowEvent::RedrawRequested => {
221-
match wgpu_state.surface.get_current_texture() {
222-
Ok(frame) => {
223-
let view = frame
224-
.texture
225-
.create_view(&wgpu::TextureViewDescriptor::default());
226-
let mut encoder = wgpu_state.device.create_command_encoder(
227-
&wgpu::CommandEncoderDescriptor { label: None },
228-
);
229-
{
230-
let mut rpass =
231-
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
232-
label: None,
233-
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
234-
view: &view,
235-
depth_slice: None,
236-
resolve_target: None,
237-
ops: wgpu::Operations {
238-
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
239-
store: wgpu::StoreOp::Store,
240-
},
241-
})],
242-
depth_stencil_attachment: None,
243-
timestamp_writes: None,
244-
occlusion_query_set: None,
245-
multiview_mask: None,
246-
});
247-
rpass.set_pipeline(&wgpu_state.render_pipeline);
248-
rpass.draw(0..3, 0..1);
249-
}
250-
251-
wgpu_state.queue.submit(Some(encoder.finish()));
225+
let frame = match wgpu_state.surface.get_current_texture() {
226+
CurrentSurfaceTexture::Success(frame) => frame,
227+
CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => {
228+
// Try again later
252229
if let Some(window) = &self.window {
253-
window.pre_present_notify();
230+
window.request_redraw();
254231
}
255-
frame.present();
232+
return;
256233
}
257-
Err(SurfaceError::Timeout | SurfaceError::Occluded) => {
258-
// Try again later
234+
CurrentSurfaceTexture::Suboptimal(_) | CurrentSurfaceTexture::Outdated => {
235+
wgpu_state
236+
.surface
237+
.configure(&wgpu_state.device, &wgpu_state.config);
259238
if let Some(window) = &self.window {
260239
window.request_redraw();
261240
}
241+
return;
262242
}
263-
Err(err) => {
264-
// TODO: reconfigure the surface instead
265-
panic!("get_current_texture: {err}");
243+
CurrentSurfaceTexture::Validation => {
244+
unreachable!("No error scope registered, so validation errors will panic")
266245
}
246+
CurrentSurfaceTexture::Lost => {
247+
wgpu_state.surface = wgpu_state
248+
.instance
249+
.create_surface(wgpu_state.window.clone())
250+
.unwrap();
251+
wgpu_state
252+
.surface
253+
.configure(&wgpu_state.device, &wgpu_state.config);
254+
if let Some(window) = &self.window {
255+
window.request_redraw();
256+
}
257+
return;
258+
}
259+
};
260+
261+
let view = frame
262+
.texture
263+
.create_view(&wgpu::TextureViewDescriptor::default());
264+
let mut encoder = wgpu_state
265+
.device
266+
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
267+
{
268+
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
269+
label: None,
270+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
271+
view: &view,
272+
depth_slice: None,
273+
resolve_target: None,
274+
ops: wgpu::Operations {
275+
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
276+
store: wgpu::StoreOp::Store,
277+
},
278+
})],
279+
depth_stencil_attachment: None,
280+
timestamp_writes: None,
281+
occlusion_query_set: None,
282+
multiview_mask: None,
283+
});
284+
rpass.set_pipeline(&wgpu_state.render_pipeline);
285+
rpass.draw(0..3, 0..1);
286+
}
287+
288+
wgpu_state.queue.submit(Some(encoder.finish()));
289+
if let Some(window) = &self.window {
290+
window.pre_present_notify();
267291
}
292+
frame.present();
268293
}
269294
WindowEvent::Occluded(is_occluded) => {
270295
if !is_occluded {

0 commit comments

Comments
 (0)