From 326ad03ce15e284e9ba280dfc523ea1aed112d56 Mon Sep 17 00:00:00 2001
From: Kevin Reid <kpreid@switchb.org>
Date: Thu, 6 Mar 2025 12:27:33 -0800
Subject: [PATCH] Move `trace_dir`/`trace_path` to a custom enum inside
 `DeviceDescriptor`.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This allows `wgpu` to not unconditionally depend on `std::path::Path`.
It’s also, in my opinion, more user-friendly, because the feature which
most users will not use (and is not currently functional) is now a
defaultable struct field instead of a required parameter.

The disadvantage is that `wgpu-types` now has to know about tracing.
---
 Cargo.lock                                    |  1 +
 benches/benches/root.rs                       | 16 +++++-----
 deno_webgpu/adapter.rs                        | 15 ++++-----
 examples/features/Cargo.toml                  |  3 ++
 examples/features/src/framework.rs            | 20 ++++++------
 .../features/src/hello_synchronization/mod.rs | 16 +++++-----
 examples/features/src/hello_triangle/mod.rs   | 20 ++++++------
 examples/features/src/hello_windows/mod.rs    | 16 +++++-----
 examples/features/src/hello_workgroups/mod.rs | 16 +++++-----
 .../features/src/render_to_texture/mod.rs     | 16 +++++-----
 examples/features/src/repeated_compute/mod.rs | 16 +++++-----
 examples/features/src/storage_texture/mod.rs  | 16 +++++-----
 .../features/src/timestamp_queries/mod.rs     | 16 +++++-----
 examples/features/src/uniform_values/mod.rs   | 16 +++++-----
 .../standalone/01_hello_compute/src/main.rs   | 16 +++++-----
 .../standalone/02_hello_window/src/main.rs    |  5 +--
 .../03_custom_backend/src/custom.rs           |  1 -
 .../standalone/03_custom_backend/src/main.rs  | 11 +++----
 player/src/bin/play.rs                        |  9 ++----
 player/tests/root.rs                          |  2 +-
 tests/gpu-tests/device.rs                     | 31 +++++++++----------
 tests/src/init.rs                             | 16 +++++-----
 tests/validation-tests/root.rs                |  2 +-
 wgpu-core/Cargo.toml                          |  2 +-
 wgpu-core/src/device/resource.rs              | 31 ++++++++++++++-----
 wgpu-core/src/device/trace.rs                 |  5 ++-
 wgpu-core/src/instance.rs                     | 25 +++------------
 wgpu-types/Cargo.toml                         |  2 ++
 wgpu-types/src/lib.rs                         | 24 ++++++++++++++
 wgpu/src/api/adapter.rs                       |  9 ++----
 wgpu/src/backend/webgpu.rs                    |  5 ++-
 wgpu/src/backend/wgpu_core.rs                 | 24 +++++++++-----
 wgpu/src/dispatch.rs                          |  1 -
 wgpu/src/lib.rs                               |  2 +-
 34 files changed, 209 insertions(+), 217 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 775a427d3..7db816a1c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4718,6 +4718,7 @@ dependencies = [
  "web-time 1.1.0",
  "wgpu",
  "wgpu-test",
+ "wgpu-types",
  "winit 0.29.15",
 ]
 
