-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
asset hooks #12077
base: main
Are you sure you want to change the base?
asset hooks #12077
Changes from all commits
cb92852
0879383
c4d3510
535ec11
55be783
91eaaa0
462f1fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,8 +5,8 @@ use crate::{ | |||||||||||||
Settings, | ||||||||||||||
}, | ||||||||||||||
path::AssetPath, | ||||||||||||||
Asset, AssetLoadError, AssetServer, AssetServerMode, Assets, Handle, LoadedUntypedAsset, | ||||||||||||||
UntypedAssetId, UntypedHandle, | ||||||||||||||
Asset, AssetHooks, AssetLoadError, AssetServer, AssetServerMode, Assets, Handle, | ||||||||||||||
LoadedUntypedAsset, UntypedAssetId, UntypedHandle, | ||||||||||||||
}; | ||||||||||||||
use bevy_ecs::world::World; | ||||||||||||||
use bevy_utils::{BoxedFuture, CowArc, HashMap, HashSet}; | ||||||||||||||
|
@@ -234,6 +234,9 @@ impl ErasedLoadedAsset { | |||||||||||||
pub trait AssetContainer: Downcast + Any + Send + Sync + 'static { | ||||||||||||||
fn insert(self: Box<Self>, id: UntypedAssetId, world: &mut World); | ||||||||||||||
fn asset_type_name(&self) -> &'static str; | ||||||||||||||
|
||||||||||||||
/// Exists such that we can determine the type statically for the loading hook from an asset that has an erased type. | ||||||||||||||
fn load_hook(&mut self, asset_hooks: &mut AssetHooks, world: &mut World); | ||||||||||||||
Comment on lines
+238
to
+239
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl_downcast!(AssetContainer); | ||||||||||||||
|
@@ -246,6 +249,10 @@ impl<A: Asset> AssetContainer for A { | |||||||||||||
fn asset_type_name(&self) -> &'static str { | ||||||||||||||
std::any::type_name::<A>() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn load_hook(&mut self, asset_hooks: &mut AssetHooks, world: &mut World) { | ||||||||||||||
asset_hooks.trigger::<A>(self, world); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+253
to
+255
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/// An error that occurs when attempting to call [`LoadContext::load_direct`] | ||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -20,13 +20,15 @@ use crate::{ | |||||||||||||||||||||||||||||||
use bevy_ecs::prelude::*; | ||||||||||||||||||||||||||||||||
use bevy_log::{error, info}; | ||||||||||||||||||||||||||||||||
use bevy_tasks::IoTaskPool; | ||||||||||||||||||||||||||||||||
use bevy_utils::{CowArc, HashSet}; | ||||||||||||||||||||||||||||||||
use bevy_utils::{hashbrown, CowArc, HashSet}; | ||||||||||||||||||||||||||||||||
use crossbeam_channel::{Receiver, Sender}; | ||||||||||||||||||||||||||||||||
use futures_lite::StreamExt; | ||||||||||||||||||||||||||||||||
use info::*; | ||||||||||||||||||||||||||||||||
use loaders::*; | ||||||||||||||||||||||||||||||||
use parking_lot::RwLock; | ||||||||||||||||||||||||||||||||
use std::ops::{Deref, DerefMut}; | ||||||||||||||||||||||||||||||||
use std::path::PathBuf; | ||||||||||||||||||||||||||||||||
use std::sync::atomic::{AtomicBool, Ordering}; | ||||||||||||||||||||||||||||||||
use std::{any::TypeId, path::Path, sync::Arc}; | ||||||||||||||||||||||||||||||||
use thiserror::Error; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
@@ -58,6 +60,108 @@ pub(crate) struct AssetServerData { | |||||||||||||||||||||||||||||||
sources: AssetSources, | ||||||||||||||||||||||||||||||||
mode: AssetServerMode, | ||||||||||||||||||||||||||||||||
meta_check: AssetMetaCheck, | ||||||||||||||||||||||||||||||||
pub(crate) hooks: RwLock<AssetHooks>, | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
struct TypeMap(hashbrown::HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>); | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned, the additions to this file should really be moved in a separate module. I notice that the key type for |
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
impl TypeMap { | ||||||||||||||||||||||||||||||||
pub fn set<T: std::any::Any + Send + Sync + 'static>(&mut self, t: T) { | ||||||||||||||||||||||||||||||||
self.0.insert(TypeId::of::<T>(), Box::new(t)); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
pub fn has<T: 'static + Send + Sync + std::any::Any>(&self) -> bool { | ||||||||||||||||||||||||||||||||
self.0.contains_key(&TypeId::of::<T>()) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
pub fn get_mut<T: 'static + Send + Sync + std::any::Any>(&mut self) -> Option<&mut T> { | ||||||||||||||||||||||||||||||||
self.0 | ||||||||||||||||||||||||||||||||
.get_mut(&TypeId::of::<T>()) | ||||||||||||||||||||||||||||||||
.map(|t| t.downcast_mut::<T>().unwrap()) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
#[allow(clippy::unused_unit)] | ||||||||||||||||||||||||||||||||
Comment on lines
+81
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
type AssetHookVec<A> = | ||||||||||||||||||||||||||||||||
Vec<Box<dyn FnMut(&mut A, &mut bevy_ecs::world::World) -> () + Send + Sync + 'static>>; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
pub struct AssetHooks(TypeMap); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
impl Default for AssetHooks { | ||||||||||||||||||||||||||||||||
fn default() -> Self { | ||||||||||||||||||||||||||||||||
Self(TypeMap(hashbrown::HashMap::new())) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
impl<A: Asset> AssetWrapper<A> { | ||||||||||||||||||||||||||||||||
fn new(asset: &mut A) -> Self { | ||||||||||||||||||||||||||||||||
Self { | ||||||||||||||||||||||||||||||||
ptr: asset as *mut A, | ||||||||||||||||||||||||||||||||
flag: Default::default(), | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
/// Safe wrapper around asset pointers to allow for passing into systems. | ||||||||||||||||||||||||||||||||
Comment on lines
+101
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
pub struct AssetWrapper<A: Asset> { | ||||||||||||||||||||||||||||||||
ptr: *mut A, | ||||||||||||||||||||||||||||||||
flag: Arc<AtomicBool>, | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
Comment on lines
+103
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Also, the struct definition should go above it's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
impl<A: Asset> Deref for AssetWrapper<A> { | ||||||||||||||||||||||||||||||||
type Target = A; | ||||||||||||||||||||||||||||||||
fn deref(&self) -> &A { | ||||||||||||||||||||||||||||||||
// SAFETY: This is safe because we ensure that we flag right after the system that gets the asset | ||||||||||||||||||||||||||||||||
// that it has dropped the AssetWrapper. If it hasn't we panic. | ||||||||||||||||||||||||||||||||
unsafe { &*self.ptr } | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
Comment on lines
+111
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
impl<A: Asset> DerefMut for AssetWrapper<A> { | ||||||||||||||||||||||||||||||||
fn deref_mut(&mut self) -> &mut A { | ||||||||||||||||||||||||||||||||
// SAFETY: This is safe because we ensure that we flag right after the system that gets the asset | ||||||||||||||||||||||||||||||||
// that it has dropped the AssetWrapper. If it hasn't we panic. | ||||||||||||||||||||||||||||||||
unsafe { &mut *self.ptr } | ||||||||||||||||||||||||||||||||
Comment on lines
+119
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
impl<A: Asset> Drop for AssetWrapper<A> { | ||||||||||||||||||||||||||||||||
fn drop(&mut self) { | ||||||||||||||||||||||||||||||||
self.flag.store(true, Ordering::Release); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
impl AssetHooks { | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||||||||||||||||||||||||||||||||
fn add_asset_hook<A, Out, Marker, F>(&mut self, f: F) | ||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||
A: Asset, | ||||||||||||||||||||||||||||||||
F: IntoSystem<AssetWrapper<A>, Out, Marker> + Sync + Send + 'static + Clone, | ||||||||||||||||||||||||||||||||
Comment on lines
+132
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There should not be an output. Also |
||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||
if !self.0.has::<AssetHookVec<A>>() { | ||||||||||||||||||||||||||||||||
self.0.set::<AssetHookVec<A>>(Vec::new()); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
Comment on lines
+137
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This, followed by the |
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
let hooks = self.0.get_mut::<AssetHookVec<A>>().unwrap(); | ||||||||||||||||||||||||||||||||
hooks.push(Box::new(move |asset, world| { | ||||||||||||||||||||||||||||||||
let mut system: F::System = IntoSystem::into_system(f.clone()); | ||||||||||||||||||||||||||||||||
system.initialize(world); | ||||||||||||||||||||||||||||||||
let asset_wrapper = AssetWrapper::new(asset); | ||||||||||||||||||||||||||||||||
Comment on lines
+141
to
+145
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Avoid cloning.
Comment on lines
+143
to
+145
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like the initialization logic shouldn't be ran in the closure, but rather in the |
||||||||||||||||||||||||||||||||
let flag = asset_wrapper.flag.clone(); | ||||||||||||||||||||||||||||||||
system.run(asset_wrapper, world); | ||||||||||||||||||||||||||||||||
if !flag.load(Ordering::Acquire) { | ||||||||||||||||||||||||||||||||
panic!("tried to hold asset pointer past lifetime"); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
system.apply_deferred(world); | ||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
pub(crate) fn trigger<A>(&mut self, asset: &mut A, world: &mut World) | ||||||||||||||||||||||||||||||||
Comment on lines
+153
to
+154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||
A: Asset, | ||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||
let Some(hooks) = self.0.get_mut::<AssetHookVec<A>>() else { | ||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||
for hook in hooks { | ||||||||||||||||||||||||||||||||
hook(asset, world); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/// The "asset mode" the server is currently in. | ||||||||||||||||||||||||||||||||
|
@@ -118,6 +222,7 @@ impl AssetServer { | |||||||||||||||||||||||||||||||
asset_event_receiver, | ||||||||||||||||||||||||||||||||
loaders, | ||||||||||||||||||||||||||||||||
infos: RwLock::new(infos), | ||||||||||||||||||||||||||||||||
hooks: RwLock::new(AssetHooks::default()), | ||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
@@ -140,6 +245,13 @@ impl AssetServer { | |||||||||||||||||||||||||||||||
self.data.loaders.write().push(loader); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
pub fn register_asset_hook<A: Asset, Out, Marker>( | ||||||||||||||||||||||||||||||||
&self, | ||||||||||||||||||||||||||||||||
hook: impl IntoSystem<AssetWrapper<A>, Out, Marker> + Sync + Send + 'static + Clone, | ||||||||||||||||||||||||||||||||
Comment on lines
+248
to
+250
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||
self.data.hooks.write().add_asset_hook(hook); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/// Registers a new [`Asset`] type. [`Asset`] types must be registered before assets of that type can be loaded. | ||||||||||||||||||||||||||||||||
pub fn register_asset<A: Asset>(&self, assets: &Assets<A>) { | ||||||||||||||||||||||||||||||||
self.register_handle_provider(assets.get_handle_provider()); | ||||||||||||||||||||||||||||||||
|
@@ -590,6 +702,7 @@ impl AssetServer { | |||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
pub(crate) fn load_asset<A: Asset>(&self, asset: impl Into<LoadedAsset<A>>) -> Handle<A> { | ||||||||||||||||||||||||||||||||
let loaded_asset: LoadedAsset<A> = asset.into(); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
let erased_loaded_asset: ErasedLoadedAsset = loaded_asset.into(); | ||||||||||||||||||||||||||||||||
Comment on lines
704
to
706
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
self.load_asset_untyped(None, erased_loaded_asset) | ||||||||||||||||||||||||||||||||
.typed_debug_checked() | ||||||||||||||||||||||||||||||||
|
@@ -1040,7 +1153,13 @@ pub fn handle_internal_asset_events(world: &mut World) { | |||||||||||||||||||||||||||||||
let mut untyped_failures = vec![]; | ||||||||||||||||||||||||||||||||
for event in server.data.asset_event_receiver.try_iter() { | ||||||||||||||||||||||||||||||||
match event { | ||||||||||||||||||||||||||||||||
InternalAssetEvent::Loaded { id, loaded_asset } => { | ||||||||||||||||||||||||||||||||
InternalAssetEvent::Loaded { | ||||||||||||||||||||||||||||||||
id, | ||||||||||||||||||||||||||||||||
mut loaded_asset, | ||||||||||||||||||||||||||||||||
} => { | ||||||||||||||||||||||||||||||||
loaded_asset | ||||||||||||||||||||||||||||||||
.value | ||||||||||||||||||||||||||||||||
.load_hook(&mut server.data.hooks.write(), world); | ||||||||||||||||||||||||||||||||
Comment on lines
+1160
to
+1162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
infos.process_asset_load( | ||||||||||||||||||||||||||||||||
id, | ||||||||||||||||||||||||||||||||
loaded_asset, | ||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.