Repository: gfx-rs/wgpu
Pull request #5457 (2024-03-30 04:57:13)

docs: Fix incorrect git URL in README for naga-cli installation
**Connections**
n/a

**Description**
The naga README pointed readers to the now-archived repo [gfx-rs/naga](https://github.com/gfx-rs/naga), resulting in an old version of `naga-cli` (0.14.0) being installed.
Switching the URL to this repository fixes the issue.

**Testing**
n/a

<!-- 
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`.
- [ ] Run `cargo clippy`. If applicable, add:
  - [ ] `--target wasm32-unknown-unknown`
  - [ ] `--target wasm32-unknown-emscripten`
- [ ] Run `cargo xtask test` to run tests.
- [ ] Add change to `CHANGELOG.md`. See simple instructions inside file.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b98734c5e..7e447cc67 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,44 +52,44 @@ Bottom level categories:
 #### General
 
 - Many numeric built-ins have had a constant evaluation implementation added for them, which allows them to be used in a `const` context:
-    - [#4879](https://github.com/gfx-rs/wgpu/pull/4879) by @ErichDonGubler:
-        - `abs`
-        - `acos`
-        - `acosh`
-        - `asin`
-        - `asinh`
-        - `atan`
-        - `atanh`
-        - `cos`
-        - `cosh`
-        - `round`
-        - `saturate`
-        - `sin`
-        - `sinh`
-        - `sqrt`
-        - `step`
-        - `tan`
-        - `tanh`
-    - [#5098](https://github.com/gfx-rs/wgpu/pull/5098) by @ErichDonGubler:
-        - `ceil`
-        - `countLeadingZeros`
-        - `countOneBits`
-        - `countTrailingZeros`
-        - `degrees`
-        - `exp`
-        - `exp2`
-        - `floor`
-        - `fract`
-        - `fma`
-        - `inverseSqrt`
-        - `log`
-        - `log2`
-        - `max`
-        - `min`
-        - `radians`
-        - `reverseBits`
-        - `sign`
-        - `trunc`
+  - [#4879](https://github.com/gfx-rs/wgpu/pull/4879) by @ErichDonGubler:
+    - `abs`
+    - `acos`
+    - `acosh`
+    - `asin`
+    - `asinh`
+    - `atan`
+    - `atanh`
+    - `cos`
+    - `cosh`
+    - `round`
+    - `saturate`
+    - `sin`
+    - `sinh`
+    - `sqrt`
+    - `step`
+    - `tan`
+    - `tanh`
+  - [#5098](https://github.com/gfx-rs/wgpu/pull/5098) by @ErichDonGubler:
+    - `ceil`
+    - `countLeadingZeros`
+    - `countOneBits`
+    - `countTrailingZeros`
+    - `degrees`
+    - `exp`
+    - `exp2`
+    - `floor`
+    - `fract`
+    - `fma`
+    - `inverseSqrt`
+    - `log`
+    - `log2`
+    - `max`
+    - `min`
+    - `radians`
+    - `reverseBits`
+    - `sign`
+    - `trunc`
 - Eager release of GPU resources comes from device.trackers. By @bradwerth in [#5075](https://github.com/gfx-rs/wgpu/pull/5075)
 - `wgpu-types`'s `trace` and `replay` features have been replaced by the `serde` feature. By @KirmesBude in [#5149](https://github.com/gfx-rs/wgpu/pull/5149)
 - `wgpu-core`'s `serial-pass` feature has been removed. Use `serde` instead. By @KirmesBude in [#5149](https://github.com/gfx-rs/wgpu/pull/5149)
@@ -101,10 +101,12 @@ Bottom level categories:
   By @ErichDonGubler in [#5146](https://github.com/gfx-rs/wgpu/pull/5146), [#5046](https://github.com/gfx-rs/wgpu/pull/5046).
 - Signed and unsigned 64 bit integer support in shaders. By @rodolphito and @cwfitzgerald in [#5154](https://github.com/gfx-rs/wgpu/pull/5154)
 - `wgpu::Instance` can now report which `wgpu::Backends` are available based on the build configuration. By @wumpf [#5167](https://github.com/gfx-rs/wgpu/pull/5167)
-```diff
--wgpu::Instance::any_backend_feature_enabled()
-+!wgpu::Instance::enabled_backend_features().is_empty()
-```
+
+  ```diff
+  -wgpu::Instance::any_backend_feature_enabled()
+  +!wgpu::Instance::enabled_backend_features().is_empty()
+  ```
+
 - `wgpu::CommandEncoder::write_timestamp` requires now the new `wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS` feature which is available on all native backends but not on WebGPU (due to a spec change `write_timestamp` is no longer supported on WebGPU). By @wumpf in [#5188](https://github.com/gfx-rs/wgpu/pull/5188)
 - Breaking change: [`wgpu_core::pipeline::ProgrammableStageDescriptor`](https://docs.rs/wgpu-core/latest/wgpu_core/pipeline/struct.ProgrammableStageDescriptor.html#structfield.entry_point) is now optional. By @ErichDonGubler in [#5305](https://github.com/gfx-rs/wgpu/pull/5305).
 - `Features::downlevel{_webgl2,}_features` was made const by @MultisampledNight in [#5343](https://github.com/gfx-rs/wgpu/pull/5343)
@@ -157,11 +159,20 @@ Bottom level categories:
 - Fixes for being able to use an OpenGL 4.1 core context provided by macOS with wgpu. By @bes in [#5331](https://github.com/gfx-rs/wgpu/pull/5331).
 - Don't create a program for shader-clearing if that workaround isn't required. By @Dinnerbone in [#5348](https://github.com/gfx-rs/wgpu/pull/5348).
 - Fix crash when holding multiple devices on wayland/surfaceless. By @ashdnazg in [#5351](https://github.com/gfx-rs/wgpu/pull/5351).
+- Don't depend on bind group and bind group layout entry order in HAL. This caused incorrect severely incorrect command execution and, in some cases, crashes. By @ErichDonGubler in [#5421](https://github.com/gfx-rs/wgpu/pull/5421).
 
 #### Vulkan
 
 - Set object labels when the DEBUG flag is set, even if the VALIDATION flag is disabled. By @DJMcNab in [#5345](https://github.com/gfx-rs/wgpu/pull/5345).
 
+#### Metal
+
+- Don't depend on bind group and bind group layout entry order in HAL. This caused incorrect severely incorrect command execution and, in some cases, crashes. By @ErichDonGubler in [#5421](https://github.com/gfx-rs/wgpu/pull/5421).
+
+#### DX12
+
+- Don't depend on bind group and bind group layout entry order in HAL. This caused incorrect severely incorrect command execution and, in some cases, crashes. By @ErichDonGubler in [#5421](https://github.com/gfx-rs/wgpu/pull/5421).
+
 ## v0.19.3 (2024-03-01)
 
 This release includes `wgpu`, `wgpu-core`, and `wgpu-hal`. All other crates are unchanged.
@@ -183,6 +194,7 @@ By @cwfitzgerald in [#5325](https://github.com/gfx-rs/wgpu/pull/5325).
 #### General
 
 - Fix an issue where command encoders weren't properly freed if an error occurred during command encoding. By @ErichDonGubler in [#5251](https://github.com/gfx-rs/wgpu/pull/5251).
+- Fix incorrect validation causing all indexed draws on render bundles to fail. By @wumpf in [#5430](https://github.com/gfx-rs/wgpu/pull/5340).
 
 #### Android
 - Fix linking error when targeting android without `winit`. By @ashdnazg in [#5326](https://github.com/gfx-rs/wgpu/pull/5326).
diff --git a/Cargo.lock b/Cargo.lock
index e1aaddcfc..eb2e05042 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -708,6 +708,12 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
 
+[[package]]
+name = "cooked-waker"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f"
+
 [[package]]
 name = "core-foundation"
 version = "0.9.4"
@@ -970,26 +976,32 @@ dependencies = [
 
 [[package]]
 name = "deno_console"
-version = "0.125.0"
+version = "0.143.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92543d4f4d82f2350123bd4b60e97a73aba1a9bbca8c931e827459096dedabba"
+checksum = "1f770d8deb0eb0bfd596d242d9eaef5312ef57f0130964cb53c7f6a8107d13be"
 dependencies = [
  "deno_core",
 ]
 
 [[package]]
 name = "deno_core"
-version = "0.232.0"
+version = "0.272.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "229ffd108e028b148a1a5a6122f771bc7c37094170226f44b8b93b3a9b79d114"
+checksum = "07093891f2af763023614cfe2d1ce5f9ce5a7920c4fcf2f00911bd0d93083523"
 dependencies = [
  "anyhow",
+ "bincode",
+ "bit-set",
+ "bit-vec",
  "bytes",
+ "cooked-waker",
+ "deno_core_icudata",
  "deno_ops",
  "deno_unsync",
  "futures",
  "libc",
  "log",
+ "memoffset 0.9.1",
  "parking_lot",
  "pin-project",
  "serde",
@@ -997,16 +1009,23 @@ dependencies = [
  "serde_v8",
  "smallvec",
  "sourcemap",
+ "static_assertions",
  "tokio",
  "url",
  "v8",
 ]
 
+[[package]]
+name = "deno_core_icudata"
+version = "0.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1"
+
 [[package]]
 name = "deno_ops"
-version = "0.108.0"
+version = "0.148.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7dde627916f8539f3f0d2e754dda40810c8ca4d655f2eaac1ef54785a12fd27"
+checksum = "5bc73fc07ad26e71715d5a726d1dd228587c0d121a591b1931a0fcf958a2ec3b"
 dependencies = [
  "proc-macro-rules",
  "proc-macro2",
@@ -1028,9 +1047,9 @@ dependencies = [
 
 [[package]]
 name = "deno_url"
-version = "0.125.0"
+version = "0.143.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25ec92af225230fe4a429de0b5891f35b1ba5f143f8c1605bb7b9d1cb767ac73"
+checksum = "39d9e6ffd6a7157bfd3cf1385c59232e49587c9bbb898e64010f7f082242a203"
 dependencies = [
  "deno_core",
  "serde",
@@ -1039,9 +1058,9 @@ dependencies = [
 
 [[package]]
 name = "deno_web"
-version = "0.156.0"
+version = "0.174.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3aeef7522f46b3442e24a750ef914ca54aade2110d6540a66e4ea17b4eb68bb7"
+checksum = "708666b5b346e6880c1372006615814db7fc5ef36bd1785f0b0e4f8617082999"
 dependencies = [
  "async-trait",
  "base64-simd 0.8.0",
@@ -1071,9 +1090,9 @@ dependencies = [
 
 [[package]]
 name = "deno_webidl"
-version = "0.125.0"
+version = "0.143.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74b1a86e9a1dec0dc5d4dc132faee72ac50297f41e30f7cab57dd52dda380eed"
+checksum = "bddad93aa68e3c3c2d36976cd401af27a6ce750c23060e02401daf240f2acbe2"
 dependencies = [
  "deno_core",
 ]
@@ -1381,9 +1400,9 @@ dependencies = [
 
 [[package]]
 name = "fslock"
-version = "0.1.8"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57eafdd0c16f57161105ae1b98a1238f97645f2f588438b2949c99a2af9616bf"
+checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
 dependencies = [
  "libc",
  "winapi",
@@ -1675,6 +1694,15 @@ dependencies = [
  "bitflags 2.4.2",
 ]
 
+[[package]]
+name = "gzip-header"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
+dependencies = [
+ "crc32fast",
+]
+
 [[package]]
 name = "half"
 version = "2.4.0"
@@ -2048,6 +2076,15 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "metal"
 version = "0.27.0"
@@ -2251,7 +2288,7 @@ dependencies = [
  "bitflags 1.3.2",
  "cfg-if",
  "libc",
- "memoffset",
+ "memoffset 0.6.5",
 ]
 
 [[package]]
@@ -2264,7 +2301,7 @@ dependencies = [
  "bitflags 1.3.2",
  "cfg-if",
  "libc",
- "memoffset",
+ "memoffset 0.6.5",
 ]
 
 [[package]]
@@ -3057,12 +3094,11 @@ dependencies = [
 
 [[package]]
 name = "serde_v8"
-version = "0.141.0"
+version = "0.181.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc689cb316d67b200e9f7449ce76cceb7e483e0f828d1a9c3d057c4367b6c26e"
+checksum = "fd25bb66a20a1a405fb3733aaaf8a8a77a14fd55c8f5fd9db2a2e95bbd7eeab9"
 dependencies = [
  "bytes",
- "derive_more",
  "num-bigint",
  "serde",
  "smallvec",
@@ -3656,12 +3692,15 @@ dependencies = [
 
 [[package]]
 name = "v8"
-version = "0.81.0"
+version = "0.89.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b75f5f378b9b54aff3b10da8170d26af4cfd217f644cf671badcd13af5db4beb"
+checksum = "fe2197fbef82c98f7953d13568a961d4e1c663793b5caf3c74455a13918cdf33"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.4.2",
  "fslock",
+ "gzip-header",
+ "home",
+ "miniz_oxide",
  "once_cell",
  "which",
 ]
@@ -4209,14 +4248,15 @@ dependencies = [
 
 [[package]]
 name = "which"
-version = "4.4.2"
+version = "5.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
 dependencies = [
  "either",
  "home",
  "once_cell",
  "rustix",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index a059d7b86..7cb26434b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -165,11 +165,11 @@ web-sys = "0.3.69"
 web-time = "0.2.4"
 
 # deno dependencies
-deno_console = "0.125.0"
-deno_core = "0.232.0"
-deno_url = "0.125.0"
-deno_web = "0.156.0"
-deno_webidl = "0.125.0"
+deno_console = "0.143.0"
+deno_core = "0.272.0"
+deno_url = "0.143.0"
+deno_web = "0.174.0"
+deno_webidl = "0.143.0"
 deno_webgpu = { version = "0.85.0", path = "./deno_webgpu" }
 tokio = "1.36.0"
 termcolor = "1.4.1"
diff --git a/cts_runner/src/bootstrap.js b/cts_runner/src/bootstrap.js
index 640075e7e..b16884393 100644
--- a/cts_runner/src/bootstrap.js
+++ b/cts_runner/src/bootstrap.js
@@ -5,8 +5,7 @@
 //
 delete Object.prototype.__proto__;
 
-const core = Deno.core;
-const primordials = globalThis.__bootstrap.primordials;
+import { core, primordials } from "ext:core/mod.js";
 const {
   Error,
   ObjectDefineProperty,
@@ -25,9 +24,10 @@ import * as base64 from "ext:deno_web/05_base64.js";
 import * as encoding from "ext:deno_web/08_text_encoding.js";
 import { Console } from "ext:deno_console/01_console.js";
 import * as url from "ext:deno_url/00_url.js";
-import DOMException from "ext:deno_web/01_dom_exception.js";
+import { DOMException } from "ext:deno_web/01_dom_exception.js";
 import * as performance from "ext:deno_web/15_performance.js";
 import * as webgpu from "ext:deno_webgpu/01_webgpu.js";
+import * as imageData from "ext:deno_web/16_image_data.js";
 
 // imports needed to pass module evaluation
 import "ext:deno_url/01_urlpattern.js";
@@ -42,8 +42,6 @@ import "ext:deno_web/14_compression.js";
 
 let globalThis_;
 
-const { BadResource, Interrupted } = core;
-
 class NotFound extends Error {
   constructor(msg) {
     super(msg);
@@ -183,6 +181,7 @@ const windowOrWorkerGlobalScope = {
   clearInterval: util.writable(timers.clearInterval),
   clearTimeout: util.writable(timers.clearTimeout),
   performance: util.writable(performance.performance),
+  ImageData: core.propNonEnumerable(imageData.ImageData),
 
   GPU: util.nonEnumerable(webgpu.GPU),
   GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
@@ -248,10 +247,8 @@ core.registerErrorClass("NotFound", NotFound);
 core.registerErrorClass("AlreadyExists", AlreadyExists);
 core.registerErrorClass("InvalidData", InvalidData);
 core.registerErrorClass("TimedOut", TimedOut);
-core.registerErrorClass("Interrupted", Interrupted);
 core.registerErrorClass("WriteZero", WriteZero);
 core.registerErrorClass("UnexpectedEof", UnexpectedEof);
-core.registerErrorClass("BadResource", BadResource);
 core.registerErrorClass("NotSupported", NotSupported);
 core.registerErrorBuilder(
   "DOMExceptionOperationError",
diff --git a/cts_runner/src/main.rs b/cts_runner/src/main.rs
index fa1b8baa1..201fda80d 100644
--- a/cts_runner/src/main.rs
+++ b/cts_runner/src/main.rs
@@ -45,13 +45,13 @@ mod native {
             ],
             ..Default::default()
         };
-        let mut isolate = JsRuntime::new(options);
+        let mut js_runtime = JsRuntime::new(options);
         let args = args_iter.collect::<Vec<String>>();
         let cfg = json!({"args": args, "cwd": env::current_dir().unwrap().to_string_lossy() });
 
         {
-            let context = isolate.main_context();
-            let scope = &mut isolate.handle_scope();
+            let context = js_runtime.main_context();
+            let scope = &mut js_runtime.handle_scope();
             let context_local = v8::Local::new(scope, context);
             let global_obj = context_local.global(scope);
             let bootstrap_str = v8::String::new(scope, "bootstrap").unwrap();
@@ -65,20 +65,12 @@ mod native {
                 .unwrap();
         }
 
-        isolate.op_state().borrow_mut().put(Permissions {});
+        js_runtime.op_state().borrow_mut().put(Permissions {});
 
-        let mod_id = isolate.load_main_module(&specifier, None).await?;
-        let mod_rx = isolate.mod_evaluate(mod_id);
-
-        let rx = tokio::spawn(async move {
-            match mod_rx.await {
-                Ok(err @ Err(_)) => err,
-                _ => Ok(()),
-            }
-        });
-
-        isolate.run_event_loop(false).await?;
-        rx.await.unwrap()?;
+        let mod_id = js_runtime.load_main_es_module(&specifier).await?;
+        let result = js_runtime.mod_evaluate(mod_id);
+        js_runtime.run_event_loop(Default::default()).await?;
+        result.await?;
 
         Ok(())
     }
@@ -87,7 +79,7 @@ mod native {
         cts_runner,
         deps = [deno_webidl, deno_web],
         ops = [op_exit, op_read_file_sync, op_write_file_sync],
-        esm_entry_point = "ext:cts_runner/bootstrap.js",
+        esm_entry_point = "ext:cts_runner/src/bootstrap.js",
         esm = ["src/bootstrap.js"],
     );
 
diff --git a/deno_webgpu/01_webgpu.js b/deno_webgpu/01_webgpu.js
index d7a88ad44..6aecb0323 100644
--- a/deno_webgpu/01_webgpu.js
+++ b/deno_webgpu/01_webgpu.js
@@ -6,12 +6,11 @@
 /// <reference path="../web/lib.deno_web.d.ts" />
 /// <reference path="./lib.deno_webgpu.d.ts" />
 
-const core = globalThis.Deno.core;
-const ops = core.ops;
-const primordials = globalThis.__bootstrap.primordials;
+import { core, primordials } from "ext:core/mod.js";
+import * as ops from "ext:core/ops";
 import * as webidl from "ext:deno_webidl/00_webidl.js";
 import { EventTarget } from "ext:deno_web/02_event.js";
-import DOMException from "ext:deno_web/01_dom_exception.js";
+import { DOMException } from "ext:deno_web/01_dom_exception.js";
 import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
 const {
   ArrayBuffer,
@@ -247,8 +246,7 @@ class GPU {
       "Argument 1",
     );
 
-    const { err, ...data } = await core.opAsync(
-      "op_webgpu_request_adapter",
+    const { err, ...data } = await ops.op_webgpu_request_adapter(
       options.powerPreference,
       options.forceFallbackAdapter,
     );
@@ -338,8 +336,7 @@ class GPUAdapter {
       }
     }
 
-    const { rid, features, limits } = await core.opAsync(
-      "op_webgpu_request_device",
+    const { rid, features, limits } = await ops.op_webgpu_request_device(
       this[_adapter].rid,
       descriptor.label,
       requiredFeatures,
@@ -377,10 +374,7 @@ class GPUAdapter {
       architecture,
       device,
       description,
-    } = await core.opAsync(
-      "op_webgpu_request_adapter_info",
-      this[_adapter].rid,
-    );
+    } = await ops.op_webgpu_request_adapter_info(this[_adapter].rid);
 
     const adapterInfo = webidl.createBranded(GPUAdapterInfo);
     adapterInfo[_vendor] = unmaskHints.includes("vendor") ? vendor : "";
@@ -677,6 +671,7 @@ class GPUSupportedFeatures {
   constructor() {
     webidl.illegalConstructor();
   }
+
   [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
     if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) {
       return `${this.constructor.name} ${
@@ -687,23 +682,23 @@ class GPUSupportedFeatures {
       return `${this.constructor.name} ${inspect({}, inspectOptions)}`;
     }
   }
+}
+const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype;
 
