1use crate::assets::deps::Dependencies;
2
3pub trait AssetSource: 'static {
7 type Processed: 'static;
8}
9
10pub trait Asset<B>: AssetSource {
15 type Deps<'a>: Dependencies<'a>;
16
17 fn upload<'a>(&self, backend: &B, deps: &Self::Deps<'a>) -> Option<Self::Processed>;
18}
19
20#[macro_export]
38macro_rules! asset {
39 ($ty:ty => $processed:ty, |$self:ident, $backend:ident : &$backend_ty:ty| $body:block) => {
40 impl $crate::assets::upload::AssetSource for $ty {
41 type Processed = $processed;
42 }
43 impl $crate::assets::upload::Asset<$backend_ty> for $ty {
44 type Deps<'a> = ();
45
46 fn upload<'a>(&$self, $backend: &$backend_ty, _deps: &()) -> Option<$processed> $body
47 }
48 };
49
50 ($ty:ty => $processed:ty, deps: [$dep:ty], |$self:ident, $backend:ident : &$backend_ty:ty, $deps:ident| $body:block) => {
51 impl $crate::assets::upload::AssetSource for $ty {
52 type Processed = $processed;
53 }
54 impl $crate::assets::upload::Asset<$backend_ty> for $ty {
55 type Deps<'a> = $crate::ecs::resources::Read<'a, $dep>;
56
57 fn upload<'a>(&$self, $backend: &$backend_ty, $deps: &Self::Deps<'a>) -> Option<$processed> $body
58 }
59 };
60
61 ($ty:ty => $processed:ty, deps: [$($dep:ty),+ $(,)?], |$self:ident, $backend:ident : &$backend_ty:ty, $deps:ident| $body:block) => {
62 impl $crate::assets::upload::AssetSource for $ty {
63 type Processed = $processed;
64 }
65 impl $crate::assets::upload::Asset<$backend_ty> for $ty {
66 type Deps<'a> = ($($crate::ecs::resources::Read<'a, $dep>,)+);
67
68 fn upload<'a>(&$self, $backend: &$backend_ty, $deps: &Self::Deps<'a>) -> Option<$processed> $body
69 }
70 };
71}
72
73#[cfg(test)]
74mod tests {
75 use crate::{
76 assets::{plugin::sync_assets, storage::Assets},
77 ecs::{resources::Resources, schedule::Schedule},
78 };
79
80 struct TestBackend(i32);
81 struct DepA(i32);
82 struct DepB(i32);
83
84 struct NoDepsAsset(i32);
85 crate::asset!(NoDepsAsset => i32, |self, backend: &TestBackend| {
86 Some(self.0 * backend.0)
87 });
88
89 struct OneDepAsset(i32);
90 crate::asset!(OneDepAsset => i32, deps: [DepA], |self, backend: &TestBackend, deps| {
91 Some(self.0 * backend.0 * deps.0)
92 });
93
94 struct TwoDepAsset(i32);
95 crate::asset!(TwoDepAsset => i32, deps: [DepA, DepB], |self, backend: &TestBackend, deps| {
96 Some(self.0 * backend.0 * deps.0.0 * deps.1.0)
97 });
98
99 #[test]
100 fn no_deps_asset_uploads_once_the_backend_exists() {
101 let mut world = hecs::World::default();
102 let mut resources = Resources::default();
103 resources.insert(hecs::CommandBuffer::default());
104 resources.insert(Assets::<NoDepsAsset>::new());
105
106 let handle = resources.get_mut::<Assets<NoDepsAsset>>().insert("test", NoDepsAsset(3));
107
108 let mut schedule = Schedule::default();
109 schedule.add_system(sync_assets::<TestBackend, NoDepsAsset>);
110
111 schedule.run(&mut world, &mut resources);
112 assert!(!resources.get::<Assets<NoDepsAsset>>().is_ready(handle));
113
114 resources.insert(TestBackend(2));
115 schedule.run(&mut world, &mut resources);
116 assert_eq!(*resources.get::<Assets<NoDepsAsset>>().get(handle).unwrap(), 6);
117 }
118
119 #[test]
120 fn one_dep_asset_waits_for_its_dependency() {
121 let mut world = hecs::World::default();
122 let mut resources = Resources::default();
123 resources.insert(hecs::CommandBuffer::default());
124 resources.insert(Assets::<OneDepAsset>::new());
125 resources.insert(TestBackend(2));
126
127 let handle = resources.get_mut::<Assets<OneDepAsset>>().insert("test", OneDepAsset(3));
128
129 let mut schedule = Schedule::default();
130 schedule.add_system(sync_assets::<TestBackend, OneDepAsset>);
131
132 schedule.run(&mut world, &mut resources);
133 assert!(!resources.get::<Assets<OneDepAsset>>().is_ready(handle));
134
135 resources.insert(DepA(5));
136 schedule.run(&mut world, &mut resources);
137 assert_eq!(*resources.get::<Assets<OneDepAsset>>().get(handle).unwrap(), 30);
138 }
139
140 #[test]
141 fn two_dep_asset_waits_for_both_dependencies() {
142 let mut world = hecs::World::default();
143 let mut resources = Resources::default();
144 resources.insert(hecs::CommandBuffer::default());
145 resources.insert(Assets::<TwoDepAsset>::new());
146 resources.insert(TestBackend(2));
147 resources.insert(DepA(5));
148
149 let handle = resources.get_mut::<Assets<TwoDepAsset>>().insert("test", TwoDepAsset(3));
150
151 let mut schedule = Schedule::default();
152 schedule.add_system(sync_assets::<TestBackend, TwoDepAsset>);
153
154 schedule.run(&mut world, &mut resources);
155 assert!(!resources.get::<Assets<TwoDepAsset>>().is_ready(handle));
156
157 resources.insert(DepB(7));
158 schedule.run(&mut world, &mut resources);
159 assert_eq!(*resources.get::<Assets<TwoDepAsset>>().get(handle).unwrap(), 210);
160 }
161}