Repository: gfx-rs/wgpu (language: rust)

Issue:
Add a default feature for wgpu that enables Vulkan & GLES on Windows & Linux
We currently only enable backends based on the target platform with the only exception being GLES which is always available.

We should expose a new feature flag (enabled by default) that covers the GLES and DX11 backends so that users not interested in those backends can avoid compiling them in their binaries.

Feature name ideas:
- `legacy`
- `compat` (mapping to a future WebGPU Compat; might not be the best choice since we support even lower limits/features than what WebGPU Compat is aiming for; i.e. GLES 3.1+ and DX11 FL10+)

See background for this: https://github.com/gfx-rs/wgpu/issues/1221#issuecomment-1430067962



Discussion / hints:
I was recently looking into this but the first roadblock I hit is that we can't differentiate between a new `gles` crate feature and the current `angle` crate feature, both enable `wgc/gles`.

A solution could be to introduce target-specific features in `wgpu-core` and `wgpu-hal` that are no-op on certain platforms.
E.g. `gles-native` (no-op on MacOS) and `gles-angle` (no-op on everything but MacOS). This proposal would be similar to https://github.com/gfx-rs/wgpu/pull/3466.

I'd be happy to make a PR!
I think we're moving in the direction of every backend being feature flagged. We have all the infrastructure in place, we just haven't lifted this to the wgpu level.

Distinguishing between angle and native would be useful!
So after playing around with it a bit I was so unsatisfied it doesn't make sense to me to proceed like that.
The issue basically boils down to Cargo not being able to connect crate features that activate optional dependencies to specific targets.

Introducing `gles-native` and `gles-angle` we can use `cfg`s to guard code to specific targets, but the dependencies these features activate will be active nevertheless. E.g. if we use `default = ["gles-native"]`, OpenGL won't work on MacOS, but the dependencies will still be present.

So the only good solution I can come up with to this problem is to create a separate crate that adds these defaults.
```toml
[package]
name = "wgpu-default-features"

# We want the wgpu-core Metal backend on macOS and iOS.
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc]
workspace = true
features = ["metal"]

# We want the wgpu-core Direct3D backends and OpenGL (via WGL) on Windows.
[target.'cfg(windows)'.dependencies.wgc]
workspace = true
features = ["dx11", "dx12", "gles"]

# We want the wgpu-core Vulkan backend on Unix (but not emscripten, macOS, iOS) and Windows.
[target.'cfg(any(windows, all(unix, not(target_os = "emscripten"), not(target_os = "ios"), not(target_os = "macos"))))'.dependencies.wgc]
workspace = true
features = ["vulkan"]

# We want the wgpu-core GLES backend on Unix (but not macOS, iOS).
[target.'cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies.wgc]
workspace = true
features = ["gles"]
```
And then we can use `default ["dep:wgpu-default-features"]` in `wgpu`.

Just for completeness, not actually proposing this: we could abandon default features.
Cc https://github.com/rust-lang/cargo/issues/1197.
Left a user story there as well.
cont' conversation thread from https://github.com/gfx-rs/wgpu/pull/4815#issuecomment-1837096793
to quickly recap (plz correct me if needed): I suggested going all in on a style like `gles-linux`, `gles-windows`, `gles-mac` which is then translated to just `gles` in the code via https://docs.rs/cfg_aliases/latest/cfg_aliases/
As @daxpedda points out (and described here already!) this doesn't _quite_ work since for instance `gles-linux = ["wgc/gles"]` would drag in`wgc/gles` for on Mac no matter what.

It's pretty awful, but it might be better than the alternative to just hand this problem on to wgc in turn? I.e. `gles-linux = ["wgc/gles-linux"]`. It seems to me that this is the least invasive situation and gives the same feature (literally) to the few direct users for wgpu-core where the problem would bubble up otherwise in the exact same way.
> It's pretty awful, but it might be better than the alternative to just hand this problem on to wgc in turn?

We would have to go further into hal though, because in wgc we are having the exact same problem: `gles-linux = ["hal/gles"]`. Looking at hal, the `gles` feature activates the following dependencies:
- `naga/glsl-out`
- `glow`
- `glutin_wgl_sys`
- `khronos-egl`
- `libloading`

Both `naga/glsl-out` and `glow` are not target specific dependencies, so we would be hitting the same problem again in hal as well: enabling `hal/gles-linux` would still bring in these dependencies no matter the target.
Why is it an issue that the dependencies will be present on the dev machine? They won't be compiled for the targets that don't support them.

> Introducing `gles-native` and `gles-angle` we can use `cfg`s to guard code to specific targets, but the dependencies these features activate will be active nevertheless.

What does "active" mean in this context?
> Why is it an issue that the dependencies will be present on the dev machine? They won't be compiled for the targets that don't support them.

Dead code elimination is not perfect, especially with things that rely on FFI and linked libraries, I don't have any data on that though. But it also does increase compile time, which is not insignificant when looking at the size and amount of dependencies we are talking about here.
E.g. I know it was reported somewhere that MoltenVK is a problem because of linking, which is why it was removed from the default set in `wgpu`.

> > Introducing `gles-native` and `gles-angle` we can use `cfg`s to guard code to specific targets, but the dependencies these features activate will be active nevertheless.
> 
> What does "active" mean in this context?

I should probably stick with the word "enable".
It means that a feature that enables a dependency pulls it in, it becomes part of the dependency graph and has to be compiled and such.
Note that this currently prevents the usage of pipeline-overridable constants on linux, since these don't seem to be supported on glsl-out.

Unsure if they will ever be supported on that backend, if not then I'd say this gains another strong reason to happen.
@9SMTM6 that's a completely orthogonal issue and not a known one I think: Pipeline overridable constants should work everywhere just fine starting with wgpu 0.20.0. Please open a new issue, ideally with a code snippet reproducing the problem.
This issue here is about having no easy opt out of the GLES backend when it's unwanted
@Wumpf I have just realized that too, since I've gotten around to patching out the glsl backend.

Sorry for that, it made sense to me at that point, but evidently my pipeline is too complex for me to be able to properly attribute reason. I did check whether I only had wgpu 20.1+ with cargo tree, but as I said, with the glsl backend patched out it still doesn't work. And the way the error surfaced did not really make sense.

