Desktop: Fix crash caused by submitting work mid surface-reconfigure (#4317)

Implement wgpu-sync
This commit is contained in:
Timon
2026-07-09 12:09:01 +00:00
committed by GitHub
parent 6a54dcb5da
commit 97f8113fe4
12 changed files with 284 additions and 47 deletions

View File

@@ -1,13 +1,12 @@
use wgpu::PresentMode;
use crate::window::Window;
use crate::wrapper::{WgpuContext, WgpuExecutor};
use crate::wrapper::{WgpuContext, WgpuCurrentSurfaceTexture, WgpuExecutor, WgpuSurface};
#[derive(derivative::Derivative)]
#[derivative(Debug)]
pub(crate) struct RenderState {
surface: wgpu::Surface<'static>,
context: WgpuContext,
surface: WgpuSurface,
executor: WgpuExecutor,
config: wgpu::SurfaceConfiguration,
render_pipeline: wgpu::RenderPipeline,
@@ -162,12 +161,11 @@ impl RenderState {
cache: None,
});
let wgpu_executor = WgpuExecutor::with_context(context.clone()).expect("Failed to create WgpuExecutor");
let executor = WgpuExecutor::with_context(context).expect("Failed to create WgpuExecutor");
Self {
surface,
context,
executor: wgpu_executor,
executor,
config,
render_pipeline,
transparent_texture,
@@ -193,12 +191,6 @@ impl RenderState {
self.desired_width = width;
self.desired_height = height;
self.surface_outdated = true;
if width > 0 && height > 0 && (self.config.width != width || self.config.height != height) {
self.config.width = width;
self.config.height = height;
self.surface.configure(&self.context.device, &self.config);
}
}
pub(crate) fn bind_viewport_texture(&mut self, viewport_texture: std::sync::Arc<wgpu::Texture>) {
@@ -254,6 +246,14 @@ impl RenderState {
if !self.surface_outdated {
return Ok(());
}
// Apply resize once per presented frame.
if self.desired_width > 0 && self.desired_height > 0 && (self.config.width != self.desired_width || self.config.height != self.desired_height) {
self.config.width = self.desired_width;
self.config.height = self.desired_height;
self.surface.configure(&self.executor.context().device, &self.config);
}
let ui_scale = if let Some(ui_texture) = &self.ui_texture
&& (self.desired_width != ui_texture.width() || self.desired_height != ui_texture.height())
{
@@ -266,21 +266,19 @@ impl RenderState {
self.render_overlays(scene);
}
let (output, suboptimal) = match self.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t) => (t, false),
// wgpu reports the swapchain no longer matches the underlying surface; present this frame and reconfigure after present, since `Surface::configure` panics while an acquired `SurfaceTexture` is still alive
wgpu::CurrentSurfaceTexture::Suboptimal(t) => (t, true),
// Window is minimized or behind another window: skip the frame silently and try again once it becomes visible
wgpu::CurrentSurfaceTexture::Occluded => return Ok(()),
wgpu::CurrentSurfaceTexture::Lost => return Err(RenderError::SurfaceLost),
wgpu::CurrentSurfaceTexture::Outdated => return Err(RenderError::SurfaceOutdated),
wgpu::CurrentSurfaceTexture::Timeout => return Err(RenderError::SurfaceTimeout),
wgpu::CurrentSurfaceTexture::Validation => return Err(RenderError::SurfaceValidation),
let (surface_texture, suboptimal) = match self.surface.get_current_texture(&self.executor.context().queue) {
WgpuCurrentSurfaceTexture::Success(t) => (t, false),
WgpuCurrentSurfaceTexture::Suboptimal(t) => (t, true),
WgpuCurrentSurfaceTexture::Occluded => return Ok(()),
WgpuCurrentSurfaceTexture::Lost => return Err(RenderError::SurfaceLost),
WgpuCurrentSurfaceTexture::Outdated => return Err(RenderError::SurfaceOutdated),
WgpuCurrentSurfaceTexture::Timeout => return Err(RenderError::SurfaceTimeout),
WgpuCurrentSurfaceTexture::Validation => return Err(RenderError::SurfaceValidation),
};
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self.context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder") });
let mut encoder = self.executor.context().device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder") });
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@@ -318,12 +316,12 @@ impl RenderState {
tracing::warn!("No bind group available - showing clear color only");
}
}
self.context.queue.submit(std::iter::once(encoder.finish()));
surface_texture.queue.submit(std::iter::once(encoder.finish()));
window.pre_present_notify();
output.present();
surface_texture.present();
if suboptimal {
self.surface.configure(&self.context.device, &self.config);
self.surface.configure(&self.executor.context().device, &self.config);
}
if ui_scale.is_some() {
@@ -340,7 +338,7 @@ impl RenderState {
let overlays_texture_view = self.overlays_texture.as_ref().unwrap_or(&self.transparent_texture).create_view(&wgpu::TextureViewDescriptor::default());
let ui_texture_view = self.ui_texture.as_ref().unwrap_or(&self.transparent_texture).create_view(&wgpu::TextureViewDescriptor::default());
let bind_group = self.context.device.create_bind_group(&wgpu::BindGroupDescriptor {
let bind_group = self.executor.context().device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.render_pipeline.get_bind_group_layout(0),
entries: &[
wgpu::BindGroupEntry {