Repository: gfx-rs/wgpu
Pull request #5919 (2024-07-06 10:10:38)

Naga features for platform dependent compilation of msl & hlsl
**Connections**
* Quite related to https://github.com/gfx-rs/wgpu/issues/3514

**Description**

Adds `cfg_alias` indirection to Naga's x_out features which allows us to introduce new features for platform dependent compilation. This in turn allows wgpu-hal to request shading language output more conditionally.

Drawbacks:
* build.rs also on Naga
* new feature names are kinda clunky

**Testing**
Eyeballing on Windows. Single sample of `cargo clean && cargo build -p wgpu --timings` looked even more promising than I would have imagined:

Before:
![image](https://github.com/gfx-rs/wgpu/assets/1220815/4046de3c-d5ed-4415-91f7-e023c0a3fbc9)

After:
![image](https://github.com/gfx-rs/wgpu/assets/1220815/a1d49f21-174a-4c96-8aa9-8f856b415d72)

1.4s cpu time saved on this setup for not actually touching anything 😮. When building wgpu this is even on the hot path, meaning the the wgpu build as a whole got that much faster!
Edit: as expected lots of noise here. Running again on trunk I got 7.0s and 6.2s here, so above trunk compilation is likely with cold file caches. ymmv.

TODO:
* [ ] changelog entry
* [ ] test this manually on mac or linux and report impact

**Checklist**

- [x] Run `cargo fmt`.
- [x] 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/.github/workflows/docs.yml b/.github/workflows/docs.yml
index abf07a36c..b1c83e53b 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -41,7 +41,7 @@ jobs:
         if: ${{ failure() }}
 
       - name: Deploy the docs
-        uses: JamesIves/github-pages-deploy-action@v4.6.1
+        uses: JamesIves/github-pages-deploy-action@v4.6.3
         if: github.ref == 'refs/heads/trunk'
         with:
           token: ${{ secrets.WEB_DEPLOY }}
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index e5560b50c..e8a630024 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -41,7 +41,7 @@ jobs:
         run: cargo xtask run-wasm --no-serve
 
       - name: Deploy WebGPU examples
-        uses: JamesIves/github-pages-deploy-action@v4.6.1
+        uses: JamesIves/github-pages-deploy-action@v4.6.3
         if: github.ref == 'refs/heads/trunk'
         with:
           token: ${{ secrets.WEB_DEPLOY }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 526f6f99c..e580c550d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -151,6 +151,11 @@ By @teoxoy in [#5901](https://github.com/gfx-rs/wgpu/pull/5901)
 
 - Added `as_hal` for `Buffer` to access wgpu created buffers form wgpu-hal. By @JasondeWolff in [#5724](https://github.com/gfx-rs/wgpu/pull/5724)
 - Unconsumed vertex outputs are now always allowed. Removed `StageError::InputNotConsumed`, `Features::SHADER_UNUSED_VERTEX_OUTPUT`, and associated validation. By @Imberflur in [#5531](https://github.com/gfx-rs/wgpu/pull/5531)
+- Added memory allocation hints to `DeviceDescriptor` by @nical in [#5875](https://github.com/gfx-rs/wgpu/pull/5875)
+    - `MemoryHints::Performance`, the default, favors performance over memory usage and will likely cause large amounts of VRAM to be allocated up-front. This hint is typically good for games.
+    - `MemoryHints::MemoryUsage` favors memory usage over performance. This hint is typically useful for smaller applications or UI libraries.
+    - `MemoryHints::Manual` allows the user to specify parameters for the underlying GPU memory allocator. These parameters are subject to change.
+    - These hints may be ignored by some backends. Currently only the Vulkan and D3D12 backends take them into account.
 
 #### Naga
 
diff --git a/Cargo.lock b/Cargo.lock
index 1fd477852..245273bb6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2185,6 +2185,7 @@ dependencies = [
  "arrayvec 0.7.4",
  "bit-set",
  "bitflags 2.6.0",
+ "cfg_aliases",
  "codespan-reporting",
  "diff",
  "env_logger",
diff --git a/benches/benches/root.rs b/benches/benches/root.rs
index 98563f839..6ef2efabc 100644
--- a/benches/benches/root.rs
+++ b/benches/benches/root.rs
@@ -44,6 +44,7 @@ impl DeviceState {
             &wgpu::DeviceDescriptor {
                 required_features: adapter.features(),
                 required_limits: adapter.limits(),
+                memory_hints: wgpu::MemoryHints::Performance,
                 label: Some("RenderPass Device"),
             },
             None,
diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs
index d77c60cac..aafb225fb 100644
--- a/deno_webgpu/lib.rs
+++ b/deno_webgpu/lib.rs
@@ -668,6 +668,7 @@ pub fn op_webgpu_request_device(
         label: Some(Cow::Owned(label)),
         required_features: required_features.into(),
         required_limits: required_limits.unwrap_or_default(),
+        memory_hints: wgpu_types::MemoryHints::default(),
     };
 
     let (device, queue, maybe_err) = gfx_select!(adapter => instance.adapter_request_device(
diff --git a/examples/src/framework.rs b/examples/src/framework.rs
index b384169c7..ff86cc235 100644
--- a/examples/src/framework.rs
+++ b/examples/src/framework.rs
@@ -319,6 +319,7 @@ impl ExampleContext {
                     label: None,
                     required_features: (optional_features & adapter_features) | required_features,
                     required_limits: needed_limits,
+                    memory_hints: wgpu::MemoryHints::MemoryUsage,
                 },
                 trace_dir.ok().as_ref().map(std::path::Path::new),
             )
diff --git a/examples/src/hello_compute/mod.rs b/examples/src/hello_compute/mod.rs
index cdd6d439d..fb23e1395 100644
--- a/examples/src/hello_compute/mod.rs
+++ b/examples/src/hello_compute/mod.rs
@@ -50,6 +50,7 @@ async fn execute_gpu(numbers: &[u32]) -> Option<Vec<u32>> {
                 label: None,
                 required_features: wgpu::Features::empty(),
                 required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
             },
             None,
         )
diff --git a/examples/src/hello_synchronization/mod.rs b/examples/src/hello_synchronization/mod.rs
index 9b6675289..d98f1bb8d 100644
--- a/examples/src/hello_synchronization/mod.rs
+++ b/examples/src/hello_synchronization/mod.rs
@@ -19,6 +19,7 @@ async fn run() {
                 label: None,
                 required_features: wgpu::Features::empty(),
                 required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::Performance,
             },
             None,
         )
diff --git a/examples/src/hello_triangle/mod.rs b/examples/src/hello_triangle/mod.rs
index e4d42674f..41c058350 100644
--- a/examples/src/hello_triangle/mod.rs
+++ b/examples/src/hello_triangle/mod.rs
@@ -32,6 +32,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
                 // 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,
         )
diff --git a/examples/src/hello_windows/mod.rs b/examples/src/hello_windows/mod.rs
index 7d81dbef7..b568f35d3 100644
--- a/examples/src/hello_windows/mod.rs
+++ b/examples/src/hello_windows/mod.rs
@@ -75,6 +75,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Arc<Window>, wgpu::Color
                 label: None,
                 required_features: wgpu::Features::empty(),
                 required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
             },
             None,
         )
diff --git a/examples/src/hello_workgroups/mod.rs b/examples/src/hello_workgroups/mod.rs
index 0416451da..0184981c0 100644
--- a/examples/src/hello_workgroups/mod.rs
+++ b/examples/src/hello_workgroups/mod.rs
@@ -32,6 +32,7 @@ async fn run() {
                 label: None,
                 required_features: wgpu::Features::empty(),
                 required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
             },
             None,
         )
diff --git a/examples/src/render_to_texture/mod.rs b/examples/src/render_to_texture/mod.rs
index caed73674..c0922bc2e 100644
--- a/examples/src/render_to_texture/mod.rs
+++ b/examples/src/render_to_texture/mod.rs
@@ -21,6 +21,7 @@ async fn run(_path: Option<String>) {
                 label: None,
                 required_features: wgpu::Features::empty(),
                 required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
             },
             None,
         )
diff --git a/examples/src/repeated_compute/mod.rs b/examples/src/repeated_compute/mod.rs
index 72b615251..330b930f6 100644
--- a/examples/src/repeated_compute/mod.rs
+++ b/examples/src/repeated_compute/mod.rs
@@ -172,6 +172,7 @@ impl WgpuContext {
                     label: None,
                     required_features: wgpu::Features::empty(),
                     required_limits: wgpu::Limits::downlevel_defaults(),
+                    memory_hints: wgpu::MemoryHints::Performance,
                 },
                 None,
             )
diff --git a/examples/src/storage_texture/mod.rs b/examples/src/storage_texture/mod.rs
index 04253e818..d6a06d6e2 100644
--- a/examples/src/storage_texture/mod.rs
+++ b/examples/src/storage_texture/mod.rs
@@ -35,6 +35,7 @@ async fn run(_path: Option<String>) {
                 label: None,
                 required_features: wgpu::Features::empty(),
                 required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
             },
             None,
         )
diff --git a/examples/src/timestamp_queries/mod.rs b/examples/src/timestamp_queries/mod.rs
index e396023a0..d712762cf 100644
--- a/examples/src/timestamp_queries/mod.rs
+++ b/examples/src/timestamp_queries/mod.rs
@@ -216,6 +216,7 @@ async fn run() {
                 label: None,
                 required_features: features,
                 required_limits: wgpu::Limits::downlevel_defaults(),
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
             },
             None,
         )
diff --git a/examples/src/uniform_values/mod.rs b/examples/src/uniform_values/mod.rs
index c53a18972..0adbf4e46 100644
--- a/examples/src/uniform_values/mod.rs
+++ b/examples/src/uniform_values/mod.rs
@@ -115,6 +115,7 @@ impl WgpuContext {
                     label: None,
                     required_features: wgpu::Features::empty(),
                     required_limits: wgpu::Limits::downlevel_defaults(),
+                    memory_hints: wgpu::MemoryHints::MemoryUsage,
                 },
                 None,
             )
diff --git a/naga/Cargo.toml b/naga/Cargo.toml
index 698244a97..8478cc6f7 100644
--- a/naga/Cargo.toml
+++ b/naga/Cargo.toml
@@ -24,7 +24,18 @@ default = []
 dot-out = []
 glsl-in = ["dep:pp-rs"]
 glsl-out = []
+
+## Enables outputting to the Metal Shading Language (MSL).
+##
+## This enables MSL output regardless of the target platform.
+## If you want to enable it only when targeting iOS/tvOS/watchOS/macOS, use `naga/msl-out-if-target-apple`.
 msl-out = []
+
+## Enables outputting to the Metal Shading Language (MSL) only if the target platform is iOS/tvOS/watchOS/macOS.
+##
+## If you want to enable MSL output it regardless of the target platform, use `naga/msl-out`.
+msl-out-if-target-apple = []
+
 serialize = ["dep:serde", "bitflags/serde", "indexmap/serde"]
 deserialize = ["dep:serde", "bitflags/serde", "indexmap/serde"]
 arbitrary = ["dep:arbitrary", "bitflags/arbitrary", "indexmap/arbitrary"]