At this point I think the issue lies with naga_oil, I found part of the verbiage there, but I'll have to test more before I make a issue out of that ;-P.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86d5b1d965..40eabeeaee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,6 +51,12 @@ also allows more easily creating these structures inline.
 
 By @cwfitzgerald in [#7133](https://github.com/gfx-rs/wgpu/pull/7133)
 
+#### All Backends Now Have Features
+
+Previously, the `vulkan` and `gles` backends were non-optional on windows, linux, and android and there was no way to disable them. We have now figured out how to properly make them disablable! Additionally, if you turn on the `webgl` feature, you will only get the GLES backend on WebAssembly, it won't leak into native builds, like previously it might have.
+
+By @cwfitzgerald in [#7076](https://github.com/gfx-rs/wgpu/pull/7076).
+
 #### `device.poll` Api Reworked
 
 This release reworked the poll api significantly to allow polling to return errors when polling hits internal timeout limits.
diff --git a/Cargo.lock b/Cargo.lock
index ac0a215b6d..0cf0087ace 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4507,7 +4507,6 @@ dependencies = [
  "parking_lot",
  "profiling",
  "raw-window-handle 0.6.2",
- "serde",
  "smallvec",
  "static_assertions",
  "wasm-bindgen",
@@ -4557,10 +4556,42 @@ dependencies = [
  "serde",
  "smallvec",
  "thiserror 2.0.11",
+ "wgpu-core-deps-apple",
+ "wgpu-core-deps-emscripten",
+ "wgpu-core-deps-wasm",
+ "wgpu-core-deps-windows-linux-android",
  "wgpu-hal",
  "wgpu-types",
 ]
 
+[[package]]
+name = "wgpu-core-deps-apple"
+version = "24.0.0"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-core-deps-emscripten"
+version = "24.0.0"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-core-deps-wasm"
+version = "24.0.0"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-core-deps-windows-linux-android"
+version = "24.0.0"
+dependencies = [
+ "wgpu-hal",
+]
+
 [[package]]
 name = "wgpu-example-01-hello-compute"
 version = "0.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index 32461eddf1..ae3573aac7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,7 @@ members = [
     "player",
     "tests",
     "wgpu-core",
+    "wgpu-core/platform-deps/*",
     "wgpu-hal",
     "wgpu-info",
     "wgpu-macros",
@@ -35,6 +36,7 @@ default-members = [
     "player",
     "tests",
     "wgpu-core",
+    "wgpu-core/platform-deps/*",
     "wgpu-hal",
     "wgpu-info",
     "wgpu-macros",
@@ -74,6 +76,11 @@ wgpu-macros = { version = "24.0.0", path = "./wgpu-macros" }
 wgpu-test = { version = "24.0.0", path = "./tests" }
 wgpu-types = { version = "24.0.0", path = "./wgpu-types" }
 
+wgpu-core-deps-windows-linux-android = { version = "24.0.0", path = "./wgpu-core/platform-deps/windows-linux-android" }
+wgpu-core-deps-apple = { version = "24.0.0", path = "./wgpu-core/platform-deps/apple" }
+wgpu-core-deps-wasm = { version = "24.0.0", path = "./wgpu-core/platform-deps/wasm" }
+wgpu-core-deps-emscripten = { version = "24.0.0", path = "./wgpu-core/platform-deps/emscripten" }
+
 anyhow = { version = "1.0.95", default-features = false }
 approx = "0.5"
 argh = "0.1.13"
diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml
index 29f1fd8df2..863769913d 100644
--- a/wgpu-core/Cargo.toml
+++ b/wgpu-core/Cargo.toml
@@ -35,10 +35,10 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wgpu_validate_locks)'] }
 [lib]
 
 [features]
-## Internally count resources and events for debugging purposes. If the counters
-## feature is disabled, the counting infrastructure is removed from the build and
-## the exposed counters always return 0.
-counters = ["wgpu-types/counters"]
+#! See docuemntation for the `wgpu` crate for more in-depth information on these features.
+
+#! ### Logging Configuration
+# --------------------------------------------------------------------
 
 ## Log all API entry points at info instead of trace level.
 ## Also, promotes certain debug log calls to info.
@@ -47,17 +47,25 @@ api_log_info = []
 ## Log resource lifecycle management at info instead of trace level.
 resource_log_info = []
 
-## Support the Renderdoc graphics debugger:
-## <https://renderdoc.org/>
-renderdoc = ["wgpu-hal/renderdoc"]
+#! ### Runtime Checks
+# --------------------------------------------------------------------
+
+## Validates indirect draw/dispatch calls. This will also enable naga's
+## WGSL frontend since we use a WGSL compute shader to do the validation.
+indirect-validation = ["naga/wgsl-in"]
 
 ## Apply run-time checks, even in release builds. These are in addition
 ## to the validation carried out at public APIs in all builds.
 strict_asserts = ["wgpu-types/strict_asserts"]
 
-## Validates indirect draw/dispatch calls. This will also enable naga's
-## WGSL frontend since we use a WGSL compute shader to do the validation.
-indirect-validation = ["naga/wgsl-in"]
+#! ### Debugging
+# --------------------------------------------------------------------
+
+## Enable lock order observation.
+observe_locks = ["dep:ron", "serde/serde_derive"]
+
+#! ### Serialization
+# --------------------------------------------------------------------
 
 ## Enables serialization via `serde` on common wgpu types.
 serde = ["dep:serde", "wgpu-types/serde", "arrayvec/serde", "hashbrown/serde"]
@@ -65,15 +73,18 @@ serde = ["dep:serde", "wgpu-types/serde", "arrayvec/serde", "hashbrown/serde"]
 ## Enable API tracing.
 trace = ["dep:ron", "serde", "naga/serialize"]
 
-## Enable lock order observation.
-observe_locks = ["dep:ron", "serde/serde_derive"]
-
 ## Enable API replaying
 replay = ["serde", "naga/deserialize"]
 
-## Enable creating instances using raw-window-handle
+#! ### Surface Support
+# --------------------------------------------------------------------
+
+## Enable creating surfaces using raw-window-handle
 raw-window-handle = ["dep:raw-window-handle"]
 
+#! ### Shading Language Support
+# --------------------------------------------------------------------
+
 ## Enable `ShaderModuleSource::Wgsl`
 wgsl = ["naga/wgsl-in"]
 
@@ -83,40 +94,68 @@ glsl = ["naga/glsl-in"]
 ## Enable `ShaderModuleSource::SpirV`
 spirv = ["naga/spv-in", "dep:bytemuck"]
 
+#! ### Other
+# --------------------------------------------------------------------
+
+## Internally count resources and events for debugging purposes. If the counters
+## feature is disabled, the counting infrastructure is removed from the build and
+## the exposed counters always return 0.
+counters = ["wgpu-types/counters"]
+
 ## Implement `Send` and `Sync` on Wasm, but only if atomics are not enabled.
-##
-## WebGL/WebGPU objects can not be shared between threads.
-## However, it can be useful to artificially mark them as `Send` and `Sync`
-## anyways to make it easier to write cross-platform code.
-## This is technically *very* unsafe in a multithreaded environment,
-## but on a wasm binary compiled without atomics we know we are definitely
-## not in a multithreaded environment.
 fragile-send-sync-non-atomic-wasm = [
     "wgpu-hal/fragile-send-sync-non-atomic-wasm",
-    "wgpu-types/fragile-send-sync-non-atomic-wasm",
 ]
 
-#! ### Backends, passed through to wgpu-hal
+#! ### External libraries
 # --------------------------------------------------------------------
+#! The following features facilitate integration with third-party supporting libraries.
 
-## Enable the `metal` backend.
-metal = ["wgpu-hal/metal"]
+## Enable using the `mach-dxcompiler-rs` crate to compile DX12 shaders.
+static-dxc = ["wgpu-hal/static-dxc"]
 
-## Enable the `vulkan` backend.
-vulkan = ["wgpu-hal/vulkan"]
-
-## Enable the `GLES` backend.
-##
-## This is used for all of GLES, OpenGL, and WebGL.
-gles = ["wgpu-hal/gles"]
+#! ### Target Conditional Features
+# --------------------------------------------------------------------
+# Look to wgpu-hal's Cargo.toml for explaination how these features and the wgpu-core
+# platform crates collude to provide platform-specific behavior.
+
+## DX12 backend
+dx12 = ["wgpu-core-deps-windows-linux-android/dx12"]
+## Metal backend
+metal = ["wgpu-core-deps-apple/metal"]
+## Vulkan backend, only available on Windows, Linux, Android
+vulkan = ["wgpu-core-deps-windows-linux-android/vulkan"]
+## OpenGL backend, only available on Windows, Linux, Android, and Emscripten
+gles = [
+    "wgpu-core-deps-windows-linux-android/gles",
+    "wgpu-core-deps-emscripten/gles",
+]
 
-## Enable the `dx12` backend.
-dx12 = ["wgpu-hal/dx12"]
+## WebGL backend, only available on Emscripten
+webgl = ["wgpu-core-deps-wasm/webgl"]
+## OpenGL backend, on macOS only
+angle = ["wgpu-core-deps-apple/angle"]
+## Vulkan portability backend, only available on macOS
+vulkan-portability = ["wgpu-core-deps-apple/vulkan-portability"]
+## Renderdoc integration, only available on Windows, Linux, and Android
+renderdoc = ["wgpu-core-deps-windows-linux-android/renderdoc"]
 
 ## Enable the `noop` backend.
 # TODO(https://github.com/gfx-rs/wgpu/issues/7120): there should be a hal feature
 noop = []
 
+# The target limitation here isn't needed, but prevents more than one of these
+# platform crates from being included in the build at a time, preventing users
+# from getting confused by seeing them in the list of crates.
+[target.'cfg(target_vendor = "apple")'.dependencies]
+wgpu-core-deps-apple = { workspace = true, optional = true }
+[target.'cfg(target_os = "emscripten")'.dependencies]
+wgpu-core-deps-emscripten = { workspace = true, optional = true }
+[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
+wgpu-core-deps-wasm = { workspace = true, optional = true }
+[target.'cfg(any(windows, target_os = "linux", target_os = "android"))'.dependencies]
+wgpu-core-deps-windows-linux-android = { workspace = true, optional = true }
+
 [dependencies]
 naga.workspace = true
 wgpu-hal.workspace = true
diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs
index 4d35481678..55c43fba20 100644
--- a/wgpu-core/build.rs
+++ b/wgpu-core/build.rs
@@ -1,13 +1,22 @@
 fn main() {
     cfg_aliases::cfg_aliases! {
+        windows_linux_android: { any(windows, target_os = "linux", target_os = "android") },
         send_sync: { any(
             not(target_arch = "wasm32"),
             all(feature = "fragile-send-sync-non-atomic-wasm", not(target_feature = "atomics"))
         ) },
-        webgl: { all(target_arch = "wasm32", not(target_os = "emscripten"), gles) },
         dx12: { all(target_os = "windows", feature = "dx12") },
-        gles: { all(feature = "gles") },
+        webgl: { all(target_arch = "wasm32", not(target_os = "emscripten"), feature = "webgl") },
+        gles: { any(
+            all(windows_linux_android, feature = "gles"), // Regular GLES
+            all(webgl), // WebGL
+            all(target_os = "emscripten", feature = "gles"), // Emscripten GLES
+            all(target_vendor = "apple", feature = "angle") // ANGLE on Apple
+        ) },
+        vulkan: { any(
+            all(windows_linux_android, feature = "vulkan"), // Regular Vulkan
+            all(target_vendor = "apple", feature = "vulkan-portability") // Vulkan Portability on Apple
+        ) },
         metal: { all(target_vendor = "apple", feature = "metal") },
-        vulkan: { all(not(target_arch = "wasm32"), feature = "vulkan") }
     }
 }
diff --git a/wgpu-core/platform-deps/apple/Cargo.toml b/wgpu-core/platform-deps/apple/Cargo.toml
new file mode 100644
index 0000000000..9734162fff
--- /dev/null
+++ b/wgpu-core/platform-deps/apple/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "wgpu-core-deps-apple"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+description = "Feature unification helper crate for Apple platforms"
+homepage.workspace = true
+repository.workspace = true
+keywords.workspace = true
+license.workspace = true
+readme = "README.md"
+
+# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to
+# copy the crates it actually uses out of the workspace, so it's meaningful for
+# them to have less restrictive MSRVs individually than the workspace as a
+# whole, if their code permits. See `../README.md` for details.
+rust-version = "1.76"
+
+[features]
+metal = ["wgpu-hal/metal"]
+angle = ["wgpu-hal/gles", "wgpu-hal/renderdoc"]
+vulkan-portability = ["wgpu-hal/vulkan", "wgpu-hal/renderdoc"]
+
+# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.
+[target.'cfg(target_vendor = "apple")'.dependencies]
+wgpu-hal.workspace = true
diff --git a/wgpu-core/platform-deps/apple/README.md b/wgpu-core/platform-deps/apple/README.md
new file mode 100644
index 0000000000..5123eeb293
--- /dev/null
+++ b/wgpu-core/platform-deps/apple/README.md
@@ -0,0 +1,3 @@
+This crate exists to allow platform and feature specific features work correctly. The features
+enabled on this crate are only enabled on `target_vendor = "apple"` platforms. See wgpu-hal's `Cargo.toml`
+for more information.
\ No newline at end of file
diff --git a/wgpu-core/platform-deps/apple/src/lib.rs b/wgpu-core/platform-deps/apple/src/lib.rs
new file mode 100644
index 0000000000..59fddaa920
--- /dev/null
+++ b/wgpu-core/platform-deps/apple/src/lib.rs
@@ -0,0 +1,3 @@
+//! This crate exists to allow platform and feature specific features work correctly. The features
+//! enabled on this crate are only enabled on `target_vendor = "apple"` platforms. See wgpu-hal's `Cargo.toml`
+//! for more information.
diff --git a/wgpu-core/platform-deps/emscripten/Cargo.toml b/wgpu-core/platform-deps/emscripten/Cargo.toml
new file mode 100644
index 0000000000..d38ceb9bc7
--- /dev/null
+++ b/wgpu-core/platform-deps/emscripten/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "wgpu-core-deps-emscripten"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+description = "Feature unification helper crate for the Emscripten platform"
+homepage.workspace = true
+repository.workspace = true
+keywords.workspace = true
+license.workspace = true
+readme = "README.md"
+
+# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to
+# copy the crates it actually uses out of the workspace, so it's meaningful for
+# them to have less restrictive MSRVs individually than the workspace as a
+# whole, if their code permits. See `../README.md` for details.
+rust-version = "1.76"
+
+[features]
+gles = ["wgpu-hal/gles"]
+
+# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.
+[target.'cfg(target_os = "emscripten")'.dependencies]
+wgpu-hal.workspace = true
diff --git a/wgpu-core/platform-deps/emscripten/README.md b/wgpu-core/platform-deps/emscripten/README.md
new file mode 100644
index 0000000000..46a8dce223
--- /dev/null
+++ b/wgpu-core/platform-deps/emscripten/README.md
@@ -0,0 +1,3 @@
+This crate exists to allow platform and feature specific features work correctly. The features
+enabled on this crate are only enabled on `target_os = "emscripten"` platforms.
+See wgpu-hal's `Cargo.toml` for more information.
diff --git a/wgpu-core/platform-deps/emscripten/src/lib.rs b/wgpu-core/platform-deps/emscripten/src/lib.rs
new file mode 100644
index 0000000000..d969cf885f
--- /dev/null
+++ b/wgpu-core/platform-deps/emscripten/src/lib.rs
@@ -0,0 +1,3 @@
+//! This crate exists to allow platform and feature specific features work correctly. The features
+//! enabled on this crate are only enabled on `target_os = "emscripten"` platforms.
+//! See wgpu-hal's `Cargo.toml` for more information.
diff --git a/wgpu-core/platform-deps/wasm/Cargo.toml b/wgpu-core/platform-deps/wasm/Cargo.toml
new file mode 100644
index 0000000000..a318f38f58
--- /dev/null
+++ b/wgpu-core/platform-deps/wasm/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "wgpu-core-deps-wasm"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+description = "Feature unification helper crate for the WebAssembly platform"
+homepage.workspace = true
+repository.workspace = true
+keywords.workspace = true
+license.workspace = true
+readme = "README.md"
+
+# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to
+# copy the crates it actually uses out of the workspace, so it's meaningful for
+# them to have less restrictive MSRVs individually than the workspace as a
+# whole, if their code permits. See `../README.md` for details.
+rust-version = "1.76"
+
+[features]
+webgl = ["wgpu-hal/gles"]
+
+# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.
+[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
+wgpu-hal.workspace = true
diff --git a/wgpu-core/platform-deps/wasm/README.md b/wgpu-core/platform-deps/wasm/README.md
new file mode 100644
index 0000000000..39e1ff1b54
--- /dev/null
+++ b/wgpu-core/platform-deps/wasm/README.md
@@ -0,0 +1,3 @@
+This crate exists to allow platform and feature specific features work correctly. The features
+enabled on this crate are only enabled on `target_arch = "wasm32"` platforms. See wgpu-hal's `Cargo.toml`
+for more information.
diff --git a/wgpu-core/platform-deps/wasm/src/lib.rs b/wgpu-core/platform-deps/wasm/src/lib.rs
new file mode 100644
index 0000000000..6c03e6e5fc
--- /dev/null
+++ b/wgpu-core/platform-deps/wasm/src/lib.rs
@@ -0,0 +1,3 @@
+//! This crate exists to allow platform and feature specific features work correctly. The features
+//! enabled on this crate are only enabled on `target_arch = "wasm32"` platforms. See wgpu-hal's `Cargo.toml`
+//! for more information.
diff --git a/wgpu-core/platform-deps/windows-linux-android/Cargo.toml b/wgpu-core/platform-deps/windows-linux-android/Cargo.toml
new file mode 100644
index 0000000000..7de228cdfb
--- /dev/null
+++ b/wgpu-core/platform-deps/windows-linux-android/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "wgpu-core-deps-windows-linux-android"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+description = "Feature unification helper crate for the Windows/Linux/Android platforms"
+homepage.workspace = true
+repository.workspace = true
+keywords.workspace = true
+license.workspace = true
+readme = "README.md"
+
+# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to
+# copy the crates it actually uses out of the workspace, so it's meaningful for
+# them to have less restrictive MSRVs individually than the workspace as a
+# whole, if their code permits. See `../README.md` for details.
+rust-version = "1.76"
+
+[features]
+gles = ["wgpu-hal/gles"]
+vulkan = ["wgpu-hal/vulkan"]
+dx12 = ["wgpu-hal/dx12"]
+renderdoc = ["wgpu-hal/renderdoc"]
+
+# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.
+[target.'cfg(any(windows, target_os = "linux", target_os = "android"))'.dependencies]
+wgpu-hal.workspace = true
diff --git a/wgpu-core/platform-deps/windows-linux-android/README.md b/wgpu-core/platform-deps/windows-linux-android/README.md
new file mode 100644
index 0000000000..fdc9f9d3a2
--- /dev/null
+++ b/wgpu-core/platform-deps/windows-linux-android/README.md
@@ -0,0 +1,3 @@
+This crate exists to allow platform and feature specific features work correctly. The features
+enabled on this crate are only enabled on `windows`, `target_os = "linux"`, and `target_os = "android"`
+platforms. See wgpu-hal's `Cargo.toml` for more information.
diff --git a/wgpu-core/platform-deps/windows-linux-android/src/lib.rs b/wgpu-core/platform-deps/windows-linux-android/src/lib.rs
new file mode 100644
index 0000000000..2b06dda017
--- /dev/null
+++ b/wgpu-core/platform-deps/windows-linux-android/src/lib.rs
@@ -0,0 +1,3 @@
+//! This crate exists to allow platform and feature specific features work correctly. The features
+//! enabled on this crate are only enabled on `windows`, `target_os = "linux"`, and `target_os = "android"`
+//! platforms. See wgpu-hal's `Cargo.toml` for more information.
diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml
index e3056e2c77..9255d507a7 100644
--- a/wgpu-hal/Cargo.toml
+++ b/wgpu-hal/Cargo.toml
@@ -40,51 +40,98 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(web_sys_unstable_apis)'] }
 [lib]
 
 [features]
+
+########################
+### Backend Features ###
+########################
+
+# The interaction of features between wgpu-core and wgpu-hal is a bit nuanced to get
+# the desired behavior on all platforms.
+#
+# At the wgpu-hal level the features are defined to enable the backends on all platforms
+# that can compile the backend. Vulkan for example will have an effect on Windows, Mac, Linux, and Android.
+# This is done with target conditional dependencies in wgpu-hal. This allows `--all-features`
+# to compile on all platforms.
+#
+# wgpu-core's features are defined to enable the backends on their "default" platforms. For example we
+# exclude the Vulkan backend on MacOS unless a separate feature `vulkan-portability` is enabled. In response
+# to these features, it enables features of platform specific crates. For example, the `vulkan` feature in wgpu-core
+# enables the `vulkan` feature in `wgpu-core-deps-windows-linux-android` which in turn enables the
+# `vulkan` feature in `wgpu-hal` _only_ on those platforms. If you enable the `vulkan-portability` feature, it 
+# will enable the `vulkan` feature in `wgpu-core-deps-apple`. The only way to do this is unfortunately to have
+# a separate crate for each platform category that participates in the feature unification.
+#
+# This trick doesn't work at the `wgpu` level, because the `wgpu` -> `wgpu-core` dependency is conditional,
+# making the Cargo.toml signifigantly more complicated in all areas.
+#
+# See https://github.com/gfx-rs/wgpu/issues/3514, https://github.com/gfx-rs/wgpu/pull/7076,
+# and https://github.com/rust-lang/cargo/issues/1197 for more information.
+
 ## 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",
+    "naga/msl-out",
+    "dep:arrayvec",
     "dep:block",
+    "dep:core-graphics-types",
+    "dep:hashbrown",
+    "dep:libc",
+    "dep:log",
+    "dep:metal",
+    "dep:objc",
+    "dep:profiling",
 ]
 vulkan = [
     "naga/spv-out",
+    "dep:android_system_properties",
+    "dep:arrayvec",
     "dep:ash",
     "dep:gpu-alloc",
     "dep:gpu-descriptor",
+    "dep:hashbrown",
+    "dep:libc",
     "dep:libloading",
-    "dep:smallvec",
-    "dep:android_system_properties",
+    "dep:log",
     "dep:ordered-float",
+    "dep:profiling",
+    "dep:smallvec",
+    "dep:windows",
+    "windows/Win32",
 ]
 gles = [
     "naga/glsl-out",
-    "once_cell/std",
+    "dep:arrayvec",
     "dep:bytemuck",
     "dep:glow",
     "dep:glutin_wgl_sys",
+    "dep:hashbrown",
+    "dep:js-sys",
     "dep:khronos-egl",
     "dep:libloading",
+    "dep:log",
     "dep:ndk-sys",
-    "dep:once_cell",
+    "dep:profiling",
+    "dep:wasm-bindgen",
+    "dep:web-sys",
+    "once_cell/std",
     "windows/Win32_Graphics_OpenGL",
     "windows/Win32_Graphics_Gdi",
     "windows/Win32_System_LibraryLoader",
     "windows/Win32_UI_WindowsAndMessaging",
 ]
 ## Enables the DX12 backend when targeting Windows.
-##
-## Has no effect if not targeting Windows.
 dx12 = [
-    # DX12 is only available on Windows, therefore request HLSL output also only if we target Windows.
+    "naga/hlsl-out",
+    "dep:arrayvec",
     "dep:bit-set",
+    "dep:hashbrown",
     "dep:libloading",
+    "dep:log",
+    "dep:ordered-float",
+    "dep:profiling",
     "dep:range-alloc",
     "dep:windows-core",
-    "dep:ordered-float",
     "gpu-allocator/d3d12",
-    "naga/hlsl-out-if-target-windows",
     "windows/Win32_Graphics_Direct3D_Fxc",
     "windows/Win32_Graphics_Direct3D_Dxc",
     "windows/Win32_Graphics_Direct3D",
@@ -98,12 +145,21 @@ dx12 = [
     "windows/Win32_System_Threading",
     "windows/Win32_UI_WindowsAndMessaging",
 ]
-## Enables statically linking DXC.
+
+###########################
+### Misc Other Features ###
+###########################
+
 static-dxc = ["dep:mach-dxcompiler-rs"]
-renderdoc = ["dep:libloading", "dep:renderdoc-sys"]
+renderdoc = ["dep:libloading", "dep:renderdoc-sys", "dep:log"]
 fragile-send-sync-non-atomic-wasm = [
     "wgpu-types/fragile-send-sync-non-atomic-wasm",
 ]
+
+###################################
+### Internal Debugging Features ###
+###################################
+
 # Panic when running into an out-of-memory error (for debugging purposes).
 #
 # Only affects the d3d12 and vulkan backends.
@@ -132,18 +188,21 @@ required-features = ["gles"]
 naga.workspace = true
 wgpu-types.workspace = true
 
-arrayvec.workspace = true
+# Dependencies in the lib and empty backend
 bitflags.workspace = true
-hashbrown.workspace = true
-log.workspace = true
-once_cell = { workspace = true, optional = true }
-ordered-float = { workspace = true, optional = true }
-parking_lot.workspace = true
-profiling = { workspace = true, default-features = false }
 raw-window-handle.workspace = true
-rustc-hash.workspace = true
+parking_lot.workspace = true
 thiserror.workspace = true
 
+# Target agnostic dependencies used only in backends.
+arrayvec = { workspace = true, optional = true }
+hashbrown = { workspace = true, optional = true }
+log = { workspace = true, optional = true }
+once_cell = { workspace = true, optional = true }
+ordered-float = { workspace = true, optional = true }
+profiling = { workspace = true, optional = true, default-features = false }
+rustc-hash = { workspace = true, optional = true }
+
 # Backend: GLES
 bytemuck = { workspace = true, optional = true }
 glow = { workspace = true, optional = true }
@@ -169,7 +228,7 @@ renderdoc-sys = { workspace = true, optional = true }
 
 [target.'cfg(unix)'.dependencies]
 # Backend: Vulkan
-libc.workspace = true
+libc = { workspace = true, optional = true }
 
 #########################
 ### Platform: Windows ###
@@ -200,9 +259,9 @@ mach-dxcompiler-rs = { workspace = true, optional = true }
 [target.'cfg(target_vendor = "apple")'.dependencies]
 # Backend: Metal
 block = { workspace = true, optional = true }
-core-graphics-types.workspace = true
-metal.workspace = true
-objc.workspace = true
+core-graphics-types = { workspace = true, optional = true }
+metal = { workspace = true, optional = true }
+objc = { workspace = true, optional = true }
 
 #########################
 ### Platform: Android ###
@@ -218,15 +277,15 @@ ndk-sys = { workspace = true, optional = true }
 
 [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
 # Backend: GLES
-wasm-bindgen.workspace = true
-web-sys = { workspace = true, features = [
+wasm-bindgen = { workspace = true, optional = true }
+web-sys = { workspace = true, optional = true, features = [
     "default",
     "Window",
     "HtmlCanvasElement",
     "WebGl2RenderingContext",
     "OffscreenCanvas",
 ] }
-js-sys = { workspace = true, features = ["default"] }
+js-sys = { workspace = true, optional = true, features = ["default"] }
 
 ############################
 ### Platform: Emscripten ###
@@ -234,7 +293,10 @@ js-sys = { workspace = true, features = ["default"] }
 
 [target.'cfg(target_os = "emscripten")'.dependencies]
 # Backend: GLES
-khronos-egl = { workspace = true, features = ["static", "no-pkg-config"] }
+khronos-egl = { workspace = true, optional = true, features = [
+    "static",
+    "no-pkg-config",
+] }
 # Note: it's unused by emscripten, but we keep it to have single code base in egl.rs
 libloading = { workspace = true, optional = true }
 
diff --git a/wgpu-hal/src/auxil/renderdoc.rs b/wgpu-hal/src/auxil/renderdoc.rs
index 3879bb9545..87b2cdca10 100644
--- a/wgpu-hal/src/auxil/renderdoc.rs
+++ b/wgpu-hal/src/auxil/renderdoc.rs
@@ -1,4 +1,5 @@
 //! RenderDoc integration - <https://renderdoc.org/>
+#![cfg_attr(not(any(feature = "gles", feature = "vulkan")), allow(dead_code))]
 
 use std::{ffi, os, ptr};
 
diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml
index 1c63eb7b52..7b95565da6 100644
--- a/wgpu-types/Cargo.toml
+++ b/wgpu-types/Cargo.toml
@@ -40,7 +40,7 @@ default = ["std"]
 std = ["js-sys/std", "web-sys/std", "thiserror/std"]
 strict_asserts = []
 fragile-send-sync-non-atomic-wasm = []
-serde = ["dep:serde"]
+serde = ["dep:serde", "bitflags/serde"]
 # Enables some internal instrumentation for debugging purposes.
 counters = []
 
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index 5574fd5eec..aa6f25d05c 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -29,13 +29,10 @@ ignored = ["cfg_aliases"]
 [lib]
 
 [features]
-default = ["wgsl", "dx12", "metal", "webgpu"]
+default = ["dx12", "metal", "gles", "vulkan", "wgsl", "webgpu"]
 
 #! ### Backends
 # --------------------------------------------------------------------
-#! ⚠️ WIP: Not all backends can be manually configured today.
-#! On Windows, Linux & Android the Vulkan & GLES backends are always enabled.
-#! See [#3514](https://github.com/gfx-rs/wgpu/issues/3514) for more details.
 
 ## Enables the DX12 backend on Windows.
 dx12 = ["wgpu-core?/dx12"]
@@ -43,19 +40,35 @@ dx12 = ["wgpu-core?/dx12"]
 ## Enables the Metal backend on macOS & iOS.
 metal = ["wgpu-core?/metal"]
 
-## Enables the WebGPU backend on Wasm. Disabled when targeting `emscripten`.
-webgpu = ["naga?/wgsl-out"]
+## Enables the Vulkan backend on Windows, Linux, and Android.
+vulkan = ["wgpu-core?/vulkan"]
+
+## Enables the OpenGL/GLES backend on Windows, Linux, Android, and Emscripten.
+gles = ["wgpu-core?/gles"]
+
+## Enables the WebGPU backend on WebAssembly.
+webgpu = [
+    "naga?/wgsl-out",
+    "dep:wasm-bindgen-futures",
+    "web-sys/Document",
+    "web-sys/Event",
+    "web-sys/Navigator",
+    "web-sys/NodeList",
+    "web-sys/Window",
+    "web-sys/WorkerGlobalScope",
+    "web-sys/WorkerNavigator",
+]
 
-## Enables the GLES backend via [ANGLE](https://github.com/google/angle) on macOS using.
-angle = ["wgpu-core?/gles"]
+#! ### Conditional Backends
 
-## Enables the Vulkan backend on macOS & iOS.
-vulkan-portability = ["wgpu-core?/vulkan"]
+## Enables the GLES backend on macOS only for use with [ANGLE](https://github.com/google/angle).
+angle = ["wgpu-core?/angle"]
 
-## Enables the GLES backend on Wasm
-##
-## * ⚠️ WIP: Currently will also enable GLES dependencies on any other targets.
-webgl = ["dep:wgpu-hal", "wgpu-core/gles"]
+## Enables the Vulkan backend on macOS & iOS only for use with [MoltenVK](https://github.com/KhronosGroup/MoltenVK).
+vulkan-portability = ["wgpu-core?/vulkan-portability"]
+
+## Enables the GLES backend on WebAssembly only.
+webgl = ["wgpu-core/webgl", "dep:wgpu-hal", "dep:smallvec"]
 
 ## Enables the noop backend for testing.
 ##
@@ -71,6 +84,8 @@ noop = ["wgpu-core/noop"]
 
 #! ### Shading language support
 # --------------------------------------------------------------------
+#! These features enable support for that input language on all platforms.
+#! We will translate the input language to whatever the backend requires.
 
 ## Enable accepting SPIR-V shaders as input.
 spirv = ["naga/spv-in", "wgpu-core?/spirv"]
@@ -84,24 +99,32 @@ wgsl = ["wgpu-core?/wgsl"]
 ## Enable accepting naga IR shaders as input.
 naga-ir = ["dep:naga"]
 
-#! ### Logging & Tracing
+#! ### Assertions and Serialization
 # --------------------------------------------------------------------
-#! The following features do not have any effect on the WebGPU backend.
-
 ## Apply run-time checks, even in release builds. These are in addition
 ## to the validation carried out at public APIs in all builds.
 strict_asserts = ["wgpu-core?/strict_asserts", "wgpu-types/strict_asserts"]
 
 ## Enables serialization via `serde` on common wgpu types.
-serde = ["dep:serde", "wgpu-core?/serde"]
+serde = ["wgpu-core?/serde", "wgpu-types/serde"]
 
 # Uncomment once we get to https://github.com/gfx-rs/wgpu/issues/5974
 # ## Allow writing of trace capture files. See [`Adapter::request_device`].
 # trace = ["serde", "wgpu-core/trace"]
 
-## Allow deserializing of trace capture files that were written with the `trace` feature.
-## To replay a trace file use the [wgpu player](https://github.com/gfx-rs/wgpu/tree/trunk/player).
-replay = ["serde", "wgpu-core?/replay"]
+#! ### External libraries
+# --------------------------------------------------------------------
+#! The following features facilitate integration with third-party supporting libraries.
+
+## Enables statically linking DXC.
+##
+## Normally, to use the modern DXC shader compiler with WGPU, the final application
+## must be shipped alongside `dxcompiler.dll` and `dxil.dll` (which can be downloaded from [Microsoft's GitHub][dxc]).
+## This feature statically links a version of DXC so that no external binaries are required
+## to compile DX12 shaders.
+##
+## [dxc]: https://github.com/Microsoft/DirectXShaderCompiler
+static-dxc = ["wgpu-core?/static-dxc"]
 
 #! ### Other
 # --------------------------------------------------------------------
@@ -117,26 +140,12 @@ counters = ["wgpu-core?/counters"]
 ## However, it can be useful to artificially mark them as `Send` and `Sync`
 ## anyways to make it easier to write cross-platform code.
 ## This is technically *very* unsafe in a multithreaded environment,
-## but on a wasm binary compiled without atomics we know we are definitely
-## not in a multithreaded environment.
+## but on a wasm binary compiled without atomics is a definitionally single-threaded environment.
 fragile-send-sync-non-atomic-wasm = [
-    "wgpu-hal?/fragile-send-sync-non-atomic-wasm",
     "wgpu-core?/fragile-send-sync-non-atomic-wasm",
     "wgpu-types/fragile-send-sync-non-atomic-wasm",
 ]
 
-
-#! ### External libraries
-# --------------------------------------------------------------------
-#! The following features facilitate integration with third-party supporting libraries.
-
-## Enables statically linking DXC.
-## Normally, to use the modern DXC shader compiler with WGPU, the final application
-## must be shipped alongside `dxcompiler.dll` and `dxil.dll` (which can be downloaded from Microsoft's GitHub).
-## This feature statically links a version of DXC so that no external binaries are required
-## to compile DX12 shaders.
-static-dxc = ["wgpu-hal?/static-dxc"]
-
 #########################
 # Standard Dependencies #
 #########################
@@ -144,8 +153,9 @@ static-dxc = ["wgpu-hal?/static-dxc"]
 [dependencies]
 naga = { workspace = true, optional = true }
 wgpu-core = { workspace = true, optional = true }
-wgpu-types = { workspace = true, features = ["serde"] }
+wgpu-types.workspace = true
 
+# Needed for both wgpu-core and webgpu
 arrayvec.workspace = true
 bitflags.workspace = true
 document-features.workspace = true
@@ -154,70 +164,57 @@ log.workspace = true
 parking_lot.workspace = true
 profiling.workspace = true
 raw-window-handle = { workspace = true, features = ["std"] }
-serde = { workspace = true, features = ["default", "derive"], optional = true }
-smallvec.workspace = true
 static_assertions.workspace = true
 
 ########################################
 # Target Specific Feature Dependencies #
 ########################################
 
-# Windows
-[target.'cfg(windows)'.dependencies]
+###################
+# Not Webassembly #
+###################
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+# Needed for only wgpu-core backend. Not optional as only wgpu-core backend exists on native platforms.
 wgpu-core = { workspace = true, features = [
     "raw-window-handle",
-    "vulkan",
-    "gles",
+    "renderdoc",
+    "indirect-validation",
 ] }
-wgpu-hal = { workspace = true, features = ["renderdoc"] }
-
-# Apple Platforms
-[target.'cfg(target_vendor = "apple")'.dependencies]
-wgpu-core = { workspace = true, features = ["raw-window-handle"] }
-wgpu-hal = { workspace = true, features = [] }
+wgpu-hal.workspace = true
 
-# Linux + Android
-[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
-wgpu-core = { workspace = true, features = [
-    "raw-window-handle",
-    "vulkan",
-    "gles",
-] }
-wgpu-hal = { workspace = true, features = ["renderdoc"] }
+smallvec.workspace = true
 
-# Webassembly
+###############
+# Webassembly #
+###############
 [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
-wgpu-core = { workspace = true, optional = true, features = [
-    "raw-window-handle",
-] }
-wgpu-hal = { workspace = true, optional = true }
-
+# Needed for all backends
 js-sys = { workspace = true, features = ["default"] }
-parking_lot.workspace = true
-wasm-bindgen-futures.workspace = true
 wasm-bindgen.workspace = true
 web-sys = { workspace = true, features = [
-    "default",
-    "Document",
-    "Navigator",
-    "Node",
-    "NodeList",
     "HtmlCanvasElement",
     "OffscreenCanvas",
-    "ImageBitmap",
-    "ImageBitmapRenderingContext",
-    "Window",
-    "WorkerGlobalScope",
-    "WorkerNavigator",
-    # Needed by webgpu_sys
-    "Event",
-    "EventTarget",
 ] }
 
-# Emscripten
+# Needed for only wgpu-core backend. Optional as webgl is optional on WebAssembly.
+wgpu-core = { workspace = true, optional = true, features = [
+    "raw-window-handle",
+] }
+wgpu-hal = { workspace = true, optional = true }
+
+smallvec = { workspace = true, optional = true }
+
+# Needed for the webgpu backend. Optional as webgpu is optional on WebAssembly.
+wasm-bindgen-futures = { workspace = true, optional = true }
+
+##############
+# Emscripten #
+##############
 [target.'cfg(target_os = "emscripten")'.dependencies]
-wgpu-core = { workspace = true, features = ["raw-window-handle", "gles"] }
-wgpu-hal = { workspace = true }
+wgpu-core = { workspace = true, features = ["raw-window-handle"] }
+wgpu-hal.workspace = true
+
+smallvec.workspace = true
 
 [build-dependencies]
 cfg_aliases.workspace = true
diff --git a/xtask/src/check_feature_dependencies.rs b/xtask/src/check_feature_dependencies.rs
index 6601bc63b9..3a1e4c3f0c 100644
--- a/xtask/src/check_feature_dependencies.rs
+++ b/xtask/src/check_feature_dependencies.rs
@@ -3,7 +3,6 @@ use xshell::Shell;
 
 #[derive(Debug)]
 enum Search<'a> {
-    #[expect(dead_code)]
     Positive(&'a str),
     Negative(&'a str),
 }
@@ -30,7 +29,6 @@ const ALL_WGPU_FEATURES: &[&str] = &[
     "wgsl",
     "naga-ir",
     "serde",
-    "replay",
     "counters",
     "fragile-send-sync-non-atomic-wasm",
     "static-dxc",
@@ -63,6 +61,100 @@ pub fn check_feature_dependencies(shell: Shell, arguments: Arguments) -> anyhow:
             default_features: false,
             search_terms: &[Search::Negative("naga")],
         },
+        Requirement {
+            human_readable_name: "wasm32 with `webgl` feature depends on `glow`",
+            target: "wasm32-unknown-unknown",
+            packages: &["wgpu"],
+            features: &["webgl"],
+            default_features: false,
+            search_terms: &[Search::Positive("glow")],
+        },
+        Requirement {
+            human_readable_name: "windows with `webgl` does not depend on `glow`",
+            target: "x86_64-pc-windows-msvc",
+            packages: &["wgpu"],
+            features: &["webgl"],
+            default_features: false,
+            search_terms: &[Search::Negative("glow")],
+        },
+        Requirement {
+            human_readable_name: "apple with `vulkan` feature does not depend on `ash`",
+            target: "aarch64-apple-darwin",
+            packages: &["wgpu"],
+            features: &["vulkan"],
+            default_features: false,
+            search_terms: &[Search::Negative("ash")],
+        },
+        Requirement {
+            human_readable_name:
+                "apple with `vulkan-portability` feature depends on `ash` and `renderdoc-sys`",
+            target: "aarch64-apple-darwin",
+            packages: &["wgpu"],
+            features: &["vulkan-portability"],
+            default_features: false,
+            search_terms: &[Search::Positive("ash"), Search::Positive("renderdoc-sys")],
+        },
+        Requirement {
+            human_readable_name: "apple with 'gles' feature does not depend on 'glow'",
+            target: "aarch64-apple-darwin",
+            packages: &["wgpu"],
+            features: &["gles"],
+            default_features: false,
+            search_terms: &[Search::Negative("glow")],
+        },
+        Requirement {
+            human_readable_name: "apple with 'angle' feature depends on 'glow' and `renderdoc-sys`",
+            target: "aarch64-apple-darwin",
+            packages: &["wgpu"],
+            features: &["angle"],
+            default_features: false,
+            search_terms: &[Search::Positive("glow"), Search::Positive("renderdoc-sys")],
+        },
+        Requirement {
+            human_readable_name: "apple with no features does not depend on 'renderdoc-sys'",
+            target: "aarch64-apple-darwin",
+            packages: &["wgpu"],
+            features: &[],
+            default_features: false,
+            search_terms: &[Search::Negative("renderdoc-sys")],
+        },
+        Requirement {
+            human_readable_name:
+                "windows with no features does not depend on 'glow', `windows`, or `ash`",
+            target: "x86_64-pc-windows-msvc",
+            packages: &["wgpu"],
+            features: &[],
+            default_features: false,
+            search_terms: &[
+                Search::Negative("glow"),
+                Search::Negative("windows"),
+                Search::Negative("ash"),
+            ],
+        },
+        Requirement {
+            human_readable_name: "windows with no features depends on renderdoc-sys",
+            target: "x86_64-pc-windows-msvc",
+            packages: &["wgpu"],
+            features: &[],
+            default_features: false,
+            search_terms: &[Search::Positive("renderdoc-sys")],
+        },
+        Requirement {
+            human_readable_name: "emscripten with webgl feature does not depend on glow",
+            target: "wasm32-unknown-emscripten",
+            packages: &["wgpu"],
+            features: &["webgl"],
+            default_features: false,
+            search_terms: &[Search::Negative("glow")],
+        },
+        Requirement {
+            human_readable_name: "emscripten with gles feature depends on glow",
+            target: "wasm32-unknown-emscripten",
+            packages: &["wgpu"],
+            features: &["gles"],
+            default_features: false,
+            search_terms: &[Search::Positive("glow")],
+        },
     ];
 
     let mut any_failures = false;
@@ -91,16 +183,25 @@ pub fn check_feature_dependencies(shell: Shell, arguments: Arguments) -> anyhow:
 
         log::debug!("{output}");
 
-        for search_term in requirement.search_terms {
+        for (i, search_term) in requirement.search_terms.into_iter().enumerate() {
+            // Add a space and after to make sure we're getting a full match
             let found = match search_term {
-                Search::Positive(search_term) => output.contains(search_term),
-                Search::Negative(search_term) => !output.contains(search_term),
+                Search::Positive(search_term) => output.contains(&format!(" {search_term} ")),
+                Search::Negative(search_term) => !output.contains(&format!(" {search_term} ")),
             };
 
             if found {
-                log::info!("✅ Passed!");
+                log::info!(
+                    "✅ Passed! ({} of {})",
+                    i + 1,
+                    requirement.search_terms.len()
+                );
             } else {
-                log::info!("❌ Failed");
+                log::info!(
+                    "❌ Failed! ({} of {})",
+                    i + 1,
+                    requirement.search_terms.len()
+                );
                 any_failures = true;
             }
         }


Test patch: