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"); } }