diff --git a/benches/benches/root.rs b/benches/benches/root.rs
index 2a8dac029..c087f1f84 100644
--- a/benches/benches/root.rs
+++ b/benches/benches/root.rs
@@ -43,15 +43,13 @@ impl DeviceState {
 
         eprintln!("{adapter_info:?}");
 
-        let (device, queue) = block_on(adapter.request_device(
-            &wgpu::DeviceDescriptor {
-                required_features: adapter.features(),
-                required_limits: adapter.limits(),
-                memory_hints: wgpu::MemoryHints::Performance,
-                label: Some("Compute/RenderPass Device"),
-            },
-            None,
-        ))
+        let (device, queue) = block_on(adapter.request_device(&wgpu::DeviceDescriptor {
+            required_features: adapter.features(),
+            required_limits: adapter.limits(),
+            memory_hints: wgpu::MemoryHints::Performance,
+            label: Some("Compute/RenderPass Device"),
+            trace: wgpu::Trace::Off,
+        }))
         .unwrap();
 
         Self {
diff --git a/deno_webgpu/adapter.rs b/deno_webgpu/adapter.rs
index ffa8827b3..169c9d60c 100644
--- a/deno_webgpu/adapter.rs
+++ b/deno_webgpu/adapter.rs
@@ -128,6 +128,8 @@ impl GPUAdapter {
         let required_limits =
             serde_json::from_value(serde_json::to_value(descriptor.required_limits)?)?;
 
+        let webgpu_trace = std::env::var_os("DENO_WEBGPU_TRACE").unwrap();
+
         let wgpu_descriptor = wgpu_types::DeviceDescriptor {
             label: crate::transform_label(descriptor.label.clone()),
             required_features: super::webidl::feature_names_to_features(
@@ -135,17 +137,12 @@ impl GPUAdapter {
             ),
             required_limits,
             memory_hints: Default::default(),
+            trace: wgpu_types::Trace::Directory(std::path::PathBuf::from(webgpu_trace)),
         };
 
-        let webgpu_trace = std::env::var("DENO_WEBGPU_TRACE").unwrap();
-
-        let (device, queue) = self.instance.adapter_request_device(
-            self.id,
-            &wgpu_descriptor,
-            Some(webgpu_trace.as_str()),
-            None,
-            None,
-        )?;
+        let (device, queue) =
+            self.instance
+                .adapter_request_device(self.id, &wgpu_descriptor, None, None)?;
 
         let (lost_sender, lost_receiver) = tokio::sync::oneshot::channel();
         let (uncaptured_sender, mut uncaptured_receiver) = tokio::sync::mpsc::unbounded_channel();
diff --git a/examples/features/Cargo.toml b/examples/features/Cargo.toml
index 11cf6b211..ec79438af 100644
--- a/examples/features/Cargo.toml
+++ b/examples/features/Cargo.toml
@@ -45,6 +45,9 @@ png.workspace = true
 pollster.workspace = true
 web-time.workspace = true
 wgpu.workspace = true
+wgpu-types = { workspace = true, features = [
+    "trace", # TODO(#5974): this should be a dep on wgpu/trace and not wgpu-types at all
+] }
 winit.workspace = true
 
 [dev-dependencies]
diff --git a/examples/features/src/framework.rs b/examples/features/src/framework.rs
index 100e872b6..d52c3b84e 100644
--- a/examples/features/src/framework.rs
+++ b/examples/features/src/framework.rs
@@ -281,18 +281,18 @@ impl ExampleContext {
         // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.
         let needed_limits = E::required_limits().using_resolution(adapter.limits());
 
-        let trace_dir = std::env::var("WGPU_TRACE");
         let (device, queue) = adapter
-            .request_device(
-                &wgpu::DeviceDescriptor {
-                    label: None,
-                    required_features: (E::optional_features() & adapter.features())
-                        | E::required_features(),
-                    required_limits: needed_limits,
-                    memory_hints: wgpu::MemoryHints::MemoryUsage,
+            .request_device(&wgpu::DeviceDescriptor {
+                label: None,
+                required_features: (E::optional_features() & adapter.features())
+                    | E::required_features(),
+                required_limits: needed_limits,
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
+                trace: match std::env::var_os("WGPU_TRACE") {
+                    Some(path) => wgpu::Trace::Directory(path.into()),
+                    None => wgpu::Trace::Off,
                 },
-                trace_dir.ok().as_ref().map(std::path::Path::new),
-            )
+            })
             .await
             .expect("Unable to find a suitable GPU adapter!");
 
diff --git a/examples/features/src/hello_synchronization/mod.rs b/examples/features/src/hello_synchronization/mod.rs
index 737aed750..41d51acd5 100644
--- a/examples/features/src/hello_synchronization/mod.rs
+++ b/examples/features/src/hello_synchronization/mod.rs
@@ -12,15 +12,13 @@ async fn run() {
         .await
         .unwrap();
     let (device, queue) = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: wgpu::Features::empty(),
-                required_limits: wgpu::Limits::downlevel_defaults(),
-                memory_hints: wgpu::MemoryHints::Performance,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: wgpu::Features::empty(),
+            required_limits: wgpu::Limits::downlevel_defaults(),
+            memory_hints: wgpu::MemoryHints::Performance,
+            trace: wgpu::Trace::Off,
+        })
         .await
         .unwrap();
 
diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs
index cc65c3a89..cba78face 100644
--- a/examples/features/src/hello_triangle/mod.rs
+++ b/examples/features/src/hello_triangle/mod.rs
@@ -25,17 +25,15 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
 
     // Create the logical device and command queue
     let (device, queue) = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: wgpu::Features::empty(),
-                // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
-                required_limits: wgpu::Limits::downlevel_webgl2_defaults()
-                    .using_resolution(adapter.limits()),
-                memory_hints: wgpu::MemoryHints::MemoryUsage,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: wgpu::Features::empty(),
+            // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
+            required_limits: wgpu::Limits::downlevel_webgl2_defaults()
+                .using_resolution(adapter.limits()),
+            memory_hints: wgpu::MemoryHints::MemoryUsage,
+            trace: wgpu::Trace::Off,
+        })
         .await
         .expect("Failed to create device");
 
diff --git a/examples/features/src/hello_windows/mod.rs b/examples/features/src/hello_windows/mod.rs
index d4aa994ab..2fb24a081 100644
--- a/examples/features/src/hello_windows/mod.rs
+++ b/examples/features/src/hello_windows/mod.rs
@@ -70,15 +70,13 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Arc<Window>, wgpu::Color
 
     // Create the logical device and command queue
     let (device, queue) = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: wgpu::Features::empty(),
-                required_limits: wgpu::Limits::downlevel_defaults(),
-                memory_hints: wgpu::MemoryHints::MemoryUsage,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: wgpu::Features::empty(),
+            required_limits: wgpu::Limits::downlevel_defaults(),
+            memory_hints: wgpu::MemoryHints::MemoryUsage,
+            trace: wgpu::Trace::Off,
+        })
         .await
         .expect("Failed to create device");
 
diff --git a/examples/features/src/hello_workgroups/mod.rs b/examples/features/src/hello_workgroups/mod.rs
index cdddfe98a..5a8e7ffab 100644
--- a/examples/features/src/hello_workgroups/mod.rs
+++ b/examples/features/src/hello_workgroups/mod.rs
@@ -27,15 +27,13 @@ async fn run() {
         .await
         .unwrap();
     let (device, queue) = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: wgpu::Features::empty(),
-                required_limits: wgpu::Limits::downlevel_defaults(),
-                memory_hints: wgpu::MemoryHints::MemoryUsage,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: wgpu::Features::empty(),
+            required_limits: wgpu::Limits::downlevel_defaults(),
+            memory_hints: wgpu::MemoryHints::MemoryUsage,
+            trace: wgpu::Trace::Off,
+        })
         .await
         .unwrap();
 
