Skip to content

Commit

Permalink
Merge branch 'main' into fix/rewrite-winit-loop
Browse files Browse the repository at this point in the history
  • Loading branch information
pietrosophya authored Apr 3, 2024
2 parents 7a70e51 + ba8d702 commit 29e51a0
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 52 deletions.
32 changes: 32 additions & 0 deletions crates/bevy_dev_tools/src/close_on_esc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use bevy_ecs::prelude::*;
use bevy_input::{keyboard::KeyCode, ButtonInput};
use bevy_window::Window;

/// Close the focused window whenever the escape key (<kbd>Esc</kbd>) is pressed
///
/// This is useful for examples or prototyping.
///
/// # Example
///
/// ```no_run
/// # use bevy_app::prelude::*;
/// # use bevy_dev_tools::close_on_esc;
/// #
/// App::new()
/// .add_systems(Update, close_on_esc);
/// ```
pub fn close_on_esc(
mut commands: Commands,
focused_windows: Query<(Entity, &Window)>,
input: Res<ButtonInput<KeyCode>>,
) {
for (window, focus) in focused_windows.iter() {
if !focus.focused {
continue;
}

if input.just_pressed(KeyCode::Escape) {
commands.entity(window).despawn();
}
}
}
5 changes: 5 additions & 0 deletions crates/bevy_dev_tools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ use bevy_app::prelude::*;

#[cfg(feature = "bevy_ci_testing")]
pub mod ci_testing;

pub mod fps_overlay;

#[cfg(feature = "bevy_ui_debug")]
pub mod ui_debug_overlay;

mod close_on_esc;

pub use crate::close_on_esc::close_on_esc;

/// Enables developer tools in an [`App`]. This plugin is added automatically with `bevy_dev_tools`
/// feature.
///
Expand Down
269 changes: 269 additions & 0 deletions crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2136,6 +2136,209 @@ impl World {
}
}

/// Iterates over all resources in the world.
///
/// The returned iterator provides lifetimed, but type-unsafe pointers. Actually reading the contents
/// of each resource will require the use of unsafe code.
///
/// # Examples
///
/// ## Printing the size of all resources
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Resource)]
/// # struct A(u32);
/// # #[derive(Resource)]
/// # struct B(u32);
/// #
/// # let mut world = World::new();
/// # world.insert_resource(A(1));
/// # world.insert_resource(B(2));
/// let mut total = 0;
/// for (info, _) in world.iter_resources() {
/// println!("Resource: {}", info.name());
/// println!("Size: {} bytes", info.layout().size());
/// total += info.layout().size();
/// }
/// println!("Total size: {} bytes", total);
/// # assert_eq!(total, std::mem::size_of::<A>() + std::mem::size_of::<B>());
/// ```
///
/// ## Dynamically running closures for resources matching specific `TypeId`s
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use std::collections::HashMap;
/// # use std::any::TypeId;
/// # use bevy_ptr::Ptr;
/// # #[derive(Resource)]
/// # struct A(u32);
/// # #[derive(Resource)]
/// # struct B(u32);
/// #
/// # let mut world = World::new();
/// # world.insert_resource(A(1));
/// # world.insert_resource(B(2));
/// #
/// // In this example, `A` and `B` are resources. We deliberately do not use the
/// // `bevy_reflect` crate here to showcase the low-level [`Ptr`] usage. You should
/// // probably use something like `ReflectFromPtr` in a real-world scenario.
///
/// // Create the hash map that will store the closures for each resource type
/// let mut closures: HashMap<TypeId, Box<dyn Fn(&Ptr<'_>)>> = HashMap::new();
///
/// // Add closure for `A`
/// closures.insert(TypeId::of::<A>(), Box::new(|ptr| {
/// // SAFETY: We assert ptr is the same type of A with TypeId of A
/// let a = unsafe { &ptr.deref::<A>() };
/// # assert_eq!(a.0, 1);
/// // ... do something with `a` here
/// }));
///
/// // Add closure for `B`
/// closures.insert(TypeId::of::<B>(), Box::new(|ptr| {
/// // SAFETY: We assert ptr is the same type of B with TypeId of B
/// let b = unsafe { &ptr.deref::<B>() };
/// # assert_eq!(b.0, 2);
/// // ... do something with `b` here
/// }));
///
/// // Iterate all resources, in order to run the closures for each matching resource type
/// for (info, ptr) in world.iter_resources() {
/// let Some(type_id) = info.type_id() else {
/// // It's possible for resources to not have a `TypeId` (e.g. non-Rust resources
/// // dynamically inserted via a scripting language) in which case we can't match them.
/// continue;
/// };
///
/// let Some(closure) = closures.get(&type_id) else {
/// // No closure for this resource type, skip it.
/// continue;
/// };
///
/// // Run the closure for the resource
/// closure(&ptr);
/// }
/// ```
#[inline]
pub fn iter_resources(&self) -> impl Iterator<Item = (&ComponentInfo, Ptr<'_>)> {
self.storages
.resources
.iter()
.filter_map(|(component_id, data)| {
// SAFETY: If a resource has been initialized, a corresponding ComponentInfo must exist with it's ID.
let component_info = unsafe {
self.components
.get_info(component_id)
.debug_checked_unwrap()
};
Some((component_info, data.get_data()?))
})
}

