use crate::{RenderApp, RenderStage}; use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetEvent, Assets, Handle}; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, RunSystem, SystemParam, SystemParamItem}, }; use bevy_utils::{HashMap, HashSet}; use std::marker::PhantomData; pub enum PrepareAssetError { RetryNextUpdate(E), } pub trait RenderAsset: Asset { type ExtractedAsset: Send + Sync + 'static; type PreparedAsset: Send + Sync + 'static; type Param: SystemParam; fn extract_asset(&self) -> Self::ExtractedAsset; fn prepare_asset( extracted_asset: Self::ExtractedAsset, param: &mut SystemParamItem, ) -> Result>; } /// Extracts assets into gpu-usable data pub struct RenderAssetPlugin(PhantomData A>); impl Default for RenderAssetPlugin { fn default() -> Self { Self(PhantomData) } } impl Plugin for RenderAssetPlugin { fn build(&self, app: &mut App) { let render_app = app.sub_app(RenderApp); let prepare_asset_system = PrepareAssetSystem::::system(&mut render_app.world); render_app .init_resource::>() .init_resource::>() .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_render_asset::) .add_system_to_stage(RenderStage::Prepare, prepare_asset_system); } } pub struct ExtractedAssets { extracted: Vec<(Handle, A::ExtractedAsset)>, removed: Vec>, } impl Default for ExtractedAssets { fn default() -> Self { Self { extracted: Default::default(), removed: Default::default(), } } } pub type RenderAssets = HashMap, ::PreparedAsset>; fn extract_render_asset( mut commands: Commands, mut events: EventReader>, assets: Res>, ) { let mut changed_assets = HashSet::default(); let mut removed = Vec::new(); for event in events.iter() { match event { AssetEvent::Created { handle } => { changed_assets.insert(handle); } AssetEvent::Modified { handle } => { changed_assets.insert(handle); } AssetEvent::Removed { handle } => { if !changed_assets.remove(handle) { removed.push(handle.clone_weak()); } } } } let mut extracted_assets = Vec::new(); for handle in changed_assets.drain() { if let Some(asset) = assets.get(handle) { extracted_assets.push((handle.clone_weak(), asset.extract_asset())); } } commands.insert_resource(ExtractedAssets { extracted: extracted_assets, removed, }) } pub type RenderAssetParams = ( SResMut>, SResMut>, SResMut>, ::Param, ); // TODO: consider storing inside system? pub struct PrepareNextFrameAssets { assets: Vec<(Handle, A::ExtractedAsset)>, } impl Default for PrepareNextFrameAssets { fn default() -> Self { Self { assets: Default::default(), } } } pub struct PrepareAssetSystem(PhantomData); impl RunSystem for PrepareAssetSystem { type Param = RenderAssetParams; fn run( (mut extracted_assets, mut render_assets, mut prepare_next_frame, mut param): SystemParamItem, ) { let mut queued_assets = std::mem::take(&mut prepare_next_frame.assets); for (handle, extracted_asset) in queued_assets.drain(..) { match R::prepare_asset(extracted_asset, &mut param) { Ok(prepared_asset) => { render_assets.insert(handle, prepared_asset); } Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => { prepare_next_frame.assets.push((handle, extracted_asset)); } } } for removed in std::mem::take(&mut extracted_assets.removed) { render_assets.remove(&removed); } for (handle, extracted_asset) in std::mem::take(&mut extracted_assets.extracted) { match R::prepare_asset(extracted_asset, &mut param) { Ok(prepared_asset) => { render_assets.insert(handle, prepared_asset); } Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => { prepare_next_frame.assets.push((handle, extracted_asset)); } } } } }