[backdrop_dyn] Handle upstream pipeline failure (#553)

Following #537 it is possible for the flatten stage to fail and flag a
failure. In some cases this can cause invalid / corrupt bounding box
data to propagate downstream, leading to a hang in the per-tile backdrop
calculation loop.

Triggering this is highly subtle, so I don't have a test case as part of
vello scenes that can reliably reproduce this. Regardless, it makes
sense to check for the upstream failures and terminate the work in
general.

I made backdrop_dyn check for any upstream failure and I didn't make it
signal its own failure flag. I also didn't change the logic in the CPU
shader since the other stages I checked (flatten, coarse) do not
implement error signaling in their CPU counterparts. Let me know if
you'd like me to work on those.
diff --git a/crates/shaders/src/cpu/backdrop.rs b/crates/shaders/src/cpu/backdrop.rs
index 9e60c52..41303e1 100644
--- a/crates/shaders/src/cpu/backdrop.rs
+++ b/crates/shaders/src/cpu/backdrop.rs
@@ -1,11 +1,11 @@
 // Copyright 2023 the Vello Authors
 // SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
 
-use vello_encoding::{ConfigUniform, Path, Tile};
+use vello_encoding::{BumpAllocators, ConfigUniform, Path, Tile};
 
 use super::CpuBinding;
 
-fn backdrop_main(config: &ConfigUniform, paths: &[Path], tiles: &mut [Tile]) {
+fn backdrop_main(config: &ConfigUniform, _: &BumpAllocators, paths: &[Path], tiles: &mut [Tile]) {
     for drawobj_ix in 0..config.layout.n_draw_objects {
         let path = paths[drawobj_ix as usize];
         let width = path.bbox[2] - path.bbox[0];
@@ -24,7 +24,8 @@
 
 pub fn backdrop(_n_wg: u32, resources: &[CpuBinding]) {
     let config = resources[0].as_typed();
-    let paths = resources[1].as_slice();
-    let mut tiles = resources[2].as_slice_mut();
-    backdrop_main(&config, &paths, &mut tiles);
+    let bump = resources[1].as_typed();
+    let paths = resources[2].as_slice();
+    let mut tiles = resources[3].as_slice_mut();
+    backdrop_main(&config, &bump, &paths, &mut tiles);
 }
diff --git a/shader/backdrop_dyn.wgsl b/shader/backdrop_dyn.wgsl
index 2664f6c..7bdf5c7 100644
--- a/shader/backdrop_dyn.wgsl
+++ b/shader/backdrop_dyn.wgsl
@@ -3,6 +3,7 @@
 
 // Prefix sum for dynamically allocated backdrops
 
+#import bump
 #import config
 #import tile
 
@@ -10,9 +11,12 @@
 var<uniform> config: Config;
 
 @group(0) @binding(1)
-var<storage> paths: array<Path>;
+var<storage, read_write> bump: BumpAllocators;
 
 @group(0) @binding(2)
+var<storage> paths: array<Path>;
+
+@group(0) @binding(3)
 var<storage, read_write> tiles: array<Tile>;
 
 let WG_SIZE = 256u;
@@ -26,6 +30,14 @@
     @builtin(global_invocation_id) global_id: vec3<u32>,
     @builtin(local_invocation_id) local_id: vec3<u32>,
 ) {
+    // Abort if any of the prior stages failed.
+    if local_id.x == 0u {
+        sh_row_count[0] = atomicLoad(&bump.failed);
+    }
+    let failed = workgroupUniformLoad(&sh_row_count[0]);
+    if failed != 0u {
+        return;
+    }
     let drawobj_ix = global_id.x;
     var row_count = 0u;
     if drawobj_ix < config.n_drawobj {
@@ -34,6 +46,9 @@
         sh_row_width[local_id.x] = path.bbox.z - path.bbox.x;
         row_count = path.bbox.w - path.bbox.y;
         sh_offset[local_id.x] = path.tiles;
+    } else {
+        // Explicitly zero the row width, just in case.
+        sh_row_width[local_id.x] = 0u;
     }
     sh_row_count[local_id.x] = row_count;
 
diff --git a/src/render.rs b/src/render.rs
index ab44615..a248de3 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -358,7 +358,7 @@
         recording.dispatch(
             shaders.backdrop,
             wg_counts.backdrop,
-            [config_buf, path_buf, tile_buf],
+            [config_buf, bump_buf, path_buf, tile_buf],
         );
         recording.dispatch(
             shaders.coarse,
diff --git a/src/shaders.rs b/src/shaders.rs
index 2e3a699..7b9cdb9 100644
--- a/src/shaders.rs
+++ b/src/shaders.rs
@@ -180,7 +180,7 @@
     );
     let backdrop = add_shader!(
         backdrop_dyn,
-        [Uniform, BufReadOnly, Buffer],
+        [Uniform, Buffer, BufReadOnly, Buffer],
         CpuShaderType::Present(vello_shaders::cpu::backdrop)
     );
     let coarse = add_shader!(