use std::sync::atomic::Ordering;
use parking_lot::ArcRwLockReadGuard;
use parking_lot::RawRwLock;
use crate::engine::activation::RunContext;
use crate::engine::borrow::BorrowGuard;
use crate::engine::boundary::BoundaryResource;
use crate::engine::commands::{Command, CommandEvents};
use crate::engine::error::{ECSError, ECSResult, ExecutionError, InternalViolation};
use crate::engine::query::{BuiltQuery, QueryBuilder};
use crate::engine::storage::{cast_slice, cast_slice_mut};
use crate::engine::types::{BoundaryID, ChannelID};
#[cfg(feature = "gpu")]
use crate::engine::types::GPUResourceID;
#[cfg(feature = "gpu")]
use crate::gpu::GPUResource;
use crate::{ComponentID, Entity};
use super::data::ECSData;
use super::ecs_manager::{CommandDrainError, ECSManager};
use super::iteration::IterationScope;
use super::query_param::{EntityQueryParam, QueryParam, Read, Write};
#[derive(Copy, Clone)]
pub struct ECSReference<'a> {
pub(super) manager: &'a ECSManager,
}
impl<'a> ECSReference<'a> {
#[inline]
pub(crate) fn clear_borrows(&self) {
self.manager.borrows.clear();
}
#[inline]
#[allow(clippy::result_large_err)]
pub(crate) fn apply_deferred_commands_with_events(
&self,
) -> Result<CommandEvents, CommandDrainError> {
self.manager.apply_deferred_commands_with_events()
}
#[inline]
pub fn with_exclusive<R>(&self, f: impl FnOnce(&mut ECSData) -> ECSResult<R>) -> ECSResult<R> {
if self.manager.active_iters.load(Ordering::Acquire) != 0 {
return Err(ECSError::from(
ExecutionError::StructuralMutationDuringIteration,
));
}
let _phase = self.manager.phase_write()?;
let data = unsafe { self.manager.data_mut_unchecked(&_phase) };
f(data)
}
#[inline]
pub fn defer(&self, command: Command) -> ECSResult<()> {
let mut queue = self.manager.deferred.lock().map_err(|_| {
ECSError::from(ExecutionError::LockPoisoned {
what: "deferred command queue",
})
})?;
queue.push(command);
Ok(())
}
#[inline]
pub fn query(&self) -> ECSResult<QueryBuilder> {
Ok(QueryBuilder::with_registry(self.manager.registry()))
}
#[inline]
pub fn run_context(&self) -> RunContext {
crate::engine::activation::current_run_context()
}
pub fn boundary<R: BoundaryResource + 'static>(
&self,
id: BoundaryID,
) -> ECSResult<BoundaryHandle<R>> {
let slot = {
let guard = self.manager.boundary_resources.lock().map_err(|_| {
ECSError::from(ExecutionError::LockPoisoned {
what: "boundary_resources (ECSReference::boundary)",
})
})?;
if id as usize >= guard.slots.len() {
return Err(ECSError::from(ExecutionError::BoundaryAccessFailed {
reason: crate::engine::error::BoundaryAccessFailure::OutOfRange,
id,
}));
}
guard.slots[id as usize].clone()
};
let guard = slot.read_arc();
let typed = match guard.as_any().downcast_ref::<R>() {
Some(resource) => TypedResourcePtr(std::ptr::NonNull::from(resource)),
None => {
return Err(ECSError::from(ExecutionError::BoundaryAccessFailed {
reason: crate::engine::error::BoundaryAccessFailure::TypeMismatch,
id,
}));
}
};
Ok(BoundaryHandle {
guard,
typed,
_phantom: std::marker::PhantomData,
})
}
pub unsafe fn for_each_abstraction(
&self,
query: BuiltQuery,
f: impl Fn(&[&[u8]], &mut [&mut [u8]]) + Send + Sync,
) -> ECSResult<()> {
let _phase = self.manager.phase_read()?;
let _iter_scope = IterationScope::new(&self.manager.active_iters);
let _borrows = BorrowGuard::new(&self.manager.borrows, query.read_ids(), query.write_ids())
.map_err(ECSError::from)?;
let data = unsafe { self.manager.data_ref_unchecked(&_phase) };
data.for_each_abstraction_unchecked(query, f)
.map_err(ECSError::from)?;
Ok(())
}
pub unsafe fn for_each_abstraction_fallible(
&self,
query: BuiltQuery,
f: impl Fn(&[&[u8]], &mut [&mut [u8]]) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()> {
let _phase = self.manager.phase_read()?;
let _iter_scope = IterationScope::new(&self.manager.active_iters);
let _borrows = BorrowGuard::new(&self.manager.borrows, query.read_ids(), query.write_ids())
.map_err(ECSError::from)?;
let data = unsafe { self.manager.data_ref_unchecked(&_phase) };
data.for_each_abstraction_fallible_unchecked(query, f)
}
pub unsafe fn for_each_entity_abstraction(
&self,
query: BuiltQuery,
f: impl Fn(&[Entity], &[&[u8]], &mut [&mut [u8]]) + Send + Sync,
) -> ECSResult<()> {
let _phase = self.manager.phase_read()?;
let _iter_scope = IterationScope::new(&self.manager.active_iters);
let _borrows = BorrowGuard::new(&self.manager.borrows, query.read_ids(), query.write_ids())
.map_err(ECSError::from)?;
let data = unsafe { self.manager.data_ref_unchecked(&_phase) };
data.for_each_entity_abstraction_unchecked(query, f)
.map_err(ECSError::from)?;
Ok(())
}
pub unsafe fn for_each_entity_abstraction_fallible(
&self,
query: BuiltQuery,
f: impl Fn(&[Entity], &[&[u8]], &mut [&mut [u8]]) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()> {
let _phase = self.manager.phase_read()?;
let _iter_scope = IterationScope::new(&self.manager.active_iters);
let _borrows = BorrowGuard::new(&self.manager.borrows, query.read_ids(), query.write_ids())
.map_err(ECSError::from)?;
let data = unsafe { self.manager.data_ref_unchecked(&_phase) };
data.for_each_entity_abstraction_fallible_unchecked(query, f)
}
pub unsafe fn reduce_abstraction<R>(
&self,
query: BuiltQuery,
init: impl Fn() -> R + Send + Sync,
fold_chunk: impl Fn(&mut R, &[&[u8]], usize) + Send + Sync,
combine: impl Fn(&mut R, R) + Send + Sync,
) -> ECSResult<R>
where
R: Send + 'static,
{
if !query.write_ids().is_empty() {
return Err(InternalViolation::ReduceWritesNotAllowed.into());
}
let _phase = self.manager.phase_read()?;
let _iter = IterationScope::new(&self.manager.active_iters);
let _borrows = BorrowGuard::new(&self.manager.borrows, query.read_ids(), query.write_ids())
.map_err(ECSError::from)?;
let data = unsafe { self.manager.data_ref_unchecked(&_phase) };
data.reduce_abstraction_unchecked(query, init, fold_chunk, combine)
.map_err(ECSError::from)
}
#[cfg(feature = "gpu")]
pub fn register_gpu_resource<R: GPUResource + 'static>(
&self,
r: R,
) -> ECSResult<GPUResourceID> {
self.with_exclusive(|data| data.register_gpu_resource(r))
}
}
impl ECSReference<'_> {
pub fn for_each<P, F>(&self, query: BuiltQuery, f: F) -> ECSResult<()>
where
P: QueryParam,
F: for<'i> Fn(P::Item<'i>) + Send + Sync,
{
P::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |reads, writes| {
P::for_each_chunk(reads, writes, &f)
})
}
}
pub fn for_each_entity<P, F>(&self, query: BuiltQuery, f: F) -> ECSResult<()>
where
P: EntityQueryParam,
F: for<'i> Fn(P::EntityItem<'i>) + Send + Sync,
{
P::validate(&query)?;
unsafe {
self.for_each_entity_abstraction(query, move |entities, reads, writes| {
P::for_each_entity_chunk(entities, reads, writes, &f)
})
}
}
pub fn for_each_entity_fallible<P, F>(&self, query: BuiltQuery, f: F) -> ECSResult<()>
where
P: EntityQueryParam,
F: for<'i> Fn(P::EntityItem<'i>) -> ECSResult<()> + Send + Sync,
{
P::validate(&query)?;
unsafe {
self.for_each_entity_abstraction_fallible(query, move |entities, reads, writes| {
P::for_each_entity_chunk_fallible(entities, reads, writes, &f)
})
}
}
}
impl ECSReference<'_> {
pub fn reduce_read<A, R>(
&self,
query: BuiltQuery,
init: impl Fn() -> R + Send + Sync,
fold: impl Fn(&mut R, &A) + Send + Sync,
combine: impl Fn(&mut R, R) + Send + Sync,
) -> ECSResult<R>
where
A: 'static + Send + Sync,
R: Send + 'static,
{
if query.reads().len() != 1 || !query.writes().is_empty() {
return Err(InternalViolation::QueryShapeMismatch {
method: "reduce_read",
expected_reads: 1,
expected_writes: 0,
}
.into());
}
query.validate_read_type::<A>(0, "reduce_read")?;
unsafe {
self.reduce_abstraction(
query,
init,
move |acc, cols, _| {
let slice = cast_slice::<A>(cols[0].as_ptr(), cols[0].len());
for v in slice {
fold(acc, v);
}
},
combine,
)
}
}
pub fn reduce_read2<A, B, R>(
&self,
query: BuiltQuery,
init: impl Fn() -> R + Send + Sync,
fold: impl Fn(&mut R, &A, &B) + Send + Sync,
combine: impl Fn(&mut R, R) + Send + Sync,
) -> ECSResult<R>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
R: Send + 'static,
{
if query.reads().len() != 2 || !query.writes().is_empty() {
return Err(InternalViolation::QueryShapeMismatch {
method: "reduce_read2",
expected_reads: 2,
expected_writes: 0,
}
.into());
}
query.validate_read_type::<A>(0, "reduce_read2")?;
query.validate_read_type::<B>(1, "reduce_read2")?;
unsafe {
self.reduce_abstraction(
query,
init,
move |acc, cols, _| {
let a = cast_slice::<A>(cols[0].as_ptr(), cols[0].len());
let b = cast_slice::<B>(cols[1].as_ptr(), cols[1].len());
debug_assert_eq!(a.len(), b.len());
for i in 0..a.len() {
fold(acc, &a[i], &b[i]);
}
},
combine,
)
}
}
}
impl ECSReference<'_> {
pub fn for_each_r1<A>(&self, query: BuiltQuery, f: impl Fn(&A) + Send + Sync) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Read<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |reads, _| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
for v in a {
f(v);
}
})
}
}
pub fn for_each_entity_r1<A>(
&self,
query: BuiltQuery,
f: impl Fn(Entity, &A) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Read<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_entity_abstraction(query, move |entities, reads, _| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
debug_assert_eq!(entities.len(), a.len());
for i in 0..a.len() {
f(entities[i], &a[i]);
}
})
}
}
pub fn for_each_w1<A>(
&self,
query: BuiltQuery,
f: impl Fn(&mut A) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Write<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |_, writes| {
let slice = cast_slice_mut::<A>(writes[0].as_mut_ptr(), writes[0].len());
for item in slice {
f(item);
}
})
}
}
pub fn for_each_entity_w1<A>(
&self,
query: BuiltQuery,
f: impl Fn(Entity, &mut A) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Write<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_entity_abstraction(query, move |entities, _, writes| {
let slice = cast_slice_mut::<A>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(entities.len(), slice.len());
for i in 0..slice.len() {
f(entities[i], &mut slice[i]);
}
})
}
}
pub fn for_each_r2<A, B>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &B) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
{
<(Read<A>, Read<B>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |reads, _| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice::<B>(reads[1].as_ptr(), reads[1].len());
debug_assert_eq!(a.len(), b.len());
for i in 0..a.len() {
f(&a[i], &b[i]);
}
})
}
}
pub fn for_each_r3<A, B, C>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &B, &C) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
C: 'static + Send + Sync,
{
<(Read<A>, Read<B>, Read<C>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |reads, _| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice::<B>(reads[1].as_ptr(), reads[1].len());
let c = cast_slice::<C>(reads[2].as_ptr(), reads[2].len());
debug_assert_eq!(a.len(), b.len());
debug_assert_eq!(a.len(), c.len());
for i in 0..a.len() {
f(&a[i], &b[i], &c[i]);
}
})
}
}
pub fn for_each_r1w1<A, B>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &mut B) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
{
<(Read<A>, Write<B>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |reads, writes| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice_mut::<B>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(a.len(), b.len());
for i in 0..a.len() {
f(&a[i], &mut b[i]);
}
})
}
}
pub fn for_each_entity_r1w1<A, B>(
&self,
query: BuiltQuery,
f: impl Fn(Entity, &A, &mut B) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
{
<(Read<A>, Write<B>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_entity_abstraction(query, move |entities, reads, writes| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice_mut::<B>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(entities.len(), a.len());
debug_assert_eq!(a.len(), b.len());
for i in 0..a.len() {
f(entities[i], &a[i], &mut b[i]);
}
})
}
}
pub fn for_each_r2w1<A, B, C>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &B, &mut C) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
C: 'static + Send + Sync,
{
<(Read<A>, Read<B>, Write<C>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |reads, writes| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice::<B>(reads[1].as_ptr(), reads[1].len());
let c = cast_slice_mut::<C>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(a.len(), b.len());
debug_assert_eq!(a.len(), c.len());
for i in 0..a.len() {
f(&a[i], &b[i], &mut c[i]);
}
})
}
}
pub fn for_each_r2w2<A, B, C, D>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &B, &mut C, &mut D) + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
C: 'static + Send + Sync,
D: 'static + Send + Sync,
{
<(Read<A>, Read<B>, Write<C>, Write<D>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction(query, move |reads, writes| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice::<B>(reads[1].as_ptr(), reads[1].len());
let c = cast_slice_mut::<C>(writes[0].as_mut_ptr(), writes[0].len());
let d = cast_slice_mut::<D>(writes[1].as_mut_ptr(), writes[1].len());
debug_assert_eq!(a.len(), b.len());
debug_assert_eq!(a.len(), c.len());
debug_assert_eq!(a.len(), d.len());
for i in 0..a.len() {
f(&a[i], &b[i], &mut c[i], &mut d[i]);
}
})
}
}
}
impl ECSReference<'_> {
pub fn for_each_r1_fallible<A>(
&self,
query: BuiltQuery,
f: impl Fn(&A) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Read<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction_fallible(query, move |reads, _| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
for v in a {
f(v)?;
}
Ok(())
})
}
}
pub fn for_each_entity_r1_fallible<A>(
&self,
query: BuiltQuery,
f: impl Fn(Entity, &A) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Read<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_entity_abstraction_fallible(query, move |entities, reads, _| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
debug_assert_eq!(entities.len(), a.len());
for i in 0..a.len() {
f(entities[i], &a[i])?;
}
Ok(())
})
}
}
pub fn for_each_w1_fallible<A>(
&self,
query: BuiltQuery,
f: impl Fn(&mut A) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Write<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction_fallible(query, move |_, writes| {
let slice = cast_slice_mut::<A>(writes[0].as_mut_ptr(), writes[0].len());
for item in slice {
f(item)?;
}
Ok(())
})
}
}
pub fn for_each_entity_w1_fallible<A>(
&self,
query: BuiltQuery,
f: impl Fn(Entity, &mut A) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
{
<(Write<A>,) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_entity_abstraction_fallible(query, move |entities, _, writes| {
let slice = cast_slice_mut::<A>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(entities.len(), slice.len());
for i in 0..slice.len() {
f(entities[i], &mut slice[i])?;
}
Ok(())
})
}
}
pub fn for_each_r2_fallible<A, B>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &B) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
{
<(Read<A>, Read<B>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction_fallible(query, move |reads, _| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice::<B>(reads[1].as_ptr(), reads[1].len());
debug_assert_eq!(a.len(), b.len());
for i in 0..a.len() {
f(&a[i], &b[i])?;
}
Ok(())
})
}
}
pub fn for_each_r1w1_fallible<A, B>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &mut B) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
{
<(Read<A>, Write<B>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction_fallible(query, move |reads, writes| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice_mut::<B>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(a.len(), b.len());
for i in 0..a.len() {
f(&a[i], &mut b[i])?;
}
Ok(())
})
}
}
pub fn for_each_entity_r1w1_fallible<A, B>(
&self,
query: BuiltQuery,
f: impl Fn(Entity, &A, &mut B) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
{
<(Read<A>, Write<B>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_entity_abstraction_fallible(query, move |entities, reads, writes| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice_mut::<B>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(entities.len(), a.len());
debug_assert_eq!(a.len(), b.len());
for i in 0..a.len() {
f(entities[i], &a[i], &mut b[i])?;
}
Ok(())
})
}
}
pub fn for_each_r2w1_fallible<A, B, C>(
&self,
query: BuiltQuery,
f: impl Fn(&A, &B, &mut C) -> ECSResult<()> + Send + Sync,
) -> ECSResult<()>
where
A: 'static + Send + Sync,
B: 'static + Send + Sync,
C: 'static + Send + Sync,
{
<(Read<A>, Read<B>, Write<C>) as QueryParam>::validate(&query)?;
unsafe {
self.for_each_abstraction_fallible(query, move |reads, writes| {
let a = cast_slice::<A>(reads[0].as_ptr(), reads[0].len());
let b = cast_slice::<B>(reads[1].as_ptr(), reads[1].len());
let c = cast_slice_mut::<C>(writes[0].as_mut_ptr(), writes[0].len());
debug_assert_eq!(a.len(), b.len());
debug_assert_eq!(a.len(), c.len());
for i in 0..a.len() {
f(&a[i], &b[i], &mut c[i])?;
}
Ok(())
})
}
}
pub fn read_entity_component<T: 'static + Clone>(
&self,
entity: Entity,
component_id: ComponentID,
) -> ECSResult<T> {
let _phase = self.manager.phase_read()?;
let data = unsafe { self.manager.data_ref_unchecked(&_phase) };
data.read_component::<T>(entity, component_id)
}
#[inline]
pub(crate) fn finalise_boundaries_with_profiles(
&self,
channels: &[ChannelID],
profiles: &[crate::engine::boundary::BoundaryChannelProfile],
) -> ECSResult<()> {
self.manager
.finalise_boundaries_with_profiles(channels, profiles)
}
}
struct TypedResourcePtr<R>(std::ptr::NonNull<R>);
unsafe impl<R: Sync> Send for TypedResourcePtr<R> {}
unsafe impl<R: Sync> Sync for TypedResourcePtr<R> {}
pub struct BoundaryHandle<R: BoundaryResource + 'static> {
#[allow(dead_code)]
guard: ArcRwLockReadGuard<RawRwLock, dyn BoundaryResource>,
typed: TypedResourcePtr<R>,
_phantom: std::marker::PhantomData<R>,
}
impl<R: BoundaryResource + 'static> std::ops::Deref for BoundaryHandle<R> {
type Target = R;
fn deref(&self) -> &R {
unsafe { self.typed.0.as_ref() }
}
}