Repository: gfx-rs/wgpu
Pull request #6216 (2024-09-04 21:42:37)

Make bind group an Option for set_bind_group calls.
This is just an API change for all the "set_bind_group" calls. Calls that pass a `Some()` argument have unchanged behavior. The `None` cases are left as TODOs.

**Connections**
Issue #4738

**Description**
`set_bind_group` now takes `Option<&BindGroup>`.

**Testing**
Existing tests, player scenes, and examples are all updated to use the new syntax.

<!-- 
Thanks for filing! The codeowners file will automatically request reviews from the appropriate teams.

After you get a review and have addressed any comments, please explicitly re-request a review from the
person(s) who reviewed your changes. This will make sure it gets re-added to their review queue - you're no bothering us!
-->

**Checklist**

- [x] Run `cargo fmt`.
- [x] Run `cargo clippy`. If applicable, add:
  - [x] `--target wasm32-unknown-unknown`
  - [x] `--target wasm32-unknown-emscripten`
- [x] Run `cargo xtask test` to run tests.
- [x] Add change to `CHANGELOG.md`. See simple instructions inside file.



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 265cee595..9762a8ecf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -220,20 +220,18 @@ jobs:
           # build docs
           cargo doc --target ${{ matrix.target }} --all-features --no-deps
 
-      # wgpu-core docs are not feasible due to <https://github.com/gfx-rs/wgpu/issues/4905>
-      #
-      # - name: check private item docs
-      #   if: matrix.kind == 'native'
-      #   shell: bash
-      #   run: |
-      #     set -e
-      #
-      #     # wgpu_core package
-      #     cargo +${{ env.DOCS_RUST_VERSION }} doc --target ${{ matrix.target }} \
-      #           --package wgpu-core \
-      #           --package wgpu-hal \
-      #           --package naga \
-      #           --all-features --no-deps --document-private-items
+      - name: check private item docs
+        if: matrix.kind == 'native'
+        shell: bash
+        run: |
+          set -e
+
+          # wgpu_core package
+          cargo doc --target ${{ matrix.target }} \
+                --package wgpu-core \
+                --package wgpu-hal \
+                --package naga \
+                --all-features --no-deps --document-private-items
 
   # We run minimal checks on the MSRV of the core crates, ensuring that
   # its dependency tree does not cause issues for firefox.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3fe5c417..96b823a2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -65,6 +65,15 @@ traits that have now been implemented for `wgpu` resources.
 
 By @teoxoy [#6134](https://github.com/gfx-rs/wgpu/pull/6134).
 
+#### `set_bind_group` now takes an `Option` for the bind group argument.
+
+https://gpuweb.github.io/gpuweb/#programmable-passes-bind-groups specifies that bindGroup
+is nullable. This change is the start of implementing this part of the spec. Callers that
+specify a `Some()` value should have unchanged behavior. Handling of `None` values still
+needs to be implemented by backends.
+
+By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
+
 ### New Features
 
 #### Naga
@@ -106,7 +115,7 @@ By @teoxoy [#6134](https://github.com/gfx-rs/wgpu/pull/6134).
 
 - `wgpu_hal::gles::Adapter::new_external` now requires the context to be current when dropping the adapter and related objects. By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114).
 - Reduce the amount of debug and trace logs emitted by wgpu-core and wgpu-hal. By @nical in [#6065](https://github.com/gfx-rs/wgpu/issues/6065)
-- `Rg11b10Float` is renamed to `Rg11b10UFloat`. By @sagudev in [#6108](https://github.com/gfx-rs/wgpu/pull/6108)
+- `Rg11b10Float` is renamed to `Rg11b10Ufloat`. By @sagudev in [#6108](https://github.com/gfx-rs/wgpu/pull/6108)
 
 #### HAL
 
diff --git a/benches/benches/computepass.rs b/benches/benches/computepass.rs
index 2af141360..719f84696 100644
--- a/benches/benches/computepass.rs
+++ b/benches/benches/computepass.rs
@@ -389,7 +389,7 @@ impl ComputepassState {
         let end_idx = start_idx + dispatch_per_pass;
         for dispatch_idx in start_idx..end_idx {
             compute_pass.set_pipeline(&self.pipeline);
-            compute_pass.set_bind_group(0, &self.bind_groups[dispatch_idx], &[]);
+            compute_pass.set_bind_group(0, Some(&self.bind_groups[dispatch_idx]), &[]);
             compute_pass.dispatch_workgroups(1, 1, 1);
         }
 
@@ -412,7 +412,7 @@ impl ComputepassState {
         });
 
         compute_pass.set_pipeline(self.bindless_pipeline.as_ref().unwrap());
-        compute_pass.set_bind_group(0, self.bindless_bind_group.as_ref().unwrap(), &[]);
+        compute_pass.set_bind_group(0, Some(self.bindless_bind_group.as_ref().unwrap()), &[]);
         for _ in 0..dispatch_count_bindless {
             compute_pass.dispatch_workgroups(1, 1, 1);
         }
@@ -447,7 +447,7 @@ fn run_bench(ctx: &mut Criterion) {
             };
 
             group.bench_function(
-                &format!("{cpasses} computepasses x {dispatch_per_pass} dispatches ({label})"),
+                format!("{cpasses} computepasses x {dispatch_per_pass} dispatches ({label})"),
                 |b| {
                     Lazy::force(&state);
 
@@ -496,7 +496,7 @@ fn run_bench(ctx: &mut Criterion) {
     for threads in [2, 4, 8] {
         let dispatch_per_pass = dispatch_count / threads;
         group.bench_function(
-            &format!("{threads} threads x {dispatch_per_pass} dispatch"),
+            format!("{threads} threads x {dispatch_per_pass} dispatch"),
             |b| {
                 Lazy::force(&state);
 
@@ -537,7 +537,7 @@ fn run_bench(ctx: &mut Criterion) {
     let mut group = ctx.benchmark_group("Computepass: Bindless");
     group.throughput(Throughput::Elements(dispatch_count_bindless as _));
 
-    group.bench_function(&format!("{dispatch_count_bindless} dispatch"), |b| {
+    group.bench_function(format!("{dispatch_count_bindless} dispatch"), |b| {
         Lazy::force(&state);
 
         b.iter_custom(|iters| {
diff --git a/benches/benches/renderpass.rs b/benches/benches/renderpass.rs
index 7f2e14116..fe23aa62b 100644
--- a/benches/benches/renderpass.rs
+++ b/benches/benches/renderpass.rs
@@ -367,7 +367,7 @@ impl RenderpassState {
         let end_idx = start_idx + draws_per_pass;
         for draw_idx in start_idx..end_idx {
             render_pass.set_pipeline(&self.pipeline);
-            render_pass.set_bind_group(0, &self.bind_groups[draw_idx], &[]);
+            render_pass.set_bind_group(0, Some(&self.bind_groups[draw_idx]), &[]);
             for i in 0..VERTEX_BUFFERS_PER_DRAW {
                 render_pass.set_vertex_buffer(
                     i as u32,
@@ -410,7 +410,7 @@ impl RenderpassState {
         });
 
         render_pass.set_pipeline(self.bindless_pipeline.as_ref().unwrap());
-        render_pass.set_bind_group(0, self.bindless_bind_group.as_ref().unwrap(), &[]);
+        render_pass.set_bind_group(0, Some(self.bindless_bind_group.as_ref().unwrap()), &[]);
         for i in 0..VERTEX_BUFFERS_PER_DRAW {
             render_pass.set_vertex_buffer(i as u32, self.vertex_buffers[0].slice(..));
         }
@@ -448,7 +448,7 @@ fn run_bench(ctx: &mut Criterion) {
             };
 
             group.bench_function(
-                &format!("{rpasses} renderpasses x {draws_per_pass} draws ({label})"),
+                format!("{rpasses} renderpasses x {draws_per_pass} draws ({label})"),
                 |b| {
                     Lazy::force(&state);
 
@@ -501,41 +501,38 @@ fn run_bench(ctx: &mut Criterion) {
 
     for threads in [2, 4, 8] {
         let draws_per_pass = draw_count / threads;
-        group.bench_function(
-            &format!("{threads} threads x {draws_per_pass} draws"),
-            |b| {
-                Lazy::force(&state);
+        group.bench_function(format!("{threads} threads x {draws_per_pass} draws"), |b| {
+            Lazy::force(&state);
 
-                b.iter_custom(|iters| {
-                    profiling::scope!("benchmark invocation");
+            b.iter_custom(|iters| {
+                profiling::scope!("benchmark invocation");
 
-                    // This benchmark hangs on Apple Paravirtualized GPUs. No idea why.
-                    if state.device_state.adapter_info.name.contains("Paravirtual") {
-                        return Duration::from_secs_f32(1.0);
-                    }
+                // This benchmark hangs on Apple Paravirtualized GPUs. No idea why.
+                if state.device_state.adapter_info.name.contains("Paravirtual") {
+                    return Duration::from_secs_f32(1.0);
+                }
 
-                    let mut duration = Duration::ZERO;
+                let mut duration = Duration::ZERO;
 
-                    for _ in 0..iters {
-                        profiling::scope!("benchmark iteration");
+                for _ in 0..iters {
+                    profiling::scope!("benchmark iteration");
 
-                        let start = Instant::now();
+                    let start = Instant::now();
 
-                        let buffers = (0..threads)
-                            .into_par_iter()
-                            .map(|i| state.run_subpass(i, threads, draw_count))
-                            .collect::<Vec<_>>();
+                    let buffers = (0..threads)
+                        .into_par_iter()
+                        .map(|i| state.run_subpass(i, threads, draw_count))
+                        .collect::<Vec<_>>();
 
-                        duration += start.elapsed();
+                    duration += start.elapsed();
 
-                        state.device_state.queue.submit(buffers);
-                        state.device_state.device.poll(wgpu::Maintain::Wait);
-                    }
+                    state.device_state.queue.submit(buffers);
+                    state.device_state.device.poll(wgpu::Maintain::Wait);
+                }
 
-                    duration
-                })
-            },
-        );
+                duration
+            })
+        });
     }
     group.finish();
 
@@ -543,7 +540,7 @@ fn run_bench(ctx: &mut Criterion) {
     let mut group = ctx.benchmark_group("Renderpass: Bindless");
     group.throughput(Throughput::Elements(draw_count as _));
 
-    group.bench_function(&format!("{draw_count} draws"), |b| {
+    group.bench_function(format!("{draw_count} draws"), |b| {
         Lazy::force(&state);
 
         b.iter_custom(|iters| {
diff --git a/benches/benches/resource_creation.rs b/benches/benches/resource_creation.rs
index c23f132bb..da0c79a40 100644
--- a/benches/benches/resource_creation.rs
+++ b/benches/benches/resource_creation.rs
@@ -17,7 +17,7 @@ fn run_bench(ctx: &mut Criterion) {
     for threads in [1, 2, 4, 8] {
         let resources_per_thread = RESOURCES_TO_CREATE / threads;
         group.bench_function(
-            &format!("{threads} threads x {resources_per_thread} resource"),
+            format!("{threads} threads x {resources_per_thread} resource"),
             |b| {
                 Lazy::force(&state);
 
diff --git a/deno_webgpu/bundle.rs b/deno_webgpu/bundle.rs
index 0d1421d20..58d179051 100644
--- a/deno_webgpu/bundle.rs
+++ b/deno_webgpu/bundle.rs
@@ -150,7 +150,7 @@ pub fn op_webgpu_render_bundle_encoder_set_bind_group(
         wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
             &mut render_bundle_encoder_resource.0.borrow_mut(),
             index,
-            bind_group_resource.1,
+            Some(bind_group_resource.1),
             dynamic_offsets_data.as_ptr(),
             dynamic_offsets_data.len(),
         );
diff --git a/deno_webgpu/compute_pass.rs b/deno_webgpu/compute_pass.rs
index e3e69860a..6755e84a6 100644
--- a/deno_webgpu/compute_pass.rs
+++ b/deno_webgpu/compute_pass.rs
@@ -136,7 +136,7 @@ pub fn op_webgpu_compute_pass_set_bind_group(
         .compute_pass_set_bind_group(
             &mut compute_pass_resource.0.borrow_mut(),
             index,
-            bind_group_resource.1,
+            Some(bind_group_resource.1),
             dynamic_offsets_data,
         )?;
 
diff --git a/deno_webgpu/render_pass.rs b/deno_webgpu/render_pass.rs
index 2d4557cf0..4929fbbe9 100644
--- a/deno_webgpu/render_pass.rs
+++ b/deno_webgpu/render_pass.rs
@@ -231,7 +231,7 @@ pub fn op_webgpu_render_pass_set_bind_group(
         .render_pass_set_bind_group(
             &mut render_pass_resource.0.borrow_mut(),
             index,
-            bind_group_resource.1,
+            Some(bind_group_resource.1),
             dynamic_offsets_data,
         )?;
 
diff --git a/examples/src/boids/mod.rs b/examples/src/boids/mod.rs
index f7875ed76..c527be96d 100644
--- a/examples/src/boids/mod.rs
+++ b/examples/src/boids/mod.rs
@@ -298,7 +298,7 @@ impl crate::framework::Example for Example {
                 timestamp_writes: None,
             });
             cpass.set_pipeline(&self.compute_pipeline);
-            cpass.set_bind_group(0, &self.particle_bind_groups[self.frame_num % 2], &[]);
+            cpass.set_bind_group(0, Some(&self.particle_bind_groups[self.frame_num % 2]), &[]);
             cpass.dispatch_workgroups(self.work_group_count, 1, 1);
         }
         command_encoder.pop_debug_group();
diff --git a/examples/src/bunnymark/mod.rs b/examples/src/bunnymark/mod.rs
index 54bdc2a94..0c242c713 100644
--- a/examples/src/bunnymark/mod.rs
+++ b/examples/src/bunnymark/mod.rs
@@ -34,19 +34,29 @@ impl Bunny {
         self.position[0] += self.velocity[0] * delta;
         self.position[1] += self.velocity[1] * delta;
         self.velocity[1] += GRAVITY * delta;
+
         if (self.velocity[0] > 0.0 && self.position[0] + 0.5 * BUNNY_SIZE > extent[0] as f32)
             || (self.velocity[0] < 0.0 && self.position[0] - 0.5 * BUNNY_SIZE < 0.0)
         {
             self.velocity[0] *= -1.0;
         }
+
         if self.velocity[1] < 0.0 && self.position[1] < 0.5 * BUNNY_SIZE {
             self.velocity[1] *= -1.0;
         }
+
+        // Top boundary check
+        if self.velocity[1] > 0.0 && self.position[1] + 0.5 * BUNNY_SIZE > extent[1] as f32 {
+            self.velocity[1] *= -1.0;
+        }
     }
 }
 
 /// Example struct holds references to wgpu resources and frame persistent data
 struct Example {
+    view: wgpu::TextureView,
+    sampler: wgpu::Sampler,
+    global_bind_group_layout: wgpu::BindGroupLayout,
     global_group: wgpu::BindGroup,
     local_group: wgpu::BindGroup,
     pipeline: wgpu::RenderPipeline,
@@ -118,11 +128,11 @@ impl Example {
                 occlusion_query_set: None,
             });
             rpass.set_pipeline(&self.pipeline);
-            rpass.set_bind_group(0, &self.global_group, &[]);
+            rpass.set_bind_group(0, Some(&self.global_group), &[]);
             for i in 0..self.bunnies.len() {
                 let offset =
                     (i as wgpu::DynamicOffset) * (uniform_alignment as wgpu::DynamicOffset);
-                rpass.set_bind_group(1, &self.local_group, &[offset]);
+                rpass.set_bind_group(1, Some(&self.local_group), &[offset]);
                 rpass.draw(0..4, 0..1);
             }
         }
@@ -286,6 +296,7 @@ impl crate::framework::Example for Example {
             size: [BUNNY_SIZE; 2],
             pad: [0.0; 2],
         };
+
         let global_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
             label: Some("global"),
             contents: bytemuck::bytes_of(&globals),
@@ -335,6 +346,9 @@ impl crate::framework::Example for Example {
         let rng = WyRand::new_seed(42);
 
         let mut ex = Example {
+            view,
+            sampler,
+            global_bind_group_layout,
             pipeline,
             global_group,
             local_group,
@@ -366,11 +380,51 @@ impl crate::framework::Example for Example {
 
     fn resize(
         &mut self,
-        _sc_desc: &wgpu::SurfaceConfiguration,
-        _device: &wgpu::Device,
+        sc_desc: &wgpu::SurfaceConfiguration,
+        device: &wgpu::Device,
         _queue: &wgpu::Queue,
     ) {
-        //empty
+        self.extent = [sc_desc.width, sc_desc.height];
+
+        let globals = Globals {
+            mvp: glam::Mat4::orthographic_rh(
+                0.0,
+                sc_desc.width as f32,
+                0.0,
+                sc_desc.height as f32,
+                -1.0,
+                1.0,
+            )
+            .to_cols_array_2d(),
+            size: [BUNNY_SIZE; 2],
+            pad: [0.0; 2],
+        };
+
+        let global_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+            label: Some("global"),
+            contents: bytemuck::bytes_of(&globals),
+            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
+        });
+
+        let global_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+            layout: &self.global_bind_group_layout,
+            entries: &[
+                wgpu::BindGroupEntry {
+                    binding: 0,
+                    resource: global_buffer.as_entire_binding(),
+                },
+                wgpu::BindGroupEntry {
+                    binding: 1,
+                    resource: wgpu::BindingResource::TextureView(&self.view),
+                },
+                wgpu::BindGroupEntry {
+                    binding: 2,
+                    resource: wgpu::BindingResource::Sampler(&self.sampler),
+                },
+            ],
+            label: None,
+        });
+        self.global_group = global_group;
     }
 
     fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
diff --git a/examples/src/conservative_raster/mod.rs b/examples/src/conservative_raster/mod.rs
index d02913475..46fb8742a 100644
--- a/examples/src/conservative_raster/mod.rs
+++ b/examples/src/conservative_raster/mod.rs
@@ -305,7 +305,7 @@ impl crate::framework::Example for Example {
             });
 
             rpass.set_pipeline(&self.pipeline_upscale);
-            rpass.set_bind_group(0, &self.bind_group_upscale, &[]);
+            rpass.set_bind_group(0, Some(&self.bind_group_upscale), &[]);
             rpass.draw(0..3, 0..1);
 
             if let Some(pipeline_lines) = &self.pipeline_lines {
diff --git a/examples/src/cube/mod.rs b/examples/src/cube/mod.rs
index 00e2ffcfc..78dc06e06 100644
--- a/examples/src/cube/mod.rs
+++ b/examples/src/cube/mod.rs
@@ -361,7 +361,7 @@ impl crate::framework::Example for Example {
             });
             rpass.push_debug_group("Prepare data for draw.");
             rpass.set_pipeline(&self.pipeline);
-            rpass.set_bind_group(0, &self.bind_group, &[]);
+            rpass.set_bind_group(0, Some(&self.bind_group), &[]);
             rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint16);
             rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));
             rpass.pop_debug_group();
diff --git a/examples/src/hello_compute/mod.rs b/examples/src/hello_compute/mod.rs
index a5257c07f..e53f49fa4 100644
--- a/examples/src/hello_compute/mod.rs
+++ b/examples/src/hello_compute/mod.rs
@@ -135,7 +135,7 @@ async fn execute_gpu_inner(
             timestamp_writes: None,
         });
         cpass.set_pipeline(&compute_pipeline);
-        cpass.set_bind_group(0, &bind_group, &[]);
+        cpass.set_bind_group(0, Some(&bind_group), &[]);
         cpass.insert_debug_marker("compute collatz iterations");
         cpass.dispatch_workgroups(numbers.len() as u32, 1, 1); // Number of cells to run, the (x,y,z) size of item being processed
     }
diff --git a/examples/src/hello_synchronization/mod.rs b/examples/src/hello_synchronization/mod.rs
index 20743549f..fad5d7a9d 100644
--- a/examples/src/hello_synchronization/mod.rs
+++ b/examples/src/hello_synchronization/mod.rs
@@ -128,7 +128,7 @@ async fn execute(
             timestamp_writes: None,
         });
         compute_pass.set_pipeline(&patient_pipeline);
-        compute_pass.set_bind_group(0, &bind_group, &[]);
+        compute_pass.set_bind_group(0, Some(&bind_group), &[]);
         compute_pass.dispatch_workgroups(local_patient_workgroup_results.len() as u32, 1, 1);
     }
     queue.submit(Some(command_encoder.finish()));
@@ -150,7 +150,7 @@ async fn execute(
             timestamp_writes: None,
         });
         compute_pass.set_pipeline(&hasty_pipeline);
-        compute_pass.set_bind_group(0, &bind_group, &[]);
+        compute_pass.set_bind_group(0, Some(&bind_group), &[]);
         compute_pass.dispatch_workgroups(local_patient_workgroup_results.len() as u32, 1, 1);
     }
     queue.submit(Some(command_encoder.finish()));
diff --git a/examples/src/hello_workgroups/mod.rs b/examples/src/hello_workgroups/mod.rs
index d140bc5b0..7a653cf3e 100644
--- a/examples/src/hello_workgroups/mod.rs
+++ b/examples/src/hello_workgroups/mod.rs
@@ -127,7 +127,7 @@ async fn run() {
             timestamp_writes: None,
         });
         compute_pass.set_pipeline(&pipeline);
-        compute_pass.set_bind_group(0, &bind_group, &[]);
+        compute_pass.set_bind_group(0, Some(&bind_group), &[]);
         /* Note that since each workgroup will cover both arrays, we only need to
         cover the length of one array. */
         compute_pass.dispatch_workgroups(local_a.len() as u32, 1, 1);
diff --git a/examples/src/mipmap/mod.rs b/examples/src/mipmap/mod.rs
index a3f7833b4..179970ad7 100644
--- a/examples/src/mipmap/mod.rs
+++ b/examples/src/mipmap/mod.rs
@@ -180,7 +180,7 @@ impl Example {
                 );
             }
             rpass.set_pipeline(&pipeline);
-            rpass.set_bind_group(0, &bind_group, &[]);
+            rpass.set_bind_group(0, Some(&bind_group), &[]);
             rpass.draw(0..3, 0..1);
             if let Some(ref query_sets) = query_sets {
                 rpass.write_timestamp(&query_sets.timestamp, timestamp_query_index_base + 1);
@@ -497,7 +497,7 @@ impl crate::framework::Example for Example {
                 occlusion_query_set: None,
             });
             rpass.set_pipeline(&self.draw_pipeline);
-            rpass.set_bind_group(0, &self.bind_group, &[]);
+            rpass.set_bind_group(0, Some(&self.bind_group), &[]);
             rpass.draw(0..4, 0..1);
         }
 
diff --git a/examples/src/repeated_compute/mod.rs b/examples/src/repeated_compute/mod.rs
index 5dac9ce7c..83dcd4099 100644
--- a/examples/src/repeated_compute/mod.rs
+++ b/examples/src/repeated_compute/mod.rs
@@ -59,7 +59,7 @@ async fn compute(local_buffer: &mut [u32], context: &WgpuContext) {
             timestamp_writes: None,
         });
         compute_pass.set_pipeline(&context.pipeline);
-        compute_pass.set_bind_group(0, &context.bind_group, &[]);
+        compute_pass.set_bind_group(0, Some(&context.bind_group), &[]);
         compute_pass.dispatch_workgroups(local_buffer.len() as u32, 1, 1);
     }
     // We finish the compute pass by dropping it.
diff --git a/examples/src/shadow/mod.rs b/examples/src/shadow/mod.rs
index 5e3511ed8..a7edcce7e 100644
--- a/examples/src/shadow/mod.rs
+++ b/examples/src/shadow/mod.rs
@@ -779,10 +779,10 @@ impl crate::framework::Example for Example {
                     occlusion_query_set: None,
                 });
                 pass.set_pipeline(&self.shadow_pass.pipeline);
-                pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]);
+                pass.set_bind_group(0, Some(&self.shadow_pass.bind_group), &[]);
 
                 for entity in &self.entities {
-                    pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]);
+                    pass.set_bind_group(1, Some(&self.entity_bind_group), &[entity.uniform_offset]);
                     pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format);
                     pass.set_vertex_buffer(0, entity.vertex_buf.slice(..));
                     pass.draw_indexed(0..entity.index_count as u32, 0, 0..1);
@@ -823,10 +823,10 @@ impl crate::framework::Example for Example {
                 occlusion_query_set: None,
             });
             pass.set_pipeline(&self.forward_pass.pipeline);
-            pass.set_bind_group(0, &self.forward_pass.bind_group, &[]);
+            pass.set_bind_group(0, Some(&self.forward_pass.bind_group), &[]);
 
             for entity in &self.entities {
-                pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]);
+                pass.set_bind_group(1, Some(&self.entity_bind_group), &[entity.uniform_offset]);
                 pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format);
                 pass.set_vertex_buffer(0, entity.vertex_buf.slice(..));
                 pass.draw_indexed(0..entity.index_count as u32, 0, 0..1);
diff --git a/examples/src/skybox/mod.rs b/examples/src/skybox/mod.rs
index 7397d8846..82e58ef6d 100644
--- a/examples/src/skybox/mod.rs
+++ b/examples/src/skybox/mod.rs
@@ -451,7 +451,7 @@ impl crate::framework::Example for Example {
                 occlusion_query_set: None,
             });
 
-            rpass.set_bind_group(0, &self.bind_group, &[]);
+            rpass.set_bind_group(0, Some(&self.bind_group), &[]);
             rpass.set_pipeline(&self.entity_pipeline);
 
             for entity in self.entities.iter() {
diff --git a/examples/src/srgb_blend/mod.rs b/examples/src/srgb_blend/mod.rs
index 63e5e79cb..822d95d3c 100644
--- a/examples/src/srgb_blend/mod.rs
+++ b/examples/src/srgb_blend/mod.rs
@@ -202,7 +202,7 @@ impl<const SRGB: bool> crate::framework::Example for Example<SRGB> {
             });
             rpass.push_debug_group("Prepare data for draw.");
             rpass.set_pipeline(&self.pipeline);
-            rpass.set_bind_group(0, &self.bind_group, &[]);
+            rpass.set_bind_group(0, Some(&self.bind_group), &[]);
             rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint16);
             rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));
             rpass.pop_debug_group();
diff --git a/examples/src/storage_texture/mod.rs b/examples/src/storage_texture/mod.rs
index 904a721f0..a68758419 100644
--- a/examples/src/storage_texture/mod.rs
+++ b/examples/src/storage_texture/mod.rs
@@ -117,7 +117,7 @@ async fn run(_path: Option<String>) {
             label: None,
             timestamp_writes: None,
         });
-        compute_pass.set_bind_group(0, &bind_group, &[]);
+        compute_pass.set_bind_group(0, Some(&bind_group), &[]);
         compute_pass.set_pipeline(&pipeline);
         compute_pass.dispatch_workgroups(TEXTURE_DIMS.0 as u32, TEXTURE_DIMS.1 as u32, 1);
     }
diff --git a/examples/src/texture_arrays/mod.rs b/examples/src/texture_arrays/mod.rs
index 00b90603f..8c81950e9 100644
--- a/examples/src/texture_arrays/mod.rs
+++ b/examples/src/texture_arrays/mod.rs
@@ -391,12 +391,12 @@ impl crate::framework::Example for Example {
         rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
         rpass.set_index_buffer(self.index_buffer.slice(..), self.index_format);
         if self.uniform_workaround {
-            rpass.set_bind_group(0, &self.bind_group, &[0]);
+            rpass.set_bind_group(0, Some(&self.bind_group), &[0]);
             rpass.draw_indexed(0..6, 0, 0..1);
-            rpass.set_bind_group(0, &self.bind_group, &[256]);
+            rpass.set_bind_group(0, Some(&self.bind_group), &[256]);
             rpass.draw_indexed(6..12, 0, 0..1);
         } else {
-            rpass.set_bind_group(0, &self.bind_group, &[0]);
+            rpass.set_bind_group(0, Some(&self.bind_group), &[0]);
             rpass.draw_indexed(0..12, 0, 0..1);
         }
 
diff --git a/examples/src/timestamp_queries/mod.rs b/examples/src/timestamp_queries/mod.rs
index 0daa71c0e..2921ae4c8 100644
--- a/examples/src/timestamp_queries/mod.rs
+++ b/examples/src/timestamp_queries/mod.rs
@@ -324,7 +324,7 @@ fn compute_pass(
     });
     *next_unused_query += 2;
     cpass.set_pipeline(&compute_pipeline);
-    cpass.set_bind_group(0, &bind_group, &[]);
+    cpass.set_bind_group(0, Some(&bind_group), &[]);
     cpass.dispatch_workgroups(1, 1, 1);
     if device
         .features()
diff --git a/examples/src/uniform_values/mod.rs b/examples/src/uniform_values/mod.rs
index 770fcd1aa..1ef58de09 100644
--- a/examples/src/uniform_values/mod.rs
+++ b/examples/src/uniform_values/mod.rs
@@ -327,7 +327,11 @@ async fn run(event_loop: EventLoop<()>, window: Arc<Window>) {
                                     });
                                 render_pass.set_pipeline(&wgpu_context_ref.pipeline);
                                 // (9)
-                                render_pass.set_bind_group(0, &wgpu_context_ref.bind_group, &[]);
+                                render_pass.set_bind_group(
+                                    0,
+                                    Some(&wgpu_context_ref.bind_group),
+                                    &[],
+                                );
                                 render_pass.draw(0..3, 0..1);
                             }
                             wgpu_context_ref.queue.submit(Some(encoder.finish()));
diff --git a/examples/src/water/mod.rs b/examples/src/water/mod.rs
index 7f766034a..505f5707e 100644
--- a/examples/src/water/mod.rs
+++ b/examples/src/water/mod.rs
@@ -630,7 +630,7 @@ impl crate::framework::Example for Example {
                     multiview: None,
                 });
             encoder.set_pipeline(&terrain_pipeline);
-            encoder.set_bind_group(0, &terrain_flipped_bind_group, &[]);
+            encoder.set_bind_group(0, Some(&terrain_flipped_bind_group), &[]);
             encoder.set_vertex_buffer(0, terrain_vertex_buf.slice(..));
             encoder.draw(0..terrain_vertices.len() as u32, 0..1);
             encoder.finish(&wgpu::RenderBundleDescriptor::default())
@@ -784,7 +784,7 @@ impl crate::framework::Example for Example {
                 occlusion_query_set: None,
             });
             rpass.set_pipeline(&self.terrain_pipeline);
-            rpass.set_bind_group(0, &self.terrain_normal_bind_group, &[]);
+            rpass.set_bind_group(0, Some(&self.terrain_normal_bind_group), &[]);
             rpass.set_vertex_buffer(0, self.terrain_vertex_buf.slice(..));
             rpass.draw(0..self.terrain_vertex_count as u32, 0..1);
         }
@@ -811,7 +811,7 @@ impl crate::framework::Example for Example {
             });
 
             rpass.set_pipeline(&self.water_pipeline);
-            rpass.set_bind_group(0, &self.water_bind_group, &[]);
+            rpass.set_bind_group(0, Some(&self.water_bind_group), &[]);
             rpass.set_vertex_buffer(0, self.water_vertex_buf.slice(..));
             rpass.draw(0..self.water_vertex_count as u32, 0..1);
         }