@@ -32,7 +43,18 @@ spv-in = ["dep:petgraph", "dep:spirv"]
 spv-out = ["dep:spirv"]
 wgsl-in = ["dep:hexf-parse", "dep:unicode-xid", "compact"]
 wgsl-out = []
+
+## Enables outputting to HLSL (Microsoft's High-Level Shader Language).
+##
+## This enables HLSL output regardless of the target platform.
+## If you want to enable it only when targeting Windows, use `hlsl-out-if-target-windows`.
 hlsl-out = []
+
+## Enables outputting to HLSL (Microsoft's High-Level Shader Language) only if the target platform is Windows.
+##
+## If you want to enable HLSL output it regardless of the target platform, use `naga/hlsl-out`.
+hlsl-out-if-target-windows = []
+
 compact = []
 
 [dependencies]
@@ -56,6 +78,9 @@ hexf-parse = { version = "0.2.1", optional = true }
 unicode-xid = { version = "0.2.3", optional = true }
 arrayvec.workspace = true
 
+[build-dependencies]
+cfg_aliases.workspace = true
+
 [dev-dependencies]
 diff = "0.1"
 env_logger = "0.11"
diff --git a/naga/build.rs b/naga/build.rs
new file mode 100644
index 000000000..e263f626a
--- /dev/null
+++ b/naga/build.rs
@@ -0,0 +1,10 @@
+fn main() {
+    cfg_aliases::cfg_aliases! {
+        dot_out: { feature = "dot-out" },
+        glsl_out: { feature = "glsl-out" },
+        hlsl_out: { any(feature = "hlsl-out", all(target_os = "windows", feature = "hlsl-out-if-target-windows")) },
+        msl_out: { any(feature = "msl-out", all(any(target_os = "ios", target_os = "macos"), feature = "msl-out-if-target-apple")) },
+        spv_out: { feature = "spv-out" },
+        wgsl_out: { feature = "wgsl-out" },
+    }
+}
diff --git a/naga/src/back/mod.rs b/naga/src/back/mod.rs
index fb77b107c..364d0f250 100644
--- a/naga/src/back/mod.rs
+++ b/naga/src/back/mod.rs
@@ -3,25 +3,20 @@ Backend functions that export shader [`Module`](super::Module)s into binary and
 */
 #![allow(dead_code)] // can be dead if none of the enabled backends need it
 
-#[cfg(feature = "dot-out")]
+#[cfg(dot_out)]
 pub mod dot;
-#[cfg(feature = "glsl-out")]
+#[cfg(glsl_out)]
 pub mod glsl;
-#[cfg(feature = "hlsl-out")]
+#[cfg(hlsl_out)]
 pub mod hlsl;
-#[cfg(feature = "msl-out")]
+#[cfg(msl_out)]
 pub mod msl;
-#[cfg(feature = "spv-out")]
+#[cfg(spv_out)]
 pub mod spv;
-#[cfg(feature = "wgsl-out")]
+#[cfg(wgsl_out)]
 pub mod wgsl;
 
-#[cfg(any(
-    feature = "hlsl-out",
-    feature = "msl-out",
-    feature = "spv-out",
-    feature = "glsl-out"
-))]
+#[cfg(any(hlsl_out, msl_out, spv_out, glsl_out))]
 pub mod pipeline_constants;
 
 /// Names of vector components.
diff --git a/naga/src/front/spv/mod.rs b/naga/src/front/spv/mod.rs
index d21811d1d..d154712b2 100644
--- a/naga/src/front/spv/mod.rs
+++ b/naga/src/front/spv/mod.rs
@@ -4335,7 +4335,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
 
         if !self.upgrade_atomics.is_empty() {
             log::info!("Upgrading atomic pointers...");
-            module.upgrade_atomics(std::mem::take(&mut self.upgrade_atomics))?;
+            module.upgrade_atomics(mem::take(&mut self.upgrade_atomics))?;
         }
 
         // Do entry point specific processing after all functions are parsed so that we can
@@ -5710,7 +5710,7 @@ mod test {
         let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
     }
 
