Add asset_processor feature and remove AssetMode::ProcessedDev (#10194)

# Objective

Users shouldn't need to change their source code between "development
workflows" and "releasing". Currently, Bevy Asset V2 has two "processed"
asset modes `Processed` (assumes assets are already processed) and
`ProcessedDev` (starts an asset processor and processes assets). This
means that the mode must be changed _in code_ when switching from "app
dev" to "release". Very suboptimal.

We have already removed "runtime opt-in" for hot-reloading. Enabling the
`file_watcher` feature _automatically_ enables file watching in code.
This means deploying a game (without hot reloading enabled) just means
calling `cargo build --release` instead of `cargo run --features
bevy/file_watcher`.

We should adopt this pattern for asset processing.

## Solution

This adds the `asset_processor` feature, which will start the
`AssetProcessor` when an `AssetPlugin` runs in `AssetMode::Processed`.

The "asset processing workflow" is now:
1. Enable `AssetMode::Processed` on `AssetPlugin`
2. When developing, run with the `asset_processor` and `file_watcher`
features
3. When releasing, build without these features.

The `AssetMode::ProcessedDev` mode has been removed.
This commit is contained in:
Carter Anderson 2023-10-20 13:50:26 -07:00 committed by GitHub
parent 7132404b38
commit 6f27e0e35f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 38 deletions

View file

@ -244,6 +244,9 @@ shader_format_spirv = ["bevy_internal/shader_format_spirv"]
# 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. 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 = ["bevy_internal/webgl"] webgl2 = ["bevy_internal/webgl"]
# Enables the built-in asset processor for processed assets.
asset_processor = ["bevy_internal/asset_processor"]
# Enables watching the filesystem for Bevy Asset hot-reloading # Enables watching the filesystem for Bevy Asset hot-reloading
file_watcher = ["bevy_internal/file_watcher"] file_watcher = ["bevy_internal/file_watcher"]
@ -1080,7 +1083,7 @@ wasm = true
name = "asset_processing" name = "asset_processing"
path = "examples/asset/processing/processing.rs" path = "examples/asset/processing/processing.rs"
doc-scrape-examples = true doc-scrape-examples = true
required-features = ["file_watcher"] required-features = ["file_watcher", "asset_processor"]
[package.metadata.example.asset_processing] [package.metadata.example.asset_processing]
name = "Asset Processing" name = "Asset Processing"

View file

@ -14,6 +14,7 @@ keywords = ["bevy"]
file_watcher = ["notify-debouncer-full", "watch"] file_watcher = ["notify-debouncer-full", "watch"]
embedded_watcher = ["file_watcher"] embedded_watcher = ["file_watcher"]
multi-threaded = ["bevy_tasks/multi-threaded"] multi-threaded = ["bevy_tasks/multi-threaded"]
asset_processor = []
watch = [] watch = []
[dependencies] [dependencies]

View file

@ -41,7 +41,7 @@ use crate::{
io::{embedded::EmbeddedAssetRegistry, AssetSourceBuilder, AssetSourceBuilders, AssetSourceId}, io::{embedded::EmbeddedAssetRegistry, AssetSourceBuilder, AssetSourceBuilders, AssetSourceId},
processor::{AssetProcessor, Process}, processor::{AssetProcessor, Process},
}; };
use bevy_app::{App, First, MainScheduleOrder, Plugin, PostUpdate, Startup}; use bevy_app::{App, First, MainScheduleOrder, Plugin, PostUpdate};
use bevy_ecs::{ use bevy_ecs::{
reflect::AppTypeRegistry, reflect::AppTypeRegistry,
schedule::{IntoSystemConfigs, IntoSystemSetConfigs, ScheduleLabel, SystemSet}, schedule::{IntoSystemConfigs, IntoSystemSetConfigs, ScheduleLabel, SystemSet},
@ -80,21 +80,21 @@ pub enum AssetMode {
/// [`AssetReader`]: crate::io::AssetReader /// [`AssetReader`]: crate::io::AssetReader
/// [`AssetSource`]: crate::io::AssetSource /// [`AssetSource`]: crate::io::AssetSource
Unprocessed, Unprocessed,
/// Loads assets from their final processed [`AssetReader`]. This should generally only be used when distributing apps. /// Assets will be "pre-processed". This enables assets to be imported / converted / optimized ahead of time.
/// Use [`AssetMode::ProcessedDev`] to develop apps that process assets, then switch to [`AssetMode::Processed`] when deploying the apps.
/// ///
/// [`AssetReader`]: crate::io::AssetReader /// Assets will be read from their unprocessed [`AssetSource`] (defaults to the `assets` folder),
Processed, /// processed according to their [`AssetMeta`], and written to their processed [`AssetSource`] (defaults to the `imported_assets/Default` folder).
/// Starts an [`AssetProcessor`] in the background that reads assets from their unprocessed [`AssetSource`] (defaults to the `assets` folder),
/// processes them according to their [`AssetMeta`], and writes them to their processed [`AssetSource`] (defaults to the `imported_assets/Default` folder).
/// ///
/// Apps will load assets from the processed [`AssetSource`]. Asset loads will wait until the asset processor has finished processing the requested asset. /// By default, this assumes the processor _has already been run_. It will load assets from their final processed [`AssetReader`].
/// ///
/// This should generally be used in combination with the `file_watcher` cargo feature to support hot-reloading and re-processing assets. /// When developing an app, you should enable the `asset_processor` cargo feature, which will run the asset processor at startup. This should generally
/// be used in combination with the `file_watcher` cargo feature, which enables hot-reloading of assets that have changed. When both features are enabled,
/// changes to "original/source assets" will be detected, the asset will be re-processed, and then the final processed asset will be hot-reloaded in the app.
/// ///
/// [`AssetMeta`]: crate::meta::AssetMeta /// [`AssetMeta`]: crate::meta::AssetMeta
/// [`AssetSource`]: crate::io::AssetSource /// [`AssetSource`]: crate::io::AssetSource
ProcessedDev, /// [`AssetReader`]: crate::io::AssetReader
Processed,
} }
impl Default for AssetPlugin { impl Default for AssetPlugin {
@ -146,28 +146,32 @@ impl Plugin for AssetPlugin {
)); ));
} }
AssetMode::Processed => { AssetMode::Processed => {
let mut builders = app.world.resource_mut::<AssetSourceBuilders>(); #[cfg(feature = "asset_processor")]
let sources = builders.build_sources(false, watch); {
app.insert_resource(AssetServer::new( let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
sources, let processor = AssetProcessor::new(&mut builders);
AssetServerMode::Processed, let mut sources = builders.build_sources(false, watch);
watch, sources.gate_on_processor(processor.data.clone());
)); // the main asset server shares loaders with the processor asset server
} app.insert_resource(AssetServer::new_with_loaders(
AssetMode::ProcessedDev => { sources,
let mut builders = app.world.resource_mut::<AssetSourceBuilders>(); processor.server().data.loaders.clone(),
let processor = AssetProcessor::new(&mut builders); AssetServerMode::Processed,
let mut sources = builders.build_sources(false, watch); watch,
sources.gate_on_processor(processor.data.clone()); ))
// the main asset server shares loaders with the processor asset server .insert_resource(processor)
app.insert_resource(AssetServer::new_with_loaders( .add_systems(bevy_app::Startup, AssetProcessor::start);
sources, }
processor.server().data.loaders.clone(), #[cfg(not(feature = "asset_processor"))]
AssetServerMode::Processed, {
watch, let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
)) let sources = builders.build_sources(false, watch);
.insert_resource(processor) app.insert_resource(AssetServer::new(
.add_systems(Startup, AssetProcessor::start); sources,
AssetServerMode::Processed,
watch,
));
}
} }
} }
} }