diff --git a/naga/src/back/glsl/features.rs b/naga/src/back/glsl/features.rs
index b22bcbe65..362f4bb4f 100644
--- a/naga/src/back/glsl/features.rs
+++ b/naga/src/back/glsl/features.rs
@@ -399,7 +399,7 @@ impl<'a, W> Writer<'a, W> {
                             | StorageFormat::Rg16Float
                             | StorageFormat::Rgb10a2Uint
                             | StorageFormat::Rgb10a2Unorm
-                            | StorageFormat::Rg11b10UFloat
+                            | StorageFormat::Rg11b10Ufloat
                             | StorageFormat::Rg32Uint
                             | StorageFormat::Rg32Sint
                             | StorageFormat::Rg32Float => {
diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs
index ec05f2e47..159d9cdcf 100644
--- a/naga/src/back/glsl/mod.rs
+++ b/naga/src/back/glsl/mod.rs
@@ -4825,7 +4825,7 @@ fn glsl_storage_format(format: crate::StorageFormat) -> Result<&'static str, Err
         Sf::Rgba8Sint => "rgba8i",
         Sf::Rgb10a2Uint => "rgb10_a2ui",
         Sf::Rgb10a2Unorm => "rgb10_a2",
-        Sf::Rg11b10UFloat => "r11f_g11f_b10f",
+        Sf::Rg11b10Ufloat => "r11f_g11f_b10f",
         Sf::Rg32Uint => "rg32ui",
         Sf::Rg32Sint => "rg32i",
         Sf::Rg32Float => "rg32f",
diff --git a/naga/src/back/hlsl/conv.rs b/naga/src/back/hlsl/conv.rs
index e2ddd399d..473fc9476 100644
--- a/naga/src/back/hlsl/conv.rs
+++ b/naga/src/back/hlsl/conv.rs
@@ -132,7 +132,7 @@ impl crate::StorageFormat {
             Self::Rg8Sint | Self::Rg16Sint | Self::Rg32Uint => "int2",
             Self::Rg8Uint | Self::Rg16Uint | Self::Rg32Sint => "uint2",
 
-            Self::Rg11b10UFloat => "float3",
+            Self::Rg11b10Ufloat => "float3",
 
             Self::Rgba16Float | Self::Rgba32Float => "float4",
             Self::Rgba8Unorm | Self::Bgra8Unorm | Self::Rgba16Unorm | Self::Rgb10a2Unorm => {
diff --git a/naga/src/back/spv/instructions.rs b/naga/src/back/spv/instructions.rs
index 9029c973d..9bd58508a 100644
--- a/naga/src/back/spv/instructions.rs
+++ b/naga/src/back/spv/instructions.rs
@@ -1170,7 +1170,7 @@ impl From<crate::StorageFormat> for spirv::ImageFormat {
             Sf::Bgra8Unorm => Self::Unknown,
             Sf::Rgb10a2Uint => Self::Rgb10a2ui,
             Sf::Rgb10a2Unorm => Self::Rgb10A2,
-            Sf::Rg11b10UFloat => Self::R11fG11fB10f,
+            Sf::Rg11b10Ufloat => Self::R11fG11fB10f,
             Sf::Rg32Uint => Self::Rg32ui,
             Sf::Rg32Sint => Self::Rg32i,
             Sf::Rg32Float => Self::Rg32f,
diff --git a/naga/src/back/wgsl/writer.rs b/naga/src/back/wgsl/writer.rs
index 3070bd852..0f2635eb0 100644
--- a/naga/src/back/wgsl/writer.rs
+++ b/naga/src/back/wgsl/writer.rs
@@ -2023,7 +2023,7 @@ const fn storage_format_str(format: crate::StorageFormat) -> &'static str {
         Sf::Bgra8Unorm => "bgra8unorm",
         Sf::Rgb10a2Uint => "rgb10a2uint",
         Sf::Rgb10a2Unorm => "rgb10a2unorm",
-        Sf::Rg11b10UFloat => "rg11b10float",
+        Sf::Rg11b10Ufloat => "rg11b10float",
         Sf::Rg32Uint => "rg32uint",
         Sf::Rg32Sint => "rg32sint",
         Sf::Rg32Float => "rg32float",
diff --git a/naga/src/front/glsl/parser/types.rs b/naga/src/front/glsl/parser/types.rs
index d22387f37..829b9fd89 100644
--- a/naga/src/front/glsl/parser/types.rs
+++ b/naga/src/front/glsl/parser/types.rs
@@ -397,7 +397,7 @@ fn map_image_format(word: &str) -> Option<crate::StorageFormat> {
         "rgba16f" => Sf::Rgba16Float,
         "rg32f" => Sf::Rg32Float,
         "rg16f" => Sf::Rg16Float,
-        "r11f_g11f_b10f" => Sf::Rg11b10UFloat,
+        "r11f_g11f_b10f" => Sf::Rg11b10Ufloat,
         "r32f" => Sf::R32Float,
         "r16f" => Sf::R16Float,
         "rgba16" => Sf::Rgba16Unorm,
diff --git a/naga/src/front/spv/convert.rs b/naga/src/front/spv/convert.rs
index 88d171b5b..f131db616 100644
--- a/naga/src/front/spv/convert.rs
+++ b/naga/src/front/spv/convert.rs
@@ -104,7 +104,7 @@ pub(super) fn map_image_format(word: spirv::Word) -> Result<crate::StorageFormat
         Some(spirv::ImageFormat::Rgba8i) => Ok(crate::StorageFormat::Rgba8Sint),
         Some(spirv::ImageFormat::Rgb10a2ui) => Ok(crate::StorageFormat::Rgb10a2Uint),
         Some(spirv::ImageFormat::Rgb10A2) => Ok(crate::StorageFormat::Rgb10a2Unorm),
-        Some(spirv::ImageFormat::R11fG11fB10f) => Ok(crate::StorageFormat::Rg11b10UFloat),
+        Some(spirv::ImageFormat::R11fG11fB10f) => Ok(crate::StorageFormat::Rg11b10Ufloat),
         Some(spirv::ImageFormat::Rg32ui) => Ok(crate::StorageFormat::Rg32Uint),
         Some(spirv::ImageFormat::Rg32i) => Ok(crate::StorageFormat::Rg32Sint),
         Some(spirv::ImageFormat::Rg32f) => Ok(crate::StorageFormat::Rg32Float),
diff --git a/naga/src/front/wgsl/lower/mod.rs b/naga/src/front/wgsl/lower/mod.rs
index 5731775fa..7586f98dc 100644
--- a/naga/src/front/wgsl/lower/mod.rs
+++ b/naga/src/front/wgsl/lower/mod.rs
@@ -286,7 +286,7 @@ pub enum ExpressionContextType<'temp, 'out> {
     /// We are lowering to an arbitrary runtime expression, to be
     /// included in a function's body.
     ///
-    /// The given [`RuntimeExpressionContext`] holds information about local
+    /// The given [`LocalExpressionContext`] holds information about local
     /// variables, arguments, and other definitions available only to runtime
     /// expressions, not constant or override expressions.
     Runtime(LocalExpressionContext<'temp, 'out>),
diff --git a/naga/src/front/wgsl/parse/ast.rs b/naga/src/front/wgsl/parse/ast.rs
index a133eacec..c4a798411 100644
--- a/naga/src/front/wgsl/parse/ast.rs
+++ b/naga/src/front/wgsl/parse/ast.rs
@@ -484,5 +484,5 @@ pub enum LocalDecl<'a> {
 #[derive(Debug)]
 /// A placeholder for a local variable declaration.
 ///
-/// See [`Function::locals`] for more information.
+/// See [`super::ExpressionContext::locals`] for more information.
 pub struct Local;
diff --git a/naga/src/front/wgsl/parse/conv.rs b/naga/src/front/wgsl/parse/conv.rs
index c5d31d07c..3ba71b07c 100644
--- a/naga/src/front/wgsl/parse/conv.rs
+++ b/naga/src/front/wgsl/parse/conv.rs
@@ -94,7 +94,7 @@ pub fn map_storage_format(word: &str, span: Span) -> Result<crate::StorageFormat
         "rgba8sint" => Sf::Rgba8Sint,
         "rgb10a2uint" => Sf::Rgb10a2Uint,
         "rgb10a2unorm" => Sf::Rgb10a2Unorm,
-        "rg11b10float" => Sf::Rg11b10UFloat,
+        "rg11b10float" => Sf::Rg11b10Ufloat,
         "rg32uint" => Sf::Rg32Uint,
         "rg32sint" => Sf::Rg32Sint,
         "rg32float" => Sf::Rg32Float,
diff --git a/naga/src/front/wgsl/parse/mod.rs b/naga/src/front/wgsl/parse/mod.rs
index e5bd0fa7d..0157aa3a7 100644
--- a/naga/src/front/wgsl/parse/mod.rs
+++ b/naga/src/front/wgsl/parse/mod.rs
@@ -31,20 +31,20 @@ struct ExpressionContext<'input, 'temp, 'out> {
 
     /// A map from identifiers in scope to the locals/arguments they represent.
     ///
-    /// The handles refer to the [`Function::locals`] area; see that field's
+    /// The handles refer to the [`locals`] arena; see that field's
     /// documentation for details.
     ///
-    /// [`Function::locals`]: ast::Function::locals
+    /// [`locals`]: ExpressionContext::locals
     local_table: &'temp mut SymbolTable<&'input str, Handle<ast::Local>>,
 
     /// Local variable and function argument arena for the function we're building.
     ///
-    /// Note that the `Local` here is actually a zero-sized type. The AST keeps
-    /// all the detailed information about locals - names, types, etc. - in
-    /// [`LocalDecl`] statements. For arguments, that information is kept in
-    /// [`arguments`]. This `Arena`'s only role is to assign a unique `Handle`
-    /// to each of them, and track their definitions' spans for use in
-    /// diagnostics.
+    /// Note that the [`ast::Local`] here is actually a zero-sized type. This
+    /// `Arena`'s only role is to assign a unique `Handle` to each local
+    /// identifier, and track its definition's span for use in diagnostics. All
+    /// the detailed information about locals - names, types, etc. - is kept in
+    /// the [`LocalDecl`] statements we parsed from their declarations. For
+    /// arguments, that information is kept in [`arguments`].
     ///
     /// In the AST, when an [`Ident`] expression refers to a local variable or
     /// argument, its [`IdentExpr`] holds the referent's `Handle<Local>` in this
@@ -53,14 +53,15 @@ struct ExpressionContext<'input, 'temp, 'out> {
     /// During lowering, [`LocalDecl`] statements add entries to a per-function
     /// table that maps `Handle<Local>` values to their Naga representations,
     /// accessed via [`StatementContext::local_table`] and
-    /// [`RuntimeExpressionContext::local_table`]. This table is then consulted when
+    /// [`LocalExpressionContext::local_table`]. This table is then consulted when
     /// lowering subsequent [`Ident`] expressions.
     ///
-    /// [`LocalDecl`]: StatementKind::LocalDecl
-    /// [`arguments`]: Function::arguments
-    /// [`Ident`]: Expression::Ident
-    /// [`StatementContext::local_table`]: StatementContext::local_table
-    /// [`RuntimeExpressionContext::local_table`]: RuntimeExpressionContext::local_table
+    /// [`LocalDecl`]: ast::StatementKind::LocalDecl
+    /// [`arguments`]: ast::Function::arguments
+    /// [`Ident`]: ast::Expression::Ident
+    /// [`IdentExpr`]: ast::IdentExpr
+    /// [`StatementContext::local_table`]: super::lower::StatementContext::local_table
+    /// [`LocalExpressionContext::local_table`]: super::lower::LocalExpressionContext::local_table
     locals: &'out mut Arena<ast::Local>,
 
     /// Identifiers used by the current global declaration that have no local definition.
diff --git a/naga/src/front/wgsl/to_wgsl.rs b/naga/src/front/wgsl/to_wgsl.rs
index ec3af8edd..0884e0003 100644
--- a/naga/src/front/wgsl/to_wgsl.rs
+++ b/naga/src/front/wgsl/to_wgsl.rs
@@ -175,7 +175,7 @@ impl crate::StorageFormat {
             Sf::Bgra8Unorm => "bgra8unorm",
             Sf::Rgb10a2Uint => "rgb10a2uint",
             Sf::Rgb10a2Unorm => "rgb10a2unorm",
-            Sf::Rg11b10UFloat => "rg11b10float",
+            Sf::Rg11b10Ufloat => "rg11b10float",
             Sf::Rg32Uint => "rg32uint",
             Sf::Rg32Sint => "rg32sint",
             Sf::Rg32Float => "rg32float",
diff --git a/naga/src/lib.rs b/naga/src/lib.rs
index a8676a1d3..85fd7a450 100644
--- a/naga/src/lib.rs
+++ b/naga/src/lib.rs
@@ -622,7 +622,7 @@ pub enum StorageFormat {
     // Packed 32-bit formats
     Rgb10a2Uint,
     Rgb10a2Unorm,
-    Rg11b10UFloat,
+    Rg11b10Ufloat,
 
     // 64-bit formats
     Rg32Uint,
diff --git a/naga/src/proc/mod.rs b/naga/src/proc/mod.rs
index ef771d878..a5b3ea4e3 100644
--- a/naga/src/proc/mod.rs
+++ b/naga/src/proc/mod.rs
@@ -48,7 +48,7 @@ impl From<super::StorageFormat> for super::ScalarKind {
             Sf::Bgra8Unorm => Sk::Float,
             Sf::Rgb10a2Uint => Sk::Uint,
             Sf::Rgb10a2Unorm => Sk::Float,
-            Sf::Rg11b10UFloat => Sk::Float,
+            Sf::Rg11b10Ufloat => Sk::Float,
             Sf::Rg32Uint => Sk::Uint,
             Sf::Rg32Sint => Sk::Sint,
             Sf::Rg32Float => Sk::Float,
diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs
index a7a43e1e2..775bd5f82 100644
--- a/wgpu-core/src/command/bundle.rs
+++ b/wgpu-core/src/command/bundle.rs
@@ -582,8 +582,15 @@ fn set_bind_group(
     dynamic_offsets: &[u32],
     index: u32,
     num_dynamic_offsets: usize,
-    bind_group_id: id::Id<id::markers::BindGroup>,
+    bind_group_id: Option<id::Id<id::markers::BindGroup>>,
 ) -> Result<(), RenderBundleErrorInner> {
+    if bind_group_id.is_none() {
+        // TODO: do appropriate cleanup for null bind_group.
+        return Ok(());
+    }
+
+    let bind_group_id = bind_group_id.unwrap();
+
     let bind_group = bind_group_guard
         .get_owned(bind_group_id)
         .map_err(|_| RenderCommandError::InvalidBindGroupId(bind_group_id))?;
@@ -981,12 +988,17 @@ impl RenderBundle {
                     num_dynamic_offsets,
                     bind_group,
                 } => {
-                    let raw_bg = bind_group.try_raw(snatch_guard)?;
+                    let mut bg = None;
+                    if bind_group.is_some() {
+                        let bind_group = bind_group.as_ref().unwrap();
+                        let raw_bg = bind_group.try_raw(snatch_guard)?;
+                        bg = Some(raw_bg);
+                    }
                     unsafe {
                         raw.set_bind_group(
                             pipeline_layout.as_ref().unwrap().raw(),
                             *index,
-                            raw_bg,
+                            bg,
                             &offsets[..*num_dynamic_offsets],
                         )
                     };
@@ -1501,7 +1513,7 @@ impl State {
                         let offsets = &contents.dynamic_offsets;
                         return Some(ArcRenderCommand::SetBindGroup {
                             index: i.try_into().unwrap(),
-                            bind_group: contents.bind_group.clone(),
+                            bind_group: Some(contents.bind_group.clone()),
                             num_dynamic_offsets: offsets.end - offsets.start,
                         });
                     }
@@ -1581,7 +1593,7 @@ pub mod bundle_ffi {
     pub unsafe extern "C" fn wgpu_render_bundle_set_bind_group(
         bundle: &mut RenderBundleEncoder,
         index: u32,
-        bind_group_id: id::BindGroupId,
+        bind_group_id: Option<id::BindGroupId>,
         offsets: *const DynamicOffset,
         offset_length: usize,
     ) {
diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs
index eb929740c..2a258f1ef 100644
--- a/wgpu-core/src/command/compute.rs
+++ b/wgpu-core/src/command/compute.rs
@@ -649,10 +649,8 @@ fn set_bind_group(
     dynamic_offsets: &[DynamicOffset],
     index: u32,
     num_dynamic_offsets: usize,
-    bind_group: Arc<BindGroup>,
+    bind_group: Option<Arc<BindGroup>>,
 ) -> Result<(), ComputePassErrorInner> {
-    bind_group.same_device_as(cmd_buf)?;
-
     let max_bind_groups = state.device.limits.max_bind_groups;
     if index >= max_bind_groups {
         return Err(ComputePassErrorInner::BindGroupIndexOutOfRange {
@@ -668,7 +666,16 @@ fn set_bind_group(
     );
     state.dynamic_offset_count += num_dynamic_offsets;
 
+    if bind_group.is_none() {
+        // TODO: Handle bind_group None.
+        return Ok(());
+    }
+
+    let bind_group = bind_group.unwrap();
     let bind_group = state.tracker.bind_groups.insert_single(bind_group);
+
+    bind_group.same_device_as(cmd_buf)?;
+
     bind_group.validate_dynamic_bindings(index, &state.temp_offsets)?;
 
     state
@@ -700,7 +707,7 @@ fn set_bind_group(
                     state.raw_encoder.set_bind_group(
                         pipeline_layout,
                         index + i as u32,
-                        raw_bg,
+                        Some(raw_bg),
                         &e.dynamic_offsets,
                     );
                 }
@@ -745,7 +752,7 @@ fn set_pipeline(
                         state.raw_encoder.set_bind_group(
                             pipeline.layout.raw(),
                             start_index as u32 + i as u32,
-                            raw_bg,
+                            Some(raw_bg),
                             &e.dynamic_offsets,
                         );
                     }
@@ -952,7 +959,7 @@ impl Global {
         &self,
         pass: &mut ComputePass,
         index: u32,
-        bind_group_id: id::BindGroupId,
+        bind_group_id: Option<id::BindGroupId>,
         offsets: &[DynamicOffset],
     ) -> Result<(), ComputePassError> {
         let scope = PassErrorScope::SetBindGroup;
@@ -973,12 +980,18 @@ impl Global {
             return Ok(());
         }
 
-        let hub = &self.hub;
-        let bind_group = hub
-            .bind_groups
-            .get(bind_group_id)
-            .map_err(|_| ComputePassErrorInner::InvalidBindGroupId(bind_group_id))
-            .map_pass_err(scope)?;
+        let mut bind_group = None;
+        if bind_group_id.is_some() {
+            let bind_group_id = bind_group_id.unwrap();
+
+            let hub = &self.hub;
+            let bg = hub
+                .bind_groups
+                .get(bind_group_id)
+                .map_err(|_| ComputePassErrorInner::InvalidBindGroupId(bind_group_id))
+                .map_pass_err(scope)?;
+            bind_group = Some(bg);
+        }
 
         base.commands.push(ArcComputeCommand::SetBindGroup {
             index,
diff --git a/wgpu-core/src/command/compute_command.rs b/wgpu-core/src/command/compute_command.rs
index e16487b7e..6596b8e6a 100644
--- a/wgpu-core/src/command/compute_command.rs
+++ b/wgpu-core/src/command/compute_command.rs
@@ -13,7 +13,7 @@ pub enum ComputeCommand {
     SetBindGroup {
         index: u32,
         num_dynamic_offsets: usize,
-        bind_group_id: id::BindGroupId,
+        bind_group_id: Option<id::BindGroupId>,
     },
 
     SetPipeline(id::ComputePipelineId),
@@ -89,16 +89,29 @@ impl ComputeCommand {
                         index,
                         num_dynamic_offsets,
                         bind_group_id,
-                    } => ArcComputeCommand::SetBindGroup {
-                        index,
-                        num_dynamic_offsets,
-                        bind_group: bind_group_guard.get_owned(bind_group_id).map_err(|_| {
+                    } => {
+                        if bind_group_id.is_none() {
+                            return Ok(ArcComputeCommand::SetBindGroup {
+                                index,
+                                num_dynamic_offsets,
+                                bind_group: None,
+                            });
+                        }
+
+                        let bind_group_id = bind_group_id.unwrap();
+                        let bg = bind_group_guard.get_owned(bind_group_id).map_err(|_| {
                             ComputePassError {
                                 scope: PassErrorScope::SetBindGroup,
                                 inner: ComputePassErrorInner::InvalidBindGroupId(bind_group_id),
                             }
-                        })?,
-                    },
+                        })?;
+
+                        ArcComputeCommand::SetBindGroup {
+                            index,
+                            num_dynamic_offsets,
+                            bind_group: Some(bg),
+                        }
+                    }
 
                     ComputeCommand::SetPipeline(pipeline_id) => ArcComputeCommand::SetPipeline(
                         pipelines_guard
@@ -185,7 +198,7 @@ pub enum ArcComputeCommand {
     SetBindGroup {
         index: u32,
         num_dynamic_offsets: usize,
-        bind_group: Arc<BindGroup>,
+        bind_group: Option<Arc<BindGroup>>,
     },
 
     SetPipeline(Arc<ComputePipeline>),
@@ -215,6 +228,7 @@ pub enum ArcComputeCommand {
     },
 
     PushDebugGroup {
+        #[cfg_attr(target_os = "emscripten", allow(dead_code))]
         color: u32,
         len: usize,
     },
@@ -222,6 +236,7 @@ pub enum ArcComputeCommand {
     PopDebugGroup,
 
     InsertDebugMarker {
+        #[cfg_attr(target_os = "emscripten", allow(dead_code))]
         color: u32,
         len: usize,
     },
diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs
index 313bf813a..14b7892d0 100644
--- a/wgpu-core/src/command/mod.rs
+++ b/wgpu-core/src/command/mod.rs
@@ -82,8 +82,8 @@ pub(crate) enum CommandEncoderStatus {
     ///
     /// When a `CommandEncoder` is left in this state, we have also
     /// returned an error result from the function that encountered
-    /// the problem. Future attempts to use the encoder (that is,
-    /// calls to [`CommandBuffer::get_encoder`]) will also return
+    /// the problem. Future attempts to use the encoder (for example,
+    /// calls to [`CommandBuffer::check_recording`]) will also return
     /// errors.
     ///
     /// Calling [`Global::command_encoder_finish`] in this state
@@ -805,7 +805,7 @@ impl<T: Copy + PartialEq> Default for StateChange<T> {
 
 #[derive(Debug)]
 struct BindGroupStateChange {
-    last_states: [StateChange<id::BindGroupId>; hal::MAX_BIND_GROUPS],
+    last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
 }
 
 impl BindGroupStateChange {
@@ -817,7 +817,7 @@ impl BindGroupStateChange {
 
     fn set_and_check_redundant(
         &mut self,
-        bind_group_id: id::BindGroupId,
+        bind_group_id: Option<id::BindGroupId>,
         index: u32,
         dynamic_offsets: &mut Vec<u32>,
         offsets: &[wgt::DynamicOffset],
diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs
index f85696ced..684bf3612 100644
--- a/wgpu-core/src/command/render.rs
+++ b/wgpu-core/src/command/render.rs
@@ -635,8 +635,8 @@ pub enum RenderPassErrorInner {
     SurfaceTextureDropped,
     #[error("Not enough memory left for render pass")]
     OutOfMemory,
-    #[error("The bind group at index {0:?} is invalid")]
-    InvalidBindGroup(u32),
+    #[error("BindGroupId {0:?} is invalid")]
+    InvalidBindGroupId(id::BindGroupId),
     #[error("Unable to clear non-present/read-only depth")]
     InvalidDepthOps,
     #[error("Unable to clear non-present/read-only stencil")]
@@ -1934,12 +1934,16 @@ fn set_bind_group(
     dynamic_offsets: &[DynamicOffset],
     index: u32,
     num_dynamic_offsets: usize,
-    bind_group: Arc<BindGroup>,
+    bind_group: Option<Arc<BindGroup>>,
 ) -> Result<(), RenderPassErrorInner> {
-    api_log!(
-        "RenderPass::set_bind_group {index} {}",
-        bind_group.error_ident()
-    );
+    if bind_group.is_none() {
+        api_log!("RenderPass::set_bind_group {index} None");
+    } else {
+        api_log!(
+            "RenderPass::set_bind_group {index} {}",
+            bind_group.as_ref().unwrap().error_ident()
+        );
+    }
 
     let max_bind_groups = state.device.limits.max_bind_groups;
     if index >= max_bind_groups {
@@ -1957,6 +1961,12 @@ fn set_bind_group(
     );
     state.dynamic_offset_count += num_dynamic_offsets;
 
+    if bind_group.is_none() {
+        // TODO: Handle bind_group None.
+        return Ok(());
+    }
+
+    let bind_group = bind_group.unwrap();
     let bind_group = state.tracker.bind_groups.insert_single(bind_group);
 
     bind_group.same_device_as(cmd_buf.as_ref())?;
@@ -1999,7 +2009,7 @@ fn set_bind_group(
                     state.raw_encoder.set_bind_group(
                         pipeline_layout,
                         index + i as u32,
-                        raw_bg,
+                        Some(raw_bg),
                         &e.dynamic_offsets,
                     );
                 }
@@ -2073,7 +2083,7 @@ fn set_pipeline(
                         state.raw_encoder.set_bind_group(
                             pipeline.layout.raw(),
                             start_index as u32 + i as u32,
-                            raw_bg,
+                            Some(raw_bg),
                             &e.dynamic_offsets,
                         );
                     }
@@ -2788,7 +2798,7 @@ impl Global {
         &self,
         pass: &mut RenderPass,
         index: u32,
-        bind_group_id: id::BindGroupId,
+        bind_group_id: Option<id::BindGroupId>,
         offsets: &[DynamicOffset],
     ) -> Result<(), RenderPassError> {
         let scope = PassErrorScope::SetBindGroup;
@@ -2808,12 +2818,18 @@ impl Global {
             return Ok(());
         }
 
-        let hub = &self.hub;
-        let bind_group = hub
-            .bind_groups
-            .get(bind_group_id)
-            .map_err(|_| RenderPassErrorInner::InvalidBindGroup(index))
-            .map_pass_err(scope)?;
+        let mut bind_group = None;
+        if bind_group_id.is_some() {
+            let bind_group_id = bind_group_id.unwrap();
+
+            let hub = &self.hub;
+            let bg = hub
+                .bind_groups
+                .get(bind_group_id)
+                .map_err(|_| RenderPassErrorInner::InvalidBindGroupId(bind_group_id))
+                .map_pass_err(scope)?;
+            bind_group = Some(bg);
+        }
 
         base.commands.push(ArcRenderCommand::SetBindGroup {
             index,
diff --git a/wgpu-core/src/command/render_command.rs b/wgpu-core/src/command/render_command.rs
index 891ee3cfb..7b8a2f283 100644
--- a/wgpu-core/src/command/render_command.rs
+++ b/wgpu-core/src/command/render_command.rs
@@ -17,7 +17,7 @@ pub enum RenderCommand {
     SetBindGroup {
         index: u32,
         num_dynamic_offsets: usize,
-        bind_group_id: id::BindGroupId,
+        bind_group_id: Option<id::BindGroupId>,
     },
     SetPipeline(id::RenderPipelineId),
     SetIndexBuffer {
@@ -147,16 +147,29 @@ impl RenderCommand {
                         index,
                         num_dynamic_offsets,
                         bind_group_id,
-                    } => ArcRenderCommand::SetBindGroup {
-                        index,
-                        num_dynamic_offsets,
-                        bind_group: bind_group_guard.get_owned(bind_group_id).map_err(|_| {
+                    } => {
+                        if bind_group_id.is_none() {
+                            return Ok(ArcRenderCommand::SetBindGroup {
+                                index,
+                                num_dynamic_offsets,
+                                bind_group: None,
+                            });
+                        }
+
+                        let bind_group_id = bind_group_id.unwrap();
+                        let bg = bind_group_guard.get_owned(bind_group_id).map_err(|_| {
                             RenderPassError {
                                 scope: PassErrorScope::SetBindGroup,
-                                inner: RenderPassErrorInner::InvalidBindGroup(index),
+                                inner: RenderPassErrorInner::InvalidBindGroupId(bind_group_id),
                             }
-                        })?,
-                    },
+                        })?;
+
+                        ArcRenderCommand::SetBindGroup {
+                            index,
+                            num_dynamic_offsets,
+                            bind_group: Some(bg),
+                        }
+                    }
 
                     RenderCommand::SetPipeline(pipeline_id) => ArcRenderCommand::SetPipeline(
                         pipelines_guard
@@ -384,7 +397,7 @@ pub enum ArcRenderCommand {
     SetBindGroup {
         index: u32,
         num_dynamic_offsets: usize,
-        bind_group: Arc<BindGroup>,
+        bind_group: Option<Arc<BindGroup>>,
     },
     SetPipeline(Arc<RenderPipeline>),
     SetIndexBuffer {
@@ -464,11 +477,13 @@ pub enum ArcRenderCommand {
         indexed: bool,
     },
     PushDebugGroup {
+        #[cfg_attr(target_os = "emscripten", allow(dead_code))]
         color: u32,
         len: usize,
     },
     PopDebugGroup,
     InsertDebugMarker {
+        #[cfg_attr(target_os = "emscripten", allow(dead_code))]
         color: u32,
         len: usize,
     },
diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs
index 8620f4eee..cfa0a460e 100644
--- a/wgpu-core/src/device/resource.rs
+++ b/wgpu-core/src/device/resource.rs
@@ -664,6 +664,8 @@ impl Device {
             .describe_format_features(desc.format)
             .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;
 
+        unsafe { self.raw().add_raw_texture(&*hal_texture) };
+
         let texture = Texture::new(
             self,
             resource::TextureInner::Native { raw: hal_texture },
@@ -689,6 +691,8 @@ impl Device {
         hal_buffer: Box<dyn hal::DynBuffer>,
         desc: &resource::BufferDescriptor,
     ) -> Arc<Buffer> {
+        unsafe { self.raw().add_raw_buffer(&*hal_buffer) };
+
         let buffer = Buffer {
             raw: Snatchable::new(hal_buffer),
             device: self.clone(),
diff --git a/wgpu-core/src/lock/observing.rs b/wgpu-core/src/lock/observing.rs
index a5e02c54b..c530a5653 100644
--- a/wgpu-core/src/lock/observing.rs
+++ b/wgpu-core/src/lock/observing.rs
@@ -369,16 +369,16 @@ impl ObservationLog {
         self.write_location(new_location);
         self.write_action(&Action::Acquisition {
             older_rank: older_lock.rank.bit.number(),
-            older_location: older_lock.location as *const _ as usize,
+            older_location: addr(older_lock.location),
             newer_rank: new_rank.bit.number(),
-            newer_location: new_location as *const _ as usize,
+            newer_location: addr(new_location),
         });
     }
 
     fn write_location(&mut self, location: &'static Location<'static>) {
         if self.locations_seen.insert(location) {
             self.write_action(&Action::Location {
-                address: location as *const _ as usize,
+                address: addr(location),
                 file: location.file(),
                 line: location.line(),
                 column: location.column(),
@@ -473,3 +473,8 @@ impl LockRankSet {
         self.bits().trailing_zeros()
     }
 }
+
+/// Convenience for `std::ptr::from_ref(t) as usize`.
+fn addr<T>(t: &T) -> usize {
+    std::ptr::from_ref(t) as usize
+}
diff --git a/wgpu-core/src/track/texture.rs b/wgpu-core/src/track/texture.rs
index 1c74bffd9..60c223896 100644
--- a/wgpu-core/src/track/texture.rs
+++ b/wgpu-core/src/track/texture.rs
@@ -442,10 +442,9 @@ impl TextureTracker {
         let transitions = self
             .temp
             .drain(..)
-            .map(|pending| {
+            .inspect(|pending| {
                 let tex = unsafe { self.metadata.get_resource_unchecked(pending.id as _) };
                 textures.push(tex.inner.get(snatch_guard));
-                pending
             })
             .collect();
         (transitions, textures)
diff --git a/wgpu-core/src/validation.rs b/wgpu-core/src/validation.rs
index ea2608d75..1c5f28d90 100644
--- a/wgpu-core/src/validation.rs
+++ b/wgpu-core/src/validation.rs
@@ -275,7 +275,7 @@ fn map_storage_format_to_naga(format: wgt::TextureFormat) -> Option<naga::Storag
 
         Tf::Rgb10a2Uint => Sf::Rgb10a2Uint,
         Tf::Rgb10a2Unorm => Sf::Rgb10a2Unorm,
-        Tf::Rg11b10UFloat => Sf::Rg11b10UFloat,
+        Tf::Rg11b10Ufloat => Sf::Rg11b10Ufloat,
 
         Tf::Rg32Uint => Sf::Rg32Uint,
         Tf::Rg32Sint => Sf::Rg32Sint,
@@ -331,7 +331,7 @@ fn map_storage_format_from_naga(format: naga::StorageFormat) -> wgt::TextureForm
 
         Sf::Rgb10a2Uint => Tf::Rgb10a2Uint,
         Sf::Rgb10a2Unorm => Tf::Rgb10a2Unorm,
-        Sf::Rg11b10UFloat => Tf::Rg11b10UFloat,
+        Sf::Rg11b10Ufloat => Tf::Rg11b10Ufloat,
 
         Sf::Rg32Uint => Tf::Rg32Uint,
         Sf::Rg32Sint => Tf::Rg32Sint,
@@ -658,7 +658,7 @@ impl NumericType {
             Tf::Rgba8Sint | Tf::Rgba16Sint | Tf::Rgba32Sint => {
                 (NumericDimension::Vector(Vs::Quad), Scalar::I32)
             }
-            Tf::Rg11b10UFloat => (NumericDimension::Vector(Vs::Tri), Scalar::F32),
+            Tf::Rg11b10Ufloat => (NumericDimension::Vector(Vs::Tri), Scalar::F32),
             Tf::Stencil8
             | Tf::Depth16Unorm
             | Tf::Depth32Float
diff --git a/wgpu-hal/src/auxil/dxgi/conv.rs b/wgpu-hal/src/auxil/dxgi/conv.rs
index 09843f011..878dab39e 100644
--- a/wgpu-hal/src/auxil/dxgi/conv.rs
+++ b/wgpu-hal/src/auxil/dxgi/conv.rs
@@ -47,7 +47,7 @@ pub fn map_texture_format_failable(
         Tf::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
         Tf::Rgb10a2Uint => DXGI_FORMAT_R10G10B10A2_UINT,
         Tf::Rgb10a2Unorm => DXGI_FORMAT_R10G10B10A2_UNORM,
-        Tf::Rg11b10UFloat => DXGI_FORMAT_R11G11B10_FLOAT,
+        Tf::Rg11b10Ufloat => DXGI_FORMAT_R11G11B10_FLOAT,
         Tf::Rg32Uint => DXGI_FORMAT_R32G32_UINT,
         Tf::Rg32Sint => DXGI_FORMAT_R32G32_SINT,
         Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT,
diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs
index c5286c12d..43425bb5f 100644
--- a/wgpu-hal/src/dx12/device.rs
+++ b/wgpu-hal/src/dx12/device.rs
@@ -444,6 +444,10 @@ impl crate::Device for super::Device {
         self.counters.buffers.sub(1);
     }
 
+    unsafe fn add_raw_buffer(&self, _buffer: &super::Buffer) {
+        self.counters.buffers.add(1);
+    }
+
     unsafe fn map_buffer(
         &self,
         buffer: &super::Buffer,
@@ -531,6 +535,10 @@ impl crate::Device for super::Device {
         self.counters.textures.sub(1);
     }
 
+    unsafe fn add_raw_texture(&self, _texture: &super::Texture) {
+        self.counters.textures.add(1);
+    }
+
     unsafe fn create_texture_view(
         &self,
         texture: &super::Texture,
diff --git a/wgpu-hal/src/dynamic/command.rs b/wgpu-hal/src/dynamic/command.rs
index 6c0f1cb02..4ecdf7472 100644
--- a/wgpu-hal/src/dynamic/command.rs
+++ b/wgpu-hal/src/dynamic/command.rs
@@ -61,7 +61,7 @@ pub trait DynCommandEncoder: DynResource + std::fmt::Debug {
         &mut self,
         layout: &dyn DynPipelineLayout,
         index: u32,
-        group: &dyn DynBindGroup,
+        group: Option<&dyn DynBindGroup>,
         dynamic_offsets: &[wgt::DynamicOffset],
     );
 
@@ -282,9 +282,15 @@ impl<C: CommandEncoder + DynResource> DynCommandEncoder for C {
         &mut self,
         layout: &dyn DynPipelineLayout,
         index: u32,
-        group: &dyn DynBindGroup,
+        group: Option<&dyn DynBindGroup>,
         dynamic_offsets: &[wgt::DynamicOffset],
     ) {
+        if group.is_none() {
+            // TODO: Handle group None correctly.
+            return;
+        }
+        let group = group.unwrap();
+
         let layout = layout.expect_downcast_ref();
         let group = group.expect_downcast_ref();
         unsafe { C::set_bind_group(self, layout, index, group, dynamic_offsets) };
diff --git a/wgpu-hal/src/dynamic/device.rs b/wgpu-hal/src/dynamic/device.rs
index 1386196d6..f044a001d 100644
--- a/wgpu-hal/src/dynamic/device.rs
+++ b/wgpu-hal/src/dynamic/device.rs
@@ -24,6 +24,7 @@ pub trait DynDevice: DynResource {
     ) -> Result<Box<dyn DynBuffer>, DeviceError>;
 
     unsafe fn destroy_buffer(&self, buffer: Box<dyn DynBuffer>);
+    unsafe fn add_raw_buffer(&self, buffer: &dyn DynBuffer);
 
     unsafe fn map_buffer(
         &self,
@@ -41,6 +42,8 @@ pub trait DynDevice: DynResource {
         desc: &TextureDescriptor,
     ) -> Result<Box<dyn DynTexture>, DeviceError>;
     unsafe fn destroy_texture(&self, texture: Box<dyn DynTexture>);
+    unsafe fn add_raw_texture(&self, texture: &dyn DynTexture);
+
     unsafe fn create_texture_view(
         &self,
         texture: &dyn DynTexture,
@@ -177,6 +180,10 @@ impl<D: Device + DynResource> DynDevice for D {
     unsafe fn destroy_buffer(&self, buffer: Box<dyn DynBuffer>) {
         unsafe { D::destroy_buffer(self, buffer.unbox()) };
     }
+    unsafe fn add_raw_buffer(&self, buffer: &dyn DynBuffer) {
+        let buffer = buffer.expect_downcast_ref();
+        unsafe { D::add_raw_buffer(self, buffer) };
+    }
 
     unsafe fn map_buffer(
         &self,
@@ -217,6 +224,11 @@ impl<D: Device + DynResource> DynDevice for D {
         unsafe { D::destroy_texture(self, texture.unbox()) };
     }
 
+    unsafe fn add_raw_texture(&self, texture: &dyn DynTexture) {
+        let texture = texture.expect_downcast_ref();
+        unsafe { D::add_raw_texture(self, texture) };
+    }
+
     unsafe fn create_texture_view(
         &self,
         texture: &dyn DynTexture,
diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs
index 4d8868c36..72d9784d6 100644
--- a/wgpu-hal/src/empty.rs
+++ b/wgpu-hal/src/empty.rs
@@ -168,6 +168,8 @@ impl crate::Device for Context {
         Ok(Resource)
     }
     unsafe fn destroy_buffer(&self, buffer: Resource) {}
+    unsafe fn add_raw_buffer(&self, _buffer: &Resource) {}
+
     unsafe fn map_buffer(
         &self,
         buffer: &Resource,
@@ -183,6 +185,8 @@ impl crate::Device for Context {
         Ok(Resource)
     }
     unsafe fn destroy_texture(&self, texture: Resource) {}
+    unsafe fn add_raw_texture(&self, _texture: &Resource) {}
+
     unsafe fn create_texture_view(
         &self,
         texture: &Resource,
diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs
index de3c0e903..a654215d2 100644
--- a/wgpu-hal/src/gles/adapter.rs
+++ b/wgpu-hal/src/gles/adapter.rs
@@ -1107,7 +1107,7 @@ impl crate::Adapter for super::Adapter {
             Tf::Rgba8Sint => renderable | storage,
             Tf::Rgb10a2Uint => renderable,
             Tf::Rgb10a2Unorm => filterable_renderable,
-            Tf::Rg11b10UFloat => filterable | float_renderable,
+            Tf::Rg11b10Ufloat => filterable | float_renderable,
             Tf::Rg32Uint => renderable,
             Tf::Rg32Sint => renderable,
             Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
diff --git a/wgpu-hal/src/gles/conv.rs b/wgpu-hal/src/gles/conv.rs
index 8733d5495..3a6d5ebb2 100644
--- a/wgpu-hal/src/gles/conv.rs
+++ b/wgpu-hal/src/gles/conv.rs
@@ -45,7 +45,7 @@ impl super::AdapterShared {
                 glow::RGBA,
                 glow::UNSIGNED_INT_2_10_10_10_REV,
             ),
-            Tf::Rg11b10UFloat => (
+            Tf::Rg11b10Ufloat => (
                 glow::R11F_G11F_B10F,
                 glow::RGB,
                 glow::UNSIGNED_INT_10F_11F_11F_REV,
diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs
index 490f73259..15292d95c 100644
--- a/wgpu-hal/src/gles/device.rs
+++ b/wgpu-hal/src/gles/device.rs
@@ -647,6 +647,10 @@ impl crate::Device for super::Device {
         self.counters.buffers.sub(1);
     }
 
+    unsafe fn add_raw_buffer(&self, _buffer: &super::Buffer) {
+        self.counters.buffers.add(1);
+    }
+
     unsafe fn map_buffer(
         &self,
         buffer: &super::Buffer,
@@ -982,6 +986,10 @@ impl crate::Device for super::Device {
         self.counters.textures.sub(1);
     }
 
+    unsafe fn add_raw_texture(&self, _texture: &super::Texture) {
+        self.counters.textures.add(1);
+    }
+
     unsafe fn create_texture_view(
         &self,
         texture: &super::Texture,
diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs
index 868dafacf..6578252c1 100644
--- a/wgpu-hal/src/lib.rs
+++ b/wgpu-hal/src/lib.rs
@@ -51,8 +51,8 @@
 //!   must use [`CommandEncoder::transition_buffers`] between those two
 //!   operations.
 //!
-//! - Pipeline layouts are *explicitly specified* when setting bind
-//!   group. Incompatible layouts disturb groups bound at higher indices.
+//! - Pipeline layouts are *explicitly specified* when setting bind groups.
+//!   Incompatible layouts disturb groups bound at higher indices.
 //!
 //! - The API *accepts collections as iterators*, to avoid forcing the user to
 //!   store data in particular containers. The implementation doesn't guarantee
@@ -723,6 +723,9 @@ pub trait Device: WasmNotSendSync {
     /// - The given `buffer` must not currently be mapped.
     unsafe fn destroy_buffer(&self, buffer: <Self::A as Api>::Buffer);
 
+    /// A hook for when a wgpu-core buffer is created from a raw wgpu-hal buffer.
+    unsafe fn add_raw_buffer(&self, buffer: &<Self::A as Api>::Buffer);
+
     /// Return a pointer to CPU memory mapping the contents of `buffer`.
     ///
     /// Buffer mappings are persistent: the buffer may remain mapped on the CPU
@@ -814,6 +817,10 @@ pub trait Device: WasmNotSendSync {
         desc: &TextureDescriptor,
     ) -> Result<<Self::A as Api>::Texture, DeviceError>;
     unsafe fn destroy_texture(&self, texture: <Self::A as Api>::Texture);
+
+    /// A hook for when a wgpu-core texture is created from a raw wgpu-hal texture.
+    unsafe fn add_raw_texture(&self, texture: &<Self::A as Api>::Texture);
+
     unsafe fn create_texture_view(
         &self,
         texture: &<Self::A as Api>::Texture,
@@ -1242,8 +1249,40 @@ pub trait CommandEncoder: WasmNotSendSync + fmt::Debug {
 
     // pass common
 
-    /// Sets the bind group at `index` to `group`, assuming the layout
-    /// of all the preceding groups to be taken from `layout`.
+    /// Sets the bind group at `index` to `group`.
+    ///
+    /// If this is not the first call to `set_bind_group` within the current
+    /// render or compute pass:
+    ///
+    /// - If `layout` contains `n` bind group layouts, then any previously set
+    ///   bind groups at indices `n` or higher are cleared.
+    ///
+    /// - If the first `m` bind group layouts of `layout` are equal to those of
+    ///   the previously passed layout, but no more, then any previously set
+    ///   bind groups at indices `m` or higher are cleared.
+    ///
+    /// It follows from the above that passing the same layout as before doesn't
+    /// clear any bind groups.
+    ///
+    /// # Safety
+    ///
+    /// - This [`CommandEncoder`] must be within a render or compute pass.
+    ///
+    /// - `index` must be the valid index of some bind group layout in `layout`.
+    ///   Call this the "relevant bind group layout".
+    ///
+    /// - The layout of `group` must be equal to the relevant bind group layout.
+    ///
+    /// - The length of `dynamic_offsets` must match the number of buffer
+    ///   bindings [with dynamic offsets][hdo] in the relevant bind group
+    ///   layout.
+    ///
+    /// - If those buffer bindings are ordered by increasing [`binding` number]
+    ///   and paired with elements from `dynamic_offsets`, then each offset must
+    ///   be a valid offset for the binding's corresponding buffer in `group`.
+    ///
+    /// [hdo]: wgt::BindingType::Buffer::has_dynamic_offset
+    /// [`binding` number]: wgt::BindGroupLayoutEntry::binding
     unsafe fn set_bind_group(
         &mut self,
         layout: &<Self::A as Api>::PipelineLayout,
@@ -1295,11 +1334,43 @@ pub trait CommandEncoder: WasmNotSendSync + fmt::Debug {
 
     // render passes
 
-    // Begins a render pass, clears all active bindings.
+    /// Begin a new render pass, clearing all active bindings.
+    ///
+    /// This clears any bindings established by the following calls:
+    ///
+    /// - [`set_bind_group`](CommandEncoder::set_bind_group)
+    /// - [`set_push_constants`](CommandEncoder::set_push_constants)
+    /// - [`begin_query`](CommandEncoder::begin_query)
+    /// - [`set_render_pipeline`](CommandEncoder::set_render_pipeline)
+    /// - [`set_index_buffer`](CommandEncoder::set_index_buffer)
+    /// - [`set_vertex_buffer`](CommandEncoder::set_vertex_buffer)
+    ///
+    /// # Safety
+    ///
+    /// - All prior calls to [`begin_render_pass`] on this [`CommandEncoder`] must have been followed
+    ///   by a call to [`end_render_pass`].
+    ///
+    /// - All prior calls to [`begin_compute_pass`] on this [`CommandEncoder`] must have been followed
+    ///   by a call to [`end_compute_pass`].
+    ///
+    /// [`begin_render_pass`]: CommandEncoder::begin_render_pass
+    /// [`begin_compute_pass`]: CommandEncoder::begin_compute_pass
+    /// [`end_render_pass`]: CommandEncoder::end_render_pass
+    /// [`end_compute_pass`]: CommandEncoder::end_compute_pass
     unsafe fn begin_render_pass(
         &mut self,
         desc: &RenderPassDescriptor<<Self::A as Api>::QuerySet, <Self::A as Api>::TextureView>,
     );
+
+    /// End the current render pass.
+    ///
+    /// # Safety
+    ///
+    /// - There must have been a prior call to [`begin_render_pass`] on this [`CommandEncoder`]
+    ///   that has not been followed by a call to [`end_render_pass`].
+    ///
+    /// [`begin_render_pass`]: CommandEncoder::begin_render_pass
+    /// [`end_render_pass`]: CommandEncoder::end_render_pass
     unsafe fn end_render_pass(&mut self);
 
     unsafe fn set_render_pipeline(&mut self, pipeline: &<Self::A as Api>::RenderPipeline);
@@ -1365,11 +1436,41 @@ pub trait CommandEncoder: WasmNotSendSync + fmt::Debug {
 
     // compute passes
 
-    // Begins a compute pass, clears all active bindings.
+    /// Begin a new compute pass, clearing all active bindings.
+    ///
+    /// This clears any bindings established by the following calls:
+    ///
+    /// - [`set_bind_group`](CommandEncoder::set_bind_group)
+    /// - [`set_push_constants`](CommandEncoder::set_push_constants)
+    /// - [`begin_query`](CommandEncoder::begin_query)
+    /// - [`set_compute_pipeline`](CommandEncoder::set_compute_pipeline)
+    ///
+    /// # Safety
+    ///
+    /// - All prior calls to [`begin_render_pass`] on this [`CommandEncoder`] must have been followed
+    ///   by a call to [`end_render_pass`].
+    ///
+    /// - All prior calls to [`begin_compute_pass`] on this [`CommandEncoder`] must have been followed
+    ///   by a call to [`end_compute_pass`].
+    ///
+    /// [`begin_render_pass`]: CommandEncoder::begin_render_pass
+    /// [`begin_compute_pass`]: CommandEncoder::begin_compute_pass
+    /// [`end_render_pass`]: CommandEncoder::end_render_pass
+    /// [`end_compute_pass`]: CommandEncoder::end_compute_pass
     unsafe fn begin_compute_pass(
         &mut self,
         desc: &ComputePassDescriptor<<Self::A as Api>::QuerySet>,
     );
+
+    /// End the current compute pass.
+    ///
+    /// # Safety
+    ///
+    /// - There must have been a prior call to [`begin_compute_pass`] on this [`CommandEncoder`]
+    ///   that has not been followed by a call to [`end_compute_pass`].
+    ///
+    /// [`begin_compute_pass`]: CommandEncoder::begin_compute_pass
+    /// [`end_compute_pass`]: CommandEncoder::end_compute_pass
     unsafe fn end_compute_pass(&mut self);
 
     unsafe fn set_compute_pipeline(&mut self, pipeline: &<Self::A as Api>::ComputePipeline);
diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs
index 983899d02..e7db97a1f 100644
--- a/wgpu-hal/src/metal/adapter.rs
+++ b/wgpu-hal/src/metal/adapter.rs
@@ -178,7 +178,7 @@ impl crate::Adapter for super::Adapter {
                 flags.set(Tfc::STORAGE, pc.format_rgb10a2_unorm_all);
                 flags
             }
-            Tf::Rg11b10UFloat => {
+            Tf::Rg11b10Ufloat => {
                 let mut flags = all_caps;
                 flags.set(Tfc::STORAGE, pc.format_rg11b10_all);
                 flags
@@ -1040,7 +1040,7 @@ impl super::PrivateCapabilities {
             Tf::Rgba8Sint => RGBA8Sint,
             Tf::Rgb10a2Uint => RGB10A2Uint,
             Tf::Rgb10a2Unorm => RGB10A2Unorm,
-            Tf::Rg11b10UFloat => RG11B10Float,
+            Tf::Rg11b10Ufloat => RG11B10Float,
             Tf::Rg32Uint => RG32Uint,
             Tf::Rg32Sint => RG32Sint,
             Tf::Rg32Float => RG32Float,
diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs
index 077c10f51..347a97a08 100644
--- a/wgpu-hal/src/metal/device.rs
+++ b/wgpu-hal/src/metal/device.rs
@@ -356,6 +356,10 @@ impl crate::Device for super::Device {
         self.counters.buffers.sub(1);
     }
 
+    unsafe fn add_raw_buffer(&self, _buffer: &super::Buffer) {
+        self.counters.buffers.add(1);
+    }
+
     unsafe fn map_buffer(
         &self,
         buffer: &super::Buffer,
@@ -436,6 +440,10 @@ impl crate::Device for super::Device {
         self.counters.textures.sub(1);
     }
 
+    unsafe fn add_raw_texture(&self, _texture: &super::Texture) {
+        self.counters.textures.add(1);
+    }
+
     unsafe fn create_texture_view(
         &self,
         texture: &super::Texture,
diff --git a/wgpu-hal/src/vulkan/conv.rs b/wgpu-hal/src/vulkan/conv.rs
index 38642ba08..b82930706 100644
--- a/wgpu-hal/src/vulkan/conv.rs
+++ b/wgpu-hal/src/vulkan/conv.rs
@@ -36,7 +36,7 @@ impl super::PrivateCapabilities {
             Tf::Rgba8Sint => F::R8G8B8A8_SINT,
             Tf::Rgb10a2Uint => F::A2B10G10R10_UINT_PACK32,
             Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32,
-            Tf::Rg11b10UFloat => F::B10G11R11_UFLOAT_PACK32,
+            Tf::Rg11b10Ufloat => F::B10G11R11_UFLOAT_PACK32,
             Tf::Rg32Uint => F::R32G32_UINT,
             Tf::Rg32Sint => F::R32G32_SINT,
             Tf::Rg32Float => F::R32G32_SFLOAT,
diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs
index f21a24364..54905b4ba 100644
--- a/wgpu-hal/src/vulkan/device.rs
+++ b/wgpu-hal/src/vulkan/device.rs
@@ -956,6 +956,10 @@ impl crate::Device for super::Device {
         self.counters.buffers.sub(1);
     }
 
+    unsafe fn add_raw_buffer(&self, _buffer: &super::Buffer) {
+        self.counters.buffers.add(1);
+    }
+
     unsafe fn map_buffer(
         &self,
         buffer: &super::Buffer,
@@ -1127,6 +1131,10 @@ impl crate::Device for super::Device {
         self.counters.textures.sub(1);
     }
 
+    unsafe fn add_raw_texture(&self, _texture: &super::Texture) {
+        self.counters.textures.add(1);
+    }
+
     unsafe fn create_texture_view(
         &self,
         texture: &super::Texture,
diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs
index 124fd0abc..f44c4fa30 100644
--- a/wgpu-hal/src/vulkan/instance.rs
+++ b/wgpu-hal/src/vulkan/instance.rs
@@ -554,10 +554,9 @@ impl Drop for super::InstanceShared {
     fn drop(&mut self) {
         unsafe {
             // Keep du alive since destroy_instance may also log
-            let _du = self.debug_utils.take().map(|du| {
+            let _du = self.debug_utils.take().inspect(|du| {
                 du.extension
                     .destroy_debug_utils_messenger(du.messenger, None);
-                du
             });
             if self.drop_guard.is_none() {
                 self.raw.destroy_instance(None);
diff --git a/wgpu-info/src/texture.rs b/wgpu-info/src/texture.rs
index 40771d067..2487bf350 100644
--- a/wgpu-info/src/texture.rs
+++ b/wgpu-info/src/texture.rs
@@ -32,7 +32,7 @@ pub const TEXTURE_FORMAT_LIST: [wgpu::TextureFormat; 116] = [
     wgpu::TextureFormat::Rgb9e5Ufloat,
     wgpu::TextureFormat::Rgb10a2Uint,
     wgpu::TextureFormat::Rgb10a2Unorm,
-    wgpu::TextureFormat::Rg11b10UFloat,
+    wgpu::TextureFormat::Rg11b10Ufloat,
     wgpu::TextureFormat::Rg32Uint,
     wgpu::TextureFormat::Rg32Sint,
     wgpu::TextureFormat::Rg32Float,
diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs
index 118ae001e..e44f91665 100644
--- a/wgpu-types/src/lib.rs
+++ b/wgpu-types/src/lib.rs
@@ -402,7 +402,7 @@ bitflags::bitflags! {
         const SHADER_F16 = 1 << 8;
 
 
-        /// Allows for usage of textures of format [`TextureFormat::Rg11b10UFloat`] as a render target
+        /// Allows for usage of textures of format [`TextureFormat::Rg11b10Ufloat`] as a render target
         ///
         /// Supported platforms:
         /// - Vulkan
@@ -2554,7 +2554,7 @@ pub enum TextureFormat {
     /// Red, green, blue, and alpha channels. 10 bit integer for RGB channels, 2 bit integer for alpha channel. [0, 1023] ([0, 3] for alpha) converted to/from float [0, 1] in shader.
     Rgb10a2Unorm,
     /// Red, green, and blue channels. 11 bit float with no sign bit for RG channels. 10 bit float with no sign bit for blue channel. Float in shader.
-    Rg11b10UFloat,
+    Rg11b10Ufloat,
 
     // Normal 64 bit formats
     /// Red and green channels. 32 bit integer per channel. Unsigned in shader.
@@ -2842,7 +2842,7 @@ impl<'de> Deserialize<'de> for TextureFormat {
                     "bgra8unorm-srgb" => TextureFormat::Bgra8UnormSrgb,
                     "rgb10a2uint" => TextureFormat::Rgb10a2Uint,
                     "rgb10a2unorm" => TextureFormat::Rgb10a2Unorm,
-                    "rg11b10ufloat" => TextureFormat::Rg11b10UFloat,
+                    "rg11b10ufloat" => TextureFormat::Rg11b10Ufloat,
                     "rg32uint" => TextureFormat::Rg32Uint,
                     "rg32sint" => TextureFormat::Rg32Sint,
                     "rg32float" => TextureFormat::Rg32Float,
@@ -2970,7 +2970,7 @@ impl Serialize for TextureFormat {
             TextureFormat::Bgra8UnormSrgb => "bgra8unorm-srgb",
             TextureFormat::Rgb10a2Uint => "rgb10a2uint",
             TextureFormat::Rgb10a2Unorm => "rgb10a2unorm",
-            TextureFormat::Rg11b10UFloat => "rg11b10ufloat",
+            TextureFormat::Rg11b10Ufloat => "rg11b10ufloat",
             TextureFormat::Rg32Uint => "rg32uint",
             TextureFormat::Rg32Sint => "rg32sint",
             TextureFormat::Rg32Float => "rg32float",
@@ -3212,7 +3212,7 @@ impl TextureFormat {
             | Self::Rgb9e5Ufloat
             | Self::Rgb10a2Uint
             | Self::Rgb10a2Unorm
-            | Self::Rg11b10UFloat
+            | Self::Rg11b10Ufloat
             | Self::Rg32Uint
             | Self::Rg32Sint
             | Self::Rg32Float
@@ -3320,7 +3320,7 @@ impl TextureFormat {
             | Self::Rgb9e5Ufloat
             | Self::Rgb10a2Uint
             | Self::Rgb10a2Unorm
-            | Self::Rg11b10UFloat
+            | Self::Rg11b10Ufloat
             | Self::Rg32Uint
             | Self::Rg32Sint
             | Self::Rg32Float
@@ -3439,7 +3439,7 @@ impl TextureFormat {
             Self::Bgra8UnormSrgb =>       (msaa_resolve, attachment),
             Self::Rgb10a2Uint =>          (        msaa, attachment),
             Self::Rgb10a2Unorm =>         (msaa_resolve, attachment),
-            Self::Rg11b10UFloat =>        (        msaa,   rg11b10f),
+            Self::Rg11b10Ufloat =>        (        msaa,   rg11b10f),
             Self::Rg32Uint =>             (        noaa,  all_flags),
             Self::Rg32Sint =>             (        noaa,  all_flags),
             Self::Rg32Float =>            (        noaa,  all_flags),
@@ -3550,7 +3550,7 @@ impl TextureFormat {
             | Self::Rg16Float
             | Self::Rgba16Float
             | Self::Rgb10a2Unorm
-            | Self::Rg11b10UFloat => Some(float),
+            | Self::Rg11b10Ufloat => Some(float),
 
             Self::R32Float | Self::Rg32Float | Self::Rgba32Float => Some(float32_sample_type),
 
@@ -3682,7 +3682,7 @@ impl TextureFormat {
             | Self::Rg16Sint
             | Self::Rg16Float => Some(4),
             Self::R32Uint | Self::R32Sint | Self::R32Float => Some(4),
-            Self::Rgb9e5Ufloat | Self::Rgb10a2Uint | Self::Rgb10a2Unorm | Self::Rg11b10UFloat => {
+            Self::Rgb9e5Ufloat | Self::Rgb10a2Uint | Self::Rgb10a2Unorm | Self::Rg11b10Ufloat => {
                 Some(4)
             }
 
@@ -3785,7 +3785,7 @@ impl TextureFormat {
             | Self::Rg32Float
             | Self::Rgb10a2Uint
             | Self::Rgb10a2Unorm
-            | Self::Rg11b10UFloat => Some(8),
+            | Self::Rg11b10Ufloat => Some(8),
             Self::Rgba32Uint | Self::Rgba32Sint | Self::Rgba32Float => Some(16),
             Self::Stencil8
             | Self::Depth16Unorm
@@ -3868,7 +3868,7 @@ impl TextureFormat {
             | Self::Rgba32Float
             | Self::Rgb10a2Uint
             | Self::Rgb10a2Unorm
-            | Self::Rg11b10UFloat => Some(4),
+            | Self::Rg11b10Ufloat => Some(4),
             Self::Stencil8
             | Self::Depth16Unorm
             | Self::Depth24Plus
@@ -3959,7 +3959,7 @@ impl TextureFormat {
             | Self::Rgba32Sint
             | Self::Rgba32Float => 4,
 
-            Self::Rgb9e5Ufloat | Self::Rg11b10UFloat => 3,
+            Self::Rgb9e5Ufloat | Self::Rg11b10Ufloat => 3,
             Self::Rgb10a2Uint | Self::Rgb10a2Unorm => 4,
 
             Self::Stencil8 | Self::Depth16Unorm | Self::Depth24Plus | Self::Depth32Float => 1,
@@ -4177,7 +4177,7 @@ fn texture_format_serialize() {
         "\"rgb10a2unorm\"".to_string()
     );
     assert_eq!(
-        serde_json::to_string(&TextureFormat::Rg11b10UFloat).unwrap(),
+        serde_json::to_string(&TextureFormat::Rg11b10Ufloat).unwrap(),
         "\"rg11b10ufloat\"".to_string()
     );
     assert_eq!(
@@ -4474,7 +4474,7 @@ fn texture_format_deserialize() {
     );
     assert_eq!(
         serde_json::from_str::<TextureFormat>("\"rg11b10ufloat\"").unwrap(),
-        TextureFormat::Rg11b10UFloat
+        TextureFormat::Rg11b10Ufloat
     );
     assert_eq!(
         serde_json::from_str::<TextureFormat>("\"rg32uint\"").unwrap(),
diff --git a/wgpu/src/api/compute_pass.rs b/wgpu/src/api/compute_pass.rs
index bd0575bbd..306a9f0b4 100644
--- a/wgpu/src/api/compute_pass.rs
+++ b/wgpu/src/api/compute_pass.rs
@@ -48,14 +48,15 @@ impl<'encoder> ComputePass<'encoder> {
     pub fn set_bind_group(
         &mut self,
         index: u32,
-        bind_group: &BindGroup,
+        bind_group: Option<&BindGroup>,
         offsets: &[DynamicOffset],
     ) {
+        let bg = bind_group.map(|x| x.data.as_ref());
         DynContext::compute_pass_set_bind_group(
             &*self.inner.context,
             self.inner.data.as_mut(),
             index,
-            bind_group.data.as_ref(),
+            bg,
             offsets,
         );
     }
diff --git a/wgpu/src/api/queue.rs b/wgpu/src/api/queue.rs
index 65e6dd1ce..b57b33ece 100644
--- a/wgpu/src/api/queue.rs
+++ b/wgpu/src/api/queue.rs
@@ -37,7 +37,10 @@ impl Drop for Queue {
 /// This type is unique to the Rust API of `wgpu`.
 /// There is no analogue in the WebGPU specification.
 #[derive(Debug, Clone)]
-pub struct SubmissionIndex(pub(crate) Arc<crate::Data>);
+pub struct SubmissionIndex {
+    #[cfg_attr(not(native), allow(dead_code))]
+    pub(crate) data: Arc<crate::Data>,
+}
 #[cfg(send_sync)]
 static_assertions::assert_impl_all!(SubmissionIndex: Send, Sync);
 
@@ -248,7 +251,7 @@ impl Queue {
         let data =
             DynContext::queue_submit(&*self.context, self.data.as_ref(), &mut command_buffers);
 
-        SubmissionIndex(data)
+        SubmissionIndex { data }
     }
 
     /// Gets the amount of nanoseconds each tick of a timestamp query represents.
diff --git a/wgpu/src/api/render_bundle_encoder.rs b/wgpu/src/api/render_bundle_encoder.rs
index 446b4cbf8..d69548cdf 100644
--- a/wgpu/src/api/render_bundle_encoder.rs
+++ b/wgpu/src/api/render_bundle_encoder.rs
@@ -66,14 +66,15 @@ impl<'a> RenderBundleEncoder<'a> {
     pub fn set_bind_group(
         &mut self,
         index: u32,
-        bind_group: &'a BindGroup,
+        bind_group: Option<&'a BindGroup>,
         offsets: &[DynamicOffset],
     ) {
+        let bg = bind_group.map(|x| x.data.as_ref());
         DynContext::render_bundle_encoder_set_bind_group(
             &*self.parent.context,
             self.data.as_mut(),
             index,
-            bind_group.data.as_ref(),
+            bg,
             offsets,
         )
     }
diff --git a/wgpu/src/api/render_pass.rs b/wgpu/src/api/render_pass.rs
index 8fddfd009..7cdbc3135 100644
--- a/wgpu/src/api/render_pass.rs
+++ b/wgpu/src/api/render_pass.rs
@@ -77,14 +77,15 @@ impl<'encoder> RenderPass<'encoder> {
     pub fn set_bind_group(
         &mut self,
         index: u32,
-        bind_group: &BindGroup,
+        bind_group: Option<&BindGroup>,
         offsets: &[DynamicOffset],
     ) {
+        let bg = bind_group.map(|x| x.data.as_ref());
         DynContext::render_pass_set_bind_group(
             &*self.inner.context,
             self.inner.data.as_mut(),
             index,
-            bind_group.data.as_ref(),
+            bg,
             offsets,
         )
     }
diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs
index 5a9a3b988..30ad22f73 100644
--- a/wgpu/src/backend/webgpu.rs
+++ b/wgpu/src/backend/webgpu.rs
@@ -225,7 +225,7 @@ fn map_texture_format(texture_format: wgt::TextureFormat) -> webgpu_sys::GpuText
             unimplemented!("Current version of web_sys is missing {texture_format:?}")
         }
         TextureFormat::Rgb10a2Unorm => tf::Rgb10a2unorm,
-        TextureFormat::Rg11b10UFloat => tf::Rg11b10ufloat,
+        TextureFormat::Rg11b10Ufloat => tf::Rg11b10ufloat,
         // 64-bit formats
         TextureFormat::Rg32Uint => tf::Rg32uint,
         TextureFormat::Rg32Sint => tf::Rg32sint,
@@ -1403,14 +1403,6 @@ impl crate::context::Context for ContextWebGpu {
         map_wgt_limits(device_data.0.limits())
     }
 
-    fn device_downlevel_properties(
-        &self,
-        _device_data: &Self::DeviceData,
-    ) -> wgt::DownlevelCapabilities {
-        // WebGPU is assumed to be fully compliant
-        wgt::DownlevelCapabilities::default()
-    }
-
     #[cfg_attr(
         not(any(
             feature = "spirv",
@@ -2033,12 +2025,6 @@ impl crate::context::Context for ContextWebGpu {
         device_data.0.destroy();
     }
 
-    fn device_mark_lost(&self, _device_data: &Self::DeviceData, _message: &str) {
-        // TODO: figure out the GPUDevice implementation of this, including resolving
-        // the device.lost promise, which will require a different invocation pattern
-        // with a callback.
-    }
-
     fn queue_drop(&self, _queue_data: &Self::QueueData) {
         // Queue is dropped automatically
     }
@@ -2771,9 +2757,14 @@ impl crate::context::Context for ContextWebGpu {
         &self,
         pass_data: &mut Self::ComputePassData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[wgt::DynamicOffset],
     ) {
+        if bind_group_data.is_none() {
+            // TODO: Handle the None case.
+            return;
+        }
+        let bind_group_data = bind_group_data.unwrap();
         if offsets.is_empty() {
             pass_data.0.set_bind_group(index, Some(&bind_group_data.0));
         } else {
@@ -2883,9 +2874,14 @@ impl crate::context::Context for ContextWebGpu {
         &self,
         encoder_data: &mut Self::RenderBundleEncoderData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[wgt::DynamicOffset],
     ) {
+        if bind_group_data.is_none() {
+            // TODO: Handle the None case.
+            return;
+        }
+        let bind_group_data = bind_group_data.unwrap();
         if offsets.is_empty() {
             encoder_data
                 .0
@@ -3023,52 +3019,6 @@ impl crate::context::Context for ContextWebGpu {
             .draw_indexed_indirect_with_f64(&indirect_buffer_data.0.buffer, indirect_offset as f64);
     }
 
-    fn render_bundle_encoder_multi_draw_indirect(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count: u32,
-    ) {
-        panic!("MULTI_DRAW_INDIRECT feature must be enabled to call multi_draw_indirect")
-    }
-
-    fn render_bundle_encoder_multi_draw_indexed_indirect(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count: u32,
-    ) {
-        panic!("MULTI_DRAW_INDIRECT feature must be enabled to call multi_draw_indexed_indirect")
-    }
-
-    fn render_bundle_encoder_multi_draw_indirect_count(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count_buffer_data: &Self::BufferData,
-        _count_buffer_offset: wgt::BufferAddress,
-        _max_count: u32,
-    ) {
-        panic!(
-            "MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indirect_count"
-        )
-    }
-
-    fn render_bundle_encoder_multi_draw_indexed_indirect_count(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count_buffer_data: &Self::BufferData,
-        _count_buffer_offset: wgt::BufferAddress,
-        _max_count: u32,
-    ) {
-        panic!("MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indexed_indirect_count")
-    }
-
     fn render_pass_set_pipeline(
         &self,
         pass_data: &mut Self::RenderPassData,
@@ -3081,9 +3031,14 @@ impl crate::context::Context for ContextWebGpu {
         &self,
         pass_data: &mut Self::RenderPassData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[wgt::DynamicOffset],
     ) {
+        if bind_group_data.is_none() {
+            // TODO: Handle the None case.
+            return;
+        }
+        let bind_group_data = bind_group_data.unwrap();
         if offsets.is_empty() {
             pass_data.0.set_bind_group(index, Some(&bind_group_data.0));
         } else {
diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs
index 8ed6acdd8..9b2597cb4 100644
--- a/wgpu/src/backend/wgpu_core.rs
+++ b/wgpu/src/backend/wgpu_core.rs
@@ -793,13 +793,6 @@ impl crate::Context for ContextWgpuCore {
         }
     }
 
-    fn device_downlevel_properties(&self, device_data: &Self::DeviceData) -> DownlevelCapabilities {
-        match self.0.device_downlevel_properties(device_data.id) {
-            Ok(limits) => limits,
-            Err(err) => self.handle_error_fatal(err, "Device::downlevel_properties"),
-        }
-    }
-
     #[cfg_attr(
         not(any(
             feature = "spirv",
@@ -1374,17 +1367,12 @@ impl crate::Context for ContextWgpuCore {
     fn device_destroy(&self, device_data: &Self::DeviceData) {
         self.0.device_destroy(device_data.id);
     }
-    fn device_mark_lost(&self, device_data: &Self::DeviceData, message: &str) {
-        // We do not provide a reason to device_lose, because all reasons other than
-        // destroyed (which this is not) are "unknown".
-        self.0.device_mark_lost(device_data.id, message);
-    }
     fn device_poll(
         &self,
         device_data: &Self::DeviceData,
         maintain: crate::Maintain,
     ) -> wgt::MaintainResult {
-        let maintain_inner = maintain.map_index(|i| *i.0.as_ref().downcast_ref().unwrap());
+        let maintain_inner = maintain.map_index(|i| *i.data.as_ref().downcast_ref().unwrap());
         match self.0.device_poll(device_data.id, maintain_inner) {
             Ok(done) => match done {
                 true => wgt::MaintainResult::SubmissionQueueEmpty,
@@ -2180,15 +2168,14 @@ impl crate::Context for ContextWgpuCore {
         &self,
         pass_data: &mut Self::ComputePassData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[wgt::DynamicOffset],
     ) {
-        if let Err(cause) = self.0.compute_pass_set_bind_group(
-            &mut pass_data.pass,
-            index,
-            *bind_group_data,
-            offsets,
-        ) {
+        let bg = bind_group_data.cloned();
+        if let Err(cause) =
+            self.0
+                .compute_pass_set_bind_group(&mut pass_data.pass, index, bg, offsets)
+        {
             self.handle_error(
                 &pass_data.error_sink,
                 cause,
@@ -2376,14 +2363,15 @@ impl crate::Context for ContextWgpuCore {
         &self,
         encoder_data: &mut Self::RenderBundleEncoderData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[wgt::DynamicOffset],
     ) {
+        let bg = bind_group_data.cloned();
         unsafe {
             wgpu_render_bundle_set_bind_group(
                 encoder_data,
                 index,
-                *bind_group_data,
+                bg,
                 offsets.as_ptr(),
                 offsets.len(),
             )
@@ -2484,50 +2472,6 @@ impl crate::Context for ContextWgpuCore {
         )
     }
 
-    fn render_bundle_encoder_multi_draw_indirect(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count: u32,
-    ) {
-        unimplemented!()
-    }
-
-    fn render_bundle_encoder_multi_draw_indexed_indirect(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count: u32,
-    ) {
-        unimplemented!()
-    }
-
-    fn render_bundle_encoder_multi_draw_indirect_count(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count_buffer_data: &Self::BufferData,
-        _count_buffer_offset: wgt::BufferAddress,
-        _max_count: u32,
-    ) {
-        unimplemented!()
-    }
-
-    fn render_bundle_encoder_multi_draw_indexed_indirect_count(
-        &self,
-        _encoder_data: &mut Self::RenderBundleEncoderData,
-        _indirect_buffer_data: &Self::BufferData,
-        _indirect_offset: wgt::BufferAddress,
-        _count_buffer_data: &Self::BufferData,
-        _count_buffer_offset: wgt::BufferAddress,
-        _max_count: u32,
-    ) {
-        unimplemented!()
-    }
-
     fn render_pass_set_pipeline(
         &self,
         pass_data: &mut Self::RenderPassData,
@@ -2550,12 +2494,13 @@ impl crate::Context for ContextWgpuCore {
         &self,
         pass_data: &mut Self::RenderPassData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[wgt::DynamicOffset],
     ) {
+        let bg = bind_group_data.cloned();
         if let Err(cause) =
             self.0
-                .render_pass_set_bind_group(&mut pass_data.pass, index, *bind_group_data, offsets)
+                .render_pass_set_bind_group(&mut pass_data.pass, index, bg, offsets)
         {
             self.handle_error(
                 &pass_data.error_sink,
diff --git a/wgpu/src/context.rs b/wgpu/src/context.rs
index 5e6d85192..dd989ddd2 100644
--- a/wgpu/src/context.rs
+++ b/wgpu/src/context.rs
@@ -21,6 +21,7 @@ use crate::{
 /// Meta trait for an data associated with an id tracked by a context.
 ///
 /// There is no need to manually implement this trait since there is a blanket implementation for this trait.
+#[cfg_attr(target_os = "emscripten", allow(dead_code))]
 pub trait ContextData: Debug + WasmNotSendSync + 'static {}
 impl<T: Debug + WasmNotSendSync + 'static> ContextData for T {}
 
@@ -59,6 +60,7 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
 
     type CompilationInfoFuture: Future<Output = CompilationInfo> + WasmNotSend + 'static;
 
+    #[cfg(not(target_os = "emscripten"))]
     fn init(instance_desc: wgt::InstanceDescriptor) -> Self;
     unsafe fn instance_create_surface(
         &self,
@@ -122,7 +124,6 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
 
     fn device_features(&self, device_data: &Self::DeviceData) -> Features;
     fn device_limits(&self, device_data: &Self::DeviceData) -> Limits;
-    fn device_downlevel_properties(&self, device_data: &Self::DeviceData) -> DownlevelCapabilities;
     fn device_create_shader_module(
         &self,
         device_data: &Self::DeviceData,
@@ -203,7 +204,6 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
         device_lost_callback: DeviceLostCallback,
     );
     fn device_destroy(&self, device_data: &Self::DeviceData);
-    fn device_mark_lost(&self, device_data: &Self::DeviceData, message: &str);
     fn queue_drop(&self, queue_data: &Self::QueueData);
     fn device_poll(&self, device_data: &Self::DeviceData, maintain: Maintain) -> MaintainResult;
     fn device_on_uncaptured_error(
@@ -441,7 +441,7 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
         &self,
         pass_data: &mut Self::ComputePassData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[DynamicOffset],
     );
     fn compute_pass_set_push_constants(
@@ -494,7 +494,7 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
         &self,
         encoder_data: &mut Self::RenderBundleEncoderData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[DynamicOffset],
     );
     #[allow(clippy::too_many_arguments)]
@@ -547,40 +547,6 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
         indirect_buffer_data: &Self::BufferData,
         indirect_offset: BufferAddress,
     );
-    fn render_bundle_encoder_multi_draw_indirect(
-        &self,
-        encoder_data: &mut Self::RenderBundleEncoderData,
-        indirect_buffer_data: &Self::BufferData,
-        indirect_offset: BufferAddress,
-        count: u32,
-    );
-    fn render_bundle_encoder_multi_draw_indexed_indirect(
-        &self,
-        encoder_data: &mut Self::RenderBundleEncoderData,
-        indirect_buffer_data: &Self::BufferData,
-        indirect_offset: BufferAddress,
-        count: u32,
-    );
-    #[allow(clippy::too_many_arguments)]
-    fn render_bundle_encoder_multi_draw_indirect_count(
-        &self,
-        encoder_data: &mut Self::RenderBundleEncoderData,
-        indirect_buffer_data: &Self::BufferData,
-        indirect_offset: BufferAddress,
-        count_buffer_data: &Self::BufferData,
-        count_buffer_offset: BufferAddress,
-        max_count: u32,
-    );
-    #[allow(clippy::too_many_arguments)]
-    fn render_bundle_encoder_multi_draw_indexed_indirect_count(
-        &self,
-        encoder_data: &mut Self::RenderBundleEncoderData,
-        indirect_buffer_data: &Self::BufferData,
-        indirect_offset: BufferAddress,
-        count_buffer_data: &Self::BufferData,
-        count_buffer_offset: BufferAddress,
-        max_count: u32,
-    );
 
     fn render_pass_set_pipeline(
         &self,
@@ -591,7 +557,7 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
         &self,
         pass_data: &mut Self::RenderPassData,
         index: u32,
-        bind_group_data: &Self::BindGroupData,
+        bind_group_data: Option<&Self::BindGroupData>,
         offsets: &[DynamicOffset],
     );
     #[allow(clippy::too_many_arguments)]
@@ -788,6 +754,7 @@ pub type DeviceLostCallback = Box<dyn Fn(DeviceLostReason, String) + 'static>;
 
 /// An object safe variant of [`Context`] implemented by all types that implement [`Context`].
 pub(crate) trait DynContext: Debug + WasmNotSendSync {
+    #[cfg(not(target_os = "emscripten"))]
     fn as_any(&self) -> &dyn Any;
 
     unsafe fn instance_create_surface(
@@ -850,7 +817,6 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
 
     fn device_features(&self, device_data: &crate::Data) -> Features;
     fn device_limits(&self, device_data: &crate::Data) -> Limits;
-    fn device_downlevel_properties(&self, device_data: &crate::Data) -> DownlevelCapabilities;
     fn device_create_shader_module(
         &self,
         device_data: &crate::Data,
@@ -931,7 +897,6 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
         device_lost_callback: DeviceLostCallback,
     );
     fn device_destroy(&self, device_data: &crate::Data);
-    fn device_mark_lost(&self, device_data: &crate::Data, message: &str);
     fn queue_drop(&self, queue_data: &crate::Data);
     fn device_poll(&self, device_data: &crate::Data, maintain: Maintain) -> MaintainResult;
     fn device_on_uncaptured_error(
@@ -1147,7 +1112,7 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
         &self,
         pass_data: &mut crate::Data,
         index: u32,
-        bind_group_data: &crate::Data,
+        bind_group_data: Option<&crate::Data>,
         offsets: &[DynamicOffset],
     );
     fn compute_pass_set_push_constants(
@@ -1190,7 +1155,7 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
         &self,
         encoder_data: &mut crate::Data,
         index: u32,
-        bind_group_data: &crate::Data,
+        bind_group_data: Option<&crate::Data>,
         offsets: &[DynamicOffset],
     );
     #[allow(clippy::too_many_arguments)]
@@ -1243,47 +1208,13 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
         indirect_buffer_data: &crate::Data,
         indirect_offset: BufferAddress,
     );
-    fn render_bundle_encoder_multi_draw_indirect(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        count: u32,
-    );
-    fn render_bundle_encoder_multi_draw_indexed_indirect(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        count: u32,
-    );
-    #[allow(clippy::too_many_arguments)]
-    fn render_bundle_encoder_multi_draw_indirect_count(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        count_buffer_data: &crate::Data,
-        count_buffer_offset: BufferAddress,
-        max_count: u32,
-    );
-    #[allow(clippy::too_many_arguments)]
-    fn render_bundle_encoder_multi_draw_indexed_indirect_count(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        command_buffer_data: &crate::Data,
-        count_buffer_offset: BufferAddress,
-        max_count: u32,
-    );
 
     fn render_pass_set_pipeline(&self, pass_data: &mut crate::Data, pipeline_data: &crate::Data);
     fn render_pass_set_bind_group(
         &self,
         pass_data: &mut crate::Data,
         index: u32,
-        bind_group_data: &crate::Data,
+        bind_group_data: Option<&crate::Data>,
         offsets: &[DynamicOffset],
     );
     #[allow(clippy::too_many_arguments)]
@@ -1422,6 +1353,7 @@ impl<T> DynContext for T
 where
     T: Context + 'static,
 {
+    #[cfg(not(target_os = "emscripten"))]
     fn as_any(&self) -> &dyn Any {
         self
     }
@@ -1564,11 +1496,6 @@ where
         Context::device_limits(self, device_data)
     }
 
-    fn device_downlevel_properties(&self, device_data: &crate::Data) -> DownlevelCapabilities {
-        let device_data = downcast_ref(device_data);
-        Context::device_downlevel_properties(self, device_data)
-    }
-
     fn device_create_shader_module(
         &self,
         device_data: &crate::Data,
@@ -1736,11 +1663,6 @@ where
         Context::device_destroy(self, device_data)
     }
 
-    fn device_mark_lost(&self, device_data: &crate::Data, message: &str) {
-        let device_data = downcast_ref(device_data);
-        Context::device_mark_lost(self, device_data, message)
-    }
-
     fn queue_drop(&self, queue_data: &crate::Data) {
         let queue_data = downcast_ref(queue_data);
         Context::queue_drop(self, queue_data)
@@ -2239,12 +2161,12 @@ where
         &self,
         pass_data: &mut crate::Data,
         index: u32,
-        bind_group_data: &crate::Data,
+        bind_group_data: Option<&crate::Data>,
         offsets: &[DynamicOffset],
     ) {
         let pass_data = downcast_mut::<T::ComputePassData>(pass_data);
-        let bind_group_data = downcast_ref(bind_group_data);
-        Context::compute_pass_set_bind_group(self, pass_data, index, bind_group_data, offsets)
+        let bg = bind_group_data.map(downcast_ref);
+        Context::compute_pass_set_bind_group(self, pass_data, index, bg, offsets)
     }
 
     fn compute_pass_set_push_constants(
@@ -2350,18 +2272,12 @@ where
         &self,
         encoder_data: &mut crate::Data,
         index: u32,
-        bind_group_data: &crate::Data,
+        bind_group_data: Option<&crate::Data>,
         offsets: &[DynamicOffset],
     ) {
         let encoder_data = downcast_mut::<T::RenderBundleEncoderData>(encoder_data);
-        let bind_group_data = downcast_ref(bind_group_data);
-        Context::render_bundle_encoder_set_bind_group(
-            self,
-            encoder_data,
-            index,
-            bind_group_data,
-            offsets,
-        )
+        let bg = bind_group_data.map(downcast_ref);
+        Context::render_bundle_encoder_set_bind_group(self, encoder_data, index, bg, offsets)
     }
 
     fn render_bundle_encoder_set_index_buffer(
@@ -2474,88 +2390,6 @@ where
         )
     }
 
-    fn render_bundle_encoder_multi_draw_indirect(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        count: u32,
-    ) {
-        let encoder_data = downcast_mut::<T::RenderBundleEncoderData>(encoder_data);
-        let indirect_buffer_data = downcast_ref(indirect_buffer_data);
-        Context::render_bundle_encoder_multi_draw_indirect(
-            self,
-            encoder_data,
-            indirect_buffer_data,
-            indirect_offset,
-            count,
-        )
-    }
-
-    fn render_bundle_encoder_multi_draw_indexed_indirect(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        count: u32,
-    ) {
-        let encoder_data = downcast_mut::<T::RenderBundleEncoderData>(encoder_data);
-        let indirect_buffer_data = downcast_ref(indirect_buffer_data);
-        Context::render_bundle_encoder_multi_draw_indexed_indirect(
-            self,
-            encoder_data,
-            indirect_buffer_data,
-            indirect_offset,
-            count,
-        )
-    }
-
-    fn render_bundle_encoder_multi_draw_indirect_count(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        count_buffer_data: &crate::Data,
-        count_buffer_offset: BufferAddress,
-        max_count: u32,
-    ) {
-        let encoder_data = downcast_mut::<T::RenderBundleEncoderData>(encoder_data);
-        let indirect_buffer_data = downcast_ref(indirect_buffer_data);
-        let count_buffer_data = downcast_ref(count_buffer_data);
-        Context::render_bundle_encoder_multi_draw_indirect_count(
-            self,
-            encoder_data,
-            indirect_buffer_data,
-            indirect_offset,
-            count_buffer_data,
-            count_buffer_offset,
-            max_count,
-        )
-    }
-
-    fn render_bundle_encoder_multi_draw_indexed_indirect_count(
-        &self,
-        encoder_data: &mut crate::Data,
-        indirect_buffer_data: &crate::Data,
-        indirect_offset: BufferAddress,
-        count_buffer_data: &crate::Data,
-        count_buffer_offset: BufferAddress,
-        max_count: u32,
-    ) {
-        let encoder_data = downcast_mut::<T::RenderBundleEncoderData>(encoder_data);
-        let indirect_buffer_data = downcast_ref(indirect_buffer_data);
-        let count_buffer_data = downcast_ref(count_buffer_data);
-        Context::render_bundle_encoder_multi_draw_indexed_indirect_count(
-            self,
-            encoder_data,
-            indirect_buffer_data,
-            indirect_offset,
-            count_buffer_data,
-            count_buffer_offset,
-            max_count,
-        )
-    }
-
     fn render_pass_set_pipeline(&self, pass_data: &mut crate::Data, pipeline_data: &crate::Data) {
         let pass_data = downcast_mut::<T::RenderPassData>(pass_data);
         let pipeline_data = downcast_ref(pipeline_data);
@@ -2566,12 +2400,12 @@ where
         &self,
         pass_data: &mut crate::Data,
         index: u32,
-        bind_group_data: &crate::Data,
+        bind_group_data: Option<&crate::Data>,
         offsets: &[DynamicOffset],
     ) {
         let pass_data = downcast_mut::<T::RenderPassData>(pass_data);
-        let bind_group_data = downcast_ref(bind_group_data);
-        Context::render_pass_set_bind_group(self, pass_data, index, bind_group_data, offsets)
+        let bg = bind_group_data.map(downcast_ref);
+        Context::render_pass_set_bind_group(self, pass_data, index, bg, offsets)
     }
 
     fn render_pass_set_index_buffer(
@@ -2864,6 +2698,7 @@ pub trait QueueWriteBuffer: WasmNotSendSync + Debug {
 
     fn slice_mut(&mut self) -> &mut [u8];
 
+    #[cfg(not(target_os = "emscripten"))]
     fn as_any(&self) -> &dyn Any;
 }
 
diff --git a/wgpu/src/util/encoder.rs b/wgpu/src/util/encoder.rs
index bef8fe950..c6d42e3eb 100644
--- a/wgpu/src/util/encoder.rs
+++ b/wgpu/src/util/encoder.rs
@@ -10,7 +10,12 @@ pub trait RenderEncoder<'a> {
     /// in the active pipeline when any `draw()` function is called must match the layout of this bind group.
     ///
     /// If the bind group have dynamic offsets, provide them in order of their declaration.
-    fn set_bind_group(&mut self, index: u32, bind_group: &'a BindGroup, offsets: &[DynamicOffset]);
+    fn set_bind_group(
+        &mut self,
+        index: u32,
+        bind_group: Option<&'a BindGroup>,
+        offsets: &[DynamicOffset],
+    );
 
     /// Sets the active render pipeline.
     ///
@@ -101,7 +106,12 @@ pub trait RenderEncoder<'a> {
 
 impl<'a> RenderEncoder<'a> for RenderPass<'a> {
     #[inline(always)]
-    fn set_bind_group(&mut self, index: u32, bind_group: &'a BindGroup, offsets: &[DynamicOffset]) {
+    fn set_bind_group(
+        &mut self,
+        index: u32,
+        bind_group: Option<&'a BindGroup>,
+        offsets: &[DynamicOffset],
+    ) {
         Self::set_bind_group(self, index, bind_group, offsets);
     }
 
@@ -152,7 +162,12 @@ impl<'a> RenderEncoder<'a> for RenderPass<'a> {
 
 impl<'a> RenderEncoder<'a> for RenderBundleEncoder<'a> {
     #[inline(always)]
-    fn set_bind_group(&mut self, index: u32, bind_group: &'a BindGroup, offsets: &[DynamicOffset]) {
+    fn set_bind_group(
+        &mut self,
+        index: u32,
+        bind_group: Option<&'a BindGroup>,
+        offsets: &[DynamicOffset],
+    ) {
         Self::set_bind_group(self, index, bind_group, offsets);
     }
 


Test patch:
diff --git a/naga/tests/snapshots.rs b/naga/tests/snapshots.rs
index 69e5922b3..0e285e7b0 100644
--- a/naga/tests/snapshots.rs
+++ b/naga/tests/snapshots.rs
@@ -14,8 +14,12 @@ const BASE_DIR_OUT: &str = "tests/out";
 bitflags::bitflags! {
     #[derive(Clone, Copy)]
     struct Targets: u32 {
+        /// A serialization of the `naga::Module`, in RON format.
         const IR = 1;
+
+        /// A serialization of the `naga::valid::ModuleInfo`, in RON format.
         const ANALYSIS = 1 << 1;
+
         const SPIRV = 1 << 2;
         const METAL = 1 << 3;
         const GLSL = 1 << 4;
diff --git a/naga/tests/validation.rs b/naga/tests/validation.rs
index c7af7b4c0..f20ae688b 100644
--- a/naga/tests/validation.rs
+++ b/naga/tests/validation.rs
@@ -316,6 +316,8 @@ fn main() {{
 #[cfg(feature = "wgsl-in")]
 #[test]
 fn incompatible_interpolation_and_sampling_types() {
+    use dummy_interpolation_shader::DummyInterpolationShader;
+
     // NOTE: Things we expect to actually compile are in the `interpolate` snapshot test.
     use itertools::Itertools;
 
@@ -396,6 +398,8 @@ fn incompatible_interpolation_and_sampling_types() {
 #[cfg(all(feature = "wgsl-in", feature = "glsl-out"))]
 #[test]
 fn no_flat_first_in_glsl() {
+    use dummy_interpolation_shader::DummyInterpolationShader;
+
     let DummyInterpolationShader {
         source: _,
         module,
@@ -432,44 +436,46 @@ fn no_flat_first_in_glsl() {
     ));
 }
 
-struct DummyInterpolationShader {
-    source: String,
-    module: naga::Module,
-    interpolate_attr: String,
-    entry_point: &'static str,
-}
-
-impl DummyInterpolationShader {
-    fn new(interpolation: naga::Interpolation, sampling: Option<naga::Sampling>) -> Self {
-        // NOTE: If you have to add variants below, make sure to add them to the
-        // `cartesian_product`'d combinations in tests around here!
-        let interpolation_str = match interpolation {
-            naga::Interpolation::Flat => "flat",
-            naga::Interpolation::Linear => "linear",
-            naga::Interpolation::Perspective => "perspective",
-        };
-        let sampling_str = match sampling {
-            None => String::new(),
-            Some(sampling) => format!(
-                ", {}",
-                match sampling {
-                    naga::Sampling::First => "first",
-                    naga::Sampling::Either => "either",
-                    naga::Sampling::Center => "center",
-                    naga::Sampling::Centroid => "centroid",
-                    naga::Sampling::Sample => "sample",
-                }
-            ),
-        };
-        let member_type = match interpolation {
-            naga::Interpolation::Perspective | naga::Interpolation::Linear => "f32",
-            naga::Interpolation::Flat => "u32",
-        };
+#[cfg(all(test, feature = "wgsl-in"))]
+mod dummy_interpolation_shader {
+    pub struct DummyInterpolationShader {
+        pub source: String,
+        pub module: naga::Module,
+        pub interpolate_attr: String,
+        pub entry_point: &'static str,
+    }
 
-        let interpolate_attr = format!("@interpolate({interpolation_str}{sampling_str})");
-        let source = format!(
-            "\
-struct VertexOutput {{
+    impl DummyInterpolationShader {
+        pub fn new(interpolation: naga::Interpolation, sampling: Option<naga::Sampling>) -> Self {
+            // NOTE: If you have to add variants below, make sure to add them to the
+            // `cartesian_product`'d combinations in tests around here!
+            let interpolation_str = match interpolation {
+                naga::Interpolation::Flat => "flat",
+                naga::Interpolation::Linear => "linear",
+                naga::Interpolation::Perspective => "perspective",
+            };
+            let sampling_str = match sampling {
+                None => String::new(),
+                Some(sampling) => format!(
+                    ", {}",
+                    match sampling {
+                        naga::Sampling::First => "first",
+                        naga::Sampling::Either => "either",
+                        naga::Sampling::Center => "center",
+                        naga::Sampling::Centroid => "centroid",
+                        naga::Sampling::Sample => "sample",
+                    }
+                ),
+            };
+            let member_type = match interpolation {
+                naga::Interpolation::Perspective | naga::Interpolation::Linear => "f32",
+                naga::Interpolation::Flat => "u32",
+            };
+
+            let interpolate_attr = format!("@interpolate({interpolation_str}{sampling_str})");
+            let source = format!(
+                "\
+                struct VertexOutput {{
     @location(0) {interpolate_attr} member: {member_type},
 }}
 
@@ -478,14 +484,15 @@ fn main(input: VertexOutput) {{
     // ...
 }}
 "
-        );
-        let module = naga::front::wgsl::parse_str(&source).unwrap();
+            );
+            let module = naga::front::wgsl::parse_str(&source).unwrap();
 
-        Self {
-            source,
-            module,
-            interpolate_attr,
-            entry_point: "main",
+            Self {
+                source,
+                module,
+                interpolate_attr,
+                entry_point: "main",
+            }
         }
     }
 }
diff --git a/player/tests/data/bind-group.ron b/player/tests/data/bind-group.ron
index a53a77b16..87b3a1c72 100644
--- a/player/tests/data/bind-group.ron
+++ b/player/tests/data/bind-group.ron
@@ -70,7 +70,7 @@
                         SetBindGroup(
                             index: 0,
                             num_dynamic_offsets: 0,
-                            bind_group_id: Id(0, 1, Empty),
+                            bind_group_id: Some(Id(0, 1, Empty)),
                         ),
                         SetPipeline(Id(0, 1, Empty)),
                     ],
diff --git a/player/tests/data/zero-init-buffer.ron b/player/tests/data/zero-init-buffer.ron
index b13786e26..2b3f4a890 100644
--- a/player/tests/data/zero-init-buffer.ron
+++ b/player/tests/data/zero-init-buffer.ron
@@ -149,7 +149,7 @@
                         SetBindGroup(
                             index: 0,
                             num_dynamic_offsets: 0,
-                            bind_group_id: Id(0, 1, Empty),
+                            bind_group_id: Some(Id(0, 1, Empty)),
                         ),
                         Dispatch((4, 1, 1)),
                     ],
diff --git a/player/tests/data/zero-init-texture-binding.ron b/player/tests/data/zero-init-texture-binding.ron
index ba4951c19..e828b6c13 100644
--- a/player/tests/data/zero-init-texture-binding.ron
+++ b/player/tests/data/zero-init-texture-binding.ron
@@ -150,7 +150,7 @@
                         SetBindGroup(
                             index: 0,
                             num_dynamic_offsets: 0,
-                            bind_group_id: Id(0, 1, Empty),
+                            bind_group_id: Some(Id(0, 1, Empty)),
                         ),
                         Dispatch((4, 1, 1)),
                     ],
diff --git a/tests/src/image.rs b/tests/src/image.rs
index e72d3ee44..602d93c4e 100644
--- a/tests/src/image.rs
+++ b/tests/src/image.rs
@@ -377,7 +377,7 @@ fn copy_via_compute(
         let mut pass = encoder.begin_compute_pass(&ComputePassDescriptor::default());
 
         pass.set_pipeline(&pipeline_copy);
-        pass.set_bind_group(0, &bg, &[]);
+        pass.set_bind_group(0, Some(&bg), &[]);
         pass.dispatch_workgroups(1, 1, 1);
     }
 
diff --git a/tests/tests/bgra8unorm_storage.rs b/tests/tests/bgra8unorm_storage.rs
index 0859473b2..fa8310c7e 100644
--- a/tests/tests/bgra8unorm_storage.rs
+++ b/tests/tests/bgra8unorm_storage.rs
@@ -110,7 +110,7 @@ static BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new()
                 timestamp_writes: None,
             });
 
-            pass.set_bind_group(0, &bg, &[]);
+            pass.set_bind_group(0, Some(&bg), &[]);
             pass.set_pipeline(&pipeline);
             pass.dispatch_workgroups(256, 256, 1);
         }
diff --git a/tests/tests/bind_group_layout_dedup.rs b/tests/tests/bind_group_layout_dedup.rs
index b322b019b..f81360288 100644
--- a/tests/tests/bind_group_layout_dedup.rs
+++ b/tests/tests/bind_group_layout_dedup.rs
@@ -92,11 +92,11 @@ async fn bgl_dedupe(ctx: TestingContext) {
         timestamp_writes: None,
     });
 
-    pass.set_bind_group(0, &bg_1b, &[]);
+    pass.set_bind_group(0, Some(&bg_1b), &[]);
     pass.set_pipeline(&pipeline);
     pass.dispatch_workgroups(1, 1, 1);
 
-    pass.set_bind_group(0, &bg_1a, &[]);
+    pass.set_bind_group(0, Some(&bg_1a), &[]);
     pass.dispatch_workgroups(1, 1, 1);
 
     drop(pass);
@@ -179,7 +179,7 @@ fn bgl_dedupe_with_dropped_user_handle(ctx: TestingContext) {
         timestamp_writes: None,
     });
 
-    pass.set_bind_group(0, &bg, &[]);
+    pass.set_bind_group(0, Some(&bg), &[]);
     pass.set_pipeline(&pipeline);
     pass.dispatch_workgroups(1, 1, 1);
 
@@ -250,10 +250,10 @@ fn get_derived_bgl(ctx: TestingContext) {
 
     pass.set_pipeline(&pipeline);
 
-    pass.set_bind_group(0, &bg1, &[]);
+    pass.set_bind_group(0, Some(&bg1), &[]);
     pass.dispatch_workgroups(1, 1, 1);
 
-    pass.set_bind_group(0, &bg2, &[]);
+    pass.set_bind_group(0, Some(&bg2), &[]);
     pass.dispatch_workgroups(1, 1, 1);
 
     drop(pass);
@@ -313,7 +313,7 @@ fn separate_pipelines_have_incompatible_derived_bgls(ctx: TestingContext) {
     pass.set_pipeline(&pipeline1);
 
     // We use the wrong bind group for this pipeline here. This should fail.
-    pass.set_bind_group(0, &bg2, &[]);
+    pass.set_bind_group(0, Some(&bg2), &[]);
     pass.dispatch_workgroups(1, 1, 1);
 
     fail(
@@ -385,7 +385,7 @@ fn derived_bgls_incompatible_with_regular_bgls(ctx: TestingContext) {
 
     pass.set_pipeline(&pipeline);
 
-    pass.set_bind_group(0, &bg, &[]);
+    pass.set_bind_group(0, Some(&bg), &[]);
     pass.dispatch_workgroups(1, 1, 1);
 
     fail(
@@ -476,8 +476,8 @@ fn bgl_dedupe_derived(ctx: TestingContext) {
         timestamp_writes: None,
     });
     pass.set_pipeline(&pipeline);
-    pass.set_bind_group(0, &bind_group_0, &[]);
-    pass.set_bind_group(1, &bind_group_1, &[]);
+    pass.set_bind_group(0, Some(&bind_group_0), &[]);
+    pass.set_bind_group(1, Some(&bind_group_1), &[]);
     pass.dispatch_workgroups(1, 1, 1);
 
     drop(pass);
diff --git a/tests/tests/buffer.rs b/tests/tests/buffer.rs
index b3a48f178..f76a9fc35 100644
--- a/tests/tests/buffer.rs
+++ b/tests/tests/buffer.rs
@@ -328,7 +328,7 @@ static MINIMUM_BUFFER_BINDING_SIZE_DISPATCH: GpuTestConfiguration = GpuTestConfi
                     timestamp_writes: None,
                 });
 
-                pass.set_bind_group(0, &bind_group, &[]);
+                pass.set_bind_group(0, Some(&bind_group), &[]);
                 pass.set_pipeline(&pipeline);
                 pass.dispatch_workgroups(1, 1, 1);
 
diff --git a/tests/tests/clear_texture.rs b/tests/tests/clear_texture.rs
index f62e2be21..484681130 100644
--- a/tests/tests/clear_texture.rs
+++ b/tests/tests/clear_texture.rs
@@ -26,7 +26,7 @@ static TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT: &[wgpu::TextureFormat] = &[
     wgpu::TextureFormat::Bgra8UnormSrgb,
     wgpu::TextureFormat::Rgb10a2Uint,
     wgpu::TextureFormat::Rgb10a2Unorm,
-    wgpu::TextureFormat::Rg11b10UFloat,
+    wgpu::TextureFormat::Rg11b10Ufloat,
     wgpu::TextureFormat::Rg32Uint,
     wgpu::TextureFormat::Rg32Sint,
     wgpu::TextureFormat::Rg32Float,
diff --git a/tests/tests/compute_pass_ownership.rs b/tests/tests/compute_pass_ownership.rs
index df50f51e0..6b5bad8cf 100644
--- a/tests/tests/compute_pass_ownership.rs
+++ b/tests/tests/compute_pass_ownership.rs
@@ -45,7 +45,7 @@ async fn compute_pass_resource_ownership(ctx: TestingContext) {
     {
         let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());
         cpass.set_pipeline(&pipeline);
-        cpass.set_bind_group(0, &bind_group, &[]);
+        cpass.set_bind_group(0, Some(&bind_group), &[]);
         cpass.dispatch_workgroups_indirect(&indirect_buffer, 0);
 
         // Now drop all resources we set. Then do a device poll to make sure the resources are really not dropped too early, no matter what.
@@ -95,7 +95,7 @@ async fn compute_pass_query_set_ownership_pipeline_statistics(ctx: TestingContex
     {
         let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());
         cpass.set_pipeline(&pipeline);
-        cpass.set_bind_group(0, &bind_group, &[]);
+        cpass.set_bind_group(0, Some(&bind_group), &[]);
         cpass.begin_pipeline_statistics_query(&query_set, 0);
         cpass.dispatch_workgroups(1, 1, 1);
         cpass.end_pipeline_statistics_query();
@@ -153,7 +153,7 @@ async fn compute_pass_query_set_ownership_timestamps(ctx: TestingContext) {
             }),
         });
         cpass.set_pipeline(&pipeline);
-        cpass.set_bind_group(0, &bind_group, &[]);
+        cpass.set_bind_group(0, Some(&bind_group), &[]);
         cpass.write_timestamp(&query_set_write_timestamp, 0);
         cpass.dispatch_workgroups(1, 1, 1);
 
@@ -203,7 +203,7 @@ async fn compute_pass_keep_encoder_alive(ctx: TestingContext) {
 
     // Record some draw commands.
     cpass.set_pipeline(&pipeline);
-    cpass.set_bind_group(0, &bind_group, &[]);
+    cpass.set_bind_group(0, Some(&bind_group), &[]);
     cpass.dispatch_workgroups_indirect(&indirect_buffer, 0);
 
     // Dropping the pass will still execute the pass, even though there's no way to submit it.
diff --git a/tests/tests/mem_leaks.rs b/tests/tests/mem_leaks.rs
index 75de0776e..84879efda 100644
--- a/tests/tests/mem_leaks.rs
+++ b/tests/tests/mem_leaks.rs
@@ -194,7 +194,7 @@ async fn draw_test_with_reports(
     });
 
     rpass.set_pipeline(&pipeline);
-    rpass.set_bind_group(0, &bg, &[]);
+    rpass.set_bind_group(0, Some(&bg), &[]);
 
     let global_report = ctx.instance.generate_report().unwrap();
     let report = global_report.hub_report();
diff --git a/tests/tests/nv12_texture/mod.rs b/tests/tests/nv12_texture/mod.rs
index 6ded163a3..d6af8496f 100644
--- a/tests/tests/nv12_texture/mod.rs
+++ b/tests/tests/nv12_texture/mod.rs
@@ -115,7 +115,7 @@ static NV12_TEXTURE_CREATION_SAMPLING: GpuTestConfiguration = GpuTestConfigurati
             occlusion_query_set: None,
         });
         rpass.set_pipeline(&pipeline);
-        rpass.set_bind_group(0, &bind_group, &[]);
+        rpass.set_bind_group(0, Some(&bind_group), &[]);
         rpass.draw(0..4, 0..1);
         drop(rpass);
         ctx.queue.submit(Some(encoder.finish()));
diff --git a/tests/tests/partially_bounded_arrays/mod.rs b/tests/tests/partially_bounded_arrays/mod.rs
index 195fd88dd..4e6d6fc09 100644
--- a/tests/tests/partially_bounded_arrays/mod.rs
+++ b/tests/tests/partially_bounded_arrays/mod.rs
@@ -90,7 +90,7 @@ static PARTIALLY_BOUNDED_ARRAY: GpuTestConfiguration = GpuTestConfiguration::new
                 timestamp_writes: None,
             });
             cpass.set_pipeline(&compute_pipeline);
-            cpass.set_bind_group(0, &bind_group, &[]);
+            cpass.set_bind_group(0, Some(&bind_group), &[]);
             cpass.dispatch_workgroups(1, 1, 1);
         }
 
diff --git a/tests/tests/pipeline_cache.rs b/tests/tests/pipeline_cache.rs
index 67e9e6827..c88a871c7 100644
--- a/tests/tests/pipeline_cache.rs
+++ b/tests/tests/pipeline_cache.rs
@@ -32,7 +32,7 @@ fn shader() -> String {
         r#"
         @group(0) @binding(0)
         var<storage, read_write> output: array<u32>;
-    
+
         @compute @workgroup_size(1)
         fn main() {{
         {body}
@@ -167,7 +167,7 @@ async fn validate_pipeline(
             timestamp_writes: None,
         });
         cpass.set_pipeline(&pipeline);
-        cpass.set_bind_group(0, bind_group, &[]);
+        cpass.set_bind_group(0, Some(bind_group), &[]);
 
         cpass.dispatch_workgroups(1, 1, 1);
     }
diff --git a/tests/tests/poll.rs b/tests/tests/poll.rs
index 7e99cbcd7..aeea2617f 100644
--- a/tests/tests/poll.rs
+++ b/tests/tests/poll.rs
@@ -46,7 +46,7 @@ fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer {
         .create_command_encoder(&CommandEncoderDescriptor::default());
 
     let mut cpass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::default());
-    cpass.set_bind_group(0, &bind_group, &[]);
+    cpass.set_bind_group(0, Some(&bind_group), &[]);
     drop(cpass);
 
     cmd_buf.finish()
diff --git a/tests/tests/push_constants.rs b/tests/tests/push_constants.rs
index 905578d53..047fe5c8f 100644
--- a/tests/tests/push_constants.rs
+++ b/tests/tests/push_constants.rs
@@ -119,7 +119,7 @@ async fn partial_update_test(ctx: TestingContext) {
             timestamp_writes: None,
         });
         cpass.set_pipeline(&pipeline);
-        cpass.set_bind_group(0, &bind_group, &[]);
+        cpass.set_bind_group(0, Some(&bind_group), &[]);
 
         // -- Dispatch 0 --
 
diff --git a/tests/tests/regression/issue_3349.rs b/tests/tests/regression/issue_3349.rs
index 21929bd9b..b1361722f 100644
--- a/tests/tests/regression/issue_3349.rs
+++ b/tests/tests/regression/issue_3349.rs
@@ -163,7 +163,7 @@ async fn multi_stage_data_binding_test(ctx: TestingContext) {
         });
 
         rpass.set_pipeline(&pipeline);
-        rpass.set_bind_group(0, &bg, &[]);
+        rpass.set_bind_group(0, Some(&bg), &[]);
         rpass.set_push_constants(
             wgpu::ShaderStages::VERTEX_FRAGMENT,
             0,
diff --git a/tests/tests/render_pass_ownership.rs b/tests/tests/render_pass_ownership.rs
index 7c41c8591..5086d38d9 100644
--- a/tests/tests/render_pass_ownership.rs
+++ b/tests/tests/render_pass_ownership.rs
@@ -87,7 +87,7 @@ async fn render_pass_resource_ownership(ctx: TestingContext) {
         drop(depth_stencil_view);
 
         rpass.set_pipeline(&pipeline);
-        rpass.set_bind_group(0, &bind_group, &[]);
+        rpass.set_bind_group(0, Some(&bind_group), &[]);
         rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
         rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
         rpass.begin_occlusion_query(0);
@@ -163,7 +163,7 @@ async fn render_pass_query_set_ownership_pipeline_statistics(ctx: TestingContext
             ..Default::default()
         });
         rpass.set_pipeline(&pipeline);
-        rpass.set_bind_group(0, &bind_group, &[]);
+        rpass.set_bind_group(0, Some(&bind_group), &[]);
         rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
         rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
         rpass.begin_pipeline_statistics_query(&query_set, 0);
@@ -242,7 +242,7 @@ async fn render_pass_query_set_ownership_timestamps(ctx: TestingContext) {
         rpass.write_timestamp(&query_set_write_timestamp, 0);
 
         rpass.set_pipeline(&pipeline);
-        rpass.set_bind_group(0, &bind_group, &[]);
+        rpass.set_bind_group(0, Some(&bind_group), &[]);
         rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
         rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
         rpass.draw(0..3, 0..1);
@@ -305,7 +305,7 @@ async fn render_pass_keep_encoder_alive(ctx: TestingContext) {
 
     // Record some a draw command.
     rpass.set_pipeline(&pipeline);
-    rpass.set_bind_group(0, &bind_group, &[]);
+    rpass.set_bind_group(0, Some(&bind_group), &[]);
     rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
     rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
     rpass.draw(0..3, 0..1);
diff --git a/tests/tests/shader/mod.rs b/tests/tests/shader/mod.rs
index 7d6ed7aaa..2e7dfd942 100644
--- a/tests/tests/shader/mod.rs
+++ b/tests/tests/shader/mod.rs
@@ -349,7 +349,7 @@ async fn shader_input_output_test(
             timestamp_writes: None,
         });
         cpass.set_pipeline(&pipeline);
-        cpass.set_bind_group(0, &bg, &[]);
+        cpass.set_bind_group(0, Some(&bg), &[]);
 
         if let InputStorageType::PushConstant = storage_type {
             cpass.set_push_constants(0, bytemuck::cast_slice(&test.input_values))
diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs
index beacb4fcc..084498770 100644
--- a/tests/tests/shader/zero_init_workgroup_mem.rs
+++ b/tests/tests/shader/zero_init_workgroup_mem.rs
@@ -119,7 +119,7 @@ static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::
 
         cpass.set_pipeline(&pipeline_read);
         for i in 0..NR_OF_DISPATCHES {
-            cpass.set_bind_group(0, &bg, &[i * BUFFER_BINDING_SIZE]);
+            cpass.set_bind_group(0, Some(&bg), &[i * BUFFER_BINDING_SIZE]);
             cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2);
         }
         drop(cpass);
diff --git a/tests/tests/shader_view_format/mod.rs b/tests/tests/shader_view_format/mod.rs
index b2bc0426e..052573bc0 100644
--- a/tests/tests/shader_view_format/mod.rs
+++ b/tests/tests/shader_view_format/mod.rs
@@ -148,7 +148,7 @@ async fn reinterpret(
         occlusion_query_set: None,
     });
     rpass.set_pipeline(&pipeline);
-    rpass.set_bind_group(0, &bind_group, &[]);
+    rpass.set_bind_group(0, Some(&bind_group), &[]);
     rpass.draw(0..3, 0..1);
     drop(rpass);
     ctx.queue.submit(Some(encoder.finish()));
diff --git a/tests/tests/subgroup_operations/mod.rs b/tests/tests/subgroup_operations/mod.rs
index b11dd7f6a..ecf8adfd7 100644
--- a/tests/tests/subgroup_operations/mod.rs
+++ b/tests/tests/subgroup_operations/mod.rs
@@ -93,7 +93,7 @@ static SUBGROUP_OPERATIONS: GpuTestConfiguration = GpuTestConfiguration::new()
                 timestamp_writes: None,
             });
             cpass.set_pipeline(&compute_pipeline);
-            cpass.set_bind_group(0, &bind_group, &[]);
+            cpass.set_bind_group(0, Some(&bind_group), &[]);
             cpass.dispatch_workgroups(1, 1, 1);
         }
         ctx.queue.submit(Some(encoder.finish()));
diff --git a/tests/tests/vertex_formats/mod.rs b/tests/tests/vertex_formats/mod.rs
index d2ff09447..d447ac8f7 100644
--- a/tests/tests/vertex_formats/mod.rs
+++ b/tests/tests/vertex_formats/mod.rs
@@ -315,7 +315,7 @@ async fn vertex_formats_common(ctx: TestingContext, tests: &[Test<'_>]) {
 
         rpass.set_vertex_buffer(0, buffer_input.slice(..));
         rpass.set_pipeline(&pipeline);
-        rpass.set_bind_group(0, &bg, &[]);
+        rpass.set_bind_group(0, Some(&bg), &[]);
 
         // Draw three vertices and no instance, which is enough to generate the
         // checksums.
diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs
index d2b6a874f..5c5ce8a20 100644
--- a/tests/tests/vertex_indices/mod.rs
+++ b/tests/tests/vertex_indices/mod.rs
@@ -409,7 +409,7 @@ async fn vertex_index_common(ctx: TestingContext) {
             render_encoder.set_vertex_buffer(1, identity_buffer.slice(..));
             render_encoder.set_index_buffer(identity_buffer.slice(..), wgpu::IndexFormat::Uint32);
             render_encoder.set_pipeline(pipeline);
-            render_encoder.set_bind_group(0, &bg, &[]);
+            render_encoder.set_bind_group(0, Some(&bg), &[]);
 
             let draws = test.case.draws();