diff --git a/crates/gpu/src/ctx.rs b/crates/gpu/src/ctx.rs index b47dab4a0..c235b93ae 100644 --- a/crates/gpu/src/ctx.rs +++ b/crates/gpu/src/ctx.rs @@ -937,15 +937,24 @@ mod tests { println!("CHILD: no GPU"); return; }; - // hold device memory until an allocation fails: 1 GiB at a time, then smaller, so little is left + // hold device memory until an allocation fails: 1 GiB at a time, then smaller, so little is + // left; no larger than the device allows in one buffer, which would be a validation error + let max = g.device.limits().max_buffer_size & !(wgpu::COPY_BUFFER_ALIGNMENT - 1); + let mut sizes: Vec = [1u64 << 30, 1 << 28, 1 << 26].into_iter().map(|s| s.min(max)).collect(); + sizes.dedup(); let mut held = Vec::new(); let mut full = false; - for size in [1u64 << 30, 1 << 28, 1 << 26] { + for size in sizes { for _ in 0..128 { - let scope = g.device.push_error_scope(wgpu::ErrorFilter::OutOfMemory); + let validation = g.device.push_error_scope(wgpu::ErrorFilter::Validation); + let oom = g.device.push_error_scope(wgpu::ErrorFilter::OutOfMemory); let usage = wgpu::BufferUsages::STORAGE; let buf = g.device.create_buffer(&wgpu::BufferDescriptor { label: None, size, usage, mapped_at_creation: false }); - if pollster::block_on(scope.pop()).is_some() { + let ran_out = pollster::block_on(oom.pop()).is_some(); + if let Some(e) = pollster::block_on(validation.pop()) { + panic!("holding {size} bytes of device memory: {e}"); + } + if ran_out { full = true; break; } diff --git a/crates/gpu/src/lib.rs b/crates/gpu/src/lib.rs index 452e2dd67..f1b952f3e 100644 --- a/crates/gpu/src/lib.rs +++ b/crates/gpu/src/lib.rs @@ -520,6 +520,9 @@ pub fn try_render( format!("{what}: {}; the GPU is not used again", f.reason) } ctx::FailKind::OutOfMemory => { + // an upload that ran out of memory left a zeroed buffer; the shared source + // may be one, and the next render of the photo would reuse it + render::release_shared_source(); gpu.trim(0); format!("{what}: {}", f.reason) }