/// Mutably iterates over all resources in the world.
///
/// The returned iterator provides lifetimed, but type-unsafe pointers. Actually reading from or writing
/// to the contents of each resource will require the use of unsafe code.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::change_detection::MutUntyped;
/// # use std::collections::HashMap;
/// # use std::any::TypeId;
/// # #[derive(Resource)]
/// # struct A(u32);
/// # #[derive(Resource)]
/// # struct B(u32);
/// #
/// # let mut world = World::new();
/// # world.insert_resource(A(1));
/// # world.insert_resource(B(2));
/// #
/// // In this example, `A` and `B` are resources. We deliberately do not use the
/// // `bevy_reflect` crate here to showcase the low-level `MutUntyped` usage. You should
/// // probably use something like `ReflectFromPtr` in a real-world scenario.
///
/// // Create the hash map that will store the mutator closures for each resource type
/// let mut mutators: HashMap<TypeId, Box<dyn Fn(&mut MutUntyped<'_>)>> = HashMap::new();
///
/// // Add mutator closure for `A`
/// mutators.insert(TypeId::of::<A>(), Box::new(|mut_untyped| {
/// // Note: `MutUntyped::as_mut()` automatically marks the resource as changed
/// // for ECS change detection, and gives us a `PtrMut` we can use to mutate the resource.
/// // SAFETY: We assert ptr is the same type of A with TypeId of A
/// let a = unsafe { &mut mut_untyped.as_mut().deref_mut::<A>() };
/// # a.0 += 1;
/// // ... mutate `a` here
/// }));
///
/// // Add mutator closure for `B`
/// mutators.insert(TypeId::of::<B>(), Box::new(|mut_untyped| {
/// // SAFETY: We assert ptr is the same type of B with TypeId of B
/// let b = unsafe { &mut mut_untyped.as_mut().deref_mut::<B>() };
/// # b.0 += 1;
/// // ... mutate `b` here
/// }));
///
/// // Iterate all resources, in order to run the mutator closures for each matching resource type
/// for (info, mut mut_untyped) in world.iter_resources_mut() {
/// let Some(type_id) = info.type_id() else {
/// // It's possible for resources to not have a `TypeId` (e.g. non-Rust resources
/// // dynamically inserted via a scripting language) in which case we can't match them.
/// continue;
/// };
///
/// let Some(mutator) = mutators.get(&type_id) else {
/// // No mutator closure for this resource type, skip it.
/// continue;
/// };
///
/// // Run the mutator closure for the resource
/// mutator(&mut mut_untyped);
/// }
/// # assert_eq!(world.resource::<A>().0, 2);
/// # assert_eq!(world.resource::<B>().0, 3);
/// ```
#[inline]
pub fn iter_resources_mut(&mut self) -> impl Iterator<Item = (&ComponentInfo, MutUntyped<'_>)> {
self.storages
.resources
.iter()
.filter_map(|(component_id, data)| {
// SAFETY: If a resource has been initialized, a corresponding ComponentInfo must exist with it's ID.
let component_info = unsafe {
self.components
.get_info(component_id)
.debug_checked_unwrap()
};
let (ptr, ticks) = data.get_with_ticks()?;

// SAFETY:
// - We have exclusive access to the world, so no other code can be aliasing the `TickCells`
// - We only hold one `TicksMut` at a time, and we let go of it before getting the next one
let ticks = unsafe {
TicksMut::from_tick_cells(
ticks,
self.last_change_tick(),
self.read_change_tick(),
)
};

let mut_untyped = MutUntyped {
// SAFETY:
// - We have exclusive access to the world, so no other code can be aliasing the `Ptr`
// - We iterate one resource at a time, and we let go of each `PtrMut` before getting the next one
value: unsafe { ptr.assert_unique() },
ticks,
};

Some((component_info, mut_untyped))
})
}

