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.
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
file_watcher = ["bevy_internal/file_watcher"]
@ -1080,7 +1083,7 @@ wasm = true
name = "asset_processing"
path = "examples/asset/processing/processing.rs"
doc-scrape-examples = true
required-features = ["file_watcher"]
required-features = ["file_watcher", "asset_processor"]
[package.metadata.example.asset_processing]
name = "Asset Processing"

View file

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

View file

@ -41,7 +41,7 @@ use crate::{
io::{embedded::EmbeddedAssetRegistry, AssetSourceBuilder, AssetSourceBuilders, AssetSourceId},
processor::{AssetProcessor, Process},
};
use bevy_app::{App, First, MainScheduleOrder, Plugin, PostUpdate, Startup};
use bevy_app::{App, First, MainScheduleOrder, Plugin, PostUpdate};
use bevy_ecs::{
reflect::AppTypeRegistry,
schedule::{IntoSystemConfigs, IntoSystemSetConfigs, ScheduleLabel, SystemSet},
@ -80,21 +80,21 @@ pub enum AssetMode {
/// [`AssetReader`]: crate::io::AssetReader
/// [`AssetSource`]: crate::io::AssetSource
Unprocessed,
/// Loads assets from their final processed [`AssetReader`]. This should generally only be used when distributing apps.
/// Use [`AssetMode::ProcessedDev`] to develop apps that process assets, then switch to [`AssetMode::Processed`] when deploying the apps.
/// Assets will be "pre-processed". This enables assets to be imported / converted / optimized ahead of time.
///
/// [`AssetReader`]: crate::io::AssetReader
Processed,
/// 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).
/// Assets will be read from their unprocessed [`AssetSource`] (defaults to the `assets` folder),
/// processed according to their [`AssetMeta`], and written 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
/// [`AssetSource`]: crate::io::AssetSource
ProcessedDev,
/// [`AssetReader`]: crate::io::AssetReader
Processed,
}
impl Default for AssetPlugin {
@ -146,28 +146,32 @@ impl Plugin for AssetPlugin {
));
}
AssetMode::Processed => {
let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
let sources = builders.build_sources(false, watch);
app.insert_resource(AssetServer::new(
sources,
AssetServerMode::Processed,
watch,
));
}
AssetMode::ProcessedDev => {
let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
let processor = AssetProcessor::new(&mut builders);
let mut sources = builders.build_sources(false, 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(
sources,
processor.server().data.loaders.clone(),
AssetServerMode::Processed,
watch,
))
.insert_resource(processor)
.add_systems(Startup, AssetProcessor::start);
#[cfg(feature = "asset_processor")]
{
let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
let processor = AssetProcessor::new(&mut builders);
let mut sources = builders.build_sources(false, 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(
sources,
processor.server().data.loaders.clone(),
AssetServerMode::Processed,
watch,
))
.insert_resource(processor)
.add_systems(bevy_app::Startup, AssetProcessor::start);
}
#[cfg(not(feature = "asset_processor"))]
{
let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
let sources = builders.build_sources(false, watch);
app.insert_resource(AssetServer::new(
sources,
AssetServerMode::Processed,
watch,
));
}
}
}
}

View file

@ -102,6 +102,9 @@ glam_assert = ["bevy_math/glam_assert"]
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
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|
|-|-|
|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.|
|basis-universal|Basis Universal compressed texture support|
|bevy_ci_testing|Enable systems that allow for automated testing on CI|

View file

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