Fix missing viewport surfaces after a skipped startup frame (#1791)

Thanks @allisonChilton.
This commit is contained in:
allisonChilton authored and GitHub committed 2026-10-17 06:10:46 -04:00
1 parent 44732f8b40
commit 8c58efdecf
3 files changed
+45 -37

No files matched your search

+2
View File
@@ -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. 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 A frame whose meshes the GPU refuses to stage (device lost or out of memory) is
skipped with a warning instead of panicking (#1469). 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.
+2
View File
@@ -210,3 +210,5 @@ SolidCraft changes:
clamped to it (`clamp_surface_extent`, aspect ratio kept) and egui is drawn at the matching 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 smaller scale, instead of a `Surface::configure` validation panic (#869). The resize path and
an unsupported surface no longer unwrap. 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.
+41 -37
View File
@@ -588,43 +588,6 @@ impl Painter {
return vsync_sec; 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 { if surface_state.needs_reconfigure {
Self::configure_surface(surface_state, render_state, &self.config.surface); Self::configure_surface(surface_state, render_state, &self.config.surface);
surface_state.needs_reconfigure = false; 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 mut capture_buffer = None;
{ {
let renderer = render_state.renderer.read(); let renderer = render_state.renderer.read();