Skip to content

Commit

Permalink
Merge branch 'main' into asset-source-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
thepackett committed Jan 26, 2024
2 parents f8ea0ff + 7ae36a9 commit 8a5b7e5
Show file tree
Hide file tree
Showing 57 changed files with 452 additions and 238 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod io;
pub mod meta;
pub mod processor;
pub mod saver;
pub mod transformer;

pub mod prelude {
#[doc(hidden)]
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_asset/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AssetPath<'b>>,
Expand Down Expand Up @@ -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<'_>,
Expand Down
108 changes: 102 additions & 6 deletions crates/bevy_asset/src/processor/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -34,13 +35,62 @@ pub trait Process: Send + Sync + Sized + 'static {
) -> BoxedFuture<'a, Result<<Self::OutputLoader as AssetLoader>::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<AssetInput = L::Asset>,
S: AssetSaver<Asset = T::AssetOutput>,
> {
transformer: T,
saver: S,
marker: PhantomData<fn() -> 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<LoaderSettings, TransformerSettings, SaverSettings> {
/// 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<AssetInput = L::Asset>,
S: AssetSaver<Asset = T::AssetOutput>,
> LoadTransformAndSave<L, T, S>
{
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.
///
Expand Down Expand Up @@ -112,6 +162,52 @@ pub enum ProcessError {
ExtensionRequired,
}

impl<
Loader: AssetLoader,
T: AssetTransformer<AssetInput = Loader::Asset>,
Saver: AssetSaver<Asset = T::AssetOutput>,
> Process for LoadTransformAndSave<Loader, T, Saver>
{
type Settings = LoadTransformAndSaveSettings<Loader::Settings, T::Settings, Saver::Settings>;
type OutputLoader = Saver::OutputLoader;

fn process<'a>(
&'a self,
context: &'a mut ProcessContext,
meta: AssetMeta<(), Self>,
writer: &'a mut Writer,
) -> BoxedFuture<'a, Result<<Self::OutputLoader as AssetLoader>::Settings, ProcessError>> {
Box::pin(async move {
let AssetAction::Process { settings, .. } = meta.asset else {
return Err(ProcessError::WrongMetaType);
};
let loader_meta = AssetMeta::<Loader, ()>::new(AssetAction::Load {
loader: std::any::type_name::<Loader>().to_string(),
settings: settings.loader_settings,
});
let loaded_asset = context
.load_source_asset(loader_meta)
.await?
.take::<Loader::Asset>()
.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::<T::AssetOutput>::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<Loader: AssetLoader, Saver: AssetSaver<Asset = Loader::Asset>> Process
for LoadAndSave<Loader, Saver>
{
Expand Down
20 changes: 20 additions & 0 deletions crates/bevy_asset/src/transformer.rs
Original file line number Diff line number Diff line change
@@ -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<Box<dyn std::error::Error + Send + Sync + 'static>>;

fn transform<'a>(
&'a self,
asset: Self::AssetInput,
settings: &'a Self::Settings,
) -> Result<Self::AssetOutput, Box<dyn std::error::Error + Send + Sync + 'static>>;
}
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ keywords = ["bevy"]
[features]
trace = []
webgl = []
webgpu = []
tonemapping_luts = ["bevy_render/ktx2", "bevy_render/zstd"]

[dependencies]
Expand Down
24 changes: 18 additions & 6 deletions crates/bevy_core_pipeline/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,24 +294,32 @@ 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<CachedTexture>,
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,
mip_level_count: Some(1u32),
..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
Expand Down Expand Up @@ -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<CachedTexture> = (0..mip_count)
.map(|mip| {
texture_cache.get(
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_core_pipeline/src/deferred/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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()
}),
);
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_gizmos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ keywords = ["bevy"]

[features]
webgl = []
webgpu = []

[dependencies]
# Bevy
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ keywords = ["bevy"]

[features]
webgl = []
webgpu = []
shader_format_glsl = ["naga_oil/glsl"]
pbr_transmission_textures = []

Expand Down Expand Up @@ -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",
] }

Expand Down
Loading

0 comments on commit 8a5b7e5

Please sign in to comment.