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.
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.
+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
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.
+41 -37
View File
@@ -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();