format comments (#1612)

Uses the new unstable comment formatting features added to rustfmt.toml.
This commit is contained in:
Carter Anderson 2021-03-11 00:27:30 +00:00
parent be1c317d4e
commit b17f8a4bce
125 changed files with 894 additions and 599 deletions

View File

@ -9,24 +9,25 @@ use bevy_utils::tracing::info_span;
#[allow(clippy::needless_doctest_main)] #[allow(clippy::needless_doctest_main)]
/// Containers of app logic and data /// Containers of app logic and data
/// ///
/// App store the ECS World, Resources, Schedule, and Executor. They also store the "run" function of the App, which /// App store the ECS World, Resources, Schedule, and Executor. They also store the "run" function
/// by default executes the App schedule once. Apps are constructed using the builder pattern. /// of the App, which by default executes the App schedule once. Apps are constructed using the
/// builder pattern.
/// ///
/// ## Example /// ## Example
/// Here is a simple "Hello World" Bevy app: /// Here is a simple "Hello World" Bevy app:
/// ``` /// ```
///# use bevy_app::prelude::*; /// # use bevy_app::prelude::*;
///# use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// ///
///fn main() { /// fn main() {
/// App::build() /// App::build()
/// .add_system(hello_world_system.system()) /// .add_system(hello_world_system.system())
/// .run(); /// .run();
///} /// }
/// ///
///fn hello_world_system() { /// fn hello_world_system() {
/// println!("hello world"); /// println!("hello world");
///} /// }
/// ``` /// ```
pub struct App { pub struct App {
pub world: World, pub world: World,

View File

@ -234,7 +234,8 @@ impl AppBuilder {
.add_system_to_stage(CoreStage::First, Events::<T>::update_system.system()) .add_system_to_stage(CoreStage::First, Events::<T>::update_system.system())
} }
/// Inserts a resource to the current [App] and overwrites any resource previously added of the same type. /// Inserts a resource to the current [App] and overwrites any resource previously added of the
/// same type.
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
where where
T: Component, T: Component,
@ -255,9 +256,9 @@ impl AppBuilder {
where where
R: FromWorld + Send + Sync + 'static, R: FromWorld + Send + Sync + 'static,
{ {
// PERF: We could avoid double hashing here, since the `from_resources` call is guaranteed not to // PERF: We could avoid double hashing here, since the `from_resources` call is guaranteed
// modify the map. However, we would need to be borrowing resources both mutably and immutably, // not to modify the map. However, we would need to be borrowing resources both
// so we would need to be extremely certain this is correct // mutably and immutably, so we would need to be extremely certain this is correct
if !self.world_mut().contains_resource::<R>() { if !self.world_mut().contains_resource::<R>() {
let resource = R::from_world(self.world_mut()); let resource = R::from_world(self.world_mut());
self.insert_resource(resource); self.insert_resource(resource);

View File

@ -55,10 +55,12 @@ enum State {
B, B,
} }
/// An event collection that represents the events that occurred within the last two [Events::update] calls. Events can be cheaply read using /// An event collection that represents the events that occurred within the last two
/// an [EventReader]. This collection is meant to be paired with a system that calls [Events::update] exactly once per update/frame. [Events::update_system] /// [Events::update] calls. Events can be cheaply read using an [EventReader]. This collection is
/// is a system that does this. [EventReader]s are expected to read events from this collection at least once per update/frame. If events are not handled /// meant to be paired with a system that calls [Events::update] exactly once per update/frame.
/// within one frame/update, they will be dropped. /// [Events::update_system] is a system that does this. [EventReader]s are expected to read events
/// from this collection at least once per update/frame. If events are not handled within one
/// frame/update, they will be dropped.
/// ///
/// # Example /// # Example
/// ``` /// ```
@ -89,14 +91,16 @@ enum State {
/// ///
/// # Details /// # Details
/// ///
/// [Events] is implemented using a double buffer. Each call to [Events::update] swaps buffers and clears out the oldest buffer. /// [Events] is implemented using a double buffer. Each call to [Events::update] swaps buffers and
/// [EventReader]s that read at least once per update will never drop events. [EventReader]s that read once within two updates might /// clears out the oldest buffer. [EventReader]s that read at least once per update will never drop
/// still receive some events. [EventReader]s that read after two updates are guaranteed to drop all events that occurred before those updates. /// events. [EventReader]s that read once within two updates might still receive some events.
/// [EventReader]s that read after two updates are guaranteed to drop all events that occurred
/// before those updates.
/// ///
/// The buffers in [Events] will grow indefinitely if [Events::update] is never called. /// The buffers in [Events] will grow indefinitely if [Events::update] is never called.
/// ///
/// An alternative call pattern would be to call [Events::update] manually across frames to control when events are cleared. However /// An alternative call pattern would be to call [Events::update] manually across frames to control
/// this complicates consumption /// when events are cleared. However this complicates consumption
#[derive(Debug)] #[derive(Debug)]
pub struct Events<T> { pub struct Events<T> {
events_a: Vec<EventInstance<T>>, events_a: Vec<EventInstance<T>>,
@ -180,7 +184,8 @@ impl<T> ManualEventReader<T> {
} }
} }
/// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read messages. /// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read
/// messages.
fn internal_event_reader<'a, T>( fn internal_event_reader<'a, T>(
last_event_count: &mut usize, last_event_count: &mut usize,
events: &'a Events<T>, events: &'a Events<T>,
@ -232,7 +237,8 @@ fn internal_event_reader<'a, T>(
impl<'a, T: Component> EventReader<'a, T> { impl<'a, T: Component> EventReader<'a, T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now. /// event counter, which means subsequent event reads will not include events that happened
/// before now.
pub fn iter(&mut self) -> impl DoubleEndedIterator<Item = &T> { pub fn iter(&mut self) -> impl DoubleEndedIterator<Item = &T> {
self.iter_with_id().map(|(event, _id)| event) self.iter_with_id().map(|(event, _id)| event)
} }
@ -247,7 +253,8 @@ impl<'a, T: Component> EventReader<'a, T> {
} }
impl<T: Component> Events<T> { impl<T: Component> Events<T> {
/// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read the event. /// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read
/// the event.
pub fn send(&mut self, event: T) { pub fn send(&mut self, event: T) {
let event_id = EventId { let event_id = EventId {
id: self.event_count, id: self.event_count,
@ -273,7 +280,8 @@ impl<T: Component> Events<T> {
} }
} }
/// Gets a new [ManualEventReader]. This will ignore all events already in the event buffers. It will read all future events. /// Gets a new [ManualEventReader]. This will ignore all events already in the event buffers. It
/// will read all future events.
pub fn get_reader_current(&self) -> ManualEventReader<T> { pub fn get_reader_current(&self) -> ManualEventReader<T> {
ManualEventReader { ManualEventReader {
last_event_count: self.event_count, last_event_count: self.event_count,
@ -281,7 +289,8 @@ impl<T: Component> Events<T> {
} }
} }
/// Swaps the event buffers and clears the oldest event buffer. In general, this should be called once per frame/update. /// Swaps the event buffers and clears the oldest event buffer. In general, this should be
/// called once per frame/update.
pub fn update(&mut self) { pub fn update(&mut self) {
match self.state { match self.state {
State::A => { State::A => {
@ -335,10 +344,11 @@ impl<T: Component> Events<T> {
} }
/// Iterates over events that happened since the last "update" call. /// Iterates over events that happened since the last "update" call.
/// WARNING: You probably don't want to use this call. In most cases you should use an `EventReader`. You should only use /// WARNING: You probably don't want to use this call. In most cases you should use an
/// this if you know you only need to consume events between the last `update()` call and your call to `iter_current_update_events`. /// `EventReader`. You should only use this if you know you only need to consume events
/// If events happen outside that window, they will not be handled. For example, any events that happen after this call and before /// between the last `update()` call and your call to `iter_current_update_events`.
/// the next `update()` call will be dropped. /// If events happen outside that window, they will not be handled. For example, any events that
/// happen after this call and before the next `update()` call will be dropped.
pub fn iter_current_update_events(&self) -> impl DoubleEndedIterator<Item = &T> { pub fn iter_current_update_events(&self) -> impl DoubleEndedIterator<Item = &T> {
match self.state { match self.state {
State::A => self.events_a.iter().map(map_instance_event), State::A => self.events_a.iter().map(map_instance_event),
@ -363,7 +373,8 @@ mod tests {
let event_1 = TestEvent { i: 1 }; let event_1 = TestEvent { i: 1 };
let event_2 = TestEvent { i: 2 }; let event_2 = TestEvent { i: 2 };
// this reader will miss event_0 and event_1 because it wont read them over the course of two updates // this reader will miss event_0 and event_1 because it wont read them over the course of
// two updates
let mut reader_missed = events.get_reader(); let mut reader_missed = events.get_reader();
let mut reader_a = events.get_reader(); let mut reader_a = events.get_reader();

View File

@ -33,7 +33,8 @@ pub enum CoreStage {
First, First,
/// Name of app stage responsible for performing setup before an update. Runs before UPDATE. /// Name of app stage responsible for performing setup before an update. Runs before UPDATE.
PreUpdate, PreUpdate,
/// Name of app stage responsible for doing most app logic. Systems should be registered here by default. /// Name of app stage responsible for doing most app logic. Systems should be registered here
/// by default.
Update, Update,
/// Name of app stage responsible for processing the results of UPDATE. Runs after UPDATE. /// Name of app stage responsible for processing the results of UPDATE. Runs after UPDATE.
PostUpdate, PostUpdate,

View File

@ -3,7 +3,8 @@ use std::any::Any;
/// A collection of Bevy App logic and configuration /// A collection of Bevy App logic and configuration
/// ///
/// Plugins use [AppBuilder] to configure an [App](crate::App). When an [App](crate::App) registers a plugin, the plugin's [Plugin::build] function is run. /// Plugins use [AppBuilder] to configure an [App](crate::App). When an [App](crate::App) registers
/// a plugin, the plugin's [Plugin::build] function is run.
pub trait Plugin: Any + Send + Sync { pub trait Plugin: Any + Send + Sync {
fn build(&self, app: &mut AppBuilder); fn build(&self, app: &mut AppBuilder);
fn name(&self) -> &str { fn name(&self) -> &str {

View File

@ -41,7 +41,8 @@ impl ScheduleRunnerSettings {
} }
} }
/// Configures an App to run its [Schedule](bevy_ecs::schedule::Schedule) according to a given [RunMode] /// Configures an App to run its [Schedule](bevy_ecs::schedule::Schedule) according to a given
/// [RunMode]
#[derive(Default)] #[derive(Default)]
pub struct ScheduleRunnerPlugin {} pub struct ScheduleRunnerPlugin {}

View File

@ -226,7 +226,8 @@ impl AssetServer {
let asset_loader = self.get_path_asset_loader(asset_path.path())?; let asset_loader = self.get_path_asset_loader(asset_path.path())?;
let asset_path_id: AssetPathId = asset_path.get_id(); let asset_path_id: AssetPathId = asset_path.get_id();
// load metadata and update source info. this is done in a scope to ensure we release the locks before loading // load metadata and update source info. this is done in a scope to ensure we release the
// locks before loading
let version = { let version = {
let mut asset_sources = self.server.asset_sources.write(); let mut asset_sources = self.server.asset_sources.write();
let source_info = match asset_sources.entry(asset_path_id.source_path_id()) { let source_info = match asset_sources.entry(asset_path_id.source_path_id()) {
@ -282,7 +283,8 @@ impl AssetServer {
.await .await
.map_err(AssetServerError::AssetLoaderError)?; .map_err(AssetServerError::AssetLoaderError)?;
// if version has changed since we loaded and grabbed a lock, return. theres is a newer version being loaded // if version has changed since we loaded and grabbed a lock, return. theres is a newer
// version being loaded
let mut asset_sources = self.server.asset_sources.write(); let mut asset_sources = self.server.asset_sources.write();
let source_info = asset_sources let source_info = asset_sources
.get_mut(&asset_path_id.source_path_id()) .get_mut(&asset_path_id.source_path_id())

View File

@ -2,7 +2,8 @@ use crossbeam_channel::Receiver;
use notify::{Event, RecommendedWatcher, RecursiveMode, Result, Watcher}; use notify::{Event, RecommendedWatcher, RecursiveMode, Result, Watcher};
use std::path::Path; use std::path::Path;
/// Watches for changes to assets on the filesystem. This is used by the `AssetServer` to reload them /// Watches for changes to assets on the filesystem. This is used by the `AssetServer` to reload
/// them
pub struct FilesystemWatcher { pub struct FilesystemWatcher {
pub watcher: RecommendedWatcher, pub watcher: RecommendedWatcher,
pub receiver: Receiver<Result<Event>>, pub receiver: Receiver<Result<Event>>,

View File

@ -56,7 +56,8 @@ impl HandleId {
/// A handle into a specific Asset of type `T` /// A handle into a specific Asset of type `T`
/// ///
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets) collection. /// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
/// collection.
#[derive(Reflect)] #[derive(Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Handle<T> pub struct Handle<T>
@ -147,7 +148,8 @@ impl<T: Asset> Drop for Handle<T> {
fn drop(&mut self) { fn drop(&mut self) {
match self.handle_type { match self.handle_type {
HandleType::Strong(ref sender) => { HandleType::Strong(ref sender) => {
// ignore send errors because this means the channel is shut down / the game has stopped // ignore send errors because this means the channel is shut down / the game has
// stopped
let _ = sender.send(RefChange::Decrement(self.id)); let _ = sender.send(RefChange::Decrement(self.id));
} }
HandleType::Weak => {} HandleType::Weak => {}
@ -233,7 +235,8 @@ unsafe impl<T: Asset> Sync for Handle<T> {}
/// A non-generic version of [Handle] /// A non-generic version of [Handle]
/// ///
/// This allows handles to be mingled in a cross asset context. For example, storing `Handle<A>` and `Handle<B>` in the same `HashSet<HandleUntyped>`. /// This allows handles to be mingled in a cross asset context. For example, storing `Handle<A>` and
/// `Handle<B>` in the same `HashSet<HandleUntyped>`.
#[derive(Debug)] #[derive(Debug)]
pub struct HandleUntyped { pub struct HandleUntyped {
pub id: HandleId, pub id: HandleId,
@ -299,7 +302,8 @@ impl Drop for HandleUntyped {
fn drop(&mut self) { fn drop(&mut self) {
match self.handle_type { match self.handle_type {
HandleType::Strong(ref sender) => { HandleType::Strong(ref sender) => {
// ignore send errors because this means the channel is shut down / the game has stopped // ignore send errors because this means the channel is shut down / the game has
// stopped
let _ = sender.send(RefChange::Decrement(self.id)); let _ = sender.send(RefChange::Decrement(self.id));
} }
HandleType::Weak => {} HandleType::Weak => {}

View File

@ -39,8 +39,8 @@ pub enum AssetStage {
AssetEvents, AssetEvents,
} }
/// Adds support for Assets to an App. Assets are typed collections with change tracking, which are added as App Resources. /// Adds support for Assets to an App. Assets are typed collections with change tracking, which are
/// Examples of assets: textures, sounds, 3d models, maps, scenes /// added as App Resources. Examples of assets: textures, sounds, 3d models, maps, scenes
#[derive(Default)] #[derive(Default)]
pub struct AssetPlugin; pub struct AssetPlugin;

View File

@ -101,8 +101,8 @@ unsafe impl Byteable for isize {}
unsafe impl Byteable for f32 {} unsafe impl Byteable for f32 {}
unsafe impl Byteable for f64 {} unsafe impl Byteable for f64 {}
unsafe impl Byteable for Vec2 {} unsafe impl Byteable for Vec2 {}
// NOTE: Vec3 actually takes up the size of 4 floats / 16 bytes due to SIMD. This is actually convenient because GLSL // NOTE: Vec3 actually takes up the size of 4 floats / 16 bytes due to SIMD. This is actually
// uniform buffer objects pad Vec3s to be 16 bytes. // convenient because GLSL uniform buffer objects pad Vec3s to be 16 bytes.
unsafe impl Byteable for Vec3 {} unsafe impl Byteable for Vec3 {}
unsafe impl Byteable for Vec4 {} unsafe impl Byteable for Vec4 {}

View File

@ -5,9 +5,9 @@ use std::{
ops::Neg, ops::Neg,
}; };
/// A wrapper type that enables ordering floats. This is a work around for the famous "rust float ordering" problem. /// A wrapper type that enables ordering floats. This is a work around for the famous "rust float
/// By using it, you acknowledge that sorting NaN is undefined according to spec. This implementation treats NaN as the /// ordering" problem. By using it, you acknowledge that sorting NaN is undefined according to spec.
/// "smallest" float. /// This implementation treats NaN as the "smallest" float.
#[derive(Debug, Copy, Clone, PartialOrd)] #[derive(Debug, Copy, Clone, PartialOrd)]
pub struct FloatOrd(pub f32); pub struct FloatOrd(pub f32);

View File

@ -61,7 +61,8 @@ impl Labels {
} }
} }
/// Maintains a mapping from [Entity](bevy_ecs::prelude::Entity) ids to entity labels and entity labels to [Entities](bevy_ecs::prelude::Entity). /// Maintains a mapping from [Entity](bevy_ecs::prelude::Entity) ids to entity labels and entity
/// labels to [Entities](bevy_ecs::prelude::Entity).
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct EntityLabels { pub struct EntityLabels {
label_entities: HashMap<Cow<'static, str>, Vec<Entity>>, label_entities: HashMap<Cow<'static, str>, Vec<Entity>>,

View File

@ -31,7 +31,8 @@ pub struct CorePlugin;
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)] #[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)]
pub enum CoreSystem { pub enum CoreSystem {
/// Updates the elapsed time. Any system that interacts with [Time] component should run after this. /// Updates the elapsed time. Any system that interacts with [Time] component should run after
/// this.
Time, Time,
} }
@ -54,7 +55,8 @@ impl Plugin for CorePlugin {
.register_type::<Labels>() .register_type::<Labels>()
.register_type::<Range<f32>>() .register_type::<Range<f32>>()
.register_type::<Timer>() .register_type::<Timer>()
// time system is added as an "exclusive system" to ensure it runs before other systems in CoreStage::First // time system is added as an "exclusive system" to ensure it runs before other systems
// in CoreStage::First
.add_system_to_stage( .add_system_to_stage(
CoreStage::First, CoreStage::First,
time_system.exclusive_system().label(CoreSystem::Time), time_system.exclusive_system().label(CoreSystem::Time),

View File

@ -36,9 +36,11 @@ impl TaskPoolThreadAssignmentPolicy {
/// this helper will do nothing. /// this helper will do nothing.
#[derive(Clone)] #[derive(Clone)]
pub struct DefaultTaskPoolOptions { pub struct DefaultTaskPoolOptions {
/// If the number of physical cores is less than min_total_threads, force using min_total_threads /// If the number of physical cores is less than min_total_threads, force using
/// min_total_threads
pub min_total_threads: usize, pub min_total_threads: usize,
/// If the number of physical cores is grater than max_total_threads, force using max_total_threads /// If the number of physical cores is grater than max_total_threads, force using
/// max_total_threads
pub max_total_threads: usize, pub max_total_threads: usize,
/// Used to determine number of IO threads to allocate /// Used to determine number of IO threads to allocate

View File

@ -169,8 +169,8 @@ impl System for FixedTimestep {
} }
unsafe fn run_unsafe(&mut self, _input: Self::In, world: &World) -> Self::Out { unsafe fn run_unsafe(&mut self, _input: Self::In, world: &World) -> Self::Out {
// SAFE: this system inherits the internal system's component access and archetype component access, // SAFE: this system inherits the internal system's component access and archetype component
// which means the caller has ensured running the internal system is safe // access, which means the caller has ensured running the internal system is safe
self.internal_system.run_unsafe((), world) self.internal_system.run_unsafe((), world)
} }

View File

@ -6,7 +6,8 @@ use bevy_utils::Duration;
/// Tracks elapsed time. Enters the finished state once `duration` is reached. /// Tracks elapsed time. Enters the finished state once `duration` is reached.
/// ///
/// Non repeating timers will stop tracking and stay in the finished state until reset. /// Non repeating timers will stop tracking and stay in the finished state until reset.
/// Repeating timers will only be in the finished state on each tick `duration` is reached or exceeded, and can still be reset at any given point. /// Repeating timers will only be in the finished state on each tick `duration` is reached or
/// exceeded, and can still be reset at any given point.
/// ///
/// Paused timers will not have elapsed time increased. /// Paused timers will not have elapsed time increased.
#[derive(Clone, Debug, Default, Reflect)] #[derive(Clone, Debug, Default, Reflect)]

View File

@ -11,8 +11,8 @@ mod shader_defs;
use proc_macro::TokenStream; use proc_macro::TokenStream;
/// Derives the FromResources trait. Each field must also implement the FromResources trait or this will fail. FromResources is /// Derives the FromResources trait. Each field must also implement the FromResources trait or this
/// automatically implemented for types that implement Default. /// will fail. FromResources is automatically implemented for types that implement Default.
#[proc_macro_derive(FromResources, attributes(as_crate))] #[proc_macro_derive(FromResources, attributes(as_crate))]
pub fn derive_from_resources(input: TokenStream) -> TokenStream { pub fn derive_from_resources(input: TokenStream) -> TokenStream {
resource::derive_from_resources(input) resource::derive_from_resources(input)

View File

@ -264,7 +264,8 @@ impl Archetype {
self.table_info.entity_rows.reserve(additional); self.table_info.entity_rows.reserve(additional);
} }
/// Removes the entity at `index` by swapping it out. Returns the table row the entity is stored in. /// Removes the entity at `index` by swapping it out. Returns the table row the entity is stored
/// in.
pub(crate) fn swap_remove(&mut self, index: usize) -> ArchetypeSwapRemoveResult { pub(crate) fn swap_remove(&mut self, index: usize) -> ArchetypeSwapRemoveResult {
let is_last = index == self.entities.len() - 1; let is_last = index == self.entities.len() - 1;
self.entities.swap_remove(index); self.entities.swap_remove(index);
@ -374,8 +375,8 @@ impl Default for Archetypes {
}; };
archetypes.get_id_or_insert(TableId::empty(), Vec::new(), Vec::new()); archetypes.get_id_or_insert(TableId::empty(), Vec::new(), Vec::new());
// adds the resource archetype. it is "special" in that it is inaccessible via a "hash", which prevents entities from // adds the resource archetype. it is "special" in that it is inaccessible via a "hash",
// being added to it // which prevents entities from being added to it
archetypes.archetypes.push(Archetype::new( archetypes.archetypes.push(Archetype::new(
ArchetypeId::resource(), ArchetypeId::resource(),
TableId::empty(), TableId::empty(),
@ -470,6 +471,7 @@ impl Archetypes {
/// Gets the archetype id matching the given inputs or inserts a new one if it doesn't exist. /// Gets the archetype id matching the given inputs or inserts a new one if it doesn't exist.
/// `table_components` and `sparse_set_components` must be sorted /// `table_components` and `sparse_set_components` must be sorted
///
/// # Safety /// # Safety
/// TableId must exist in tables /// TableId must exist in tables
pub(crate) fn get_id_or_insert( pub(crate) fn get_id_or_insert(

View File

@ -8,14 +8,15 @@ use crate::{
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
use std::{any::TypeId, collections::HashMap}; use std::{any::TypeId, collections::HashMap};
/// An ordered collection of components, commonly used for spawning entities, and adding and removing components in bulk. /// An ordered collection of components, commonly used for spawning entities, and adding and
/// removing components in bulk.
/// ///
/// You cannot query for a bundle, only individual components within it. /// You cannot query for a bundle, only individual components within it.
/// ///
/// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`. /// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`.
/// The `Bundle` trait is automatically implemented for tuples of components: /// The `Bundle` trait is automatically implemented for tuples of components:
/// `(ComponentA, ComponentB)` is a very convenient shorthand when working with one-off collections of components. /// `(ComponentA, ComponentB)` is a very convenient shorthand when working with one-off collections
/// Note that both `()` and `(ComponentA, )` are valid tuples. /// of components. Note that both `()` and `(ComponentA, )` are valid tuples.
/// ///
/// You can nest bundles like so: /// You can nest bundles like so:
/// ``` /// ```
@ -34,23 +35,29 @@ use std::{any::TypeId, collections::HashMap};
/// z: String, /// z: String,
/// } /// }
/// ``` /// ```
///
/// # Safety /// # Safety
/// [Bundle::type_info] must return the TypeInfo for each component type in the bundle, in the _exact_ /// [Bundle::type_info] must return the TypeInfo for each component type in the bundle, in the
/// order that [Bundle::get_components] is called. /// _exact_ order that [Bundle::get_components] is called.
/// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by [Bundle::type_info] /// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by
/// [Bundle::type_info]
pub unsafe trait Bundle: Send + Sync + 'static { pub unsafe trait Bundle: Send + Sync + 'static {
/// Gets this [Bundle]'s components type info, in the order of this bundle's Components /// Gets this [Bundle]'s components type info, in the order of this bundle's Components
fn type_info() -> Vec<TypeInfo>; fn type_info() -> Vec<TypeInfo>;
/// Calls `func`, which should return data for each component in the bundle, in the order of this bundle's Components /// Calls `func`, which should return data for each component in the bundle, in the order of
/// this bundle's Components
///
/// # Safety /// # Safety
/// Caller must return data for each component in the bundle, in the order of this bundle's Components /// Caller must return data for each component in the bundle, in the order of this bundle's
/// Components
unsafe fn from_components(func: impl FnMut() -> *mut u8) -> Self unsafe fn from_components(func: impl FnMut() -> *mut u8) -> Self
where where
Self: Sized; Self: Sized;
/// Calls `func` on each value, in the order of this bundle's Components. This will "mem::forget" the bundle /// Calls `func` on each value, in the order of this bundle's Components. This will
/// fields, so callers are responsible for dropping the fields if that is desirable. /// "mem::forget" the bundle fields, so callers are responsible for dropping the fields if
/// that is desirable.
fn get_components(self, func: impl FnMut(*mut u8)); fn get_components(self, func: impl FnMut(*mut u8));
} }
@ -126,7 +133,8 @@ impl BundleInfo {
bundle_flags: &[ComponentFlags], bundle_flags: &[ComponentFlags],
bundle: T, bundle: T,
) { ) {
// NOTE: get_components calls this closure on each component in "bundle order". bundle_info.component_ids are also in "bundle order" // NOTE: get_components calls this closure on each component in "bundle order".
// bundle_info.component_ids are also in "bundle order"
let mut bundle_component = 0; let mut bundle_component = 0;
bundle.get_components(|component_ptr| { bundle.get_components(|component_ptr| {
// SAFE: component_id was initialized by get_dynamic_bundle_info // SAFE: component_id was initialized by get_dynamic_bundle_info

View File

@ -31,7 +31,8 @@ pub struct ComponentInfo {
name: String, name: String,
id: ComponentId, id: ComponentId,
type_id: Option<TypeId>, type_id: Option<TypeId>,
// SAFETY: This must remain private. It must only be set to "true" if this component is actually Send + Sync // SAFETY: This must remain private. It must only be set to "true" if this component is
// actually Send + Sync
is_send_and_sync: bool, is_send_and_sync: bool,
layout: Layout, layout: Layout,
drop: unsafe fn(*mut u8), drop: unsafe fn(*mut u8),
@ -116,7 +117,8 @@ impl SparseSetIndex for ComponentId {
pub struct ComponentDescriptor { pub struct ComponentDescriptor {
name: String, name: String,
storage_type: StorageType, storage_type: StorageType,
// SAFETY: This must remain private. It must only be set to "true" if this component is actually Send + Sync // SAFETY: This must remain private. It must only be set to "true" if this component is
// actually Send + Sync
is_send_and_sync: bool, is_send_and_sync: bool,
type_id: Option<TypeId>, type_id: Option<TypeId>,
layout: Layout, layout: Layout,

View File

@ -56,9 +56,9 @@ impl Entity {
self.id self.id
} }
/// Returns the generation of this Entity's id. The generation is incremented each time an entity with /// Returns the generation of this Entity's id. The generation is incremented each time an
/// a given id is despawned. This serves as a "count" of the number of times a given id has been reused /// entity with a given id is despawned. This serves as a "count" of the number of times a
/// (id, generation) pairs uniquely identify a given Entity. /// given id has been reused (id, generation) pairs uniquely identify a given Entity.
#[inline] #[inline]
pub fn generation(self) -> u32 { pub fn generation(self) -> u32 {
self.generation self.generation
@ -121,16 +121,16 @@ pub struct Entities {
/// The `pending` and `free_cursor` fields describe three sets of Entity IDs /// The `pending` and `free_cursor` fields describe three sets of Entity IDs
/// that have been freed or are in the process of being allocated: /// that have been freed or are in the process of being allocated:
/// ///
/// - The `freelist` IDs, previously freed by `free()`. These IDs are available to any /// - The `freelist` IDs, previously freed by `free()`. These IDs are available to any of
/// of `alloc()`, `reserve_entity()` or `reserve_entities()`. Allocation will /// `alloc()`, `reserve_entity()` or `reserve_entities()`. Allocation will always prefer
/// always prefer these over brand new IDs. /// these over brand new IDs.
/// ///
/// - The `reserved` list of IDs that were once in the freelist, but got /// - The `reserved` list of IDs that were once in the freelist, but got reserved by
/// reserved by `reserve_entities` or `reserve_entity()`. They are now waiting /// `reserve_entities` or `reserve_entity()`. They are now waiting for `flush()` to make them
/// for `flush()` to make them fully allocated. /// fully allocated.
/// ///
/// - The count of new IDs that do not yet exist in `self.meta()`, but which /// - The count of new IDs that do not yet exist in `self.meta()`, but which we have handed out
/// we have handed out and reserved. `flush()` will allocate room for them in `self.meta()`. /// and reserved. `flush()` will allocate room for them in `self.meta()`.
/// ///
/// The contents of `pending` look like this: /// The contents of `pending` look like this:
/// ///
@ -257,7 +257,8 @@ impl Entities {
/// Allocate a specific entity ID, overwriting its generation /// Allocate a specific entity ID, overwriting its generation
/// ///
/// Returns the location of the entity currently using the given ID, if any. Location should be written immediately. /// Returns the location of the entity currently using the given ID, if any. Location should be
/// written immediately.
pub fn alloc_at(&mut self, entity: Entity) -> Option<EntityLocation> { pub fn alloc_at(&mut self, entity: Entity) -> Option<EntityLocation> {
self.verify_flushed(); self.verify_flushed();

View File

@ -171,7 +171,8 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
} }
pub fn get_conflicts(&self, filtered_access: &FilteredAccess<T>) -> Vec<T> { pub fn get_conflicts(&self, filtered_access: &FilteredAccess<T>) -> Vec<T> {
// if combined unfiltered access is incompatible, check each filtered access for compatibility // if combined unfiltered access is incompatible, check each filtered access for
// compatibility
if !filtered_access.access.is_compatible(&self.combined_access) { if !filtered_access.access.is_compatible(&self.combined_access) {
for current_filtered_access in self.filtered_accesses.iter() { for current_filtered_access in self.filtered_accesses.iter() {
if !current_filtered_access.is_compatible(&filtered_access) { if !current_filtered_access.is_compatible(&filtered_access) {

View File

@ -22,44 +22,60 @@ pub trait Fetch<'w>: Sized {
type State: FetchState; type State: FetchState;
/// Creates a new instance of this fetch. /// Creates a new instance of this fetch.
///
/// # Safety /// # Safety
/// `state` must have been initialized (via [FetchState::init]) using the same `world` passed in to this function. /// `state` must have been initialized (via [FetchState::init]) using the same `world` passed in
/// to this function.
unsafe fn init(world: &World, state: &Self::State) -> Self; unsafe fn init(world: &World, state: &Self::State) -> Self;
/// Returns true if (and only if) every table of every archetype matched by this Fetch contains all of the matched components. /// Returns true if (and only if) every table of every archetype matched by this Fetch contains
/// This is used to select a more efficient "table iterator" for "dense" queries. /// all of the matched components. This is used to select a more efficient "table iterator"
/// If this returns true, [Fetch::set_table] and [Fetch::table_fetch] will be called for iterators /// for "dense" queries. If this returns true, [Fetch::set_table] and [Fetch::table_fetch]
/// If this returns false, [Fetch::set_archetype] and [Fetch::archetype_fetch] will be called for iterators /// will be called for iterators If this returns false, [Fetch::set_archetype] and
/// [Fetch::archetype_fetch] will be called for iterators
fn is_dense(&self) -> bool; fn is_dense(&self) -> bool;
/// Adjusts internal state to account for the next [Archetype]. This will always be called on archetypes that match this [Fetch] /// Adjusts internal state to account for the next [Archetype]. This will always be called on
/// archetypes that match this [Fetch]
///
/// # Safety /// # Safety
/// `archetype` and `tables` must be from the [World] [Fetch::init] was called on. `state` must be the [Self::State] this was initialized with. /// `archetype` and `tables` must be from the [World] [Fetch::init] was called on. `state` must
/// be the [Self::State] this was initialized with.
unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables); unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables);
/// Adjusts internal state to account for the next [Table]. This will always be called on tables that match this [Fetch] /// Adjusts internal state to account for the next [Table]. This will always be called on tables
/// that match this [Fetch]
///
/// # Safety /// # Safety
/// `table` must be from the [World] [Fetch::init] was called on. `state` must be the [Self::State] this was initialized with. /// `table` must be from the [World] [Fetch::init] was called on. `state` must be the
/// [Self::State] this was initialized with.
unsafe fn set_table(&mut self, state: &Self::State, table: &Table); unsafe fn set_table(&mut self, state: &Self::State, table: &Table);
/// Fetch [Self::Item] for the given `archetype_index` in the current [Archetype]. This must always be called after [Fetch::set_archetype] with an `archetype_index` /// Fetch [Self::Item] for the given `archetype_index` in the current [Archetype]. This must
/// in the range of the current [Archetype] /// always be called after [Fetch::set_archetype] with an `archetype_index` in the range of
/// the current [Archetype]
///
/// # Safety /// # Safety
/// Must always be called _after_ [Fetch::set_archetype]. `archetype_index` must be in the range of the current archetype /// Must always be called _after_ [Fetch::set_archetype]. `archetype_index` must be in the range
/// of the current archetype
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item; unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item;
/// Fetch [Self::Item] for the given `table_row` in the current [Table]. This must always be called after [Fetch::set_table] with a `table_row` /// Fetch [Self::Item] for the given `table_row` in the current [Table]. This must always be
/// in the range of the current [Table] /// called after [Fetch::set_table] with a `table_row` in the range of the current [Table]
///
/// # Safety /// # Safety
/// Must always be called _after_ [Fetch::set_table]. `table_row` must be in the range of the current table /// Must always be called _after_ [Fetch::set_table]. `table_row` must be in the range of the
/// current table
unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item; unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item;
} }
/// State used to construct a Fetch. This will be cached inside QueryState, so it is best to move as much data / /// State used to construct a Fetch. This will be cached inside QueryState, so it is best to move as
/// computation here as possible to reduce the cost of constructing Fetch. /// much data / computation here as possible to reduce the cost of constructing Fetch.
/// SAFETY: /// SAFETY:
/// Implementor must ensure that [FetchState::update_component_access] and [FetchState::update_archetype_component_access] exactly /// Implementor must ensure that [FetchState::update_component_access] and
/// reflects the results of [FetchState::matches_archetype], [FetchState::matches_table], [Fetch::archetype_fetch], and [Fetch::table_fetch] /// [FetchState::update_archetype_component_access] exactly reflects the results of
/// [FetchState::matches_archetype], [FetchState::matches_table], [Fetch::archetype_fetch], and
/// [Fetch::table_fetch]
pub unsafe trait FetchState: Send + Sync + Sized { pub unsafe trait FetchState: Send + Sync + Sized {
fn init(world: &mut World) -> Self; fn init(world: &mut World) -> Self;
fn update_component_access(&self, access: &mut FilteredAccess<ComponentId>); fn update_component_access(&self, access: &mut FilteredAccess<ComponentId>);
@ -167,7 +183,8 @@ pub struct ReadState<T> {
marker: PhantomData<T>, marker: PhantomData<T>,
} }
// SAFE: component access and archetype component access are properly updated to reflect that T is read // SAFE: component access and archetype component access are properly updated to reflect that T is
// read
unsafe impl<T: Component> FetchState for ReadState<T> { unsafe impl<T: Component> FetchState for ReadState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_info = world.components.get_or_insert_info::<T>();
@ -312,7 +329,8 @@ pub struct WriteState<T> {
marker: PhantomData<T>, marker: PhantomData<T>,
} }
// SAFE: component access and archetype component access are properly updated to reflect that T is written // SAFE: component access and archetype component access are properly updated to reflect that T is
// written
unsafe impl<T: Component> FetchState for WriteState<T> { unsafe impl<T: Component> FetchState for WriteState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_info = world.components.get_or_insert_info::<T>();
@ -457,7 +475,8 @@ pub struct OptionState<T: FetchState> {
state: T, state: T,
} }
// SAFE: component access and archetype component access are properly updated according to the internal Fetch // SAFE: component access and archetype component access are properly updated according to the
// internal Fetch
unsafe impl<T: FetchState> FetchState for OptionState<T> { unsafe impl<T: FetchState> FetchState for OptionState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
Self { Self {
@ -589,7 +608,8 @@ pub struct FlagsState<T> {
marker: PhantomData<T>, marker: PhantomData<T>,
} }
// SAFE: component access and archetype component access are properly updated to reflect that T is read // SAFE: component access and archetype component access are properly updated to reflect that T is
// read
unsafe impl<T: Component> FetchState for FlagsState<T> { unsafe impl<T: Component> FetchState for FlagsState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_info = world.components.get_or_insert_info::<T>();

View File

@ -10,8 +10,8 @@ use crate::{
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
use std::{marker::PhantomData, ptr}; use std::{marker::PhantomData, ptr};
// TODO: uncomment this and use as shorthand (remove where F::Fetch: FilterFetch everywhere) when this bug is fixed in Rust 1.51: // TODO: uncomment this and use as shorthand (remove where F::Fetch: FilterFetch everywhere) when
// https://github.com/rust-lang/rust/pull/81671 // this bug is fixed in Rust 1.51: https://github.com/rust-lang/rust/pull/81671
// pub trait QueryFilter: WorldQuery // pub trait QueryFilter: WorldQuery
// where // where
// Self::Fetch: FilterFetch, // Self::Fetch: FilterFetch,
@ -21,14 +21,17 @@ use std::{marker::PhantomData, ptr};
// impl<T: WorldQuery> QueryFilter for T where T::Fetch: FilterFetch { // impl<T: WorldQuery> QueryFilter for T where T::Fetch: FilterFetch {
// } // }
/// Fetch methods used by query filters. This trait exists to allow "short circuit" behaviors for relevant query filter fetches. /// Fetch methods used by query filters. This trait exists to allow "short circuit" behaviors for
/// relevant query filter fetches.
pub trait FilterFetch: for<'a> Fetch<'a> { pub trait FilterFetch: for<'a> Fetch<'a> {
/// # Safety /// # Safety
/// Must always be called _after_ [Fetch::set_archetype]. `archetype_index` must be in the range of the current archetype /// Must always be called _after_ [Fetch::set_archetype]. `archetype_index` must be in the range
/// of the current archetype
unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool; unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool;
/// # Safety /// # Safety
/// Must always be called _after_ [Fetch::set_table]. `table_row` must be in the range of the current table /// Must always be called _after_ [Fetch::set_table]. `table_row` must be in the range of the
/// current table
unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool; unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool;
} }
@ -47,7 +50,8 @@ where
} }
} }
/// Filter that retrieves components of type `T` that have either been mutated or added since the start of the frame. /// Filter that retrieves components of type `T` that have either been mutated or added since the
/// start of the frame.
pub struct With<T>(PhantomData<T>); pub struct With<T>(PhantomData<T>);
impl<T: Component> WorldQuery for With<T> { impl<T: Component> WorldQuery for With<T> {
@ -137,7 +141,8 @@ impl<'a, T: Component> Fetch<'a> for WithFetch<T> {
} }
} }
/// Filter that retrieves components of type `T` that have either been mutated or added since the start of the frame. /// Filter that retrieves components of type `T` that have either been mutated or added since the
/// start of the frame.
pub struct Without<T>(PhantomData<T>); pub struct Without<T>(PhantomData<T>);
impl<T: Component> WorldQuery for Without<T> { impl<T: Component> WorldQuery for Without<T> {
@ -573,13 +578,15 @@ macro_rules! impl_flag_filter {
} }
impl_flag_filter!( impl_flag_filter!(
/// Filter that retrieves components of type `T` that have been added since the start of the frame /// Filter that retrieves components of type `T` that have been added since the start of the
/// frame
/// ///
/// This filter is useful as a performance optimization as it means that the query contains fewer items /// This filter is useful as a performance optimization as it means that the query contains
/// for a system to iterate over. /// fewer items for a system to iterate over.
/// ///
/// Because the ordering of systems can change and this filter is only effective on changes before the query executes /// Because the ordering of systems can change and this filter is only effective on changes
/// you need to use explicit dependency ordering or ordered stages for these query filters to be useful. /// before the query executes you need to use explicit dependency ordering or ordered
/// stages for these query filters to be useful.
/// ///
/// ///
/// Example: /// Example:
@ -604,14 +611,15 @@ impl_flag_filter!(
); );
impl_flag_filter!( impl_flag_filter!(
/// Filter that retrieves components of type `T` that have been mutated since the start of the frame. /// Filter that retrieves components of type `T` that have been mutated since the start of the
/// Added components do not count as mutated. /// frame. Added components do not count as mutated.
/// ///
/// This filter is useful as a performance optimization as it means that the query contains fewer items /// This filter is useful as a performance optimization as it means that the query contains
/// for a system to iterate over. /// fewer items for a system to iterate over.
/// ///
/// Because the ordering of systems can change and this filter is only effective on changes before the query executes /// Because the ordering of systems can change and this filter is only effective on changes
/// you need to use explicit dependency ordering or ordered stages for these query filters to be useful. /// before the query executes you need to use explicit dependency ordering or ordered
/// stages for these query filters to be useful.
/// ///
/// Example: /// Example:
/// ``` /// ```
@ -635,15 +643,18 @@ impl_flag_filter!(
); );
impl_flag_filter!( impl_flag_filter!(
/// Filter that retrieves components of type `T` that have been added or mutated since the start of the frame /// Filter that retrieves components of type `T` that have been added or mutated since the
/// start of the frame
/// ///
/// This filter is useful as a performance optimization as it means that the query contains fewer items /// This filter is useful as a performance optimization as it means that the query contains
/// for a system to iterate over. /// fewer items for a system to iterate over.
/// ///
/// Because the ordering of systems can change and this filter is only effective on changes before the query executes /// Because the ordering of systems can change and this filter is only effective on changes
/// you need to use explicit dependency ordering or ordered stages for these query filters to be useful. /// before the query executes you need to use explicit dependency ordering or ordered
/// stages for these query filters to be useful.
/// ///
/// Also see the documentation for [`Mutated<T>`] and [`Added`] as this filter is a logical OR of them. /// Also see the documentation for [`Mutated<T>`] and [`Added`] as this filter is a logical OR
/// of them.
Changed, Changed,
ChangedState, ChangedState,
ChangedFetch, ChangedFetch,

View File

@ -199,8 +199,8 @@ where
/// # Safety /// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query. /// have unique access to the components they query.
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` with /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
/// a mismatched WorldId is unsafe. /// with a mismatched WorldId is unsafe.
#[inline] #[inline]
pub(crate) unsafe fn iter_unchecked_manual<'w, 's>( pub(crate) unsafe fn iter_unchecked_manual<'w, 's>(
&'s self, &'s self,
@ -296,8 +296,8 @@ where
/// # Safety /// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query. /// have unique access to the components they query.
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` with /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
/// a mismatched WorldId is unsafe. /// with a mismatched WorldId is unsafe.
pub(crate) unsafe fn for_each_unchecked_manual<'w, 's>( pub(crate) unsafe fn for_each_unchecked_manual<'w, 's>(
&'s self, &'s self,
world: &'w World, world: &'w World,
@ -341,8 +341,8 @@ where
/// # Safety /// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query. /// have unique access to the components they query.
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` with /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
/// a mismatched WorldId is unsafe. /// with a mismatched WorldId is unsafe.
pub unsafe fn par_for_each_unchecked_manual<'w, 's>( pub unsafe fn par_for_each_unchecked_manual<'w, 's>(
&'s self, &'s self,
world: &'w World, world: &'w World,

View File

@ -43,8 +43,10 @@ impl ReflectComponent {
} }
/// # Safety /// # Safety
/// This method does not prevent you from having two mutable pointers to the same data, violating Rust's aliasing rules. To avoid this: /// This method does not prevent you from having two mutable pointers to the same data,
/// * Only call this method in an exclusive system to avoid sharing across threads (or use a scheduler that enforces safe memory access). /// violating Rust's aliasing rules. To avoid this:
/// * Only call this method in an exclusive system to avoid sharing across threads (or use a
/// scheduler that enforces safe memory access).
/// * Don't call this method more than once in the same scope for a given component. /// * Don't call this method more than once in the same scope for a given component.
pub unsafe fn reflect_component_unchecked_mut<'a>( pub unsafe fn reflect_component_unchecked_mut<'a>(
&self, &self,

View File

@ -38,8 +38,8 @@ impl ParallelSystemExecutor for SingleThreadedExecutor {
} }
impl SingleThreadedExecutor { impl SingleThreadedExecutor {
/// Calls system.new_archetype() for each archetype added since the last call to [update_archetypes] and /// Calls system.new_archetype() for each archetype added since the last call to
/// updates cached archetype_component_access. /// [update_archetypes] and updates cached archetype_component_access.
fn update_archetypes(&mut self, systems: &mut [ParallelSystemContainer], world: &World) { fn update_archetypes(&mut self, systems: &mut [ParallelSystemContainer], world: &World) {
let archetypes = world.archetypes(); let archetypes = world.archetypes();
let old_generation = self.archetype_generation; let old_generation = self.archetype_generation;

View File

@ -148,8 +148,8 @@ impl ParallelSystemExecutor for ParallelExecutor {
} }
impl ParallelExecutor { impl ParallelExecutor {
/// Calls system.new_archetype() for each archetype added since the last call to [update_archetypes] and /// Calls system.new_archetype() for each archetype added since the last call to
/// updates cached archetype_component_access. /// [update_archetypes] and updates cached archetype_component_access.
fn update_archetypes(&mut self, systems: &mut [ParallelSystemContainer], world: &World) { fn update_archetypes(&mut self, systems: &mut [ParallelSystemContainer], world: &World) {
let archetypes = world.archetypes(); let archetypes = world.archetypes();
let old_generation = self.archetype_generation; let old_generation = self.archetype_generation;

View File

@ -35,13 +35,14 @@ impl_downcast!(Stage);
/// This occurs because, in the absence of explicit constraints, systems are executed in /// This occurs because, in the absence of explicit constraints, systems are executed in
/// an unstable, arbitrary order within each stage that may vary between runs and frames. /// an unstable, arbitrary order within each stage that may vary between runs and frames.
/// ///
/// Some ambiguities reported by the ambiguity checker may be warranted (to allow two systems to run without blocking each other) /// Some ambiguities reported by the ambiguity checker may be warranted (to allow two systems to run
/// or spurious, as the exact combination of archetypes used may prevent them from ever conflicting during actual gameplay. /// without blocking each other) or spurious, as the exact combination of archetypes used may
/// You can resolve the warnings produced by the ambiguity checker by adding `.before` or `.after` to one of the conflicting systems /// prevent them from ever conflicting during actual gameplay. You can resolve the warnings produced
/// by the ambiguity checker by adding `.before` or `.after` to one of the conflicting systems
/// referencing the other system to force a specific ordering. /// referencing the other system to force a specific ordering.
/// ///
/// The checker may report a system more times than the amount of constraints it would actually need to have /// The checker may report a system more times than the amount of constraints it would actually need
/// unambiguous order with regards to a group of already-constrained systems. /// to have unambiguous order with regards to a group of already-constrained systems.
pub struct ReportExecutionOrderAmbiguities; pub struct ReportExecutionOrderAmbiguities;
struct VirtualSystemSet { struct VirtualSystemSet {
@ -523,7 +524,7 @@ fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize, Vec<
for (index_a, relations) in all_relations.drain(..).enumerate() { for (index_a, relations) in all_relations.drain(..).enumerate() {
// TODO: prove that `.take(index_a)` would be correct here, and uncomment it if so. // TODO: prove that `.take(index_a)` would be correct here, and uncomment it if so.
for index_b in full_bitset.difference(&relations) for index_b in full_bitset.difference(&relations)
/*.take(index_a)*/ // .take(index_a)
{ {
if !processed.contains(index_b) if !processed.contains(index_b)
&& all_ambiguity_sets[index_a].is_disjoint(&all_ambiguity_sets[index_b]) && all_ambiguity_sets[index_a].is_disjoint(&all_ambiguity_sets[index_b])

View File

@ -217,7 +217,8 @@ impl<T: Clone> State<T> {
self.next.as_ref() self.next.as_ref()
} }
/// Queue a state change. This will fail if there is already a state in the queue, or if the given `state` matches the current state /// Queue a state change. This will fail if there is already a state in the queue, or if the
/// given `state` matches the current state
pub fn set_next(&mut self, state: T) -> Result<(), StateError> { pub fn set_next(&mut self, state: T) -> Result<(), StateError> {
if std::mem::discriminant(&self.current) == std::mem::discriminant(&state) { if std::mem::discriminant(&self.current) == std::mem::discriminant(&state) {
return Err(StateError::AlreadyInState); return Err(StateError::AlreadyInState);
@ -231,7 +232,8 @@ impl<T: Clone> State<T> {
Ok(()) Ok(())
} }
/// Same as [Self::set_next], but if there is already a next state, it will be overwritten instead of failing /// Same as [Self::set_next], but if there is already a next state, it will be overwritten
/// instead of failing
pub fn overwrite_next(&mut self, state: T) -> Result<(), StateError> { pub fn overwrite_next(&mut self, state: T) -> Result<(), StateError> {
if std::mem::discriminant(&self.current) == std::mem::discriminant(&state) { if std::mem::discriminant(&self.current) == std::mem::discriminant(&state) {
return Err(StateError::AlreadyInState); return Err(StateError::AlreadyInState);

View File

@ -98,7 +98,9 @@ impl BlobVec {
std::ptr::copy_nonoverlapping(value, ptr, self.item_layout.size()); std::ptr::copy_nonoverlapping(value, ptr, self.item_layout.size());
} }
/// increases the length by one (and grows the vec if needed) with uninitialized memory and returns the index /// increases the length by one (and grows the vec if needed) with uninitialized memory and
/// returns the index
///
/// # Safety /// # Safety
/// the newly allocated space must be immediately populated with a valid value /// the newly allocated space must be immediately populated with a valid value
#[inline] #[inline]
@ -110,17 +112,19 @@ impl BlobVec {
} }
/// # Safety /// # Safety
/// len must be <= capacity. if length is decreased, "out of bounds" items must be dropped. Newly added items must be /// len must be <= capacity. if length is decreased, "out of bounds" items must be dropped.
/// immediately populated with valid values and length must be increased. For better unwind safety, call [BlobVec::set_len] /// Newly added items must be immediately populated with valid values and length must be
/// _after_ populating a new value. /// increased. For better unwind safety, call [BlobVec::set_len] _after_ populating a new
/// value.
pub unsafe fn set_len(&mut self, len: usize) { pub unsafe fn set_len(&mut self, len: usize) {
debug_assert!(len <= self.capacity()); debug_assert!(len <= self.capacity());
self.len = len; self.len = len;
} }
/// Performs a "swap remove" at the given `index`, which removes the item at `index` and moves the last item /// Performs a "swap remove" at the given `index`, which removes the item at `index` and moves
/// in the [BlobVec] to `index` (if `index` is not the last item). It is the caller's responsibility to /// the last item in the [BlobVec] to `index` (if `index` is not the last item). It is the
/// drop the returned pointer, if that is desirable. /// caller's responsibility to drop the returned pointer, if that is desirable.
///
/// # Safety /// # Safety
/// It is the caller's responsibility to ensure that `index` is < self.len() /// It is the caller's responsibility to ensure that `index` is < self.len()
/// Callers should _only_ access the returned pointer immediately after calling this function. /// Callers should _only_ access the returned pointer immediately after calling this function.
@ -161,6 +165,7 @@ impl BlobVec {
} }
/// Gets a pointer to the start of the vec /// Gets a pointer to the start of the vec
///
/// # Safety /// # Safety
/// must ensure rust mutability rules are not violated /// must ensure rust mutability rules are not violated
#[inline] #[inline]
@ -170,13 +175,13 @@ impl BlobVec {
pub fn clear(&mut self) { pub fn clear(&mut self) {
let len = self.len; let len = self.len;
// We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't accidentally // We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't
// drop elements twice in the event of a drop impl panicking. // accidentally drop elements twice in the event of a drop impl panicking.
self.len = 0; self.len = 0;
for i in 0..len { for i in 0..len {
unsafe { unsafe {
// NOTE: this doesn't use self.get_unchecked(i) because the debug_assert on index will // NOTE: this doesn't use self.get_unchecked(i) because the debug_assert on index
// panic here due to self.len being set to 0 // will panic here due to self.len being set to 0
let ptr = self.get_ptr().as_ptr().add(i * self.item_layout.size()); let ptr = self.get_ptr().as_ptr().add(i * self.item_layout.size());
(self.drop)(ptr); (self.drop)(ptr);
} }
@ -260,7 +265,8 @@ mod tests {
use crate::component::TypeInfo; use crate::component::TypeInfo;
use std::{alloc::Layout, cell::RefCell, rc::Rc}; use std::{alloc::Layout, cell::RefCell, rc::Rc};
/// # Safety: /// # Safety
///
/// `blob_vec` must have a layout that matches Layout::new::<T>() /// `blob_vec` must have a layout that matches Layout::new::<T>()
unsafe fn push<T>(blob_vec: &mut BlobVec, mut value: T) { unsafe fn push<T>(blob_vec: &mut BlobVec, mut value: T) {
let index = blob_vec.push_uninit(); let index = blob_vec.push_uninit();
@ -268,7 +274,8 @@ mod tests {
std::mem::forget(value); std::mem::forget(value);
} }
/// # Safety: /// # Safety
///
/// `blob_vec` must have a layout that matches Layout::new::<T>() /// `blob_vec` must have a layout that matches Layout::new::<T>()
unsafe fn swap_remove<T>(blob_vec: &mut BlobVec, index: usize) -> T { unsafe fn swap_remove<T>(blob_vec: &mut BlobVec, index: usize) -> T {
assert!(index < blob_vec.len()); assert!(index < blob_vec.len());
@ -276,9 +283,10 @@ mod tests {
value.cast::<T>().read() value.cast::<T>().read()
} }
/// # Safety: /// # Safety
/// `blob_vec` must have a layout that matches Layout::new::<T>(), it most store a valid T value at ///
/// the given `index` /// `blob_vec` must have a layout that matches Layout::new::<T>(), it most store a valid T value
/// at the given `index`
unsafe fn get_mut<T>(blob_vec: &mut BlobVec, index: usize) -> &mut T { unsafe fn get_mut<T>(blob_vec: &mut BlobVec, index: usize) -> &mut T {
assert!(index < blob_vec.len()); assert!(index < blob_vec.len());
&mut *blob_vec.get_unchecked(index).cast::<T>() &mut *blob_vec.get_unchecked(index).cast::<T>()

View File

@ -113,10 +113,12 @@ impl ComponentSparseSet {
} }
/// Inserts the `entity` key and component `value` pair into this sparse set. /// Inserts the `entity` key and component `value` pair into this sparse set.
/// The caller is responsible for ensuring the value is not dropped. This collection will drop the value when needed. /// The caller is responsible for ensuring the value is not dropped. This collection will drop
/// the value when needed.
///
/// # Safety /// # Safety
/// The `value` pointer must point to a valid address that matches the `Layout` inside the `ComponentInfo` given /// The `value` pointer must point to a valid address that matches the `Layout` inside the
/// when constructing this sparse set. /// `ComponentInfo` given when constructing this sparse set.
pub unsafe fn insert(&mut self, entity: Entity, value: *mut u8, flags: ComponentFlags) { pub unsafe fn insert(&mut self, entity: Entity, value: *mut u8, flags: ComponentFlags) {
let dense = &mut self.dense; let dense = &mut self.dense;
let entities = &mut self.entities; let entities = &mut self.entities;
@ -175,8 +177,9 @@ impl ComponentSparseSet {
}) })
} }
/// Removes the `entity` from this sparse set and returns a pointer to the associated value (if it exists). /// Removes the `entity` from this sparse set and returns a pointer to the associated value (if
/// It is the caller's responsibility to drop the returned ptr (if Some is returned). /// it exists). It is the caller's responsibility to drop the returned ptr (if Some is
/// returned).
pub fn remove_and_forget(&mut self, entity: Entity) -> Option<*mut u8> { pub fn remove_and_forget(&mut self, entity: Entity) -> Option<*mut u8> {
self.sparse.remove(entity).map(|dense_index| { self.sparse.remove(entity).map(|dense_index| {
// SAFE: unique access to flags // SAFE: unique access to flags
@ -268,8 +271,8 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
self.dense.push(value); self.dense.push(value);
} }
// PERF: switch to this. it's faster but it has an invalid memory access on table_add_remove_many // PERF: switch to this. it's faster but it has an invalid memory access on
// let dense = &mut self.dense; // table_add_remove_many let dense = &mut self.dense;
// let indices = &mut self.indices; // let indices = &mut self.indices;
// let dense_index = *self.sparse.get_or_insert_with(index.clone(), move || { // let dense_index = *self.sparse.get_or_insert_with(index.clone(), move || {
// if dense.len() == dense.capacity() { // if dense.len() == dense.capacity() {

View File

@ -49,7 +49,8 @@ impl Column {
/// # Safety /// # Safety
/// Assumes data has already been allocated for the given row/column. /// Assumes data has already been allocated for the given row/column.
/// Allows aliased mutable accesses to the data at the given `row`. Caller must ensure that this does not happen. /// Allows aliased mutable accesses to the data at the given `row`. Caller must ensure that this
/// does not happen.
#[inline] #[inline]
pub unsafe fn set_unchecked(&self, row: usize, data: *mut u8) { pub unsafe fn set_unchecked(&self, row: usize, data: *mut u8) {
self.data.set_unchecked(row, data); self.data.set_unchecked(row, data);
@ -67,7 +68,8 @@ impl Column {
/// # Safety /// # Safety
/// Assumes data has already been allocated for the given row/column. /// Assumes data has already been allocated for the given row/column.
/// Allows aliased mutable accesses to the row's ComponentFlags. Caller must ensure that this does not happen. /// Allows aliased mutable accesses to the row's ComponentFlags. Caller must ensure that this
/// does not happen.
#[inline] #[inline]
#[allow(clippy::mut_from_ref)] #[allow(clippy::mut_from_ref)]
pub unsafe fn get_flags_unchecked_mut(&self, row: usize) -> &mut ComponentFlags { pub unsafe fn get_flags_unchecked_mut(&self, row: usize) -> &mut ComponentFlags {
@ -193,7 +195,9 @@ impl Table {
) )
} }
/// Removes the entity at the given row and returns the entity swapped in to replace it (if an entity was swapped in) /// Removes the entity at the given row and returns the entity swapped in to replace it (if an
/// entity was swapped in)
///
/// # Safety /// # Safety
/// `row` must be in-bounds /// `row` must be in-bounds
pub unsafe fn swap_remove_unchecked(&mut self, row: usize) -> Option<Entity> { pub unsafe fn swap_remove_unchecked(&mut self, row: usize) -> Option<Entity> {
@ -209,9 +213,11 @@ impl Table {
} }
} }
/// Moves the `row` column values to `new_table`, for the columns shared between both tables. Returns the index of the /// Moves the `row` column values to `new_table`, for the columns shared between both tables.
/// new row in `new_table` and the entity in this table swapped in to replace it (if an entity was swapped in). /// Returns the index of the new row in `new_table` and the entity in this table swapped in
/// missing columns will be "forgotten". It is the caller's responsibility to drop them /// to replace it (if an entity was swapped in). missing columns will be "forgotten". It is
/// the caller's responsibility to drop them
///
/// # Safety /// # Safety
/// Row must be in-bounds /// Row must be in-bounds
pub unsafe fn move_to_and_forget_missing_unchecked( pub unsafe fn move_to_and_forget_missing_unchecked(
@ -239,8 +245,10 @@ impl Table {
} }
} }
/// Moves the `row` column values to `new_table`, for the columns shared between both tables. Returns the index of the /// Moves the `row` column values to `new_table`, for the columns shared between both tables.
/// new row in `new_table` and the entity in this table swapped in to replace it (if an entity was swapped in). /// Returns the index of the new row in `new_table` and the entity in this table swapped in
/// to replace it (if an entity was swapped in).
///
/// # Safety /// # Safety
/// row must be in-bounds /// row must be in-bounds
pub unsafe fn move_to_and_drop_missing_unchecked( pub unsafe fn move_to_and_drop_missing_unchecked(
@ -270,8 +278,10 @@ impl Table {
} }
} }
/// Moves the `row` column values to `new_table`, for the columns shared between both tables. Returns the index of the /// Moves the `row` column values to `new_table`, for the columns shared between both tables.
/// new row in `new_table` and the entity in this table swapped in to replace it (if an entity was swapped in). /// Returns the index of the new row in `new_table` and the entity in this table swapped in
/// to replace it (if an entity was swapped in).
///
/// # Safety /// # Safety
/// `row` must be in-bounds. `new_table` must contain every component this table has /// `row` must be in-bounds. `new_table` must contain every component this table has
pub unsafe fn move_to_superset_unchecked( pub unsafe fn move_to_superset_unchecked(
@ -326,6 +336,7 @@ impl Table {
} }
/// Allocates space for a new entity /// Allocates space for a new entity
///
/// # Safety /// # Safety
/// the allocated row must be written to immediately with valid values in each column /// the allocated row must be written to immediately with valid values in each column
pub unsafe fn allocate(&mut self, entity: Entity) -> usize { pub unsafe fn allocate(&mut self, entity: Entity) -> usize {

View File

@ -49,7 +49,10 @@ impl<'a> Commands<'a> {
/// Creates a new entity with the components contained in `bundle`. /// Creates a new entity with the components contained in `bundle`.
/// ///
/// Note that `bundle` is a [Bundle], which is a collection of components. [Bundle] is automatically implemented for tuples of components. You can also create your own bundle types by deriving [`derive@Bundle`]. If you would like to spawn an entity with a single component, consider wrapping the component in a tuple (which [Bundle] is implemented for). /// Note that `bundle` is a [Bundle], which is a collection of components. [Bundle] is
/// automatically implemented for tuples of components. You can also create your own bundle
/// types by deriving [`derive@Bundle`]. If you would like to spawn an entity with a single
/// component, consider wrapping the component in a tuple (which [Bundle] is implemented for).
/// ///
/// See [`Self::set_current_entity`], [`Self::insert`]. /// See [`Self::set_current_entity`], [`Self::insert`].
/// ///
@ -88,7 +91,8 @@ impl<'a> Commands<'a> {
self self
} }
/// Equivalent to iterating `bundles_iter` and calling [`Self::spawn`] on each bundle, but slightly more performant. /// Equivalent to iterating `bundles_iter` and calling [`Self::spawn`] on each bundle, but
/// slightly more performant.
pub fn spawn_batch<I>(&mut self, bundles_iter: I) -> &mut Self pub fn spawn_batch<I>(&mut self, bundles_iter: I) -> &mut Self
where where
I: IntoIterator + Send + Sync + 'static, I: IntoIterator + Send + Sync + 'static,
@ -167,7 +171,10 @@ impl<'a> Commands<'a> {
/// ///
/// # Warning /// # Warning
/// ///
/// It's possible to call this with a bundle, but this is likely not intended and [`Self::with_bundle`] should be used instead. If `with` is called with a bundle, the bundle itself will be added as a component instead of the bundles' inner components each being added. /// It's possible to call this with a bundle, but this is likely not intended and
/// [`Self::with_bundle`] should be used instead. If `with` is called with a bundle, the bundle
/// itself will be added as a component instead of the bundles' inner components each being
/// added.
/// ///
/// # Example /// # Example
/// ///
@ -207,7 +214,8 @@ impl<'a> Commands<'a> {
self self
} }
/// Adds a command directly to the command list. Prefer this to [`Self::add_command_boxed`] if the type of `command` is statically known. /// Adds a command directly to the command list. Prefer this to [`Self::add_command_boxed`] if
/// the type of `command` is statically known.
pub fn add_command<C: Command>(&mut self, command: C) -> &mut Self { pub fn add_command<C: Command>(&mut self, command: C) -> &mut Self {
self.queue.push(Box::new(command)); self.queue.push(Box::new(command));
self self
@ -344,7 +352,8 @@ where
{ {
fn write(self: Box<Self>, world: &mut World) { fn write(self: Box<Self>, world: &mut World) {
if let Some(mut entity_mut) = world.get_entity_mut(self.entity) { if let Some(mut entity_mut) = world.get_entity_mut(self.entity) {
// remove intersection to gracefully handle components that were removed before running this command // remove intersection to gracefully handle components that were removed before running
// this command
entity_mut.remove_bundle_intersection::<T>(); entity_mut.remove_bundle_intersection::<T>();
} }
} }

View File

@ -13,7 +13,8 @@ pub struct SystemState {
pub(crate) name: Cow<'static, str>, pub(crate) name: Cow<'static, str>,
pub(crate) component_access_set: FilteredAccessSet<ComponentId>, pub(crate) component_access_set: FilteredAccessSet<ComponentId>,
pub(crate) archetype_component_access: Access<ArchetypeComponentId>, pub(crate) archetype_component_access: Access<ArchetypeComponentId>,
// NOTE: this must be kept private. making a SystemState non-send is irreversible to prevent SystemParams from overriding each other // NOTE: this must be kept private. making a SystemState non-send is irreversible to prevent
// SystemParams from overriding each other
is_send: bool, is_send: bool,
} }

View File

@ -24,8 +24,8 @@ where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
{ {
/// # Safety /// # Safety
/// This will create a Query that could violate memory safety rules. Make sure that this is only called in /// This will create a Query that could violate memory safety rules. Make sure that this is only
/// ways that ensure the Queries have unique mutable access. /// called in ways that ensure the Queries have unique mutable access.
#[inline] #[inline]
pub(crate) unsafe fn new(world: &'w World, state: &'w QueryState<Q, F>) -> Self { pub(crate) unsafe fn new(world: &'w World, state: &'w QueryState<Q, F>) -> Self {
Self { world, state } Self { world, state }
@ -37,41 +37,49 @@ where
where where
Q::Fetch: ReadOnlyFetch, Q::Fetch: ReadOnlyFetch,
{ {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { self.state.iter_unchecked_manual(self.world) } unsafe { self.state.iter_unchecked_manual(self.world) }
} }
/// Iterates over the query results /// Iterates over the query results
#[inline] #[inline]
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> { pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { self.state.iter_unchecked_manual(self.world) } unsafe { self.state.iter_unchecked_manual(self.world) }
} }
/// Iterates over the query results /// Iterates over the query results
///
/// # Safety /// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component /// This allows aliased mutability. You must make sure this call does not result in multiple
/// mutable references to the same component
#[inline] #[inline]
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> { pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> {
// SEMI-SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SEMI-SAFE: system runs without conflicts with other systems. same-system queries have
// runtime borrow checks when they conflict
self.state.iter_unchecked_manual(self.world) self.state.iter_unchecked_manual(self.world)
} }
/// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot be chained like a normal iterator. /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot
/// This can only be called for read-only queries /// be chained like a normal iterator. This can only be called for read-only queries
#[inline] #[inline]
pub fn for_each(&self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item)) pub fn for_each(&self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item))
where where
Q::Fetch: ReadOnlyFetch, Q::Fetch: ReadOnlyFetch,
{ {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { self.state.for_each_unchecked_manual(self.world, f) }; unsafe { self.state.for_each_unchecked_manual(self.world, f) };
} }
/// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot be chained like a normal iterator. /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot
/// be chained like a normal iterator.
#[inline] #[inline]
pub fn for_each_mut(&self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item)) { pub fn for_each_mut(&self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item)) {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { self.state.for_each_unchecked_manual(self.world, f) }; unsafe { self.state.for_each_unchecked_manual(self.world, f) };
} }
@ -85,7 +93,8 @@ where
) where ) where
Q::Fetch: ReadOnlyFetch, Q::Fetch: ReadOnlyFetch,
{ {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { unsafe {
self.state self.state
.par_for_each_unchecked_manual(self.world, task_pool, batch_size, f) .par_for_each_unchecked_manual(self.world, task_pool, batch_size, f)
@ -100,7 +109,8 @@ where
batch_size: usize, batch_size: usize,
f: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone, f: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone,
) { ) {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { unsafe {
self.state self.state
.par_for_each_unchecked_manual(self.world, task_pool, batch_size, f) .par_for_each_unchecked_manual(self.world, task_pool, batch_size, f)
@ -113,7 +123,8 @@ where
where where
Q::Fetch: ReadOnlyFetch, Q::Fetch: ReadOnlyFetch,
{ {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { self.state.get_unchecked_manual(self.world, entity) } unsafe { self.state.get_unchecked_manual(self.world, entity) }
} }
@ -123,24 +134,29 @@ where
&mut self, &mut self,
entity: Entity, entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> { ) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
// // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // // SAFE: system runs without conflicts with other systems. same-system queries have
// runtime borrow checks when they conflict
unsafe { self.state.get_unchecked_manual(self.world, entity) } unsafe { self.state.get_unchecked_manual(self.world, entity) }
} }
/// Gets the query result for the given `entity` /// Gets the query result for the given `entity`
///
/// # Safety /// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component /// This allows aliased mutability. You must make sure this call does not result in multiple
/// mutable references to the same component
#[inline] #[inline]
pub unsafe fn get_unchecked( pub unsafe fn get_unchecked(
&self, &self,
entity: Entity, entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> { ) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
// SEMI-SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict // SEMI-SAFE: system runs without conflicts with other systems. same-system queries have
// runtime borrow checks when they conflict
self.state.get_unchecked_manual(self.world, entity) self.state.get_unchecked_manual(self.world, entity)
} }
/// Gets a reference to the entity's component of the given type. This will fail if the entity does not have /// Gets a reference to the entity's component of the given type. This will fail if the entity
/// the given component type or if the given component type does not match this query. /// does not have the given component type or if the given component type does not match
/// this query.
#[inline] #[inline]
pub fn get_component<T: Component>(&self, entity: Entity) -> Result<&T, QueryComponentError> { pub fn get_component<T: Component>(&self, entity: Entity) -> Result<&T, QueryComponentError> {
let world = self.world; let world = self.world;
@ -168,8 +184,9 @@ where
} }
} }
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have /// Gets a mutable reference to the entity's component of the given type. This will fail if the
/// the given component type or if the given component type does not match this query. /// entity does not have the given component type or if the given component type does not
/// match this query.
#[inline] #[inline]
pub fn get_component_mut<T: Component>( pub fn get_component_mut<T: Component>(
&mut self, &mut self,
@ -179,10 +196,12 @@ where
unsafe { self.get_component_unchecked_mut(entity) } unsafe { self.get_component_unchecked_mut(entity) }
} }
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have /// Gets a mutable reference to the entity's component of the given type. This will fail if the
/// the given component type or the component does not match the query. /// entity does not have the given component type or the component does not match the query.
///
/// # Safety /// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component /// This allows aliased mutability. You must make sure this call does not result in multiple
/// mutable references to the same component
#[inline] #[inline]
pub unsafe fn get_component_unchecked_mut<T: Component>( pub unsafe fn get_component_unchecked_mut<T: Component>(
&self, &self,

View File

@ -27,9 +27,10 @@ pub trait System: Send + Sync + 'static {
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId>; fn archetype_component_access(&self) -> &Access<ArchetypeComponentId>;
fn is_send(&self) -> bool; fn is_send(&self) -> bool;
/// # Safety /// # Safety
/// This might access World and Resources in an unsafe manner. This should only be called in one of the following contexts: /// This might access World and Resources in an unsafe manner. This should only be called in one
/// 1. This system is the only system running on the given World across all threads /// of the following contexts: 1. This system is the only system running on the given World
/// 2. This system only runs in parallel with other systems that do not conflict with the `archetype_component_access()` /// across all threads 2. This system only runs in parallel with other systems that do not
/// conflict with the `archetype_component_access()`
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out; unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out;
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
// SAFE: world and resources are exclusively borrowed // SAFE: world and resources are exclusively borrowed

View File

@ -38,8 +38,9 @@ pub trait SystemParam: Sized {
/// # Safety /// # Safety
/// it is the implementors responsibility to ensure `system_state` is populated with the _exact_ /// it is the implementors responsibility to ensure `system_state` is populated with the _exact_
/// [World] access used by the SystemParamState (and associated FetchSystemParam). Additionally, it is the /// [World] access used by the SystemParamState (and associated FetchSystemParam). Additionally, it
/// implementor's responsibility to ensure there is no conflicting access across all SystemParams. /// is the implementor's responsibility to ensure there is no conflicting access across all
/// SystemParams.
pub unsafe trait SystemParamState: Send + Sync + 'static { pub unsafe trait SystemParamState: Send + Sync + 'static {
type Config: Default + Send + Sync; type Config: Default + Send + Sync;
fn init(world: &mut World, system_state: &mut SystemState, config: Self::Config) -> Self; fn init(world: &mut World, system_state: &mut SystemState, config: Self::Config) -> Self;
@ -52,8 +53,8 @@ pub unsafe trait SystemParamState: Send + Sync + 'static {
pub trait SystemParamFetch<'a>: SystemParamState { pub trait SystemParamFetch<'a>: SystemParamState {
type Item; type Item;
/// # Safety /// # Safety
/// This call might access any of the input parameters in an unsafe way. Make sure the data access is safe in /// This call might access any of the input parameters in an unsafe way. Make sure the data
/// the context of the system scheduler /// access is safe in the context of the system scheduler
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_state: &'a SystemState,
@ -70,8 +71,8 @@ where
type Fetch = QueryState<Q, F>; type Fetch = QueryState<Q, F>;
} }
// SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemState. If this QueryState conflicts // SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemState. If
// with any prior access, a panic will occur. // this QueryState conflicts with any prior access, a panic will occur.
unsafe impl<Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamState for QueryState<Q, F> unsafe impl<Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamState for QueryState<Q, F>
where where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
@ -168,7 +169,8 @@ impl<'w, T: Component> Res<'w, T> {
self.flags.contains(ComponentFlags::MUTATED) self.flags.contains(ComponentFlags::MUTATED)
} }
/// Returns true if (and only if) this resource been either mutated or added since the start of the frame. /// Returns true if (and only if) this resource been either mutated or added since the start of
/// the frame.
pub fn changed(&self) -> bool { pub fn changed(&self) -> bool {
self.flags self.flags
.intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED) .intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED)
@ -192,8 +194,8 @@ impl<'a, T: Component> SystemParam for Res<'a, T> {
type Fetch = ResState<T>; type Fetch = ResState<T>;
} }
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemState. If this Res conflicts // SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemState. If this Res
// with any prior access, a panic will occur. // conflicts with any prior access, a panic will occur.
unsafe impl<T: Component> SystemParamState for ResState<T> { unsafe impl<T: Component> SystemParamState for ResState<T> {
type Config = (); type Config = ();
@ -293,7 +295,8 @@ impl<'w, T: Component> ResMut<'w, T> {
self.flags.contains(ComponentFlags::MUTATED) self.flags.contains(ComponentFlags::MUTATED)
} }
/// Returns true if (and only if) this resource been either mutated or added since the start of the frame. /// Returns true if (and only if) this resource been either mutated or added since the start of
/// the frame.
pub fn changed(&self) -> bool { pub fn changed(&self) -> bool {
self.flags self.flags
.intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED) .intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED)
@ -324,8 +327,8 @@ impl<'a, T: Component> SystemParam for ResMut<'a, T> {
type Fetch = ResMutState<T>; type Fetch = ResMutState<T>;
} }
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemState. If this Res conflicts // SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemState. If this Res
// with any prior access, a panic will occur. // conflicts with any prior access, a panic will occur.
unsafe impl<T: Component> SystemParamState for ResMutState<T> { unsafe impl<T: Component> SystemParamState for ResMutState<T> {
type Config = (); type Config = ();
@ -505,8 +508,8 @@ impl<'a, T: Component> SystemParam for RemovedComponents<'a, T> {
type Fetch = RemovedComponentsState<T>; type Fetch = RemovedComponentsState<T>;
} }
// SAFE: no component access. removed component entity collections can be read in parallel and are never mutably borrowed // SAFE: no component access. removed component entity collections can be read in parallel and are
// during system execution // never mutably borrowed during system execution
unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> { unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
type Config = (); type Config = ();
@ -557,8 +560,8 @@ impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
type Fetch = NonSendState<T>; type Fetch = NonSendState<T>;
} }
// SAFE: NonSendComponentId and ArchetypeComponentId access is applied to SystemState. If this NonSend conflicts // SAFE: NonSendComponentId and ArchetypeComponentId access is applied to SystemState. If this
// with any prior access, a panic will occur. // NonSend conflicts with any prior access, a panic will occur.
unsafe impl<T: 'static> SystemParamState for NonSendState<T> { unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
type Config = (); type Config = ();
@ -643,8 +646,8 @@ impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
type Fetch = NonSendMutState<T>; type Fetch = NonSendMutState<T>;
} }
// SAFE: NonSendMut ComponentId and ArchetypeComponentId access is applied to SystemState. If this NonSendMut conflicts // SAFE: NonSendMut ComponentId and ArchetypeComponentId access is applied to SystemState. If this
// with any prior access, a panic will occur. // NonSendMut conflicts with any prior access, a panic will occur.
unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> { unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
type Config = (); type Config = ();
@ -858,6 +861,6 @@ macro_rules! impl_system_param_tuple {
}; };
} }
// TODO: consider creating a Config trait with a default() function, then implementing that for tuples. // TODO: consider creating a Config trait with a default() function, then implementing that for
// that would allow us to go past tuples of len 12 // tuples. that would allow us to go past tuples of len 12
all_tuples!(impl_system_param_tuple, 0, 12, P); all_tuples!(impl_system_param_tuple, 0, 12, P);

View File

@ -36,7 +36,8 @@ impl<'w> EntityRef<'w> {
#[inline] #[inline]
pub fn archetype(&self) -> &Archetype { pub fn archetype(&self) -> &Archetype {
// SAFE: EntityRefs always point to valid entities. Valid entities always have valid archetypes // SAFE: EntityRefs always point to valid entities. Valid entities always have valid
// archetypes
unsafe { unsafe {
self.world self.world
.archetypes .archetypes
@ -76,7 +77,8 @@ impl<'w> EntityRef<'w> {
} }
/// # Safety /// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component /// This allows aliased mutability. You must make sure this call does not result in multiple
/// mutable references to the same component
#[inline] #[inline]
pub unsafe fn get_unchecked_mut<T: Component>(&self) -> Option<Mut<'w, T>> { pub unsafe fn get_unchecked_mut<T: Component>(&self) -> Option<Mut<'w, T>> {
get_component_and_flags_with_type(self.world, TypeId::of::<T>(), self.entity, self.location) get_component_and_flags_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
@ -121,7 +123,8 @@ impl<'w> EntityMut<'w> {
#[inline] #[inline]
pub fn archetype(&self) -> &Archetype { pub fn archetype(&self) -> &Archetype {
// SAFE: EntityRefs always point to valid entities. Valid entities always have valid archetypes // SAFE: EntityRefs always point to valid entities. Valid entities always have valid
// archetypes
unsafe { unsafe {
self.world self.world
.archetypes .archetypes
@ -157,7 +160,8 @@ impl<'w> EntityMut<'w> {
#[inline] #[inline]
pub fn get_mut<T: Component>(&mut self) -> Option<Mut<'w, T>> { pub fn get_mut<T: Component>(&mut self) -> Option<Mut<'w, T>> {
// SAFE: world access is unique, entity location is valid, and returned component is of type T // SAFE: world access is unique, entity location is valid, and returned component is of type
// T
unsafe { unsafe {
get_component_and_flags_with_type( get_component_and_flags_with_type(
self.world, self.world,
@ -173,7 +177,8 @@ impl<'w> EntityMut<'w> {
} }
/// # Safety /// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component /// This allows aliased mutability. You must make sure this call does not result in multiple
/// mutable references to the same component
#[inline] #[inline]
pub unsafe fn get_unchecked_mut<T: Component>(&self) -> Option<Mut<'w, T>> { pub unsafe fn get_unchecked_mut<T: Component>(&self) -> Option<Mut<'w, T>> {
get_component_and_flags_with_type(self.world, TypeId::of::<T>(), self.entity, self.location) get_component_and_flags_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
@ -227,7 +232,8 @@ impl<'w> EntityMut<'w> {
let (old_table, new_table) = storages let (old_table, new_table) = storages
.tables .tables
.get_2_mut(old_table_id, new_archetype.table_id()); .get_2_mut(old_table_id, new_archetype.table_id());
// PERF: store "non bundle" components in edge, then just move those to avoid redundant copies // PERF: store "non bundle" components in edge, then just move those to avoid
// redundant copies
let move_result = let move_result =
old_table.move_to_superset_unchecked(old_table_row, new_table); old_table.move_to_superset_unchecked(old_table_row, new_table);
@ -235,7 +241,8 @@ impl<'w> EntityMut<'w> {
// if an entity was moved into this entity's table spot, update its table row // if an entity was moved into this entity's table spot, update its table row
if let Some(swapped_entity) = move_result.swapped_entity { if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap(); let swapped_location = entities.get(swapped_entity).unwrap();
// SAFE: entity is live and is therefore contained in an archetype that exists // SAFE: entity is live and is therefore contained in an archetype that
// exists
archetypes archetypes
.get_unchecked_mut(swapped_location.archetype_id) .get_unchecked_mut(swapped_location.archetype_id)
.set_entity_table_row(swapped_location.index, old_table_row); .set_entity_table_row(swapped_location.index, old_table_row);
@ -297,7 +304,8 @@ impl<'w> EntityMut<'w> {
let old_archetype = unsafe { archetypes.get_unchecked_mut(old_location.archetype_id) }; let old_archetype = unsafe { archetypes.get_unchecked_mut(old_location.archetype_id) };
let mut bundle_components = bundle_info.component_ids.iter().cloned(); let mut bundle_components = bundle_info.component_ids.iter().cloned();
let entity = self.entity; let entity = self.entity;
// SAFE: bundle components are iterated in order, which guarantees that the component type matches // SAFE: bundle components are iterated in order, which guarantees that the component type
// matches
let result = unsafe { let result = unsafe {
T::from_components(|| { T::from_components(|| {
let component_id = bundle_components.next().unwrap(); let component_id = bundle_components.next().unwrap();
@ -330,7 +338,8 @@ impl<'w> EntityMut<'w> {
.tables .tables
.get_2_mut(old_table_id, new_archetype.table_id()); .get_2_mut(old_table_id, new_archetype.table_id());
// SAFE: table_row exists. All "missing" components have been extracted into the bundle above and the caller takes ownership // SAFE: table_row exists. All "missing" components have been extracted into the bundle
// above and the caller takes ownership
let move_result = let move_result =
unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) }; unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) };
@ -505,22 +514,24 @@ impl<'w> EntityMut<'w> {
/// # Safety /// # Safety
/// Caller must not modify the world in a way that changes the current entity's location /// Caller must not modify the world in a way that changes the current entity's location
/// If the caller _does_ do something that could change the location, self.update_location() must be /// If the caller _does_ do something that could change the location, self.update_location()
/// called before using any other methods in EntityMut /// must be called before using any other methods in EntityMut
#[inline] #[inline]
pub unsafe fn world_mut(&mut self) -> &mut World { pub unsafe fn world_mut(&mut self) -> &mut World {
self.world self.world
} }
/// Updates the internal entity location to match the current location in the internal [World]. /// Updates the internal entity location to match the current location in the internal [World].
/// This is only needed if the user called [EntityMut::world], which enables the location to change. /// This is only needed if the user called [EntityMut::world], which enables the location to
/// change.
pub fn update_location(&mut self) { pub fn update_location(&mut self) {
self.location = self.world.entities().get(self.entity).unwrap(); self.location = self.world.entities().get(self.entity).unwrap();
} }
} }
/// # Safety /// # Safety
/// `entity_location` must be within bounds of the given archetype and `entity` must exist inside the archetype /// `entity_location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype
#[inline] #[inline]
unsafe fn get_component( unsafe fn get_component(
world: &World, world: &World,
@ -580,7 +591,8 @@ unsafe fn get_component_and_flags(
} }
/// # Safety /// # Safety
// `entity_location` must be within bounds of the given archetype and `entity` must exist inside the archetype // `entity_location` must be within bounds of the given archetype and `entity` must exist inside the
// archetype
/// The relevant table row must be removed separately /// The relevant table row must be removed separately
/// `component_id` must be valid /// `component_id` must be valid
#[inline] #[inline]
@ -666,9 +678,10 @@ unsafe fn contains_component_with_id(
.contains(component_id) .contains(component_id)
} }
/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same [ArchetypeId], /// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same
/// in the event that adding the given bundle does not result in an Archetype change. Results are cached in the /// [ArchetypeId], in the event that adding the given bundle does not result in an Archetype change.
/// Archetype Graph to avoid redundant work. /// Results are cached in the Archetype Graph to avoid redundant work.
///
/// # Safety /// # Safety
/// `archetype_id` must exist and components in `bundle_info` must exist /// `archetype_id` must exist and components in `bundle_info` must exist
pub(crate) unsafe fn add_bundle_to_archetype( pub(crate) unsafe fn add_bundle_to_archetype(
@ -760,11 +773,13 @@ pub(crate) unsafe fn add_bundle_to_archetype(
} }
} }
/// Removes a bundle from the given archetype and returns the resulting archetype (or None if the removal was invalid). /// Removes a bundle from the given archetype and returns the resulting archetype (or None if the
/// in the event that adding the given bundle does not result in an Archetype change. Results are cached in the /// removal was invalid). in the event that adding the given bundle does not result in an Archetype
/// Archetype Graph to avoid redundant work. /// change. Results are cached in the Archetype Graph to avoid redundant work.
/// if `intersection` is false, attempting to remove a bundle with components _not_ contained in the current archetype will fail, /// if `intersection` is false, attempting to remove a bundle with components _not_ contained in the
/// returning None. if `intersection` is true, components in the bundle but not in the current archetype will be ignored /// current archetype will fail, returning None. if `intersection` is true, components in the bundle
/// but not in the current archetype will be ignored
///
/// # Safety /// # Safety
/// `archetype_id` must exist and components in `bundle_info` must exist /// `archetype_id` must exist and components in `bundle_info` must exist
unsafe fn remove_bundle_from_archetype( unsafe fn remove_bundle_from_archetype(
@ -775,7 +790,8 @@ unsafe fn remove_bundle_from_archetype(
bundle_info: &BundleInfo, bundle_info: &BundleInfo,
intersection: bool, intersection: bool,
) -> Option<ArchetypeId> { ) -> Option<ArchetypeId> {
// check the archetype graph to see if the Bundle has been removed from this archetype in the past // check the archetype graph to see if the Bundle has been removed from this archetype in the
// past
let remove_bundle_result = { let remove_bundle_result = {
// SAFE: entity location is valid and therefore the archetype exists // SAFE: entity location is valid and therefore the archetype exists
let current_archetype = archetypes.get_unchecked_mut(archetype_id); let current_archetype = archetypes.get_unchecked_mut(archetype_id);
@ -808,8 +824,9 @@ unsafe fn remove_bundle_from_archetype(
StorageType::SparseSet => removed_sparse_set_components.push(component_id), StorageType::SparseSet => removed_sparse_set_components.push(component_id),
} }
} else if !intersection { } else if !intersection {
// a component in the bundle was not present in the entity's archetype, so this removal is invalid // a component in the bundle was not present in the entity's archetype, so this
// cache the result in the archetype graph // removal is invalid cache the result in the archetype
// graph
current_archetype current_archetype
.edges_mut() .edges_mut()
.set_remove_bundle(bundle_info.id, None); .set_remove_bundle(bundle_info.id, None);
@ -817,7 +834,8 @@ unsafe fn remove_bundle_from_archetype(
} }
} }
// sort removed components so we can do an efficient "sorted remove". archetype components are already sorted // sort removed components so we can do an efficient "sorted remove". archetype
// components are already sorted
removed_table_components.sort(); removed_table_components.sort();
removed_sparse_set_components.sort(); removed_sparse_set_components.sort();
next_table_components = current_archetype.table_components().to_vec(); next_table_components = current_archetype.table_components().to_vec();

View File

@ -30,9 +30,10 @@ impl Default for WorldId {
} }
} }
/// [World] stores and exposes operations on [entities](Entity), [components](Component), and their associated metadata. /// [World] stores and exposes operations on [entities](Entity), [components](Component), and their
/// Each [Entity] has a set of components. Each component can have up to one instance of each component type. /// associated metadata. Each [Entity] has a set of components. Each component can have up to one
/// Entity components can be created, updated, removed, and queried using a given [World]. /// instance of each component type. Entity components can be created, updated, removed, and queried
/// using a given [World].
#[derive(Default)] #[derive(Default)]
pub struct World { pub struct World {
id: WorldId, id: WorldId,
@ -96,16 +97,17 @@ impl World {
&self.bundles &self.bundles
} }
/// Retrieves a [WorldCell], which safely enables multiple mutable World accesses at the same time, /// Retrieves a [WorldCell], which safely enables multiple mutable World accesses at the same
/// provided those accesses do not conflict with each other. /// time, provided those accesses do not conflict with each other.
#[inline] #[inline]
pub fn cell(&mut self) -> WorldCell<'_> { pub fn cell(&mut self) -> WorldCell<'_> {
WorldCell::new(self) WorldCell::new(self)
} }
/// Registers a new component using the given [ComponentDescriptor]. Components do not need to be manually /// Registers a new component using the given [ComponentDescriptor]. Components do not need to
/// registered. This just provides a way to override default configuration. Attempting to register a component /// be manually registered. This just provides a way to override default configuration.
/// with a type that has already been used by [World] will result in an error. /// Attempting to register a component with a type that has already been used by [World]
/// will result in an error.
/// ///
/// The default component storage type can be overridden like this: /// The default component storage type can be overridden like this:
/// ///
@ -269,7 +271,8 @@ impl World {
// PERF: consider avoiding allocating entities in the empty archetype unless needed // PERF: consider avoiding allocating entities in the empty archetype unless needed
// SAFE: archetype tables always exist // SAFE: archetype tables always exist
let table = self.storages.tables.get_unchecked_mut(archetype.table_id()); let table = self.storages.tables.get_unchecked_mut(archetype.table_id());
// SAFE: no components are allocated by archetype.allocate() because the archetype is empty // SAFE: no components are allocated by archetype.allocate() because the archetype is
// empty
let location = archetype.allocate(entity, table.allocate(entity)); let location = archetype.allocate(entity, table.allocate(entity));
// SAFE: entity index was just allocated // SAFE: entity index was just allocated
self.entities self.entities
@ -347,9 +350,9 @@ impl World {
self.get_entity_mut(entity)?.get_mut() self.get_entity_mut(entity)?.get_mut()
} }
/// Despawns the given `entity`, if it exists. This will also remove all of the entity's [Component]s. /// Despawns the given `entity`, if it exists. This will also remove all of the entity's
/// Returns `true` if the `entity` is successfully despawned and `false` if the `entity` does not exist. /// [Component]s. Returns `true` if the `entity` is successfully despawned and `false` if
/// ``` /// the `entity` does not exist. ```
/// use bevy_ecs::world::World; /// use bevy_ecs::world::World;
/// ///
/// struct Position { /// struct Position {
@ -391,7 +394,6 @@ impl World {
/// Returns [QueryState] for the given [WorldQuery], which is used to efficiently /// Returns [QueryState] for the given [WorldQuery], which is used to efficiently
/// run queries on the [World]. /// run queries on the [World].
/// ``` /// ```
///
/// use bevy_ecs::{entity::Entity, world::World}; /// use bevy_ecs::{entity::Entity, world::World};
/// ///
/// #[derive(Debug, PartialEq)] /// #[derive(Debug, PartialEq)]
@ -450,7 +452,8 @@ impl World {
QueryState::new(self) QueryState::new(self)
} }
/// Returns an iterator of entities that had components of type `T` removed since the last call to [World::clear_trackers]. /// Returns an iterator of entities that had components of type `T` removed since the last call
/// to [World::clear_trackers].
pub fn removed<T: Component>(&self) -> std::iter::Cloned<std::slice::Iter<'_, Entity>> { pub fn removed<T: Component>(&self) -> std::iter::Cloned<std::slice::Iter<'_, Entity>> {
if let Some(component_id) = self.components.get_id(TypeId::of::<T>()) { if let Some(component_id) = self.components.get_id(TypeId::of::<T>()) {
self.removed_with_id(component_id) self.removed_with_id(component_id)
@ -459,7 +462,8 @@ impl World {
} }
} }
/// Returns an iterator of entities that had components with the given `component_id` removed since the last call to [World::clear_trackers]. /// Returns an iterator of entities that had components with the given `component_id` removed
/// since the last call to [World::clear_trackers].
pub fn removed_with_id( pub fn removed_with_id(
&self, &self,
component_id: ComponentId, component_id: ComponentId,
@ -501,8 +505,8 @@ impl World {
if column.is_empty() { if column.is_empty() {
return None; return None;
} }
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the ptr value / drop is called when // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the
// T is dropped // ptr value / drop is called when T is dropped
let (ptr, _) = unsafe { column.swap_remove_and_forget_unchecked(0) }; let (ptr, _) = unsafe { column.swap_remove_and_forget_unchecked(0) };
// SAFE: column is of type T // SAFE: column is of type T
Some(unsafe { ptr.cast::<T>().read() }) Some(unsafe { ptr.cast::<T>().read() })
@ -528,8 +532,8 @@ impl World {
unsafe { self.get_resource_with_id(component_id) } unsafe { self.get_resource_with_id(component_id) }
} }
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns [None] /// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
/// Resources are "unique" data of a given type. /// [None] Resources are "unique" data of a given type.
#[inline] #[inline]
pub fn get_resource_mut<T: Component>(&mut self) -> Option<Mut<'_, T>> { pub fn get_resource_mut<T: Component>(&mut self) -> Option<Mut<'_, T>> {
// SAFE: unique world access // SAFE: unique world access
@ -537,7 +541,8 @@ impl World {
} }
// PERF: optimize this to avoid redundant lookups // PERF: optimize this to avoid redundant lookups
/// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of calling `func`. /// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of
/// calling `func`.
#[inline] #[inline]
pub fn get_resource_or_insert_with<T: Component>( pub fn get_resource_or_insert_with<T: Component>(
&mut self, &mut self,
@ -549,19 +554,20 @@ impl World {
self.get_resource_mut().unwrap() self.get_resource_mut().unwrap()
} }
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns [None] /// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
/// Resources are "unique" data of a given type. /// [None] Resources are "unique" data of a given type.
///
/// # Safety /// # Safety
/// This will allow aliased mutable access to the given resource type. The caller must ensure that only /// This will allow aliased mutable access to the given resource type. The caller must ensure
/// one mutable access exists at a time. /// that only one mutable access exists at a time.
#[inline] #[inline]
pub unsafe fn get_resource_unchecked_mut<T: Component>(&self) -> Option<Mut<'_, T>> { pub unsafe fn get_resource_unchecked_mut<T: Component>(&self) -> Option<Mut<'_, T>> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?; let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
self.get_resource_unchecked_mut_with_id(component_id) self.get_resource_unchecked_mut_with_id(component_id)
} }
/// Gets a reference to the non-send resource of the given type, if it exists. Otherwise returns [None] /// Gets a reference to the non-send resource of the given type, if it exists. Otherwise returns
/// Resources are "unique" data of a given type. /// [None] Resources are "unique" data of a given type.
#[inline] #[inline]
pub fn get_non_send_resource<T: 'static>(&self) -> Option<&T> { pub fn get_non_send_resource<T: 'static>(&self) -> Option<&T> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?; let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
@ -569,28 +575,29 @@ impl World {
unsafe { self.get_non_send_with_id(component_id) } unsafe { self.get_non_send_with_id(component_id) }
} }
/// Gets a mutable reference to the non-send resource of the given type, if it exists. Otherwise returns [None] /// Gets a mutable reference to the non-send resource of the given type, if it exists. Otherwise
/// Resources are "unique" data of a given type. /// returns [None] Resources are "unique" data of a given type.
#[inline] #[inline]
pub fn get_non_send_resource_mut<T: 'static>(&mut self) -> Option<Mut<'_, T>> { pub fn get_non_send_resource_mut<T: 'static>(&mut self) -> Option<Mut<'_, T>> {
// SAFE: unique world access // SAFE: unique world access
unsafe { self.get_non_send_resource_unchecked_mut() } unsafe { self.get_non_send_resource_unchecked_mut() }
} }
/// Gets a mutable reference to the non-send resource of the given type, if it exists. Otherwise returns [None] /// Gets a mutable reference to the non-send resource of the given type, if it exists. Otherwise
/// Resources are "unique" data of a given type. /// returns [None] Resources are "unique" data of a given type.
///
/// # Safety /// # Safety
/// This will allow aliased mutable access to the given non-send resource type. The caller must ensure that only /// This will allow aliased mutable access to the given non-send resource type. The caller must
/// one mutable access exists at a time. /// ensure that only one mutable access exists at a time.
#[inline] #[inline]
pub unsafe fn get_non_send_resource_unchecked_mut<T: 'static>(&self) -> Option<Mut<'_, T>> { pub unsafe fn get_non_send_resource_unchecked_mut<T: 'static>(&self) -> Option<Mut<'_, T>> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?; let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
self.get_non_send_unchecked_mut_with_id(component_id) self.get_non_send_unchecked_mut_with_id(component_id)
} }
/// Temporarily removes the requested resource from this [World], then re-adds it before returning. /// Temporarily removes the requested resource from this [World], then re-adds it before
/// This enables safe mutable access to a resource while still providing mutable world access /// returning. This enables safe mutable access to a resource while still providing mutable
/// ``` /// world access ```
/// use bevy_ecs::world::{World, Mut}; /// use bevy_ecs::world::{World, Mut};
/// struct A(u32); /// struct A(u32);
/// struct B(u32); /// struct B(u32);
@ -621,8 +628,8 @@ impl World {
if column.is_empty() { if column.is_empty() {
panic!("resource does not exist"); panic!("resource does not exist");
} }
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the ptr value / drop is called when // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of
// T is dropped // the ptr value / drop is called when T is dropped
unsafe { column.swap_remove_and_forget_unchecked(0) } unsafe { column.swap_remove_and_forget_unchecked(0) }
}; };
// SAFE: pointer is of type T // SAFE: pointer is of type T
@ -801,7 +808,8 @@ impl World {
.get_unchecked_mut(empty_archetype.table_id()); .get_unchecked_mut(empty_archetype.table_id());
// PERF: consider pre-allocating space for flushed entities // PERF: consider pre-allocating space for flushed entities
self.entities.flush(|entity, location| { self.entities.flush(|entity, location| {
// SAFE: no components are allocated by archetype.allocate() because the archetype is empty // SAFE: no components are allocated by archetype.allocate() because the archetype
// is empty
*location = empty_archetype.allocate(entity, table.allocate(entity)); *location = empty_archetype.allocate(entity, table.allocate(entity));
}); });
} }

View File

@ -41,7 +41,8 @@ impl<'w, T> Mut<'w, T> {
self.flags.contains(ComponentFlags::MUTATED) self.flags.contains(ComponentFlags::MUTATED)
} }
/// Returns true if (and only if) this component been either mutated or added since the start of the frame. /// Returns true if (and only if) this component been either mutated or added since the start of
/// the frame.
pub fn changed(&self) -> bool { pub fn changed(&self) -> bool {
self.flags self.flags
.intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED) .intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED)

View File

@ -88,7 +88,8 @@ where
fn next(&mut self) -> Option<Entity> { fn next(&mut self) -> Option<Entity> {
let bundle = self.inner.next()?; let bundle = self.inner.next()?;
let entity = self.entities.alloc(); let entity = self.entities.alloc();
// SAFE: component values are immediately written to relevant storages (which have been allocated) // SAFE: component values are immediately written to relevant storages (which have been
// allocated)
unsafe { unsafe {
let table_row = self.table.allocate(entity); let table_row = self.table.allocate(entity);
let location = self.archetype.allocate(entity, table_row); let location = self.archetype.allocate(entity, table_row);

View File

@ -11,8 +11,8 @@ use std::{
rc::Rc, rc::Rc,
}; };
/// Exposes safe mutable access to multiple resources at a time in a World. Attempting to access World in a way that violates /// Exposes safe mutable access to multiple resources at a time in a World. Attempting to access
/// Rust's mutability rules will panic thanks to runtime checks. /// World in a way that violates Rust's mutability rules will panic thanks to runtime checks.
pub struct WorldCell<'w> { pub struct WorldCell<'w> {
pub(crate) world: &'w mut World, pub(crate) world: &'w mut World,
pub(crate) access: Rc<RefCell<ArchetypeComponentAccess>>, pub(crate) access: Rc<RefCell<ArchetypeComponentAccess>>,

View File

@ -33,7 +33,8 @@ pub enum MouseScrollUnit {
Pixel, Pixel,
} }
/// A mouse scroll wheel event, where x represents horizontal scroll and y represents vertical scroll. /// A mouse scroll wheel event, where x represents horizontal scroll and y represents vertical
/// scroll.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MouseWheel { pub struct MouseWheel {
pub unit: MouseScrollUnit, pub unit: MouseScrollUnit,

View File

@ -79,7 +79,8 @@ pub fn lights_node_system(
mut state: Local<LightsNodeSystemState>, mut state: Local<LightsNodeSystemState>,
render_resource_context: Res<Box<dyn RenderResourceContext>>, render_resource_context: Res<Box<dyn RenderResourceContext>>,
ambient_light_resource: Res<AmbientLight>, ambient_light_resource: Res<AmbientLight>,
// TODO: this write on RenderResourceBindings will prevent this system from running in parallel with other systems that do the same // TODO: this write on RenderResourceBindings will prevent this system from running in parallel
// with other systems that do the same
mut render_resource_bindings: ResMut<RenderResourceBindings>, mut render_resource_bindings: ResMut<RenderResourceBindings>,
query: Query<(&Light, &GlobalTransform)>, query: Query<(&Light, &GlobalTransform)>,
) { ) {

View File

@ -4,8 +4,8 @@ use bevy_utils::HashMap;
use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef}; use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef};
/// An ordered ReflectValue->ReflectValue mapping. ReflectValue Keys are assumed to return a non-None hash. /// An ordered ReflectValue->ReflectValue mapping. ReflectValue Keys are assumed to return a
/// Ideally the ordering is stable across runs, but this is not required. /// non-None hash. Ideally the ordering is stable across runs, but this is not required.
/// This corresponds to types like [std::collections::HashMap]. /// This corresponds to types like [std::collections::HashMap].
pub trait Map: Reflect { pub trait Map: Reflect {
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect>; fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect>;

View File

@ -31,11 +31,14 @@ pub trait Reflect: Any + Send + Sync {
fn reflect_ref(&self) -> ReflectRef; fn reflect_ref(&self) -> ReflectRef;
fn reflect_mut(&mut self) -> ReflectMut; fn reflect_mut(&mut self) -> ReflectMut;
fn clone_value(&self) -> Box<dyn Reflect>; fn clone_value(&self) -> Box<dyn Reflect>;
/// Returns a hash of the value (which includes the type) if hashing is supported. Otherwise `None` will be returned. /// Returns a hash of the value (which includes the type) if hashing is supported. Otherwise
/// `None` will be returned.
fn reflect_hash(&self) -> Option<u64>; fn reflect_hash(&self) -> Option<u64>;
/// Returns a "partial equal" comparison result if comparison is supported. Otherwise `None` will be returned. /// Returns a "partial equal" comparison result if comparison is supported. Otherwise `None`
/// will be returned.
fn reflect_partial_eq(&self, _value: &dyn Reflect) -> Option<bool>; fn reflect_partial_eq(&self, _value: &dyn Reflect) -> Option<bool>;
/// Returns a serializable value, if serialization is supported. Otherwise `None` will be returned. /// Returns a serializable value, if serialization is supported. Otherwise `None` will be
/// returned.
fn serializable(&self) -> Option<Serializable>; fn serializable(&self) -> Option<Serializable>;
} }
@ -47,7 +50,8 @@ impl Debug for dyn Reflect {
impl dyn Reflect { impl dyn Reflect {
pub fn downcast<T: Reflect>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> { pub fn downcast<T: Reflect>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> {
// SAFE?: Same approach used by std::any::Box::downcast. ReflectValue is always Any and type has been checked. // SAFE?: Same approach used by std::any::Box::downcast. ReflectValue is always Any and type
// has been checked.
if self.is::<T>() { if self.is::<T>() {
unsafe { unsafe {
let raw: *mut dyn Reflect = Box::into_raw(self); let raw: *mut dyn Reflect = Box::into_raw(self);

View File

@ -13,7 +13,8 @@ pub struct TypeRegistry {
ambiguous_names: HashSet<String>, ambiguous_names: HashSet<String>,
} }
// TODO: remove this wrapper once we migrate to Atelier Assets and the Scene AssetLoader doesn't need a TypeRegistry ref // TODO: remove this wrapper once we migrate to Atelier Assets and the Scene AssetLoader doesn't
// need a TypeRegistry ref
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct TypeRegistryArc { pub struct TypeRegistryArc {
pub internal: Arc<RwLock<TypeRegistry>>, pub internal: Arc<RwLock<TypeRegistry>>,

View File

@ -73,7 +73,8 @@ pub fn camera_system<T: CameraProjection + Component>(
)>, )>,
) { ) {
let mut changed_window_ids = Vec::new(); let mut changed_window_ids = Vec::new();
// handle resize events. latest events are handled first because we only want to resize each window once // handle resize events. latest events are handled first because we only want to resize each
// window once
for event in window_resized_events.iter().rev() { for event in window_resized_events.iter().rev() {
if changed_window_ids.contains(&event.id) { if changed_window_ids.contains(&event.id) {
continue; continue;
@ -82,7 +83,8 @@ pub fn camera_system<T: CameraProjection + Component>(
changed_window_ids.push(event.id); changed_window_ids.push(event.id);
} }
// handle resize events. latest events are handled first because we only want to resize each window once // handle resize events. latest events are handled first because we only want to resize each
// window once
for event in window_created_events.iter().rev() { for event in window_created_events.iter().rev() {
if changed_window_ids.contains(&event.id) { if changed_window_ids.contains(&event.id) {
continue; continue;

View File

@ -34,8 +34,8 @@ pub type Layer = u8;
/// Cameras with this component will only render entities with intersecting /// Cameras with this component will only render entities with intersecting
/// layers. /// layers.
/// ///
/// There are 32 layers numbered `0` - [`TOTAL_LAYERS`](RenderLayers::TOTAL_LAYERS). Entities may belong to one or more /// There are 32 layers numbered `0` - [`TOTAL_LAYERS`](RenderLayers::TOTAL_LAYERS). Entities may
/// layers, or no layer at all. /// belong to one or more layers, or no layer at all.
/// ///
/// The [`Default`] instance of `RenderLayers` contains layer `0`, the first layer. /// The [`Default`] instance of `RenderLayers` contains layer `0`, the first layer.
/// ///
@ -228,7 +228,8 @@ pub fn visible_entities_system(
let order = if let Ok(global_transform) = visible_transform_query.get(entity) { let order = if let Ok(global_transform) = visible_transform_query.get(entity) {
let position = global_transform.translation; let position = global_transform.translation;
// smaller distances are sorted to lower indices by using the distance from the camera // smaller distances are sorted to lower indices by using the distance from the
// camera
FloatOrd(match camera.depth_calculation { FloatOrd(match camera.depth_calculation {
DepthCalculation::ZDifference => camera_position.z - position.z, DepthCalculation::ZDifference => camera_position.z - position.z,
DepthCalculation::Distance => (camera_position - position).length_squared(), DepthCalculation::Distance => (camera_position - position).length_squared(),
@ -253,6 +254,7 @@ pub fn visible_entities_system(
transparent_entities.sort_by_key(|e| -e.order); transparent_entities.sort_by_key(|e| -e.order);
visible_entities.value.extend(transparent_entities); visible_entities.value.extend(transparent_entities);
// TODO: check for big changes in visible entities len() vs capacity() (ex: 2x) and resize to prevent holding unneeded memory // TODO: check for big changes in visible entities len() vs capacity() (ex: 2x) and resize
// to prevent holding unneeded memory
} }
} }

View File

@ -13,7 +13,8 @@ use std::ops::{Add, AddAssign, Mul, MulAssign};
// TODO: Separate types for non-linear sRGB and linear sRGB, with conversions between // TODO: Separate types for non-linear sRGB and linear sRGB, with conversions between
// see comment on bevy issue #688 https://github.com/bevyengine/bevy/pull/688#issuecomment-711414011 // see comment on bevy issue #688 https://github.com/bevyengine/bevy/pull/688#issuecomment-711414011
/// RGBA color in the Linear sRGB colorspace (often colloquially referred to as "linear", "RGB", or "linear RGB"). /// RGBA color in the Linear sRGB colorspace (often colloquially referred to as "linear", "RGB", or
/// "linear RGB").
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
#[reflect(PartialEq, Serialize, Deserialize)] #[reflect(PartialEq, Serialize, Deserialize)]

View File

@ -3,7 +3,7 @@ pub trait SrgbColorSpace {
fn nonlinear_to_linear_srgb(self) -> Self; fn nonlinear_to_linear_srgb(self) -> Self;
} }
//source: https://entropymine.com/imageworsener/srgbformula/ // source: https://entropymine.com/imageworsener/srgbformula/
impl SrgbColorSpace for f32 { impl SrgbColorSpace for f32 {
fn linear_to_nonlinear_srgb(self) -> f32 { fn linear_to_nonlinear_srgb(self) -> f32 {
if self <= 0.0 { if self <= 0.0 {
@ -13,7 +13,7 @@ impl SrgbColorSpace for f32 {
if self <= 0.0031308 { if self <= 0.0031308 {
self * 12.92 // linear falloff in dark values self * 12.92 // linear falloff in dark values
} else { } else {
(1.055 * self.powf(1.0 / 2.4)) - 0.055 //gamma curve in other area (1.055 * self.powf(1.0 / 2.4)) - 0.055 // gamma curve in other area
} }
} }
@ -24,7 +24,7 @@ impl SrgbColorSpace for f32 {
if self <= 0.04045 { if self <= 0.04045 {
self / 12.92 // linear falloff in dark values self / 12.92 // linear falloff in dark values
} else { } else {
((self + 0.055) / 1.055).powf(2.4) //gamma curve in other area ((self + 0.055) / 1.055).powf(2.4) // gamma curve in other area
} }
} }
} }

View File

@ -277,7 +277,8 @@ impl<'a> DrawContext<'a> {
} }
} }
// if none of the given RenderResourceBindings have the current bind group, try their assets // if none of the given RenderResourceBindings have the current bind group, try their
// assets
let asset_render_resource_bindings = let asset_render_resource_bindings =
if let Some(value) = asset_render_resource_bindings.as_mut() { if let Some(value) = asset_render_resource_bindings.as_mut() {
value value

View File

@ -68,7 +68,8 @@ pub enum RenderSystem {
pub enum RenderStage { pub enum RenderStage {
/// Stage where render resources are set up /// Stage where render resources are set up
RenderResource, RenderResource,
/// Stage where Render Graph systems are run. In general you shouldn't add systems to this stage manually. /// Stage where Render Graph systems are run. In general you shouldn't add systems to this
/// stage manually.
RenderGraphSystems, RenderGraphSystems,
// Stage where draw systems are executed. This is generally where Draw components are setup // Stage where draw systems are executed. This is generally where Draw components are setup
Draw, Draw,
@ -78,7 +79,8 @@ pub enum RenderStage {
/// Adds core render types and systems to an App /// Adds core render types and systems to an App
pub struct RenderPlugin { pub struct RenderPlugin {
/// configures the "base render graph". If this is not `None`, the "base render graph" will be added /// configures the "base render graph". If this is not `None`, the "base render graph" will be
/// added
pub base_render_graph_config: Option<BaseRenderGraphConfig>, pub base_render_graph_config: Option<BaseRenderGraphConfig>,
} }

View File

@ -208,7 +208,8 @@ impl From<&Indices> for IndexFormat {
#[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"] #[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"]
pub struct Mesh { pub struct Mesh {
primitive_topology: PrimitiveTopology, primitive_topology: PrimitiveTopology,
/// `bevy_utils::HashMap` with all defined vertex attributes (Positions, Normals, ...) for this mesh. Attribute name maps to attribute values. /// `bevy_utils::HashMap` with all defined vertex attributes (Positions, Normals, ...) for this
/// mesh. Attribute name maps to attribute values.
attributes: HashMap<Cow<'static, str>, VertexAttributeValues>, attributes: HashMap<Cow<'static, str>, VertexAttributeValues>,
indices: Option<Indices>, indices: Option<Indices>,
} }
@ -233,7 +234,8 @@ pub struct Mesh {
impl Mesh { impl Mesh {
/// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`] /// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`]
pub const ATTRIBUTE_COLOR: &'static str = "Vertex_Color"; pub const ATTRIBUTE_COLOR: &'static str = "Vertex_Color";
/// The direction the vertex normal is facing in. Use in conjunction with [`Mesh::set_attribute`] /// The direction the vertex normal is facing in. Use in conjunction with
/// [`Mesh::set_attribute`]
pub const ATTRIBUTE_NORMAL: &'static str = "Vertex_Normal"; pub const ATTRIBUTE_NORMAL: &'static str = "Vertex_Normal";
/// Where the vertex is located in space. Use in conjunction with [`Mesh::set_attribute`] /// Where the vertex is located in space. Use in conjunction with [`Mesh::set_attribute`]
pub const ATTRIBUTE_POSITION: &'static str = "Vertex_Position"; pub const ATTRIBUTE_POSITION: &'static str = "Vertex_Position";
@ -279,7 +281,8 @@ impl Mesh {
} }
/// Indices describe how triangles are constructed out of the vertex attributes. /// Indices describe how triangles are constructed out of the vertex attributes.
/// They are only useful for the [`crate::pipeline::PrimitiveTopology`] variants that use triangles /// They are only useful for the [`crate::pipeline::PrimitiveTopology`] variants that use
/// triangles
pub fn set_indices(&mut self, indices: Option<Indices>) { pub fn set_indices(&mut self, indices: Option<Indices>) {
self.indices = indices; self.indices = indices;
} }

View File

@ -39,7 +39,8 @@ pub enum CapsuleUvProfile {
Aspect, Aspect,
/// Hemispheres get UV space according to the ratio of latitudes to rings. /// Hemispheres get UV space according to the ratio of latitudes to rings.
Uniform, Uniform,
/// Upper third of the texture goes to the northern hemisphere, middle third to the cylinder and lower third to the southern one. /// Upper third of the texture goes to the northern hemisphere, middle third to the cylinder
/// and lower third to the southern one.
Fixed, Fixed,
} }

View File

@ -30,8 +30,8 @@ pub enum BindType {
readonly: bool, readonly: bool,
}, },
Sampler { Sampler {
/// The sampling result is produced based on more than a single color sample from a texture, /// The sampling result is produced based on more than a single color sample from a
/// e.g. when bilinear interpolation is enabled. /// texture, e.g. when bilinear interpolation is enabled.
/// ///
/// A filtering sampler can only be used with a filterable texture. /// A filtering sampler can only be used with a filterable texture.
filtering: bool, filtering: bool,

View File

@ -197,7 +197,8 @@ impl PipelineCompiler {
} }
specialized_descriptor.layout = Some(layout); specialized_descriptor.layout = Some(layout);
// create a vertex layout that provides all attributes from either the specialized vertex buffers or a zero buffer // create a vertex layout that provides all attributes from either the specialized vertex
// buffers or a zero buffer
let mut pipeline_layout = specialized_descriptor.layout.as_mut().unwrap(); let mut pipeline_layout = specialized_descriptor.layout.as_mut().unwrap();
// the vertex buffer descriptor of the mesh // the vertex buffer descriptor of the mesh
let mesh_vertex_buffer_layout = &pipeline_specialization.vertex_buffer_layout; let mesh_vertex_buffer_layout = &pipeline_specialization.vertex_buffer_layout;
@ -235,7 +236,7 @@ impl PipelineCompiler {
} }
} }
//TODO: add other buffers (like instancing) here // TODO: add other buffers (like instancing) here
let mut vertex_buffer_descriptors = Vec::<VertexBufferLayout>::default(); let mut vertex_buffer_descriptors = Vec::<VertexBufferLayout>::default();
if !pipeline_layout.vertex_buffer_descriptors.is_empty() { if !pipeline_layout.vertex_buffer_descriptors.is_empty() {
vertex_buffer_descriptors.push(compiled_vertex_buffer_descriptor); vertex_buffer_descriptors.push(compiled_vertex_buffer_descriptor);

View File

@ -58,8 +58,8 @@ impl PipelineLayout {
.map(|(_, value)| value) .map(|(_, value)| value)
.collect::<Vec<BindGroupDescriptor>>(); .collect::<Vec<BindGroupDescriptor>>();
// NOTE: for some reason bind groups need to be sorted by index. this is likely an issue with bevy and not with wgpu // NOTE: for some reason bind groups need to be sorted by index. this is likely an issue
// TODO: try removing this // with bevy and not with wgpu TODO: try removing this
bind_groups_result.sort_by(|a, b| a.index.partial_cmp(&b.index).unwrap()); bind_groups_result.sort_by(|a, b| a.index.partial_cmp(&b.index).unwrap());
PipelineLayout { PipelineLayout {

View File

@ -17,7 +17,8 @@ use bevy_utils::HashSet;
pub struct RenderPipeline { pub struct RenderPipeline {
pub pipeline: Handle<PipelineDescriptor>, pub pipeline: Handle<PipelineDescriptor>,
pub specialization: PipelineSpecialization, pub specialization: PipelineSpecialization,
/// used to track if PipelineSpecialization::dynamic_bindings is in sync with RenderResourceBindings /// used to track if PipelineSpecialization::dynamic_bindings is in sync with
/// RenderResourceBindings
pub dynamic_bindings_generation: usize, pub dynamic_bindings_generation: usize,
} }

View File

@ -92,9 +92,9 @@ impl Default for BaseRenderGraphConfig {
} }
} }
/// The "base render graph" provides a core set of render graph nodes which can be used to build any graph. /// The "base render graph" provides a core set of render graph nodes which can be used to build any
/// By itself this graph doesn't do much, but it allows Render plugins to interop with each other by having a common /// graph. By itself this graph doesn't do much, but it allows Render plugins to interop with each
/// set of nodes. It can be customized using `BaseRenderGraphConfig`. /// other by having a common set of nodes. It can be customized using `BaseRenderGraphConfig`.
pub(crate) fn add_base_graph(config: &BaseRenderGraphConfig, world: &mut World) { pub(crate) fn add_base_graph(config: &BaseRenderGraphConfig, world: &mut World) {
let world = world.cell(); let world = world.cell();
let mut graph = world.get_resource_mut::<RenderGraph>().unwrap(); let mut graph = world.get_resource_mut::<RenderGraph>().unwrap();
@ -124,7 +124,8 @@ pub(crate) fn add_base_graph(config: &BaseRenderGraphConfig, world: &mut World)
mip_level_count: 1, mip_level_count: 1,
sample_count: msaa.samples, sample_count: msaa.samples,
dimension: TextureDimension::D2, dimension: TextureDimension::D2,
format: TextureFormat::Depth32Float, // PERF: vulkan docs recommend using 24 bit depth for better performance format: TextureFormat::Depth32Float, /* PERF: vulkan docs recommend using 24
* bit depth for better performance */
usage: TextureUsage::OUTPUT_ATTACHMENT, usage: TextureUsage::OUTPUT_ATTACHMENT,
}, },
), ),

View File

@ -47,7 +47,8 @@ pub enum Command {
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct CommandQueue { pub struct CommandQueue {
// TODO: this shouldn't really need a mutex. it just needs to be shared on whatever thread it's scheduled on // TODO: this shouldn't really need a mutex. it just needs to be shared on whatever thread it's
// scheduled on
queue: Arc<Mutex<Vec<Command>>>, queue: Arc<Mutex<Vec<Command>>>,
} }

View File

@ -28,10 +28,12 @@ pub trait Node: Downcast + Send + Sync + 'static {
&[] &[]
} }
/// Prepare the graph node with unique world access. This runs once per graph run before [Node::update] is called. /// Prepare the graph node with unique world access. This runs once per graph run before
/// [Node::update] is called.
fn prepare(&mut self, _world: &mut World) {} fn prepare(&mut self, _world: &mut World) {}
/// Run the graph node logic. This runs once per graph run after [Node::prepare] has been called on all nodes. /// Run the graph node logic. This runs once per graph run after [Node::prepare] has been called
/// on all nodes.
fn update( fn update(
&mut self, &mut self,
world: &World, world: &World,

View File

@ -70,8 +70,8 @@ pub fn camera_node_system(
mut state: Local<CameraNodeState>, mut state: Local<CameraNodeState>,
active_cameras: Res<ActiveCameras>, active_cameras: Res<ActiveCameras>,
render_resource_context: Res<Box<dyn RenderResourceContext>>, render_resource_context: Res<Box<dyn RenderResourceContext>>,
// PERF: this write on RenderResourceAssignments will prevent this system from running in parallel // PERF: this write on RenderResourceAssignments will prevent this system from running in
// with other systems that do the same // parallel with other systems that do the same
mut render_resource_bindings: ResMut<RenderResourceBindings>, mut render_resource_bindings: ResMut<RenderResourceBindings>,
query: Query<(&Camera, &GlobalTransform)>, query: Query<(&Camera, &GlobalTransform)>,
) { ) {

View File

@ -182,7 +182,8 @@ where
self.current_staging_buffer_offset = 0; self.current_staging_buffer_offset = 0;
} }
/// Find a spot for the given RenderResources in each uniform's BufferArray and prepare space in the staging buffer /// Find a spot for the given RenderResources in each uniform's BufferArray and prepare space in
/// the staging buffer
fn prepare_uniform_buffers(&mut self, id: I, render_resources: &T) { fn prepare_uniform_buffers(&mut self, id: I, render_resources: &T) {
for (i, render_resource) in render_resources.iter().enumerate() { for (i, render_resource) in render_resources.iter().enumerate() {
if let Some(RenderResourceType::Buffer) = render_resource.resource_type() { if let Some(RenderResourceType::Buffer) = render_resource.resource_type() {
@ -497,7 +498,8 @@ fn render_resources_node_system<T: RenderResources>(
staging_buffer, staging_buffer,
0..state.uniform_buffer_arrays.staging_buffer_size as u64, 0..state.uniform_buffer_arrays.staging_buffer_size as u64,
&mut |mut staging_buffer, _render_resource_context| { &mut |mut staging_buffer, _render_resource_context| {
// if the buffer array was resized, write all entities to the new buffer, otherwise only write changes // if the buffer array was resized, write all entities to the new buffer, otherwise
// only write changes
if resized { if resized {
for (entity, uniforms, visible, mut render_pipelines) in for (entity, uniforms, visible, mut render_pipelines) in
queries.q1_mut().iter_mut() queries.q1_mut().iter_mut()

View File

@ -67,7 +67,8 @@ impl Stages {
} }
pub fn borrow<'a>(&self, render_graph: &'a mut RenderGraph) -> Vec<StageBorrow<'a>> { pub fn borrow<'a>(&self, render_graph: &'a mut RenderGraph) -> Vec<StageBorrow<'a>> {
// unfortunately borrowing render graph nodes in a specific order takes a little bit of gymnastics // unfortunately borrowing render graph nodes in a specific order takes a little bit of
// gymnastics
let mut stage_borrows = Vec::with_capacity(self.stages.len()); let mut stage_borrows = Vec::with_capacity(self.stages.len());
let mut node_borrows = Vec::new(); let mut node_borrows = Vec::new();
@ -101,13 +102,15 @@ impl Stages {
} }
} }
/// Produces a collection of `Stages`, which are sets of OrderedJobs that must be run before moving on to the next stage /// Produces a collection of `Stages`, which are sets of OrderedJobs that must be run before moving
/// on to the next stage
pub trait RenderGraphStager { pub trait RenderGraphStager {
fn get_stages(&mut self, render_graph: &RenderGraph) -> Result<Stages, RenderGraphError>; fn get_stages(&mut self, render_graph: &RenderGraph) -> Result<Stages, RenderGraphError>;
} }
// TODO: remove this // TODO: remove this
/// This scheduler ignores dependencies and puts everything in one stage. It shouldn't be used for anything :) /// This scheduler ignores dependencies and puts everything in one stage. It shouldn't be used for
/// anything :)
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct LinearStager; pub struct LinearStager;
@ -229,8 +232,9 @@ fn stage_node(
}) })
.count(); .count();
// if the current node has more than one parent on the highest stage (aka requires synchronization), then move it to the next // if the current node has more than one parent on the highest stage (aka requires
// stage and start a new job on that stage // synchronization), then move it to the next stage and start a new job on that
// stage
if max_stage_parent_count > 1 { if max_stage_parent_count > 1 {
stage_index = max_parent_stage + 1; stage_index = max_parent_stage + 1;
} else { } else {
@ -492,7 +496,8 @@ mod tests {
assert_eq!(job.nodes.len(), 7, "expect exactly 7 nodes in the job"); assert_eq!(job.nodes.len(), 7, "expect exactly 7 nodes in the job");
// its hard to test the exact order of this job's nodes because of hashing, so instead we'll test the constraints that must hold true // its hard to test the exact order of this job's nodes because of hashing, so instead we'll
// test the constraints that must hold true
let index = let index =
|node_id: NodeId| -> usize { job.nodes.iter().position(|id| *id == node_id).unwrap() }; |node_id: NodeId| -> usize { job.nodes.iter().position(|id| *id == node_id).unwrap() };

View File

@ -61,13 +61,15 @@ pub enum BindGroupStatus {
NoMatch, NoMatch,
} }
// PERF: if the bindings are scoped to a specific pipeline layout, then names could be replaced with indices here for a perf boost // PERF: if the bindings are scoped to a specific pipeline layout, then names could be replaced with
// indices here for a perf boost
#[derive(Eq, PartialEq, Debug, Default, Clone)] #[derive(Eq, PartialEq, Debug, Default, Clone)]
pub struct RenderResourceBindings { pub struct RenderResourceBindings {
pub bindings: HashMap<String, RenderResourceBinding>, pub bindings: HashMap<String, RenderResourceBinding>,
/// A Buffer that contains all attributes a mesh has defined /// A Buffer that contains all attributes a mesh has defined
pub vertex_attribute_buffer: Option<BufferId>, pub vertex_attribute_buffer: Option<BufferId>,
/// A Buffer that is filled with zeros that will be used for attributes required by the shader, but undefined by the mesh. /// A Buffer that is filled with zeros that will be used for attributes required by the shader,
/// but undefined by the mesh.
pub vertex_fallback_buffer: Option<BufferId>, pub vertex_fallback_buffer: Option<BufferId>,
pub index_buffer: Option<(BufferId, IndexFormat)>, pub index_buffer: Option<(BufferId, IndexFormat)>,
assets: HashSet<(HandleUntyped, TypeId)>, assets: HashSet<(HandleUntyped, TypeId)>,
@ -87,7 +89,8 @@ impl RenderResourceBindings {
self.bindings.insert(name.to_string(), binding); self.bindings.insert(name.to_string(), binding);
} }
/// The current "generation" of dynamic bindings. This number increments every time a dynamic binding changes /// The current "generation" of dynamic bindings. This number increments every time a dynamic
/// binding changes
pub fn dynamic_bindings_generation(&self) -> usize { pub fn dynamic_bindings_generation(&self) -> usize {
self.dynamic_bindings_generation self.dynamic_bindings_generation
} }
@ -182,8 +185,9 @@ impl RenderResourceBindings {
Some(bind_group) Some(bind_group)
} }
BindGroupStatus::Unchanged(id) => { BindGroupStatus::Unchanged(id) => {
// PERF: this is only required because RenderResourceContext::remove_stale_bind_groups doesn't inform RenderResourceBindings // PERF: this is only required because
// when a stale bind group has been removed // RenderResourceContext::remove_stale_bind_groups doesn't inform
// RenderResourceBindings when a stale bind group has been removed
let bind_group = self let bind_group = self
.get_bind_group(id) .get_bind_group(id)
.expect("`RenderResourceSet` was just changed, so it should exist."); .expect("`RenderResourceSet` was just changed, so it should exist.");

View File

@ -77,13 +77,16 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static {
fn remove_stale_bind_groups(&self); fn remove_stale_bind_groups(&self);
/// Reflects the pipeline layout from its shaders. /// Reflects the pipeline layout from its shaders.
/// ///
/// If `bevy_conventions` is true, it will be assumed that the shader follows "bevy shader conventions". These allow /// If `bevy_conventions` is true, it will be assumed that the shader follows "bevy shader
/// richer reflection, such as inferred Vertex Buffer names and inferred instancing. /// conventions". These allow richer reflection, such as inferred Vertex Buffer names and
/// inferred instancing.
/// ///
/// If `dynamic_bindings` has values, shader uniforms will be set to "dynamic" if there is a matching binding in the list /// If `dynamic_bindings` has values, shader uniforms will be set to "dynamic" if there is a
/// matching binding in the list
/// ///
/// If `vertex_buffer_descriptors` is set, the pipeline's vertex buffers /// If `vertex_buffer_descriptors` is set, the pipeline's vertex buffers
/// will inherit their layouts from global descriptors, otherwise the layout will be assumed to be complete / local. /// will inherit their layouts from global descriptors, otherwise the layout will be assumed to
/// be complete / local.
fn reflect_pipeline_layout( fn reflect_pipeline_layout(
&self, &self,
shaders: &Assets<Shader>, shaders: &Assets<Shader>,

View File

@ -4,7 +4,8 @@ use crate::{pipeline::RenderPipelines, Texture};
pub use bevy_derive::ShaderDefs; pub use bevy_derive::ShaderDefs;
use bevy_ecs::system::{Query, Res}; use bevy_ecs::system::{Query, Res};
/// Something that can either be "defined" or "not defined". This is used to determine if a "shader def" should be considered "defined" /// Something that can either be "defined" or "not defined". This is used to determine if a "shader
/// def" should be considered "defined"
pub trait ShaderDef { pub trait ShaderDef {
fn is_defined(&self) -> bool; fn is_defined(&self) -> bool;
} }

View File

@ -101,7 +101,8 @@ impl Texture {
.resize(size.volume() * self.format.pixel_size(), 0); .resize(size.volume() * self.format.pixel_size(), 0);
} }
/// Changes the `size`, asserting that the total number of data elements (pixels) remains the same. /// Changes the `size`, asserting that the total number of data elements (pixels) remains the
/// same.
pub fn reinterpret_size(&mut self, new_size: Extent3d) { pub fn reinterpret_size(&mut self, new_size: Extent3d) {
assert!( assert!(
new_size.volume() == self.size.volume(), new_size.volume() == self.size.volume(),
@ -113,9 +114,9 @@ impl Texture {
self.size = new_size; self.size = new_size;
} }
/// Takes a 2D texture containing vertically stacked images of the same size, and reinterprets it as a 2D array texture, /// Takes a 2D texture containing vertically stacked images of the same size, and reinterprets
/// where each of the stacked images becomes one layer of the array. This is primarily for use with the `texture2DArray` /// it as a 2D array texture, where each of the stacked images becomes one layer of the
/// shader uniform type. /// array. This is primarily for use with the `texture2DArray` shader uniform type.
pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) { pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) {
// Must be a stacked image, and the height must be divisible by layers. // Must be a stacked image, and the height must be divisible by layers.
assert!(self.dimension == TextureDimension::D2); assert!(self.dimension == TextureDimension::D2);
@ -171,8 +172,9 @@ impl Texture {
} }
AssetEvent::Removed { handle } => { AssetEvent::Removed { handle } => {
Self::remove_current_texture_resources(render_resource_context, handle); Self::remove_current_texture_resources(render_resource_context, handle);
// if texture was modified and removed in the same update, ignore the modification // if texture was modified and removed in the same update, ignore the
// events are ordered so future modification events are ok // modification events are ordered so future modification
// events are ok
changed_textures.remove(handle); changed_textures.remove(handle);
} }
} }
@ -217,7 +219,8 @@ impl Texture {
} }
} }
/// Load a bytes buffer in a [`Texture`], according to type `image_type`, using the `image` crate` /// Load a bytes buffer in a [`Texture`], according to type `image_type`, using the `image`
/// crate`
pub fn from_buffer(buffer: &[u8], image_type: ImageType) -> Result<Texture, TextureError> { pub fn from_buffer(buffer: &[u8], image_type: ImageType) -> Result<Texture, TextureError> {
let format = match image_type { let format = match image_type {
ImageType::MimeType(mime_type) => match mime_type { ImageType::MimeType(mime_type) => match mime_type {

View File

@ -95,7 +95,8 @@ pub struct PixelInfo {
/// Underlying texture data format. /// Underlying texture data format.
/// ///
/// If there is a conversion in the format (such as srgb -> linear), The conversion listed is for /// If there is a conversion in the format (such as srgb -> linear), The conversion listed is for
/// loading from texture in a shader. When writing to the texture, the opposite conversion takes place. /// loading from texture in a shader. When writing to the texture, the opposite conversion takes
/// place.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum TextureFormat { pub enum TextureFormat {
// Normal 8 bit formats // Normal 8 bit formats

View File

@ -81,7 +81,8 @@ impl SceneSpawner {
for instance_id in instance_ids { for instance_id in instance_ids {
if let Some(instance) = self.spawned_instances.get(&instance_id) { if let Some(instance) = self.spawned_instances.get(&instance_id) {
for entity in instance.entity_map.values() { for entity in instance.entity_map.values() {
let _ = world.despawn(entity); // Ignore the result, despawn only cares if it exists. let _ = world.despawn(entity); // Ignore the result, despawn only cares if
// it exists.
} }
} }
} }

View File

@ -51,8 +51,8 @@ impl DynamicTextureAtlasBuilder {
// let change_list = self.atlas_allocator.resize_and_rearrange(new_size2); // let change_list = self.atlas_allocator.resize_and_rearrange(new_size2);
// for change in change_list.changes { // for change in change_list.changes {
// if let Some(changed_texture_handle) = self.allocation_textures.remove(&change.old.id) { // if let Some(changed_texture_handle) = self.allocation_textures.remove(&change.old.id)
// let changed_texture = textures.get(&changed_texture_handle).unwrap(); // { let changed_texture = textures.get(&changed_texture_handle).unwrap();
// self.place_texture(change.new, changed_texture_handle, changed_texture); // self.place_texture(change.new, changed_texture_handle, changed_texture);
// } // }
// } // }

View File

@ -68,7 +68,8 @@ impl Plugin for SpritePlugin {
color_materials.set_untracked(Handle::<ColorMaterial>::default(), ColorMaterial::default()); color_materials.set_untracked(Handle::<ColorMaterial>::default(), ColorMaterial::default());
meshes.set_untracked( meshes.set_untracked(
QUAD_HANDLE, QUAD_HANDLE,
// Use a flipped quad because the camera is facing "forward" but quads should face backward // Use a flipped quad because the camera is facing "forward" but quads should face
// backward
Mesh::from(shape::Quad::new(Vec2::new(1.0, 1.0))), Mesh::from(shape::Quad::new(Vec2::new(1.0, 1.0))),
) )
} }

View File

@ -1,7 +1,8 @@
use bevy_core::Byteable; use bevy_core::Byteable;
use bevy_math::Vec2; use bevy_math::Vec2;
/// A rectangle defined by two points. There is no defined origin, so 0,0 could be anywhere (top-left, bottom-left, etc) /// A rectangle defined by two points. There is no defined origin, so 0,0 could be anywhere
/// (top-left, bottom-left, etc)
#[repr(C)] #[repr(C)]
#[derive(Default, Clone, Copy, Debug)] #[derive(Default, Clone, Copy, Debug)]
pub struct Rect { pub struct Rect {

View File

@ -86,7 +86,8 @@ pub fn sprite_system(
if let Some(ref texture_handle) = material.texture { if let Some(ref texture_handle) = material.texture {
if let Some(texture) = textures.get(texture_handle) { if let Some(texture) = textures.get(texture_handle) {
let texture_size = texture.size.as_vec3().truncate(); let texture_size = texture.size.as_vec3().truncate();
// only set sprite size if it has changed (this check prevents change detection from triggering) // only set sprite size if it has changed (this check prevents change
// detection from triggering)
if sprite.size != texture_size { if sprite.size != texture_size {
sprite.size = texture_size; sprite.size = texture_size;
} }

View File

@ -114,7 +114,8 @@ pub fn event_resets_if_listeners_are_cleared() {
event.notify(std::usize::MAX); event.notify(std::usize::MAX);
futures_lite::future::block_on(listener1); futures_lite::future::block_on(listener1);
// If all listeners are notified, the structure should now be cleared. We're free to listen again // If all listeners are notified, the structure should now be cleared. We're free to listen
// again
let listener2 = event.listen(); let listener2 = event.listen();
let listener3 = event.listen(); let listener3 = event.listen();

View File

@ -381,7 +381,8 @@ where
.max_by_key(f) .max_by_key(f)
} }
/// Returns the item that gives the maximum value with respect to the specified comparison function. /// Returns the item that gives the maximum value with respect to the specified comparison
/// function.
/// ///
/// See [`Iterator::max_by()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.max_by) /// See [`Iterator::max_by()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.max_by)
fn max_by<F>(mut self, pool: &TaskPool, f: F) -> Option<Self::Item> fn max_by<F>(mut self, pool: &TaskPool, f: F) -> Option<Self::Item>
@ -420,7 +421,8 @@ where
.min_by_key(f) .min_by_key(f)
} }
/// Returns the item that gives the minimum value with respect to the specified comparison function. /// Returns the item that gives the minimum value with respect to the specified comparison
/// function.
/// ///
/// See [`Iterator::min_by()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.min_by) /// See [`Iterator::min_by()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.min_by)
fn min_by<F>(mut self, pool: &TaskPool, f: F) -> Option<Self::Item> fn min_by<F>(mut self, pool: &TaskPool, f: F) -> Option<Self::Item>

View File

@ -22,7 +22,8 @@ impl<T> Task<T> {
Self(task) Self(task)
} }
/// Detaches the task to let it keep running in the background. See `async_executor::Task::detach` /// Detaches the task to let it keep running in the background. See
/// `async_executor::Task::detach`
pub fn detach(self) { pub fn detach(self) {
self.0.detach(); self.0.detach();
} }

View File

@ -86,8 +86,8 @@ pub struct TaskPool {
/// The executor for the pool /// The executor for the pool
/// ///
/// This has to be separate from TaskPoolInner because we have to create an Arc<Executor> to /// This has to be separate from TaskPoolInner because we have to create an Arc<Executor> to
/// pass into the worker threads, and we must create the worker threads before we can create the /// pass into the worker threads, and we must create the worker threads before we can create
/// Vec<Task<T>> contained within TaskPoolInner /// the Vec<Task<T>> contained within TaskPoolInner
executor: Arc<async_executor::Executor<'static>>, executor: Arc<async_executor::Executor<'static>>,
/// Inner state of the pool /// Inner state of the pool
@ -200,17 +200,18 @@ impl TaskPool {
// Pin the futures on the stack. // Pin the futures on the stack.
pin!(fut); pin!(fut);
// SAFETY: This function blocks until all futures complete, so we do not read/write the // SAFETY: This function blocks until all futures complete, so we do not read/write
// data from futures outside of the 'scope lifetime. However, rust has no way of knowing // the data from futures outside of the 'scope lifetime. However,
// this so we must convert to 'static here to appease the compiler as it is unable to // rust has no way of knowing this so we must convert to 'static
// validate safety. // here to appease the compiler as it is unable to validate safety.
let fut: Pin<&mut (dyn Future<Output = Vec<T>>)> = fut; let fut: Pin<&mut (dyn Future<Output = Vec<T>>)> = fut;
let fut: Pin<&'static mut (dyn Future<Output = Vec<T>> + 'static)> = let fut: Pin<&'static mut (dyn Future<Output = Vec<T>> + 'static)> =
unsafe { mem::transmute(fut) }; unsafe { mem::transmute(fut) };
// The thread that calls scope() will participate in driving tasks in the pool forward // The thread that calls scope() will participate in driving tasks in the pool
// until the tasks that are spawned by this scope() call complete. (If the caller of scope() // forward until the tasks that are spawned by this scope() call
// happens to be a thread in this thread pool, and we only have one thread in the pool, then // complete. (If the caller of scope() happens to be a thread in
// this thread pool, and we only have one thread in the pool, then
// simply calling future::block_on(spawned) would deadlock.) // simply calling future::block_on(spawned) would deadlock.)
let mut spawned = local_executor.spawn(fut); let mut spawned = local_executor.spawn(fut);
loop { loop {
@ -376,7 +377,8 @@ mod tests {
scope.spawn_local(async move { scope.spawn_local(async move {
inner_count_clone.fetch_add(1, Ordering::Release); inner_count_clone.fetch_add(1, Ordering::Release);
if std::thread::current().id() != spawner { if std::thread::current().id() != spawner {
// NOTE: This check is using an atomic rather than simply panicing the thread to avoid deadlocking the barrier on failure // NOTE: This check is using an atomic rather than simply panicing the
// thread to avoid deadlocking the barrier on failure
inner_thread_check_failed.store(true, Ordering::Release); inner_thread_check_failed.store(true, Ordering::Release);
} }
}); });

View File

@ -171,7 +171,8 @@ pub fn text2d_system(
&mut *textures, &mut *textures,
) { ) {
Err(TextError::NoSuchFont) => { Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, let's add this entity to the queue for further processing // There was an error processing the text layout, let's add this entity to the
// queue for further processing
new_queue.push(entity); new_queue.push(entity);
} }
Err(e @ TextError::FailedToAddGlyph(_)) => { Err(e @ TextError::FailedToAddGlyph(_)) => {

View File

@ -63,7 +63,8 @@ impl GlobalTransform {
} }
} }
/// Returns transform with the same translation and scale, but rotation so that transform.forward() points at target /// Returns transform with the same translation and scale, but rotation so that
/// transform.forward() points at target
#[inline] #[inline]
pub fn looking_at(mut self, target: Vec3, up: Vec3) -> Self { pub fn looking_at(mut self, target: Vec3, up: Vec3) -> Self {
self.look_at(target, up); self.look_at(target, up);

View File

@ -12,8 +12,8 @@ pub struct Parent(pub Entity);
// TODO: We need to impl either FromWorld or Default so Parent can be registered as Properties. // TODO: We need to impl either FromWorld or Default so Parent can be registered as Properties.
// This is because Properties deserialize by creating an instance and apply a patch on top. // This is because Properties deserialize by creating an instance and apply a patch on top.
// However Parent should only ever be set with a real user-defined entity. Its worth looking into better // However Parent should only ever be set with a real user-defined entity. Its worth looking into
// ways to handle cases like this. // better ways to handle cases like this.
impl FromWorld for Parent { impl FromWorld for Parent {
fn from_world(_world: &mut World) -> Self { fn from_world(_world: &mut World) -> Self {
Parent(Entity::new(u32::MAX)) Parent(Entity::new(u32::MAX))

View File

@ -63,7 +63,8 @@ impl Transform {
} }
} }
/// Returns transform with the same translation and scale, but rotation so that transform.forward() points at target /// Returns transform with the same translation and scale, but rotation so that
/// transform.forward() points at target
#[inline] #[inline]
pub fn looking_at(mut self, target: Vec3, up: Vec3) -> Self { pub fn looking_at(mut self, target: Vec3, up: Vec3) -> Self {
self.look_at(target, up); self.look_at(target, up);

View File

@ -29,7 +29,8 @@ impl Command for InsertChildren {
added = true; added = true;
} }
// NOTE: ideally this is just an else statement, but currently that _incorrectly_ fails borrow-checking // NOTE: ideally this is just an else statement, but currently that _incorrectly_ fails
// borrow-checking
if !added { if !added {
world world
.entity_mut(self.parent) .entity_mut(self.parent)
@ -64,7 +65,8 @@ impl Command for PushChildren {
added = true; added = true;
} }
// NOTE: ideally this is just an else statement, but currently that _incorrectly_ fails borrow-checking // NOTE: ideally this is just an else statement, but currently that _incorrectly_ fails
// borrow-checking
if !added { if !added {
world world
.entity_mut(self.parent) .entity_mut(self.parent)
@ -270,8 +272,8 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
let mut builder = WorldChildBuilder { let mut builder = WorldChildBuilder {
current_entity: None, current_entity: None,
parent_entities: vec![entity], parent_entities: vec![entity],
// SAFE: self.update_location() is called below. It is impossible to make EntityMut function calls on `self` // SAFE: self.update_location() is called below. It is impossible to make EntityMut
// within the scope defined here // function calls on `self` within the scope defined here
world: unsafe { self.world_mut() }, world: unsafe { self.world_mut() },
}; };

View File

@ -106,7 +106,8 @@ mod tests {
{ {
let mut commands = Commands::new(&mut queue, &world); let mut commands = Commands::new(&mut queue, &world);
commands.despawn_recursive(parent_entity); commands.despawn_recursive(parent_entity);
commands.despawn_recursive(parent_entity); // despawning the same entity twice should not panic commands.despawn_recursive(parent_entity); // despawning the same entity twice should
// not panic
} }
queue.apply(&mut world); queue.apply(&mut world);

View File

@ -10,8 +10,8 @@ use smallvec::SmallVec;
pub fn parent_update_system( pub fn parent_update_system(
mut commands: Commands, mut commands: Commands,
removed_parent_query: Query<(Entity, &PreviousParent), Without<Parent>>, removed_parent_query: Query<(Entity, &PreviousParent), Without<Parent>>,
// The next query could be run with a Changed<Parent> filter. However, this would mean that modifications later in the frame are lost. // The next query could be run with a Changed<Parent> filter. However, this would mean that
// See issue 891: https://github.com/bevyengine/bevy/issues/891 // modifications later in the frame are lost. See issue 891: https://github.com/bevyengine/bevy/issues/891
mut parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>)>, mut parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>)>,
mut children_query: Query<&mut Children>, mut children_query: Query<&mut Children>,
) { ) {

View File

@ -93,7 +93,8 @@ pub fn ui_focus_system(
let extents = node.size / 2.0; let extents = node.size / 2.0;
let min = ui_position - extents; let min = ui_position - extents;
let max = ui_position + extents; let max = ui_position + extents;
// if the current cursor position is within the bounds of the node, consider it for clicking // if the current cursor position is within the bounds of the node, consider it for
// clicking
if (min.x..max.x).contains(&cursor_position.x) if (min.x..max.x).contains(&cursor_position.x)
&& (min.y..max.y).contains(&cursor_position.y) && (min.y..max.y).contains(&cursor_position.y)
{ {
@ -120,7 +121,8 @@ pub fn ui_focus_system(
// only consider nodes with Interaction "clickable" // only consider nodes with Interaction "clickable"
if *interaction != Interaction::Clicked { if *interaction != Interaction::Clicked {
*interaction = Interaction::Clicked; *interaction = Interaction::Clicked;
// if the mouse was simultaneously released, reset this Interaction in the next frame // if the mouse was simultaneously released, reset this Interaction in the next
// frame
if mouse_released { if mouse_released {
state.entities_to_reset.push(entity); state.entities_to_reset.push(entity);
} }

View File

@ -105,7 +105,8 @@ pub fn text_system(
&mut *textures, &mut *textures,
) { ) {
Err(TextError::NoSuchFont) => { Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, let's add this entity to the queue for further processing // There was an error processing the text layout, let's add this entity to the
// queue for further processing
new_queue.push(entity); new_queue.push(entity);
} }
Err(e @ TextError::FailedToAddGlyph(_)) => { Err(e @ TextError::FailedToAddGlyph(_)) => {

View File

@ -66,8 +66,9 @@ impl WgpuRenderContext {
} }
} }
/// Consume this context, finalize the current CommandEncoder (if it exists), and take the current WgpuResources. /// Consume this context, finalize the current CommandEncoder (if it exists), and take the
/// This is intended to be called from a worker thread right before synchronizing with the main thread. /// current WgpuResources. This is intended to be called from a worker thread right before
/// synchronizing with the main thread.
pub fn finish(&mut self) -> Option<wgpu::CommandBuffer> { pub fn finish(&mut self) -> Option<wgpu::CommandBuffer> {
self.command_encoder.take().map(|encoder| encoder.finish()) self.command_encoder.take().map(|encoder| encoder.finish())
} }

View File

@ -17,27 +17,32 @@ pub struct WgpuBindGroupInfo {
} }
/// Grabs a read lock on all wgpu resources. When paired with WgpuResourceRefs, this allows /// Grabs a read lock on all wgpu resources. When paired with WgpuResourceRefs, this allows
/// you to pass in wgpu resources to wgpu::RenderPass<'a> with the appropriate lifetime. This is accomplished by /// you to pass in wgpu resources to wgpu::RenderPass<'a> with the appropriate lifetime. This is
/// grabbing a WgpuResourcesReadLock _before_ creating a wgpu::RenderPass, getting a WgpuResourcesRefs, and storing that /// accomplished by grabbing a WgpuResourcesReadLock _before_ creating a wgpu::RenderPass, getting a
/// in the pass. /// WgpuResourcesRefs, and storing that in the pass.
/// ///
/// This is only a problem because RwLockReadGuard.read() erases the guard's lifetime and creates a new anonymous lifetime. If /// This is only a problem because RwLockReadGuard.read() erases the guard's lifetime and creates a
/// you call RwLockReadGuard.read() during a pass, the reference will have an anonymous lifetime that lives for less than the /// new anonymous lifetime. If you call RwLockReadGuard.read() during a pass, the reference will
/// pass, which violates the lifetime constraints in place. /// have an anonymous lifetime that lives for less than the pass, which violates the lifetime
/// constraints in place.
/// ///
/// The biggest implication of this design (other than the additional boilerplate here) is that beginning a render pass /// The biggest implication of this design (other than the additional boilerplate here) is that
/// blocks writes to these resources. This means that if the pass attempts to write any resource, a deadlock will occur. WgpuResourceRefs /// beginning a render pass blocks writes to these resources. This means that if the pass attempts
/// only has immutable references, so the only way to make a deadlock happen is to access WgpuResources directly in the pass. It also means /// to write any resource, a deadlock will occur. WgpuResourceRefs only has immutable references, so
/// that other threads attempting to write resources will need to wait for pass encoding to finish. Almost all writes should occur before /// the only way to make a deadlock happen is to access WgpuResources directly in the pass. It also
/// passes start, so this hopefully won't be a problem. /// means that other threads attempting to write resources will need to wait for pass encoding to
/// finish. Almost all writes should occur before passes start, so this hopefully won't be a
/// problem.
/// ///
/// It is worth comparing the performance of this to transactional / copy-based approaches. This lock based design guarantees /// It is worth comparing the performance of this to transactional / copy-based approaches. This
/// consistency, doesn't perform redundant allocations, and only blocks when a write is occurring. A copy based approach would /// lock based design guarantees consistency, doesn't perform redundant allocations, and only blocks
/// never block, but would require more allocations / state-synchronization, which I expect will be more expensive. It would also be /// when a write is occurring. A copy based approach would never block, but would require more
/// allocations / state-synchronization, which I expect will be more expensive. It would also be
/// "eventually consistent" instead of "strongly consistent". /// "eventually consistent" instead of "strongly consistent".
/// ///
/// Single threaded implementations don't need to worry about these lifetimes constraints at all. RenderPasses can use a RenderContext's /// Single threaded implementations don't need to worry about these lifetimes constraints at all.
/// WgpuResources directly. RenderContext already has a lifetime greater than the RenderPass. /// RenderPasses can use a RenderContext's WgpuResources directly. RenderContext already has a
/// lifetime greater than the RenderPass.
#[derive(Debug)] #[derive(Debug)]
pub struct WgpuResourcesReadLock<'a> { pub struct WgpuResourcesReadLock<'a> {
pub buffers: RwLockReadGuard<'a, HashMap<BufferId, Arc<wgpu::Buffer>>>, pub buffers: RwLockReadGuard<'a, HashMap<BufferId, Arc<wgpu::Buffer>>>,
@ -62,7 +67,8 @@ impl<'a> WgpuResourcesReadLock<'a> {
} }
} }
/// Stores read only references to WgpuResource collections. See WgpuResourcesReadLock docs for context on why this exists /// Stores read only references to WgpuResource collections. See WgpuResourcesReadLock docs for
/// context on why this exists
#[derive(Debug)] #[derive(Debug)]
pub struct WgpuResourceRefs<'a> { pub struct WgpuResourceRefs<'a> {
pub buffers: &'a HashMap<BufferId, Arc<wgpu::Buffer>>, pub buffers: &'a HashMap<BufferId, Arc<wgpu::Buffer>>,

View File

@ -30,8 +30,8 @@ pub struct WindowCreated {
pub id: WindowId, pub id: WindowId,
} }
/// An event that is sent whenever a close was requested for a window. For example: when the "close" button /// An event that is sent whenever a close was requested for a window. For example: when the "close"
/// is pressed on a window. /// button is pressed on a window.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct WindowCloseRequested { pub struct WindowCloseRequested {
pub id: WindowId, pub id: WindowId,

View File

@ -304,13 +304,15 @@ impl Window {
/// Modifies the position of the window in physical pixels. /// Modifies the position of the window in physical pixels.
/// ///
/// Note that the top-left hand corner of the desktop is not necessarily the same as the screen. If the user uses a desktop with multiple monitors, /// Note that the top-left hand corner of the desktop is not necessarily the same as the screen.
/// the top-left hand corner of the desktop is the top-left hand corner of the monitor at the top-left of the desktop. This automatically un-maximizes /// If the user uses a desktop with multiple monitors, the top-left hand corner of the
/// the window if it's maximized. /// desktop is the top-left hand corner of the monitor at the top-left of the desktop. This
/// automatically un-maximizes the window if it's maximized.
/// ///
/// # Platform-specific /// # Platform-specific
/// ///
/// - iOS: Can only be called on the main thread. Sets the top left coordinates of the window in the screen space coordinate system. /// - iOS: Can only be called on the main thread. Sets the top left coordinates of the window in
/// the screen space coordinate system.
/// - Web: Sets the top-left coordinates relative to the viewport. /// - Web: Sets the top-left coordinates relative to the viewport.
/// - Android / Wayland: Unsupported. /// - Android / Wayland: Unsupported.
#[inline] #[inline]

View File

@ -365,7 +365,8 @@ pub fn winit_runner_with(mut app: App, mut event_loop: EventLoop<()>) {
let winit_window = winit_windows.get_window(window_id).unwrap(); let winit_window = winit_windows.get_window(window_id).unwrap();
let mut location = touch.location.to_logical(winit_window.scale_factor()); let mut location = touch.location.to_logical(winit_window.scale_factor());
// On a mobile window, the start is from the top while on PC/Linux/OSX from bottom // On a mobile window, the start is from the top while on PC/Linux/OSX from
// bottom
if cfg!(target_os = "android") || cfg!(target_os = "ios") { if cfg!(target_os = "android") || cfg!(target_os = "ios") {
let window_height = windows.get_primary().unwrap().height(); let window_height = windows.get_primary().unwrap().height();
location.y = window_height - location.y; location.y = window_height - location.y;

View File

@ -1,6 +1,7 @@
use bevy::{asset::LoadState, prelude::*, sprite::TextureAtlasBuilder}; use bevy::{asset::LoadState, prelude::*, sprite::TextureAtlasBuilder};
/// In this example we generate a new texture atlas (sprite sheet) from a folder containing individual sprites /// In this example we generate a new texture atlas (sprite sheet) from a folder containing
/// individual sprites
fn main() { fn main() {
App::build() App::build()
.init_resource::<RpgSpriteHandles>() .init_resource::<RpgSpriteHandles>()

View File

@ -1,8 +1,9 @@
use bevy::prelude::*; use bevy::prelude::*;
/// This example shows how to configure Multi-Sample Anti-Aliasing. Setting the sample count higher will result in smoother edges, /// This example shows how to configure Multi-Sample Anti-Aliasing. Setting the sample count higher
/// but it will also increase the cost to render those edges. The range should generally be somewhere between 1 (no multi sampling, /// will result in smoother edges, but it will also increase the cost to render those edges. The
/// but cheap) to 8 (crisp but expensive) /// range should generally be somewhere between 1 (no multi sampling, but cheap) to 8 (crisp but
/// expensive)
fn main() { fn main() {
App::build() App::build()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })

View File

@ -1,7 +1,7 @@
use bevy::prelude::*; use bevy::prelude::*;
/// This example illustrates how to create parent->child relationships between entities how parent transforms /// This example illustrates how to create parent->child relationships between entities how parent
/// are propagated to their descendants /// transforms are propagated to their descendants
fn main() { fn main() {
App::build() App::build()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })

View File

@ -5,9 +5,10 @@ use bevy::{
use rand::{rngs::StdRng, Rng, SeedableRng}; use rand::{rngs::StdRng, Rng, SeedableRng};
/// This example spawns a large number of cubes, each with its own changing position and material /// This example spawns a large number of cubes, each with its own changing position and material
/// This is intended to be a stress test of bevy's ability to render many objects with different properties /// This is intended to be a stress test of bevy's ability to render many objects with different
/// For the best results, run it in release mode: ```cargo run --example spawner --release /// properties For the best results, run it in release mode: ```cargo run --example spawner
/// NOTE: Bevy still has a number of optimizations to do in this area. Expect the performance here to go way up in the future /// --release NOTE: Bevy still has a number of optimizations to do in this area. Expect the
/// performance here to go way up in the future
fn main() { fn main() {
App::build() App::build()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)

View File

@ -6,7 +6,8 @@ use bevy::{
}, },
}; };
/// This example visualizes camera z-ordering by setting the material of rotating cubes to their distance from the camera /// This example visualizes camera z-ordering by setting the material of rotating cubes to their
/// distance from the camera
fn main() { fn main() {
App::build() App::build()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)

Some files were not shown because too many files have changed in this diff Show More