Renderer: skip a frame the GPU won't stage instead of crashing (#1469) (#1579)

Thanks @DuBearte.
This commit is contained in:
DuBearte authored and GitHub committed 2026-10-16 00:27:33 -04:00
1 parent c419dec76c
commit 3fe929e91c
3 files changed
+23 -4

No files matched your search

+2
View File
@@ -186,3 +186,5 @@ its original `egui.wgsl` shader are retained; no images or other artwork are cop
SolidCraft's narrow patch selects a safe 1x MSAA path on the chosen OpenGL adapter
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).
+2
View File
@@ -147,6 +147,8 @@ SolidCraft changes:
- `src/lib.rs`: use 1x MSAA on the selected OpenGL adapter before creating pipelines,
exposing the effective sample count so host paint callbacks can match it.
- `src/winit.rs`: use that same effective count for depth and resolve attachments.
- `src/renderer.rs`: when the GPU refuses a staging buffer for a frame's meshes (device lost
or out of memory), log a warning and draw nothing that frame instead of panicking (#1469).
- Other backends retain requested MSAA, without another adapter or device probe.
- `src/lib.rs`: clamp MSAA to the largest sample count the device supports for the surface and
depth formats (`supported_msaa_samples`, `RenderState::msaa_supported`): a saved 8x on a
+19 -4
View File
@@ -254,6 +254,11 @@ pub struct Renderer {
options: RendererOptions,
/// The last `update_buffers` could not upload this frame's meshes (the GPU refused a
/// staging buffer: device lost or out of memory), so `render` draws nothing this frame
/// instead of panicking (SolidCraft #1469).
skip_frame: bool,
/// Storage for resources shared with all invocations of [`CallbackTrait`]'s methods.
///
/// See also [`CallbackTrait`].
@@ -453,6 +458,7 @@ impl Renderer {
slices: Vec::with_capacity(64),
capacity: INDEX_BUFFER_START_CAPACITY,
},
skip_frame: false,
uniform_buffer,
// Buffers on wgpu are zero initialized, so this is indeed its current state!
previous_uniform_buffer_content: UniformBuffer::zeroed(),
@@ -484,6 +490,10 @@ impl Renderer {
) {
profiling::function_scope!();
if self.skip_frame {
return;
}
let pixels_per_point = screen_descriptor.pixels_per_point;
let size_in_pixels = screen_descriptor.size_in_pixels;
@@ -922,6 +932,7 @@ impl Renderer {
) -> Vec<wgpu::CommandBuffer> {
profiling::function_scope!();
self.skip_frame = false;
let screen_size_in_points = screen_descriptor.screen_size_in_points();
let uniform_buffer_content = UniformBuffer {
@@ -981,11 +992,13 @@ impl Renderer {
);
let Some(mut index_buffer_staging) = index_buffer_staging else {
panic!(
"Failed to create staging buffer for index data. Index count: {index_count}. Required index buffer size: {required_index_buffer_size}. Actual size {} and capacity: {} (bytes)",
log::warn!(
"Failed to create staging buffer for index data; skipping this frame. Index count: {index_count}. Required index buffer size: {required_index_buffer_size}. Actual size {} and capacity: {} (bytes)",
self.index_buffer.buffer.size(),
self.index_buffer.capacity
);
self.skip_frame = true;
return Vec::new();
};
let mut index_offset = 0;
@@ -1027,11 +1040,13 @@ impl Renderer {
);
let Some(mut vertex_buffer_staging) = vertex_buffer_staging else {
panic!(
"Failed to create staging buffer for vertex data. Vertex count: {vertex_count}. Required vertex buffer size: {required_vertex_buffer_size}. Actual size {} and capacity: {} (bytes)",
log::warn!(
"Failed to create staging buffer for vertex data; skipping this frame. Vertex count: {vertex_count}. Required vertex buffer size: {required_vertex_buffer_size}. Actual size {} and capacity: {} (bytes)",
self.vertex_buffer.buffer.size(),
self.vertex_buffer.capacity
);
self.skip_frame = true;
return Vec::new();
};
let mut vertex_offset = 0;