Merge branch 'main' into transmission

This commit is contained in:
Marco Buono 2023-05-31 22:26:38 -03:00
commit 96c36ef780
54 changed files with 455 additions and 222 deletions

View file

@ -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"

View file

@ -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);

View file

@ -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));
});

View file

@ -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))
}

View file

@ -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));
});

View file

@ -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 [BezoldBrü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

View file

@ -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),
}

View file

@ -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);
}

View file

@ -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)

View file

@ -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);

View file

@ -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,
}
}
}

View file

@ -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) {

View file

@ -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();

View file

@ -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()

View file

@ -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,
}

View file

@ -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>> {

View file

@ -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()
}
}

View file

@ -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 {

View file

@ -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.

View file

@ -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;
}
_ => (),
}

View file

@ -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

View file

@ -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;
}
_ => {}
}

View file

@ -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());
}

View file

@ -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;
}

View file

@ -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

View file

@ -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 }

View file

@ -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]`
///

View file

@ -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)]

View file

@ -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,
{

View file

@ -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,

View file

@ -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"),

View file

@ -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"]

View file

@ -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

View file

@ -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 {

View file

@ -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;
}

View file

@ -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 {

View file

@ -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))

View file

@ -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;

View file

@ -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

View file

@ -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).

View file

@ -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)

View file

@ -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();
}

View file

@ -37,7 +37,7 @@ fn main() {
}),
..default()
}))
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin)
.add_plugin(LogDiagnosticsPlugin::default())
.insert_resource(BevyCounter {
count: 0,

View file

@ -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,

View file

@ -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);

View file

@ -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))

View file

@ -18,7 +18,7 @@ fn main() {
}),
..default()
}))
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin)
.insert_resource(Config {
line_count: 50_000,
fancy: false,

View file

@ -21,7 +21,7 @@ fn main() {
}),
..default()
}))
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin)
.add_plugin(LogDiagnosticsPlugin::default())
.add_systems(Startup, setup);

View file

@ -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))

View file

@ -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,

View file

@ -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)

View file

@ -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.

View file

@ -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();

View file

@ -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() {