gfx-rs / wgpu

A cross-platform, safe, pure-Rust graphics API.
https://wgpu.rs
Apache License 2.0
12.12k stars 880 forks source link

Is there any chance that I can choose to enable what backend will be compiled into the binary? #1221

Closed xhebox closed 1 year ago

xhebox commented 3 years ago

Is your feature request related to a problem? Please describe. I've tried gfx-hal, and that is good library. But wgpu is much easier to use than gfx. I prefer wgpu and I know clearly I only want to use vulkan. Despite what backend flags I passed to init the instance, gl backend is also compiled.

Describe the solution you'd like Some features passed from wgpu-rs to wgpu, so that I can choose what I want to use.

Describe alternatives you've considered None

Additional context None

kvark commented 3 years ago

Are you concerned about the code size for the GL backend that becomes a part of the binary? It would be interesting to make a test, say with your application, by compile-time disabling GL and seeing how it affected the size. This would be done by checking out wgpu and adding a patch section to your Cargo toml.

xhebox commented 3 years ago

Yes, it is around 0.7MB using cargo-bloat. Another big library is spirv_cross. This is what I got under windows, after removing gl and dx11:

cargo bloat --release --crates

 File  .text     Size Crate
23.3%  34.3%   1.7MiB spirv_cross
22.2%  32.6%   1.6MiB std
11.8%  17.3% 877.4KiB [Unknown]
10.7%  15.7% 795.2KiB wgpu_core
 0.1%   0.1%   4.4KiB wepoll_sys
68.0% 100.0%   4.9MiB .text section size, the file size is 7.3MiB

I'll get the output under linux later.

kvark commented 3 years ago

SPIRV-Cross has just become an optional dependency in #1220, so maybe this is fine now? Note that there is going to be a bit of a delay before wgpu-rs can consider it to be optional.

xhebox commented 3 years ago

OK, I remembered it wrong... Basically the largest one is spirv_cross. Of course, this result is with lto = true. This is the result under linux:

15.3%  21.2% 758.8KiB wgpu
13.7%  19.0% 678.7KiB spirv_cross
 9.0%  12.5% 446.7KiB std
 8.1%  11.2% 401.0KiB wgpu_core
 7.0%   9.7% 346.9KiB naga
 3.2%   4.4% 156.9KiB [Unknown]
 2.8%   3.9% 138.8KiB wgpu_lut
 2.5%   3.4% 122.4KiB gfx_backend_gl
 1.8%   2.6%  91.3KiB inplace_it
 1.7%   2.3%  83.4KiB hashbrown
 1.1%   1.5%  52.8KiB gfx_backend_vulkan
 0.8%   1.2%  41.7KiB gpu_descriptor
 0.7%   1.0%  35.2KiB ash
 0.5%   0.7%  24.7KiB spirv_headers
 0.4%   0.6%  21.1KiB async_io
 0.4%   0.6%  20.6KiB gfx_hal
 0.4%   0.5%  18.0KiB async_global_executor
 0.3%   0.4%  14.8KiB glow
 0.3%   0.4%  14.1KiB parking_lot
 0.2%   0.3%  11.0KiB miniz_oxide
 1.4%   1.9%  68.9KiB And 26 more crates. Use -n N to show more.

This what I got without lto:

14.7%  23.8% 972.8KiB wgpu
10.3%  16.7% 680.9KiB spirv_cross
 6.1%   9.9% 403.6KiB naga
 5.4%   8.7% 357.4KiB std
 5.4%   8.7% 355.3KiB wgpu_core
 4.2%   6.7% 275.7KiB inplace_it
 2.8%   4.6% 186.7KiB wgpu_lut
 2.4%   3.9% 157.5KiB gfx_backend_gl
 2.1%   3.5% 141.0KiB [Unknown]
 1.3%   2.1%  85.7KiB hashbrown
 1.0%   1.6%  66.3KiB gfx_backend_vulkan
 0.8%   1.3%  54.1KiB ash
 0.5%   0.8%  32.1KiB async_global_executor
 0.5%   0.8%  31.3KiB glow
 0.4%   0.7%  29.8KiB async_io
 0.3%   0.6%  23.2KiB gpu_descriptor
 0.3%   0.5%  20.9KiB gpu_alloc
 0.3%   0.5%  20.9KiB gfx_hal
 0.2%   0.3%  13.5KiB spirv_headers
 0.2%   0.3%  13.3KiB parking_lot
 1.5%   2.4%  96.6KiB And 36 more crates. Use -n N to show more.
61.5% 100.0%   4.0MiB .text section size, the file size is 6.5MiB

Well, I guess that I found that spirv_cross is used by other backends. So I want to disable other backends. I won't close the issue. It is indeed possible that other backends will involve some unused dependencies.

kvark commented 3 years ago

This is very informative, thank you! Would you mind doing a local patch to wgpu-rs that doesn't enable "cross" feature in wpu-core? I'm curious to see the numbers when this feature isn't enabled.

xhebox commented 3 years ago

Here it is :)

[profile.release]
panic = "abort"
strip = "debuginfo"
lto = true

20.0%  26.7% 804.9KiB wgpu
12.1%  16.2% 487.0KiB std
10.3%  13.8% 416.0KiB naga
10.0%  13.4% 403.4KiB wgpu_core
 3.9%   5.3% 158.9KiB gfx_backend_gl
 3.4%   4.5% 136.5KiB wgpu_lut
 3.3%   4.4% 132.6KiB inplace_it
 2.4%   3.2%  97.4KiB hashbrown
 1.3%   1.8%  53.3KiB gfx_backend_vulkan
 1.0%   1.4%  41.3KiB gpu_descriptor
 0.9%   1.2%  35.2KiB ash
 0.7%   0.9%  28.0KiB spirv_headers
 0.5%   0.7%  21.1KiB async_io
 0.5%   0.7%  20.6KiB gfx_hal
 0.5%   0.6%  18.2KiB async_global_executor
 0.4%   0.6%  16.8KiB [Unknown]
 0.4%   0.5%  14.8KiB glow
 0.4%   0.5%  14.1KiB parking_lot
 0.3%   0.4%  11.0KiB miniz_oxide
 0.2%   0.3%   8.3KiB smallvec
 1.5%   2.0%  61.4KiB And 26 more crates. Use -n N to show more.
74.7% 100.0%   2.9MiB .text section size, the file size is 3.9MiB

I believe that wgpu can be made smaller somehow. You could also use cargo bloat to track the largest part in wgpu. link here.

Shatur commented 2 years ago

It would be great to control backends. In Bevy, for example, wgpu is a public API. And to be able to run the same game logic on the server, I need to build Bevy with wgpu, but without backends: https://github.com/bevyengine/bevy/issues/3155#issuecomment-987278686

kvark commented 2 years ago

If backends can be enabled individually with features, how can we make sure that people can just use "auto" mode - the logic that we have now? I.e. introducing features would immediately require the users to complicate their cargo manifests with something like this:

[target.'cfg(target_arch = "wasm32")'.dependencies]
wgpu = { version = "0.11", features = ["gles"] }

[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies]
wgpu = { version = "0.11", features = ["metal"] }

[target.'cfg(all(not(target_arch = "wasm32"), unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies]
wgpu = { version = "0.11", features = ["vulkan", "gles", "renderdoc"] }

[target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies]
wgpu = { version = "0.11", features = ["vulkan", "dx12", "renderdoc"] }

I don't think our users are going to be excited for this...

@Shatur it's clear you don't want to run the same rendering logic on client and server. So wgpu use in the logic you do want to run is rather accidental, is it?

Shatur commented 2 years ago

I.e. introducing features would immediately require the users to complicate their cargo manifests with something like this:

Totally agree, I understand why we have prereconfigured backends.

it's clear you don't want to run the same rendering logic on client and server. So wgpu use in the logic you do want to run is rather accidental, is it?

It would be great to avoid having wgpu on server. But for relatively complex games, the server uses exactly the same logic as the client. So in Bevy we could do the following: 1) Guard all wgpu usage with compile-time checks. But this make code very ugly, see https://github.com/bevyengine/bevy/issues/3155#issue-1059219919 2) Add additional API over wgpu that "fakes" wgpu if wgpu is disabled in features. But it is very difficult to maintain and it's basically mirroring of wgpu code. 3) Use wgpu as a public API by simply removing all rendering backends from the server. This is how it done in Godot.

@cart suggested to use the approach number 3, but it currently impossible due to preconfigured backends for specific platforms. So I decided to ask :smile: Is it possible to move this preconfigured features to default-features to let users opt-out them?

kvark commented 2 years ago

Main blocker is that default features can't be platform dependent. I think it's https://github.com/rust-lang/cargo/issues/1197, although Rust issues are sometimes hard to navigate... If it was possible, we'd take it in a heartbeat.

There is another possible solution just for your case - via backends of wgpu-rs itself (as opposed to wgpu-core). It currently has the "web" backend and the wgpu-core backend. It needs also to have a "native header" backend, linking to an implementation to webgpu-headers. So we could add a "dummy" backend there.

Problem is - this would only help your case but not @xhebox or other peoples similar needs. So I can't recommend this path.

Shatur commented 2 years ago

So we could add a "dummy" backend there.

Hm... Yes, it would solve the problem.

Problem is - this would only help your case but not @xhebox or other peoples similar needs. So I can't recommend this path.

Agree, it would be better to solve the problem globally via backend configuration. But now I'm not sure if there is an elegant solution. We could move out automatic backend selection into a separate crate that depends on wgpu, but it looks dirty. The dummy backend seems like a more clean solution as to me.

kvark commented 2 years ago

The dummy backend for your specific case is ultimately better than other alternatives, since the whole wgpu-core gets compiled out from the server. I filed https://github.com/gfx-rs/wgpu/issues/2291

Shatur commented 2 years ago

You are right, a dummy backend for wgpu is much better for servers then just disabling graphical backends and could be a separate feature. Thanks!

aloucks commented 2 years ago

I believe WGPU used to work in the way that @xhebox is asking for. Each backend was a compile time feature. It was kind of a pain to switch back and forth between backends while testing changes. Now we've moved to the opposite extreme where everything is compiled into the binary at all times and there is no way to exclude the backends that you don't need or care about.

I think the situation is overall better now, but I do see still a use case for wanting to remove extra backends if you know you're not going to target them.

Perhaps the suggestion above about having the backends as default features that you may opt-out of is the ideal middle ground.

kvark commented 2 years ago

@aloucks we can't follow this suggestion because default features in cargo today can't be platform-dependent...

aloucks commented 2 years ago

@kvark I don't think they need to be.

I was able to set metal as a default feature and then adjust the config guards to alias empty to metal when not on an apple platform. I could build and run examples on Windows 10. There's probably a bit more work involved to expose it cleanly from the base wgpu crate, but I think it's doable in theory.

I don't think this is terribly high priority to fix or anything, but I'd always be happy to have a way to reduce compile time :)

$ git diff -p
diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml
index 43e850ba..238e90ab 100644
--- a/wgpu-hal/Cargo.toml
+++ b/wgpu-hal/Cargo.toml
@@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0"
 [lib]

 [features]
-default = []
+default = ["metal"]
 metal = ["naga/msl-out", "block", "foreign-types"]
 vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "inplace_it"]
 gles = ["naga/glsl-out", "glow", "egl", "libloading"]
diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs
index 4b292a3b..e12ebe63 100644
--- a/wgpu-hal/src/lib.rs
+++ b/wgpu-hal/src/lib.rs
@@ -47,8 +47,6 @@
     clippy::pattern_type_mismatch,
 )]

-#[cfg(all(feature = "metal", not(any(target_os = "macos", target_os = "ios"))))]
-compile_error!("Metal API enabled on non-Apple OS. If your project is not using resolver=\"2\" in Cargo.toml, it should."); 
 #[cfg(all(feature = "dx12", not(windows)))]
 compile_error!("DX12 API enabled on non-Windows OS. If your project is not using resolver=\"2\" in Cargo.toml, it should.");

@@ -57,8 +55,10 @@ mod dx12;
 mod empty;
 #[cfg(all(feature = "gles"))]
 mod gles;
-#[cfg(all(feature = "metal"))]
+#[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
 mod metal;
+#[cfg(not(all(feature = "metal", any(target_os = "macos", target_os = "ios"))))]
+use empty as metal;
 #[cfg(feature = "vulkan")]
 mod vulkan;
kvark commented 2 years ago

@aloucks we should probably discuss this on the matrix if you are able to. So far, I don't see how this would work, still.

metal = ["naga/msl-out", "block", "foreign-types"]

If metal is in defaults, then these dependencies are enabled for all platforms, which is highly undesirable.

teoxoy commented 1 year ago

We now only enable backends based on the target platform with the only exception being GLES which is always available.

https://github.com/gfx-rs/wgpu/blob/581b22e6a026ac92589be2f36a30357600f9d02c/wgpu/Cargo.toml#L121-L133

I think this behavior makes sense, additionally we can expose a feature flag compat (mapping to a future WebGPU Compat - there has been some talk about this in the group) that covers DX11 and GLES.

@xhebox @aloucks does it sound like a satisfactory solution?

xhebox commented 1 year ago

We now only enable backends based on the target platform with the only exception being GLES which is always available.

https://github.com/gfx-rs/wgpu/blob/581b22e6a026ac92589be2f36a30357600f9d02c/wgpu/Cargo.toml#L121-L133

I think this behavior makes sense, additionally we can expose a feature flag compat (mapping to a future WebGPU Compat - there has been some talk about this in the group about this) that covers DX11 and GLES.

@xhebox @aloucks does it sound like a satisfactory solution?

Sounds great!

teoxoy commented 1 year ago

I opened a new issue to track it: https://github.com/gfx-rs/wgpu/issues/3514 since this issue is a bit old with more baggage behind it.