diff --git a/examples/features/src/render_to_texture/mod.rs b/examples/features/src/render_to_texture/mod.rs
index eb25d3616..917ac7310 100644
--- a/examples/features/src/render_to_texture/mod.rs
+++ b/examples/features/src/render_to_texture/mod.rs
@@ -16,15 +16,13 @@ async fn run(_path: Option<String>) {
         .await
         .unwrap();
     let (device, queue) = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: wgpu::Features::empty(),
-                required_limits: wgpu::Limits::downlevel_defaults(),
-                memory_hints: wgpu::MemoryHints::MemoryUsage,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: wgpu::Features::empty(),
+            required_limits: wgpu::Limits::downlevel_defaults(),
+            memory_hints: wgpu::MemoryHints::MemoryUsage,
+            trace: wgpu::Trace::Off,
+        })
         .await
         .unwrap();
 
diff --git a/examples/features/src/repeated_compute/mod.rs b/examples/features/src/repeated_compute/mod.rs
index a31a456e6..2b9b19995 100644
--- a/examples/features/src/repeated_compute/mod.rs
+++ b/examples/features/src/repeated_compute/mod.rs
@@ -158,15 +158,13 @@ impl WgpuContext {
             .await
             .unwrap();
         let (device, queue) = adapter
-            .request_device(
-                &wgpu::DeviceDescriptor {
-                    label: None,
-                    required_features: wgpu::Features::empty(),
-                    required_limits: wgpu::Limits::downlevel_defaults(),
-                    memory_hints: wgpu::MemoryHints::Performance,
-                },
-                None,
-            )
+            .request_device(&wgpu::DeviceDescriptor {
+                label: None,
+                required_features: wgpu::Features::empty(),
+                required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::Performance,
+                trace: wgpu::Trace::Off,
+            })
             .await
             .unwrap();
 
diff --git a/examples/features/src/storage_texture/mod.rs b/examples/features/src/storage_texture/mod.rs
index 542ea7b84..b028e09a9 100644
--- a/examples/features/src/storage_texture/mod.rs
+++ b/examples/features/src/storage_texture/mod.rs
@@ -30,15 +30,13 @@ async fn run(_path: Option<String>) {
         .await
         .unwrap();
     let (device, queue) = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: wgpu::Features::empty(),
-                required_limits: wgpu::Limits::downlevel_defaults(),
-                memory_hints: wgpu::MemoryHints::MemoryUsage,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: wgpu::Features::empty(),
+            required_limits: wgpu::Limits::downlevel_defaults(),
+            memory_hints: wgpu::MemoryHints::MemoryUsage,
+            trace: wgpu::Trace::Off,
+        })
         .await
         .unwrap();
 
diff --git a/examples/features/src/timestamp_queries/mod.rs b/examples/features/src/timestamp_queries/mod.rs
index ef2b89cbc..7bac87e1a 100644
--- a/examples/features/src/timestamp_queries/mod.rs
+++ b/examples/features/src/timestamp_queries/mod.rs
@@ -206,15 +206,13 @@ async fn run() {
     // `request_device` instantiates the feature specific connection to the GPU, defining some parameters,
     //  `features` being the available features.
     let (device, queue) = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: features,
-                required_limits: wgpu::Limits::downlevel_defaults(),
-                memory_hints: wgpu::MemoryHints::MemoryUsage,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: features,
+            required_limits: wgpu::Limits::downlevel_defaults(),
+            memory_hints: wgpu::MemoryHints::MemoryUsage,
+            trace: wgpu::Trace::Off,
+        })
         .await
         .unwrap();
 