/// Gets a `!Send` resource to the resource with the id [`ComponentId`] if it exists.
/// The returned pointer must not be used to modify the resource, and must not be
/// dereferenced after the immutable borrow of the [`World`] ends.
Expand Down Expand Up @@ -2554,6 +2757,12 @@ mod tests {
#[derive(Resource)]
struct TestResource(u32);

#[derive(Resource)]
struct TestResource2(String);

#[derive(Resource)]
struct TestResource3;

#[test]
fn get_resource_by_id() {
let mut world = World::new();
Expand Down Expand Up @@ -2594,6 +2803,66 @@ mod tests {
assert_eq!(resource.0, 43);
}

#[test]
fn iter_resources() {
let mut world = World::new();
world.insert_resource(TestResource(42));
world.insert_resource(TestResource2("Hello, world!".to_string()));
world.insert_resource(TestResource3);
world.remove_resource::<TestResource3>();

let mut iter = world.iter_resources();

let (info, ptr) = iter.next().unwrap();
assert_eq!(info.name(), std::any::type_name::<TestResource>());
// SAFETY: We know that the resource is of type `TestResource`
assert_eq!(unsafe { ptr.deref::<TestResource>().0 }, 42);

let (info, ptr) = iter.next().unwrap();
assert_eq!(info.name(), std::any::type_name::<TestResource2>());
assert_eq!(
// SAFETY: We know that the resource is of type `TestResource2`
unsafe { &ptr.deref::<TestResource2>().0 },
&"Hello, world!".to_string()
);

assert!(iter.next().is_none());
}

#[test]
fn iter_resources_mut() {
let mut world = World::new();
world.insert_resource(TestResource(42));
world.insert_resource(TestResource2("Hello, world!".to_string()));
world.insert_resource(TestResource3);
world.remove_resource::<TestResource3>();

let mut iter = world.iter_resources_mut();

let (info, mut mut_untyped) = iter.next().unwrap();
assert_eq!(info.name(), std::any::type_name::<TestResource>());
// SAFETY: We know that the resource is of type `TestResource`
unsafe {
mut_untyped.as_mut().deref_mut::<TestResource>().0 = 43;
};

let (info, mut mut_untyped) = iter.next().unwrap();
assert_eq!(info.name(), std::any::type_name::<TestResource2>());
// SAFETY: We know that the resource is of type `TestResource2`
unsafe {
mut_untyped.as_mut().deref_mut::<TestResource2>().0 = "Hello, world?".to_string();
};

assert!(iter.next().is_none());
std::mem::drop(iter);

assert_eq!(world.resource::<TestResource>().0, 43);
assert_eq!(
world.resource::<TestResource2>().0,
"Hello, world?".to_string()
);
}

#[test]
fn custom_resource_with_layout() {
static DROP_COUNT: AtomicU32 = AtomicU32::new(0);
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_gizmos/src/arcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
///
/// # Arguments
/// - `position` sets the center of this circle.
/// - `radius` controls the distance from `position` to this arc, and thus its curvature.
/// - `direction_angle` sets the clockwise angle in radians between `Vec2::Y` and
/// the vector from `position` to the midpoint of the arc.
/// - `arc_angle` sets the length of this arc, in radians.
/// - `radius` controls the distance from `position` to this arc, and thus its curvature.
/// - `color` sets the color to draw the arc.
///
/// # Example
/// ```
Expand Down
15 changes: 7 additions & 8 deletions crates/bevy_ui/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ mod tests {
use bevy_render::camera::OrthographicProjection;
use bevy_render::prelude::Camera;
use bevy_render::texture::Image;
use bevy_transform::prelude::{GlobalTransform, Transform};
use bevy_transform::prelude::GlobalTransform;
use bevy_transform::systems::{propagate_transforms, sync_simple_transforms};
use bevy_utils::prelude::default;
use bevy_utils::HashMap;
use bevy_window::PrimaryWindow;
Expand Down Expand Up @@ -399,6 +400,8 @@ mod tests {
update_target_camera_system,
apply_deferred,
ui_layout_system,
sync_simple_transforms,
propagate_transforms,
)
.chain(),
);
Expand Down Expand Up @@ -697,15 +700,11 @@ mod tests {
ui_schedule.run(&mut world);

let overlap_check = world
.query_filtered::<(Entity, &Node, &mut GlobalTransform, &Transform), Without<Parent>>()
.iter_mut(&mut world)
.query_filtered::<(Entity, &Node, &GlobalTransform), Without<Parent>>()
.iter(&world)
.fold(
Option::<(Rect, bool)>::None,
|option_rect, (entity, node, mut global_transform, transform)| {
// fix global transform - for some reason the global transform isn't populated yet.
// might be related to how these specific tests are working directly with World instead of App
*global_transform = GlobalTransform::from(transform.compute_affine());
let global_transform = &*global_transform;
|option_rect, (entity, node, global_transform)| {
let current_rect = node.logical_rect(global_transform);
assert!(
current_rect.height().abs() + current_rect.width().abs() > 0.,
Expand Down
Loading

0 comments on commit 29e51a0

Please sign in to comment.