mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-14 11:17:01 -04:00
gpu: a device that cannot render the effect stage falls back to CPU effects instead of panicking
The GPU effect stage (#35) renders into Rgba32Float, which is not renderable on every device (the FreeBSD CI's software adapter rejects it). FxStage::supported only checks compute/storage limits, so GpuCompositor::new created an invalid pipeline and wgpu's default error handler panicked. Stage creation now runs inside a validation error scope; on an error the compositor runs without it and effects render on the CPU, as on devices without compute shaders. Found by the FreeBSD CI job. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
f2b36fe277
commit
c736a97432
1 file changed
+27
-1
+27
-1
@@ -317,7 +317,23 @@ impl GpuCompositor {
|
||||
view_formats: &[],
|
||||
});
|
||||
let dummy = dummy_tex.create_view(&Default::default());
|
||||
let fx = fx::FxStage::supported(device).then(|| fx::FxStage::new(device, &shader, &layer_bgl));
|
||||
// The effect stage's pipelines can still be invalid on a device that passes the limits
|
||||
// check (Rgba32Float is not renderable everywhere, e.g. software Vulkan/GL). Validation
|
||||
// errors would panic, so they are captured: without the stage, effects render on the CPU.
|
||||
let fx = fx::FxStage::supported(device)
|
||||
.then(|| {
|
||||
let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||
let stage = fx::FxStage::new(device, &shader, &layer_bgl);
|
||||
match poll_now(scope.pop()) {
|
||||
Some(None) => Some(stage),
|
||||
Some(Some(e)) => {
|
||||
log::warn!("GPU effects unavailable on this device, rendering them on the CPU: {e}");
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
Self {
|
||||
device: device.clone(),
|
||||
queue: queue.clone(),
|
||||
@@ -846,6 +862,16 @@ struct SrcInfo {
|
||||
color: filmcraft_color::ColorInfo,
|
||||
}
|
||||
|
||||
/// Poll a future once: wgpu's native error scopes resolve immediately. `None` if it is still
|
||||
/// pending (then the caller assumes the worst).
|
||||
fn poll_now<F: std::future::Future>(f: F) -> Option<F::Output> {
|
||||
let mut f = std::pin::pin!(f);
|
||||
match f.as_mut().poll(&mut std::task::Context::from_waker(std::task::Waker::noop())) {
|
||||
std::task::Poll::Ready(v) => Some(v),
|
||||
std::task::Poll::Pending => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A render pass drawing into the accumulator.
|
||||
fn accum_pass<'e>(enc: &'e mut wgpu::CommandEncoder, view: &wgpu::TextureView, load: wgpu::LoadOp<wgpu::Color>) -> wgpu::RenderPass<'e> {
|
||||
enc.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
|
||||
Reference in new issue
Block a user