View file

@ -102,6 +102,9 @@ glam_assert = ["bevy_math/glam_assert"]
default_font = ["bevy_text?/default_font"] default_font = ["bevy_text?/default_font"]
# Enables the built-in asset processor for processed assets.
asset_processor = ["bevy_asset?/asset_processor"]
# Enables watching the filesystem for Bevy Asset hot-reloading # Enables watching the filesystem for Bevy Asset hot-reloading
file_watcher = ["bevy_asset?/file_watcher"] file_watcher = ["bevy_asset?/file_watcher"]

View file

@ -43,6 +43,7 @@ The default feature set enables most of the expected features of a game engine,
|feature name|description| |feature name|description|
|-|-| |-|-|
|accesskit_unix|Enable AccessKit on Unix backends (currently only works with experimental screen readers and forks.)| |accesskit_unix|Enable AccessKit on Unix backends (currently only works with experimental screen readers and forks.)|
|asset_processor|Enables the built-in asset processor for processed assets.|
|async-io|Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io.| |async-io|Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io.|
|basis-universal|Basis Universal compressed texture support| |basis-universal|Basis Universal compressed texture support|
|bevy_ci_testing|Enable systems that allow for automated testing on CI| |bevy_ci_testing|Enable systems that allow for automated testing on CI|

View file

@ -17,18 +17,19 @@ use thiserror::Error;
fn main() { fn main() {
App::new() App::new()
// Enabling `processed_dev` will configure the AssetPlugin to use asset processing. // Using the "processed" mode will configure the AssetPlugin to use asset processing.
// This will run the AssetProcessor in the background, which will listen for changes to // If you also enable the `asset_processor` cargo feature, this will run the AssetProcessor
// the `assets` folder, run them through configured asset processors, and write the results // in the background, run them through configured asset processors, and write the results to
// to the `imported_assets` folder. // the `imported_assets` folder. If you also enable the `file_watcher` cargo feature, changes to the
// source assets will be detected and they will be reprocessed.
// //
// The AssetProcessor will create `.meta` files automatically for assets in the `assets` folder, // The AssetProcessor will create `.meta` files automatically for assets in the `assets` folder,
// which can then be used to configure how the asset will be processed. // which can then be used to configure how the asset will be processed.
.add_plugins(( .add_plugins((
DefaultPlugins.set(AssetPlugin { DefaultPlugins.set(AssetPlugin {
mode: AssetMode::Processed,
// This is just overriding the default paths to scope this to the correct example folder // This is just overriding the default paths to scope this to the correct example folder
// You can generally skip this in your own projects // You can generally skip this in your own projects
mode: AssetMode::ProcessedDev,
file_path: "examples/asset/processing/assets".to_string(), file_path: "examples/asset/processing/assets".to_string(),
processed_file_path: "examples/asset/processing/imported_assets/Default" processed_file_path: "examples/asset/processing/imported_assets/Default"
.to_string(), .to_string(),