diff --git a/examples/features/src/uniform_values/mod.rs b/examples/features/src/uniform_values/mod.rs
index 72bac0fa5..3a6933bf1 100644
--- a/examples/features/src/uniform_values/mod.rs
+++ b/examples/features/src/uniform_values/mod.rs
@@ -110,15 +110,13 @@ impl WgpuContext {
             .await
             .unwrap();
         let (device, queue) = adapter
-            .request_device(
-                &wgpu::DeviceDescriptor {
-                    label: None,
-                    required_features: wgpu::Features::empty(),
-                    required_limits: wgpu::Limits::downlevel_defaults(),
-                    memory_hints: wgpu::MemoryHints::MemoryUsage,
-                },
-                None,
-            )
+            .request_device(&wgpu::DeviceDescriptor {
+                label: None,
+                required_features: wgpu::Features::empty(),
+                required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
+                trace: wgpu::Trace::Off,
+            })
             .await
             .unwrap();
 
diff --git a/examples/standalone/01_hello_compute/src/main.rs b/examples/standalone/01_hello_compute/src/main.rs
index 71f9d2b9b..19d95620c 100644
--- a/examples/standalone/01_hello_compute/src/main.rs
+++ b/examples/standalone/01_hello_compute/src/main.rs
@@ -66,15 +66,13 @@ fn main() {
     //
     // The `Device` is used to create and manage GPU resources.
     // The `Queue` is a queue used to submit work for the GPU to process.
-    let (device, queue) = pollster::block_on(adapter.request_device(
-        &wgpu::DeviceDescriptor {
-            label: None,
-            required_features: wgpu::Features::empty(),
-            required_limits: wgpu::Limits::downlevel_defaults(),
-            memory_hints: wgpu::MemoryHints::MemoryUsage,
-        },
-        None,
-    ))
+    let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
+        label: None,
+        required_features: wgpu::Features::empty(),
+        required_limits: wgpu::Limits::downlevel_defaults(),
+        memory_hints: wgpu::MemoryHints::MemoryUsage,
+        trace: wgpu::Trace::Off,
+    }))
     .expect("Failed to create device");
 
     // Create a shader module from our shader code. This will parse and validate the shader.