-    #[cfg(all(feature = "wgsl-in", feature = "wgsl-out"))]
+    #[cfg(all(feature = "wgsl-in", wgsl_out))]
     #[test]
     fn atomic_i_inc() {
         let _ = env_logger::builder().is_test(true).try_init();
diff --git a/naga/src/keywords/mod.rs b/naga/src/keywords/mod.rs
index d54a1704f..2b4cc9ae1 100644
--- a/naga/src/keywords/mod.rs
+++ b/naga/src/keywords/mod.rs
@@ -2,5 +2,5 @@
 Lists of reserved keywords for each shading language with a [frontend][crate::front] or [backend][crate::back].
 */
 
-#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
+#[cfg(any(feature = "wgsl-in", wgsl_out))]
 pub mod wgsl;
diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs
index 3cef19aed..8c580588f 100644
--- a/wgpu-core/src/instance.rs
+++ b/wgpu-core/src/instance.rs
@@ -351,9 +351,11 @@ impl<A: HalApi> Adapter<A> {
         }
 
         let open = unsafe {
-            self.raw
-                .adapter
-                .open(desc.required_features, &desc.required_limits)
+            self.raw.adapter.open(
+                desc.required_features,
+                &desc.required_limits,
+                &desc.memory_hints,
+            )
         }
         .map_err(|err| match err {
             hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml
index b00f6e8ce..5b1fcb726 100644
--- a/wgpu-hal/Cargo.toml
+++ b/wgpu-hal/Cargo.toml
@@ -37,7 +37,14 @@ ignored = ["cfg_aliases"]
 [lib]
 
 [features]
-metal = ["naga/msl-out", "dep:block"]
+## Enables the Metal backend when targeting Apple platforms.
+##
+## Has no effect on non-Apple platforms.
+metal = [
+    # Metal is only available on Apple platforms, therefore request MSL output also only if we target an Apple platform.
+    "naga/msl-out-if-target-apple",
+    "dep:block",
+]
 vulkan = [
     "naga/spv-out",
     "dep:ash",
@@ -56,8 +63,12 @@ gles = [
     "dep:ndk-sys",
     "winapi/libloaderapi",
 ]
+## Enables the DX12 backend when targeting Windows.
+##
+## Has no effect if not targeting Windows.
 dx12 = [
-    "naga/hlsl-out",
+    # DX12 is only available on Windows, therefore request HLSL output also only if we target Windows.
+    "naga/hlsl-out-if-target-windows",
     "dep:d3d12",
     "dep:bit-set",
     "dep:libloading",
diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs
index bd09a4e72..d61cec738 100644
--- a/wgpu-hal/examples/halmark/main.rs
+++ b/wgpu-hal/examples/halmark/main.rs
@@ -125,7 +125,11 @@ impl<A: hal::Api> Example<A> {
 
         let hal::OpenDevice { device, queue } = unsafe {
             adapter
-                .open(wgt::Features::empty(), &wgt::Limits::default())
+                .open(
+                    wgt::Features::empty(),
+                    &wgt::Limits::default(),
+                    &wgt::MemoryHints::default(),
+                )
                 .unwrap()
         };
 
diff --git a/wgpu-hal/examples/raw-gles.rs b/wgpu-hal/examples/raw-gles.rs
index 675a51869..ceab5b065 100644
--- a/wgpu-hal/examples/raw-gles.rs
+++ b/wgpu-hal/examples/raw-gles.rs
@@ -124,9 +124,11 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
     use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
 
     let od = unsafe {
-        exposed
-            .adapter
-            .open(wgt::Features::empty(), &wgt::Limits::downlevel_defaults())
+        exposed.adapter.open(
+            wgt::Features::empty(),
+            &wgt::Limits::downlevel_defaults(),
+            &wgt::MemoryHints::default(),
+        )
     }
     .unwrap();
 
diff --git a/wgpu-hal/examples/ray-traced-triangle/main.rs b/wgpu-hal/examples/ray-traced-triangle/main.rs
index f27e3d067..e6481aae6 100644
--- a/wgpu-hal/examples/ray-traced-triangle/main.rs
+++ b/wgpu-hal/examples/ray-traced-triangle/main.rs
@@ -249,8 +249,15 @@ impl<A: hal::Api> Example<A> {
             .expect("Surface doesn't support presentation");
         log::info!("Surface caps: {:#?}", surface_caps);
 
-        let hal::OpenDevice { device, queue } =
-            unsafe { adapter.open(features, &wgt::Limits::default()).unwrap() };
+        let hal::OpenDevice { device, queue } = unsafe {
+            adapter
+                .open(
+                    features,
+                    &wgt::Limits::default(),
+                    &wgt::MemoryHints::Performance,
+                )
+                .unwrap()
+        };
 
         let window_size: (u32, u32) = window.inner_size().into();
         dbg!(&surface_caps.formats);
diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs
index a81f15fc3..6c8ed1cca 100644
--- a/wgpu-hal/src/dx12/adapter.rs
+++ b/wgpu-hal/src/dx12/adapter.rs
@@ -503,6 +503,7 @@ impl crate::Adapter for super::Adapter {
         &self,
         _features: wgt::Features,
         limits: &wgt::Limits,
+        memory_hints: &wgt::MemoryHints,
     ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
         let queue = {
             profiling::scope!("ID3D12Device::CreateCommandQueue");
@@ -520,6 +521,7 @@ impl crate::Adapter for super::Adapter {
             self.device.clone(),
             queue.clone(),
             limits,
+            memory_hints,
             self.private_caps,
             &self.library,
             self.dxc_container.clone(),
diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs
index ceb430a70..eeb60acbf 100644
--- a/wgpu-hal/src/dx12/device.rs
+++ b/wgpu-hal/src/dx12/device.rs
@@ -28,12 +28,13 @@ impl super::Device {
         raw: d3d12::Device,
         present_queue: d3d12::CommandQueue,
         limits: &wgt::Limits,
+        memory_hints: &wgt::MemoryHints,
         private_caps: super::PrivateCapabilities,
         library: &Arc<d3d12::D3D12Lib>,
         dxc_container: Option<Arc<shader_compilation::DxcContainer>>,
     ) -> Result<Self, DeviceError> {
         let mem_allocator = if private_caps.suballocation_supported {
-            super::suballocation::create_allocator_wrapper(&raw)?
+            super::suballocation::create_allocator_wrapper(&raw, memory_hints)?
         } else {
             None
         };
diff --git a/wgpu-hal/src/dx12/suballocation.rs b/wgpu-hal/src/dx12/suballocation.rs
index 35204a1b9..b7ddbaf0b 100644
--- a/wgpu-hal/src/dx12/suballocation.rs
+++ b/wgpu-hal/src/dx12/suballocation.rs
@@ -46,13 +46,31 @@ mod placed {
 
     pub(crate) fn create_allocator_wrapper(
         raw: &d3d12::Device,
+        memory_hints: &wgt::MemoryHints,
     ) -> Result<Option<Mutex<GpuAllocatorWrapper>>, crate::DeviceError> {
         let device = raw.as_ptr();
 
+        // TODO: the allocator's configuration should take hardware capability into
+        // account.
+        let mb = 1024 * 1024;
+        let allocation_sizes = match memory_hints {
+            wgt::MemoryHints::Performance => gpu_allocator::AllocationSizes::default(),
+            wgt::MemoryHints::MemoryUsage => gpu_allocator::AllocationSizes::new(8 * mb, 4 * mb),
+            wgt::MemoryHints::Manual {
+                suballocated_device_memory_block_size,
+            } => {
+                // TODO: Would it be useful to expose the host size in memory hints
+                // instead of always using half of the device size?
+                let device_size = suballocated_device_memory_block_size.start;
+                let host_size = device_size / 2;
+                gpu_allocator::AllocationSizes::new(device_size, host_size)
+            }
+        };
+
         match gpu_allocator::d3d12::Allocator::new(&gpu_allocator::d3d12::AllocatorCreateDesc {
             device: gpu_allocator::d3d12::ID3D12DeviceVersion::Device(device.as_windows().clone()),
             debug_settings: Default::default(),
-            allocation_sizes: gpu_allocator::AllocationSizes::default(),
+            allocation_sizes,
         }) {
             Ok(allocator) => Ok(Some(Mutex::new(GpuAllocatorWrapper { allocator }))),
             Err(e) => {
@@ -279,6 +297,7 @@ mod committed {
     #[allow(unused)]
     pub(crate) fn create_allocator_wrapper(
         _raw: &d3d12::Device,
+        _memory_hints: &wgt::MemoryHints,
     ) -> Result<Option<Mutex<GpuAllocatorWrapper>>, crate::DeviceError> {
         Ok(None)
     }
diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs
index 227dce7ee..5d6c42ab8 100644
--- a/wgpu-hal/src/empty.rs
+++ b/wgpu-hal/src/empty.rs
@@ -92,6 +92,7 @@ impl crate::Adapter for Context {
         &self,
         features: wgt::Features,
         _limits: &wgt::Limits,
+        _memory_hints: &wgt::MemoryHints,
     ) -> DeviceResult<crate::OpenDevice<Api>> {
         Err(crate::DeviceError::Lost)
     }
diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs
index 933c36dc8..1cda99b33 100644
--- a/wgpu-hal/src/gles/adapter.rs
+++ b/wgpu-hal/src/gles/adapter.rs
@@ -929,6 +929,7 @@ impl crate::Adapter for super::Adapter {
         &self,
         features: wgt::Features,
         _limits: &wgt::Limits,
+        _memory_hints: &wgt::MemoryHints,
     ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
         let gl = &self.shared.context.lock();
         unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs
index ccc459c10..e63f25ab0 100644
--- a/wgpu-hal/src/lib.rs
+++ b/wgpu-hal/src/lib.rs
@@ -562,6 +562,7 @@ pub trait Adapter: WasmNotSendSync {
         &self,
         features: wgt::Features,
         limits: &wgt::Limits,
+        memory_hints: &wgt::MemoryHints,
     ) -> Result<OpenDevice<Self::A>, DeviceError>;
 
     /// Return the set of supported capabilities for a texture format.
diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs
index 7f8e789b4..924902517 100644
--- a/wgpu-hal/src/metal/adapter.rs
+++ b/wgpu-hal/src/metal/adapter.rs
@@ -25,6 +25,7 @@ impl crate::Adapter for super::Adapter {
         &self,
         features: wgt::Features,
         _limits: &wgt::Limits,
+        _memory_hints: &wgt::MemoryHints,
     ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
         let queue = self
             .shared
diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs
index efe32929a..81205c629 100644
--- a/wgpu-hal/src/vulkan/adapter.rs
+++ b/wgpu-hal/src/vulkan/adapter.rs
@@ -1583,6 +1583,7 @@ impl super::Adapter {
         handle_is_owned: bool,
         enabled_extensions: &[&'static CStr],
         features: wgt::Features,
+        memory_hints: &wgt::MemoryHints,
         family_index: u32,
         queue_index: u32,
     ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
@@ -1833,7 +1834,54 @@ impl super::Adapter {
 
         let mem_allocator = {
             let limits = self.phd_capabilities.properties.limits;
-            let config = gpu_alloc::Config::i_am_prototyping(); //TODO
+
+            // Note: the parameters here are not set in stone nor where they picked with
+            // strong confidence.
+            // `final_free_list_chunk` should be bigger than starting_free_list_chunk if
+            // we want the behavior of starting with smaller block sizes and using larger
+            // ones only after we observe that the small ones aren't enough, which I think
+            // is a good "I don't know what the workload is going to be like" approach.
+            //
+            // For reference, `VMA`, and `gpu_allocator` both start with 256 MB blocks
+            // (then VMA doubles the block size each time it needs a new block).
+            // At some point it would be good to experiment with real workloads
+            //
+            // TODO(#5925): The plan is to switch the Vulkan backend from `gpu_alloc` to
+            // `gpu_allocator` which has a different (simpler) set of configuration options.
+            //
+            // TODO: These parameters should take hardware capabilities into account.
+            let mb = 1024 * 1024;
+            let perf_cfg = gpu_alloc::Config {
+                starting_free_list_chunk: 128 * mb,
+                final_free_list_chunk: 512 * mb,
+                minimal_buddy_size: 1,
+                initial_buddy_dedicated_size: 8 * mb,
+                dedicated_threshold: 32 * mb,
+                preferred_dedicated_threshold: mb,
+                transient_dedicated_threshold: 128 * mb,
+            };
+            let mem_usage_cfg = gpu_alloc::Config {
+                starting_free_list_chunk: 8 * mb,
+                final_free_list_chunk: 64 * mb,
+                minimal_buddy_size: 1,
+                initial_buddy_dedicated_size: 8 * mb,
+                dedicated_threshold: 8 * mb,
+                preferred_dedicated_threshold: mb,
+                transient_dedicated_threshold: 16 * mb,
+            };
+            let config = match memory_hints {
+                wgt::MemoryHints::Performance => perf_cfg,
+                wgt::MemoryHints::MemoryUsage => mem_usage_cfg,
+                wgt::MemoryHints::Manual {
+                    suballocated_device_memory_block_size,
+                } => gpu_alloc::Config {
+                    starting_free_list_chunk: suballocated_device_memory_block_size.start,
+                    final_free_list_chunk: suballocated_device_memory_block_size.end,
+                    initial_buddy_dedicated_size: suballocated_device_memory_block_size.start,
+                    ..perf_cfg
+                },
+            };
+
             let max_memory_allocation_size =
                 if let Some(maintenance_3) = self.phd_capabilities.maintenance_3 {
                     maintenance_3.max_memory_allocation_size
@@ -1895,6 +1943,7 @@ impl crate::Adapter for super::Adapter {
         &self,
         features: wgt::Features,
         _limits: &wgt::Limits,
+        memory_hints: &wgt::MemoryHints,
     ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
         let enabled_extensions = self.required_device_extensions(features);
         let mut enabled_phd_features = self.physical_device_features(&enabled_extensions, features);
@@ -1928,6 +1977,7 @@ impl crate::Adapter for super::Adapter {
                 true,
                 &enabled_extensions,
                 features,
+                memory_hints,
                 family_info.queue_family_index,
                 0,
             )
diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs
index 04532a4c7..d61f43496 100644
--- a/wgpu-types/src/lib.rs
+++ b/wgpu-types/src/lib.rs
@@ -1769,11 +1769,43 @@ pub struct AdapterInfo {
     pub backend: Backend,
 }
 
+/// Hints to the device about the memory allocation strategy.
+///
+/// Some backends may ignore these hints.
+#[derive(Clone, Debug, Default)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum MemoryHints {
+    /// Favor performance over memory usage (the default value).
+    #[default]
+    Performance,
+    /// Favor memory usage over performance.
+    MemoryUsage,
+    /// Applications that have control over the content that is rendered
+    /// (typically games) may find an optimal compromise between memory
+    /// usage and performance by specifying the allocation configuration.
+    Manual {
+        /// Defines the range of allowed memory block sizes for sub-allocated
+        /// resources.
+        ///
+        /// The backend may attempt to group multiple resources into fewer
+        /// device memory blocks (sub-allocation) for performance reasons.
+        /// The start of the provided range specifies the initial memory
+        /// block size for sub-allocated resources. After running out of
+        /// space in existing memory blocks, the backend may chose to
+        /// progressively increase the block size of subsequent allocations
+        /// up to a limit specified by the end of the range.
+        ///
+        /// This does not limit resource sizes. If a resource does not fit
+        /// in the specified range, it will typically be placed in a dedicated
+        /// memory block.
+        suballocated_device_memory_block_size: Range<u64>,
+    },
+}
+
 /// Describes a [`Device`](../wgpu/struct.Device.html).
 ///
 /// Corresponds to [WebGPU `GPUDeviceDescriptor`](
 /// https://gpuweb.github.io/gpuweb/#gpudevicedescriptor).
-#[repr(C)]
 #[derive(Clone, Debug, Default)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub struct DeviceDescriptor<L> {
@@ -1791,6 +1823,8 @@ pub struct DeviceDescriptor<L> {
     /// Exactly the specified limits, and no better or worse,
     /// will be allowed in validation of API calls on the resulting device.
     pub required_limits: Limits,
+    /// Hints for memory allocation strategies.
+    pub memory_hints: MemoryHints,
 }
 
 impl<L> DeviceDescriptor<L> {
@@ -1800,6 +1834,7 @@ impl<L> DeviceDescriptor<L> {
             label: fun(&self.label),
             required_features: self.required_features,
             required_limits: self.required_limits.clone(),
+            memory_hints: self.memory_hints.clone(),
         }
     }
 }
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index b0d27e3ef..7da27e355 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -53,7 +53,7 @@ pub use wgt::{
     DepthStencilState, DeviceLostReason, DeviceType, DownlevelCapabilities, DownlevelFlags,
     Dx12Compiler, DynamicOffset, Extent3d, Face, Features, FilterMode, FrontFace,
     Gles3MinorVersion, ImageDataLayout, ImageSubresourceRange, IndexFormat, InstanceDescriptor,
-    InstanceFlags, Limits, MaintainResult, MultisampleState, Origin2d, Origin3d,
+    InstanceFlags, Limits, MaintainResult, MemoryHints, MultisampleState, Origin2d, Origin3d,
     PipelineStatisticsTypes, PolygonMode, PowerPreference, PredefinedColorSpace, PresentMode,
     PresentationTimestamp, PrimitiveState, PrimitiveTopology, PushConstantRange, QueryType,
     RenderBundleDepthStencil, SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel,


Test patch:
diff --git a/naga/tests/snapshots.rs b/naga/tests/snapshots.rs
index d18ea5b62..76fb293d5 100644
--- a/naga/tests/snapshots.rs
+++ b/naga/tests/snapshots.rs
@@ -50,7 +50,7 @@ struct SpirvOutParameters {
     #[serde(default)]
     separate_entry_points: bool,
     #[serde(default)]
-    #[cfg(all(feature = "deserialize", feature = "spv-out"))]
+    #[cfg(all(feature = "deserialize", spv_out))]
     binding_map: naga::back::spv::BindingMap,
 }
 
@@ -69,34 +69,26 @@ struct Parameters {
     bounds_check_policies: naga::proc::BoundsCheckPolicies,
     #[serde(default)]
     spv: SpirvOutParameters,
-    #[cfg(all(feature = "deserialize", feature = "msl-out"))]
+    #[cfg(all(feature = "deserialize", msl_out))]
     #[serde(default)]
     msl: naga::back::msl::Options,
-    #[cfg(all(feature = "deserialize", feature = "msl-out"))]
+    #[cfg(all(feature = "deserialize", msl_out))]
     #[serde(default)]
     msl_pipeline: naga::back::msl::PipelineOptions,
-    #[cfg(all(feature = "deserialize", feature = "glsl-out"))]
+    #[cfg(all(feature = "deserialize", glsl_out))]
     #[serde(default)]
     glsl: naga::back::glsl::Options,
     #[serde(default)]
     glsl_exclude_list: naga::FastHashSet<String>,
-    #[cfg(all(feature = "deserialize", feature = "hlsl-out"))]
+    #[cfg(all(feature = "deserialize", hlsl_out))]
     #[serde(default)]
     hlsl: naga::back::hlsl::Options,
     #[serde(default)]
     wgsl: WgslOutParameters,
-    #[cfg(all(feature = "deserialize", feature = "glsl-out"))]
+    #[cfg(all(feature = "deserialize", glsl_out))]
     #[serde(default)]
     glsl_multiview: Option<std::num::NonZeroU32>,
-    #[cfg(all(
-        feature = "deserialize",
-        any(
-            feature = "hlsl-out",
-            feature = "msl-out",
-            feature = "spv-out",
-            feature = "glsl-out"
-        )
-    ))]
+    #[cfg(all(feature = "deserialize", any(hlsl_out, msl_out, spv_out, glsl_out)))]
     #[serde(default)]
     pipeline_constants: naga::back::PipelineConstants,
 }
@@ -260,9 +252,9 @@ impl Input {
     }
 }
 
-#[cfg(feature = "hlsl-out")]
+#[cfg(hlsl_out)]
 type FragmentEntryPoint<'a> = naga::back::hlsl::FragmentEntryPoint<'a>;
-#[cfg(not(feature = "hlsl-out"))]
+#[cfg(not(hlsl_out))]
 type FragmentEntryPoint<'a> = ();
 
 #[allow(unused_variables)]
@@ -357,7 +349,7 @@ fn check_targets(
         }
     }
 
-    #[cfg(all(feature = "deserialize", feature = "spv-out"))]
+    #[cfg(all(feature = "deserialize", spv_out))]
     {
         let debug_info = source_code.map(|code| naga::back::spv::DebugInfo {
             source_code: code,
@@ -376,7 +368,7 @@ fn check_targets(
             );
         }
     }
-    #[cfg(all(feature = "deserialize", feature = "msl-out"))]
+    #[cfg(all(feature = "deserialize", msl_out))]
     {
         if targets.contains(Targets::METAL) {
             write_output_msl(
@@ -390,7 +382,7 @@ fn check_targets(
             );
         }
     }
-    #[cfg(all(feature = "deserialize", feature = "glsl-out"))]
+    #[cfg(all(feature = "deserialize", glsl_out))]
     {
         if targets.contains(Targets::GLSL) {
             for ep in module.entry_points.iter() {
@@ -411,14 +403,14 @@ fn check_targets(
             }
         }
     }
-    #[cfg(feature = "dot-out")]
+    #[cfg(dot_out)]
     {
         if targets.contains(Targets::DOT) {
             let string = naga::back::dot::write(module, Some(&info), Default::default()).unwrap();
             input.write_output_file("dot", "dot", string);
         }
     }
-    #[cfg(all(feature = "deserialize", feature = "hlsl-out"))]
+    #[cfg(all(feature = "deserialize", hlsl_out))]
     {
         if targets.contains(Targets::HLSL) {
             write_output_hlsl(
@@ -431,7 +423,7 @@ fn check_targets(
             );
         }
     }
-    #[cfg(all(feature = "deserialize", feature = "wgsl-out"))]
+    #[cfg(all(feature = "deserialize", wgsl_out))]
     {
         if targets.contains(Targets::WGSL) {
             write_output_wgsl(input, module, &info, &params.wgsl);
@@ -439,7 +431,7 @@ fn check_targets(
     }
 }
 
-#[cfg(feature = "spv-out")]
+#[cfg(spv_out)]
 fn write_output_spv(
     input: &Input,
     module: &naga::Module,
@@ -499,7 +491,7 @@ fn write_output_spv(
     }
 }
 
-#[cfg(feature = "spv-out")]
+#[cfg(spv_out)]
 fn write_output_spv_inner(
     input: &Input,
     module: &naga::Module,
@@ -525,7 +517,7 @@ fn write_output_spv_inner(
     input.write_output_file("spv", extension, dis);
 }
 
-#[cfg(feature = "msl-out")]
+#[cfg(msl_out)]
 fn write_output_msl(
     input: &Input,
     module: &naga::Module,
@@ -557,7 +549,7 @@ fn write_output_msl(
     input.write_output_file("msl", "msl", string);
 }
 
-#[cfg(feature = "glsl-out")]
+#[cfg(glsl_out)]
 #[allow(clippy::too_many_arguments)]
 fn write_output_glsl(
     input: &Input,
@@ -599,7 +591,7 @@ fn write_output_glsl(
     input.write_output_file("glsl", &extension, buffer);
 }
 
-#[cfg(feature = "hlsl-out")]
+#[cfg(hlsl_out)]
 fn write_output_hlsl(
     input: &Input,
     module: &naga::Module,
@@ -652,7 +644,7 @@ fn write_output_hlsl(
     config.to_file(input.output_path("hlsl", "ron")).unwrap();
 }
 
-#[cfg(feature = "wgsl-out")]
+#[cfg(wgsl_out)]
 fn write_output_wgsl(
     input: &Input,
     module: &naga::Module,
@@ -957,7 +949,7 @@ fn convert_wgsl() {
     }
 }
 
-#[cfg(all(feature = "wgsl-in", feature = "hlsl-out"))]
+#[cfg(all(feature = "wgsl-in", hlsl_out))]
 #[test]
 fn unconsumed_vertex_outputs_hlsl_out() {
     let load_and_parse = |name| {
@@ -1110,7 +1102,7 @@ fn convert_glsl_folder() {
         .validate(&module)
         .unwrap();
 
-        #[cfg(feature = "wgsl-out")]
+        #[cfg(wgsl_out)]
         {
             write_output_wgsl(&input, &module, &info, &WgslOutParameters::default());
         }
diff --git a/naga/tests/spirv_capabilities.rs b/naga/tests/spirv_capabilities.rs
index 82d7ef74b..f221c7896 100644
--- a/naga/tests/spirv_capabilities.rs
+++ b/naga/tests/spirv_capabilities.rs
@@ -2,7 +2,7 @@
 Test SPIR-V backend capability checks.
 */
 
-#![cfg(all(feature = "wgsl-in", feature = "spv-out"))]
+#![cfg(all(feature = "wgsl-in", spv_out))]
 
 use spirv::Capability as Ca;
 
diff --git a/player/tests/test.rs b/player/tests/test.rs
index a6c7222b6..2aca181c8 100644
--- a/player/tests/test.rs
+++ b/player/tests/test.rs
@@ -112,6 +112,7 @@ impl Test<'_> {
                 label: None,
                 required_features: self.features,
                 required_limits: wgt::Limits::default(),
+                memory_hints: wgt::MemoryHints::default(),
             },
             None,
             Some(device_id),
diff --git a/tests/src/init.rs b/tests/src/init.rs
index f66f08489..3a11b3abe 100644
--- a/tests/src/init.rs
+++ b/tests/src/init.rs
@@ -104,6 +104,7 @@ pub async fn initialize_device(
                 label: None,
                 required_features: features,
                 required_limits: limits,
+                memory_hints: wgpu::MemoryHints::MemoryUsage,
             },
             None,
         )