Skip to content

Commit

Permalink
docs: Full documentation for bevy_asset (bevyengine#3536)
Browse files Browse the repository at this point in the history
# Objective

This PR aims to document the `bevy_asset` crate to complete coverage, while also trying to improve some bits of UX.

### Progress

- [x] Root items
- [x] `handle` module
- [x] `info` module
- [x] `path` module
- [x] `loader` module
- [x] `io` and `filesystem_watcher` module
- [x] `assets` module
- [x] `asset_server` module
- [x] `diagnostic` module
- [x] `debug_asset_server` module
- [x] Crate level documentation
- [x] Add `#![warn(missing_docs)]` lint

Coverage: 100%

## Migration Guide

- Rename `FileAssetIo::get_root_path` uses to `FileAssetIo::get_base_path`

    `FileAssetIo::root_path()` is a getter for the `root_path` field, while `FileAssetIo::get_root_path` returned the parent directory of the asset root path, which was the executable's directory unless `CARGO_MANIFEST_DIR` was set. This change solves the ambiguity between the two methods.
  • Loading branch information
manokara authored and james7132 committed Oct 28, 2022
1 parent f56c101 commit 73a498c
Show file tree
Hide file tree
Showing 16 changed files with 393 additions and 60 deletions.
92 changes: 85 additions & 7 deletions crates/bevy_asset/src/asset_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,31 @@ use parking_lot::{Mutex, RwLock};
use std::{path::Path, sync::Arc};
use thiserror::Error;

/// Errors that occur while loading assets with an `AssetServer`
/// Errors that occur while loading assets with an `AssetServer`.
#[derive(Error, Debug)]
pub enum AssetServerError {
/// Asset folder is not a directory.
#[error("asset folder path is not a directory: {0}")]
AssetFolderNotADirectory(String),

/// No asset loader was found for the specified extensions.
#[error("no `AssetLoader` found{}", format_missing_asset_ext(.extensions))]
MissingAssetLoader { extensions: Vec<String> },
MissingAssetLoader {
/// The list of extensions detected on the asset source path that failed to load.
///
/// The list may be empty if the asset path is invalid or doesn't have an extension.
extensions: Vec<String>,
},

/// The handle type does not match the type of the loaded asset.
#[error("the given type does not match the type of the loaded asset")]
IncorrectHandleType,

/// Encountered an error while processing an asset.
#[error("encountered an error while loading an asset: {0}")]
AssetLoaderError(anyhow::Error),

/// Encountered an error while reading an asset from disk.
#[error("encountered an error while reading an asset: {0}")]
AssetIoError(#[from] AssetIoError),
}
Expand All @@ -48,6 +62,9 @@ pub(crate) struct AssetRefCounter {
pub(crate) mark_unused_assets: Arc<Mutex<Vec<HandleId>>>,
}

/// Internal data for the asset server.
///
/// [`AssetServer`] is the public API for interacting with the asset server.
pub struct AssetServerInternal {
pub(crate) asset_io: Box<dyn AssetIo>,
pub(crate) asset_ref_counter: AssetRefCounter,
Expand All @@ -58,17 +75,45 @@ pub struct AssetServerInternal {
handle_to_path: Arc<RwLock<HashMap<HandleId, AssetPath<'static>>>>,
}

/// Loads assets from the filesystem on background threads
/// Loads assets from the filesystem in the background.
///
/// The asset server is the primary way of loading assets in bevy. It keeps track of the load state
/// of the assets it manages and can even reload them from the filesystem with
/// [`AssetServer::watch_for_changes`]!
///
/// The asset server is a _resource_, so in order to accesss it in a system you need a `Res`
/// accessor, like this:
///
/// ```rust,no_run
/// use bevy_asset::{AssetServer, Handle};
/// use bevy_ecs::prelude::{Commands, Res};
///
/// # #[derive(Debug, bevy_reflect::TypeUuid)]
/// # #[uuid = "00000000-0000-0000-0000-000000000000"]
/// # struct Image;
///
/// fn my_system(mut commands: Commands, asset_server: Res<AssetServer>)
/// {
/// // Now you can do whatever you want with the asset server, such as loading an asset:
/// let asset_handle: Handle<Image> = asset_server.load("cool_picture.png");
/// }
/// ```
///
/// See the [`asset_loading`] example for more information.
///
/// [`asset_loading`]: https://github.com/bevyengine/bevy/tree/latest/examples/asset/asset_loading.rs
#[derive(Clone)]
pub struct AssetServer {
pub(crate) server: Arc<AssetServerInternal>,
}

impl AssetServer {
/// Creates a new asset server with the provided asset I/O.
pub fn new<T: AssetIo>(source_io: T) -> Self {
Self::with_boxed_io(Box::new(source_io))
}

/// Creates a new asset server with a boxed asset I/O.
pub fn with_boxed_io(asset_io: Box<dyn AssetIo>) -> Self {
AssetServer {
server: Arc::new(AssetServerInternal {
Expand All @@ -83,6 +128,7 @@ impl AssetServer {
}
}

/// Returns the associated asset I/O.
pub fn asset_io(&self) -> &dyn AssetIo {
&*self.server.asset_io
}
Expand All @@ -104,6 +150,10 @@ impl AssetServer {
Assets::new(self.server.asset_ref_counter.channel.sender.clone())
}

/// Adds the provided asset loader to the server.
///
/// If `loader` has one or more supported extensions in conflict with loaders that came before
/// it, it will replace them.
pub fn add_loader<T>(&self, loader: T)
where
T: AssetLoader,
Expand All @@ -126,11 +176,13 @@ impl AssetServer {
Ok(())
}

/// Gets a strong handle for an asset with the provided id.
pub fn get_handle<T: Asset, I: Into<HandleId>>(&self, id: I) -> Handle<T> {
let sender = self.server.asset_ref_counter.channel.sender.clone();
Handle::strong(id.into(), sender)
}

/// Gets an untyped strong handle for an asset with the provided id.
pub fn get_handle_untyped<I: Into<HandleId>>(&self, id: I) -> HandleUntyped {
let sender = self.server.asset_ref_counter.channel.sender.clone();
HandleUntyped::strong(id.into(), sender)
Expand Down Expand Up @@ -179,6 +231,7 @@ impl AssetServer {
})
}

/// Gets the source path of an asset from the provided handle.
pub fn get_handle_path<H: Into<HandleId>>(&self, handle: H) -> Option<AssetPath<'_>> {
self.server
.handle_to_path
Expand All @@ -187,6 +240,7 @@ impl AssetServer {
.cloned()
}

/// Gets the load state of an asset from the provided handle.
pub fn get_load_state<H: Into<HandleId>>(&self, handle: H) -> LoadState {
match handle.into() {
HandleId::AssetPathId(id) => {
Expand All @@ -199,6 +253,10 @@ impl AssetServer {
}
}

/// Gets the overall load state of a group of assets from the provided handles.
///
/// This method will only return [`LoadState::Loaded`] if all assets in the
/// group were loaded succesfully.
pub fn get_group_load_state(&self, handles: impl IntoIterator<Item = HandleId>) -> LoadState {
let mut load_state = LoadState::Loaded;
for handle_id in handles {
Expand All @@ -219,11 +277,14 @@ impl AssetServer {
load_state
}

/// Queue an [`Asset`] at the provided relative path for asynchronous loading.
/// Queues an [`Asset`] at the provided relative path for asynchronous loading.
///
/// The absolute Path to the asset is `"ROOT/ASSET_FOLDER_NAME/path"`.
/// The absolute path to the asset is `"ROOT/ASSET_FOLDER_NAME/path"`. Its extension is then
/// extracted to search for an [asset loader]. If an asset path contains multiple dots (e.g.
/// `foo.bar.baz`), each level is considered a separate extension and the asset server will try
/// to look for loaders of `bar.baz` and `baz` assets.
///
/// By default the ROOT is the directory of the Application, but this can be overridden by
/// By default the `ROOT` is the directory of the Application, but this can be overridden by
/// setting the `"CARGO_MANIFEST_DIR"` environment variable
/// (see <https://doc.rust-lang.org/cargo/reference/environment-variables.html>)
/// to another directory. When the application is run through Cargo, then
Expand All @@ -235,7 +296,10 @@ impl AssetServer {
///
/// The asset is loaded asynchronously, and will generally not be available by the time
/// this calls returns. Use [`AssetServer::get_load_state`] to determine when the asset is
/// effectively loaded and available in the [`Assets`] collection.
/// effectively loaded and available in the [`Assets`] collection. The asset will always fail to
/// load if the provided path doesn't contain an extension.
///
/// [asset loader]: AssetLoader
#[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
pub fn load<'a, T: Asset, P: Into<AssetPath<'a>>>(&self, path: P) -> Handle<T> {
self.load_untyped(path).typed()
Expand Down Expand Up @@ -365,6 +429,9 @@ impl AssetServer {
Ok(asset_path_id)
}

/// Queues the [`Asset`] at the provided path for loading and returns an untyped handle.
///
/// See [`load`](AssetServer::load).
#[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
pub fn load_untyped<'a, P: Into<AssetPath<'a>>>(&self, path: P) -> HandleUntyped {
let handle_id = self.load_untracked(path.into(), false);
Expand Down Expand Up @@ -400,6 +467,14 @@ impl AssetServer {
asset_path.into()
}

/// Loads assets from the specified folder recursively.
///
/// # Errors
///
/// - If the provided path is not a directory, it will fail with
/// [`AssetServerError::AssetFolderNotADirectory`].
/// - If something unexpected happened while loading an asset, other
/// [`AssetServerError`]s may be returned.
#[must_use = "not using the returned strong handles may result in the unexpected release of the assets"]
pub fn load_folder<P: AsRef<Path>>(
&self,
Expand Down Expand Up @@ -429,6 +504,7 @@ impl AssetServer {
Ok(handles)
}

/// Frees unused assets, unloading them from memory.
pub fn free_unused_assets(&self) {
let mut potential_frees = self.server.asset_ref_counter.mark_unused_assets.lock();

Expand All @@ -455,6 +531,7 @@ impl AssetServer {
}
}

/// Iterates through asset references and marks assets with no active handles as unused.
pub fn mark_unused_assets(&self) {
let receiver = &self.server.asset_ref_counter.channel.receiver;
let mut ref_counts = self.server.asset_ref_counter.ref_counts.write();
Expand Down Expand Up @@ -559,6 +636,7 @@ fn free_unused_assets_system_impl(asset_server: &AssetServer) {
asset_server.mark_unused_assets();
}

/// A system for freeing assets that have no active handles.
pub fn free_unused_assets_system(asset_server: Res<AssetServer>) {
free_unused_assets_system_impl(&asset_server);
}
Expand Down
Loading

0 comments on commit 73a498c

Please sign in to comment.