mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 06:30:19 +00:00
Resource change tracking (#388)
* Add mutated tracker on resources and ChangedRes query for added or mutated resources. * ResMut:::new() now takes a reference to a 'mutated' flag in its archetype. * Change FetchResource so that get() returns an Option. Systems using Resources will only be called if all fetched Resources are Some(). This is done to implement ChangedRes, which is Some iff the Resource has been changed. * Add OrRes for a logical or in tuples of Resource queries. * Separate resource query get() in is_some() and get() methods for clarity * Remove unneeded unsafe * Change ResMut::new()
This commit is contained in:
parent
12deb0bd91
commit
4ef18e2608
7 changed files with 341 additions and 37 deletions
|
@ -11,7 +11,7 @@ pub use world::*;
|
|||
|
||||
pub mod prelude {
|
||||
pub use crate::{
|
||||
resource::{FromResources, Local, Res, ResMut, Resource, Resources},
|
||||
resource::{ChangedRes, FromResources, Local, OrRes, Res, ResMut, Resource, Resources},
|
||||
system::{
|
||||
Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System,
|
||||
},
|
||||
|
|
|
@ -11,6 +11,41 @@ use core::{
|
|||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A shared borrow of a Resource
|
||||
/// that will only return in a query if the Resource has been changed
|
||||
pub struct ChangedRes<'a, T: Resource> {
|
||||
value: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T: Resource> ChangedRes<'a, T> {
|
||||
/// Creates a reference cell to a Resource from a pointer
|
||||
///
|
||||
/// # Safety
|
||||
/// The pointer must have correct lifetime / storage
|
||||
pub unsafe fn new(value: NonNull<T>) -> Self {
|
||||
Self {
|
||||
value: &*value.as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Resource> UnsafeClone for ChangedRes<'a, T> {
|
||||
unsafe fn unsafe_clone(&self) -> Self {
|
||||
Self { value: self.value }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Resource> Send for ChangedRes<'_, T> {}
|
||||
unsafe impl<T: Resource> Sync for ChangedRes<'_, T> {}
|
||||
|
||||
impl<'a, T: Resource> Deref for ChangedRes<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared borrow of a Resource
|
||||
pub struct Res<'a, T: Resource> {
|
||||
value: &'a T,
|
||||
|
@ -55,6 +90,7 @@ impl<'a, T: Resource> Deref for Res<'a, T> {
|
|||
pub struct ResMut<'a, T: Resource> {
|
||||
_marker: PhantomData<&'a T>,
|
||||
value: *mut T,
|
||||
mutated: *mut bool,
|
||||
}
|
||||
|
||||
impl<'a, T: Resource> ResMut<'a, T> {
|
||||
|
@ -62,9 +98,10 @@ impl<'a, T: Resource> ResMut<'a, T> {
|
|||
///
|
||||
/// # Safety
|
||||
/// The pointer must have correct lifetime / storage / ownership
|
||||
pub unsafe fn new(value: NonNull<T>) -> Self {
|
||||
pub unsafe fn new(value: NonNull<T>, mutated: NonNull<bool>) -> Self {
|
||||
Self {
|
||||
value: value.as_ptr(),
|
||||
mutated: mutated.as_ptr(),
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +120,10 @@ impl<'a, T: Resource> Deref for ResMut<'a, T> {
|
|||
|
||||
impl<'a, T: Resource> DerefMut for ResMut<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.value }
|
||||
unsafe {
|
||||
*self.mutated = true;
|
||||
&mut *self.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,6 +131,7 @@ impl<'a, T: Resource> UnsafeClone for ResMut<'a, T> {
|
|||
unsafe fn unsafe_clone(&self) -> Self {
|
||||
Self {
|
||||
value: self.value,
|
||||
mutated: self.mutated,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +185,11 @@ pub trait FetchResource<'a>: Sized {
|
|||
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> Self::Item;
|
||||
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
unsafe fn is_some(_resources: &'a Resources, _system_id: Option<SystemId>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Resource> ResourceQuery for Res<'a, T> {
|
||||
|
@ -175,6 +221,40 @@ impl<'a, T: Resource> FetchResource<'a> for FetchResourceRead<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Resource> ResourceQuery for ChangedRes<'a, T> {
|
||||
type Fetch = FetchResourceChanged<T>;
|
||||
}
|
||||
|
||||
/// Fetches a shared resource reference
|
||||
pub struct FetchResourceChanged<T>(NonNull<T>);
|
||||
|
||||
impl<'a, T: Resource> FetchResource<'a> for FetchResourceChanged<T> {
|
||||
type Item = ChangedRes<'a, T>;
|
||||
|
||||
unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> Self::Item {
|
||||
ChangedRes::new(resources.get_unsafe_ref::<T>(ResourceIndex::Global))
|
||||
}
|
||||
|
||||
unsafe fn is_some(resources: &'a Resources, _system_id: Option<SystemId>) -> bool {
|
||||
let (added, mutated) = resources.get_unsafe_added_and_mutated::<T>(ResourceIndex::Global);
|
||||
*added.as_ptr() || *mutated.as_ptr()
|
||||
}
|
||||
|
||||
fn borrow(resources: &Resources) {
|
||||
resources.borrow::<T>();
|
||||
}
|
||||
|
||||
fn release(resources: &Resources) {
|
||||
resources.release::<T>();
|
||||
}
|
||||
|
||||
fn access() -> TypeAccess {
|
||||
let mut access = TypeAccess::default();
|
||||
access.immutable.insert(TypeId::of::<T>());
|
||||
access
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Resource> ResourceQuery for ResMut<'a, T> {
|
||||
type Fetch = FetchResourceWrite<T>;
|
||||
}
|
||||
|
@ -186,7 +266,8 @@ impl<'a, T: Resource> FetchResource<'a> for FetchResourceWrite<T> {
|
|||
type Item = ResMut<'a, T>;
|
||||
|
||||
unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> Self::Item {
|
||||
ResMut::new(resources.get_unsafe_ref::<T>(ResourceIndex::Global))
|
||||
let (value, mutated) = resources.get_unsafe_ref_with_mutated::<T>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
}
|
||||
|
||||
fn borrow(resources: &Resources) {
|
||||
|
@ -265,6 +346,11 @@ macro_rules! tuple_impl {
|
|||
($($name::get(resources, system_id),)*)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
unsafe fn is_some(resources: &'a Resources, system_id: Option<SystemId>) -> bool {
|
||||
true $(&& $name::is_some(resources, system_id))*
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
fn access() -> TypeAccess {
|
||||
let mut access = TypeAccess::default();
|
||||
|
@ -294,3 +380,113 @@ macro_rules! tuple_impl {
|
|||
}
|
||||
|
||||
smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
|
||||
|
||||
pub struct OrRes<T>(T);
|
||||
|
||||
pub struct FetchResourceOr<T>(NonNull<T>);
|
||||
|
||||
macro_rules! tuple_impl_or {
|
||||
($($name: ident),*) => {
|
||||
impl<'a, $($name: FetchResource<'a>),*> FetchResource<'a> for FetchResourceOr<($($name,)*)> {
|
||||
type Item = OrRes<($($name::Item,)*)>;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn borrow(resources: &Resources) {
|
||||
$($name::borrow(resources);)*
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn release(resources: &Resources) {
|
||||
$($name::release(resources);)*
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> Self::Item {
|
||||
OrRes(($($name::get(resources, system_id),)*))
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
unsafe fn is_some(resources: &'a Resources, system_id: Option<SystemId>) -> bool {
|
||||
false $(|| $name::is_some(resources, system_id))*
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
fn access() -> TypeAccess {
|
||||
let mut access = TypeAccess::default();
|
||||
$(access.union(&$name::access());)*
|
||||
access
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($name: ResourceQuery),*> ResourceQuery for OrRes<($($name,)*)> {
|
||||
type Fetch = FetchResourceOr<($($name::Fetch,)*)>;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn initialize(resources: &mut Resources, system_id: Option<SystemId>) {
|
||||
$($name::initialize(resources, system_id);)*
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[allow(non_snake_case)]
|
||||
impl<$($name: UnsafeClone),*> UnsafeClone for OrRes<($($name,)*)> {
|
||||
unsafe fn unsafe_clone(&self) -> Self {
|
||||
let OrRes(($($name,)*)) = self;
|
||||
OrRes(($($name.unsafe_clone(),)*))
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($name,)*> Deref for OrRes<($($name,)*)> {
|
||||
type Target = ($($name,)*);
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
smaller_tuples_too!(tuple_impl_or, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn changed_resource() {
|
||||
let mut resources = Resources::default();
|
||||
resources.insert(123);
|
||||
assert_eq!(
|
||||
resources.query::<ChangedRes<i32>>().as_deref(),
|
||||
Some(&(123 as i32))
|
||||
);
|
||||
resources.clear_trackers();
|
||||
assert_eq!(resources.query::<ChangedRes<i32>>().as_deref(), None);
|
||||
*resources.query::<ResMut<i32>>().unwrap() += 1;
|
||||
assert_eq!(
|
||||
resources.query::<ChangedRes<i32>>().as_deref(),
|
||||
Some(&(124 as i32))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or_changed_resource() {
|
||||
let mut resources = Resources::default();
|
||||
resources.insert(123);
|
||||
resources.insert(0.2);
|
||||
assert!(resources
|
||||
.query::<OrRes<(ChangedRes<i32>, ChangedRes<f64>)>>()
|
||||
.is_some(),);
|
||||
resources.clear_trackers();
|
||||
assert!(resources
|
||||
.query::<OrRes<(ChangedRes<i32>, ChangedRes<f64>)>>()
|
||||
.is_none(),);
|
||||
*resources.query::<ResMut<i32>>().unwrap() += 1;
|
||||
assert!(resources
|
||||
.query::<OrRes<(ChangedRes<i32>, ChangedRes<f64>)>>()
|
||||
.is_some(),);
|
||||
assert!(resources
|
||||
.query::<(ChangedRes<i32>, ChangedRes<f64>)>()
|
||||
.is_none(),);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,36 +140,93 @@ impl Resources {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn query<Q: ResourceQuery>(&self) -> <Q::Fetch as FetchResource>::Item {
|
||||
unsafe { Q::Fetch::get(&self, None) }
|
||||
pub fn query<Q: ResourceQuery>(&self) -> Option<<Q::Fetch as FetchResource>::Item> {
|
||||
unsafe {
|
||||
if Q::Fetch::is_some(&self, None) {
|
||||
Some(Q::Fetch::get(&self, None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_system<Q: ResourceQuery>(
|
||||
&self,
|
||||
id: SystemId,
|
||||
) -> <Q::Fetch as FetchResource>::Item {
|
||||
unsafe { Q::Fetch::get(&self, Some(id)) }
|
||||
) -> Option<<Q::Fetch as FetchResource>::Item> {
|
||||
unsafe {
|
||||
if Q::Fetch::is_some(&self, Some(id)) {
|
||||
Some(Q::Fetch::get(&self, Some(id)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn get_unsafe_ref<T: Resource>(&self, resource_index: ResourceIndex) -> NonNull<T> {
|
||||
self.resource_data
|
||||
.get(&TypeId::of::<T>())
|
||||
.and_then(|data| {
|
||||
let index = match resource_index {
|
||||
ResourceIndex::Global => data.default_index?,
|
||||
ResourceIndex::System(id) => {
|
||||
data.system_id_to_archetype_index.get(&id.0).cloned()?
|
||||
}
|
||||
};
|
||||
self.get_resource_data_index::<T>(resource_index)
|
||||
.and_then(|(data, index)| {
|
||||
Some(NonNull::new_unchecked(
|
||||
data.archetype.get::<T>()?.as_ptr().add(index as usize),
|
||||
data.archetype.get::<T>()?.as_ptr().add(index),
|
||||
))
|
||||
})
|
||||
.unwrap_or_else(|| panic!("Resource does not exist {}", std::any::type_name::<T>()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn get_unsafe_ref_with_mutated<T: Resource>(
|
||||
&self,
|
||||
resource_index: ResourceIndex,
|
||||
) -> (NonNull<T>, NonNull<bool>) {
|
||||
self.get_resource_data_index::<T>(resource_index)
|
||||
.and_then(|(data, index)| {
|
||||
data.archetype
|
||||
.get_with_mutated::<T>()
|
||||
.map(|(resource, mutated)| {
|
||||
(
|
||||
NonNull::new_unchecked(resource.as_ptr().add(index)),
|
||||
NonNull::new_unchecked(mutated.as_ptr().add(index)),
|
||||
)
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| panic!("Resource does not exist {}", std::any::type_name::<T>()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn get_unsafe_added_and_mutated<T: Resource>(
|
||||
&self,
|
||||
resource_index: ResourceIndex,
|
||||
) -> (NonNull<bool>, NonNull<bool>) {
|
||||
self.get_resource_data_index::<T>(resource_index)
|
||||
.and_then(|(data, index)| {
|
||||
Some((
|
||||
NonNull::new_unchecked(data.archetype.get_added::<T>()?.as_ptr().add(index)),
|
||||
NonNull::new_unchecked(data.archetype.get_mutated::<T>()?.as_ptr().add(index)),
|
||||
))
|
||||
})
|
||||
.unwrap_or_else(|| panic!("Resource does not exist {}", std::any::type_name::<T>()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_resource_data_index<T: Resource>(
|
||||
&self,
|
||||
resource_index: ResourceIndex,
|
||||
) -> Option<(&ResourceData, usize)> {
|
||||
self.resource_data.get(&TypeId::of::<T>()).and_then(|data| {
|
||||
let index = match resource_index {
|
||||
ResourceIndex::Global => data.default_index?,
|
||||
ResourceIndex::System(id) => {
|
||||
data.system_id_to_archetype_index.get(&id.0).cloned()?
|
||||
}
|
||||
};
|
||||
Some((data, index as usize))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn borrow<T: Resource>(&self) {
|
||||
if let Some(data) = self.resource_data.get(&TypeId::of::<T>()) {
|
||||
data.archetype.borrow::<T>();
|
||||
|
@ -193,6 +250,14 @@ impl Resources {
|
|||
data.archetype.release_mut::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears each resource's tracker state.
|
||||
/// For example, each resource's component "mutated" state will be reset to `false`.
|
||||
pub fn clear_trackers(&mut self) {
|
||||
for (_, resource_data) in self.resource_data.iter_mut() {
|
||||
resource_data.archetype.clear_trackers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Resources {}
|
||||
|
|
|
@ -60,6 +60,7 @@ impl ParallelExecutor {
|
|||
|
||||
if self.clear_trackers {
|
||||
world.clear_trackers();
|
||||
resources.clear_trackers();
|
||||
}
|
||||
|
||||
self.last_schedule_generation = schedule_generation;
|
||||
|
|
|
@ -159,6 +159,7 @@ impl Schedule {
|
|||
}
|
||||
|
||||
world.clear_trackers();
|
||||
resources.clear_trackers();
|
||||
}
|
||||
|
||||
// TODO: move this code to ParallelExecutor
|
||||
|
|
|
@ -106,9 +106,10 @@ macro_rules! impl_into_foreach_system {
|
|||
func: move |world, resources, _archetype_access, state| {
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::borrow(&resources);
|
||||
{
|
||||
let ($($resource,)*) = resources.query_system::<($($resource,)*)>(id);
|
||||
for ($($component,)*) in world.query::<($($component,)*)>().iter() {
|
||||
fn_call!(self, ($($commands, state)*), ($($resource),*), ($($component),*))
|
||||
if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) {
|
||||
for ($($component,)*) in world.query::<($($component,)*)>().iter() {
|
||||
fn_call!(self, ($($commands, state)*), ($($resource),*), ($($component),*))
|
||||
}
|
||||
}
|
||||
}
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::release(&resources);
|
||||
|
@ -175,15 +176,16 @@ macro_rules! impl_into_query_system {
|
|||
func: move |world, resources, archetype_access, state| {
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::borrow(&resources);
|
||||
{
|
||||
let ($($resource,)*) = resources.query_system::<($($resource,)*)>(id);
|
||||
let mut i = 0;
|
||||
$(
|
||||
let $query = Query::<$query>::new(world, &state.archetype_accesses[i]);
|
||||
i += 1;
|
||||
)*
|
||||
if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) {
|
||||
let mut i = 0;
|
||||
$(
|
||||
let $query = Query::<$query>::new(world, &state.archetype_accesses[i]);
|
||||
i += 1;
|
||||
)*
|
||||
|
||||
let commands = &state.commands;
|
||||
fn_call!(self, ($($commands, commands)*), ($($resource),*), ($($query),*))
|
||||
let commands = &state.commands;
|
||||
fn_call!(self, ($($commands, commands)*), ($($resource),*), ($($query),*))
|
||||
}
|
||||
}
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::release(&resources);
|
||||
},
|
||||
|
@ -342,10 +344,11 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{IntoQuerySystem, Query};
|
||||
use super::{IntoForEachSystem, IntoQuerySystem, Query};
|
||||
use crate::{
|
||||
resource::{ResMut, Resources},
|
||||
schedule::Schedule,
|
||||
ChangedRes, Mut,
|
||||
};
|
||||
use bevy_hecs::{Entity, With, World};
|
||||
|
||||
|
@ -415,4 +418,30 @@ mod tests {
|
|||
|
||||
assert!(*resources.get::<bool>().unwrap(), "system ran");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changed_resource_system() {
|
||||
fn incr_e_on_flip(_run_on_flip: ChangedRes<bool>, mut i: Mut<i32>) {
|
||||
*i += 1;
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
let mut resources = Resources::default();
|
||||
resources.insert(false);
|
||||
let ent = world.spawn((0,));
|
||||
|
||||
let mut schedule = Schedule::default();
|
||||
schedule.add_stage("update");
|
||||
schedule.add_system_to_stage("update", incr_e_on_flip.system());
|
||||
|
||||
schedule.run(&mut world, &mut resources);
|
||||
assert_eq!(*(world.get::<i32>(ent).unwrap()), 1);
|
||||
|
||||
schedule.run(&mut world, &mut resources);
|
||||
assert_eq!(*(world.get::<i32>(ent).unwrap()), 1);
|
||||
|
||||
*resources.get_mut::<bool>().unwrap() = true;
|
||||
schedule.run(&mut world, &mut resources);
|
||||
assert_eq!(*(world.get::<i32>(ent).unwrap()), 2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,14 +176,26 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
|
|||
}
|
||||
|
||||
unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> Self::Item {
|
||||
let pipelines = {
|
||||
let (value, mutated) = resources
|
||||
.get_unsafe_ref_with_mutated::<Assets<PipelineDescriptor>>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
};
|
||||
let shaders = {
|
||||
let (value, mutated) =
|
||||
resources.get_unsafe_ref_with_mutated::<Assets<Shader>>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
};
|
||||
let pipeline_compiler = {
|
||||
let (value, mutated) =
|
||||
resources.get_unsafe_ref_with_mutated::<PipelineCompiler>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
};
|
||||
|
||||
DrawContext {
|
||||
pipelines: ResMut::new(
|
||||
resources.get_unsafe_ref::<Assets<PipelineDescriptor>>(ResourceIndex::Global),
|
||||
),
|
||||
shaders: ResMut::new(resources.get_unsafe_ref::<Assets<Shader>>(ResourceIndex::Global)),
|
||||
pipeline_compiler: ResMut::new(
|
||||
resources.get_unsafe_ref::<PipelineCompiler>(ResourceIndex::Global),
|
||||
),
|
||||
pipelines,
|
||||
shaders,
|
||||
pipeline_compiler,
|
||||
render_resource_context: Res::new(
|
||||
resources.get_unsafe_ref::<Box<dyn RenderResourceContext>>(ResourceIndex::Global),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue