From 8c58efdecf0244798a3178fc96ac53522cf2ac26 Mon Sep 17 00:00:00 2001 From: allisonChilton Date: Sat, 17 Oct 2026 04:10:46 -0600 Subject: [PATCH] Fix missing viewport surfaces after a skipped startup frame (#1791) Thanks @allisonChilton. --- ATTRIBUTION.md | 2 + vendor/README.md | 2 + vendor/egui-wgpu/src/winit.rs | 78 ++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index cab087e9..8760ef1a 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -466,3 +466,5 @@ and shares that effective count with the surface attachments and CAD callbacks. Vulkan, DirectX 12, Metal and WebGPU retain their requested multisampling. A frame whose meshes the GPU refuses to stage (device lost or out of memory) is skipped with a warning instead of panicking (#1469). +Paint callbacks are prepared only after a drawable is acquired, so a skipped +surface frame cannot strand cached meshes without their GPU initialization. diff --git a/vendor/README.md b/vendor/README.md index 36bc8f17..d043cbd5 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -210,3 +210,5 @@ SolidCraft changes: clamped to it (`clamp_surface_extent`, aspect ratio kept) and egui is drawn at the matching smaller scale, instead of a `Surface::configure` validation panic (#869). The resize path and an unsupported surface no longer unwrap. +- `src/winit.rs`: acquire a drawable before preparing paint callbacks, so a skipped + surface frame cannot discard initialization commands for cached viewport meshes. diff --git a/vendor/egui-wgpu/src/winit.rs b/vendor/egui-wgpu/src/winit.rs index a2c00fe4..832f64f2 100644 --- a/vendor/egui-wgpu/src/winit.rs +++ b/vendor/egui-wgpu/src/winit.rs @@ -588,43 +588,6 @@ impl Painter { return vsync_sec; }; - let mut encoder = - render_state - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("encoder"), - }); - - // Upload all resources for the GPU. - // SolidCraft: a clamped surface draws the whole window at a smaller scale. - let screen_descriptor = renderer::ScreenDescriptor { - size_in_pixels: [surface_state.width, surface_state.height], - pixels_per_point: pixels_per_point * surface_state.scale, - }; - - let user_cmd_bufs = { - let mut renderer = render_state.renderer.write(); - #[expect(clippy::iter_over_hash_type)] // Order doesn't matter here - for (id, image_deltas) in textures_delta.set.drain() { - for image_delta in image_deltas { - renderer.update_texture( - &render_state.device, - &render_state.queue, - id, - &image_delta, - ); - } - } - - renderer.update_buffers( - &render_state.device, - &render_state.queue, - &mut encoder, - clipped_primitives, - &screen_descriptor, - ) - }; - if surface_state.needs_reconfigure { Self::configure_surface(surface_state, render_state, &self.config.surface); surface_state.needs_reconfigure = false; @@ -670,6 +633,47 @@ impl Painter { } }; + // SolidCraft: acquire the drawable before preparing callbacks. Preparation can + // cache GPU resources whose initialization is recorded into `encoder` (such as + // the viewport's tessellated vertices). If acquisition skips this frame, dropping + // that encoder leaves the cached resources uninitialized on every later frame. + let mut encoder = + render_state + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("encoder"), + }); + + // Upload all resources for the GPU. + // SolidCraft: a clamped surface draws the whole window at a smaller scale. + let screen_descriptor = renderer::ScreenDescriptor { + size_in_pixels: [surface_state.width, surface_state.height], + pixels_per_point: pixels_per_point * surface_state.scale, + }; + + let user_cmd_bufs = { + let mut renderer = render_state.renderer.write(); + #[expect(clippy::iter_over_hash_type)] // Order doesn't matter here + for (id, image_deltas) in textures_delta.set.drain() { + for image_delta in image_deltas { + renderer.update_texture( + &render_state.device, + &render_state.queue, + id, + &image_delta, + ); + } + } + + renderer.update_buffers( + &render_state.device, + &render_state.queue, + &mut encoder, + clipped_primitives, + &screen_descriptor, + ) + }; + let mut capture_buffer = None; { let renderer = render_state.renderer.read();