-  const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype;
-
-  /**
-   * @param {string | undefined} reason
-   * @param {string} message
-   * @returns {GPUDeviceLostInfo}
-   */
-  function createGPUDeviceLostInfo(reason, message) {
-    /** @type {GPUDeviceLostInfo} */
-    const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo);
-    deviceLostInfo[_reason] = reason ?? "unknown";
-    deviceLostInfo[_message] = message;
-    return deviceLostInfo;
-  }
+/**
+ * @param {string | undefined} reason
+ * @param {string} message
+ * @returns {GPUDeviceLostInfo}
+ */
+function createGPUDeviceLostInfo(reason, message) {
+  /** @type {GPUDeviceLostInfo} */
+  const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo);
+  deviceLostInfo[_reason] = reason ?? "unknown";
+  deviceLostInfo[_message] = message;
+  return deviceLostInfo;
+}
 
-  class GPUDeviceLostInfo {
+class GPUDeviceLostInfo {
   /** @type {string} */
   [_reason];
   /** @type {string} */
diff --git a/naga/README.md b/naga/README.md
index b38f18d3b..0e07d4049 100644
--- a/naga/README.md
+++ b/naga/README.md
@@ -42,7 +42,7 @@ First, install `naga-cli` from crates.io or directly from GitHub.
 cargo install naga-cli
 
 # development version
-cargo install naga-cli --git https://github.com/gfx-rs/naga.git
+cargo install naga-cli --git https://github.com/gfx-rs/wgpu.git
 ```
 
 Then, you can run `naga` command.
diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs
index e2848f737..47beda8ec 100644
--- a/wgpu-core/src/command/bundle.rs
+++ b/wgpu-core/src/command/bundle.rs
@@ -166,7 +166,7 @@ fn validate_indexed_draw<A: HalApi>(
 ) -> Result<(), DrawError> {
     let last_index = first_index as u64 + index_count as u64;
     let index_limit = index_state.limit();
-    if last_index <= index_limit {
+    if last_index > index_limit {
         return Err(DrawError::IndexBeyondLimit {
             last_index,
             index_limit,
diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs
index 994126c26..23bd409dc 100644
--- a/wgpu-hal/src/dx12/device.rs
+++ b/wgpu-hal/src/dx12/device.rs
@@ -1100,7 +1100,16 @@ impl crate::Device for super::Device {
         }
         let mut dynamic_buffers = Vec::new();
 
-        for (layout, entry) in desc.layout.entries.iter().zip(desc.entries.iter()) {
+        let layout_and_entry_iter = desc.entries.iter().map(|entry| {
+            let layout = desc
+                .layout
+                .entries
+                .iter()
+                .find(|layout_entry| layout_entry.binding == entry.binding)
+                .expect("internal error: no layout entry found with binding slot");
+            (layout, entry)
+        });
+        for (layout, entry) in layout_and_entry_iter {
             match layout.ty {
                 wgt::BindingType::Buffer {
                     has_dynamic_offset: true,
diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs
index 8cc3129d6..50c07f3ff 100644
--- a/wgpu-hal/src/gles/device.rs
+++ b/wgpu-hal/src/gles/device.rs
@@ -1125,8 +1125,10 @@ impl crate::Device for super::Device {
                 !0;
                 bg_layout
                     .entries
-                    .last()
-                    .map_or(0, |b| b.binding as usize + 1)
+                    .iter()
+                    .map(|b| b.binding)
+                    .max()
+                    .map_or(0, |idx| idx as usize + 1)
             ]
             .into_boxed_slice();
 
@@ -1179,7 +1181,16 @@ impl crate::Device for super::Device {
     ) -> Result<super::BindGroup, crate::DeviceError> {
         let mut contents = Vec::new();
 
-        for (entry, layout) in desc.entries.iter().zip(desc.layout.entries.iter()) {
+        let layout_and_entry_iter = desc.entries.iter().map(|entry| {
+            let layout = desc
+                .layout
+                .entries
+                .iter()
+                .find(|layout_entry| layout_entry.binding == entry.binding)
+                .expect("internal error: no layout entry found with binding slot");
+            (entry, layout)
+        });
+        for (entry, layout) in layout_and_entry_iter {
             let binding = match layout.ty {
                 wgt::BindingType::Buffer { .. } => {
                     let bb = &desc.buffers[entry.resource_index as usize];
diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs
index 8b8a9bb6e..179429f5d 100644
--- a/wgpu-hal/src/metal/device.rs
+++ b/wgpu-hal/src/metal/device.rs
@@ -708,7 +708,16 @@ impl crate::Device for super::Device {
         for (&stage, counter) in super::NAGA_STAGES.iter().zip(bg.counters.iter_mut()) {
             let stage_bit = map_naga_stage(stage);
             let mut dynamic_offsets_count = 0u32;
-            for (entry, layout) in desc.entries.iter().zip(desc.layout.entries.iter()) {
+            let layout_and_entry_iter = desc.entries.iter().map(|entry| {
+                let layout = desc
+                    .layout
+                    .entries
+                    .iter()
+                    .find(|layout_entry| layout_entry.binding == entry.binding)
+                    .expect("internal error: no layout entry found with binding slot");
+                (entry, layout)
+            });
+            for (entry, layout) in layout_and_entry_iter {
                 let size = layout.count.map_or(1, |c| c.get());
                 if let wgt::BindingType::Buffer {
                     has_dynamic_offset: true,
diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs
index 4a2c328fe..266546379 100644
--- a/wgpu-hal/src/vulkan/adapter.rs
+++ b/wgpu-hal/src/vulkan/adapter.rs
@@ -151,9 +151,32 @@ impl PhysicalDeviceFeatures {
         info
     }
 
-    /// Create a `PhysicalDeviceFeatures` that will be used to create a logical device.
+    /// Create a `PhysicalDeviceFeatures` that can be used to create a logical
+    /// device.
     ///
-    /// `requested_features` should be the same as what was used to generate `enabled_extensions`.
+    /// Return a `PhysicalDeviceFeatures` value capturing all the Vulkan
+    /// features needed for the given [`Features`], [`DownlevelFlags`], and
+    /// [`PrivateCapabilities`]. You can use the returned value's
+    /// [`add_to_device_create_builder`] method to configure a
+    /// [`DeviceCreateInfoBuilder`] to build a logical device providing those
+    /// features.
+    ///
+    /// To ensure that the returned value is able to select all the Vulkan
+    /// features needed to express `requested_features`, `downlevel_flags`, and
+    /// `private_caps`:
+    ///
+    /// - The given `enabled_extensions` set must include all the extensions
+    ///   selected by [`Adapter::required_device_extensions`] when passed
+    ///   `features`.
+    ///
+    /// - The given `device_api_version` must be the Vulkan API version of the
+    ///   physical device we will use to create the logical device.
+    ///
+    /// [`Features`]: wgt::Features
+    /// [`DownlevelFlags`]: wgt::DownlevelFlags
+    /// [`PrivateCapabilities`]: super::PrivateCapabilities
+    /// [`DeviceCreateInfoBuilder`]: vk::DeviceCreateInfoBuilder
+    /// [`Adapter::required_device_extensions`]: super::Adapter::required_device_extensions
     fn from_extensions_and_requested_features(
         device_api_version: u32,
         enabled_extensions: &[&'static CStr],
@@ -414,11 +437,16 @@ impl PhysicalDeviceFeatures {
         }
     }
 
+    /// Compute the wgpu [`Features`] and [`DownlevelFlags`] supported by a physical device.
+    ///
+    /// Given `self`, together with the instance and physical device it was
+    /// built from, and a `caps` also built from those, determine which wgpu
+    /// features and downlevel flags the device can support.
     fn to_wgpu(
         &self,
         instance: &ash::Instance,
         phd: vk::PhysicalDevice,
-        caps: &PhysicalDeviceCapabilities,
+        caps: &PhysicalDeviceProperties,
     ) -> (wgt::Features, wgt::DownlevelFlags) {
         use crate::auxil::db;
         use wgt::{DownlevelFlags as Df, Features as F};
@@ -699,11 +727,13 @@ impl PhysicalDeviceFeatures {
     }
 }
 
-/// Information gathered about a physical device.
+/// Vulkan "properties" structures gathered about a physical device.
 ///
-/// This structure holds the results from the queries we make about a
-/// [`vk::PhysicalDevice`], other than features: its device properties,
-/// supported extensions, and whatever properties those extensions provide.
+/// This structure holds the properties of a [`vk::PhysicalDevice`]:
+/// - the standard Vulkan device properties
+/// - the `VkExtensionProperties` structs for all available extensions, and
+/// - the per-extension properties structures for the available extensions that
+///   `wgpu` cares about.
 ///
 /// Generally, if you get it from any of these functions, it's stored
 /// here:
@@ -718,7 +748,7 @@ impl PhysicalDeviceFeatures {
 /// This does not include device features; for those, see
 /// [`PhysicalDeviceFeatures`].
 #[derive(Default, Debug)]
-pub struct PhysicalDeviceCapabilities {
+pub struct PhysicalDeviceProperties {
     /// Extensions supported by the `vk::PhysicalDevice`,
     /// as returned by `vkEnumerateDeviceExtensionProperties`.
     supported_extensions: Vec<vk::ExtensionProperties>,
@@ -752,10 +782,10 @@ pub struct PhysicalDeviceCapabilities {
 }
 
 // This is safe because the structs have `p_next: *mut c_void`, which we null out/never read.
-unsafe impl Send for PhysicalDeviceCapabilities {}
-unsafe impl Sync for PhysicalDeviceCapabilities {}
+unsafe impl Send for PhysicalDeviceProperties {}
+unsafe impl Sync for PhysicalDeviceProperties {}
 
-impl PhysicalDeviceCapabilities {
+impl PhysicalDeviceProperties {
     pub fn properties(&self) -> vk::PhysicalDeviceProperties {
         self.properties
     }
@@ -994,9 +1024,9 @@ impl super::InstanceShared {
     fn inspect(
         &self,
         phd: vk::PhysicalDevice,
-    ) -> (PhysicalDeviceCapabilities, PhysicalDeviceFeatures) {
+    ) -> (PhysicalDeviceProperties, PhysicalDeviceFeatures) {
         let capabilities = {
-            let mut capabilities = PhysicalDeviceCapabilities::default();
+            let mut capabilities = PhysicalDeviceProperties::default();
             capabilities.supported_extensions =
                 unsafe { self.raw.enumerate_device_extension_properties(phd).unwrap() };
             capabilities.properties = unsafe { self.raw.get_physical_device_properties(phd) };
@@ -1393,7 +1423,7 @@ impl super::Adapter {
         self.raw
     }
 
-    pub fn physical_device_capabilities(&self) -> &PhysicalDeviceCapabilities {
+    pub fn physical_device_capabilities(&self) -> &PhysicalDeviceProperties {
         &self.phd_capabilities
     }
 
@@ -1418,7 +1448,20 @@ impl super::Adapter {
         supported_extensions
     }
 
-    /// `features` must be the same features used to create `enabled_extensions`.
+    /// Create a `PhysicalDeviceFeatures` for opening a logical device with
+    /// `features` from this adapter.
+    ///
+    /// The given `enabled_extensions` set must include all the extensions
+    /// selected by [`required_device_extensions`] when passed `features`.
+    /// Otherwise, the `PhysicalDeviceFeatures` value may not be able to select
+    /// all the Vulkan features needed to represent `features` and this
+    /// adapter's characteristics.
+    ///
+    /// Typically, you'd simply call `required_device_extensions`, and then pass
+    /// its return value and the feature set you gave it directly to this
+    /// function. But it's fine to add more extensions to the list.
+    ///
+    /// [`required_device_extensions`]: Self::required_device_extensions
     pub fn physical_device_features(
         &self,
         enabled_extensions: &[&'static CStr],
diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs
index 9e8ba0a74..0cd385045 100644
--- a/wgpu-hal/src/vulkan/mod.rs
+++ b/wgpu-hal/src/vulkan/mod.rs
@@ -189,7 +189,7 @@ pub struct Adapter {
     instance: Arc<InstanceShared>,
     //queue_families: Vec<vk::QueueFamilyProperties>,
     known_memory_flags: vk::MemoryPropertyFlags,
-    phd_capabilities: adapter::PhysicalDeviceCapabilities,
+    phd_capabilities: adapter::PhysicalDeviceProperties,
     //phd_features: adapter::PhysicalDeviceFeatures,
     downlevel_flags: wgt::DownlevelFlags,
     private_caps: PrivateCapabilities,


Test patch:
diff --git a/tests/tests/device.rs b/tests/tests/device.rs
index 621ac74f6..fd393b63b 100644
--- a/tests/tests/device.rs
+++ b/tests/tests/device.rs
@@ -654,3 +654,125 @@ static DROPPED_GLOBAL_THEN_DEVICE_LOST: GpuTestConfiguration = GpuTestConfigurat
             "Device lost callback should have been called."
         );
     });
+
+#[gpu_test]
+static DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConfiguration::new()
+    .parameters(TestParameters::default())
+    .run_sync(|ctx| {
+        // This test addresses a bug found in multiple backends where `wgpu_core` and `wgpu_hal`
+        // backends made different assumptions about the element order of vectors of bind group
+        // layout entries and bind group resource bindings.
+        //
+        // Said bug was exposed originally by:
+        //
+        // 1. Shader-declared bindings having a different order than resource bindings provided to
+        //    `Device::create_bind_group`.
+        // 2. Having more of one type of resource in the bind group than another.
+        //
+        // …such that internals would accidentally attempt to use an out-of-bounds index (of one
+        // resource type) in the wrong list of a different resource type. Let's reproduce that
+        // here.
+
+        let trivial_shaders_with_some_reversed_bindings = "\
+@group(0) @binding(3) var myTexture2: texture_2d<f32>;
+@group(0) @binding(2) var myTexture1: texture_2d<f32>;
+@group(0) @binding(1) var mySampler: sampler;
+
+@fragment
+fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4f {
+  return textureSample(myTexture1, mySampler, pos.xy) + textureSample(myTexture2, mySampler, pos.xy);
+}
+
+@vertex
+fn vs_main() -> @builtin(position) vec4<f32> {
+  return vec4<f32>(0.0, 0.0, 0.0, 1.0);
+}
+";
+
+        let trivial_shaders_with_some_reversed_bindings =
+            ctx.device
+                .create_shader_module(wgpu::ShaderModuleDescriptor {
+                    label: None,
+                    source: wgpu::ShaderSource::Wgsl(
+                        trivial_shaders_with_some_reversed_bindings.into(),
+                    ),
+                });
+
+        let my_texture = ctx.device.create_texture(&wgt::TextureDescriptor {
+            label: None,
+            size: wgt::Extent3d {
+                width: 1024,
+                height: 512,
+                depth_or_array_layers: 1,
+            },
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgt::TextureDimension::D2,
+            format: wgt::TextureFormat::Rgba8Unorm,
+            usage: wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TEXTURE_BINDING,
+            view_formats: &[],
+        });
+
+        let my_texture_view = my_texture.create_view(&wgpu::TextureViewDescriptor {
+            label: None,
+            format: None,
+            dimension: None,
+            aspect: wgt::TextureAspect::All,
+            base_mip_level: 0,
+            mip_level_count: None,
+            base_array_layer: 0,
+            array_layer_count: None,
+        });
+
+        let my_sampler = ctx
+            .device
+            .create_sampler(&wgpu::SamplerDescriptor::default());
+
+        let render_pipeline = ctx
+            .device
+            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+                fragment: Some(wgpu::FragmentState {
+                    module: &trivial_shaders_with_some_reversed_bindings,
+                    entry_point: "fs_main",
+                    targets: &[Some(wgt::ColorTargetState {
+                        format: wgt::TextureFormat::Bgra8Unorm,
+                        blend: None,
+                        write_mask: wgt::ColorWrites::ALL,
+                    })],
+                }),
+                layout: None,
+
+                // Other fields below aren't interesting for this text.
+                label: None,
+                vertex: wgpu::VertexState {
+                    module: &trivial_shaders_with_some_reversed_bindings,
+                    entry_point: "vs_main",
+                    buffers: &[],
+                },
+                primitive: wgt::PrimitiveState::default(),
+                depth_stencil: None,
+                multisample: wgt::MultisampleState::default(),
+                multiview: None,
+            });
+
+        // fail(&ctx.device, || {
+        // }, "");
+        ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
+            label: None,
+            layout: &render_pipeline.get_bind_group_layout(0),
+            entries: &[
+                wgpu::BindGroupEntry {
+                    binding: 1,
+                    resource: wgpu::BindingResource::Sampler(&my_sampler),
+                },
+                wgpu::BindGroupEntry {
+                    binding: 2,
+                    resource: wgpu::BindingResource::TextureView(&my_texture_view),
+                },
+                wgpu::BindGroupEntry {
+                    binding: 3,
+                    resource: wgpu::BindingResource::TextureView(&my_texture_view),
+                },
+            ],
+        });
+    });
diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs
index 745eeff8c..e0a2cbae0 100644
--- a/tests/tests/vertex_indices/mod.rs
+++ b/tests/tests/vertex_indices/mod.rs
@@ -5,9 +5,10 @@
 
 use std::{num::NonZeroU64, ops::Range};
 
-use wgpu::util::{BufferInitDescriptor, DeviceExt};
+use wgpu::util::{BufferInitDescriptor, DeviceExt, RenderEncoder};
 
 use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};
+use wgt::RenderBundleDescriptor;
 
 /// Generic struct representing a draw call
 struct Draw {
@@ -19,7 +20,7 @@ struct Draw {
 
 impl Draw {
     /// Directly execute the draw call
-    fn execute(&self, rpass: &mut wgpu::RenderPass<'_>) {
+    fn execute(&self, rpass: &mut dyn RenderEncoder<'_>) {
         if let Some(base_vertex) = self.base_vertex {
             rpass.draw_indexed(self.vertex.clone(), base_vertex, self.instance.clone());
         } else {
@@ -64,7 +65,7 @@ impl Draw {
     /// Execute the draw call from the given indirect buffer
     fn execute_indirect<'rpass>(
         &self,
-        rpass: &mut wgpu::RenderPass<'rpass>,
+        rpass: &mut dyn RenderEncoder<'rpass>,
         indirect: &'rpass wgpu::Buffer,
         offset: &mut u64,
     ) {
@@ -169,10 +170,21 @@ impl DrawCallKind {
     const ARRAY: [Self; 2] = [Self::Direct, Self::Indirect];
 }
 
+#[derive(Debug, Copy, Clone)]
+enum EncoderKind {
+    RenderPass,
+    RenderBundle,
+}
+
+impl EncoderKind {
+    const ARRAY: [Self; 2] = [Self::RenderPass, Self::RenderBundle];
+}
+
 struct Test {
     case: TestCase,
     id_source: IdSource,
     draw_call_kind: DrawCallKind,
+    encoder_kind: EncoderKind,
 }
 
 impl Test {
@@ -325,11 +337,14 @@ async fn vertex_index_common(ctx: TestingContext) {
     for case in TestCase::ARRAY {
         for id_source in IdSource::ARRAY {
             for draw_call_kind in DrawCallKind::ARRAY {
-                tests.push(Test {
-                    case,
-                    id_source,
-                    draw_call_kind,
-                })
+                for encoder_kind in EncoderKind::ARRAY {
+                    tests.push(Test {
+                        case,
+                        id_source,
+                        draw_call_kind,
+                        encoder_kind,
+                    })
+                }
             }
         }
     }
@@ -373,6 +388,7 @@ async fn vertex_index_common(ctx: TestingContext) {
             .device
             .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
 
+        let render_bundle;
         let indirect_buffer;
         let mut rpass = encoder1.begin_render_pass(&wgpu::RenderPassDescriptor {
             label: None,
@@ -386,34 +402,64 @@ async fn vertex_index_common(ctx: TestingContext) {
             occlusion_query_set: None,
         });
 
-        rpass.set_vertex_buffer(0, identity_buffer.slice(..));
-        rpass.set_vertex_buffer(1, identity_buffer.slice(..));
-        rpass.set_index_buffer(identity_buffer.slice(..), wgpu::IndexFormat::Uint32);
-        rpass.set_pipeline(pipeline);
-        rpass.set_bind_group(0, &bg, &[]);
-
-        let draws = test.case.draws();
-
-        match test.draw_call_kind {
-            DrawCallKind::Direct => {
-                for draw in draws {
-                    draw.execute(&mut rpass);
+        {
+            // Need to scope render_bundle_encoder since it's not Send and would otherwise
+            // infect the function if not going out of scope before an await call.
+            // (it is dropped via `take` + `finish` earlier, but compiler does not take this into account)
+            let mut render_bundle_encoder = match test.encoder_kind {
+                EncoderKind::RenderPass => None,
+                EncoderKind::RenderBundle => Some(ctx.device.create_render_bundle_encoder(
+                    &wgpu::RenderBundleEncoderDescriptor {
+                        label: Some("test renderbundle encoder"),
+                        color_formats: &[Some(wgpu::TextureFormat::Rgba8Unorm)],
+                        depth_stencil: None,
+                        sample_count: 1,
+                        multiview: None,
+                    },
+                )),
+            };
+
+            let render_encoder: &mut dyn RenderEncoder = render_bundle_encoder
+                .as_mut()
+                .map(|r| r as &mut dyn RenderEncoder)
+                .unwrap_or(&mut rpass);
+
+            render_encoder.set_vertex_buffer(0, identity_buffer.slice(..));
+            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, &[]);
+
+            let draws = test.case.draws();
+
+            match test.draw_call_kind {
+                DrawCallKind::Direct => {
+                    for draw in draws {
+                        draw.execute(render_encoder);
+                    }
                 }
-            }
-            DrawCallKind::Indirect => {
-                let mut indirect_bytes = Vec::new();
-                for draw in draws {
-                    draw.add_to_buffer(&mut indirect_bytes, features);
+                DrawCallKind::Indirect => {
+                    let mut indirect_bytes = Vec::new();
+                    for draw in draws {
+                        draw.add_to_buffer(&mut indirect_bytes, features);
+                    }
+                    indirect_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {
+                        label: Some("indirect"),
+                        contents: &indirect_bytes,
+                        usage: wgpu::BufferUsages::INDIRECT,
+                    });
+                    let mut offset = 0;
+                    for draw in draws {
+                        draw.execute_indirect(render_encoder, &indirect_buffer, &mut offset);
+                    }
                 }
-                indirect_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {
-                    label: Some("indirect"),
-                    contents: &indirect_bytes,
-                    usage: wgpu::BufferUsages::INDIRECT,
+            }
+
+            if let Some(render_bundle_encoder) = render_bundle_encoder.take() {
+                render_bundle = render_bundle_encoder.finish(&RenderBundleDescriptor {
+                    label: Some("test renderbundle"),
                 });
-                let mut offset = 0;
-                for draw in draws {
-                    draw.execute_indirect(&mut rpass, &indirect_buffer, &mut offset);
-                }
+                rpass.execute_bundles([&render_bundle]);
             }
         }
 
@@ -439,21 +485,18 @@ async fn vertex_index_common(ctx: TestingContext) {
             .panic_on_timeout();
         let data: Vec<u32> = bytemuck::cast_slice(&slice.get_mapped_range()).to_vec();
 
+        let case_name = format!(
+            "Case {:?} getting indices from {:?} using {:?} draw calls, encoded with a {:?}",
+            test.case, test.id_source, test.draw_call_kind, test.encoder_kind
+        );
         if data != expected {
             eprintln!(
-                "Failed: Got: {:?} Expected: {:?} - Case {:?} getting indices from {:?} using {:?} draw calls",
-                data,
-                expected,
-                test.case,
-                test.id_source,
-                test.draw_call_kind
+                "Failed: Got: {:?} Expected: {:?} - {case_name}",
+                data, expected,
             );
             failed = true;
         } else {
-            eprintln!(
-                "Passed: Case {:?} getting indices from {:?} using {:?} draw calls",
-                test.case, test.id_source, test.draw_call_kind
-            );
+            eprintln!("Passed: {case_name}");
         }
     }