From 35ac1b152ee1b2c7b2930f67d93be0db6f395b47 Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Fri, 26 Jan 2024 13:14:21 -0500 Subject: [PATCH] Update to wgpu 0.19 and raw-window-handle 0.6 (#11280) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective Keep core dependencies up to date. ## Solution Update the dependencies. wgpu 0.19 only supports raw-window-handle (rwh) 0.6, so bumping that was included in this. The rwh 0.6 version bump is just the simplest way of doing it. There might be a way we can take advantage of wgpu's new safe surface creation api, but I'm not familiar enough with bevy's window management to untangle it and my attempt ended up being a mess of lifetimes and rustc complaining about missing trait impls (that were implemented). Thanks to @MiniaczQ for the (much simpler) rwh 0.6 version bump code. Unblocks https://github.com/bevyengine/bevy/pull/9172 and https://github.com/bevyengine/bevy/pull/10812 ~~This might be blocked on cpal and oboe updating their ndk versions to 0.8, as they both currently target ndk 0.7 which uses rwh 0.5.2~~ Tested on android, and everything seems to work correctly (audio properly stops when minimized, and plays when re-focusing the app). --- ## Changelog - `wgpu` has been updated to 0.19! The long awaited arcanization has been merged (for more info, see https://gfx-rs.github.io/2023/11/24/arcanization.html), and Vulkan should now be working again on Intel GPUs. - Targeting WebGPU now requires that you add the new `webgpu` feature (setting the `RUSTFLAGS` environment variable to `--cfg=web_sys_unstable_apis` is still required). This feature currently overrides the `webgl2` feature if you have both enabled (the `webgl2` feature is enabled by default), so it is not recommended to add it as a default feature to libraries without putting it behind a flag that allows library users to opt out of it! In the future we plan on supporting wasm binaries that can target both webgl2 and webgpu now that wgpu added support for doing so (see https://github.com/bevyengine/bevy/issues/11505). - `raw-window-handle` has been updated to version 0.6. ## Migration Guide - `bevy_render::instance_index::get_instance_index()` has been removed as the webgl2 workaround is no longer required as it was fixed upstream in wgpu. The `BASE_INSTANCE_WORKAROUND` shaderdef has also been removed. - WebGPU now requires the new `webgpu` feature to be enabled. The `webgpu` feature currently overrides the `webgl2` feature so you no longer need to disable all default features and re-add them all when targeting `webgpu`, but binaries built with both the `webgpu` and `webgl2` features will only target the webgpu backend, and will only work on browsers that support WebGPU. - Places where you conditionally compiled things for webgl2 need to be updated because of this change, eg: - `#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))]` becomes `#[cfg(any(not(feature = "webgl") ,not(target_arch = "wasm32"), feature = "webgpu"))]` - `#[cfg(all(feature = "webgl", target_arch = "wasm32"))]` becomes `#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]` - `if cfg!(all(feature = "webgl", target_arch = "wasm32"))` becomes `if cfg!(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))` - `create_texture_with_data` now also takes a `TextureDataOrder`. You can probably just set this to `TextureDataOrder::default()` - `TextureFormat`'s `block_size` has been renamed to `block_copy_size` - See the `wgpu` changelog for anything I might've missed: https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md --------- Co-authored-by: François --- Cargo.toml | 5 +- crates/bevy_core_pipeline/Cargo.toml | 1 + crates/bevy_core_pipeline/src/bloom/mod.rs | 24 ++++++--- .../src/core_2d/main_pass_2d_node.rs | 2 +- .../core_3d/main_transparent_pass_3d_node.rs | 2 +- .../bevy_core_pipeline/src/deferred/node.rs | 10 ++-- crates/bevy_gizmos/Cargo.toml | 1 + crates/bevy_internal/Cargo.toml | 8 +++ crates/bevy_pbr/Cargo.toml | 5 +- crates/bevy_pbr/src/deferred/mod.rs | 22 ++++---- crates/bevy_pbr/src/light.rs | 6 ++- crates/bevy_pbr/src/prepass/mod.rs | 8 ++- crates/bevy_pbr/src/prepass/prepass.wgsl | 14 ++--- crates/bevy_pbr/src/render/light.rs | 52 ++++++++++++++----- crates/bevy_pbr/src/render/mesh.rs | 10 ++-- crates/bevy_pbr/src/render/mesh.wgsl | 14 ++--- .../bevy_pbr/src/render/mesh_functions.wgsl | 10 ++-- .../bevy_pbr/src/render/mesh_view_bindings.rs | 24 ++++++--- crates/bevy_pbr/src/ssao/mod.rs | 1 + crates/bevy_render/Cargo.toml | 11 ++-- crates/bevy_render/src/globals.rs | 2 +- crates/bevy_render/src/instance_index.wgsl | 17 ------ crates/bevy_render/src/lib.rs | 13 +---- .../render_resource/batched_uniform_buffer.rs | 8 ++- crates/bevy_render/src/render_resource/mod.rs | 2 +- .../src/render_resource/pipeline_cache.rs | 2 +- crates/bevy_render/src/renderer/mod.rs | 6 +-- .../bevy_render/src/renderer/render_device.rs | 11 ++-- crates/bevy_render/src/settings.rs | 15 ++++-- .../bevy_render/src/texture/fallback_image.rs | 7 ++- crates/bevy_render/src/texture/image.rs | 4 +- crates/bevy_render/src/texture/ktx2.rs | 4 +- crates/bevy_render/src/view/window/mod.rs | 20 +++++-- crates/bevy_sprite/Cargo.toml | 1 + crates/bevy_sprite/src/mesh2d/mesh.rs | 8 ++- .../src/mesh2d/mesh2d_functions.wgsl | 11 ++-- crates/bevy_window/Cargo.toml | 2 +- crates/bevy_window/src/raw_handle.rs | 41 ++++++++------- crates/bevy_winit/Cargo.toml | 11 ++-- crates/bevy_winit/src/lib.rs | 6 +-- crates/bevy_winit/src/system.rs | 6 +-- docs-template/EXAMPLE_README.md.tpl | 3 +- docs/cargo_features.md | 3 +- examples/README.md | 3 +- tools/build-wasm-example/src/main.rs | 27 +--------- 45 files changed, 260 insertions(+), 203 deletions(-) delete mode 100644 crates/bevy_render/src/instance_index.wgsl diff --git a/Cargo.toml b/Cargo.toml index 5f7f495b53..1b7bdf9f66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -277,9 +277,12 @@ shader_format_spirv = ["bevy_internal/shader_format_spirv"] # Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs pbr_transmission_textures = ["bevy_internal/pbr_transmission_textures"] -# Enable some limitations to be able to use WebGL2. If not enabled, it will default to WebGPU in Wasm. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU. +# Enable some limitations to be able to use WebGL2. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU. webgl2 = ["bevy_internal/webgl"] +# Enable support for WebGPU in Wasm. When enabled, this feature will override the `webgl2` feature and you won't be able to run Wasm builds with WebGL2, only with WebGPU. Requires the `RUSTFLAGS` environment variable to be set to `--cfg=web_sys_unstable_apis` when building. +webgpu = ["bevy_internal/webgpu"] + # Enables the built-in asset processor for processed assets. asset_processor = ["bevy_internal/asset_processor"] diff --git a/crates/bevy_core_pipeline/Cargo.toml b/crates/bevy_core_pipeline/Cargo.toml index cab1cb55e9..7151f7e437 100644 --- a/crates/bevy_core_pipeline/Cargo.toml +++ b/crates/bevy_core_pipeline/Cargo.toml @@ -15,6 +15,7 @@ keywords = ["bevy"] [features] trace = [] webgl = [] +webgpu = [] tonemapping_luts = ["bevy_render/ktx2", "bevy_render/zstd"] [dependencies] diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index 85f3e93ff1..e0ddd2098b 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -294,16 +294,24 @@ impl ViewNode for BloomNode { #[derive(Component)] struct BloomTexture { // First mip is half the screen resolution, successive mips are half the previous - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] texture: CachedTexture, // WebGL does not support binding specific mip levels for sampling, fallback to separate textures instead - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] texture: Vec, mip_count: u32, } impl BloomTexture { - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] fn view(&self, base_mip_level: u32) -> TextureView { self.texture.texture.create_view(&TextureViewDescriptor { base_mip_level, @@ -311,7 +319,7 @@ impl BloomTexture { ..Default::default() }) } - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] fn view(&self, base_mip_level: u32) -> TextureView { self.texture[base_mip_level as usize] .texture @@ -354,9 +362,13 @@ fn prepare_bloom_textures( view_formats: &[], }; - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] let texture = texture_cache.get(&render_device, texture_descriptor); - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] let texture: Vec = (0..mip_count) .map(|mip| { texture_cache.get( diff --git a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs index 656db89100..4013db7b3b 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs @@ -69,7 +69,7 @@ impl Node for MainPass2dNode { // WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't // reset for the next render pass so add an empty render pass without a custom viewport - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] if camera.viewport.is_some() { #[cfg(feature = "trace")] let _reset_viewport_pass_2d = info_span!("reset_viewport_pass_2d").entered(); diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs index 1ffb059007..4057e40ee7 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs @@ -60,7 +60,7 @@ impl ViewNode for MainTransparentPass3dNode { // WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't // reset for the next render pass so add an empty render pass without a custom viewport - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] if camera.viewport.is_some() { #[cfg(feature = "trace")] let _reset_viewport_pass_3d = info_span!("reset_viewport_pass_3d").entered(); diff --git a/crates/bevy_core_pipeline/src/deferred/node.rs b/crates/bevy_core_pipeline/src/deferred/node.rs index 95dcfc8ba7..4e1005febf 100644 --- a/crates/bevy_core_pipeline/src/deferred/node.rs +++ b/crates/bevy_core_pipeline/src/deferred/node.rs @@ -67,7 +67,7 @@ impl ViewNode for DeferredGBufferPrepassNode { // Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT. // Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9 // For webgl2 we fallback to manually clearing - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] if let Some(deferred_texture) = &view_prepass_textures.deferred { render_context.command_encoder().clear_texture( &deferred_texture.texture.texture, @@ -80,7 +80,7 @@ impl ViewNode for DeferredGBufferPrepassNode { .deferred .as_ref() .map(|deferred_texture| { - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] { bevy_render::render_resource::RenderPassColorAttachment { view: &deferred_texture.texture.default_view, @@ -91,7 +91,11 @@ impl ViewNode for DeferredGBufferPrepassNode { }, } } - #[cfg(not(all(feature = "webgl", target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] deferred_texture.get_attachment() }), ); diff --git a/crates/bevy_gizmos/Cargo.toml b/crates/bevy_gizmos/Cargo.toml index a76fd68389..d855c027d8 100644 --- a/crates/bevy_gizmos/Cargo.toml +++ b/crates/bevy_gizmos/Cargo.toml @@ -10,6 +10,7 @@ keywords = ["bevy"] [features] webgl = [] +webgpu = [] [dependencies] # Bevy diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 7e092e5a38..1ba3a2b7e5 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -103,6 +103,14 @@ webgl = [ "bevy_sprite?/webgl", ] +webgpu = [ + "bevy_core_pipeline?/webgpu", + "bevy_pbr?/webgpu", + "bevy_render?/webgpu", + "bevy_gizmos?/webgpu", + "bevy_sprite?/webgpu", +] + # enable systems that allow for automated testing on CI bevy_ci_testing = [ "bevy_app/bevy_ci_testing", diff --git a/crates/bevy_pbr/Cargo.toml b/crates/bevy_pbr/Cargo.toml index 23eeb71e5b..93817b7aa9 100644 --- a/crates/bevy_pbr/Cargo.toml +++ b/crates/bevy_pbr/Cargo.toml @@ -10,6 +10,7 @@ keywords = ["bevy"] [features] webgl = [] +webgpu = [] shader_format_glsl = ["naga_oil/glsl"] pbr_transmission_textures = [] @@ -39,11 +40,11 @@ smallvec = "1.6" thread_local = "1.0" [target.'cfg(target_arch = "wasm32")'.dependencies] -naga_oil = { version = "0.11" } +naga_oil = "0.12" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # Omit the `glsl` feature in non-WebAssembly by default. -naga_oil = { version = "0.11", default-features = false, features = [ +naga_oil = { version = "0.12", default-features = false, features = [ "test_shader", ] } diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index d7d76b355b..11b75e5a55 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -43,11 +43,11 @@ pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1; pub struct PbrDeferredLightingDepthId { depth_id: u32, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_0: f32, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_1: f32, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_2: f32, } @@ -56,11 +56,11 @@ impl PbrDeferredLightingDepthId { PbrDeferredLightingDepthId { depth_id: value as u32, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_0: 0.0, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_1: 0.0, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_2: 0.0, } } @@ -79,11 +79,11 @@ impl Default for PbrDeferredLightingDepthId { PbrDeferredLightingDepthId { depth_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID as u32, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_0: 0.0, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_1: 0.0, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding_2: 0.0, } } @@ -245,7 +245,7 @@ impl SpecializedRenderPipeline for DeferredLightingLayout { // Let the shader code know that it's running in a deferred pipeline. shader_defs.push("DEFERRED_LIGHTING_PIPELINE".into()); - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] shader_defs.push("WEBGL2".into()); if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) { @@ -310,7 +310,7 @@ impl SpecializedRenderPipeline for DeferredLightingLayout { shader_defs.push("SHADOW_FILTER_METHOD_JIMENEZ_14".into()); } - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into()); RenderPipelineDescriptor { diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index c4d3516453..1f08147411 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -350,7 +350,11 @@ impl CascadeShadowConfigBuilder { impl Default for CascadeShadowConfigBuilder { fn default() -> Self { - if cfg!(all(feature = "webgl", target_arch = "wasm32")) { + if cfg!(all( + feature = "webgl", + target_arch = "wasm32", + not(feature = "webgpu") + )) { // Currently only support one cascade in webgl. Self { num_cascades: 1, diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 187269fb28..6c8eef1bf6 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -325,7 +325,7 @@ where // The main limitation right now is that bind group order is hardcoded in shaders. bind_group_layouts.push(self.material_layout.clone()); - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] shader_defs.push("WEBGL2".into()); shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into()); @@ -518,7 +518,11 @@ where }; let mut push_constant_ranges = Vec::with_capacity(1); - if cfg!(all(feature = "webgl", target_arch = "wasm32")) { + if cfg!(all( + feature = "webgl", + target_arch = "wasm32", + not(feature = "webgpu") + )) { push_constant_ranges.push(PushConstantRange { stages: ShaderStages::VERTEX, range: 0..4, diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index df0ff24f2c..094364a631 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -7,8 +7,6 @@ mesh_view_bindings::{view, previous_view_proj}, } -#import bevy_render::instance_index::get_instance_index - #ifdef DEFERRED_PREPASS #import bevy_pbr::rgb9e5 #endif @@ -74,7 +72,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { vertex.normal, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - get_instance_index(vertex_no_morph.instance_index) + vertex_no_morph.instance_index ); #endif // SKINNED @@ -84,7 +82,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { vertex.tangent, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - get_instance_index(vertex_no_morph.instance_index) + vertex_no_morph.instance_index ); #endif // VERTEX_TANGENTS #endif // NORMAL_PREPASS_OR_DEFERRED_PREPASS @@ -107,13 +105,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #ifdef VERTEX_OUTPUT_INSTANCE_INDEX // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - out.instance_index = get_instance_index(vertex_no_morph.instance_index); -#endif -#ifdef BASE_INSTANCE_WORKAROUND - // Hack: this ensures the push constant is always used, which works around this issue: - // https://github.com/bevyengine/bevy/issues/10509 - // This can be removed when wgpu 0.19 is released - out.position.x += min(f32(get_instance_index(0u)), 0.0); + out.instance_index = vertex_no_morph.instance_index; #endif return out; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 0fdbb80665..8c2294c66c 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -205,13 +205,21 @@ pub const MAX_UNIFORM_BUFFER_POINT_LIGHTS: usize = 256; //NOTE: When running bevy on Adreno GPU chipsets in WebGL, any value above 1 will result in a crash // when loading the wgsl "pbr_functions.wgsl" in the function apply_fog. -#[cfg(all(feature = "webgl", target_arch = "wasm32"))] +#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] pub const MAX_DIRECTIONAL_LIGHTS: usize = 1; -#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] +#[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" +))] pub const MAX_DIRECTIONAL_LIGHTS: usize = 10; -#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] +#[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" +))] pub const MAX_CASCADES_PER_LIGHT: usize = 4; -#[cfg(all(feature = "webgl", target_arch = "wasm32"))] +#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] pub const MAX_CASCADES_PER_LIGHT: usize = 1; #[derive(Resource, Clone)] @@ -698,13 +706,21 @@ pub fn prepare_lights( let mut point_lights: Vec<_> = point_lights.iter().collect::>(); let mut directional_lights: Vec<_> = directional_lights.iter().collect::>(); - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] let max_texture_array_layers = render_device.limits().max_texture_array_layers as usize; - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] let max_texture_cubes = max_texture_array_layers / 6; - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] let max_texture_array_layers = 1; - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] let max_texture_cubes = 1; if !*max_directional_lights_warning_emitted && directional_lights.len() > MAX_DIRECTIONAL_LIGHTS @@ -1177,9 +1193,17 @@ pub fn prepare_lights( .create_view(&TextureViewDescriptor { label: Some("point_light_shadow_map_array_texture_view"), format: None, - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] dimension: Some(TextureViewDimension::CubeArray), - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all( + feature = "webgl", + target_arch = "wasm32", + not(feature = "webgpu") + ))] dimension: Some(TextureViewDimension::Cube), aspect: TextureAspect::DepthOnly, base_mip_level: 0, @@ -1192,9 +1216,13 @@ pub fn prepare_lights( .create_view(&TextureViewDescriptor { label: Some("directional_light_shadow_map_array_texture_view"), format: None, - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] dimension: Some(TextureViewDimension::D2Array), - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] dimension: Some(TextureViewDimension::D2), aspect: TextureAspect::DepthOnly, base_mip_level: 0, diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index c905332b1f..172a032b04 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -806,7 +806,7 @@ impl SpecializedMeshPipeline for MeshPipeline { shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into()); } - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] shader_defs.push("WEBGL2".into()); if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) { @@ -899,7 +899,11 @@ impl SpecializedMeshPipeline for MeshPipeline { } let mut push_constant_ranges = Vec::with_capacity(1); - if cfg!(all(feature = "webgl", target_arch = "wasm32")) { + if cfg!(all( + feature = "webgl", + target_arch = "wasm32", + not(feature = "webgpu") + )) { push_constant_ranges.push(PushConstantRange { stages: ShaderStages::VERTEX, range: 0..4, @@ -1180,7 +1184,7 @@ impl RenderCommand

for DrawMesh { pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); let batch_range = item.batch_range(); - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] pass.set_push_constants( ShaderStages::VERTEX, 0, diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index 651de128cd..73e68dfe49 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -5,7 +5,6 @@ forward_io::{Vertex, VertexOutput}, view_transformations::position_world_to_clip, } -#import bevy_render::instance_index::get_instance_index #ifdef MORPH_TARGETS fn morph_vertex(vertex_in: Vertex) -> Vertex { @@ -54,7 +53,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { vertex.normal, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - get_instance_index(vertex_no_morph.instance_index) + vertex_no_morph.instance_index ); #endif #endif @@ -78,7 +77,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { vertex.tangent, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - get_instance_index(vertex_no_morph.instance_index) + vertex_no_morph.instance_index ); #endif @@ -89,14 +88,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #ifdef VERTEX_OUTPUT_INSTANCE_INDEX // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - out.instance_index = get_instance_index(vertex_no_morph.instance_index); -#endif - -#ifdef BASE_INSTANCE_WORKAROUND - // Hack: this ensures the push constant is always used, which works around this issue: - // https://github.com/bevyengine/bevy/issues/10509 - // This can be removed when wgpu 0.19 is released - out.position.x += min(f32(get_instance_index(0u)), 0.0); + out.instance_index = vertex_no_morph.instance_index; #endif return out; diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index b4a699cddc..170c2f916a 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -6,17 +6,15 @@ mesh_types::MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT, view_transformations::position_world_to_clip, } -#import bevy_render::{ - instance_index::get_instance_index, - maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack}, -} +#import bevy_render::maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack} + fn get_model_matrix(instance_index: u32) -> mat4x4 { - return affine_to_square(mesh[get_instance_index(instance_index)].model); + return affine_to_square(mesh[instance_index].model); } fn get_previous_model_matrix(instance_index: u32) -> mat4x4 { - return affine_to_square(mesh[get_instance_index(instance_index)].previous_model); + return affine_to_square(mesh[instance_index].previous_model); } fn mesh_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.rs b/crates/bevy_pbr/src/render/mesh_view_bindings.rs index e43e1e85f5..40a3bcd616 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.rs @@ -21,9 +21,13 @@ use bevy_render::{ view::{Msaa, ViewUniform, ViewUniforms}, }; -#[cfg(all(feature = "webgl", target_arch = "wasm32"))] +#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] use bevy_render::render_resource::binding_types::texture_cube; -#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] +#[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" +))] use bevy_render::render_resource::binding_types::{texture_2d_array, texture_cube_array}; use crate::{ @@ -182,9 +186,13 @@ fn layout_entries( // Point Shadow Texture Cube Array ( 2, - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] texture_cube_array(TextureSampleType::Depth), - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] texture_cube(TextureSampleType::Depth), ), // Point Shadow Texture Array Sampler @@ -192,9 +200,13 @@ fn layout_entries( // Directional Shadow Texture Array ( 4, - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + #[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" + ))] texture_2d_array(TextureSampleType::Depth), - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] texture_2d(TextureSampleType::Depth), ), // Directional Shadow Texture Array Sampler diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index d9c9b8dc0f..d033092f06 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -340,6 +340,7 @@ impl FromWorld for SsaoPipelines { usage: TextureUsages::TEXTURE_BINDING, view_formats: &[], }), + TextureDataOrder::default(), bytemuck::cast_slice(&generate_hilbert_index_lut()), ) .create_view(&TextureViewDescriptor::default()); diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 003e1aef54..f057470fb9 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -32,6 +32,7 @@ tracing-tracy = [] wgpu_trace = ["wgpu/trace"] ci_limits = [] webgl = ["wgpu/webgl"] +webgpu = ["wgpu/webgpu"] [dependencies] # bevy @@ -62,12 +63,16 @@ image = { version = "0.24", default-features = false } codespan-reporting = "0.11.0" # `fragile-send-sync-non-atomic-wasm` feature means we can't use WASM threads for rendering # It is enabled for now to avoid having to do a significant overhaul of the renderer just for wasm -wgpu = { version = "0.18", features = [ +wgpu = { version = "0.19.1", default-features = false, features = [ + "wgsl", + "dx12", + "metal", "naga", + "naga-ir", "fragile-send-sync-non-atomic-wasm", ] } -naga = { version = "0.14.2", features = ["wgsl-in"] } -naga_oil = { version = "0.11", default-features = false, features = [ +naga = { version = "0.19", features = ["wgsl-in"] } +naga_oil = { version = "0.12", default-features = false, features = [ "test_shader", ] } serde = { version = "1", features = ["derive"] } diff --git a/crates/bevy_render/src/globals.rs b/crates/bevy_render/src/globals.rs index d337be44b9..d1a7df0b5e 100644 --- a/crates/bevy_render/src/globals.rs +++ b/crates/bevy_render/src/globals.rs @@ -56,7 +56,7 @@ pub struct GlobalsUniform { /// It wraps to zero when it reaches the maximum value of a u32. frame_count: u32, /// WebGL2 structs must be 16 byte aligned. - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _wasm_padding: f32, } diff --git a/crates/bevy_render/src/instance_index.wgsl b/crates/bevy_render/src/instance_index.wgsl deleted file mode 100644 index 47e352d3c2..0000000000 --- a/crates/bevy_render/src/instance_index.wgsl +++ /dev/null @@ -1,17 +0,0 @@ -#define_import_path bevy_render::instance_index - -#ifdef BASE_INSTANCE_WORKAROUND -// naga and wgpu should polyfill WGSL instance_index functionality where it is -// not available in GLSL. Until that is done, we can work around it in bevy -// using a push constant which is converted to a uniform by naga and wgpu. -// https://github.com/gfx-rs/wgpu/issues/1573 -var base_instance: i32; - -fn get_instance_index(instance_index: u32) -> u32 { - return u32(base_instance) + instance_index; -} -#else -fn get_instance_index(instance_index: u32) -> u32 { - return instance_index; -} -#endif diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 5014b94853..cad006b34b 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -259,11 +259,12 @@ impl Plugin for RenderPlugin { flags: settings.instance_flags, gles_minor_version: settings.gles3_minor_version, }); + // SAFETY: Plugins should be set up on the main thread. let surface = primary_window.map(|wrapper| unsafe { let handle = wrapper.get_handle(); instance - .create_surface(&handle) + .create_surface(handle) .expect("Failed to create wgpu surface") }); @@ -332,16 +333,6 @@ impl Plugin for RenderPlugin { } fn finish(&self, app: &mut App) { - load_internal_asset!( - app, - INSTANCE_INDEX_SHADER_HANDLE, - "instance_index.wgsl", - Shader::from_wgsl_with_defs, - vec![ - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] - "BASE_INSTANCE_WORKAROUND".into() - ] - ); load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl); if let Some(future_renderer_resources) = app.world.remove_resource::() diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index fab0aa19b5..59d47849a3 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -15,7 +15,11 @@ use wgpu::{BindingResource, Limits}; // `max_uniform_buffer_binding_size`. On macOS this ends up being the minimum // size of the uniform buffer as well as the size of each chunk of data at a // dynamic offset. -#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] +#[cfg(any( + not(feature = "webgl"), + not(target_arch = "wasm32"), + feature = "webgpu" +))] const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20; // WebGL2 quirk: using uniform buffers larger than 4KB will cause extremely @@ -23,7 +27,7 @@ const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20; // This is due to older shader compilers/GPUs that don't support dynamically // indexing uniform buffers, and instead emulate it with large switch statements // over buffer indices that take a long time to compile. -#[cfg(all(feature = "webgl", target_arch = "wasm32"))] +#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 12; /// Similar to [`DynamicUniformBuffer`], except every N elements (depending on size) diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index 18558dcef8..f3b1af2f02 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -32,7 +32,7 @@ pub use uniform_buffer::*; // TODO: decide where re-exports should go pub use wgpu::{ - util::{BufferInitDescriptor, DrawIndexedIndirect, DrawIndirect}, + util::{BufferInitDescriptor, DrawIndexedIndirectArgs, DrawIndirectArgs, TextureDataOrder}, AdapterInfo as WgpuAdapterInfo, AddressMode, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor, BlendOperation, BlendState, BufferAddress, BufferAsyncError, BufferBinding, diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index d7197cbd51..5f9d7ff469 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -277,7 +277,7 @@ impl ShaderCache { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { let mut shader_defs = shader_defs.to_vec(); - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] { shader_defs.push("NO_ARRAY_TEXTURES_SUPPORT".into()); shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into()); diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 9e09dfba9c..3fae24e1e4 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -133,7 +133,7 @@ const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") { pub async fn initialize_renderer( instance: &Instance, options: &WgpuSettings, - request_adapter_options: &RequestAdapterOptions<'_>, + request_adapter_options: &RequestAdapterOptions<'_, '_>, ) -> (RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter) { let adapter = instance .request_adapter(request_adapter_options) @@ -280,8 +280,8 @@ pub async fn initialize_renderer( .request_device( &wgpu::DeviceDescriptor { label: options.device_label.as_ref().map(|a| a.as_ref()), - features, - limits, + required_features: features, + required_limits: limits, }, trace_path, ) diff --git a/crates/bevy_render/src/renderer/render_device.rs b/crates/bevy_render/src/renderer/render_device.rs index 55be54b496..45bccf0bbe 100644 --- a/crates/bevy_render/src/renderer/render_device.rs +++ b/crates/bevy_render/src/renderer/render_device.rs @@ -5,7 +5,7 @@ use crate::render_resource::{ use bevy_ecs::system::Resource; use wgpu::{ util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, - BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, + BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, MaintainResult, }; use super::RenderQueue; @@ -61,7 +61,7 @@ impl RenderDevice { /// /// no-op on the web, device is automatically polled. #[inline] - pub fn poll(&self, maintain: wgpu::Maintain) -> bool { + pub fn poll(&self, maintain: wgpu::Maintain) -> MaintainResult { self.device.poll(maintain) } @@ -161,11 +161,12 @@ impl RenderDevice { &self, render_queue: &RenderQueue, desc: &wgpu::TextureDescriptor, + order: wgpu::util::TextureDataOrder, data: &[u8], ) -> Texture { - let wgpu_texture = self - .device - .create_texture_with_data(render_queue.as_ref(), desc, data); + let wgpu_texture = + self.device + .create_texture_with_data(render_queue.as_ref(), desc, order, data); Texture::from(wgpu_texture) } diff --git a/crates/bevy_render/src/settings.rs b/crates/bevy_render/src/settings.rs index 0459c390ba..b54cf8b4d9 100644 --- a/crates/bevy_render/src/settings.rs +++ b/crates/bevy_render/src/settings.rs @@ -54,8 +54,14 @@ pub struct WgpuSettings { impl Default for WgpuSettings { fn default() -> Self { - let default_backends = if cfg!(all(feature = "webgl", target_arch = "wasm32")) { + let default_backends = if cfg!(all( + feature = "webgl", + target_arch = "wasm32", + not(feature = "webgpu") + )) { Backends::GL + } else if cfg!(all(feature = "webgpu", target_arch = "wasm32")) { + Backends::BROWSER_WEBGPU } else { Backends::all() }; @@ -67,8 +73,11 @@ impl Default for WgpuSettings { let priority = settings_priority_from_env().unwrap_or(WgpuSettingsPriority::Functionality); - let limits = if cfg!(all(feature = "webgl", target_arch = "wasm32")) - || matches!(priority, WgpuSettingsPriority::WebGL2) + let limits = if cfg!(all( + feature = "webgl", + target_arch = "wasm32", + not(feature = "webgpu") + )) || matches!(priority, WgpuSettingsPriority::WebGL2) { wgpu::Limits::downlevel_webgl2_defaults() } else { diff --git a/crates/bevy_render/src/texture/fallback_image.rs b/crates/bevy_render/src/texture/fallback_image.rs index 911881f12f..f514fc9ba1 100644 --- a/crates/bevy_render/src/texture/fallback_image.rs +++ b/crates/bevy_render/src/texture/fallback_image.rs @@ -98,7 +98,12 @@ fn fallback_image_new( } let texture = if create_texture_with_data { - render_device.create_texture_with_data(render_queue, &image.texture_descriptor, &image.data) + render_device.create_texture_with_data( + render_queue, + &image.texture_descriptor, + wgpu::util::TextureDataOrder::default(), + &image.data, + ) } else { render_device.create_texture(&image.texture_descriptor) }; diff --git a/crates/bevy_render/src/texture/image.rs b/crates/bevy_render/src/texture/image.rs index a774321e83..30122e588a 100644 --- a/crates/bevy_render/src/texture/image.rs +++ b/crates/bevy_render/src/texture/image.rs @@ -793,7 +793,7 @@ impl TextureFormatPixelInfo for TextureFormat { fn pixel_size(&self) -> usize { let info = self; match info.block_dimensions() { - (1, 1) => info.block_size(None).unwrap() as usize, + (1, 1) => info.block_copy_size(None).unwrap() as usize, _ => panic!("Using pixel_size for compressed textures is invalid"), } } @@ -831,6 +831,8 @@ impl RenderAsset for Image { let texture = render_device.create_texture_with_data( render_queue, &self.texture_descriptor, + // TODO: Is this correct? Do we need to use `MipMajor` if it's a ktx2 file? + wgpu::util::TextureDataOrder::default(), &self.data, ); diff --git a/crates/bevy_render/src/texture/ktx2.rs b/crates/bevy_render/src/texture/ktx2.rs index 3cd446d6da..2ebf4d6c7e 100644 --- a/crates/bevy_render/src/texture/ktx2.rs +++ b/crates/bevy_render/src/texture/ktx2.rs @@ -160,7 +160,7 @@ pub fn ktx2_buffer_to_image( texture_format_info.block_dimensions().1, ); // Texture is not a depth or stencil format, it is possible to pass `None` and unwrap - let block_bytes = texture_format_info.block_size(None).unwrap(); + let block_bytes = texture_format_info.block_copy_size(None).unwrap(); let transcoder = LowLevelUastcTranscoder::new(); for (level, level_data) in levels.iter().enumerate() { @@ -240,7 +240,7 @@ pub fn ktx2_buffer_to_image( texture_format_info.block_dimensions().1 as usize, ); // Texture is not a depth or stencil format, it is possible to pass `None` and unwrap - let block_bytes = texture_format_info.block_size(None).unwrap() as usize; + let block_bytes = texture_format_info.block_copy_size(None).unwrap() as usize; let mut wgpu_data = vec![Vec::default(); (layer_count * face_count) as usize]; for (level, level_data) in levels.iter().enumerate() { diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index aec0e53d1a..b25957dcc4 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -16,7 +16,9 @@ use std::{ ops::{Deref, DerefMut}, sync::PoisonError, }; -use wgpu::{BufferUsages, TextureFormat, TextureUsages, TextureViewDescriptor}; +use wgpu::{ + BufferUsages, SurfaceTargetUnsafe, TextureFormat, TextureUsages, TextureViewDescriptor, +}; pub mod screenshot; @@ -193,7 +195,8 @@ fn extract_windows( } struct SurfaceData { - surface: wgpu::Surface, + // TODO: what lifetime should this be? + surface: wgpu::Surface<'static>, format: TextureFormat, } @@ -253,12 +256,16 @@ pub fn prepare_windows( .surfaces .entry(window.entity) .or_insert_with(|| { + let surface_target = SurfaceTargetUnsafe::RawHandle { + raw_display_handle: window.handle.display_handle, + raw_window_handle: window.handle.window_handle, + }; // SAFETY: The window handles in ExtractedWindows will always be valid objects to create surfaces on let surface = unsafe { // NOTE: On some OSes this MUST be called from the main thread. // As of wgpu 0.15, only fallible if the given window is a HTML canvas and obtaining a WebGPU or WebGL2 context fails. render_instance - .create_surface(&window.handle.get_handle()) + .create_surface_unsafe(surface_target) .expect("Failed to create wgpu surface") }; let caps = surface.get_capabilities(&render_adapter); @@ -293,6 +300,12 @@ pub fn prepare_windows( PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync, PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync, }, + // TODO: Expose this as a setting somewhere + // 2 is wgpu's default/what we've been using so far. + // 1 is the minimum, but may cause lower framerates due to the cpu waiting for the gpu to finish + // all work for the previous frame before starting work on the next frame, which then means the gpu + // has to wait for the cpu to finish to start on the next frame. + desired_maximum_frame_latency: 2, alpha_mode: match window.alpha_mode { CompositeAlphaMode::Auto => wgpu::CompositeAlphaMode::Auto, CompositeAlphaMode::Opaque => wgpu::CompositeAlphaMode::Opaque, @@ -347,6 +360,7 @@ pub fn prepare_windows( let may_erroneously_timeout = || { render_instance .enumerate_adapters(wgpu::Backends::VULKAN) + .iter() .any(|adapter| { let name = adapter.get_info().name; name.starts_with("Radeon") diff --git a/crates/bevy_sprite/Cargo.toml b/crates/bevy_sprite/Cargo.toml index b09603430a..e3d820a549 100644 --- a/crates/bevy_sprite/Cargo.toml +++ b/crates/bevy_sprite/Cargo.toml @@ -10,6 +10,7 @@ keywords = ["bevy"] [features] webgl = [] +webgpu = [] [dependencies] # bevy diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 4b9c9943dc..efd2a7ac67 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -511,7 +511,11 @@ impl SpecializedMeshPipeline for Mesh2dPipeline { false => TextureFormat::bevy_default(), }; let mut push_constant_ranges = Vec::with_capacity(1); - if cfg!(all(feature = "webgl", target_arch = "wasm32")) { + if cfg!(all( + feature = "webgl", + target_arch = "wasm32", + not(feature = "webgpu") + )) { push_constant_ranges.push(PushConstantRange { stages: ShaderStages::VERTEX, range: 0..4, @@ -688,7 +692,7 @@ impl RenderCommand

for DrawMesh2d { pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); let batch_range = item.batch_range(); - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] pass.set_push_constants( ShaderStages::VERTEX, 0, diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl index 5ab21e1ef8..b66b34e888 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl @@ -4,13 +4,10 @@ mesh2d_view_bindings::view, mesh2d_bindings::mesh, } -#import bevy_render::{ - instance_index::get_instance_index, - maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack}, -} +#import bevy_render::maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack} fn get_model_matrix(instance_index: u32) -> mat4x4 { - return affine_to_square(mesh[get_instance_index(instance_index)].model); + return affine_to_square(mesh[instance_index].model); } fn mesh2d_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { @@ -31,8 +28,8 @@ fn mesh2d_position_local_to_clip(model: mat4x4, vertex_position: vec4) fn mesh2d_normal_local_to_world(vertex_normal: vec3, instance_index: u32) -> vec3 { return mat2x4_f32_to_mat3x3_unpack( - mesh[get_instance_index(instance_index)].inverse_transpose_model_a, - mesh[get_instance_index(instance_index)].inverse_transpose_model_b, + mesh[instance_index].inverse_transpose_model_a, + mesh[instance_index].inverse_transpose_model_b, ) * vertex_normal; } diff --git a/crates/bevy_window/Cargo.toml b/crates/bevy_window/Cargo.toml index b67cdef0b0..b4ebbce4a2 100644 --- a/crates/bevy_window/Cargo.toml +++ b/crates/bevy_window/Cargo.toml @@ -27,7 +27,7 @@ bevy_input = { path = "../bevy_input", version = "0.12.0" } # other serde = { version = "1.0", features = ["derive"], optional = true } -raw-window-handle = "0.5" +raw-window-handle = "0.6" smol_str = "0.2" [lints] diff --git a/crates/bevy_window/src/raw_handle.rs b/crates/bevy_window/src/raw_handle.rs index 580de90b29..f9b6336149 100644 --- a/crates/bevy_window/src/raw_handle.rs +++ b/crates/bevy_window/src/raw_handle.rs @@ -1,6 +1,7 @@ use bevy_ecs::prelude::Component; use raw_window_handle::{ - HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, + DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle, + RawWindowHandle, WindowHandle, }; /// A wrapper over [`RawWindowHandle`] and [`RawDisplayHandle`] that allows us to safely pass it across threads. @@ -17,7 +18,7 @@ pub struct RawHandleWrapper { } impl RawHandleWrapper { - /// Returns a [`HasRawWindowHandle`] + [`HasRawDisplayHandle`] impl, which exposes [`RawWindowHandle`] and [`RawDisplayHandle`]. + /// Returns a [`HasWindowHandle`] + [`HasDisplayHandle`] impl, which exposes [`WindowHandle`] and [`DisplayHandle`]. /// /// # Safety /// @@ -46,26 +47,26 @@ unsafe impl Sync for RawHandleWrapper {} /// In many cases, this should only be constructed on the main thread. pub struct ThreadLockedRawWindowHandleWrapper(RawHandleWrapper); -// SAFETY: the caller has validated that this is a valid context to get [`RawHandleWrapper`] -// as otherwise an instance of this type could not have been constructed -// NOTE: we cannot simply impl HasRawWindowHandle for RawHandleWrapper, -// as the `raw_window_handle` method is safe. We cannot guarantee that all calls -// of this method are correct (as it may be off the main thread on an incompatible platform), -// and so exposing a safe method to get a [`RawWindowHandle`] directly would be UB. -unsafe impl HasRawWindowHandle for ThreadLockedRawWindowHandleWrapper { - fn raw_window_handle(&self) -> RawWindowHandle { - self.0.window_handle +impl HasWindowHandle for ThreadLockedRawWindowHandleWrapper { + fn window_handle(&self) -> Result { + // SAFETY: the caller has validated that this is a valid context to get [`RawHandleWrapper`] + // as otherwise an instance of this type could not have been constructed + // NOTE: we cannot simply impl HasRawWindowHandle for RawHandleWrapper, + // as the `raw_window_handle` method is safe. We cannot guarantee that all calls + // of this method are correct (as it may be off the main thread on an incompatible platform), + // and so exposing a safe method to get a [`RawWindowHandle`] directly would be UB. + Ok(unsafe { WindowHandle::borrow_raw(self.0.window_handle) }) } } -// SAFETY: the caller has validated that this is a valid context to get [`RawDisplayHandle`] -// as otherwise an instance of this type could not have been constructed -// NOTE: we cannot simply impl HasRawDisplayHandle for RawHandleWrapper, -// as the `raw_display_handle` method is safe. We cannot guarantee that all calls -// of this method are correct (as it may be off the main thread on an incompatible platform), -// and so exposing a safe method to get a [`RawDisplayHandle`] directly would be UB. -unsafe impl HasRawDisplayHandle for ThreadLockedRawWindowHandleWrapper { - fn raw_display_handle(&self) -> RawDisplayHandle { - self.0.display_handle +impl HasDisplayHandle for ThreadLockedRawWindowHandleWrapper { + fn display_handle(&self) -> Result { + // SAFETY: the caller has validated that this is a valid context to get [`RawDisplayHandle`] + // as otherwise an instance of this type could not have been constructed + // NOTE: we cannot simply impl HasRawDisplayHandle for RawHandleWrapper, + // as the `raw_display_handle` method is safe. We cannot guarantee that all calls + // of this method are correct (as it may be off the main thread on an incompatible platform), + // and so exposing a safe method to get a [`RawDisplayHandle`] directly would be UB. + Ok(unsafe { DisplayHandle::borrow_raw(self.0.display_handle) }) } } diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index 1619d02777..1d98f7181e 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -28,19 +28,18 @@ bevy_utils = { path = "../bevy_utils", version = "0.12.0" } bevy_tasks = { path = "../bevy_tasks", version = "0.12.0" } # other -# feature rwh_05 refers to window_raw_handle@v0.5, -# updating to rwh_06 is blocked until wgpu 0.19 release lands. -winit = { version = "0.29", default-features = false, features = ["rwh_05"] } +# feature rwh_06 refers to window_raw_handle@v0.6 +winit = { version = "0.29", default-features = false, features = ["rwh_06"] } accesskit_winit = { version = "0.17", default-features = false, features = [ - "rwh_05", + "rwh_06", ] } approx = { version = "0.5", default-features = false } -raw-window-handle = "0.5" +raw-window-handle = "0.6" [target.'cfg(target_os = "android")'.dependencies] winit = { version = "0.29", default-features = false, features = [ "android-native-activity", - "rwh_05", + "rwh_06", ] } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index dc1861a38f..05354960b8 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -797,7 +797,7 @@ pub fn winit_runner(mut app: App) { .world .query_filtered::<(Entity, &Window), (With, Without)>(); if let Ok((entity, window)) = query.get_single(&app.world) { - use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; + use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; let window = window.clone(); let ( @@ -820,8 +820,8 @@ pub fn winit_runner(mut app: App) { ); let wrapper = RawHandleWrapper { - window_handle: winit_window.raw_window_handle(), - display_handle: winit_window.raw_display_handle(), + window_handle: winit_window.window_handle().unwrap().as_raw(), + display_handle: winit_window.display_handle().unwrap().as_raw(), }; app.world.entity_mut(entity).insert(wrapper); diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index 45557b3639..caeba74595 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -12,8 +12,8 @@ use bevy_utils::{ EntityHashMap, }; use bevy_window::{RawHandleWrapper, Window, WindowClosed, WindowCreated}; -use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; +use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use winit::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, event_loop::EventLoopWindowTarget, @@ -74,8 +74,8 @@ pub(crate) fn create_windows<'a>( commands .entity(entity) .insert(RawHandleWrapper { - window_handle: winit_window.raw_window_handle(), - display_handle: winit_window.raw_display_handle(), + window_handle: winit_window.window_handle().unwrap().as_raw(), + display_handle: winit_window.display_handle().unwrap().as_raw(), }) .insert(CachedWindow { window: window.clone(), diff --git a/docs-template/EXAMPLE_README.md.tpl b/docs-template/EXAMPLE_README.md.tpl index 98336d4aff..dae5944856 100644 --- a/docs-template/EXAMPLE_README.md.tpl +++ b/docs-template/EXAMPLE_README.md.tpl @@ -238,8 +238,7 @@ ruby -run -ehttpd examples/wasm Bevy support for WebGPU is being worked on, but is currently experimental. -To build for WebGPU, you'll need to disable default features and add all those you need, making sure to omit the `webgl2` feature. - +To build for WebGPU, you'll need to enable the `webgpu` feature. This will override the `webgl2` feature, and builds with the `webgpu` feature enabled won't be able to run on browsers that don't support WebGPU. WebGPU depends on unstable APIs so you will also need to pass the `web_sys_unstable_apis` flag to your builds. For example: ```sh diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 1b2258fbf6..08bec2dcf6 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -34,7 +34,7 @@ The default feature set enables most of the expected features of a game engine, |png|PNG image format support| |tonemapping_luts|Include tonemapping Look Up Tables KTX2 files. If everything is pink, you need to enable this feature or change the `Tonemapping` method on your `Camera2dBundle` or `Camera3dBundle`.| |vorbis|OGG/VORBIS audio format support| -|webgl2|Enable some limitations to be able to use WebGL2. If not enabled, it will default to WebGPU in Wasm. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU.| +|webgl2|Enable some limitations to be able to use WebGL2. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU.| |x11|X11 display server support| |zstd|For KTX2 supercompression| @@ -80,6 +80,7 @@ The default feature set enables most of the expected features of a game engine, |trace_tracy_memory|Tracing support, with memory profiling, exposing a port for Tracy| |wav|WAV audio format support| |wayland|Wayland display server support| +|webgpu|Enable support for WebGPU in Wasm. When enabled, this feature will override the `webgl2` feature and you won't be able to run Wasm builds with WebGL2, only with WebGPU. Requires the `RUSTFLAGS` environment variable to be set to `--cfg=web_sys_unstable_apis` when building.| |webp|WebP image format support| |wgpu_trace|Save a trace of all wgpu calls| |zlib|For KTX2 supercompression| diff --git a/examples/README.md b/examples/README.md index e6b4e899a6..350485648f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -562,8 +562,7 @@ ruby -run -ehttpd examples/wasm Bevy support for WebGPU is being worked on, but is currently experimental. -To build for WebGPU, you'll need to disable default features and add all those you need, making sure to omit the `webgl2` feature. - +To build for WebGPU, you'll need to enable the `webgpu` feature. This will override the `webgl2` feature, and builds with the `webgpu` feature enabled won't be able to run on browsers that don't support WebGPU. WebGPU depends on unstable APIs so you will also need to pass the `web_sys_unstable_apis` flag to your builds. For example: ```sh diff --git a/tools/build-wasm-example/src/main.rs b/tools/build-wasm-example/src/main.rs index 8093f161f4..351439a4ff 100644 --- a/tools/build-wasm-example/src/main.rs +++ b/tools/build-wasm-example/src/main.rs @@ -44,7 +44,7 @@ fn main() { assert!(!cli.examples.is_empty(), "must have at least one example"); - let mut default_features = true; + let default_features = true; let mut features: Vec<&str> = cli.features.iter().map(|f| f.as_str()).collect(); if let Some(frames) = cli.frames { let mut file = File::create("ci_testing_config.ron").unwrap(); @@ -56,30 +56,7 @@ fn main() { match cli.api { WebApi::Webgl2 => (), WebApi::Webgpu => { - features.push("animation"); - features.push("bevy_asset"); - features.push("bevy_audio"); - features.push("bevy_gilrs"); - features.push("bevy_scene"); - features.push("bevy_winit"); - features.push("bevy_core_pipeline"); - features.push("bevy_pbr"); - features.push("bevy_gltf"); - features.push("bevy_render"); - features.push("bevy_sprite"); - features.push("bevy_text"); - features.push("bevy_ui"); - features.push("png"); - features.push("hdr"); - features.push("ktx2"); - features.push("zstd"); - features.push("vorbis"); - features.push("x11"); - features.push("bevy_gizmos"); - features.push("android_shared_stdcxx"); - features.push("tonemapping_luts"); - features.push("default_font"); - default_features = false; + features.push("webgpu"); } }