mirror of
https://github.com/bevyengine/bevy
synced 2024-11-23 05:03:47 +00:00
Merge branch 'main' into transmission
This commit is contained in:
commit
96c36ef780
54 changed files with 455 additions and 222 deletions
|
@ -726,7 +726,7 @@ path = "examples/3d/wireframe.rs"
|
||||||
name = "Wireframe"
|
name = "Wireframe"
|
||||||
description = "Showcases wireframe rendering"
|
description = "Showcases wireframe rendering"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
wasm = true
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "no_prepass"
|
name = "no_prepass"
|
||||||
|
|
|
@ -15,9 +15,14 @@ var<uniform> settings: ShowPrepassSettings;
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
@builtin(position) frag_coord: vec4<f32>,
|
@builtin(position) frag_coord: vec4<f32>,
|
||||||
|
#ifdef MULTISAMPLED
|
||||||
@builtin(sample_index) sample_index: u32,
|
@builtin(sample_index) sample_index: u32,
|
||||||
|
#endif
|
||||||
#import bevy_pbr::mesh_vertex_output
|
#import bevy_pbr::mesh_vertex_output
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @location(0) vec4<f32> {
|
||||||
|
#ifndef MULTISAMPLED
|
||||||
|
let sample_index = 0u;
|
||||||
|
#endif
|
||||||
if settings.show_depth == 1u {
|
if settings.show_depth == 1u {
|
||||||
let depth = prepass_depth(frag_coord, sample_index);
|
let depth = prepass_depth(frag_coord, sample_index);
|
||||||
return vec4(depth, depth, depth, 1.0);
|
return vec4(depth, depth, depth, 1.0);
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub fn heavy_compute(c: &mut Criterion) {
|
||||||
|
|
||||||
let mut system = IntoSystem::into_system(sys);
|
let mut system = IntoSystem::into_system(sys);
|
||||||
system.initialize(&mut world);
|
system.initialize(&mut world);
|
||||||
system.update_archetype_component_access(&world);
|
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||||
|
|
||||||
b.iter(move || system.run((), &mut world));
|
b.iter(move || system.run((), &mut world));
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl Benchmark {
|
||||||
|
|
||||||
let mut system = IntoSystem::into_system(query_system);
|
let mut system = IntoSystem::into_system(query_system);
|
||||||
system.initialize(&mut world);
|
system.initialize(&mut world);
|
||||||
system.update_archetype_component_access(&world);
|
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||||
Self(world, Box::new(system))
|
Self(world, Box::new(system))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -291,7 +291,7 @@ pub fn query_get_component_simple(criterion: &mut Criterion) {
|
||||||
|
|
||||||
let mut system = IntoSystem::into_system(query_system);
|
let mut system = IntoSystem::into_system(query_system);
|
||||||
system.initialize(&mut world);
|
system.initialize(&mut world);
|
||||||
system.update_archetype_component_access(&world);
|
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||||
|
|
||||||
bencher.iter(|| system.run(entity, &mut world));
|
bencher.iter(|| system.run(entity, &mut world));
|
||||||
});
|
});
|
||||||
|
|
|
@ -134,9 +134,7 @@ pub enum Tonemapping {
|
||||||
/// Suffers from lots hue shifting, brights don't desaturate naturally.
|
/// Suffers from lots hue shifting, brights don't desaturate naturally.
|
||||||
/// Bright primaries and secondaries don't desaturate at all.
|
/// Bright primaries and secondaries don't desaturate at all.
|
||||||
Reinhard,
|
Reinhard,
|
||||||
/// Current bevy default. Likely to change in the future.
|
|
||||||
/// Suffers from hue shifting. Brights don't desaturate much at all across the spectrum.
|
/// Suffers from hue shifting. Brights don't desaturate much at all across the spectrum.
|
||||||
#[default]
|
|
||||||
ReinhardLuminance,
|
ReinhardLuminance,
|
||||||
/// Same base implementation that Godot 4.0 uses for Tonemap ACES.
|
/// Same base implementation that Godot 4.0 uses for Tonemap ACES.
|
||||||
/// <https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl>
|
/// <https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl>
|
||||||
|
@ -156,6 +154,7 @@ pub enum Tonemapping {
|
||||||
/// Designed as a compromise if you want e.g. decent skin tones in low light, but can't afford to re-do your
|
/// Designed as a compromise if you want e.g. decent skin tones in low light, but can't afford to re-do your
|
||||||
/// VFX to look good without hue shifting.
|
/// VFX to look good without hue shifting.
|
||||||
SomewhatBoringDisplayTransform,
|
SomewhatBoringDisplayTransform,
|
||||||
|
/// Current Bevy default.
|
||||||
/// By Tomasz Stachowiak
|
/// By Tomasz Stachowiak
|
||||||
/// <https://github.com/h3r2tic/tony-mc-mapface>
|
/// <https://github.com/h3r2tic/tony-mc-mapface>
|
||||||
/// Very neutral. Subtle but intentional hue shifting. Brights desaturate across the spectrum.
|
/// Very neutral. Subtle but intentional hue shifting. Brights desaturate across the spectrum.
|
||||||
|
@ -167,6 +166,7 @@ pub enum Tonemapping {
|
||||||
/// Color hues are preserved during compression, except for a deliberate [Bezold–Brücke shift](https://en.wikipedia.org/wiki/Bezold%E2%80%93Br%C3%BCcke_shift).
|
/// Color hues are preserved during compression, except for a deliberate [Bezold–Brücke shift](https://en.wikipedia.org/wiki/Bezold%E2%80%93Br%C3%BCcke_shift).
|
||||||
/// To avoid posterization, selective desaturation is employed, with care to avoid the [Abney effect](https://en.wikipedia.org/wiki/Abney_effect).
|
/// To avoid posterization, selective desaturation is employed, with care to avoid the [Abney effect](https://en.wikipedia.org/wiki/Abney_effect).
|
||||||
/// NOTE: Requires the `tonemapping_luts` cargo feature.
|
/// NOTE: Requires the `tonemapping_luts` cargo feature.
|
||||||
|
#[default]
|
||||||
TonyMcMapface,
|
TonyMcMapface,
|
||||||
/// Default Filmic Display Transform from blender.
|
/// Default Filmic Display Transform from blender.
|
||||||
/// Somewhat neutral. Suffers from hue shifting. Brights desaturate across the spectrum.
|
/// Somewhat neutral. Suffers from hue shifting. Brights desaturate across the spectrum.
|
||||||
|
@ -328,7 +328,7 @@ pub fn get_lut_bindings<'a>(
|
||||||
bindings: [u32; 2],
|
bindings: [u32; 2],
|
||||||
) -> [BindGroupEntry<'a>; 2] {
|
) -> [BindGroupEntry<'a>; 2] {
|
||||||
let image = match tonemapping {
|
let image = match tonemapping {
|
||||||
//AgX lut texture used when tonemapping doesn't need a texture since it's very small (32x32x32)
|
// AgX lut texture used when tonemapping doesn't need a texture since it's very small (32x32x32)
|
||||||
Tonemapping::None
|
Tonemapping::None
|
||||||
| Tonemapping::Reinhard
|
| Tonemapping::Reinhard
|
||||||
| Tonemapping::ReinhardLuminance
|
| Tonemapping::ReinhardLuminance
|
||||||
|
|
|
@ -1162,12 +1162,19 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that occurs when retrieving a specific [`Entity`]'s query result.
|
/// An error that occurs when retrieving a specific [`Entity`]'s query result from [`Query`](crate::system::Query) or [`QueryState`].
|
||||||
// TODO: return the type_name as part of this error
|
// TODO: return the type_name as part of this error
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum QueryEntityError {
|
pub enum QueryEntityError {
|
||||||
|
/// The given [`Entity`]'s components do not match the query.
|
||||||
|
///
|
||||||
|
/// Either it does not have a requested component, or it has a component which the query filters out.
|
||||||
QueryDoesNotMatch(Entity),
|
QueryDoesNotMatch(Entity),
|
||||||
|
/// The given [`Entity`] does not exist.
|
||||||
NoSuchEntity(Entity),
|
NoSuchEntity(Entity),
|
||||||
|
/// The [`Entity`] was requested mutably more than once.
|
||||||
|
///
|
||||||
|
/// See [`QueryState::get_many_mut`] for an example.
|
||||||
AliasedMutability(Entity),
|
AliasedMutability(Entity),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,7 +1184,7 @@ impl fmt::Display for QueryEntityError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
QueryEntityError::QueryDoesNotMatch(_) => {
|
QueryEntityError::QueryDoesNotMatch(_) => {
|
||||||
write!(f, "The given entity does not have the requested component.")
|
write!(f, "The given entity's components do not match the query.")
|
||||||
}
|
}
|
||||||
QueryEntityError::NoSuchEntity(_) => write!(f, "The requested entity does not exist."),
|
QueryEntityError::NoSuchEntity(_) => write!(f, "The requested entity does not exist."),
|
||||||
QueryEntityError::AliasedMutability(_) => {
|
QueryEntityError::AliasedMutability(_) => {
|
||||||
|
@ -1296,11 +1303,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that occurs when evaluating a [`QueryState`] as a single expected resulted via
|
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`] as a single expected result via
|
||||||
/// [`QueryState::single`] or [`QueryState::single_mut`].
|
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum QuerySingleError {
|
pub enum QuerySingleError {
|
||||||
|
/// No entity fits the query.
|
||||||
NoEntities(&'static str),
|
NoEntities(&'static str),
|
||||||
|
/// Multiple entities fit the query.
|
||||||
MultipleEntities(&'static str),
|
MultipleEntities(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,58 @@ use std::ops::Not;
|
||||||
use crate::component::{self, ComponentId};
|
use crate::component::{self, ComponentId};
|
||||||
use crate::query::Access;
|
use crate::query::Access;
|
||||||
use crate::system::{CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System};
|
use crate::system::{CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System};
|
||||||
|
use crate::world::unsafe_world_cell::UnsafeWorldCell;
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
pub type BoxedCondition = Box<dyn ReadOnlySystem<In = (), Out = bool>>;
|
pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
|
||||||
|
|
||||||
/// A system that determines if one or more scheduled systems should run.
|
/// A system that determines if one or more scheduled systems should run.
|
||||||
///
|
///
|
||||||
/// Implemented for functions and closures that convert into [`System<In=(), Out=bool>`](crate::system::System)
|
/// Implemented for functions and closures that convert into [`System<Out=bool>`](crate::system::System)
|
||||||
/// with [read-only](crate::system::ReadOnlySystemParam) parameters.
|
/// with [read-only](crate::system::ReadOnlySystemParam) parameters.
|
||||||
pub trait Condition<Marker>: sealed::Condition<Marker> {
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// A condition that returns true every other time it's called.
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_ecs::prelude::*;
|
||||||
|
/// fn every_other_time() -> impl Condition<()> {
|
||||||
|
/// IntoSystem::into_system(|mut flag: Local<bool>| {
|
||||||
|
/// *flag = !*flag;
|
||||||
|
/// *flag
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # #[derive(Resource)] struct DidRun(bool);
|
||||||
|
/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
|
||||||
|
/// # let mut schedule = Schedule::new();
|
||||||
|
/// schedule.add_systems(my_system.run_if(every_other_time()));
|
||||||
|
/// # let mut world = World::new();
|
||||||
|
/// # world.insert_resource(DidRun(false));
|
||||||
|
/// # schedule.run(&mut world);
|
||||||
|
/// # assert!(world.resource::<DidRun>().0);
|
||||||
|
/// # world.insert_resource(DidRun(false));
|
||||||
|
/// # schedule.run(&mut world);
|
||||||
|
/// # assert!(!world.resource::<DidRun>().0);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// A condition that takes a bool as an input and returns it unchanged.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_ecs::prelude::*;
|
||||||
|
/// fn identity() -> impl Condition<(), bool> {
|
||||||
|
/// IntoSystem::into_system(|In(x)| x)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn always_true() -> bool { true }
|
||||||
|
/// # let mut schedule = Schedule::new();
|
||||||
|
/// # #[derive(Resource)] struct DidRun(bool);
|
||||||
|
/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
|
||||||
|
/// schedule.add_systems(my_system.run_if(always_true.pipe(identity())));
|
||||||
|
/// # let mut world = World::new();
|
||||||
|
/// # world.insert_resource(DidRun(false));
|
||||||
|
/// # schedule.run(&mut world);
|
||||||
|
/// # assert!(world.resource::<DidRun>().0);
|
||||||
|
pub trait Condition<Marker, In = ()>: sealed::Condition<Marker, In> {
|
||||||
/// Returns a new run condition that only returns `true`
|
/// Returns a new run condition that only returns `true`
|
||||||
/// if both this one and the passed `and_then` return `true`.
|
/// if both this one and the passed `and_then` return `true`.
|
||||||
///
|
///
|
||||||
|
@ -58,7 +101,7 @@ pub trait Condition<Marker>: sealed::Condition<Marker> {
|
||||||
/// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].
|
/// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].
|
||||||
///
|
///
|
||||||
/// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
|
/// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
|
||||||
fn and_then<M, C: Condition<M>>(self, and_then: C) -> AndThen<Self::System, C::System> {
|
fn and_then<M, C: Condition<M, In>>(self, and_then: C) -> AndThen<Self::System, C::System> {
|
||||||
let a = IntoSystem::into_system(self);
|
let a = IntoSystem::into_system(self);
|
||||||
let b = IntoSystem::into_system(and_then);
|
let b = IntoSystem::into_system(and_then);
|
||||||
let name = format!("{} && {}", a.name(), b.name());
|
let name = format!("{} && {}", a.name(), b.name());
|
||||||
|
@ -105,7 +148,7 @@ pub trait Condition<Marker>: sealed::Condition<Marker> {
|
||||||
/// # app.run(&mut world);
|
/// # app.run(&mut world);
|
||||||
/// # assert!(world.resource::<C>().0);
|
/// # assert!(world.resource::<C>().0);
|
||||||
/// ```
|
/// ```
|
||||||
fn or_else<M, C: Condition<M>>(self, or_else: C) -> OrElse<Self::System, C::System> {
|
fn or_else<M, C: Condition<M, In>>(self, or_else: C) -> OrElse<Self::System, C::System> {
|
||||||
let a = IntoSystem::into_system(self);
|
let a = IntoSystem::into_system(self);
|
||||||
let b = IntoSystem::into_system(or_else);
|
let b = IntoSystem::into_system(or_else);
|
||||||
let name = format!("{} || {}", a.name(), b.name());
|
let name = format!("{} || {}", a.name(), b.name());
|
||||||
|
@ -113,22 +156,22 @@ pub trait Condition<Marker>: sealed::Condition<Marker> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Marker, F> Condition<Marker> for F where F: sealed::Condition<Marker> {}
|
impl<Marker, In, F> Condition<Marker, In> for F where F: sealed::Condition<Marker, In> {}
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
use crate::system::{IntoSystem, ReadOnlySystem};
|
use crate::system::{IntoSystem, ReadOnlySystem};
|
||||||
|
|
||||||
pub trait Condition<Marker>:
|
pub trait Condition<Marker, In>:
|
||||||
IntoSystem<(), bool, Marker, System = Self::ReadOnlySystem>
|
IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>
|
||||||
{
|
{
|
||||||
// This associated type is necessary to let the compiler
|
// This associated type is necessary to let the compiler
|
||||||
// know that `Self::System` is `ReadOnlySystem`.
|
// know that `Self::System` is `ReadOnlySystem`.
|
||||||
type ReadOnlySystem: ReadOnlySystem<In = (), Out = bool>;
|
type ReadOnlySystem: ReadOnlySystem<In = In, Out = bool>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Marker, F> Condition<Marker> for F
|
impl<Marker, In, F> Condition<Marker, In> for F
|
||||||
where
|
where
|
||||||
F: IntoSystem<(), bool, Marker>,
|
F: IntoSystem<In, bool, Marker>,
|
||||||
F::System: ReadOnlySystem,
|
F::System: ReadOnlySystem,
|
||||||
{
|
{
|
||||||
type ReadOnlySystem = F::System;
|
type ReadOnlySystem = F::System;
|
||||||
|
@ -990,7 +1033,7 @@ where
|
||||||
self.condition.is_exclusive()
|
self.condition.is_exclusive()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
|
unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out {
|
||||||
// SAFETY: The inner condition system asserts its own safety.
|
// SAFETY: The inner condition system asserts its own safety.
|
||||||
!self.condition.run_unsafe(input, world)
|
!self.condition.run_unsafe(input, world)
|
||||||
}
|
}
|
||||||
|
@ -1007,7 +1050,7 @@ where
|
||||||
self.condition.initialize(world);
|
self.condition.initialize(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_archetype_component_access(&mut self, world: &World) {
|
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
|
||||||
self.condition.update_archetype_component_access(world);
|
self.condition.update_archetype_component_access(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crate::{
|
||||||
is_apply_system_buffers, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
|
is_apply_system_buffers, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
|
||||||
},
|
},
|
||||||
system::BoxedSystem,
|
system::BoxedSystem,
|
||||||
world::World,
|
world::{unsafe_world_cell::UnsafeWorldCell, World},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate as bevy_ecs;
|
use crate as bevy_ecs;
|
||||||
|
@ -184,7 +184,6 @@ impl SystemExecutor for MultiThreadedExecutor {
|
||||||
.map(|e| e.0.clone());
|
.map(|e| e.0.clone());
|
||||||
let thread_executor = thread_executor.as_deref();
|
let thread_executor = thread_executor.as_deref();
|
||||||
|
|
||||||
let world = SyncUnsafeCell::from_mut(world);
|
|
||||||
let SyncUnsafeSchedule {
|
let SyncUnsafeSchedule {
|
||||||
systems,
|
systems,
|
||||||
mut conditions,
|
mut conditions,
|
||||||
|
@ -197,10 +196,13 @@ impl SystemExecutor for MultiThreadedExecutor {
|
||||||
// the executor itself is a `Send` future so that it can run
|
// the executor itself is a `Send` future so that it can run
|
||||||
// alongside systems that claim the local thread
|
// alongside systems that claim the local thread
|
||||||
let executor = async {
|
let executor = async {
|
||||||
|
let world_cell = world.as_unsafe_world_cell();
|
||||||
while self.num_completed_systems < self.num_systems {
|
while self.num_completed_systems < self.num_systems {
|
||||||
// SAFETY: self.ready_systems does not contain running systems
|
// SAFETY:
|
||||||
|
// - self.ready_systems does not contain running systems.
|
||||||
|
// - `world_cell` has mutable access to the entire world.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.spawn_system_tasks(scope, systems, &mut conditions, world);
|
self.spawn_system_tasks(scope, systems, &mut conditions, world_cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.num_running_systems > 0 {
|
if self.num_running_systems > 0 {
|
||||||
|
@ -231,7 +233,7 @@ impl SystemExecutor for MultiThreadedExecutor {
|
||||||
if self.apply_final_buffers {
|
if self.apply_final_buffers {
|
||||||
// Do one final apply buffers after all systems have completed
|
// Do one final apply buffers after all systems have completed
|
||||||
// Commands should be applied while on the scope's thread, not the executor's thread
|
// Commands should be applied while on the scope's thread, not the executor's thread
|
||||||
let res = apply_system_buffers(&self.unapplied_systems, systems, world.get_mut());
|
let res = apply_system_buffers(&self.unapplied_systems, systems, world);
|
||||||
if let Err(payload) = res {
|
if let Err(payload) = res {
|
||||||
let mut panic_payload = self.panic_payload.lock().unwrap();
|
let mut panic_payload = self.panic_payload.lock().unwrap();
|
||||||
*panic_payload = Some(payload);
|
*panic_payload = Some(payload);
|
||||||
|
@ -283,14 +285,16 @@ impl MultiThreadedExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Caller must ensure that `self.ready_systems` does not contain any systems that
|
/// - Caller must ensure that `self.ready_systems` does not contain any systems that
|
||||||
/// have been mutably borrowed (such as the systems currently running).
|
/// have been mutably borrowed (such as the systems currently running).
|
||||||
|
/// - `world_cell` must have permission to access all world data (not counting
|
||||||
|
/// any world data that is claimed by systems currently running on this executor).
|
||||||
unsafe fn spawn_system_tasks<'scope>(
|
unsafe fn spawn_system_tasks<'scope>(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: &Scope<'_, 'scope, ()>,
|
scope: &Scope<'_, 'scope, ()>,
|
||||||
systems: &'scope [SyncUnsafeCell<BoxedSystem>],
|
systems: &'scope [SyncUnsafeCell<BoxedSystem>],
|
||||||
conditions: &mut Conditions,
|
conditions: &mut Conditions,
|
||||||
cell: &'scope SyncUnsafeCell<World>,
|
world_cell: UnsafeWorldCell<'scope>,
|
||||||
) {
|
) {
|
||||||
if self.exclusive_running {
|
if self.exclusive_running {
|
||||||
return;
|
return;
|
||||||
|
@ -307,10 +311,7 @@ impl MultiThreadedExecutor {
|
||||||
// Therefore, no other reference to this system exists and there is no aliasing.
|
// Therefore, no other reference to this system exists and there is no aliasing.
|
||||||
let system = unsafe { &mut *systems[system_index].get() };
|
let system = unsafe { &mut *systems[system_index].get() };
|
||||||
|
|
||||||
// SAFETY: No exclusive system is running.
|
if !self.can_run(system_index, system, conditions, world_cell) {
|
||||||
// Therefore, there is no existing mutable reference to the world.
|
|
||||||
let world = unsafe { &*cell.get() };
|
|
||||||
if !self.can_run(system_index, system, conditions, world) {
|
|
||||||
// NOTE: exclusive systems with ambiguities are susceptible to
|
// NOTE: exclusive systems with ambiguities are susceptible to
|
||||||
// being significantly displaced here (compared to single-threaded order)
|
// being significantly displaced here (compared to single-threaded order)
|
||||||
// if systems after them in topological order can run
|
// if systems after them in topological order can run
|
||||||
|
@ -320,9 +321,10 @@ impl MultiThreadedExecutor {
|
||||||
|
|
||||||
self.ready_systems.set(system_index, false);
|
self.ready_systems.set(system_index, false);
|
||||||
|
|
||||||
// SAFETY: Since `self.can_run` returned true earlier, it must have called
|
// SAFETY: `can_run` returned true, which means that:
|
||||||
// `update_archetype_component_access` for each run condition.
|
// - It must have called `update_archetype_component_access` for each run condition.
|
||||||
if !self.should_run(system_index, system, conditions, world) {
|
// - There can be no systems running whose accesses would conflict with any conditions.
|
||||||
|
if !self.should_run(system_index, system, conditions, world_cell) {
|
||||||
self.skip_system_and_signal_dependents(system_index);
|
self.skip_system_and_signal_dependents(system_index);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -331,10 +333,12 @@ impl MultiThreadedExecutor {
|
||||||
self.num_running_systems += 1;
|
self.num_running_systems += 1;
|
||||||
|
|
||||||
if self.system_task_metadata[system_index].is_exclusive {
|
if self.system_task_metadata[system_index].is_exclusive {
|
||||||
// SAFETY: `can_run` confirmed that no systems are running.
|
// SAFETY: `can_run` returned true for this system, which means
|
||||||
// Therefore, there is no existing reference to the world.
|
// that no other systems currently have access to the world.
|
||||||
|
let world = unsafe { world_cell.world_mut() };
|
||||||
|
// SAFETY: `can_run` returned true for this system,
|
||||||
|
// which means no systems are currently borrowed.
|
||||||
unsafe {
|
unsafe {
|
||||||
let world = &mut *cell.get();
|
|
||||||
self.spawn_exclusive_system_task(scope, system_index, systems, world);
|
self.spawn_exclusive_system_task(scope, system_index, systems, world);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -342,9 +346,10 @@ impl MultiThreadedExecutor {
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - No other reference to this system exists.
|
// - No other reference to this system exists.
|
||||||
// - `self.can_run` has been called, which calls `update_archetype_component_access` with this system.
|
// - `can_run` has been called, which calls `update_archetype_component_access` with this system.
|
||||||
|
// - `can_run` returned true, so no systems with conflicting world access are running.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.spawn_system_task(scope, system_index, systems, world);
|
self.spawn_system_task(scope, system_index, systems, world_cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +362,7 @@ impl MultiThreadedExecutor {
|
||||||
system_index: usize,
|
system_index: usize,
|
||||||
system: &mut BoxedSystem,
|
system: &mut BoxedSystem,
|
||||||
conditions: &mut Conditions,
|
conditions: &mut Conditions,
|
||||||
world: &World,
|
world: UnsafeWorldCell,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let system_meta = &self.system_task_metadata[system_index];
|
let system_meta = &self.system_task_metadata[system_index];
|
||||||
if system_meta.is_exclusive && self.num_running_systems > 0 {
|
if system_meta.is_exclusive && self.num_running_systems > 0 {
|
||||||
|
@ -413,15 +418,17 @@ impl MultiThreadedExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
/// * `world` must have permission to read any world data required by
|
||||||
/// `update_archetype_component` must have been called with `world`
|
/// the system's conditions: this includes conditions for the system
|
||||||
/// for each run condition in `conditions`.
|
/// itself, and conditions for any of the system's sets.
|
||||||
|
/// * `update_archetype_component` must have been called with `world`
|
||||||
|
/// for each run condition in `conditions`.
|
||||||
unsafe fn should_run(
|
unsafe fn should_run(
|
||||||
&mut self,
|
&mut self,
|
||||||
system_index: usize,
|
system_index: usize,
|
||||||
_system: &BoxedSystem,
|
_system: &BoxedSystem,
|
||||||
conditions: &mut Conditions,
|
conditions: &mut Conditions,
|
||||||
world: &World,
|
world: UnsafeWorldCell,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut should_run = !self.skipped_systems.contains(system_index);
|
let mut should_run = !self.skipped_systems.contains(system_index);
|
||||||
for set_idx in conditions.sets_with_conditions_of_systems[system_index].ones() {
|
for set_idx in conditions.sets_with_conditions_of_systems[system_index].ones() {
|
||||||
|
@ -430,7 +437,10 @@ impl MultiThreadedExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the system set's conditions.
|
// Evaluate the system set's conditions.
|
||||||
// SAFETY: `update_archetype_component_access` has been called for each run condition.
|
// SAFETY:
|
||||||
|
// - The caller ensures that `world` has permission to read any data
|
||||||
|
// required by the conditions.
|
||||||
|
// - `update_archetype_component_access` has been called for each run condition.
|
||||||
let set_conditions_met =
|
let set_conditions_met =
|
||||||
evaluate_and_fold_conditions(&mut conditions.set_conditions[set_idx], world);
|
evaluate_and_fold_conditions(&mut conditions.set_conditions[set_idx], world);
|
||||||
|
|
||||||
|
@ -444,7 +454,10 @@ impl MultiThreadedExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the system's conditions.
|
// Evaluate the system's conditions.
|
||||||
// SAFETY: `update_archetype_component_access` has been called for each run condition.
|
// SAFETY:
|
||||||
|
// - The caller ensures that `world` has permission to read any data
|
||||||
|
// required by the conditions.
|
||||||
|
// - `update_archetype_component_access` has been called for each run condition.
|
||||||
let system_conditions_met =
|
let system_conditions_met =
|
||||||
evaluate_and_fold_conditions(&mut conditions.system_conditions[system_index], world);
|
evaluate_and_fold_conditions(&mut conditions.system_conditions[system_index], world);
|
||||||
|
|
||||||
|
@ -459,6 +472,8 @@ impl MultiThreadedExecutor {
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - Caller must not alias systems that are running.
|
/// - Caller must not alias systems that are running.
|
||||||
|
/// - `world` must have permission to access the world data
|
||||||
|
/// used by the specified system.
|
||||||
/// - `update_archetype_component_access` must have been called with `world`
|
/// - `update_archetype_component_access` must have been called with `world`
|
||||||
/// on the system assocaited with `system_index`.
|
/// on the system assocaited with `system_index`.
|
||||||
unsafe fn spawn_system_task<'scope>(
|
unsafe fn spawn_system_task<'scope>(
|
||||||
|
@ -466,7 +481,7 @@ impl MultiThreadedExecutor {
|
||||||
scope: &Scope<'_, 'scope, ()>,
|
scope: &Scope<'_, 'scope, ()>,
|
||||||
system_index: usize,
|
system_index: usize,
|
||||||
systems: &'scope [SyncUnsafeCell<BoxedSystem>],
|
systems: &'scope [SyncUnsafeCell<BoxedSystem>],
|
||||||
world: &'scope World,
|
world: UnsafeWorldCell<'scope>,
|
||||||
) {
|
) {
|
||||||
// SAFETY: this system is not running, no other reference exists
|
// SAFETY: this system is not running, no other reference exists
|
||||||
let system = unsafe { &mut *systems[system_index].get() };
|
let system = unsafe { &mut *systems[system_index].get() };
|
||||||
|
@ -483,7 +498,8 @@ impl MultiThreadedExecutor {
|
||||||
let system_guard = system_span.enter();
|
let system_guard = system_span.enter();
|
||||||
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - Access: TODO.
|
// - The caller ensures that we have permission to
|
||||||
|
// access the world data used by the system.
|
||||||
// - `update_archetype_component_access` has been called.
|
// - `update_archetype_component_access` has been called.
|
||||||
unsafe { system.run_unsafe((), world) };
|
unsafe { system.run_unsafe((), world) };
|
||||||
}));
|
}));
|
||||||
|
@ -688,10 +704,14 @@ fn apply_system_buffers(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
/// - `world` must have permission to read any world data
|
||||||
/// `update_archetype_component_access` must have been called
|
/// required by `conditions`.
|
||||||
/// with `world` for each condition in `conditions`.
|
/// - `update_archetype_component_access` must have been called
|
||||||
unsafe fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &World) -> bool {
|
/// with `world` for each condition in `conditions`.
|
||||||
|
unsafe fn evaluate_and_fold_conditions(
|
||||||
|
conditions: &mut [BoxedCondition],
|
||||||
|
world: UnsafeWorldCell,
|
||||||
|
) -> bool {
|
||||||
// not short-circuiting is intentional
|
// not short-circuiting is intentional
|
||||||
#[allow(clippy::unnecessary_fold)]
|
#[allow(clippy::unnecessary_fold)]
|
||||||
conditions
|
conditions
|
||||||
|
@ -699,7 +719,8 @@ unsafe fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world:
|
||||||
.map(|condition| {
|
.map(|condition| {
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
let _condition_span = info_span!("condition", name = &*condition.name()).entered();
|
let _condition_span = info_span!("condition", name = &*condition.name()).entered();
|
||||||
// SAFETY: caller ensures system access is compatible
|
// SAFETY: The caller ensures that `world` has permission to
|
||||||
|
// access any data required by the condition.
|
||||||
unsafe { condition.run_unsafe((), world) }
|
unsafe { condition.run_unsafe((), world) }
|
||||||
})
|
})
|
||||||
.fold(true, |acc, res| acc && res)
|
.fold(true, |acc, res| acc && res)
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
component::{ComponentId, Tick},
|
component::{ComponentId, Tick},
|
||||||
prelude::World,
|
prelude::World,
|
||||||
query::Access,
|
query::Access,
|
||||||
|
world::unsafe_world_cell::UnsafeWorldCell,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ReadOnlySystem, System};
|
use super::{ReadOnlySystem, System};
|
||||||
|
@ -157,7 +158,7 @@ where
|
||||||
self.a.is_exclusive() || self.b.is_exclusive()
|
self.a.is_exclusive() || self.b.is_exclusive()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
|
unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out {
|
||||||
Func::combine(
|
Func::combine(
|
||||||
input,
|
input,
|
||||||
// SAFETY: The world accesses for both underlying systems have been registered,
|
// SAFETY: The world accesses for both underlying systems have been registered,
|
||||||
|
@ -198,7 +199,7 @@ where
|
||||||
self.component_access.extend(self.b.component_access());
|
self.component_access.extend(self.b.component_access());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_archetype_component_access(&mut self, world: &World) {
|
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
|
||||||
self.a.update_archetype_component_access(world);
|
self.a.update_archetype_component_access(world);
|
||||||
self.b.update_archetype_component_access(world);
|
self.b.update_archetype_component_access(world);
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// apps, and only when they have a scheme worked out to share an ID space (which doesn't happen
|
/// apps, and only when they have a scheme worked out to share an ID space (which doesn't happen
|
||||||
/// by default).
|
/// by default).
|
||||||
pub fn get_or_spawn<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
|
pub fn get_or_spawn<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
|
||||||
self.add(GetOrSpawn { entity });
|
self.add(move |world: &mut World| {
|
||||||
|
world.get_or_spawn(entity);
|
||||||
|
});
|
||||||
EntityCommands {
|
EntityCommands {
|
||||||
entity,
|
entity,
|
||||||
commands: self,
|
commands: self,
|
||||||
|
@ -839,8 +841,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that spawns a new entity and adds the components in a [`Bundle`] to it.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Spawn<T> {
|
pub struct Spawn<T> {
|
||||||
|
/// The [`Bundle`] of components that will be added to the newly-spawned entity.
|
||||||
pub bundle: T,
|
pub bundle: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -853,16 +857,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GetOrSpawn {
|
|
||||||
entity: Entity,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for GetOrSpawn {
|
|
||||||
fn write(self, world: &mut World) {
|
|
||||||
world.get_or_spawn(self.entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SpawnBatch<I>
|
pub struct SpawnBatch<I>
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
|
@ -907,6 +901,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that despawns a specific entity.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Despawn {
|
pub struct Despawn {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
|
@ -918,8 +913,11 @@ impl Command for Despawn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that adds the components in a [`Bundle`] to an entity.
|
||||||
pub struct Insert<T> {
|
pub struct Insert<T> {
|
||||||
|
/// The entity to which the components will be added.
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
|
/// The [`Bundle`] containing the components that will be added to the entity.
|
||||||
pub bundle: T,
|
pub bundle: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,10 +934,13 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that removes components from an entity.
|
||||||
|
/// For a [`Bundle`] type `T`, this will remove any components in the bundle.
|
||||||
|
/// Any components in the bundle that aren't found on the entity will be ignored.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Remove<T> {
|
pub struct Remove<T> {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub phantom: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Command for Remove<T>
|
impl<T> Command for Remove<T>
|
||||||
|
@ -954,17 +955,19 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Remove<T> {
|
impl<T> Remove<T> {
|
||||||
/// Creates a [`Command`] which will remove the specified [`Entity`] when flushed
|
/// Creates a [`Command`] which will remove the specified [`Entity`] when applied.
|
||||||
pub const fn new(entity: Entity) -> Self {
|
pub const fn new(entity: Entity) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entity,
|
entity,
|
||||||
phantom: PhantomData::<T>,
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that inserts a [`Resource`] into the world using a value
|
||||||
|
/// created with the [`FromWorld`] trait.
|
||||||
pub struct InitResource<R: Resource + FromWorld> {
|
pub struct InitResource<R: Resource + FromWorld> {
|
||||||
_phantom: PhantomData<R>,
|
_marker: PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Resource + FromWorld> Command for InitResource<R> {
|
impl<R: Resource + FromWorld> Command for InitResource<R> {
|
||||||
|
@ -977,11 +980,12 @@ impl<R: Resource + FromWorld> InitResource<R> {
|
||||||
/// Creates a [`Command`] which will insert a default created [`Resource`] into the [`World`]
|
/// Creates a [`Command`] which will insert a default created [`Resource`] into the [`World`]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
_phantom: PhantomData::<R>,
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that inserts a [`Resource`] into the world.
|
||||||
pub struct InsertResource<R: Resource> {
|
pub struct InsertResource<R: Resource> {
|
||||||
pub resource: R,
|
pub resource: R,
|
||||||
}
|
}
|
||||||
|
@ -992,8 +996,9 @@ impl<R: Resource> Command for InsertResource<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that removes the [resource](Resource) `R` from the world.
|
||||||
pub struct RemoveResource<R: Resource> {
|
pub struct RemoveResource<R: Resource> {
|
||||||
pub phantom: PhantomData<R>,
|
_marker: PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Resource> Command for RemoveResource<R> {
|
impl<R: Resource> Command for RemoveResource<R> {
|
||||||
|
@ -1006,7 +1011,7 @@ impl<R: Resource> RemoveResource<R> {
|
||||||
/// Creates a [`Command`] which will remove a [`Resource`] from the [`World`]
|
/// Creates a [`Command`] which will remove a [`Resource`] from the [`World`]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData::<R>,
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, In, IntoSystem,
|
check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, In, IntoSystem,
|
||||||
System, SystemMeta,
|
System, SystemMeta,
|
||||||
},
|
},
|
||||||
world::World,
|
world::{unsafe_world_cell::UnsafeWorldCell, World},
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_utils::all_tuples;
|
use bevy_utils::all_tuples;
|
||||||
|
@ -86,7 +86,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn run_unsafe(&mut self, _input: Self::In, _world: &World) -> Self::Out {
|
unsafe fn run_unsafe(&mut self, _input: Self::In, _world: UnsafeWorldCell) -> Self::Out {
|
||||||
panic!("Cannot run exclusive systems with a shared World reference");
|
panic!("Cannot run exclusive systems with a shared World reference");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ where
|
||||||
self.param_state = Some(F::Param::init(world, &mut self.system_meta));
|
self.param_state = Some(F::Param::init(world, &mut self.system_meta));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_archetype_component_access(&mut self, _world: &World) {}
|
fn update_archetype_component_access(&mut self, _world: UnsafeWorldCell) {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn check_change_tick(&mut self, change_tick: Tick) {
|
fn check_change_tick(&mut self, change_tick: Tick) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
prelude::FromWorld,
|
prelude::FromWorld,
|
||||||
query::{Access, FilteredAccessSet},
|
query::{Access, FilteredAccessSet},
|
||||||
system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem},
|
system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem},
|
||||||
world::{World, WorldId},
|
world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_utils::all_tuples;
|
use bevy_utils::all_tuples;
|
||||||
|
@ -417,7 +417,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
|
unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out {
|
||||||
let change_tick = world.increment_change_tick();
|
let change_tick = world.increment_change_tick();
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
|
@ -428,7 +428,7 @@ where
|
||||||
let params = F::Param::get_param(
|
let params = F::Param::get_param(
|
||||||
self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
|
self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
|
||||||
&self.system_meta,
|
&self.system_meta,
|
||||||
world.as_unsafe_world_cell_migration_internal(),
|
world,
|
||||||
change_tick,
|
change_tick,
|
||||||
);
|
);
|
||||||
let out = self.func.run(input, params);
|
let out = self.func.run(input, params);
|
||||||
|
@ -457,7 +457,7 @@ where
|
||||||
self.param_state = Some(F::Param::init_state(world, &mut self.system_meta));
|
self.param_state = Some(F::Param::init_state(world, &mut self.system_meta));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_archetype_component_access(&mut self, world: &World) {
|
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
|
||||||
assert!(self.world_id == Some(world.id()), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
|
assert!(self.world_id == Some(world.id()), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
|
||||||
let archetypes = world.archetypes();
|
let archetypes = world.archetypes();
|
||||||
let new_generation = archetypes.generation();
|
let new_generation = archetypes.generation();
|
||||||
|
|
|
@ -1610,7 +1610,7 @@ mod tests {
|
||||||
|
|
||||||
// set up system and verify its access is empty
|
// set up system and verify its access is empty
|
||||||
system.initialize(&mut world);
|
system.initialize(&mut world);
|
||||||
system.update_archetype_component_access(&world);
|
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
system
|
system
|
||||||
.archetype_component_access()
|
.archetype_component_access()
|
||||||
|
@ -1640,7 +1640,7 @@ mod tests {
|
||||||
world.spawn((B, C));
|
world.spawn((B, C));
|
||||||
|
|
||||||
// update system and verify its accesses are correct
|
// update system and verify its accesses are correct
|
||||||
system.update_archetype_component_access(&world);
|
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
system
|
system
|
||||||
.archetype_component_access()
|
.archetype_component_access()
|
||||||
|
@ -1658,7 +1658,7 @@ mod tests {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
world.spawn((A, B, D));
|
world.spawn((A, B, D));
|
||||||
system.update_archetype_component_access(&world);
|
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
system
|
system
|
||||||
.archetype_component_access()
|
.archetype_component_access()
|
||||||
|
|
|
@ -1054,7 +1054,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
||||||
|
|
||||||
/// Returns a mutable reference to the component `T` of the given entity.
|
/// Returns a mutable reference to the component `T` of the given entity.
|
||||||
///
|
///
|
||||||
/// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead.
|
/// In case of a nonexisting entity or mismatched component, a [`QueryComponentError`] is returned instead.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -1090,7 +1090,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
||||||
|
|
||||||
/// Returns a mutable reference to the component `T` of the given entity.
|
/// Returns a mutable reference to the component `T` of the given entity.
|
||||||
///
|
///
|
||||||
/// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead.
|
/// In case of a nonexisting entity or mismatched component, a [`QueryComponentError`] is returned instead.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -1357,12 +1357,69 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> IntoIterator for &'w mut Quer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that occurs when retrieving a specific [`Entity`]'s component from a [`Query`]
|
/// An error that occurs when retrieving a specific [`Entity`]'s component from a [`Query`].
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum QueryComponentError {
|
pub enum QueryComponentError {
|
||||||
|
/// The [`Query`] does not have read access to the requested component.
|
||||||
|
///
|
||||||
|
/// This error occurs when the requested component is not included in the original query.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_ecs::{prelude::*, system::QueryComponentError};
|
||||||
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
|
/// # struct OtherComponent;
|
||||||
|
/// #
|
||||||
|
/// # #[derive(Component, PartialEq, Debug)]
|
||||||
|
/// # struct RequestedComponent;
|
||||||
|
/// #
|
||||||
|
/// # #[derive(Resource)]
|
||||||
|
/// # struct SpecificEntity {
|
||||||
|
/// # entity: Entity,
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// fn get_missing_read_access_error(query: Query<&OtherComponent>, res: Res<SpecificEntity>) {
|
||||||
|
/// assert_eq!(
|
||||||
|
/// query.get_component::<RequestedComponent>(res.entity),
|
||||||
|
/// Err(QueryComponentError::MissingReadAccess),
|
||||||
|
/// );
|
||||||
|
/// println!("query doesn't have read access to RequestedComponent because it does not appear in Query<&OtherComponent>");
|
||||||
|
/// }
|
||||||
|
/// # bevy_ecs::system::assert_is_system(get_missing_read_access_error);
|
||||||
|
/// ```
|
||||||
MissingReadAccess,
|
MissingReadAccess,
|
||||||
|
/// The [`Query`] does not have write access to the requested component.
|
||||||
|
///
|
||||||
|
/// This error occurs when the requested component is not included in the original query, or the mutability of the requested component is mismatched with the original query.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_ecs::{prelude::*, system::QueryComponentError};
|
||||||
|
/// #
|
||||||
|
/// # #[derive(Component, PartialEq, Debug)]
|
||||||
|
/// # struct RequestedComponent;
|
||||||
|
/// #
|
||||||
|
/// # #[derive(Resource)]
|
||||||
|
/// # struct SpecificEntity {
|
||||||
|
/// # entity: Entity,
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// fn get_missing_write_access_error(mut query: Query<&RequestedComponent>, res: Res<SpecificEntity>) {
|
||||||
|
/// assert_eq!(
|
||||||
|
/// query.get_component::<RequestedComponent>(res.entity),
|
||||||
|
/// Err(QueryComponentError::MissingWriteAccess),
|
||||||
|
/// );
|
||||||
|
/// println!("query doesn't have write access to RequestedComponent because it doesn't have &mut in Query<&RequestedComponent>");
|
||||||
|
/// }
|
||||||
|
/// # bevy_ecs::system::assert_is_system(get_missing_write_access_error);
|
||||||
|
/// ```
|
||||||
MissingWriteAccess,
|
MissingWriteAccess,
|
||||||
|
/// The given [`Entity`] does not have the requested component.
|
||||||
MissingComponent,
|
MissingComponent,
|
||||||
|
/// The requested [`Entity`] does not exist.
|
||||||
NoSuchEntity,
|
NoSuchEntity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ use bevy_utils::tracing::warn;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use crate::component::Tick;
|
use crate::component::Tick;
|
||||||
|
use crate::world::unsafe_world_cell::UnsafeWorldCell;
|
||||||
use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Access, world::World};
|
use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Access, world::World};
|
||||||
|
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
@ -39,26 +40,24 @@ pub trait System: Send + Sync + 'static {
|
||||||
fn is_exclusive(&self) -> bool;
|
fn is_exclusive(&self) -> bool;
|
||||||
|
|
||||||
/// Runs the system with the given input in the world. Unlike [`System::run`], this function
|
/// Runs the system with the given input in the world. Unlike [`System::run`], this function
|
||||||
/// takes a shared reference to [`World`] and may therefore break Rust's aliasing rules, making
|
/// can be called in parallel with other systems and may break Rust's aliasing rules
|
||||||
/// it unsafe to call.
|
/// if used incorrectly, making it unsafe to call.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This might access world and resources in an unsafe manner. This should only be called in one
|
/// - The caller must ensure that `world` has permission to access any world data
|
||||||
/// of the following contexts:
|
/// registered in [`Self::archetype_component_access`]. There must be no conflicting
|
||||||
/// 1. This system is the only system running on the given world across all threads.
|
/// simultaneous accesses while the system is running.
|
||||||
/// 2. This system only runs in parallel with other systems that do not conflict with the
|
/// - The method [`Self::update_archetype_component_access`] must be called at some
|
||||||
/// [`System::archetype_component_access()`].
|
/// point before this one, with the same exact [`World`]. If `update_archetype_component_access`
|
||||||
///
|
/// panics (or otherwise does not return for any reason), this method must not be called.
|
||||||
/// Additionally, the method [`Self::update_archetype_component_access`] must be called at some
|
unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out;
|
||||||
/// point before this one, with the same exact [`World`]. If `update_archetype_component_access`
|
|
||||||
/// panics (or otherwise does not return for any reason), this method must not be called.
|
|
||||||
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out;
|
|
||||||
/// Runs the system with the given input in the world.
|
/// Runs the system with the given input in the world.
|
||||||
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
|
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
|
||||||
|
let world = world.as_unsafe_world_cell();
|
||||||
self.update_archetype_component_access(world);
|
self.update_archetype_component_access(world);
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - World and resources are exclusively borrowed, which ensures no data access conflicts.
|
// - We have exclusive access to the entire world.
|
||||||
// - `update_archetype_component_access` has been called.
|
// - `update_archetype_component_access` has been called.
|
||||||
unsafe { self.run_unsafe(input, world) }
|
unsafe { self.run_unsafe(input, world) }
|
||||||
}
|
}
|
||||||
|
@ -66,7 +65,11 @@ pub trait System: Send + Sync + 'static {
|
||||||
/// Initialize the system.
|
/// Initialize the system.
|
||||||
fn initialize(&mut self, _world: &mut World);
|
fn initialize(&mut self, _world: &mut World);
|
||||||
/// Update the system's archetype component [`Access`].
|
/// Update the system's archetype component [`Access`].
|
||||||
fn update_archetype_component_access(&mut self, world: &World);
|
///
|
||||||
|
/// ## Note for implementors
|
||||||
|
/// `world` may only be used to access metadata. This can be done in safe code
|
||||||
|
/// via functions such as [`UnsafeWorldCell::archetypes`].
|
||||||
|
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell);
|
||||||
fn check_change_tick(&mut self, change_tick: Tick);
|
fn check_change_tick(&mut self, change_tick: Tick);
|
||||||
/// Returns the system's default [system sets](crate::schedule::SystemSet).
|
/// Returns the system's default [system sets](crate::schedule::SystemSet).
|
||||||
fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
|
fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
|
||||||
|
|
|
@ -44,10 +44,10 @@ impl FromWorld for WorldId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: Has read-only access to shared World metadata
|
// SAFETY: No world data is accessed.
|
||||||
unsafe impl ReadOnlySystemParam for WorldId {}
|
unsafe impl ReadOnlySystemParam for WorldId {}
|
||||||
|
|
||||||
// SAFETY: A World's ID is immutable and fetching it from the World does not borrow anything
|
// SAFETY: No world data is accessed.
|
||||||
unsafe impl SystemParam for WorldId {
|
unsafe impl SystemParam for WorldId {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ unsafe impl SystemParam for WorldId {
|
||||||
world: UnsafeWorldCell<'world>,
|
world: UnsafeWorldCell<'world>,
|
||||||
_: Tick,
|
_: Tick,
|
||||||
) -> Self::Item<'world, 'state> {
|
) -> Self::Item<'world, 'state> {
|
||||||
world.world_metadata().id()
|
world.id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![warn(unsafe_op_in_unsafe_fn)]
|
#![warn(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
use super::{Mut, World};
|
use super::{Mut, World, WorldId};
|
||||||
use crate::{
|
use crate::{
|
||||||
archetype::{Archetype, ArchetypeComponentId, Archetypes},
|
archetype::{Archetype, ArchetypeComponentId, Archetypes},
|
||||||
bundle::Bundles,
|
bundle::Bundles,
|
||||||
|
@ -190,6 +190,14 @@ impl<'w> UnsafeWorldCell<'w> {
|
||||||
unsafe { &*self.0 }
|
unsafe { &*self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves this world's unique [ID](WorldId).
|
||||||
|
#[inline]
|
||||||
|
pub fn id(self) -> WorldId {
|
||||||
|
// SAFETY:
|
||||||
|
// - we only access world metadata
|
||||||
|
unsafe { self.world_metadata() }.id()
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves this world's [Entities] collection
|
/// Retrieves this world's [Entities] collection
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn entities(self) -> &'w Entities {
|
pub fn entities(self) -> &'w Entities {
|
||||||
|
|
|
@ -472,22 +472,39 @@ impl<P: Point> CubicCurve<P> {
|
||||||
/// A flexible iterator used to sample curves with arbitrary functions.
|
/// A flexible iterator used to sample curves with arbitrary functions.
|
||||||
///
|
///
|
||||||
/// This splits the curve into `subdivisions` of evenly spaced `t` values across the
|
/// This splits the curve into `subdivisions` of evenly spaced `t` values across the
|
||||||
/// length of the curve from start (t = 0) to end (t = 1), returning an iterator that evaluates
|
/// length of the curve from start (t = 0) to end (t = n), where `n = self.segment_count()`,
|
||||||
/// the curve with the supplied `sample_function` at each `t`.
|
/// returning an iterator evaluating the curve with the supplied `sample_function` at each `t`.
|
||||||
///
|
///
|
||||||
/// Given `subdivisions = 2`, this will split the curve into two lines, or three points, and
|
/// For `subdivisions = 2`, this will split the curve into two lines, or three points, and
|
||||||
/// return an iterator over those three points, one at the start, middle, and end.
|
/// return an iterator with 3 items, the three points, one at the start, middle, and end.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_samples(
|
pub fn iter_samples<'a, 'b: 'a>(
|
||||||
&self,
|
&'b self,
|
||||||
subdivisions: usize,
|
subdivisions: usize,
|
||||||
sample_function: fn(&Self, f32) -> P,
|
mut sample_function: impl FnMut(&Self, f32) -> P + 'a,
|
||||||
) -> impl Iterator<Item = P> + '_ {
|
) -> impl Iterator<Item = P> + 'a {
|
||||||
(0..=subdivisions).map(move |i| {
|
self.iter_uniformly(subdivisions)
|
||||||
let segments = self.segments.len() as f32;
|
.map(move |t| sample_function(self, t))
|
||||||
let t = i as f32 / subdivisions as f32 * segments;
|
}
|
||||||
sample_function(self, t)
|
|
||||||
})
|
/// An iterator that returns values of `t` uniformly spaced over `0..=subdivisions`.
|
||||||
|
#[inline]
|
||||||
|
fn iter_uniformly(&self, subdivisions: usize) -> impl Iterator<Item = f32> {
|
||||||
|
let segments = self.segments.len() as f32;
|
||||||
|
let step = segments / subdivisions as f32;
|
||||||
|
(0..=subdivisions).map(move |i| i as f32 * step)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of segments contained in this `CubicCurve`.
|
||||||
|
///
|
||||||
|
/// This spline's global `t` value is equal to how many segments it has.
|
||||||
|
///
|
||||||
|
/// All method accepting `t` on `CubicCurve` depends on the global `t`.
|
||||||
|
/// When sampling over the entire curve, you should either use one of the
|
||||||
|
/// `iter_*` methods or account for the segment count using `curve.segments().len()`.
|
||||||
|
#[inline]
|
||||||
|
pub fn segments(&self) -> &[CubicSegment<P>] {
|
||||||
|
&self.segments
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over the curve split into `subdivisions`, sampling the position at each step.
|
/// Iterate over the curve split into `subdivisions`, sampling the position at each step.
|
||||||
|
|
|
@ -491,6 +491,9 @@ pub fn queue_material_meshes<M: Material>(
|
||||||
AlphaMode::Multiply => {
|
AlphaMode::Multiply => {
|
||||||
mesh_key |= MeshPipelineKey::BLEND_MULTIPLY;
|
mesh_key |= MeshPipelineKey::BLEND_MULTIPLY;
|
||||||
}
|
}
|
||||||
|
AlphaMode::Mask(_) => {
|
||||||
|
mesh_key |= MeshPipelineKey::MAY_DISCARD;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -364,8 +364,8 @@ where
|
||||||
shader_defs.push("DEPTH_PREPASS".into());
|
shader_defs.push("DEPTH_PREPASS".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK) {
|
if key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
|
||||||
shader_defs.push("ALPHA_MASK".into());
|
shader_defs.push("MAY_DISCARD".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let blend_key = key
|
let blend_key = key
|
||||||
|
@ -467,9 +467,7 @@ where
|
||||||
// is enabled or the material uses alpha cutoff values and doesn't rely on the standard
|
// is enabled or the material uses alpha cutoff values and doesn't rely on the standard
|
||||||
// prepass shader
|
// prepass shader
|
||||||
let fragment_required = !targets.is_empty()
|
let fragment_required = !targets.is_empty()
|
||||||
|| ((key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK)
|
|| (key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
|
||||||
|| blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA
|
|
||||||
|| blend_key == MeshPipelineKey::BLEND_ALPHA)
|
|
||||||
&& self.material_fragment_shader.is_some());
|
&& self.material_fragment_shader.is_some());
|
||||||
|
|
||||||
let fragment = fragment_required.then(|| {
|
let fragment = fragment_required.then(|| {
|
||||||
|
@ -967,7 +965,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
|
||||||
let alpha_mode = material.properties.alpha_mode;
|
let alpha_mode = material.properties.alpha_mode;
|
||||||
match alpha_mode {
|
match alpha_mode {
|
||||||
AlphaMode::Opaque => {}
|
AlphaMode::Opaque => {}
|
||||||
AlphaMode::Mask(_) => mesh_key |= MeshPipelineKey::ALPHA_MASK,
|
AlphaMode::Mask(_) => mesh_key |= MeshPipelineKey::MAY_DISCARD,
|
||||||
AlphaMode::Blend
|
AlphaMode::Blend
|
||||||
| AlphaMode::Premultiplied
|
| AlphaMode::Premultiplied
|
||||||
| AlphaMode::Add
|
| AlphaMode::Add
|
||||||
|
|
|
@ -1608,11 +1608,11 @@ pub fn queue_shadows<M: Material>(
|
||||||
}
|
}
|
||||||
let alpha_mode = material.properties.alpha_mode;
|
let alpha_mode = material.properties.alpha_mode;
|
||||||
match alpha_mode {
|
match alpha_mode {
|
||||||
AlphaMode::Mask(_) => {
|
AlphaMode::Mask(_)
|
||||||
mesh_key |= MeshPipelineKey::ALPHA_MASK;
|
| AlphaMode::Blend
|
||||||
}
|
| AlphaMode::Premultiplied
|
||||||
AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add => {
|
| AlphaMode::Add => {
|
||||||
mesh_key |= MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA;
|
mesh_key |= MeshPipelineKey::MAY_DISCARD;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -616,7 +616,8 @@ bitflags::bitflags! {
|
||||||
const DEPTH_PREPASS = (1 << 3);
|
const DEPTH_PREPASS = (1 << 3);
|
||||||
const NORMAL_PREPASS = (1 << 4);
|
const NORMAL_PREPASS = (1 << 4);
|
||||||
const MOTION_VECTOR_PREPASS = (1 << 5);
|
const MOTION_VECTOR_PREPASS = (1 << 5);
|
||||||
const ALPHA_MASK = (1 << 6);
|
const MAY_DISCARD = (1 << 6); // Guards shader codepaths that may discard, allowing early depth tests in most cases
|
||||||
|
// See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
|
||||||
const ENVIRONMENT_MAP = (1 << 7);
|
const ENVIRONMENT_MAP = (1 << 7);
|
||||||
const DEPTH_CLAMP_ORTHO = (1 << 8);
|
const DEPTH_CLAMP_ORTHO = (1 << 8);
|
||||||
const TAA = (1 << 9);
|
const TAA = (1 << 9);
|
||||||
|
@ -830,6 +831,10 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if key.contains(MeshPipelineKey::MAY_DISCARD) {
|
||||||
|
shader_defs.push("MAY_DISCARD".into());
|
||||||
|
}
|
||||||
|
|
||||||
if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
|
if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
|
||||||
shader_defs.push("ENVIRONMENT_MAP".into());
|
shader_defs.push("ENVIRONMENT_MAP".into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,20 @@ fn alpha_discard(material: StandardMaterial, output_color: vec4<f32>) -> vec4<f3
|
||||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
|
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
|
||||||
// NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
|
// NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
|
||||||
color.a = 1.0;
|
color.a = 1.0;
|
||||||
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
}
|
||||||
|
|
||||||
|
#ifdef MAY_DISCARD
|
||||||
|
else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
||||||
if color.a >= material.alpha_cutoff {
|
if color.a >= material.alpha_cutoff {
|
||||||
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
|
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
|
||||||
color.a = 1.0;
|
color.a = 1.0;
|
||||||
} else {
|
} else {
|
||||||
// NOTE: output_color.a < in.material.alpha_cutoff should not is not rendered
|
// NOTE: output_color.a < in.material.alpha_cutoff should not be rendered
|
||||||
// NOTE: This and any other discards mean that early-z testing cannot be done!
|
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,19 +30,7 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05;
|
||||||
// We can use a simplified version of alpha_discard() here since we only need to handle the alpha_cutoff
|
// We can use a simplified version of alpha_discard() here since we only need to handle the alpha_cutoff
|
||||||
fn prepass_alpha_discard(in: FragmentInput) {
|
fn prepass_alpha_discard(in: FragmentInput) {
|
||||||
|
|
||||||
// This is a workaround since the preprocessor does not support
|
#ifdef MAY_DISCARD
|
||||||
// #if defined(ALPHA_MASK) || defined(BLEND_PREMULTIPLIED_ALPHA)
|
|
||||||
#ifndef ALPHA_MASK
|
|
||||||
#ifndef BLEND_PREMULTIPLIED_ALPHA
|
|
||||||
#ifndef BLEND_ALPHA
|
|
||||||
|
|
||||||
#define EMPTY_PREPASS_ALPHA_DISCARD
|
|
||||||
|
|
||||||
#endif // BLEND_ALPHA
|
|
||||||
#endif // BLEND_PREMULTIPLIED_ALPHA not defined
|
|
||||||
#endif // ALPHA_MASK not defined
|
|
||||||
|
|
||||||
#ifndef EMPTY_PREPASS_ALPHA_DISCARD
|
|
||||||
var output_color: vec4<f32> = material.base_color;
|
var output_color: vec4<f32> = material.base_color;
|
||||||
|
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
|
@ -51,22 +39,22 @@ fn prepass_alpha_discard(in: FragmentInput) {
|
||||||
}
|
}
|
||||||
#endif // VERTEX_UVS
|
#endif // VERTEX_UVS
|
||||||
|
|
||||||
#ifdef ALPHA_MASK
|
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) && output_color.a < material.alpha_cutoff {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
#else // BLEND_PREMULTIPLIED_ALPHA || BLEND_ALPHA
|
|
||||||
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||||
if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD)
|
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
||||||
&& output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
|
if output_color.a < material.alpha_cutoff {
|
||||||
discard;
|
discard;
|
||||||
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED
|
}
|
||||||
&& all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
|
} else if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) {
|
||||||
discard;
|
if output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED {
|
||||||
|
if all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // !ALPHA_MASK
|
|
||||||
|
|
||||||
#endif // EMPTY_PREPASS_ALPHA_DISCARD not defined
|
#endif // MAY_DISCARD
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PREPASS_FRAGMENT
|
#ifdef PREPASS_FRAGMENT
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::fq_std::FQDefault;
|
use crate::derive_data::StructField;
|
||||||
|
use crate::field_attributes::DefaultBehavior;
|
||||||
|
use crate::fq_std::{FQDefault, FQOption};
|
||||||
use crate::{
|
use crate::{
|
||||||
derive_data::{EnumVariantFields, ReflectEnum},
|
derive_data::{EnumVariantFields, ReflectEnum},
|
||||||
utility::ident_or_index,
|
utility::ident_or_index,
|
||||||
};
|
};
|
||||||
use proc_macro2::Ident;
|
use proc_macro2::Ident;
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::Member;
|
||||||
|
|
||||||
/// Contains all data needed to construct all variants within an enum.
|
/// Contains all data needed to construct all variants within an enum.
|
||||||
pub(crate) struct EnumVariantConstructors {
|
pub(crate) struct EnumVariantConstructors {
|
||||||
|
@ -30,7 +33,7 @@ pub(crate) fn get_variant_constructors(
|
||||||
let name = ident.to_string();
|
let name = ident.to_string();
|
||||||
let variant_constructor = reflect_enum.get_unit(ident);
|
let variant_constructor = reflect_enum.get_unit(ident);
|
||||||
|
|
||||||
let fields = match &variant.fields {
|
let fields: &[StructField] = match &variant.fields {
|
||||||
EnumVariantFields::Unit => &[],
|
EnumVariantFields::Unit => &[],
|
||||||
EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => {
|
EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => {
|
||||||
fields.as_slice()
|
fields.as_slice()
|
||||||
|
@ -39,35 +42,59 @@ pub(crate) fn get_variant_constructors(
|
||||||
let mut reflect_index: usize = 0;
|
let mut reflect_index: usize = 0;
|
||||||
let constructor_fields = fields.iter().enumerate().map(|(declare_index, field)| {
|
let constructor_fields = fields.iter().enumerate().map(|(declare_index, field)| {
|
||||||
let field_ident = ident_or_index(field.data.ident.as_ref(), declare_index);
|
let field_ident = ident_or_index(field.data.ident.as_ref(), declare_index);
|
||||||
|
let field_ty = &field.data.ty;
|
||||||
|
|
||||||
let field_value = if field.attrs.ignore.is_ignored() {
|
let field_value = if field.attrs.ignore.is_ignored() {
|
||||||
quote! { #FQDefault::default() }
|
match &field.attrs.default {
|
||||||
|
DefaultBehavior::Func(path) => quote! { #path() },
|
||||||
|
_ => quote! { #FQDefault::default() }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let error_repr = field.data.ident.as_ref().map_or_else(
|
let (resolve_error, resolve_missing) = if can_panic {
|
||||||
|| format!("at index {reflect_index}"),
|
let field_ref_str = match &field_ident {
|
||||||
|name| format!("`{name}`"),
|
Member::Named(ident) => format!("the field `{ident}`"),
|
||||||
);
|
Member::Unnamed(index) => format!("the field at index {}", index.index)
|
||||||
let unwrapper = if can_panic {
|
};
|
||||||
let type_err_message = format!(
|
let ty = field.data.ty.to_token_stream();
|
||||||
"the field {error_repr} should be of type `{}`",
|
|
||||||
field.data.ty.to_token_stream()
|
let on_error = format!("{field_ref_str} should be of type `{ty}`");
|
||||||
);
|
let on_missing = format!("{field_ref_str} is required but could not be found");
|
||||||
quote!(.expect(#type_err_message))
|
|
||||||
|
(quote!(.expect(#on_error)), quote!(.expect(#on_missing)))
|
||||||
} else {
|
} else {
|
||||||
quote!(?)
|
(quote!(?), quote!(?))
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_accessor = match &field.data.ident {
|
let field_accessor = match &field.data.ident {
|
||||||
Some(ident) => {
|
Some(ident) => {
|
||||||
let name = ident.to_string();
|
let name = ident.to_string();
|
||||||
quote!(.field(#name))
|
quote!(#ref_value.field(#name))
|
||||||
}
|
}
|
||||||
None => quote!(.field_at(#reflect_index)),
|
None => quote!(#ref_value.field_at(#reflect_index)),
|
||||||
};
|
};
|
||||||
reflect_index += 1;
|
reflect_index += 1;
|
||||||
let missing_field_err_message = format!("the field {error_repr} was not declared");
|
|
||||||
let accessor = quote!(#field_accessor .expect(#missing_field_err_message));
|
match &field.attrs.default {
|
||||||
quote! {
|
DefaultBehavior::Func(path) => quote! {
|
||||||
#bevy_reflect_path::FromReflect::from_reflect(#ref_value #accessor)
|
if let #FQOption::Some(field) = #field_accessor {
|
||||||
#unwrapper
|
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
|
||||||
|
#resolve_error
|
||||||
|
} else {
|
||||||
|
#path()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DefaultBehavior::Default => quote! {
|
||||||
|
if let #FQOption::Some(field) = #field_accessor {
|
||||||
|
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
|
||||||
|
#resolve_error
|
||||||
|
} else {
|
||||||
|
#FQDefault::default()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DefaultBehavior::Required => quote! {
|
||||||
|
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#field_accessor #resolve_missing)
|
||||||
|
#resolve_error
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
quote! { #field_ident : #field_value }
|
quote! { #field_ident : #field_value }
|
||||||
|
|
|
@ -87,7 +87,8 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
|
||||||
/// to improve performance and/or robustness.
|
/// to improve performance and/or robustness.
|
||||||
/// An example of where this is used is in the [`FromReflect`] derive macro,
|
/// An example of where this is used is in the [`FromReflect`] derive macro,
|
||||||
/// where adding this attribute will cause the `FromReflect` implementation to create
|
/// where adding this attribute will cause the `FromReflect` implementation to create
|
||||||
/// a base value using its [`Default`] implementation avoiding issues with ignored fields.
|
/// a base value using its [`Default`] implementation avoiding issues with ignored fields
|
||||||
|
/// (for structs and tuple structs only).
|
||||||
///
|
///
|
||||||
/// ## `#[reflect_value]`
|
/// ## `#[reflect_value]`
|
||||||
///
|
///
|
||||||
|
|
|
@ -751,6 +751,39 @@ mod tests {
|
||||||
assert_eq!(Some(expected), my_struct);
|
assert_eq!(Some(expected), my_struct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_reflect_should_use_default_variant_field_attributes() {
|
||||||
|
#[derive(Reflect, FromReflect, Eq, PartialEq, Debug)]
|
||||||
|
enum MyEnum {
|
||||||
|
Foo(#[reflect(default)] String),
|
||||||
|
Bar {
|
||||||
|
#[reflect(default = "get_baz_default")]
|
||||||
|
#[reflect(ignore)]
|
||||||
|
baz: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_baz_default() -> usize {
|
||||||
|
123
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected = MyEnum::Foo(String::default());
|
||||||
|
|
||||||
|
let dyn_enum = DynamicEnum::new("Foo", DynamicTuple::default());
|
||||||
|
let my_enum = <MyEnum as FromReflect>::from_reflect(&dyn_enum);
|
||||||
|
|
||||||
|
assert_eq!(Some(expected), my_enum);
|
||||||
|
|
||||||
|
let expected = MyEnum::Bar {
|
||||||
|
baz: get_baz_default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dyn_enum = DynamicEnum::new("Bar", DynamicStruct::default());
|
||||||
|
let my_enum = <MyEnum as FromReflect>::from_reflect(&dyn_enum);
|
||||||
|
|
||||||
|
assert_eq!(Some(expected), my_enum);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_reflect_should_use_default_container_attribute() {
|
fn from_reflect_should_use_default_container_attribute() {
|
||||||
#[derive(Reflect, FromReflect, Eq, PartialEq, Debug)]
|
#[derive(Reflect, FromReflect, Eq, PartialEq, Debug)]
|
||||||
|
|
|
@ -199,7 +199,7 @@ impl Image {
|
||||||
.map(DynamicImage::ImageRgba8),
|
.map(DynamicImage::ImageRgba8),
|
||||||
// This format is commonly used as the format for the swapchain texture
|
// This format is commonly used as the format for the swapchain texture
|
||||||
// This conversion is added here to support screenshots
|
// This conversion is added here to support screenshots
|
||||||
TextureFormat::Bgra8UnormSrgb => ImageBuffer::from_raw(
|
TextureFormat::Bgra8UnormSrgb | TextureFormat::Bgra8Unorm => ImageBuffer::from_raw(
|
||||||
self.texture_descriptor.size.width,
|
self.texture_descriptor.size.width,
|
||||||
self.texture_descriptor.size.height,
|
self.texture_descriptor.size.height,
|
||||||
{
|
{
|
||||||
|
|
|
@ -377,7 +377,7 @@ pub fn prepare_windows(
|
||||||
mip_level_count: 1,
|
mip_level_count: 1,
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
format: surface_configuration.format,
|
format: surface_configuration.format.add_srgb_suffix(),
|
||||||
usage: TextureUsages::RENDER_ATTACHMENT
|
usage: TextureUsages::RENDER_ATTACHMENT
|
||||||
| TextureUsages::COPY_SRC
|
| TextureUsages::COPY_SRC
|
||||||
| TextureUsages::TEXTURE_BINDING,
|
| TextureUsages::TEXTURE_BINDING,
|
||||||
|
|
|
@ -234,16 +234,11 @@ impl SpecializedRenderPipeline for ScreenshotToScreenPipeline {
|
||||||
shader: SCREENSHOT_SHADER_HANDLE.typed(),
|
shader: SCREENSHOT_SHADER_HANDLE.typed(),
|
||||||
},
|
},
|
||||||
primitive: wgpu::PrimitiveState {
|
primitive: wgpu::PrimitiveState {
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
strip_index_format: None,
|
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
|
||||||
cull_mode: Some(wgpu::Face::Back),
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
polygon_mode: wgpu::PolygonMode::Fill,
|
..Default::default()
|
||||||
conservative: false,
|
|
||||||
unclipped_depth: false,
|
|
||||||
},
|
},
|
||||||
depth_stencil: None,
|
depth_stencil: None,
|
||||||
multisample: wgpu::MultisampleState::default(),
|
multisample: Default::default(),
|
||||||
fragment: Some(FragmentState {
|
fragment: Some(FragmentState {
|
||||||
shader: SCREENSHOT_SHADER_HANDLE.typed(),
|
shader: SCREENSHOT_SHADER_HANDLE.typed(),
|
||||||
entry_point: Cow::Borrowed("fs_main"),
|
entry_point: Cow::Borrowed("fs_main"),
|
||||||
|
|
|
@ -10,7 +10,7 @@ keywords = ["bevy"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
trace = []
|
trace = []
|
||||||
wayland = ["winit/wayland"]
|
wayland = ["winit/wayland", "winit/wayland-csd-adwaita"]
|
||||||
x11 = ["winit/x11"]
|
x11 = ["winit/x11"]
|
||||||
accesskit_unix = ["accesskit_winit/accesskit_unix"]
|
accesskit_unix = ["accesskit_winit/accesskit_unix"]
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ fn star(
|
||||||
// We can now spawn the entities for the star and the camera
|
// We can now spawn the entities for the star and the camera
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
// We use a marker component to identify the custom colored meshes
|
// We use a marker component to identify the custom colored meshes
|
||||||
ColoredMesh2d::default(),
|
ColoredMesh2d,
|
||||||
// The `Handle<Mesh>` needs to be wrapped in a `Mesh2dHandle` to use 2d rendering instead of 3d
|
// The `Handle<Mesh>` needs to be wrapped in a `Mesh2dHandle` to use 2d rendering instead of 3d
|
||||||
Mesh2dHandle(meshes.add(star)),
|
Mesh2dHandle(meshes.add(star)),
|
||||||
// This bundle's components are needed for something to be rendered
|
// This bundle's components are needed for something to be rendered
|
||||||
|
|
|
@ -302,7 +302,7 @@ fn example_control_system(
|
||||||
let randomize_colors = input.just_pressed(KeyCode::C);
|
let randomize_colors = input.just_pressed(KeyCode::C);
|
||||||
|
|
||||||
for (material_handle, controls) in &controllable {
|
for (material_handle, controls) in &controllable {
|
||||||
let mut material = materials.get_mut(material_handle).unwrap();
|
let material = materials.get_mut(material_handle).unwrap();
|
||||||
material.base_color.set_a(state.alpha);
|
material.base_color.set_a(state.alpha);
|
||||||
|
|
||||||
if controls.color && randomize_colors {
|
if controls.color && randomize_colors {
|
||||||
|
|
|
@ -378,7 +378,7 @@ fn update_normal(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(normal) = normal.0.as_ref() {
|
if let Some(normal) = normal.0.as_ref() {
|
||||||
if let Some(mut image) = images.get_mut(normal) {
|
if let Some(image) = images.get_mut(normal) {
|
||||||
image.texture_descriptor.format = TextureFormat::Rgba8Unorm;
|
image.texture_descriptor.format = TextureFormat::Rgba8Unorm;
|
||||||
*already_ran = true;
|
*already_ran = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ fn asset_loaded(
|
||||||
&& asset_server.get_load_state(cubemap.image_handle.clone_weak()) == LoadState::Loaded
|
&& asset_server.get_load_state(cubemap.image_handle.clone_weak()) == LoadState::Loaded
|
||||||
{
|
{
|
||||||
info!("Swapping to {}...", CUBEMAPS[cubemap.index].0);
|
info!("Swapping to {}...", CUBEMAPS[cubemap.index].0);
|
||||||
let mut image = images.get_mut(&cubemap.image_handle).unwrap();
|
let image = images.get_mut(&cubemap.image_handle).unwrap();
|
||||||
// NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
|
// NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
|
||||||
// so they appear as one texture. The following code reconfigures the texture as necessary.
|
// so they appear as one texture. The following code reconfigures the texture as necessary.
|
||||||
if image.texture_descriptor.array_layer_count() == 1 {
|
if image.texture_descriptor.array_layer_count() == 1 {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rand::{thread_rng, Rng};
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, (light_sway, movement))
|
.add_systems(Update, (light_sway, movement))
|
||||||
|
|
|
@ -430,7 +430,7 @@ fn update_color_grading_settings(
|
||||||
mut selected_parameter: ResMut<SelectedParameter>,
|
mut selected_parameter: ResMut<SelectedParameter>,
|
||||||
) {
|
) {
|
||||||
let method = tonemapping.single();
|
let method = tonemapping.single();
|
||||||
let mut color_grading = per_method_settings.settings.get_mut(method).unwrap();
|
let color_grading = per_method_settings.settings.get_mut(method).unwrap();
|
||||||
let mut dt = time.delta_seconds() * 0.25;
|
let mut dt = time.delta_seconds() * 0.25;
|
||||||
if keys.pressed(KeyCode::Left) {
|
if keys.pressed(KeyCode::Left) {
|
||||||
dt = -dt;
|
dt = -dt;
|
||||||
|
|
|
@ -9,7 +9,7 @@ fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
// Adds frame time diagnostics
|
// Adds frame time diagnostics
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
// Adds a system that prints diagnostics to the console
|
// Adds a system that prints diagnostics to the console
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
// Any plugin can register diagnostics
|
// Any plugin can register diagnostics
|
||||||
|
|
|
@ -18,15 +18,20 @@ fn main() {
|
||||||
// The common_conditions module has a few useful run conditions
|
// The common_conditions module has a few useful run conditions
|
||||||
// for checking resources and states. These are included in the prelude.
|
// for checking resources and states. These are included in the prelude.
|
||||||
.run_if(resource_exists::<InputCounter>())
|
.run_if(resource_exists::<InputCounter>())
|
||||||
// This is a custom run condition, defined using a system that returns
|
// `.or_else()` is a run condition combinator that only evaluates the second condition
|
||||||
// a `bool` and which has read-only `SystemParam`s.
|
// if the first condition returns `false`. This behavior is known as "short-circuiting",
|
||||||
// Both run conditions must return `true` in order for the system to run.
|
// and is how the `||` operator works in Rust (as well as most C-family languages).
|
||||||
// Note that this second run condition will be evaluated even if the first returns `false`.
|
// In this case, the `has_user_input` run condition will be evaluated since the `Unused` resource has not been initialized.
|
||||||
.run_if(has_user_input),
|
.run_if(resource_exists::<Unused>().or_else(
|
||||||
|
// This is a custom run condition, defined using a system that returns
|
||||||
|
// a `bool` and which has read-only `SystemParam`s.
|
||||||
|
// Both run conditions must return `true` in order for the system to run.
|
||||||
|
// Note that this second run condition will be evaluated even if the first returns `false`.
|
||||||
|
has_user_input,
|
||||||
|
)),
|
||||||
print_input_counter
|
print_input_counter
|
||||||
// `.and_then()` is a run condition combinator that only evaluates the second condition
|
// `.and_then()` is a run condition combinator that only evaluates the second condition
|
||||||
// if the first condition returns `true`. This behavior is known as "short-circuiting",
|
// if the first condition returns `true`, analogous to the `&&` operator.
|
||||||
// and is how the `&&` operator works in Rust (as well as most C-family languages).
|
|
||||||
// In this case, the short-circuiting behavior prevents the second run condition from
|
// In this case, the short-circuiting behavior prevents the second run condition from
|
||||||
// panicking if the `InputCounter` resource has not been initialized.
|
// panicking if the `InputCounter` resource has not been initialized.
|
||||||
.run_if(resource_exists::<InputCounter>().and_then(
|
.run_if(resource_exists::<InputCounter>().and_then(
|
||||||
|
@ -51,6 +56,9 @@ fn main() {
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
struct InputCounter(usize);
|
struct InputCounter(usize);
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct Unused;
|
||||||
|
|
||||||
/// Return true if any of the defined inputs were just pressed.
|
/// Return true if any of the defined inputs were just pressed.
|
||||||
/// This is a custom run condition, it can take any normal system parameters as long as
|
/// This is a custom run condition, it can take any normal system parameters as long as
|
||||||
/// they are read only (except for local parameters which can be mutable).
|
/// they are read only (except for local parameters which can be mutable).
|
||||||
|
|
|
@ -334,7 +334,7 @@ fn contributors() -> Result<Contributors, LoadContributorsError> {
|
||||||
|
|
||||||
let contributors = BufReader::new(stdout)
|
let contributors = BufReader::new(stdout)
|
||||||
.lines()
|
.lines()
|
||||||
.filter_map(|x| x.ok())
|
.map_while(|x| x.ok())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(contributors)
|
Ok(contributors)
|
||||||
|
|
|
@ -28,6 +28,8 @@ fn main() {
|
||||||
})
|
})
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, (rotate, toggle_prepass_view))
|
.add_systems(Update, (rotate, toggle_prepass_view))
|
||||||
|
// Disabling MSAA for maximum compatibility. Shader prepass with MSAA needs GPU capability MULTISAMPLED_SHADING
|
||||||
|
.insert_resource(Msaa::Off)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.insert_resource(BevyCounter {
|
.insert_resource(BevyCounter {
|
||||||
count: 0,
|
count: 0,
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
// Since this is also used as a benchmark, we want it to display performance data.
|
// Since this is also used as a benchmark, we want it to display performance data.
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
primary_window: Some(Window {
|
primary_window: Some(Window {
|
||||||
present_mode: PresentMode::AutoNoVsync,
|
present_mode: PresentMode::AutoNoVsync,
|
||||||
|
|
|
@ -30,7 +30,7 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, button_system);
|
.add_systems(Update, button_system);
|
||||||
|
|
|
@ -28,7 +28,7 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, (move_camera, print_mesh_count))
|
.add_systems(Update, (move_camera, print_mesh_count))
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.insert_resource(Config {
|
.insert_resource(Config {
|
||||||
line_count: 50_000,
|
line_count: 50_000,
|
||||||
fancy: false,
|
fancy: false,
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_systems(Startup, setup);
|
.add_systems(Startup, setup);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, (move_camera, print_light_count))
|
.add_systems(Update, (move_camera, print_light_count))
|
||||||
|
|
|
@ -29,7 +29,7 @@ fn main() {
|
||||||
))
|
))
|
||||||
// Since this is also used as a benchmark, we want it to display performance data.
|
// Since this is also used as a benchmark, we want it to display performance data.
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
primary_window: Some(Window {
|
primary_window: Some(Window {
|
||||||
present_mode: PresentMode::AutoNoVsync,
|
present_mode: PresentMode::AutoNoVsync,
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_plugin(LogDiagnosticsPlugin::default())
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
.add_systems(Startup, spawn)
|
.add_systems(Startup, spawn)
|
||||||
.add_systems(Update, update_text_bounds)
|
.add_systems(Update, update_text_bounds)
|
||||||
|
|
|
@ -184,7 +184,7 @@ fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.insert_resource(cfg)
|
.insert_resource(cfg)
|
||||||
.add_plugins(MinimalPlugins)
|
.add_plugins(MinimalPlugins)
|
||||||
.add_plugin(TransformPlugin::default())
|
.add_plugin(TransformPlugin)
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
// Updating transforms *must* be done before `CoreSet::PostUpdate`
|
// Updating transforms *must* be done before `CoreSet::PostUpdate`
|
||||||
// or the hierarchy will momentarily be in an invalid state.
|
// or the hierarchy will momentarily be in an invalid state.
|
||||||
|
|
|
@ -11,7 +11,7 @@ use bevy::{
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, (text_update_system, text_color_system))
|
.add_systems(Update, (text_update_system, text_color_system))
|
||||||
.run();
|
.run();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
///! This example illustrates how to resize windows, and how to respond to a window being resized.
|
//! This example illustrates how to resize windows, and how to respond to a window being resized.
|
||||||
use bevy::{prelude::*, window::WindowResized};
|
use bevy::{prelude::*, window::WindowResized};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
Loading…
Reference in a new issue