format comments (#1612)
Uses the new unstable comment formatting features added to rustfmt.toml.
This commit is contained in:
parent
be1c317d4e
commit
b17f8a4bce
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {}
|
||||||
|
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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>>,
|
||||||
|
|||||||
@ -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 => {}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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 {}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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>>,
|
||||||
|
|||||||
@ -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),
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)]
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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>();
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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])
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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>()
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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>>,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -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>;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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>>,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)]
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -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>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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() };
|
||||||
|
|
||||||
|
|||||||
@ -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.");
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|||||||
@ -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))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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(_)) => {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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() },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(_)) => {
|
||||||
|
|||||||
@ -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())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>>,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>()
|
||||||
|
|||||||
@ -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 })
|
||||||
|
|||||||
@ -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 })
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
Loading…
Reference in New Issue
Block a user