diff --git a/examples/standalone/02_hello_window/src/main.rs b/examples/standalone/02_hello_window/src/main.rs
index 07f90d1fa..83687e281 100644
--- a/examples/standalone/02_hello_window/src/main.rs
+++ b/examples/standalone/02_hello_window/src/main.rs
@@ -24,10 +24,7 @@ impl State {
             .await
             .unwrap();
         let (device, queue) = adapter
-            .request_device(
-                &wgpu::DeviceDescriptor::default(),
-                None, // Trace path
-            )
+            .request_device(&wgpu::DeviceDescriptor::default())
             .await
             .unwrap();
 
diff --git a/examples/standalone/03_custom_backend/src/custom.rs b/examples/standalone/03_custom_backend/src/custom.rs
index 33cf49f69..556a1d29d 100644
--- a/examples/standalone/03_custom_backend/src/custom.rs
+++ b/examples/standalone/03_custom_backend/src/custom.rs
@@ -64,7 +64,6 @@ impl AdapterInterface for CustomAdapter {
     fn request_device(
         &self,
         desc: &wgpu::DeviceDescriptor<'_>,
-        _trace_dir: Option<&std::path::Path>,
     ) -> Pin<Box<dyn wgpu::custom::RequestDeviceFuture>> {
         assert_eq!(desc.label, Some("device"));
         let res: Result<_, wgpu::RequestDeviceError> = Ok((
diff --git a/examples/standalone/03_custom_backend/src/main.rs b/examples/standalone/03_custom_backend/src/main.rs
index ed6c0b4fb..11f05e98e 100644
--- a/examples/standalone/03_custom_backend/src/main.rs
+++ b/examples/standalone/03_custom_backend/src/main.rs
@@ -23,13 +23,10 @@ async fn main() {
         assert_eq!(counter.count(), 3);
 
         let (device, _queue) = adapter
-            .request_device(
-                &DeviceDescriptor {
-                    label: Some("device"),
-                    ..Default::default()
-                },
-                None,
-            )
+            .request_device(&DeviceDescriptor {
+                label: Some("device"),
+                ..Default::default()
+            })
             .await
             .unwrap();
         assert_eq!(counter.count(), 5);
diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs
index 936e4a34c..1e76cb2fd 100644
--- a/player/src/bin/play.rs
+++ b/player/src/bin/play.rs
@@ -86,13 +86,8 @@ fn main() {
             log::info!("Picked '{}'", info.name);
             let device_id = wgc::id::Id::zip(0, 1);
             let queue_id = wgc::id::Id::zip(0, 1);
-            let res = global.adapter_request_device(
-                adapter,
-                &desc,
-                None,
-                Some(device_id),
-                Some(queue_id),
-            );
+            let res =
+                global.adapter_request_device(adapter, &desc, Some(device_id), Some(queue_id));
             if let Err(e) = res {
                 panic!("{e:?}");
             }
diff --git a/player/tests/root.rs b/player/tests/root.rs
index 1254a7032..44491a5f4 100644
--- a/player/tests/root.rs
+++ b/player/tests/root.rs
@@ -95,8 +95,8 @@ impl Test<'_> {
                 required_features: self.features,
                 required_limits: wgt::Limits::default(),
                 memory_hints: wgt::MemoryHints::default(),
+                trace: wgt::Trace::Off,
             },
-            None,
             Some(device_id),
             Some(queue_id),
         );
diff --git a/tests/gpu-tests/device.rs b/tests/gpu-tests/device.rs
index 223f939db..9e03f92ac 100644
--- a/tests/gpu-tests/device.rs
+++ b/tests/gpu-tests/device.rs
@@ -11,7 +11,7 @@ static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguratio
         // Create a bind group using a layout from another device. This should be a validation
         // error but currently crashes.
         let (device2, _) =
-            pollster::block_on(ctx.adapter.request_device(&Default::default(), None)).unwrap();
+            pollster::block_on(ctx.adapter.request_device(&Default::default())).unwrap();
 
         {
             let bind_group_layout =
@@ -64,11 +64,11 @@ static MULTIPLE_DEVICES: GpuTestConfiguration = GpuTestConfiguration::new()
     .run_sync(|ctx| {
         use pollster::FutureExt as _;
         ctx.adapter
-            .request_device(&wgpu::DeviceDescriptor::default(), None)
+            .request_device(&wgpu::DeviceDescriptor::default())
             .block_on()
             .expect("failed to create device");
         ctx.adapter
-            .request_device(&wgpu::DeviceDescriptor::default(), None)
+            .request_device(&wgpu::DeviceDescriptor::default())
             .block_on()
             .expect("failed to create device");
     });
@@ -105,22 +105,19 @@ async fn request_device_error_message() {
     let (_instance, adapter, _surface_guard) = wgpu_test::initialize_adapter(None, false).await;
 
     let device_error = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                // Force a failure by requesting absurd limits.
-                required_features: wgpu::Features::all(),
-                required_limits: wgpu::Limits {
-                    max_texture_dimension_1d: u32::MAX,
-                    max_texture_dimension_2d: u32::MAX,
-                    max_texture_dimension_3d: u32::MAX,
-                    max_bind_groups: u32::MAX,
-                    max_push_constant_size: u32::MAX,
-                    ..Default::default()
-                },
+        .request_device(&wgpu::DeviceDescriptor {
+            // Force a failure by requesting absurd limits.
+            required_features: wgpu::Features::all(),
+            required_limits: wgpu::Limits {
+                max_texture_dimension_1d: u32::MAX,
+                max_texture_dimension_2d: u32::MAX,
+                max_texture_dimension_3d: u32::MAX,
+                max_bind_groups: u32::MAX,
+                max_push_constant_size: u32::MAX,
                 ..Default::default()
             },
-            None,
-        )
+            ..Default::default()
+        })
         .await
         .unwrap_err();
 
diff --git a/tests/src/init.rs b/tests/src/init.rs
index 0553ee212..79dea24b0 100644
--- a/tests/src/init.rs
+++ b/tests/src/init.rs
@@ -142,15 +142,13 @@ pub async fn initialize_device(
     limits: Limits,
 ) -> (Device, Queue) {
     let bundle = adapter
-        .request_device(
-            &wgpu::DeviceDescriptor {
-                label: None,
-                required_features: features,
-                required_limits: limits,
-                memory_hints: wgpu::MemoryHints::MemoryUsage,
-            },
-            None,
-        )
+        .request_device(&wgpu::DeviceDescriptor {
+            label: None,
+            required_features: features,
+            required_limits: limits,
+            memory_hints: wgpu::MemoryHints::MemoryUsage,
+            trace: wgpu::Trace::Off,
+        })
         .await;
 
     match bundle {
diff --git a/tests/validation-tests/root.rs b/tests/validation-tests/root.rs
index a0f303abe..7227355a6 100644
--- a/tests/validation-tests/root.rs
+++ b/tests/validation-tests/root.rs
@@ -24,5 +24,5 @@ fn request_noop_device_with_desc(desc: &wgpu::DeviceDescriptor) -> (wgpu::Device
             .expect("adapter");
     assert_eq!(adapter.get_info().backend, wgpu::Backend::Noop);
 
-    pollster::block_on(adapter.request_device(desc, None)).expect("device")
+    pollster::block_on(adapter.request_device(desc)).expect("device")
 }
diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml
index d5109c277..435fb358e 100644
--- a/wgpu-core/Cargo.toml
+++ b/wgpu-core/Cargo.toml
@@ -76,7 +76,7 @@ observe_locks = ["std", "dep:ron", "serde/serde_derive"]
 serde = ["dep:serde", "wgpu-types/serde", "arrayvec/serde", "hashbrown/serde"]
 
 ## Enable API tracing.
-trace = ["serde", "std", "dep:ron", "naga/serialize"]
+trace = ["serde", "std", "dep:ron", "naga/serialize", "wgpu-types/trace"]
 
 ## Enable API replaying
 replay = ["serde", "naga/deserialize"]
diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs
index bd19a20bf..740d10cfb 100644
--- a/wgpu-core/src/device/resource.rs
+++ b/wgpu-core/src/device/resource.rs
@@ -200,13 +200,27 @@ impl Device {
         raw_device: Box<dyn hal::DynDevice>,
         adapter: &Arc<Adapter>,
         desc: &DeviceDescriptor,
-        trace_dir_name: Option<&str>,
         instance_flags: wgt::InstanceFlags,
     ) -> Result<Self, DeviceError> {
         #[cfg(not(feature = "trace"))]
-        if let Some(_) = trace_dir_name {
-            log::error!("Feature 'trace' is not enabled");
-        }
+        match &desc.trace {
+            wgt::Trace::Off => {}
+            _ => {
+                log::error!("wgpu-core feature 'trace' is not enabled");
+            }
+        };
+        #[cfg(feature = "trace")]
+        let trace_dir_name: Option<&std::path::PathBuf> = match &desc.trace {
+            wgt::Trace::Off => None,
+            wgt::Trace::Directory(d) => Some(d),
+            // The enum is non_exhaustive, so we must have a fallback arm (that should be
+            // unreachable in practice).
+            t => {
+                log::error!("unimplemented wgpu_types::Trace variant {t:?}");
+                None
+            }
+        };
+
         let fence = unsafe { raw_device.create_fence() }.map_err(DeviceError::from_hal)?;
 
         let command_allocator = command::CommandAllocator::new();
@@ -272,16 +286,19 @@ impl Device {
             #[cfg(feature = "trace")]
             trace: Mutex::new(
                 rank::DEVICE_TRACE,
-                trace_dir_name.and_then(|dir_path_name| match trace::Trace::new(dir_path_name) {
+                trace_dir_name.and_then(|path| match trace::Trace::new(path.clone()) {
                     Ok(mut trace) => {
                         trace.add(trace::Action::Init {
-                            desc: desc.clone(),
+                            desc: wgt::DeviceDescriptor {
+                                trace: wgt::Trace::Off,
+                                ..desc.clone()
+                            },
                             backend: adapter.backend(),
                         });
                         Some(trace)
                     }
                     Err(e) => {
-                        log::error!("Unable to start a trace in '{dir_path_name:?}': {e}");
+                        log::error!("Unable to start a trace in '{path:?}': {e}");
                         None
                     }
                 }),
diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs
index 421b07b2c..3932d4086 100644
--- a/wgpu-core/src/device/trace.rs
+++ b/wgpu-core/src/device/trace.rs
@@ -223,13 +223,12 @@ pub struct Trace {
 
 #[cfg(feature = "trace")]
 impl Trace {
-    pub fn new(dir_path_name: &str) -> Result<Self, std::io::Error> {
-        let path = std::path::Path::new(dir_path_name);
+    pub fn new(path: std::path::PathBuf) -> Result<Self, std::io::Error> {
         log::info!("Tracing into '{:?}'", path);
         let mut file = std::fs::File::create(path.join(FILE_NAME))?;
         file.write_all(b"[\n")?;
         Ok(Self {
-            path: path.to_path_buf(),
+            path,
             file,
             config: ron::ser::PrettyConfig::default(),
             binary_id: 0,
diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs
index ab2f62e9a..e322b24ab 100644
--- a/wgpu-core/src/instance.rs
+++ b/wgpu-core/src/instance.rs
@@ -690,17 +690,10 @@ impl Adapter {
         hal_device: hal::DynOpenDevice,
         desc: &DeviceDescriptor,
         instance_flags: wgt::InstanceFlags,
-        trace_dir_name: Option<&str>,
     ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
         api_log!("Adapter::create_device");
 
-        let device = Device::new(
-            hal_device.device,
-            self,
-            desc,
-            trace_dir_name,
-            instance_flags,
-        )?;
+        let device = Device::new(hal_device.device, self, desc, instance_flags)?;
         let device = Arc::new(device);
 
         let queue = Queue::new(device.clone(), hal_device.queue)?;
@@ -715,7 +708,6 @@ impl Adapter {
         self: &Arc<Self>,
         desc: &DeviceDescriptor,
         instance_flags: wgt::InstanceFlags,
-        trace_dir_name: Option<&str>,
     ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
         // Verify all features were exposed by the adapter
         if !self.raw.features.contains(desc.required_features) {
@@ -762,7 +754,7 @@ impl Adapter {
         }
         .map_err(DeviceError::from_hal)?;
 
-        self.create_device_and_queue_from_hal(open, desc, instance_flags, trace_dir_name)
+        self.create_device_and_queue_from_hal(open, desc, instance_flags)
     }
 }
 
@@ -1039,7 +1031,6 @@ impl Global {
         &self,
         adapter_id: AdapterId,
         desc: &DeviceDescriptor,
-        trace_dir_name: Option<&str>,
         device_id_in: Option<DeviceId>,
         queue_id_in: Option<QueueId>,
     ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
@@ -1050,8 +1041,7 @@ impl Global {
         let queue_fid = self.hub.queues.prepare(queue_id_in);
 
         let adapter = self.hub.adapters.get(adapter_id);
-        let (device, queue) =
-            adapter.create_device_and_queue(desc, self.instance.flags, trace_dir_name)?;
+        let (device, queue) = adapter.create_device_and_queue(desc, self.instance.flags)?;
 
         let device_id = device_fid.assign(device);
         resource_log!("Created Device {:?}", device_id);
@@ -1071,7 +1061,6 @@ impl Global {
         adapter_id: AdapterId,
         hal_device: hal::DynOpenDevice,
         desc: &DeviceDescriptor,
-        trace_dir_name: Option<&str>,
         device_id_in: Option<DeviceId>,
         queue_id_in: Option<QueueId>,
     ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
@@ -1081,12 +1070,8 @@ impl Global {
         let queues_fid = self.hub.queues.prepare(queue_id_in);
 
         let adapter = self.hub.adapters.get(adapter_id);
-        let (device, queue) = adapter.create_device_and_queue_from_hal(
-            hal_device,
-            desc,
-            self.instance.flags,
-            trace_dir_name,
-        )?;
+        let (device, queue) =
+            adapter.create_device_and_queue_from_hal(hal_device, desc, self.instance.flags)?;
 
         let device_id = devices_fid.assign(device);
         resource_log!("Created Device {:?}", device_id);
diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml
index 26734a331..d9d024a90 100644
--- a/wgpu-types/Cargo.toml
+++ b/wgpu-types/Cargo.toml
@@ -43,6 +43,8 @@ fragile-send-sync-non-atomic-wasm = []
 serde = ["dep:serde", "bitflags/serde"]
 # Enables some internal instrumentation for debugging purposes.
 counters = []
+# Enables variants of `Trace` other than `Trace::Off`
+trace = ["std"]
 
 [dependencies]
 bitflags = { workspace = true, features = ["serde"] }
diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs
index 334894a20..c6dc50c5a 100644
--- a/wgpu-types/src/lib.rs
+++ b/wgpu-types/src/lib.rs
@@ -1148,6 +1148,9 @@ pub struct DeviceDescriptor<L> {
     pub required_limits: Limits,
     /// Hints for memory allocation strategies.
     pub memory_hints: MemoryHints,
+    /// Whether API tracing for debugging is enabled,
+    /// and where the trace is written if so.
+    pub trace: Trace,
 }
 
 impl<L> DeviceDescriptor<L> {
@@ -1159,10 +1162,31 @@ impl<L> DeviceDescriptor<L> {
             required_features: self.required_features,
             required_limits: self.required_limits.clone(),
             memory_hints: self.memory_hints.clone(),
+            trace: self.trace.clone(),
         }
     }
 }
 
+/// Controls API call tracing and specifies where the trace is written.
+///
+/// **Note:** Tracing is currently unavailable.
+/// See [issue 5974](https://github.com/gfx-rs/wgpu/issues/5974) for updates.
+#[derive(Clone, Debug, Default)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+// This enum must be non-exhaustive so that enabling the "trace" feature is not a semver break.
+#[non_exhaustive]
+pub enum Trace {
+    /// Tracing disabled.
+    #[default]
+    Off,
+
+    /// Tracing enabled.
+    #[cfg(feature = "trace")]
+    // This must be owned rather than `&'a Path`, because if it were that, then the lifetime
+    // parameter would be unused when the "trace" feature is disabled, which is prohibited.
+    Directory(std::path::PathBuf),
+}
+
 bitflags::bitflags! {
     /// Describes the shader stages that a binding will be visible from.
     ///
diff --git a/wgpu/src/api/adapter.rs b/wgpu/src/api/adapter.rs
index 40689b6f9..614f43bae 100644
--- a/wgpu/src/api/adapter.rs
+++ b/wgpu/src/api/adapter.rs
@@ -46,8 +46,7 @@ impl Adapter {
     /// # Arguments
     ///
     /// - `desc` - Description of the features and limits requested from the given device.
-    /// - `trace_path` - Can be used for API call tracing, if that feature is
-    ///   enabled in `wgpu-core`.
+    /// - `trace` - Can be used for API call tracing, if the feature is enabled.
     ///
     /// # Panics
     ///
@@ -61,9 +60,8 @@ impl Adapter {
     pub fn request_device(
         &self,
         desc: &DeviceDescriptor<'_>,
-        trace_path: Option<&std::path::Path>,
     ) -> impl Future<Output = Result<(Device, Queue), RequestDeviceError>> + WasmNotSend {
-        let device = self.inner.request_device(desc, trace_path);
+        let device = self.inner.request_device(desc);
         async move {
             device
                 .await
@@ -82,13 +80,12 @@ impl Adapter {
         &self,
         hal_device: hal::OpenDevice<A>,
         desc: &DeviceDescriptor<'_>,
-        trace_path: Option<&std::path::Path>,
     ) -> Result<(Device, Queue), RequestDeviceError> {
         let core_adapter = self.inner.as_core();
         let (device, queue) = unsafe {
             core_adapter
                 .context
-                .create_device_from_hal(core_adapter, hal_device, desc, trace_path)
+                .create_device_from_hal(core_adapter, hal_device, desc)
         }?;
 
         Ok((
diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs
index da2388981..710cc38cc 100644
--- a/wgpu/src/backend/webgpu.rs
+++ b/wgpu/src/backend/webgpu.rs
@@ -1591,10 +1591,9 @@ impl dispatch::AdapterInterface for WebAdapter {
     fn request_device(
         &self,
         desc: &crate::DeviceDescriptor<'_>,
-        trace_dir: Option<&std::path::Path>,
     ) -> Pin<Box<dyn dispatch::RequestDeviceFuture>> {
-        if trace_dir.is_some() {
-            //Error: Tracing isn't supported on the Web target
+        if !matches!(desc.trace, wgt::Trace::Off) {
+            log::warn!("The `trace` parameter is not supported on the WebGPU backend.");
         }
 
         let mapped_desc = webgpu_sys::GpuDeviceDescriptor::new();
diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs
index 2a8925f47..53717bea2 100644
--- a/wgpu/src/backend/wgpu_core.rs
+++ b/wgpu/src/backend/wgpu_core.rs
@@ -104,11 +104,16 @@ impl ContextWgpuCore {
         adapter: &CoreAdapter,
         hal_device: hal::OpenDevice<A>,
         desc: &crate::DeviceDescriptor<'_>,
-        trace_dir: Option<&std::path::Path>,
     ) -> Result<(CoreDevice, CoreQueue), crate::RequestDeviceError> {
-        if trace_dir.is_some() {
-            log::error!("Feature 'trace' has been removed temporarily, see https://github.com/gfx-rs/wgpu/issues/5974");
+        if !matches!(desc.trace, wgt::Trace::Off) {
+            log::error!(
+                "
+                Feature 'trace' has been removed temporarily; \
+                see https://github.com/gfx-rs/wgpu/issues/5974. \
+                The `trace` parameter will have no effect."
+            );
         }
+
         let (device_id, queue_id) = unsafe {
             self.0.create_device_from_hal(
                 adapter.id,
@@ -116,7 +121,6 @@ impl ContextWgpuCore {
                 &desc.map_label(|l| l.map(Borrowed)),
                 None,
                 None,
-                None,
             )
         }?;
         let error_sink = Arc::new(Mutex::new(ErrorSinkRaw::new()));
@@ -876,17 +880,21 @@ impl dispatch::AdapterInterface for CoreAdapter {
     fn request_device(
         &self,
         desc: &crate::DeviceDescriptor<'_>,
-        trace_dir: Option<&std::path::Path>,
     ) -> Pin<Box<dyn dispatch::RequestDeviceFuture>> {
-        if trace_dir.is_some() {
-            log::error!("Feature 'trace' has been removed temporarily, see https://github.com/gfx-rs/wgpu/issues/5974");
+        if !matches!(desc.trace, wgt::Trace::Off) {
+            log::error!(
+                "
+                Feature 'trace' has been removed temporarily; \
+                see https://github.com/gfx-rs/wgpu/issues/5974. \
+                The `trace` parameter will have no effect."
+            );
         }
+
         let res = self.context.0.adapter_request_device(
             self.id,
             &desc.map_label(|l| l.map(Borrowed)),
             None,
             None,
-            None,
         );
         let (device_id, queue_id) = match res {
             Ok(ids) => ids,
diff --git a/wgpu/src/dispatch.rs b/wgpu/src/dispatch.rs
index a3bc4fafa..6c47eb48c 100644
--- a/wgpu/src/dispatch.rs
+++ b/wgpu/src/dispatch.rs
@@ -82,7 +82,6 @@ pub trait AdapterInterface: CommonTraits {
     fn request_device(
         &self,
         desc: &crate::DeviceDescriptor<'_>,
-        trace_dir: Option<&std::path::Path>,
     ) -> Pin<Box<dyn RequestDeviceFuture>>;
 
     fn is_surface_supported(&self, surface: &DispatchSurface) -> bool;
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index ec09984b6..7dfa47f22 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -85,7 +85,7 @@ pub use wgt::{
     StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, SurfaceCapabilities,
     SurfaceStatus, TexelCopyBufferLayout, TextureAspect, TextureDimension, TextureFormat,
     TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, TextureTransition,
-    TextureUsages, TextureUses, TextureViewDimension, VertexAttribute, VertexFormat,
+    TextureUsages, TextureUses, TextureViewDimension, Trace, VertexAttribute, VertexFormat,
     VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT,
     COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
     QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT,