diff --git a/Cargo.toml b/Cargo.toml index 5f7f495b539dc..1b7bdf9f66311 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_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 6d86737485bac..49703ff3ece21 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -2,6 +2,7 @@ pub mod io; pub mod meta; pub mod processor; pub mod saver; +pub mod transformer; pub mod prelude { #[doc(hidden)] diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index da3468201e7a0..b2ccc4350e300 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -527,11 +527,11 @@ impl<'a> LoadContext<'a> { /// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a /// "load dependency". /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadAndSave`] preprocessor, + /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, /// changing a "load dependency" will result in re-processing of the asset. /// /// [`Process`]: crate::processor::Process - /// [`LoadAndSave`]: crate::processor::LoadAndSave + /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave pub async fn load_direct<'b>( &mut self, path: impl Into>, @@ -575,11 +575,11 @@ impl<'a> LoadContext<'a> { /// For example, if you are deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a /// "load dependency". /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadAndSave`] preprocessor, + /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, /// changing a "load dependency" will result in re-processing of the asset. /// /// [`Process`]: crate::processor::Process - /// [`LoadAndSave`]: crate::processor::LoadAndSave + /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave pub async fn load_direct_with_reader<'b>( &mut self, reader: &mut Reader<'_>, diff --git a/crates/bevy_asset/src/processor/process.rs b/crates/bevy_asset/src/processor/process.rs index fd4f46630b387..62fdd0c1fac65 100644 --- a/crates/bevy_asset/src/processor/process.rs +++ b/crates/bevy_asset/src/processor/process.rs @@ -6,7 +6,8 @@ use crate::{ meta::{AssetAction, AssetMeta, AssetMetaDyn, ProcessDependencyInfo, ProcessedInfo, Settings}, processor::AssetProcessor, saver::{AssetSaver, SavedAsset}, - AssetLoadError, AssetLoader, AssetPath, DeserializeMetaError, ErasedLoadedAsset, + transformer::AssetTransformer, + AssetLoadError, AssetLoader, AssetPath, DeserializeMetaError, ErasedLoadedAsset, LoadedAsset, MissingAssetLoaderForExtensionError, MissingAssetLoaderForTypeNameError, }; use bevy_utils::BoxedFuture; @@ -17,7 +18,7 @@ use thiserror::Error; /// Asset "processor" logic that reads input asset bytes (stored on [`ProcessContext`]), processes the value in some way, /// and then writes the final processed bytes with [`Writer`]. The resulting bytes must be loadable with the given [`Process::OutputLoader`]. /// -/// This is a "low level", maximally flexible interface. Most use cases are better served by the [`LoadAndSave`] implementation +/// This is a "low level", maximally flexible interface. Most use cases are better served by the [`LoadTransformAndSave`] implementation /// of [`Process`]. pub trait Process: Send + Sync + Sized + 'static { /// The configuration / settings used to process the asset. This will be stored in the [`AssetMeta`] and is user-configurable per-asset. @@ -34,13 +35,62 @@ pub trait Process: Send + Sync + Sized + 'static { ) -> BoxedFuture<'a, Result<::Settings, ProcessError>>; } +/// A flexible [`Process`] implementation that loads the source [`Asset`] using the `L` [`AssetLoader`], then transforms +/// the `L` asset into an `S` [`AssetSaver`] asset using the `T` [`AssetTransformer`], and lastly saves the asset using the `S` [`AssetSaver`]. +/// +/// When creating custom processors, it is generally recommended to use the [`LoadTransformAndSave`] [`Process`] implementation, +/// as it encourages you to separate your code into an [`AssetLoader`] capable of loading assets without processing enabled, +/// an [`AssetTransformer`] capable of converting from an `L` asset to an `S` asset, and +/// an [`AssetSaver`] that allows you save any `S` asset. However you can +/// also implement [`Process`] directly if [`LoadTransformAndSave`] feels limiting or unnecessary. +/// +/// This uses [`LoadTransformAndSaveSettings`] to configure the processor. +/// +/// [`Asset`]: crate::Asset +pub struct LoadTransformAndSave< + L: AssetLoader, + T: AssetTransformer, + S: AssetSaver, +> { + transformer: T, + saver: S, + marker: PhantomData L>, +} + +/// Settings for the [`LoadTransformAndSave`] [`Process::Settings`] implementation. +/// +/// `LoaderSettings` corresponds to [`AssetLoader::Settings`], `TransformerSettings` corresponds to [`AssetTransformer::Settings`], +/// and `SaverSettings` corresponds to [`AssetSaver::Settings`]. +#[derive(Serialize, Deserialize, Default)] +pub struct LoadTransformAndSaveSettings { + /// The [`AssetLoader::Settings`] for [`LoadTransformAndSave`]. + pub loader_settings: LoaderSettings, + /// The [`AssetTransformer::Settings`] for [`LoadTransformAndSave`]. + pub transformer_settings: TransformerSettings, + /// The [`AssetSaver::Settings`] for [`LoadTransformAndSave`]. + pub saver_settings: SaverSettings, +} + +impl< + L: AssetLoader, + T: AssetTransformer, + S: AssetSaver, + > LoadTransformAndSave +{ + pub fn new(transformer: T, saver: S) -> Self { + LoadTransformAndSave { + transformer, + saver, + marker: PhantomData, + } + } +} + /// A flexible [`Process`] implementation that loads the source [`Asset`] using the `L` [`AssetLoader`], then /// saves that `L` asset using the `S` [`AssetSaver`]. /// -/// When creating custom processors, it is generally recommended to use the [`LoadAndSave`] [`Process`] implementation, -/// as it encourages you to write both an [`AssetLoader`] capable of loading assets without processing enabled _and_ -/// an [`AssetSaver`] that allows you to efficiently process that asset type when that is desirable by users. However you can -/// also implement [`Process`] directly if [`LoadAndSave`] feels limiting or unnecessary. +/// This is a specialized use case of [`LoadTransformAndSave`] and is useful where there is no asset manipulation +/// such as when compressing assets. /// /// This uses [`LoadAndSaveSettings`] to configure the processor. /// @@ -112,6 +162,52 @@ pub enum ProcessError { ExtensionRequired, } +impl< + Loader: AssetLoader, + T: AssetTransformer, + Saver: AssetSaver, + > Process for LoadTransformAndSave +{ + type Settings = LoadTransformAndSaveSettings; + type OutputLoader = Saver::OutputLoader; + + fn process<'a>( + &'a self, + context: &'a mut ProcessContext, + meta: AssetMeta<(), Self>, + writer: &'a mut Writer, + ) -> BoxedFuture<'a, Result<::Settings, ProcessError>> { + Box::pin(async move { + let AssetAction::Process { settings, .. } = meta.asset else { + return Err(ProcessError::WrongMetaType); + }; + let loader_meta = AssetMeta::::new(AssetAction::Load { + loader: std::any::type_name::().to_string(), + settings: settings.loader_settings, + }); + let loaded_asset = context + .load_source_asset(loader_meta) + .await? + .take::() + .expect("Asset type is known"); + let transformed_asset = self + .transformer + .transform(loaded_asset, &settings.transformer_settings)?; + let loaded_transformed_asset = + ErasedLoadedAsset::from(LoadedAsset::from(transformed_asset)); + let saved_asset = + SavedAsset::::from_loaded(&loaded_transformed_asset).unwrap(); + + let output_settings = self + .saver + .save(writer, saved_asset, &settings.saver_settings) + .await + .map_err(|error| ProcessError::AssetSaveError(error.into()))?; + Ok(output_settings) + }) + } +} + impl> Process for LoadAndSave { diff --git a/crates/bevy_asset/src/transformer.rs b/crates/bevy_asset/src/transformer.rs new file mode 100644 index 0000000000000..13dcf648e0792 --- /dev/null +++ b/crates/bevy_asset/src/transformer.rs @@ -0,0 +1,20 @@ +use crate::{meta::Settings, Asset}; +use serde::{Deserialize, Serialize}; + +/// Transforms an [`Asset`] of a given [`AssetTransformer::AssetInput`] type to an [`Asset`] of [`AssetTransformer::AssetOutput`] type. +pub trait AssetTransformer: Send + Sync + 'static { + /// The [`Asset`] type which this [`AssetTransformer`] takes as and input. + type AssetInput: Asset; + /// The [`Asset`] type which this [`AssetTransformer`] outputs. + type AssetOutput: Asset; + /// The settings type used by this [`AssetTransformer`]. + type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>; + /// The type of [error](`std::error::Error`) which could be encountered by this saver. + type Error: Into>; + + fn transform<'a>( + &'a self, + asset: Self::AssetInput, + settings: &'a Self::Settings, + ) -> Result>; +} diff --git a/crates/bevy_core_pipeline/Cargo.toml b/crates/bevy_core_pipeline/Cargo.toml index cab1cb55e96a7..7151f7e437cb1 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 85f3e93ff1be1..e0ddd2098b2c8 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 656db89100819..4013db7b3bdfe 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 1ffb059007c8f..4057e40ee721d 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 95dcfc8ba76b3..4e1005febf186 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 a76fd68389e4c..d855c027d8812 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 7e092e5a38dca..1ba3a2b7e51ff 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 23eeb71e5b33b..93817b7aa9d3a 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 d7d76b355b518..11b75e5a556d6 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 c4d35164533fa..1f08147411d3f 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 187269fb28bfc..6c8eef1bf60ab 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 df0ff24f2c3d9..094364a631f38 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 0fdbb8066531a..8c2294c66c692 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 c905332b1fbb6..172a032b0401e 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 651de128cd7c8..73e68dfe493c0 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 b4a699cddcefa..170c2f916ae26 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 e43e1e85f5689..40a3bcd61652b 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 d9c9b8dc0fea1..d033092f06e66 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 003e1aef54c87..f057470fb9578 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 d337be44b93bc..d1a7df0b5e31a 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 47e352d3c277d..0000000000000 --- 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 5014b948537bc..cad006b34ba44 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 fab0aa19b5c9d..59d47849a3abb 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 18558dcef867f..f3b1af2f028c7 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 d7197cbd515a7..5f9d7ff4698e3 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 9e09dfba9ce52..3fae24e1e477d 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 55be54b496354..45bccf0bbe667 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 0459c390baf8b..b54cf8b4d920b 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 911881f12fd8c..f514fc9ba140f 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 a774321e83efa..30122e588a0f9 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 3cd446d6dab96..2ebf4d6c7ea9c 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 aec0e53d1aee3..b25957dcc4310 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 b09603430abce..e3d820a5498de 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 4b9c9943dc793..efd2a7ac67393 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 5ab21e1ef82ad..b66b34e88896d 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_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index df319a7857e34..f66a65e6d6b2d 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -361,7 +361,19 @@ pub fn extract_sprites( .map(|e| (commands.spawn_empty().id(), e)), ); } else { - let rect = sheet.and_then(|s| s.texture_rect(&texture_atlases)); + let atlas_rect = sheet.and_then(|s| s.texture_rect(&texture_atlases)); + let rect = match (atlas_rect, sprite.rect) { + (None, None) => None, + (None, Some(sprite_rect)) => Some(sprite_rect), + (Some(atlas_rect), None) => Some(atlas_rect), + (Some(atlas_rect), Some(mut sprite_rect)) => { + sprite_rect.min += atlas_rect.min; + sprite_rect.max += atlas_rect.min; + + Some(sprite_rect) + } + }; + // PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive extracted_sprites.sprites.insert( entity, diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index 5c60d759a5468..c62d6da82d4f3 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -21,8 +21,11 @@ pub struct Sprite { /// An optional custom size for the sprite that will be used when rendering, instead of the size /// of the sprite's image pub custom_size: Option, - /// An optional rectangle representing the region of the sprite's image to render, instead of - /// rendering the full image. This is an easy one-off alternative to using a texture atlas. + /// An optional rectangle representing the region of the sprite's image to render, instead of rendering + /// the full image. This is an easy one-off alternative to using a [`TextureAtlas`](crate::TextureAtlas). + /// + /// When used with a [`TextureAtlas`](crate::TextureAtlas), the rect + /// is offset by the atlas's minimal (top-left) corner position. pub rect: Option, /// [`Anchor`] point of the sprite in the world pub anchor: Anchor, diff --git a/crates/bevy_window/Cargo.toml b/crates/bevy_window/Cargo.toml index b67cdef0b0d8d..b4ebbce4a293b 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 580de90b293b7..f9b6336149c61 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 1619d02777658..1d98f7181e793 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 dc1861a38f99a..05354960b8437 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 45557b3639a4b..caeba74595b77 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 98336d4aff27c..dae59448568a9 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 1b2258fbf6b63..08bec2dcf6a19 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 e6b4e899a66a5..350485648f9b4 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/examples/asset/processing/asset_processing.rs b/examples/asset/processing/asset_processing.rs index 5947be2ab482a..306b7ee5a26e1 100644 --- a/examples/asset/processing/asset_processing.rs +++ b/examples/asset/processing/asset_processing.rs @@ -1,12 +1,13 @@ -//! This example illustrates how to define custom `AssetLoader`s and `AssetSaver`s, how to configure them, and how to register asset processors. +//! This example illustrates how to define custom `AssetLoader`s, `AssetTransfomers`, and `AssetSaver`s, how to configure them, and how to register asset processors. use bevy::{ asset::{ embedded_asset, io::{Reader, Writer}, - processor::LoadAndSave, + processor::LoadTransformAndSave, ron, saver::{AssetSaver, SavedAsset}, + transformer::AssetTransformer, AssetLoader, AsyncReadExt, AsyncWriteExt, LoadContext, }, prelude::*, @@ -14,6 +15,7 @@ use bevy::{ utils::{thiserror, BoxedFuture}, }; use serde::{Deserialize, Serialize}; +use std::convert::Infallible; use thiserror::Error; fn main() { @@ -59,10 +61,10 @@ impl Plugin for TextPlugin { .init_asset::() .register_asset_loader(CoolTextLoader) .register_asset_loader(TextLoader) - .register_asset_processor::>( - LoadAndSave::from(CoolTextSaver), + .register_asset_processor::>( + LoadTransformAndSave::new(CoolTextTransformer, CoolTextSaver), ) - .set_default_asset_processor::>("cool.ron"); + .set_default_asset_processor::>("cool.ron"); } } @@ -133,9 +135,7 @@ enum CoolTextLoaderError { impl AssetLoader for CoolTextLoader { type Asset = CoolText; - type Settings = (); - type Error = CoolTextLoaderError; fn load<'a>( @@ -170,16 +170,37 @@ impl AssetLoader for CoolTextLoader { } } -struct CoolTextSaver; +#[derive(Default)] +struct CoolTextTransformer; #[derive(Default, Serialize, Deserialize)] -pub struct CoolTextSaverSettings { +pub struct CoolTextTransformerSettings { appended: String, } +impl AssetTransformer for CoolTextTransformer { + type AssetInput = CoolText; + type AssetOutput = CoolText; + type Settings = CoolTextTransformerSettings; + type Error = Infallible; + + fn transform<'a>( + &'a self, + asset: Self::AssetInput, + settings: &'a Self::Settings, + ) -> Result> { + Ok(CoolText { + text: format!("{}{}", asset.text, settings.appended), + dependencies: asset.dependencies.clone(), + }) + } +} + +struct CoolTextSaver; + impl AssetSaver for CoolTextSaver { type Asset = CoolText; - type Settings = CoolTextSaverSettings; + type Settings = (); type OutputLoader = TextLoader; type Error = std::io::Error; @@ -187,11 +208,10 @@ impl AssetSaver for CoolTextSaver { &'a self, writer: &'a mut Writer, asset: SavedAsset<'a, Self::Asset>, - settings: &'a Self::Settings, + _settings: &'a Self::Settings, ) -> BoxedFuture<'a, Result> { Box::pin(async move { - let text = format!("{}{}", asset.text.clone(), settings.appended); - writer.write_all(text.as_bytes()).await?; + writer.write_all(asset.text.as_bytes()).await?; Ok(TextSettings::default()) }) } diff --git a/examples/asset/processing/assets/a.cool.ron.meta b/examples/asset/processing/assets/a.cool.ron.meta index 7feb4d3a7bf3c..d87c629bac33d 100644 --- a/examples/asset/processing/assets/a.cool.ron.meta +++ b/examples/asset/processing/assets/a.cool.ron.meta @@ -1,12 +1,13 @@ ( meta_format_version: "1.0", asset: Process( - processor: "bevy_asset::processor::process::LoadAndSave", + processor: "bevy_asset::processor::process::LoadTransformAndSave", settings: ( loader_settings: (), - saver_settings: ( + transformer_settings: ( appended: "X", ), + saver_settings: (), ), ), ) \ No newline at end of file diff --git a/examples/asset/processing/assets/d.cool.ron b/examples/asset/processing/assets/d.cool.ron index cfe835b25888d..12b8254c51cf5 100644 --- a/examples/asset/processing/assets/d.cool.ron +++ b/examples/asset/processing/assets/d.cool.ron @@ -3,6 +3,7 @@ dependencies: [ ], embedded_dependencies: [ - "foo/c.cool.ron" + "foo/c.cool.ron", + "embedded://asset_processing/e.txt" ], ) \ No newline at end of file diff --git a/examples/asset/processing/assets/d.cool.ron.meta b/examples/asset/processing/assets/d.cool.ron.meta index c79e622562868..7bc926f575993 100644 --- a/examples/asset/processing/assets/d.cool.ron.meta +++ b/examples/asset/processing/assets/d.cool.ron.meta @@ -1,12 +1,13 @@ ( meta_format_version: "1.0", asset: Process( - processor: "bevy_asset::processor::process::LoadAndSave", + processor: "bevy_asset::processor::process::LoadTransformAndSave", settings: ( loader_settings: (), - saver_settings: ( + transformer_settings: ( appended: "", ), + saver_settings: (), ), ), ) \ No newline at end of file diff --git a/examples/asset/processing/assets/foo/b.cool.ron.meta b/examples/asset/processing/assets/foo/b.cool.ron.meta index c79e622562868..7bc926f575993 100644 --- a/examples/asset/processing/assets/foo/b.cool.ron.meta +++ b/examples/asset/processing/assets/foo/b.cool.ron.meta @@ -1,12 +1,13 @@ ( meta_format_version: "1.0", asset: Process( - processor: "bevy_asset::processor::process::LoadAndSave", + processor: "bevy_asset::processor::process::LoadTransformAndSave", settings: ( loader_settings: (), - saver_settings: ( + transformer_settings: ( appended: "", ), + saver_settings: (), ), ), ) \ No newline at end of file diff --git a/examples/asset/processing/assets/foo/c.cool.ron.meta b/examples/asset/processing/assets/foo/c.cool.ron.meta index c79e622562868..7bc926f575993 100644 --- a/examples/asset/processing/assets/foo/c.cool.ron.meta +++ b/examples/asset/processing/assets/foo/c.cool.ron.meta @@ -1,12 +1,13 @@ ( meta_format_version: "1.0", asset: Process( - processor: "bevy_asset::processor::process::LoadAndSave", + processor: "bevy_asset::processor::process::LoadTransformAndSave", settings: ( loader_settings: (), - saver_settings: ( + transformer_settings: ( appended: "", ), + saver_settings: (), ), ), ) \ No newline at end of file diff --git a/tools/build-wasm-example/src/main.rs b/tools/build-wasm-example/src/main.rs index 8093f161f41d4..351439a4ffa24 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"); } }