mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 22:50:19 +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"
|
||||
description = "Showcases wireframe rendering"
|
||||
category = "3D Rendering"
|
||||
wasm = true
|
||||
wasm = false
|
||||
|
||||
[[example]]
|
||||
name = "no_prepass"
|
||||
|
|
|
@ -15,9 +15,14 @@ var<uniform> settings: ShowPrepassSettings;
|
|||
@fragment
|
||||
fn fragment(
|
||||
@builtin(position) frag_coord: vec4<f32>,
|
||||
#ifdef MULTISAMPLED
|
||||
@builtin(sample_index) sample_index: u32,
|
||||
#endif
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
) -> @location(0) vec4<f32> {
|
||||
#ifndef MULTISAMPLED
|
||||
let sample_index = 0u;
|
||||
#endif
|
||||
if settings.show_depth == 1u {
|
||||
let depth = prepass_depth(frag_coord, sample_index);
|
||||
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);
|
||||
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));
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ impl Benchmark {
|
|||
|
||||
let mut system = IntoSystem::into_system(query_system);
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
|
@ -291,7 +291,7 @@ pub fn query_get_component_simple(criterion: &mut Criterion) {
|
|||
|
||||
let mut system = IntoSystem::into_system(query_system);
|
||||
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));
|
||||
});
|
||||
|
|
|
@ -134,9 +134,7 @@ pub enum Tonemapping {
|
|||
/// Suffers from lots hue shifting, brights don't desaturate naturally.
|
||||
/// Bright primaries and secondaries don't desaturate at all.
|
||||
Reinhard,
|
||||
/// Current bevy default. Likely to change in the future.
|
||||
/// Suffers from hue shifting. Brights don't desaturate much at all across the spectrum.
|
||||
#[default]
|
||||
ReinhardLuminance,
|
||||
/// Same base implementation that Godot 4.0 uses for Tonemap ACES.
|
||||
/// <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
|
||||
/// VFX to look good without hue shifting.
|
||||
SomewhatBoringDisplayTransform,
|
||||
/// Current Bevy default.
|
||||
/// By Tomasz Stachowiak
|
||||
/// <https://github.com/h3r2tic/tony-mc-mapface>
|
||||
/// 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).
|
||||
/// 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.
|
||||
#[default]
|
||||
TonyMcMapface,
|
||||
/// Default Filmic Display Transform from blender.
|
||||
/// Somewhat neutral. Suffers from hue shifting. Brights desaturate across the spectrum.
|
||||
|
@ -328,7 +328,7 @@ pub fn get_lut_bindings<'a>(
|
|||
bindings: [u32; 2],
|
||||
) -> [BindGroupEntry<'a>; 2] {
|
||||
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::Reinhard
|
||||
| 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
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
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),
|
||||
/// The given [`Entity`] does not exist.
|
||||
NoSuchEntity(Entity),
|
||||
/// The [`Entity`] was requested mutably more than once.
|
||||
///
|
||||
/// See [`QueryState::get_many_mut`] for an example.
|
||||
AliasedMutability(Entity),
|
||||
}
|
||||
|
||||
|
@ -1177,7 +1184,7 @@ impl fmt::Display for QueryEntityError {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
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::AliasedMutability(_) => {
|
||||
|
@ -1296,11 +1303,13 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// An error that occurs when evaluating a [`QueryState`] as a single expected resulted via
|
||||
/// [`QueryState::single`] or [`QueryState::single_mut`].
|
||||
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`] as a single expected result via
|
||||
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
|
||||
#[derive(Debug)]
|
||||
pub enum QuerySingleError {
|
||||
/// No entity fits the query.
|
||||
NoEntities(&'static str),
|
||||
/// Multiple entities fit the query.
|
||||
MultipleEntities(&'static str),
|
||||
}
|
||||
|
||||
|
|
|
@ -5,15 +5,58 @@ use std::ops::Not;
|
|||
use crate::component::{self, ComponentId};
|
||||
use crate::query::Access;
|
||||
use crate::system::{CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System};
|
||||
use crate::world::unsafe_world_cell::UnsafeWorldCell;
|
||||
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.
|
||||
///
|
||||
/// 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.
|
||||
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`
|
||||
/// 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`].
|
||||
///
|
||||
/// [`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 b = IntoSystem::into_system(and_then);
|
||||
let name = format!("{} && {}", a.name(), b.name());
|
||||
|
@ -105,7 +148,7 @@ pub trait Condition<Marker>: sealed::Condition<Marker> {
|
|||
/// # app.run(&mut world);
|
||||
/// # 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 b = IntoSystem::into_system(or_else);
|
||||
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 {
|
||||
use crate::system::{IntoSystem, ReadOnlySystem};
|
||||
|
||||
pub trait Condition<Marker>:
|
||||
IntoSystem<(), bool, Marker, System = Self::ReadOnlySystem>
|
||||
pub trait Condition<Marker, In>:
|
||||
IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>
|
||||
{
|
||||
// This associated type is necessary to let the compiler
|
||||
// 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
|
||||
F: IntoSystem<(), bool, Marker>,
|
||||
F: IntoSystem<In, bool, Marker>,
|
||||
F::System: ReadOnlySystem,
|
||||
{
|
||||
type ReadOnlySystem = F::System;
|
||||
|
@ -990,7 +1033,7 @@ where
|
|||
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.
|
||||
!self.condition.run_unsafe(input, world)
|
||||
}
|
||||
|
@ -1007,7 +1050,7 @@ where
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ use crate::{
|
|||
is_apply_system_buffers, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
|
||||
},
|
||||
system::BoxedSystem,
|
||||
world::World,
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, World},
|
||||
};
|
||||
|
||||
use crate as bevy_ecs;
|
||||
|
@ -184,7 +184,6 @@ impl SystemExecutor for MultiThreadedExecutor {
|
|||
.map(|e| e.0.clone());
|
||||
let thread_executor = thread_executor.as_deref();
|
||||
|
||||
let world = SyncUnsafeCell::from_mut(world);
|
||||
let SyncUnsafeSchedule {
|
||||
systems,
|
||||
mut conditions,
|
||||
|
@ -197,10 +196,13 @@ impl SystemExecutor for MultiThreadedExecutor {
|
|||
// the executor itself is a `Send` future so that it can run
|
||||
// alongside systems that claim the local thread
|
||||
let executor = async {
|
||||
let world_cell = world.as_unsafe_world_cell();
|
||||
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 {
|
||||
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 {
|
||||
|
@ -231,7 +233,7 @@ impl SystemExecutor for MultiThreadedExecutor {
|
|||
if self.apply_final_buffers {
|
||||
// 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
|
||||
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 {
|
||||
let mut panic_payload = self.panic_payload.lock().unwrap();
|
||||
*panic_payload = Some(payload);
|
||||
|
@ -283,14 +285,16 @@ impl MultiThreadedExecutor {
|
|||
}
|
||||
|
||||
/// # Safety
|
||||
/// Caller must ensure that `self.ready_systems` does not contain any systems that
|
||||
/// have been mutably borrowed (such as the systems currently running).
|
||||
/// - Caller must ensure that `self.ready_systems` does not contain any systems that
|
||||
/// 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>(
|
||||
&mut self,
|
||||
scope: &Scope<'_, 'scope, ()>,
|
||||
systems: &'scope [SyncUnsafeCell<BoxedSystem>],
|
||||
conditions: &mut Conditions,
|
||||
cell: &'scope SyncUnsafeCell<World>,
|
||||
world_cell: UnsafeWorldCell<'scope>,
|
||||
) {
|
||||
if self.exclusive_running {
|
||||
return;
|
||||
|
@ -307,10 +311,7 @@ impl MultiThreadedExecutor {
|
|||
// Therefore, no other reference to this system exists and there is no aliasing.
|
||||
let system = unsafe { &mut *systems[system_index].get() };
|
||||
|
||||
// SAFETY: No exclusive system is running.
|
||||
// Therefore, there is no existing mutable reference to the world.
|
||||
let world = unsafe { &*cell.get() };
|
||||
if !self.can_run(system_index, system, conditions, world) {
|
||||
if !self.can_run(system_index, system, conditions, world_cell) {
|
||||
// NOTE: exclusive systems with ambiguities are susceptible to
|
||||
// being significantly displaced here (compared to single-threaded order)
|
||||
// if systems after them in topological order can run
|
||||
|
@ -320,9 +321,10 @@ impl MultiThreadedExecutor {
|
|||
|
||||
self.ready_systems.set(system_index, false);
|
||||
|
||||
// SAFETY: Since `self.can_run` returned true earlier, it must have called
|
||||
// `update_archetype_component_access` for each run condition.
|
||||
if !self.should_run(system_index, system, conditions, world) {
|
||||
// SAFETY: `can_run` returned true, which means that:
|
||||
// - It must have called `update_archetype_component_access` for each run condition.
|
||||
// - 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);
|
||||
continue;
|
||||
}
|
||||
|
@ -331,10 +333,12 @@ impl MultiThreadedExecutor {
|
|||
self.num_running_systems += 1;
|
||||
|
||||
if self.system_task_metadata[system_index].is_exclusive {
|
||||
// SAFETY: `can_run` confirmed that no systems are running.
|
||||
// Therefore, there is no existing reference to the world.
|
||||
// SAFETY: `can_run` returned true for this system, which means
|
||||
// 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 {
|
||||
let world = &mut *cell.get();
|
||||
self.spawn_exclusive_system_task(scope, system_index, systems, world);
|
||||
}
|
||||
break;
|
||||
|
@ -342,9 +346,10 @@ impl MultiThreadedExecutor {
|
|||
|
||||
// SAFETY:
|
||||
// - 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 {
|
||||
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: &mut BoxedSystem,
|
||||
conditions: &mut Conditions,
|
||||
world: &World,
|
||||
world: UnsafeWorldCell,
|
||||
) -> bool {
|
||||
let system_meta = &self.system_task_metadata[system_index];
|
||||
if system_meta.is_exclusive && self.num_running_systems > 0 {
|
||||
|
@ -413,15 +418,17 @@ impl MultiThreadedExecutor {
|
|||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `update_archetype_component` must have been called with `world`
|
||||
/// for each run condition in `conditions`.
|
||||
/// * `world` must have permission to read any world data required by
|
||||
/// the system's conditions: this includes conditions for the system
|
||||
/// 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(
|
||||
&mut self,
|
||||
system_index: usize,
|
||||
_system: &BoxedSystem,
|
||||
conditions: &mut Conditions,
|
||||
world: &World,
|
||||
world: UnsafeWorldCell,
|
||||
) -> bool {
|
||||
let mut should_run = !self.skipped_systems.contains(system_index);
|
||||
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.
|
||||
// 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 =
|
||||
evaluate_and_fold_conditions(&mut conditions.set_conditions[set_idx], world);
|
||||
|
||||
|
@ -444,7 +454,10 @@ impl MultiThreadedExecutor {
|
|||
}
|
||||
|
||||
// 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 =
|
||||
evaluate_and_fold_conditions(&mut conditions.system_conditions[system_index], world);
|
||||
|
||||
|
@ -459,6 +472,8 @@ impl MultiThreadedExecutor {
|
|||
|
||||
/// # Safety
|
||||
/// - 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`
|
||||
/// on the system assocaited with `system_index`.
|
||||
unsafe fn spawn_system_task<'scope>(
|
||||
|
@ -466,7 +481,7 @@ impl MultiThreadedExecutor {
|
|||
scope: &Scope<'_, 'scope, ()>,
|
||||
system_index: usize,
|
||||
systems: &'scope [SyncUnsafeCell<BoxedSystem>],
|
||||
world: &'scope World,
|
||||
world: UnsafeWorldCell<'scope>,
|
||||
) {
|
||||
// SAFETY: this system is not running, no other reference exists
|
||||
let system = unsafe { &mut *systems[system_index].get() };
|
||||
|
@ -483,7 +498,8 @@ impl MultiThreadedExecutor {
|
|||
let system_guard = system_span.enter();
|
||||
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
// 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.
|
||||
unsafe { system.run_unsafe((), world) };
|
||||
}));
|
||||
|
@ -688,10 +704,14 @@ fn apply_system_buffers(
|
|||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `update_archetype_component_access` must have been called
|
||||
/// with `world` for each condition in `conditions`.
|
||||
unsafe fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &World) -> bool {
|
||||
/// - `world` must have permission to read any world data
|
||||
/// required by `conditions`.
|
||||
/// - `update_archetype_component_access` must have been called
|
||||
/// with `world` for each condition in `conditions`.
|
||||
unsafe fn evaluate_and_fold_conditions(
|
||||
conditions: &mut [BoxedCondition],
|
||||
world: UnsafeWorldCell,
|
||||
) -> bool {
|
||||
// not short-circuiting is intentional
|
||||
#[allow(clippy::unnecessary_fold)]
|
||||
conditions
|
||||
|
@ -699,7 +719,8 @@ unsafe fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world:
|
|||
.map(|condition| {
|
||||
#[cfg(feature = "trace")]
|
||||
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) }
|
||||
})
|
||||
.fold(true, |acc, res| acc && res)
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
component::{ComponentId, Tick},
|
||||
prelude::World,
|
||||
query::Access,
|
||||
world::unsafe_world_cell::UnsafeWorldCell,
|
||||
};
|
||||
|
||||
use super::{ReadOnlySystem, System};
|
||||
|
@ -157,7 +158,7 @@ where
|
|||
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(
|
||||
input,
|
||||
// SAFETY: The world accesses for both underlying systems have been registered,
|
||||
|
@ -198,7 +199,7 @@ where
|
|||
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.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
|
||||
/// by default).
|
||||
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 {
|
||||
entity,
|
||||
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)]
|
||||
pub struct Spawn<T> {
|
||||
/// The [`Bundle`] of components that will be added to the newly-spawned entity.
|
||||
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>
|
||||
where
|
||||
I: IntoIterator,
|
||||
|
@ -907,6 +901,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that despawns a specific entity.
|
||||
#[derive(Debug)]
|
||||
pub struct Despawn {
|
||||
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> {
|
||||
/// The entity to which the components will be added.
|
||||
pub entity: Entity,
|
||||
/// The [`Bundle`] containing the components that will be added to the entity.
|
||||
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)]
|
||||
pub struct Remove<T> {
|
||||
pub entity: Entity,
|
||||
pub phantom: PhantomData<T>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Command for Remove<T>
|
||||
|
@ -954,17 +955,19 @@ where
|
|||
}
|
||||
|
||||
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 {
|
||||
Self {
|
||||
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> {
|
||||
_phantom: PhantomData<R>,
|
||||
_marker: PhantomData<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`]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
_phantom: PhantomData::<R>,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that inserts a [`Resource`] into the world.
|
||||
pub struct InsertResource<R: Resource> {
|
||||
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 phantom: PhantomData<R>,
|
||||
_marker: PhantomData<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`]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData::<R>,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, In, IntoSystem,
|
||||
System, SystemMeta,
|
||||
},
|
||||
world::World,
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, World},
|
||||
};
|
||||
|
||||
use bevy_utils::all_tuples;
|
||||
|
@ -86,7 +86,7 @@ where
|
|||
}
|
||||
|
||||
#[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");
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ where
|
|||
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]
|
||||
fn check_change_tick(&mut self, change_tick: Tick) {
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
prelude::FromWorld,
|
||||
query::{Access, FilteredAccessSet},
|
||||
system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem},
|
||||
world::{World, WorldId},
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
|
||||
};
|
||||
|
||||
use bevy_utils::all_tuples;
|
||||
|
@ -417,7 +417,7 @@ where
|
|||
}
|
||||
|
||||
#[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();
|
||||
|
||||
// SAFETY:
|
||||
|
@ -428,7 +428,7 @@ where
|
|||
let params = F::Param::get_param(
|
||||
self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
|
||||
&self.system_meta,
|
||||
world.as_unsafe_world_cell_migration_internal(),
|
||||
world,
|
||||
change_tick,
|
||||
);
|
||||
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));
|
||||
}
|
||||
|
||||
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.");
|
||||
let archetypes = world.archetypes();
|
||||
let new_generation = archetypes.generation();
|
||||
|
|
|
@ -1610,7 +1610,7 @@ mod tests {
|
|||
|
||||
// set up system and verify its access is empty
|
||||
system.initialize(&mut world);
|
||||
system.update_archetype_component_access(&world);
|
||||
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||
assert_eq!(
|
||||
system
|
||||
.archetype_component_access()
|
||||
|
@ -1640,7 +1640,7 @@ mod tests {
|
|||
world.spawn((B, C));
|
||||
|
||||
// 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!(
|
||||
system
|
||||
.archetype_component_access()
|
||||
|
@ -1658,7 +1658,7 @@ mod tests {
|
|||
.unwrap(),
|
||||
);
|
||||
world.spawn((A, B, D));
|
||||
system.update_archetype_component_access(&world);
|
||||
system.update_archetype_component_access(world.as_unsafe_world_cell());
|
||||
assert_eq!(
|
||||
system
|
||||
.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.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
|
@ -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.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
|
@ -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)]
|
||||
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,
|
||||
/// 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,
|
||||
/// The given [`Entity`] does not have the requested component.
|
||||
MissingComponent,
|
||||
/// The requested [`Entity`] does not exist.
|
||||
NoSuchEntity,
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use bevy_utils::tracing::warn;
|
|||
use core::fmt::Debug;
|
||||
|
||||
use crate::component::Tick;
|
||||
use crate::world::unsafe_world_cell::UnsafeWorldCell;
|
||||
use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Access, world::World};
|
||||
|
||||
use std::any::TypeId;
|
||||
|
@ -39,26 +40,24 @@ pub trait System: Send + Sync + 'static {
|
|||
fn is_exclusive(&self) -> bool;
|
||||
|
||||
/// 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
|
||||
/// it unsafe to call.
|
||||
/// can be called in parallel with other systems and may break Rust's aliasing rules
|
||||
/// if used incorrectly, making it unsafe to call.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This might access world and resources in an unsafe manner. This should only be called in one
|
||||
/// of the following contexts:
|
||||
/// 1. This system is the only system running on the given world across all threads.
|
||||
/// 2. This system only runs in parallel with other systems that do not conflict with the
|
||||
/// [`System::archetype_component_access()`].
|
||||
///
|
||||
/// Additionally, the method [`Self::update_archetype_component_access`] must be called at some
|
||||
/// 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;
|
||||
/// - The caller must ensure that `world` has permission to access any world data
|
||||
/// registered in [`Self::archetype_component_access`]. There must be no conflicting
|
||||
/// simultaneous accesses while the system is running.
|
||||
/// - The method [`Self::update_archetype_component_access`] must be called at some
|
||||
/// 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: UnsafeWorldCell) -> Self::Out;
|
||||
/// Runs the system with the given input in the world.
|
||||
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);
|
||||
// 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.
|
||||
unsafe { self.run_unsafe(input, world) }
|
||||
}
|
||||
|
@ -66,7 +65,11 @@ pub trait System: Send + Sync + 'static {
|
|||
/// Initialize the system.
|
||||
fn initialize(&mut self, _world: &mut World);
|
||||
/// 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);
|
||||
/// Returns the system's default [system sets](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 {}
|
||||
|
||||
// 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 {
|
||||
type State = ();
|
||||
|
||||
|
@ -61,7 +61,7 @@ unsafe impl SystemParam for WorldId {
|
|||
world: UnsafeWorldCell<'world>,
|
||||
_: Tick,
|
||||
) -> Self::Item<'world, 'state> {
|
||||
world.world_metadata().id()
|
||||
world.id()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![warn(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use super::{Mut, World};
|
||||
use super::{Mut, World, WorldId};
|
||||
use crate::{
|
||||
archetype::{Archetype, ArchetypeComponentId, Archetypes},
|
||||
bundle::Bundles,
|
||||
|
@ -190,6 +190,14 @@ impl<'w> UnsafeWorldCell<'w> {
|
|||
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
|
||||
#[inline]
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
/// the curve with the supplied `sample_function` at each `t`.
|
||||
/// length of the curve from start (t = 0) to end (t = n), where `n = self.segment_count()`,
|
||||
/// 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
|
||||
/// return an iterator over those three points, one at the start, middle, and end.
|
||||
/// For `subdivisions = 2`, this will split the curve into two lines, or three points, and
|
||||
/// return an iterator with 3 items, the three points, one at the start, middle, and end.
|
||||
#[inline]
|
||||
pub fn iter_samples(
|
||||
&self,
|
||||
pub fn iter_samples<'a, 'b: 'a>(
|
||||
&'b self,
|
||||
subdivisions: usize,
|
||||
sample_function: fn(&Self, f32) -> P,
|
||||
) -> impl Iterator<Item = P> + '_ {
|
||||
(0..=subdivisions).map(move |i| {
|
||||
let segments = self.segments.len() as f32;
|
||||
let t = i as f32 / subdivisions as f32 * segments;
|
||||
sample_function(self, t)
|
||||
})
|
||||
mut sample_function: impl FnMut(&Self, f32) -> P + 'a,
|
||||
) -> impl Iterator<Item = P> + 'a {
|
||||
self.iter_uniformly(subdivisions)
|
||||
.map(move |t| 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.
|
||||
|
|
|
@ -491,6 +491,9 @@ pub fn queue_material_meshes<M: Material>(
|
|||
AlphaMode::Multiply => {
|
||||
mesh_key |= MeshPipelineKey::BLEND_MULTIPLY;
|
||||
}
|
||||
AlphaMode::Mask(_) => {
|
||||
mesh_key |= MeshPipelineKey::MAY_DISCARD;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
|
@ -364,8 +364,8 @@ where
|
|||
shader_defs.push("DEPTH_PREPASS".into());
|
||||
}
|
||||
|
||||
if key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK) {
|
||||
shader_defs.push("ALPHA_MASK".into());
|
||||
if key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
|
||||
shader_defs.push("MAY_DISCARD".into());
|
||||
}
|
||||
|
||||
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
|
||||
// prepass shader
|
||||
let fragment_required = !targets.is_empty()
|
||||
|| ((key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK)
|
||||
|| blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA
|
||||
|| blend_key == MeshPipelineKey::BLEND_ALPHA)
|
||||
|| (key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
|
||||
&& self.material_fragment_shader.is_some());
|
||||
|
||||
let fragment = fragment_required.then(|| {
|
||||
|
@ -967,7 +965,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
|
|||
let alpha_mode = material.properties.alpha_mode;
|
||||
match alpha_mode {
|
||||
AlphaMode::Opaque => {}
|
||||
AlphaMode::Mask(_) => mesh_key |= MeshPipelineKey::ALPHA_MASK,
|
||||
AlphaMode::Mask(_) => mesh_key |= MeshPipelineKey::MAY_DISCARD,
|
||||
AlphaMode::Blend
|
||||
| AlphaMode::Premultiplied
|
||||
| AlphaMode::Add
|
||||
|
|
|
@ -1608,11 +1608,11 @@ pub fn queue_shadows<M: Material>(
|
|||
}
|
||||
let alpha_mode = material.properties.alpha_mode;
|
||||
match alpha_mode {
|
||||
AlphaMode::Mask(_) => {
|
||||
mesh_key |= MeshPipelineKey::ALPHA_MASK;
|
||||
}
|
||||
AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add => {
|
||||
mesh_key |= MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA;
|
||||
AlphaMode::Mask(_)
|
||||
| AlphaMode::Blend
|
||||
| AlphaMode::Premultiplied
|
||||
| AlphaMode::Add => {
|
||||
mesh_key |= MeshPipelineKey::MAY_DISCARD;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -616,7 +616,8 @@ bitflags::bitflags! {
|
|||
const DEPTH_PREPASS = (1 << 3);
|
||||
const NORMAL_PREPASS = (1 << 4);
|
||||
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 DEPTH_CLAMP_ORTHO = (1 << 8);
|
||||
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) {
|
||||
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 {
|
||||
// NOTE: If rendering as opaque, alpha should be ignored so set to 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 {
|
||||
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
|
||||
color.a = 1.0;
|
||||
} else {
|
||||
// NOTE: output_color.a < in.material.alpha_cutoff should not is not rendered
|
||||
// NOTE: This and any other discards mean that early-z testing cannot be done!
|
||||
// NOTE: output_color.a < in.material.alpha_cutoff should not be rendered
|
||||
discard;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
fn prepass_alpha_discard(in: FragmentInput) {
|
||||
|
||||
// This is a workaround since the preprocessor does not support
|
||||
// #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
|
||||
#ifdef MAY_DISCARD
|
||||
var output_color: vec4<f32> = material.base_color;
|
||||
|
||||
#ifdef VERTEX_UVS
|
||||
|
@ -51,22 +39,22 @@ fn prepass_alpha_discard(in: FragmentInput) {
|
|||
}
|
||||
#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;
|
||||
if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD)
|
||||
&& output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
|
||||
discard;
|
||||
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED
|
||||
&& all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
|
||||
discard;
|
||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
||||
if output_color.a < material.alpha_cutoff {
|
||||
discard;
|
||||
}
|
||||
} else if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) {
|
||||
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
|
||||
|
|
|
@ -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::{
|
||||
derive_data::{EnumVariantFields, ReflectEnum},
|
||||
utility::ident_or_index,
|
||||
};
|
||||
use proc_macro2::Ident;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::Member;
|
||||
|
||||
/// Contains all data needed to construct all variants within an enum.
|
||||
pub(crate) struct EnumVariantConstructors {
|
||||
|
@ -30,7 +33,7 @@ pub(crate) fn get_variant_constructors(
|
|||
let name = ident.to_string();
|
||||
let variant_constructor = reflect_enum.get_unit(ident);
|
||||
|
||||
let fields = match &variant.fields {
|
||||
let fields: &[StructField] = match &variant.fields {
|
||||
EnumVariantFields::Unit => &[],
|
||||
EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => {
|
||||
fields.as_slice()
|
||||
|
@ -39,35 +42,59 @@ pub(crate) fn get_variant_constructors(
|
|||
let mut reflect_index: usize = 0;
|
||||
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_ty = &field.data.ty;
|
||||
|
||||
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 {
|
||||
let error_repr = field.data.ident.as_ref().map_or_else(
|
||||
|| format!("at index {reflect_index}"),
|
||||
|name| format!("`{name}`"),
|
||||
);
|
||||
let unwrapper = if can_panic {
|
||||
let type_err_message = format!(
|
||||
"the field {error_repr} should be of type `{}`",
|
||||
field.data.ty.to_token_stream()
|
||||
);
|
||||
quote!(.expect(#type_err_message))
|
||||
let (resolve_error, resolve_missing) = if can_panic {
|
||||
let field_ref_str = match &field_ident {
|
||||
Member::Named(ident) => format!("the field `{ident}`"),
|
||||
Member::Unnamed(index) => format!("the field at index {}", index.index)
|
||||
};
|
||||
let ty = 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(#on_error)), quote!(.expect(#on_missing)))
|
||||
} else {
|
||||
quote!(?)
|
||||
(quote!(?), quote!(?))
|
||||
};
|
||||
|
||||
let field_accessor = match &field.data.ident {
|
||||
Some(ident) => {
|
||||
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;
|
||||
let missing_field_err_message = format!("the field {error_repr} was not declared");
|
||||
let accessor = quote!(#field_accessor .expect(#missing_field_err_message));
|
||||
quote! {
|
||||
#bevy_reflect_path::FromReflect::from_reflect(#ref_value #accessor)
|
||||
#unwrapper
|
||||
|
||||
match &field.attrs.default {
|
||||
DefaultBehavior::Func(path) => quote! {
|
||||
if let #FQOption::Some(field) = #field_accessor {
|
||||
<#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 }
|
||||
|
|
|
@ -87,7 +87,8 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
|
|||
/// to improve performance and/or robustness.
|
||||
/// An example of where this is used is in the [`FromReflect`] derive macro,
|
||||
/// 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]`
|
||||
///
|
||||
|
|
|
@ -751,6 +751,39 @@ mod tests {
|
|||
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]
|
||||
fn from_reflect_should_use_default_container_attribute() {
|
||||
#[derive(Reflect, FromReflect, Eq, PartialEq, Debug)]
|
||||
|
|
|
@ -199,7 +199,7 @@ impl Image {
|
|||
.map(DynamicImage::ImageRgba8),
|
||||
// This format is commonly used as the format for the swapchain texture
|
||||
// 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.height,
|
||||
{
|
||||
|
|
|
@ -377,7 +377,7 @@ pub fn prepare_windows(
|
|||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: surface_configuration.format,
|
||||
format: surface_configuration.format.add_srgb_suffix(),
|
||||
usage: TextureUsages::RENDER_ATTACHMENT
|
||||
| TextureUsages::COPY_SRC
|
||||
| TextureUsages::TEXTURE_BINDING,
|
||||
|
|
|
@ -234,16 +234,11 @@ impl SpecializedRenderPipeline for ScreenshotToScreenPipeline {
|
|||
shader: SCREENSHOT_SHADER_HANDLE.typed(),
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: Some(wgpu::Face::Back),
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
conservative: false,
|
||||
unclipped_depth: false,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multisample: Default::default(),
|
||||
fragment: Some(FragmentState {
|
||||
shader: SCREENSHOT_SHADER_HANDLE.typed(),
|
||||
entry_point: Cow::Borrowed("fs_main"),
|
||||
|
|
|
@ -10,7 +10,7 @@ keywords = ["bevy"]
|
|||
|
||||
[features]
|
||||
trace = []
|
||||
wayland = ["winit/wayland"]
|
||||
wayland = ["winit/wayland", "winit/wayland-csd-adwaita"]
|
||||
x11 = ["winit/x11"]
|
||||
accesskit_unix = ["accesskit_winit/accesskit_unix"]
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ fn star(
|
|||
// We can now spawn the entities for the star and the camera
|
||||
commands.spawn((
|
||||
// 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
|
||||
Mesh2dHandle(meshes.add(star)),
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
if controls.color && randomize_colors {
|
||||
|
|
|
@ -378,7 +378,7 @@ fn update_normal(
|
|||
return;
|
||||
}
|
||||
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;
|
||||
*already_ran = true;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ fn asset_loaded(
|
|||
&& asset_server.get_load_state(cubemap.image_handle.clone_weak()) == LoadState::Loaded
|
||||
{
|
||||
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,
|
||||
// so they appear as one texture. The following code reconfigures the texture as necessary.
|
||||
if image.texture_descriptor.array_layer_count() == 1 {
|
||||
|
|
|
@ -10,7 +10,7 @@ use rand::{thread_rng, Rng};
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (light_sway, movement))
|
||||
|
|
|
@ -430,7 +430,7 @@ fn update_color_grading_settings(
|
|||
mut selected_parameter: ResMut<SelectedParameter>,
|
||||
) {
|
||||
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;
|
||||
if keys.pressed(KeyCode::Left) {
|
||||
dt = -dt;
|
||||
|
|
|
@ -9,7 +9,7 @@ fn main() {
|
|||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
// Adds frame time diagnostics
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
// Adds a system that prints diagnostics to the console
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
// Any plugin can register diagnostics
|
||||
|
|
|
@ -18,15 +18,20 @@ fn main() {
|
|||
// The common_conditions module has a few useful run conditions
|
||||
// for checking resources and states. These are included in the prelude.
|
||||
.run_if(resource_exists::<InputCounter>())
|
||||
// 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`.
|
||||
.run_if(has_user_input),
|
||||
// `.or_else()` is a run condition combinator that only evaluates the second condition
|
||||
// if the first condition returns `false`. This behavior is known as "short-circuiting",
|
||||
// and is how the `||` operator works in Rust (as well as most C-family languages).
|
||||
// In this case, the `has_user_input` run condition will be evaluated since the `Unused` resource has not been initialized.
|
||||
.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
|
||||
// `.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",
|
||||
// and is how the `&&` operator works in Rust (as well as most C-family languages).
|
||||
// if the first condition returns `true`, analogous to the `&&` operator.
|
||||
// In this case, the short-circuiting behavior prevents the second run condition from
|
||||
// panicking if the `InputCounter` resource has not been initialized.
|
||||
.run_if(resource_exists::<InputCounter>().and_then(
|
||||
|
@ -51,6 +56,9 @@ fn main() {
|
|||
#[derive(Resource, Default)]
|
||||
struct InputCounter(usize);
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Unused;
|
||||
|
||||
/// 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
|
||||
/// 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)
|
||||
.lines()
|
||||
.filter_map(|x| x.ok())
|
||||
.map_while(|x| x.ok())
|
||||
.collect();
|
||||
|
||||
Ok(contributors)
|
||||
|
|
|
@ -28,6 +28,8 @@ fn main() {
|
|||
})
|
||||
.add_systems(Startup, setup)
|
||||
.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();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ fn main() {
|
|||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.insert_resource(BevyCounter {
|
||||
count: 0,
|
||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
|||
App::new()
|
||||
// Since this is also used as a benchmark, we want it to display performance data.
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
present_mode: PresentMode::AutoNoVsync,
|
||||
|
|
|
@ -30,7 +30,7 @@ fn main() {
|
|||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, button_system);
|
||||
|
|
|
@ -28,7 +28,7 @@ fn main() {
|
|||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (move_camera, print_mesh_count))
|
||||
|
|
|
@ -18,7 +18,7 @@ fn main() {
|
|||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.insert_resource(Config {
|
||||
line_count: 50_000,
|
||||
fancy: false,
|
||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
|||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_systems(Startup, setup);
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ fn main() {
|
|||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_systems(Startup, setup)
|
||||
.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.
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
present_mode: PresentMode::AutoNoVsync,
|
||||
|
|
|
@ -18,7 +18,7 @@ fn main() {
|
|||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_systems(Startup, spawn)
|
||||
.add_systems(Update, update_text_bounds)
|
||||
|
|
|
@ -184,7 +184,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(cfg)
|
||||
.add_plugins(MinimalPlugins)
|
||||
.add_plugin(TransformPlugin::default())
|
||||
.add_plugin(TransformPlugin)
|
||||
.add_systems(Startup, setup)
|
||||
// Updating transforms *must* be done before `CoreSet::PostUpdate`
|
||||
// or the hierarchy will momentarily be in an invalid state.
|
||||
|
|
|
@ -11,7 +11,7 @@ use bevy::{
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (text_update_system, text_color_system))
|
||||
.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};
|
||||
|
||||
fn main() {
|
||||
|
|
Loading…
Reference in a new issue