Merge branch 'main' into Remove-entity-reserving/pending/flushing-system

This commit is contained in:
Eagster 2025-07-10 13:31:40 -04:00 committed by GitHub
commit deb6b4d633
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 3815 additions and 3200 deletions

View File

@ -19,6 +19,7 @@ use bevy_input::keyboard::{KeyCode, KeyboardInput};
use bevy_input::ButtonState;
use bevy_input_focus::FocusedInput;
use bevy_log::warn_once;
use bevy_math::ops;
use bevy_picking::events::{Drag, DragEnd, DragStart, Pointer, Press};
use bevy_ui::{ComputedNode, ComputedNodeTarget, InteractionDisabled, UiGlobalTransform, UiScale};
@ -38,7 +39,8 @@ pub enum TrackClick {
/// A headless slider widget, which can be used to build custom sliders. Sliders have a value
/// (represented by the [`SliderValue`] component) and a range (represented by [`SliderRange`]). An
/// optional step size can be specified via [`SliderStep`].
/// optional step size can be specified via [`SliderStep`], and you can control the rounding
/// during dragging with [`SliderPrecision`].
///
/// You can also control the slider remotely by triggering a [`SetSliderValue`] event on it. This
/// can be useful in a console environment for controlling the value gamepad inputs.
@ -187,6 +189,25 @@ impl Default for SliderStep {
}
}
/// A component which controls the rounding of the slider value during dragging.
///
/// Stepping is not affected, although presumably the step size will be an integer multiple of the
/// rounding factor. This also doesn't prevent the slider value from being set to non-rounded values
/// by other means, such as manually entering digits via a numeric input field.
///
/// The value in this component represents the number of decimal places of desired precision, so a
/// value of 2 would round to the nearest 1/100th. A value of -3 would round to the nearest
/// thousand.
#[derive(Component, Debug, Default, Clone, Copy)]
pub struct SliderPrecision(pub i32);
impl SliderPrecision {
fn round(&self, value: f32) -> f32 {
let factor = ops::powf(10.0_f32, self.0 as f32);
(value * factor).round() / factor
}
}
/// Component used to manage the state of a slider during dragging.
#[derive(Component, Default)]
pub struct CoreSliderDragState {
@ -204,6 +225,7 @@ pub(crate) fn slider_on_pointer_down(
&SliderValue,
&SliderRange,
&SliderStep,
Option<&SliderPrecision>,
&ComputedNode,
&ComputedNodeTarget,
&UiGlobalTransform,
@ -217,8 +239,17 @@ pub(crate) fn slider_on_pointer_down(
if q_thumb.contains(trigger.target()) {
// Thumb click, stop propagation to prevent track click.
trigger.propagate(false);
} else if let Ok((slider, value, range, step, node, node_target, transform, disabled)) =
q_slider.get(trigger.target())
} else if let Ok((
slider,
value,
range,
step,
precision,
node,
node_target,
transform,
disabled,
)) = q_slider.get(trigger.target())
{
// Track click
trigger.propagate(false);
@ -257,7 +288,9 @@ pub(crate) fn slider_on_pointer_down(
value.0 + step.0
}
}
TrackClick::Snap => click_val,
TrackClick::Snap => precision
.map(|prec| prec.round(click_val))
.unwrap_or(click_val),
});
if matches!(slider.on_change, Callback::Ignore) {
@ -296,6 +329,7 @@ pub(crate) fn slider_on_drag(
&ComputedNode,
&CoreSlider,
&SliderRange,
Option<&SliderPrecision>,
&UiGlobalTransform,
&mut CoreSliderDragState,
Has<InteractionDisabled>,
@ -305,7 +339,8 @@ pub(crate) fn slider_on_drag(
mut commands: Commands,
ui_scale: Res<UiScale>,
) {
if let Ok((node, slider, range, transform, drag, disabled)) = q_slider.get_mut(trigger.target())
if let Ok((node, slider, range, precision, transform, drag, disabled)) =
q_slider.get_mut(trigger.target())
{
trigger.propagate(false);
if drag.dragging && !disabled {
@ -320,17 +355,22 @@ pub(crate) fn slider_on_drag(
let slider_width = ((node.size().x - thumb_size) * node.inverse_scale_factor).max(1.0);
let span = range.span();
let new_value = if span > 0. {
range.clamp(drag.offset + (distance.x * span) / slider_width)
drag.offset + (distance.x * span) / slider_width
} else {
range.start() + span * 0.5
};
let rounded_value = range.clamp(
precision
.map(|prec| prec.round(new_value))
.unwrap_or(new_value),
);
if matches!(slider.on_change, Callback::Ignore) {
commands
.entity(trigger.target())
.insert(SliderValue(new_value));
.insert(SliderValue(rounded_value));
} else {
commands.notify_with(&slider.on_change, new_value);
commands.notify_with(&slider.on_change, rounded_value);
}
}
}
@ -491,3 +531,24 @@ impl Plugin for CoreSliderPlugin {
.add_observer(slider_on_set_value);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_slider_precision_rounding() {
// Test positive precision values (decimal places)
let precision_2dp = SliderPrecision(2);
assert_eq!(precision_2dp.round(1.234567), 1.23);
assert_eq!(precision_2dp.round(1.235), 1.24);
// Test zero precision (rounds to integers)
let precision_0dp = SliderPrecision(0);
assert_eq!(precision_0dp.round(1.4), 1.0);
// Test negative precision (rounds to tens, hundreds, etc.)
let precision_neg1 = SliderPrecision(-1);
assert_eq!(precision_neg1.round(14.0), 10.0);
}
}

View File

@ -21,7 +21,7 @@ mod core_radio;
mod core_scrollbar;
mod core_slider;
use bevy_app::{App, Plugin};
use bevy_app::{PluginGroup, PluginGroupBuilder};
pub use callback::{Callback, Notify};
pub use core_button::{CoreButton, CoreButtonPlugin};
@ -33,21 +33,20 @@ pub use core_scrollbar::{
};
pub use core_slider::{
CoreSlider, CoreSliderDragState, CoreSliderPlugin, CoreSliderThumb, SetSliderValue,
SliderRange, SliderStep, SliderValue, TrackClick,
SliderPrecision, SliderRange, SliderStep, SliderValue, TrackClick,
};
/// A plugin that registers the observers for all of the core widgets. If you don't want to
/// A plugin group that registers the observers for all of the core widgets. If you don't want to
/// use all of the widgets, you can import the individual widget plugins instead.
pub struct CoreWidgetsPlugin;
pub struct CoreWidgetsPlugins;
impl Plugin for CoreWidgetsPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
CoreButtonPlugin,
CoreCheckboxPlugin,
CoreRadioGroupPlugin,
CoreScrollbarPlugin,
CoreSliderPlugin,
));
impl PluginGroup for CoreWidgetsPlugins {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(CoreButtonPlugin)
.add(CoreCheckboxPlugin)
.add(CoreRadioGroupPlugin)
.add(CoreScrollbarPlugin)
.add(CoreSliderPlugin)
}
}

View File

@ -18,12 +18,14 @@ bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev" }
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
bevy_text = { path = "../bevy_text", version = "0.17.0-dev" }
bevy_ui = { path = "../bevy_ui", version = "0.17.0-dev" }
bevy_ui_render = { path = "../bevy_ui_render", version = "0.17.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
bevy_state = { path = "../bevy_state", version = "0.17.0-dev" }

View File

@ -1,7 +1,7 @@
//! Module containing logic for FPS overlay.
use bevy_app::{Plugin, Startup, Update};
use bevy_asset::Handle;
use bevy_asset::{Assets, Handle};
use bevy_color::Color;
use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
use bevy_ecs::{
@ -12,22 +12,31 @@ use bevy_ecs::{
query::With,
resource::Resource,
schedule::{common_conditions::resource_changed, IntoScheduleConfigs},
system::{Commands, Query, Res},
system::{Commands, Query, Res, ResMut},
};
use bevy_render::view::Visibility;
use bevy_render::{storage::ShaderStorageBuffer, view::Visibility};
use bevy_text::{Font, TextColor, TextFont, TextSpan};
use bevy_time::Time;
use bevy_ui::{
widget::{Text, TextUiWriter},
GlobalZIndex, Node, PositionType,
FlexDirection, GlobalZIndex, Node, PositionType, Val,
};
use bevy_ui_render::prelude::MaterialNode;
use core::time::Duration;
use crate::frame_time_graph::{
FrameTimeGraphConfigUniform, FrameTimeGraphPlugin, FrametimeGraphMaterial,
};
/// [`GlobalZIndex`] used to render the fps overlay.
///
/// We use a number slightly under `i32::MAX` so you can render on top of it if you really need to.
pub const FPS_OVERLAY_ZINDEX: i32 = i32::MAX - 32;
// Used to scale the frame time graph based on the fps text size
const FRAME_TIME_GRAPH_WIDTH_SCALE: f32 = 6.0;
const FRAME_TIME_GRAPH_HEIGHT_SCALE: f32 = 2.0;
/// A plugin that adds an FPS overlay to the Bevy application.
///
/// This plugin will add the [`FrameTimeDiagnosticsPlugin`] if it wasn't added before.
@ -47,12 +56,18 @@ impl Plugin for FpsOverlayPlugin {
if !app.is_plugin_added::<FrameTimeDiagnosticsPlugin>() {
app.add_plugins(FrameTimeDiagnosticsPlugin::default());
}
if !app.is_plugin_added::<FrameTimeGraphPlugin>() {
app.add_plugins(FrameTimeGraphPlugin);
}
app.insert_resource(self.config.clone())
.add_systems(Startup, setup)
.add_systems(
Update,
(
(customize_text, toggle_display).run_if(resource_changed::<FpsOverlayConfig>),
(toggle_display, customize_overlay)
.run_if(resource_changed::<FpsOverlayConfig>),
update_text,
),
);
@ -72,6 +87,8 @@ pub struct FpsOverlayConfig {
///
/// Defaults to once every 100 ms.
pub refresh_interval: Duration,
/// Configuration of the frame time graph
pub frame_time_graph_config: FrameTimeGraphConfig,
}
impl Default for FpsOverlayConfig {
@ -85,6 +102,43 @@ impl Default for FpsOverlayConfig {
text_color: Color::WHITE,
enabled: true,
refresh_interval: Duration::from_millis(100),
// TODO set this to display refresh rate if possible
frame_time_graph_config: FrameTimeGraphConfig::target_fps(60.0),
}
}
}
/// Configuration of the frame time graph
#[derive(Clone, Copy)]
pub struct FrameTimeGraphConfig {
/// Is the graph visible
pub enabled: bool,
/// The minimum acceptable FPS
///
/// Anything below this will show a red bar
pub min_fps: f32,
/// The target FPS
///
/// Anything above this will show a green bar
pub target_fps: f32,
}
impl FrameTimeGraphConfig {
/// Constructs a default config for a given target fps
pub fn target_fps(target_fps: f32) -> Self {
Self {
target_fps,
..Self::default()
}
}
}
impl Default for FrameTimeGraphConfig {
fn default() -> Self {
Self {
enabled: true,
min_fps: 30.0,
target_fps: 60.0,
}
}
}
@ -92,12 +146,21 @@ impl Default for FpsOverlayConfig {
#[derive(Component)]
struct FpsText;
fn setup(mut commands: Commands, overlay_config: Res<FpsOverlayConfig>) {
#[derive(Component)]
struct FrameTimeGraph;
fn setup(
mut commands: Commands,
overlay_config: Res<FpsOverlayConfig>,
mut frame_time_graph_materials: ResMut<Assets<FrametimeGraphMaterial>>,
mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
) {
commands
.spawn((
Node {
// We need to make sure the overlay doesn't affect the position of other UI nodes
position_type: PositionType::Absolute,
flex_direction: FlexDirection::Column,
..Default::default()
},
// Render overlay on top of everything
@ -111,6 +174,29 @@ fn setup(mut commands: Commands, overlay_config: Res<FpsOverlayConfig>) {
FpsText,
))
.with_child((TextSpan::default(), overlay_config.text_config.clone()));
let font_size = overlay_config.text_config.font_size;
p.spawn((
Node {
width: Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE),
height: Val::Px(font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE),
display: if overlay_config.frame_time_graph_config.enabled {
bevy_ui::Display::DEFAULT
} else {
bevy_ui::Display::None
},
..Default::default()
},
MaterialNode::from(frame_time_graph_materials.add(FrametimeGraphMaterial {
values: buffers.add(ShaderStorageBuffer::default()),
config: FrameTimeGraphConfigUniform::new(
overlay_config.frame_time_graph_config.target_fps,
overlay_config.frame_time_graph_config.min_fps,
true,
),
})),
FrameTimeGraph,
));
});
}
@ -135,7 +221,7 @@ fn update_text(
}
}
fn customize_text(
fn customize_overlay(
overlay_config: Res<FpsOverlayConfig>,
query: Query<Entity, With<FpsText>>,
mut writer: TextUiWriter,
@ -151,6 +237,7 @@ fn customize_text(
fn toggle_display(
overlay_config: Res<FpsOverlayConfig>,
mut query: Query<&mut Visibility, With<FpsText>>,
mut graph_style: Query<&mut Node, With<FrameTimeGraph>>,
) {
for mut visibility in &mut query {
visibility.set_if_neq(match overlay_config.enabled {
@ -158,4 +245,17 @@ fn toggle_display(
false => Visibility::Hidden,
});
}
if let Ok(mut graph_style) = graph_style.single_mut() {
if overlay_config.frame_time_graph_config.enabled {
// Scale the frame time graph based on the font size of the overlay
let font_size = overlay_config.text_config.font_size;
graph_style.width = Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE);
graph_style.height = Val::Px(font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE);
graph_style.display = bevy_ui::Display::DEFAULT;
} else {
graph_style.display = bevy_ui::Display::None;
}
}
}

View File

@ -0,0 +1,68 @@
#import bevy_ui::ui_vertex_output::UiVertexOutput
@group(1) @binding(0) var<storage> values: array<f32>;
struct Config {
dt_min: f32,
dt_max: f32,
dt_min_log2: f32,
dt_max_log2: f32,
proportional_width: u32,
}
@group(1) @binding(1) var<uniform> config: Config;
const RED: vec4<f32> = vec4(1.0, 0.0, 0.0, 1.0);
const GREEN: vec4<f32> = vec4(0.0, 1.0, 0.0, 1.0);
// Gets a color based on the delta time
// TODO use customizable gradient
fn color_from_dt(dt: f32) -> vec4<f32> {
return mix(GREEN, RED, dt / config.dt_max);
}
// Draw an SDF square
fn sdf_square(pos: vec2<f32>, half_size: vec2<f32>, offset: vec2<f32>) -> f32 {
let p = pos - offset;
let dist = abs(p) - half_size;
let outside_dist = length(max(dist, vec2<f32>(0.0, 0.0)));
let inside_dist = min(max(dist.x, dist.y), 0.0);
return outside_dist + inside_dist;
}
@fragment
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
let dt_min = config.dt_min;
let dt_max = config.dt_max;
let dt_min_log2 = config.dt_min_log2;
let dt_max_log2 = config.dt_max_log2;
// The general algorithm is highly inspired by
// <https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times>
let len = arrayLength(&values);
var graph_width = 0.0;
for (var i = 0u; i <= len; i += 1u) {
let dt = values[len - i];
var frame_width: f32;
if config.proportional_width == 1u {
frame_width = (dt / dt_min) / f32(len);
} else {
frame_width = 0.015;
}
let frame_height_factor = (log2(dt) - dt_min_log2) / (dt_max_log2 - dt_min_log2);
let frame_height_factor_norm = min(max(0.0, frame_height_factor), 1.0);
let frame_height = mix(0.0, 1.0, frame_height_factor_norm);
let size = vec2(frame_width, frame_height) / 2.0;
let offset = vec2(1.0 - graph_width - size.x, 1. - size.y);
if (sdf_square(in.uv, size, offset) < 0.0) {
return color_from_dt(dt);
}
graph_width += frame_width;
}
return vec4(0.0, 0.0, 0.0, 0.5);
}

View File

@ -0,0 +1,114 @@
//! Module containing logic for the frame time graph
use bevy_app::{Plugin, Update};
use bevy_asset::{load_internal_asset, uuid_handle, Asset, Assets, Handle};
use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
use bevy_ecs::system::{Res, ResMut};
use bevy_math::ops::log2;
use bevy_reflect::TypePath;
use bevy_render::{
render_resource::{AsBindGroup, Shader, ShaderRef, ShaderType},
storage::ShaderStorageBuffer,
};
use bevy_ui_render::prelude::{UiMaterial, UiMaterialPlugin};
use crate::fps_overlay::FpsOverlayConfig;
const FRAME_TIME_GRAPH_SHADER_HANDLE: Handle<Shader> =
uuid_handle!("4e38163a-5782-47a5-af52-d9161472ab59");
/// Plugin that sets up everything to render the frame time graph material
pub struct FrameTimeGraphPlugin;
impl Plugin for FrameTimeGraphPlugin {
fn build(&self, app: &mut bevy_app::App) {
load_internal_asset!(
app,
FRAME_TIME_GRAPH_SHADER_HANDLE,
"frame_time_graph.wgsl",
Shader::from_wgsl
);
// TODO: Use plugin dependencies, see https://github.com/bevyengine/bevy/issues/69
if !app.is_plugin_added::<FrameTimeDiagnosticsPlugin>() {
panic!("Requires FrameTimeDiagnosticsPlugin");
// app.add_plugins(FrameTimeDiagnosticsPlugin);
}
app.add_plugins(UiMaterialPlugin::<FrametimeGraphMaterial>::default())
.add_systems(Update, update_frame_time_values);
}
}
/// The config values sent to the frame time graph shader
#[derive(Debug, Clone, Copy, ShaderType)]
pub struct FrameTimeGraphConfigUniform {
// minimum expected delta time
dt_min: f32,
// maximum expected delta time
dt_max: f32,
dt_min_log2: f32,
dt_max_log2: f32,
// controls whether or not the bars width are proportional to their delta time
proportional_width: u32,
}
impl FrameTimeGraphConfigUniform {
/// `proportional_width`: controls whether or not the bars width are proportional to their delta time
pub fn new(target_fps: f32, min_fps: f32, proportional_width: bool) -> Self {
// we want an upper limit that is above the target otherwise the bars will disappear
let dt_min = 1. / (target_fps * 1.2);
let dt_max = 1. / min_fps;
Self {
dt_min,
dt_max,
dt_min_log2: log2(dt_min),
dt_max_log2: log2(dt_max),
proportional_width: u32::from(proportional_width),
}
}
}
/// The material used to render the frame time graph ui node
#[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
pub struct FrametimeGraphMaterial {
/// The history of the previous frame times value.
///
/// This should be updated every frame to match the frame time history from the [`DiagnosticsStore`]
#[storage(0, read_only)]
pub values: Handle<ShaderStorageBuffer>, // Vec<f32>,
/// The configuration values used by the shader to control how the graph is rendered
#[uniform(1)]
pub config: FrameTimeGraphConfigUniform,
}
impl UiMaterial for FrametimeGraphMaterial {
fn fragment_shader() -> ShaderRef {
FRAME_TIME_GRAPH_SHADER_HANDLE.into()
}
}
/// A system that updates the frame time values sent to the frame time graph
fn update_frame_time_values(
mut frame_time_graph_materials: ResMut<Assets<FrametimeGraphMaterial>>,
mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
diagnostics_store: Res<DiagnosticsStore>,
config: Option<Res<FpsOverlayConfig>>,
) {
if !config.is_none_or(|c| c.frame_time_graph_config.enabled) {
return;
}
let Some(frame_time) = diagnostics_store.get(&FrameTimeDiagnosticsPlugin::FRAME_TIME) else {
return;
};
let frame_times = frame_time
.values()
// convert to millis
.map(|x| *x as f32 / 1000.0)
.collect::<Vec<_>>();
for (_, material) in frame_time_graph_materials.iter_mut() {
let buffer = buffers.get_mut(&material.values).unwrap();
buffer.set_data(frame_times.clone().as_slice());
}
}

View File

@ -14,6 +14,7 @@ use bevy_app::prelude::*;
pub mod ci_testing;
pub mod fps_overlay;
pub mod frame_time_graph;
pub mod picking_debug;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,219 @@
use core::marker::PhantomData;
use crate::component::Component;
use crate::entity::{ComponentCloneCtx, SourceComponent};
/// Function type that can be used to clone an entity.
pub type ComponentCloneFn = fn(&SourceComponent, &mut ComponentCloneCtx);
/// The clone behavior to use when cloning a [`Component`].
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum ComponentCloneBehavior {
/// Uses the default behavior (which is passed to [`ComponentCloneBehavior::resolve`])
#[default]
Default,
/// Do not clone this component.
Ignore,
/// Uses a custom [`ComponentCloneFn`].
Custom(ComponentCloneFn),
}
impl ComponentCloneBehavior {
/// Set clone handler based on `Clone` trait.
///
/// If set as a handler for a component that is not the same as the one used to create this handler, it will panic.
pub fn clone<C: Component + Clone>() -> Self {
Self::Custom(component_clone_via_clone::<C>)
}
/// Set clone handler based on `Reflect` trait.
#[cfg(feature = "bevy_reflect")]
pub fn reflect() -> Self {
Self::Custom(component_clone_via_reflect)
}
/// Returns the "global default"
pub fn global_default_fn() -> ComponentCloneFn {
#[cfg(feature = "bevy_reflect")]
return component_clone_via_reflect;
#[cfg(not(feature = "bevy_reflect"))]
return component_clone_ignore;
}
/// Resolves the [`ComponentCloneBehavior`] to a [`ComponentCloneFn`]. If [`ComponentCloneBehavior::Default`] is
/// specified, the given `default` function will be used.
pub fn resolve(&self, default: ComponentCloneFn) -> ComponentCloneFn {
match self {
ComponentCloneBehavior::Default => default,
ComponentCloneBehavior::Ignore => component_clone_ignore,
ComponentCloneBehavior::Custom(custom) => *custom,
}
}
}
/// Component [clone handler function](ComponentCloneFn) implemented using the [`Clone`] trait.
/// Can be [set](Component::clone_behavior) as clone handler for the specific component it is implemented for.
/// It will panic if set as handler for any other component.
///
pub fn component_clone_via_clone<C: Clone + Component>(
source: &SourceComponent,
ctx: &mut ComponentCloneCtx,
) {
if let Some(component) = source.read::<C>() {
ctx.write_target_component(component.clone());
}
}
/// Component [clone handler function](ComponentCloneFn) implemented using reflect.
/// Can be [set](Component::clone_behavior) as clone handler for any registered component,
/// but only reflected components will be cloned.
///
/// To clone a component using this handler, the following must be true:
/// - World has [`AppTypeRegistry`](crate::reflect::AppTypeRegistry)
/// - Component has [`TypeId`](core::any::TypeId)
/// - Component is registered
/// - Component has [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr) registered
/// - Component can be cloned via [`PartialReflect::reflect_clone`] _or_ has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect),
/// [`ReflectDefault`](bevy_reflect::std_traits::ReflectDefault), [`ReflectFromWorld`](crate::reflect::ReflectFromWorld)
///
/// If any of the conditions is not satisfied, the component will be skipped.
///
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
///
/// [`PartialReflect::reflect_clone`]: bevy_reflect::PartialReflect::reflect_clone
#[cfg(feature = "bevy_reflect")]
pub fn component_clone_via_reflect(source: &SourceComponent, ctx: &mut ComponentCloneCtx) {
let Some(app_registry) = ctx.type_registry().cloned() else {
return;
};
let registry = app_registry.read();
let Some(source_component_reflect) = source.read_reflect(&registry) else {
return;
};
let component_info = ctx.component_info();
// checked in read_source_component_reflect
let type_id = component_info.type_id().unwrap();
// Try to clone using `reflect_clone`
if let Ok(mut component) = source_component_reflect.reflect_clone() {
if let Some(reflect_component) =
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
{
reflect_component.map_entities(&mut *component, ctx.entity_mapper());
}
drop(registry);
ctx.write_target_component_reflect(component);
return;
}
// Try to clone using ReflectFromReflect
if let Some(reflect_from_reflect) =
registry.get_type_data::<bevy_reflect::ReflectFromReflect>(type_id)
{
if let Some(mut component) =
reflect_from_reflect.from_reflect(source_component_reflect.as_partial_reflect())
{
if let Some(reflect_component) =
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
{
reflect_component.map_entities(&mut *component, ctx.entity_mapper());
}
drop(registry);
ctx.write_target_component_reflect(component);
return;
}
}
// Else, try to clone using ReflectDefault
if let Some(reflect_default) =
registry.get_type_data::<bevy_reflect::std_traits::ReflectDefault>(type_id)
{
let mut component = reflect_default.default();
component.apply(source_component_reflect.as_partial_reflect());
drop(registry);
ctx.write_target_component_reflect(component);
return;
}
// Otherwise, try to clone using ReflectFromWorld
if let Some(reflect_from_world) =
registry.get_type_data::<crate::reflect::ReflectFromWorld>(type_id)
{
use crate::{entity::EntityMapper, world::World};
let reflect_from_world = reflect_from_world.clone();
let source_component_cloned = source_component_reflect.to_dynamic();
let component_layout = component_info.layout();
let target = ctx.target();
let component_id = ctx.component_id();
drop(registry);
ctx.queue_deferred(move |world: &mut World, mapper: &mut dyn EntityMapper| {
let mut component = reflect_from_world.from_world(world);
assert_eq!(type_id, (*component).type_id());
component.apply(source_component_cloned.as_partial_reflect());
if let Some(reflect_component) = app_registry
.read()
.get_type_data::<crate::reflect::ReflectComponent>(type_id)
{
reflect_component.map_entities(&mut *component, mapper);
}
// SAFETY:
// - component_id is from the same world as target entity
// - component is a valid value represented by component_id
unsafe {
use alloc::boxed::Box;
use bevy_ptr::OwningPtr;
let raw_component_ptr =
core::ptr::NonNull::new_unchecked(Box::into_raw(component).cast::<u8>());
world
.entity_mut(target)
.insert_by_id(component_id, OwningPtr::new(raw_component_ptr));
if component_layout.size() > 0 {
// Ensure we don't attempt to deallocate zero-sized components
alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout);
}
}
});
}
}
/// Noop implementation of component clone handler function.
///
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
pub fn component_clone_ignore(_source: &SourceComponent, _ctx: &mut ComponentCloneCtx) {}
/// Wrapper for components clone specialization using autoderef.
#[doc(hidden)]
pub struct DefaultCloneBehaviorSpecialization<T>(PhantomData<T>);
impl<T> Default for DefaultCloneBehaviorSpecialization<T> {
fn default() -> Self {
Self(PhantomData)
}
}
/// Base trait for components clone specialization using autoderef.
#[doc(hidden)]
pub trait DefaultCloneBehaviorBase {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
impl<C> DefaultCloneBehaviorBase for DefaultCloneBehaviorSpecialization<C> {
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::Default
}
}
/// Specialized trait for components clone specialization using autoderef.
#[doc(hidden)]
pub trait DefaultCloneBehaviorViaClone {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
impl<C: Clone + Component> DefaultCloneBehaviorViaClone for &DefaultCloneBehaviorSpecialization<C> {
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::clone::<C>()
}
}

View File

@ -0,0 +1,721 @@
use alloc::{borrow::Cow, vec::Vec};
use bevy_platform::{collections::HashSet, sync::PoisonError};
use bevy_ptr::OwningPtr;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use bevy_utils::{prelude::DebugName, TypeIdMap};
use core::{
alloc::Layout,
any::{Any, TypeId},
fmt::Debug,
mem::needs_drop,
};
use crate::{
archetype::ArchetypeFlags,
component::{
Component, ComponentCloneBehavior, ComponentMutability, QueuedComponents,
RequiredComponents, StorageType,
},
lifecycle::ComponentHooks,
query::DebugCheckedUnwrap as _,
resource::Resource,
storage::SparseSetIndex,
};
/// Stores metadata for a type of component or resource stored in a specific [`World`](crate::world::World).
#[derive(Debug, Clone)]
pub struct ComponentInfo {
pub(super) id: ComponentId,
pub(super) descriptor: ComponentDescriptor,
pub(super) hooks: ComponentHooks,
pub(super) required_components: RequiredComponents,
pub(super) required_by: HashSet<ComponentId>,
}
impl ComponentInfo {
/// Returns a value uniquely identifying the current component.
#[inline]
pub fn id(&self) -> ComponentId {
self.id
}
/// Returns the name of the current component.
#[inline]
pub fn name(&self) -> DebugName {
self.descriptor.name.clone()
}
/// Returns `true` if the current component is mutable.
#[inline]
pub fn mutable(&self) -> bool {
self.descriptor.mutable
}
/// Returns [`ComponentCloneBehavior`] of the current component.
#[inline]
pub fn clone_behavior(&self) -> &ComponentCloneBehavior {
&self.descriptor.clone_behavior
}
/// Returns the [`TypeId`] of the underlying component type.
/// Returns `None` if the component does not correspond to a Rust type.
#[inline]
pub fn type_id(&self) -> Option<TypeId> {
self.descriptor.type_id
}
/// Returns the layout used to store values of this component in memory.
#[inline]
pub fn layout(&self) -> Layout {
self.descriptor.layout
}
#[inline]
/// Get the function which should be called to clean up values of
/// the underlying component type. This maps to the
/// [`Drop`] implementation for 'normal' Rust components
///
/// Returns `None` if values of the underlying component type don't
/// need to be dropped, e.g. as reported by [`needs_drop`].
pub fn drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
self.descriptor.drop
}
/// Returns a value indicating the storage strategy for the current component.
#[inline]
pub fn storage_type(&self) -> StorageType {
self.descriptor.storage_type
}
/// Returns `true` if the underlying component type can be freely shared between threads.
/// If this returns `false`, then extra care must be taken to ensure that components
/// are not accessed from the wrong thread.
#[inline]
pub fn is_send_and_sync(&self) -> bool {
self.descriptor.is_send_and_sync
}
/// Create a new [`ComponentInfo`].
pub(crate) fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
ComponentInfo {
id,
descriptor,
hooks: Default::default(),
required_components: Default::default(),
required_by: Default::default(),
}
}
/// Update the given flags to include any [`ComponentHook`](crate::component::ComponentHook) registered to self
#[inline]
pub(crate) fn update_archetype_flags(&self, flags: &mut ArchetypeFlags) {
if self.hooks().on_add.is_some() {
flags.insert(ArchetypeFlags::ON_ADD_HOOK);
}
if self.hooks().on_insert.is_some() {
flags.insert(ArchetypeFlags::ON_INSERT_HOOK);
}
if self.hooks().on_replace.is_some() {
flags.insert(ArchetypeFlags::ON_REPLACE_HOOK);
}
if self.hooks().on_remove.is_some() {
flags.insert(ArchetypeFlags::ON_REMOVE_HOOK);
}
if self.hooks().on_despawn.is_some() {
flags.insert(ArchetypeFlags::ON_DESPAWN_HOOK);
}
}
/// Provides a reference to the collection of hooks associated with this [`Component`]
pub fn hooks(&self) -> &ComponentHooks {
&self.hooks
}
/// Retrieves the [`RequiredComponents`] collection, which contains all required components (and their constructors)
/// needed by this component. This includes _recursive_ required components.
pub fn required_components(&self) -> &RequiredComponents {
&self.required_components
}
}
/// A value which uniquely identifies the type of a [`Component`] or [`Resource`] within a
/// [`World`](crate::world::World).
///
/// Each time a new `Component` type is registered within a `World` using
/// e.g. [`World::register_component`](crate::world::World::register_component) or
/// [`World::register_component_with_descriptor`](crate::world::World::register_component_with_descriptor)
/// or a Resource with e.g. [`World::init_resource`](crate::world::World::init_resource),
/// a corresponding `ComponentId` is created to track it.
///
/// While the distinction between `ComponentId` and [`TypeId`] may seem superficial, breaking them
/// into two separate but related concepts allows components to exist outside of Rust's type system.
/// Each Rust type registered as a `Component` will have a corresponding `ComponentId`, but additional
/// `ComponentId`s may exist in a `World` to track components which cannot be
/// represented as Rust types for scripting or other advanced use-cases.
///
/// A `ComponentId` is tightly coupled to its parent `World`. Attempting to use a `ComponentId` from
/// one `World` to access the metadata of a `Component` in a different `World` is undefined behavior
/// and must not be attempted.
///
/// Given a type `T` which implements [`Component`], the `ComponentId` for `T` can be retrieved
/// from a `World` using [`World::component_id()`](crate::world::World::component_id) or via [`Components::component_id()`].
/// Access to the `ComponentId` for a [`Resource`] is available via [`Components::resource_id()`].
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
pub struct ComponentId(pub(super) usize);
impl ComponentId {
/// Creates a new [`ComponentId`].
///
/// The `index` is a unique value associated with each type of component in a given world.
/// Usually, this value is taken from a counter incremented for each type of component registered with the world.
#[inline]
pub const fn new(index: usize) -> ComponentId {
ComponentId(index)
}
/// Returns the index of the current component.
#[inline]
pub fn index(self) -> usize {
self.0
}
}
impl SparseSetIndex for ComponentId {
#[inline]
fn sparse_set_index(&self) -> usize {
self.index()
}
#[inline]
fn get_sparse_set_index(value: usize) -> Self {
Self(value)
}
}
/// A value describing a component or resource, which may or may not correspond to a Rust type.
#[derive(Clone)]
pub struct ComponentDescriptor {
name: DebugName,
// SAFETY: This must remain private. It must match the statically known StorageType of the
// associated rust component type if one exists.
storage_type: StorageType,
// SAFETY: This must remain private. It must only be set to "true" if this component is
// actually Send + Sync
is_send_and_sync: bool,
type_id: Option<TypeId>,
layout: Layout,
// SAFETY: this function must be safe to call with pointers pointing to items of the type
// this descriptor describes.
// None if the underlying type doesn't need to be dropped
drop: Option<for<'a> unsafe fn(OwningPtr<'a>)>,
mutable: bool,
clone_behavior: ComponentCloneBehavior,
}
// We need to ignore the `drop` field in our `Debug` impl
impl Debug for ComponentDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ComponentDescriptor")
.field("name", &self.name)
.field("storage_type", &self.storage_type)
.field("is_send_and_sync", &self.is_send_and_sync)
.field("type_id", &self.type_id)
.field("layout", &self.layout)
.field("mutable", &self.mutable)
.field("clone_behavior", &self.clone_behavior)
.finish()
}
}
impl ComponentDescriptor {
/// # Safety
///
/// `x` must point to a valid value of type `T`.
unsafe fn drop_ptr<T>(x: OwningPtr<'_>) {
// SAFETY: Contract is required to be upheld by the caller.
unsafe {
x.drop_as::<T>();
}
}
/// Create a new `ComponentDescriptor` for the type `T`.
pub fn new<T: Component>() -> Self {
Self {
name: DebugName::type_name::<T>(),
storage_type: T::STORAGE_TYPE,
is_send_and_sync: true,
type_id: Some(TypeId::of::<T>()),
layout: Layout::new::<T>(),
drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
mutable: T::Mutability::MUTABLE,
clone_behavior: T::clone_behavior(),
}
}
/// Create a new `ComponentDescriptor`.
///
/// # Safety
/// - the `drop` fn must be usable on a pointer with a value of the layout `layout`
/// - the component type must be safe to access from any thread (Send + Sync in rust terms)
pub unsafe fn new_with_layout(
name: impl Into<Cow<'static, str>>,
storage_type: StorageType,
layout: Layout,
drop: Option<for<'a> unsafe fn(OwningPtr<'a>)>,
mutable: bool,
clone_behavior: ComponentCloneBehavior,
) -> Self {
Self {
name: name.into().into(),
storage_type,
is_send_and_sync: true,
type_id: None,
layout,
drop,
mutable,
clone_behavior,
}
}
/// Create a new `ComponentDescriptor` for a resource.
///
/// The [`StorageType`] for resources is always [`StorageType::Table`].
pub fn new_resource<T: Resource>() -> Self {
Self {
name: DebugName::type_name::<T>(),
// PERF: `SparseStorage` may actually be a more
// reasonable choice as `storage_type` for resources.
storage_type: StorageType::Table,
is_send_and_sync: true,
type_id: Some(TypeId::of::<T>()),
layout: Layout::new::<T>(),
drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
mutable: true,
clone_behavior: ComponentCloneBehavior::Default,
}
}
pub(super) fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
Self {
name: DebugName::type_name::<T>(),
storage_type,
is_send_and_sync: false,
type_id: Some(TypeId::of::<T>()),
layout: Layout::new::<T>(),
drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
mutable: true,
clone_behavior: ComponentCloneBehavior::Default,
}
}
/// Returns a value indicating the storage strategy for the current component.
#[inline]
pub fn storage_type(&self) -> StorageType {
self.storage_type
}
/// Returns the [`TypeId`] of the underlying component type.
/// Returns `None` if the component does not correspond to a Rust type.
#[inline]
pub fn type_id(&self) -> Option<TypeId> {
self.type_id
}
/// Returns the name of the current component.
#[inline]
pub fn name(&self) -> DebugName {
self.name.clone()
}
/// Returns whether this component is mutable.
#[inline]
pub fn mutable(&self) -> bool {
self.mutable
}
}
/// Stores metadata associated with each kind of [`Component`] in a given [`World`](crate::world::World).
#[derive(Debug, Default)]
pub struct Components {
pub(super) components: Vec<Option<ComponentInfo>>,
pub(super) indices: TypeIdMap<ComponentId>,
pub(super) resource_indices: TypeIdMap<ComponentId>,
// This is kept internal and local to verify that no deadlocks can occor.
pub(super) queued: bevy_platform::sync::RwLock<QueuedComponents>,
}
impl Components {
/// This registers any descriptor, component or resource.
///
/// # Safety
///
/// The id must have never been registered before. This must be a fresh registration.
#[inline]
pub(super) unsafe fn register_component_inner(
&mut self,
id: ComponentId,
descriptor: ComponentDescriptor,
) {
let info = ComponentInfo::new(id, descriptor);
let least_len = id.0 + 1;
if self.components.len() < least_len {
self.components.resize_with(least_len, || None);
}
// SAFETY: We just extended the vec to make this index valid.
let slot = unsafe { self.components.get_mut(id.0).debug_checked_unwrap() };
// Caller ensures id is unique
debug_assert!(slot.is_none());
*slot = Some(info);
}
/// Returns the number of components registered or queued with this instance.
#[inline]
pub fn len(&self) -> usize {
self.num_queued() + self.num_registered()
}
/// Returns `true` if there are no components registered or queued with this instance. Otherwise, this returns `false`.
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the number of components registered with this instance.
#[inline]
pub fn num_queued(&self) -> usize {
let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len()
}
/// Returns `true` if there are any components registered with this instance. Otherwise, this returns `false`.
#[inline]
pub fn any_queued(&self) -> bool {
self.num_queued() > 0
}
/// A faster version of [`Self::num_queued`].
#[inline]
pub fn num_queued_mut(&mut self) -> usize {
let queued = self
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner);
queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len()
}
/// A faster version of [`Self::any_queued`].
#[inline]
pub fn any_queued_mut(&mut self) -> bool {
self.num_queued_mut() > 0
}
/// Returns the number of components registered with this instance.
#[inline]
pub fn num_registered(&self) -> usize {
self.components.len()
}
/// Returns `true` if there are any components registered with this instance. Otherwise, this returns `false`.
#[inline]
pub fn any_registered(&self) -> bool {
self.num_registered() > 0
}
/// Gets the metadata associated with the given component, if it is registered.
/// This will return `None` if the id is not registered or is queued.
///
/// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
#[inline]
pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> {
self.components.get(id.0).and_then(|info| info.as_ref())
}
/// Gets the [`ComponentDescriptor`] of the component with this [`ComponentId`] if it is present.
/// This will return `None` only if the id is neither registered nor queued to be registered.
///
/// Currently, the [`Cow`] will be [`Cow::Owned`] if and only if the component is queued. It will be [`Cow::Borrowed`] otherwise.
///
/// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
#[inline]
pub fn get_descriptor<'a>(&'a self, id: ComponentId) -> Option<Cow<'a, ComponentDescriptor>> {
self.components
.get(id.0)
.and_then(|info| info.as_ref().map(|info| Cow::Borrowed(&info.descriptor)))
.or_else(|| {
let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
// first check components, then resources, then dynamic
queued
.components
.values()
.chain(queued.resources.values())
.chain(queued.dynamic_registrations.iter())
.find(|queued| queued.id == id)
.map(|queued| Cow::Owned(queued.descriptor.clone()))
})
}
/// Gets the name of the component with this [`ComponentId`] if it is present.
/// This will return `None` only if the id is neither registered nor queued to be registered.
///
/// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
#[inline]
pub fn get_name<'a>(&'a self, id: ComponentId) -> Option<DebugName> {
self.components
.get(id.0)
.and_then(|info| info.as_ref().map(|info| info.descriptor.name()))
.or_else(|| {
let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
// first check components, then resources, then dynamic
queued
.components
.values()
.chain(queued.resources.values())
.chain(queued.dynamic_registrations.iter())
.find(|queued| queued.id == id)
.map(|queued| queued.descriptor.name.clone())
})
}
/// Gets the metadata associated with the given component.
/// # Safety
///
/// `id` must be a valid and fully registered [`ComponentId`].
#[inline]
pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo {
// SAFETY: The caller ensures `id` is valid.
unsafe {
self.components
.get(id.0)
.debug_checked_unwrap()
.as_ref()
.debug_checked_unwrap()
}
}
#[inline]
pub(crate) fn get_hooks_mut(&mut self, id: ComponentId) -> Option<&mut ComponentHooks> {
self.components
.get_mut(id.0)
.and_then(|info| info.as_mut().map(|info| &mut info.hooks))
}
#[inline]
pub(crate) fn get_required_components_mut(
&mut self,
id: ComponentId,
) -> Option<&mut RequiredComponents> {
self.components
.get_mut(id.0)
.and_then(|info| info.as_mut().map(|info| &mut info.required_components))
}
#[inline]
pub(crate) fn get_required_by(&self, id: ComponentId) -> Option<&HashSet<ComponentId>> {
self.components
.get(id.0)
.and_then(|info| info.as_ref().map(|info| &info.required_by))
}
#[inline]
pub(crate) fn get_required_by_mut(
&mut self,
id: ComponentId,
) -> Option<&mut HashSet<ComponentId>> {
self.components
.get_mut(id.0)
.and_then(|info| info.as_mut().map(|info| &mut info.required_by))
}
/// Returns true if the [`ComponentId`] is fully registered and valid.
/// Ids may be invalid if they are still queued to be registered.
/// Those ids are still correct, but they are not usable in every context yet.
#[inline]
pub fn is_id_valid(&self, id: ComponentId) -> bool {
self.components.get(id.0).is_some_and(Option::is_some)
}
/// Type-erased equivalent of [`Components::valid_component_id()`].
#[inline]
pub fn get_valid_id(&self, type_id: TypeId) -> Option<ComponentId> {
self.indices.get(&type_id).copied()
}
/// Returns the [`ComponentId`] of the given [`Component`] type `T` if it is fully registered.
/// If you want to include queued registration, see [`Components::component_id()`].
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// let mut world = World::new();
///
/// #[derive(Component)]
/// struct ComponentA;
///
/// let component_a_id = world.register_component::<ComponentA>();
///
/// assert_eq!(component_a_id, world.components().valid_component_id::<ComponentA>().unwrap())
/// ```
///
/// # See also
///
/// * [`Components::get_valid_id()`]
/// * [`Components::valid_resource_id()`]
/// * [`World::component_id()`](crate::world::World::component_id)
#[inline]
pub fn valid_component_id<T: Component>(&self) -> Option<ComponentId> {
self.get_valid_id(TypeId::of::<T>())
}
/// Type-erased equivalent of [`Components::valid_resource_id()`].
#[inline]
pub fn get_valid_resource_id(&self, type_id: TypeId) -> Option<ComponentId> {
self.resource_indices.get(&type_id).copied()
}
/// Returns the [`ComponentId`] of the given [`Resource`] type `T` if it is fully registered.
/// If you want to include queued registration, see [`Components::resource_id()`].
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// let mut world = World::new();
///
/// #[derive(Resource, Default)]
/// struct ResourceA;
///
/// let resource_a_id = world.init_resource::<ResourceA>();
///
/// assert_eq!(resource_a_id, world.components().valid_resource_id::<ResourceA>().unwrap())
/// ```
///
/// # See also
///
/// * [`Components::valid_component_id()`]
/// * [`Components::get_resource_id()`]
#[inline]
pub fn valid_resource_id<T: Resource>(&self) -> Option<ComponentId> {
self.get_valid_resource_id(TypeId::of::<T>())
}
/// Type-erased equivalent of [`Components::component_id()`].
#[inline]
pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> {
self.indices.get(&type_id).copied().or_else(|| {
self.queued
.read()
.unwrap_or_else(PoisonError::into_inner)
.components
.get(&type_id)
.map(|queued| queued.id)
})
}
/// Returns the [`ComponentId`] of the given [`Component`] type `T`.
///
/// The returned `ComponentId` is specific to the `Components` instance
/// it was retrieved from and should not be used with another `Components`
/// instance.
///
/// Returns [`None`] if the `Component` type has not yet been initialized using
/// [`ComponentsRegistrator::register_component()`](super::ComponentsRegistrator::register_component) or
/// [`ComponentsQueuedRegistrator::queue_register_component()`](super::ComponentsQueuedRegistrator::queue_register_component).
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// let mut world = World::new();
///
/// #[derive(Component)]
/// struct ComponentA;
///
/// let component_a_id = world.register_component::<ComponentA>();
///
/// assert_eq!(component_a_id, world.components().component_id::<ComponentA>().unwrap())
/// ```
///
/// # See also
///
/// * [`Components::get_id()`]
/// * [`Components::resource_id()`]
/// * [`World::component_id()`](crate::world::World::component_id)
#[inline]
pub fn component_id<T: Component>(&self) -> Option<ComponentId> {
self.get_id(TypeId::of::<T>())
}
/// Type-erased equivalent of [`Components::resource_id()`].
#[inline]
pub fn get_resource_id(&self, type_id: TypeId) -> Option<ComponentId> {
self.resource_indices.get(&type_id).copied().or_else(|| {
self.queued
.read()
.unwrap_or_else(PoisonError::into_inner)
.resources
.get(&type_id)
.map(|queued| queued.id)
})
}
/// Returns the [`ComponentId`] of the given [`Resource`] type `T`.
///
/// The returned `ComponentId` is specific to the `Components` instance
/// it was retrieved from and should not be used with another `Components`
/// instance.
///
/// Returns [`None`] if the `Resource` type has not yet been initialized using
/// [`ComponentsRegistrator::register_resource()`](super::ComponentsRegistrator::register_resource) or
/// [`ComponentsQueuedRegistrator::queue_register_resource()`](super::ComponentsQueuedRegistrator::queue_register_resource).
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// let mut world = World::new();
///
/// #[derive(Resource, Default)]
/// struct ResourceA;
///
/// let resource_a_id = world.init_resource::<ResourceA>();
///
/// assert_eq!(resource_a_id, world.components().resource_id::<ResourceA>().unwrap())
/// ```
///
/// # See also
///
/// * [`Components::component_id()`]
/// * [`Components::get_resource_id()`]
#[inline]
pub fn resource_id<T: Resource>(&self) -> Option<ComponentId> {
self.get_resource_id(TypeId::of::<T>())
}
/// # Safety
///
/// The [`ComponentDescriptor`] must match the [`TypeId`].
/// The [`ComponentId`] must be unique.
/// The [`TypeId`] and [`ComponentId`] must not be registered or queued.
#[inline]
pub(super) unsafe fn register_resource_unchecked(
&mut self,
type_id: TypeId,
component_id: ComponentId,
descriptor: ComponentDescriptor,
) {
// SAFETY: ensured by caller
unsafe {
self.register_component_inner(component_id, descriptor);
}
let prev = self.resource_indices.insert(type_id, component_id);
debug_assert!(prev.is_none());
}
/// Gets an iterator over all components fully registered with this instance.
pub fn iter_registered(&self) -> impl Iterator<Item = &ComponentInfo> + '_ {
self.components.iter().filter_map(Option::as_ref)
}
}

View File

@ -0,0 +1,758 @@
//! Types for declaring and storing [`Component`]s.
mod clone;
mod info;
mod register;
mod required;
mod tick;
pub use clone::*;
pub use info::*;
pub use register::*;
pub use required::*;
pub use tick::*;
use crate::{
entity::EntityMapper,
lifecycle::ComponentHook,
system::{Local, SystemParam},
world::{FromWorld, World},
};
use alloc::vec::Vec;
pub use bevy_ecs_macros::Component;
use core::{fmt::Debug, marker::PhantomData, ops::Deref};
/// A data type that can be used to store data for an [entity].
///
/// `Component` is a [derivable trait]: this means that a data type can implement it by applying a `#[derive(Component)]` attribute to it.
/// However, components must always satisfy the `Send + Sync + 'static` trait bounds.
///
/// [entity]: crate::entity
/// [derivable trait]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html
///
/// # Examples
///
/// Components can take many forms: they are usually structs, but can also be of every other kind of data type, like enums or zero sized types.
/// The following examples show how components are laid out in code.
///
/// ```
/// # use bevy_ecs::component::Component;
/// # struct Color;
/// #
/// // A component can contain data...
/// #[derive(Component)]
/// struct LicensePlate(String);
///
/// // ... but it can also be a zero-sized marker.
/// #[derive(Component)]
/// struct Car;
///
/// // Components can also be structs with named fields...
/// #[derive(Component)]
/// struct VehiclePerformance {
/// acceleration: f32,
/// top_speed: f32,
/// handling: f32,
/// }
///
/// // ... or enums.
/// #[derive(Component)]
/// enum WheelCount {
/// Two,
/// Three,
/// Four,
/// }
/// ```
///
/// # Component and data access
///
/// Components can be marked as immutable by adding the `#[component(immutable)]`
/// attribute when using the derive macro.
/// See the documentation for [`ComponentMutability`] for more details around this
/// feature.
///
/// See the [`entity`] module level documentation to learn how to add or remove components from an entity.
///
/// See the documentation for [`Query`] to learn how to access component data from a system.
///
/// [`entity`]: crate::entity#usage
/// [`Query`]: crate::system::Query
/// [`ComponentMutability`]: crate::component::ComponentMutability
///
/// # Choosing a storage type
///
/// Components can be stored in the world using different strategies with their own performance implications.
/// By default, components are added to the [`Table`] storage, which is optimized for query iteration.
///
/// Alternatively, components can be added to the [`SparseSet`] storage, which is optimized for component insertion and removal.
/// This is achieved by adding an additional `#[component(storage = "SparseSet")]` attribute to the derive one:
///
/// ```
/// # use bevy_ecs::component::Component;
/// #
/// #[derive(Component)]
/// #[component(storage = "SparseSet")]
/// struct ComponentA;
/// ```
///
/// [`Table`]: crate::storage::Table
/// [`SparseSet`]: crate::storage::SparseSet
///
/// # Required Components
///
/// Components can specify Required Components. If some [`Component`] `A` requires [`Component`] `B`, then when `A` is inserted,
/// `B` will _also_ be initialized and inserted (if it was not manually specified).
///
/// The [`Default`] constructor will be used to initialize the component, by default:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// #[require(B)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// # let mut world = World::default();
/// // This will implicitly also insert B with the Default constructor
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
///
/// // This will _not_ implicitly insert B, because it was already provided
/// world.spawn((A, B(11)));
/// ```
///
/// Components can have more than one required component:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// #[require(B, C)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// #[require(C)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // This will implicitly also insert B and C with their Default constructors
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(0), world.entity(id).get::<C>().unwrap());
/// ```
///
/// You can define inline component values that take the following forms:
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// #[require(
/// B(1), // tuple structs
/// C { // named-field structs
/// x: 1,
/// ..Default::default()
/// },
/// D::One, // enum variants
/// E::ONE, // associated consts
/// F::new(1) // constructors
/// )]
/// struct A;
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct B(u8);
///
/// #[derive(Component, PartialEq, Eq, Debug, Default)]
/// struct C {
/// x: u8,
/// y: u8,
/// }
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// enum D {
/// Zero,
/// One,
/// }
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct E(u8);
///
/// impl E {
/// pub const ONE: Self = Self(1);
/// }
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct F(u8);
///
/// impl F {
/// fn new(value: u8) -> Self {
/// Self(value)
/// }
/// }
///
/// # let mut world = World::default();
/// let id = world.spawn(A).id();
/// assert_eq!(&B(1), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C { x: 1, y: 0 }, world.entity(id).get::<C>().unwrap());
/// assert_eq!(&D::One, world.entity(id).get::<D>().unwrap());
/// assert_eq!(&E(1), world.entity(id).get::<E>().unwrap());
/// assert_eq!(&F(1), world.entity(id).get::<F>().unwrap());
/// ````
///
///
/// You can also define arbitrary expressions by using `=`
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// #[require(C = init_c())]
/// struct A;
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// #[require(C = C(20))]
/// struct B;
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct C(usize);
///
/// fn init_c() -> C {
/// C(10)
/// }
///
/// # let mut world = World::default();
/// // This will implicitly also insert C with the init_c() constructor
/// let id = world.spawn(A).id();
/// assert_eq!(&C(10), world.entity(id).get::<C>().unwrap());
///
/// // This will implicitly also insert C with the `|| C(20)` constructor closure
/// let id = world.spawn(B).id();
/// assert_eq!(&C(20), world.entity(id).get::<C>().unwrap());
/// ```
///
/// Required components are _recursive_. This means, if a Required Component has required components,
/// those components will _also_ be inserted if they are missing:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// #[require(B)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// #[require(C)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // This will implicitly also insert B and C with their Default constructors
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(0), world.entity(id).get::<C>().unwrap());
/// ```
///
/// Note that cycles in the "component require tree" will result in stack overflows when attempting to
/// insert a component.
///
/// This "multiple inheritance" pattern does mean that it is possible to have duplicate requires for a given type
/// at different levels of the inheritance tree:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct X(usize);
///
/// #[derive(Component, Default)]
/// #[require(X(1))]
/// struct Y;
///
/// #[derive(Component)]
/// #[require(
/// Y,
/// X(2),
/// )]
/// struct Z;
///
/// # let mut world = World::default();
/// // In this case, the x2 constructor is used for X
/// let id = world.spawn(Z).id();
/// assert_eq!(2, world.entity(id).get::<X>().unwrap().0);
/// ```
///
/// In general, this shouldn't happen often, but when it does the algorithm for choosing the constructor from the tree is simple and predictable:
/// 1. A constructor from a direct `#[require()]`, if one exists, is selected with priority.
/// 2. Otherwise, perform a Depth First Search on the tree of requirements and select the first one found.
///
/// From a user perspective, just think about this as the following:
/// 1. Specifying a required component constructor for Foo directly on a spawned component Bar will result in that constructor being used (and overriding existing constructors lower in the inheritance tree). This is the classic "inheritance override" behavior people expect.
/// 2. For cases where "multiple inheritance" results in constructor clashes, Components should be listed in "importance order". List a component earlier in the requirement list to initialize its inheritance tree earlier.
///
/// ## Registering required components at runtime
///
/// In most cases, required components should be registered using the `require` attribute as shown above.
/// However, in some cases, it may be useful to register required components at runtime.
///
/// This can be done through [`World::register_required_components`] or [`World::register_required_components_with`]
/// for the [`Default`] and custom constructors respectively:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // Register B as required by A and C as required by B.
/// world.register_required_components::<A, B>();
/// world.register_required_components_with::<B, C>(|| C(2));
///
/// // This will implicitly also insert B with its Default constructor
/// // and C with the custom constructor defined by B.
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(2), world.entity(id).get::<C>().unwrap());
/// ```
///
/// Similar rules as before apply to duplicate requires fer a given type at different levels
/// of the inheritance tree. `A` requiring `C` directly would take precedence over indirectly
/// requiring it through `A` requiring `B` and `B` requiring `C`.
///
/// Unlike with the `require` attribute, directly requiring the same component multiple times
/// for the same component will result in a panic. This is done to prevent conflicting constructors
/// and confusing ordering dependencies.
///
/// Note that requirements must currently be registered before the requiring component is inserted
/// into the world for the first time. Registering requirements after this will lead to a panic.
///
/// # Relationships between Entities
///
/// Sometimes it is useful to define relationships between entities. A common example is the
/// parent / child relationship. Since Components are how data is stored for Entities, one might
/// naturally think to create a Component which has a field of type [`Entity`].
///
/// To facilitate this pattern, Bevy provides the [`Relationship`](`crate::relationship::Relationship`)
/// trait. You can derive the [`Relationship`](`crate::relationship::Relationship`) and
/// [`RelationshipTarget`](`crate::relationship::RelationshipTarget`) traits in addition to the
/// Component trait in order to implement data driven relationships between entities, see the trait
/// docs for more details.
///
/// In addition, Bevy provides canonical implementations of the parent / child relationship via the
/// [`ChildOf`](crate::hierarchy::ChildOf) [`Relationship`](crate::relationship::Relationship) and
/// the [`Children`](crate::hierarchy::Children)
/// [`RelationshipTarget`](crate::relationship::RelationshipTarget).
///
/// # Adding component's hooks
///
/// See [`ComponentHooks`] for a detailed explanation of component's hooks.
///
/// Alternatively to the example shown in [`ComponentHooks`]' documentation, hooks can be configured using following attributes:
/// - `#[component(on_add = on_add_function)]`
/// - `#[component(on_insert = on_insert_function)]`
/// - `#[component(on_replace = on_replace_function)]`
/// - `#[component(on_remove = on_remove_function)]`
///
/// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::lifecycle::HookContext;
/// # use bevy_ecs::world::DeferredWorld;
/// # use bevy_ecs::entity::Entity;
/// # use bevy_ecs::component::ComponentId;
/// # use core::panic::Location;
/// #
/// #[derive(Component)]
/// #[component(on_add = my_on_add_hook)]
/// #[component(on_insert = my_on_insert_hook)]
/// // Another possible way of configuring hooks:
/// // #[component(on_add = my_on_add_hook, on_insert = my_on_insert_hook)]
/// //
/// // We don't have a replace or remove hook, so we can leave them out:
/// // #[component(on_replace = my_on_replace_hook, on_remove = my_on_remove_hook)]
/// struct ComponentA;
///
/// fn my_on_add_hook(world: DeferredWorld, context: HookContext) {
/// // ...
/// }
///
/// // You can also destructure items directly in the signature
/// fn my_on_insert_hook(world: DeferredWorld, HookContext { caller, .. }: HookContext) {
/// // ...
/// }
/// ```
///
/// This also supports function calls that yield closures
///
/// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::lifecycle::HookContext;
/// # use bevy_ecs::world::DeferredWorld;
/// #
/// #[derive(Component)]
/// #[component(on_add = my_msg_hook("hello"))]
/// #[component(on_despawn = my_msg_hook("yoink"))]
/// struct ComponentA;
///
/// // a hook closure generating function
/// fn my_msg_hook(message: &'static str) -> impl Fn(DeferredWorld, HookContext) {
/// move |_world, _ctx| {
/// println!("{message}");
/// }
/// }
///
/// ```
/// # Setting the clone behavior
///
/// You can specify how the [`Component`] is cloned when deriving it.
///
/// Your options are the functions and variants of [`ComponentCloneBehavior`]
/// See [Handlers section of `EntityClonerBuilder`](crate::entity::EntityClonerBuilder#handlers) to understand how this affects handler priority.
/// ```
/// # use bevy_ecs::prelude::*;
///
/// #[derive(Component)]
/// #[component(clone_behavior = Ignore)]
/// struct MyComponent;
///
/// ```
///
/// # Implementing the trait for foreign types
///
/// As a consequence of the [orphan rule], it is not possible to separate into two different crates the implementation of `Component` from the definition of a type.
/// This means that it is not possible to directly have a type defined in a third party library as a component.
/// This important limitation can be easily worked around using the [newtype pattern]:
/// this makes it possible to locally define and implement `Component` for a tuple struct that wraps the foreign type.
/// The following example gives a demonstration of this pattern.
///
/// ```
/// // `Component` is defined in the `bevy_ecs` crate.
/// use bevy_ecs::component::Component;
///
/// // `Duration` is defined in the `std` crate.
/// use std::time::Duration;
///
/// // It is not possible to implement `Component` for `Duration` from this position, as they are
/// // both foreign items, defined in an external crate. However, nothing prevents to define a new
/// // `Cooldown` type that wraps `Duration`. As `Cooldown` is defined in a local crate, it is
/// // possible to implement `Component` for it.
/// #[derive(Component)]
/// struct Cooldown(Duration);
/// ```
///
/// [orphan rule]: https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type
/// [newtype pattern]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
///
/// # `!Sync` Components
/// A `!Sync` type cannot implement `Component`. However, it is possible to wrap a `Send` but not `Sync`
/// type in [`SyncCell`] or the currently unstable [`Exclusive`] to make it `Sync`. This forces only
/// having mutable access (`&mut T` only, never `&T`), but makes it safe to reference across multiple
/// threads.
///
/// This will fail to compile since `RefCell` is `!Sync`.
/// ```compile_fail
/// # use std::cell::RefCell;
/// # use bevy_ecs::component::Component;
/// #[derive(Component)]
/// struct NotSync {
/// counter: RefCell<usize>,
/// }
/// ```
///
/// This will compile since the `RefCell` is wrapped with `SyncCell`.
/// ```
/// # use std::cell::RefCell;
/// # use bevy_ecs::component::Component;
/// use bevy_platform::cell::SyncCell;
///
/// // This will compile.
/// #[derive(Component)]
/// struct ActuallySync {
/// counter: SyncCell<RefCell<usize>>,
/// }
/// ```
///
/// [`SyncCell`]: bevy_platform::cell::SyncCell
/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html
/// [`ComponentHooks`]: crate::lifecycle::ComponentHooks
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a `Component`",
label = "invalid `Component`",
note = "consider annotating `{Self}` with `#[derive(Component)]`"
)]
pub trait Component: Send + Sync + 'static {
/// A constant indicating the storage type used for this component.
const STORAGE_TYPE: StorageType;
/// A marker type to assist Bevy with determining if this component is
/// mutable, or immutable. Mutable components will have [`Component<Mutability = Mutable>`],
/// while immutable components will instead have [`Component<Mutability = Immutable>`].
///
/// * For a component to be mutable, this type must be [`Mutable`].
/// * For a component to be immutable, this type must be [`Immutable`].
type Mutability: ComponentMutability;
/// Gets the `on_add` [`ComponentHook`] for this [`Component`] if one is defined.
fn on_add() -> Option<ComponentHook> {
None
}
/// Gets the `on_insert` [`ComponentHook`] for this [`Component`] if one is defined.
fn on_insert() -> Option<ComponentHook> {
None
}
/// Gets the `on_replace` [`ComponentHook`] for this [`Component`] if one is defined.
fn on_replace() -> Option<ComponentHook> {
None
}
/// Gets the `on_remove` [`ComponentHook`] for this [`Component`] if one is defined.
fn on_remove() -> Option<ComponentHook> {
None
}
/// Gets the `on_despawn` [`ComponentHook`] for this [`Component`] if one is defined.
fn on_despawn() -> Option<ComponentHook> {
None
}
/// Registers required components.
fn register_required_components(
_component_id: ComponentId,
_components: &mut ComponentsRegistrator,
_required_components: &mut RequiredComponents,
_inheritance_depth: u16,
_recursion_check_stack: &mut Vec<ComponentId>,
) {
}
/// Called when registering this component, allowing to override clone function (or disable cloning altogether) for this component.
///
/// See [Handlers section of `EntityClonerBuilder`](crate::entity::EntityClonerBuilder#handlers) to understand how this affects handler priority.
#[inline]
fn clone_behavior() -> ComponentCloneBehavior {
ComponentCloneBehavior::Default
}
/// Maps the entities on this component using the given [`EntityMapper`]. This is used to remap entities in contexts like scenes and entity cloning.
/// When deriving [`Component`], this is populated by annotating fields containing entities with `#[entities]`
///
/// ```
/// # use bevy_ecs::{component::Component, entity::Entity};
/// #[derive(Component)]
/// struct Inventory {
/// #[entities]
/// items: Vec<Entity>
/// }
/// ```
///
/// Fields with `#[entities]` must implement [`MapEntities`](crate::entity::MapEntities).
///
/// Bevy provides various implementations of [`MapEntities`](crate::entity::MapEntities), so that arbitrary combinations like these are supported with `#[entities]`:
///
/// ```rust
/// # use bevy_ecs::{component::Component, entity::Entity};
/// #[derive(Component)]
/// struct Inventory {
/// #[entities]
/// items: Vec<Option<Entity>>
/// }
/// ```
///
/// You might need more specialized logic. A likely cause of this is your component contains collections of entities that
/// don't implement [`MapEntities`](crate::entity::MapEntities). In that case, you can annotate your component with
/// `#[component(map_entities)]`. Using this attribute, you must implement `MapEntities` for the
/// component itself, and this method will simply call that implementation.
///
/// ```
/// # use bevy_ecs::{component::Component, entity::{Entity, MapEntities, EntityMapper}};
/// # use std::collections::HashMap;
/// #[derive(Component)]
/// #[component(map_entities)]
/// struct Inventory {
/// items: HashMap<Entity, usize>
/// }
///
/// impl MapEntities for Inventory {
/// fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
/// self.items = self.items
/// .drain()
/// .map(|(id, count)|(entity_mapper.get_mapped(id), count))
/// .collect();
/// }
/// }
/// # let a = Entity::from_bits(0x1_0000_0001);
/// # let b = Entity::from_bits(0x1_0000_0002);
/// # let mut inv = Inventory { items: Default::default() };
/// # inv.items.insert(a, 10);
/// # <Inventory as Component>::map_entities(&mut inv, &mut (a,b));
/// # assert_eq!(inv.items.get(&b), Some(&10));
/// ````
///
/// Alternatively, you can specify the path to a function with `#[component(map_entities = function_path)]`, similar to component hooks.
/// In this case, the inputs of the function should mirror the inputs to this method, with the second parameter being generic.
///
/// ```
/// # use bevy_ecs::{component::Component, entity::{Entity, MapEntities, EntityMapper}};
/// # use std::collections::HashMap;
/// #[derive(Component)]
/// #[component(map_entities = map_the_map)]
/// // Also works: map_the_map::<M> or map_the_map::<_>
/// struct Inventory {
/// items: HashMap<Entity, usize>
/// }
///
/// fn map_the_map<M: EntityMapper>(inv: &mut Inventory, entity_mapper: &mut M) {
/// inv.items = inv.items
/// .drain()
/// .map(|(id, count)|(entity_mapper.get_mapped(id), count))
/// .collect();
/// }
/// # let a = Entity::from_bits(0x1_0000_0001);
/// # let b = Entity::from_bits(0x1_0000_0002);
/// # let mut inv = Inventory { items: Default::default() };
/// # inv.items.insert(a, 10);
/// # <Inventory as Component>::map_entities(&mut inv, &mut (a,b));
/// # assert_eq!(inv.items.get(&b), Some(&10));
/// ````
///
/// You can use the turbofish (`::<A,B,C>`) to specify parameters when a function is generic, using either M or _ for the type of the mapper parameter.
#[inline]
fn map_entities<E: EntityMapper>(_this: &mut Self, _mapper: &mut E) {}
}
mod private {
pub trait Seal {}
}
/// The mutability option for a [`Component`]. This can either be:
/// * [`Mutable`]
/// * [`Immutable`]
///
/// This is controlled through either [`Component::Mutability`] or `#[component(immutable)]`
/// when using the derive macro.
///
/// Immutable components are guaranteed to never have an exclusive reference,
/// `&mut ...`, created while inserted onto an entity.
/// In all other ways, they are identical to mutable components.
/// This restriction allows hooks to observe all changes made to an immutable
/// component, effectively turning the `Insert` and `Replace` hooks into a
/// `OnMutate` hook.
/// This is not practical for mutable components, as the runtime cost of invoking
/// a hook for every exclusive reference created would be far too high.
///
/// # Examples
///
/// ```rust
/// # use bevy_ecs::component::Component;
/// #
/// #[derive(Component)]
/// #[component(immutable)]
/// struct ImmutableFoo;
/// ```
pub trait ComponentMutability: private::Seal + 'static {
/// Boolean to indicate if this mutability setting implies a mutable or immutable
/// component.
const MUTABLE: bool;
}
/// Parameter indicating a [`Component`] is immutable.
///
/// See [`ComponentMutability`] for details.
pub struct Immutable;
impl private::Seal for Immutable {}
impl ComponentMutability for Immutable {
const MUTABLE: bool = false;
}
/// Parameter indicating a [`Component`] is mutable.
///
/// See [`ComponentMutability`] for details.
pub struct Mutable;
impl private::Seal for Mutable {}
impl ComponentMutability for Mutable {
const MUTABLE: bool = true;
}
/// The storage used for a specific component type.
///
/// # Examples
/// The [`StorageType`] for a component is configured via the derive attribute
///
/// ```
/// # use bevy_ecs::{prelude::*, component::*};
/// #[derive(Component)]
/// #[component(storage = "SparseSet")]
/// struct A;
/// ```
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub enum StorageType {
/// Provides fast and cache-friendly iteration, but slower addition and removal of components.
/// This is the default storage type.
#[default]
Table,
/// Provides fast addition and removal of components, but slower iteration.
SparseSet,
}
/// A [`SystemParam`] that provides access to the [`ComponentId`] for a specific component type.
///
/// # Example
/// ```
/// # use bevy_ecs::{system::Local, component::{Component, ComponentId, ComponentIdFor}};
/// #[derive(Component)]
/// struct Player;
/// fn my_system(component_id: ComponentIdFor<Player>) {
/// let component_id: ComponentId = component_id.get();
/// // ...
/// }
/// ```
#[derive(SystemParam)]
pub struct ComponentIdFor<'s, T: Component>(Local<'s, InitComponentId<T>>);
impl<T: Component> ComponentIdFor<'_, T> {
/// Gets the [`ComponentId`] for the type `T`.
#[inline]
pub fn get(&self) -> ComponentId {
**self
}
}
impl<T: Component> Deref for ComponentIdFor<'_, T> {
type Target = ComponentId;
fn deref(&self) -> &Self::Target {
&self.0.component_id
}
}
impl<T: Component> From<ComponentIdFor<'_, T>> for ComponentId {
#[inline]
fn from(to_component_id: ComponentIdFor<T>) -> ComponentId {
*to_component_id
}
}
/// Initializes the [`ComponentId`] for a specific type when used with [`FromWorld`].
struct InitComponentId<T: Component> {
component_id: ComponentId,
marker: PhantomData<T>,
}
impl<T: Component> FromWorld for InitComponentId<T> {
fn from_world(world: &mut World) -> Self {
Self {
component_id: world.register_component::<T>(),
marker: PhantomData,
}
}
}

View File

@ -0,0 +1,679 @@
use alloc::{boxed::Box, vec::Vec};
use bevy_platform::sync::PoisonError;
use bevy_utils::TypeIdMap;
use core::any::Any;
use core::ops::DerefMut;
use core::{any::TypeId, fmt::Debug, ops::Deref};
use crate::query::DebugCheckedUnwrap as _;
use crate::{
component::{
Component, ComponentDescriptor, ComponentId, Components, RequiredComponents, StorageType,
},
resource::Resource,
};
/// Generates [`ComponentId`]s.
#[derive(Debug, Default)]
pub struct ComponentIds {
next: bevy_platform::sync::atomic::AtomicUsize,
}
impl ComponentIds {
/// Peeks the next [`ComponentId`] to be generated without generating it.
pub fn peek(&self) -> ComponentId {
ComponentId(
self.next
.load(bevy_platform::sync::atomic::Ordering::Relaxed),
)
}
/// Generates and returns the next [`ComponentId`].
pub fn next(&self) -> ComponentId {
ComponentId(
self.next
.fetch_add(1, bevy_platform::sync::atomic::Ordering::Relaxed),
)
}
/// Peeks the next [`ComponentId`] to be generated without generating it.
pub fn peek_mut(&mut self) -> ComponentId {
ComponentId(*self.next.get_mut())
}
/// Generates and returns the next [`ComponentId`].
pub fn next_mut(&mut self) -> ComponentId {
let id = self.next.get_mut();
let result = ComponentId(*id);
*id += 1;
result
}
/// Returns the number of [`ComponentId`]s generated.
pub fn len(&self) -> usize {
self.peek().0
}
/// Returns true if and only if no ids have been generated.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
/// A [`Components`] wrapper that enables additional features, like registration.
pub struct ComponentsRegistrator<'w> {
components: &'w mut Components,
ids: &'w mut ComponentIds,
}
impl Deref for ComponentsRegistrator<'_> {
type Target = Components;
fn deref(&self) -> &Self::Target {
self.components
}
}
impl DerefMut for ComponentsRegistrator<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.components
}
}
impl<'w> ComponentsRegistrator<'w> {
/// Constructs a new [`ComponentsRegistrator`].
///
/// # Safety
///
/// The [`Components`] and [`ComponentIds`] must match.
/// For example, they must be from the same world.
pub unsafe fn new(components: &'w mut Components, ids: &'w mut ComponentIds) -> Self {
Self { components, ids }
}
/// Converts this [`ComponentsRegistrator`] into a [`ComponentsQueuedRegistrator`].
/// This is intended for use to pass this value to a function that requires [`ComponentsQueuedRegistrator`].
/// It is generally not a good idea to queue a registration when you can instead register directly on this type.
pub fn as_queued(&self) -> ComponentsQueuedRegistrator<'_> {
// SAFETY: ensured by the caller that created self.
unsafe { ComponentsQueuedRegistrator::new(self.components, self.ids) }
}
/// Applies every queued registration.
/// This ensures that every valid [`ComponentId`] is registered,
/// enabling retrieving [`ComponentInfo`](super::ComponentInfo), etc.
pub fn apply_queued_registrations(&mut self) {
if !self.any_queued_mut() {
return;
}
// Note:
//
// This is not just draining the queue. We need to empty the queue without removing the information from `Components`.
// If we drained directly, we could break invariance.
//
// For example, say `ComponentA` and `ComponentB` are queued, and `ComponentA` requires `ComponentB`.
// If we drain directly, and `ComponentA` was the first to be registered, then, when `ComponentA`
// registers `ComponentB` in `Component::register_required_components`,
// `Components` will not know that `ComponentB` was queued
// (since it will have been drained from the queue.)
// If that happened, `Components` would assign a new `ComponentId` to `ComponentB`
// which would be *different* than the id it was assigned in the queue.
// Then, when the drain iterator gets to `ComponentB`,
// it would be unsafely registering `ComponentB`, which is already registered.
//
// As a result, we need to pop from each queue one by one instead of draining.
// components
while let Some(registrator) = {
let queued = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner);
queued.components.keys().next().copied().map(|type_id| {
// SAFETY: the id just came from a valid iterator.
unsafe { queued.components.remove(&type_id).debug_checked_unwrap() }
})
} {
registrator.register(self);
}
// resources
while let Some(registrator) = {
let queued = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner);
queued.resources.keys().next().copied().map(|type_id| {
// SAFETY: the id just came from a valid iterator.
unsafe { queued.resources.remove(&type_id).debug_checked_unwrap() }
})
} {
registrator.register(self);
}
// dynamic
let queued = &mut self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner);
if !queued.dynamic_registrations.is_empty() {
for registrator in core::mem::take(&mut queued.dynamic_registrations) {
registrator.register(self);
}
}
}
/// Registers a [`Component`] of type `T` with this instance.
/// If a component of this type has already been registered, this will return
/// the ID of the pre-existing component.
///
/// # See also
///
/// * [`Components::component_id()`]
/// * [`ComponentsRegistrator::register_component_with_descriptor()`]
#[inline]
pub fn register_component<T: Component>(&mut self) -> ComponentId {
self.register_component_checked::<T>(&mut Vec::new())
}
/// Same as [`Self::register_component_unchecked`] but keeps a checks for safety.
#[inline]
pub(super) fn register_component_checked<T: Component>(
&mut self,
recursion_check_stack: &mut Vec<ComponentId>,
) -> ComponentId {
let type_id = TypeId::of::<T>();
if let Some(id) = self.indices.get(&type_id) {
return *id;
}
if let Some(registrator) = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner)
.components
.remove(&type_id)
{
// If we are trying to register something that has already been queued, we respect the queue.
// Just like if we are trying to register something that already is, we respect the first registration.
return registrator.register(self);
}
let id = self.ids.next_mut();
// SAFETY: The component is not currently registered, and the id is fresh.
unsafe {
self.register_component_unchecked::<T>(recursion_check_stack, id);
}
id
}
/// # Safety
///
/// Neither this component, nor its id may be registered or queued. This must be a new registration.
#[inline]
unsafe fn register_component_unchecked<T: Component>(
&mut self,
recursion_check_stack: &mut Vec<ComponentId>,
id: ComponentId,
) {
// SAFETY: ensured by caller.
unsafe {
self.register_component_inner(id, ComponentDescriptor::new::<T>());
}
let type_id = TypeId::of::<T>();
let prev = self.indices.insert(type_id, id);
debug_assert!(prev.is_none());
let mut required_components = RequiredComponents::default();
T::register_required_components(
id,
self,
&mut required_components,
0,
recursion_check_stack,
);
// SAFETY: we just inserted it in `register_component_inner`
let info = unsafe {
&mut self
.components
.components
.get_mut(id.0)
.debug_checked_unwrap()
.as_mut()
.debug_checked_unwrap()
};
info.hooks.update_from_component::<T>();
info.required_components = required_components;
}
/// Registers a component described by `descriptor`.
///
/// # Note
///
/// If this method is called multiple times with identical descriptors, a distinct [`ComponentId`]
/// will be created for each one.
///
/// # See also
///
/// * [`Components::component_id()`]
/// * [`ComponentsRegistrator::register_component()`]
#[inline]
pub fn register_component_with_descriptor(
&mut self,
descriptor: ComponentDescriptor,
) -> ComponentId {
let id = self.ids.next_mut();
// SAFETY: The id is fresh.
unsafe {
self.register_component_inner(id, descriptor);
}
id
}
/// Registers a [`Resource`] of type `T` with this instance.
/// If a resource of this type has already been registered, this will return
/// the ID of the pre-existing resource.
///
/// # See also
///
/// * [`Components::resource_id()`]
/// * [`ComponentsRegistrator::register_resource_with_descriptor()`]
#[inline]
pub fn register_resource<T: Resource>(&mut self) -> ComponentId {
// SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe {
self.register_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new_resource::<T>()
})
}
}
/// Registers a [non-send resource](crate::system::NonSend) of type `T` with this instance.
/// If a resource of this type has already been registered, this will return
/// the ID of the pre-existing resource.
#[inline]
pub fn register_non_send<T: Any>(&mut self) -> ComponentId {
// SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe {
self.register_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new_non_send::<T>(StorageType::default())
})
}
}
/// Same as [`Components::register_resource_unchecked`] but handles safety.
///
/// # Safety
///
/// The [`ComponentDescriptor`] must match the [`TypeId`].
#[inline]
unsafe fn register_resource_with(
&mut self,
type_id: TypeId,
descriptor: impl FnOnce() -> ComponentDescriptor,
) -> ComponentId {
if let Some(id) = self.resource_indices.get(&type_id) {
return *id;
}
if let Some(registrator) = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner)
.resources
.remove(&type_id)
{
// If we are trying to register something that has already been queued, we respect the queue.
// Just like if we are trying to register something that already is, we respect the first registration.
return registrator.register(self);
}
let id = self.ids.next_mut();
// SAFETY: The resource is not currently registered, the id is fresh, and the [`ComponentDescriptor`] matches the [`TypeId`]
unsafe {
self.register_resource_unchecked(type_id, id, descriptor());
}
id
}
/// Registers a [`Resource`] described by `descriptor`.
///
/// # Note
///
/// If this method is called multiple times with identical descriptors, a distinct [`ComponentId`]
/// will be created for each one.
///
/// # See also
///
/// * [`Components::resource_id()`]
/// * [`ComponentsRegistrator::register_resource()`]
#[inline]
pub fn register_resource_with_descriptor(
&mut self,
descriptor: ComponentDescriptor,
) -> ComponentId {
let id = self.ids.next_mut();
// SAFETY: The id is fresh.
unsafe {
self.register_component_inner(id, descriptor);
}
id
}
}
/// A queued component registration.
pub(super) struct QueuedRegistration {
pub(super) registrator:
Box<dyn FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor)>,
pub(super) id: ComponentId,
pub(super) descriptor: ComponentDescriptor,
}
impl QueuedRegistration {
/// Creates the [`QueuedRegistration`].
///
/// # Safety
///
/// [`ComponentId`] must be unique.
unsafe fn new(
id: ComponentId,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> Self {
Self {
registrator: Box::new(func),
id,
descriptor,
}
}
/// Performs the registration, returning the now valid [`ComponentId`].
pub(super) fn register(self, registrator: &mut ComponentsRegistrator) -> ComponentId {
(self.registrator)(registrator, self.id, self.descriptor);
self.id
}
}
/// Allows queuing components to be registered.
#[derive(Default)]
pub struct QueuedComponents {
pub(super) components: TypeIdMap<QueuedRegistration>,
pub(super) resources: TypeIdMap<QueuedRegistration>,
pub(super) dynamic_registrations: Vec<QueuedRegistration>,
}
impl Debug for QueuedComponents {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let components = self
.components
.iter()
.map(|(type_id, queued)| (type_id, queued.id))
.collect::<Vec<_>>();
let resources = self
.resources
.iter()
.map(|(type_id, queued)| (type_id, queued.id))
.collect::<Vec<_>>();
let dynamic_registrations = self
.dynamic_registrations
.iter()
.map(|queued| queued.id)
.collect::<Vec<_>>();
write!(f, "components: {components:?}, resources: {resources:?}, dynamic_registrations: {dynamic_registrations:?}")
}
}
/// A type that enables queuing registration in [`Components`].
///
/// # Note
///
/// These queued registrations return [`ComponentId`]s.
/// These ids are not yet valid, but they will become valid
/// when either [`ComponentsRegistrator::apply_queued_registrations`] is called or the same registration is made directly.
/// In either case, the returned [`ComponentId`]s will be correct, but they are not correct yet.
///
/// Generally, that means these [`ComponentId`]s can be safely used for read-only purposes.
/// Modifying the contents of the world through these [`ComponentId`]s directly without waiting for them to be fully registered
/// and without then confirming that they have been fully registered is not supported.
/// Hence, extra care is needed with these [`ComponentId`]s to ensure all safety rules are followed.
///
/// As a rule of thumb, if you have mutable access to [`ComponentsRegistrator`], prefer to use that instead.
/// Use this only if you need to know the id of a component but do not need to modify the contents of the world based on that id.
#[derive(Clone, Copy)]
pub struct ComponentsQueuedRegistrator<'w> {
components: &'w Components,
ids: &'w ComponentIds,
}
impl Deref for ComponentsQueuedRegistrator<'_> {
type Target = Components;
fn deref(&self) -> &Self::Target {
self.components
}
}
impl<'w> ComponentsQueuedRegistrator<'w> {
/// Constructs a new [`ComponentsQueuedRegistrator`].
///
/// # Safety
///
/// The [`Components`] and [`ComponentIds`] must match.
/// For example, they must be from the same world.
pub unsafe fn new(components: &'w Components, ids: &'w ComponentIds) -> Self {
Self { components, ids }
}
/// Queues this function to run as a component registrator.
///
/// # Safety
///
/// The [`TypeId`] must not already be registered or queued as a component.
unsafe fn force_register_arbitrary_component(
&self,
type_id: TypeId,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> ComponentId {
let id = self.ids.next();
self.components
.queued
.write()
.unwrap_or_else(PoisonError::into_inner)
.components
.insert(
type_id,
// SAFETY: The id was just generated.
unsafe { QueuedRegistration::new(id, descriptor, func) },
);
id
}
/// Queues this function to run as a resource registrator.
///
/// # Safety
///
/// The [`TypeId`] must not already be registered or queued as a resource.
unsafe fn force_register_arbitrary_resource(
&self,
type_id: TypeId,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> ComponentId {
let id = self.ids.next();
self.components
.queued
.write()
.unwrap_or_else(PoisonError::into_inner)
.resources
.insert(
type_id,
// SAFETY: The id was just generated.
unsafe { QueuedRegistration::new(id, descriptor, func) },
);
id
}
/// Queues this function to run as a dynamic registrator.
fn force_register_arbitrary_dynamic(
&self,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> ComponentId {
let id = self.ids.next();
self.components
.queued
.write()
.unwrap_or_else(PoisonError::into_inner)
.dynamic_registrations
.push(
// SAFETY: The id was just generated.
unsafe { QueuedRegistration::new(id, descriptor, func) },
);
id
}
/// This is a queued version of [`ComponentsRegistrator::register_component`].
/// This will reserve an id and queue the registration.
/// These registrations will be carried out at the next opportunity.
///
/// If this has already been registered or queued, this returns the previous [`ComponentId`].
///
/// # Note
///
/// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later.
/// See type level docs for details.
#[inline]
pub fn queue_register_component<T: Component>(&self) -> ComponentId {
self.component_id::<T>().unwrap_or_else(|| {
// SAFETY: We just checked that this type was not in the queue.
unsafe {
self.force_register_arbitrary_component(
TypeId::of::<T>(),
ComponentDescriptor::new::<T>(),
|registrator, id, _descriptor| {
// SAFETY: We just checked that this is not currently registered or queued, and if it was registered since, this would have been dropped from the queue.
#[expect(unused_unsafe, reason = "More precise to specify.")]
unsafe {
registrator.register_component_unchecked::<T>(&mut Vec::new(), id);
}
},
)
}
})
}
/// This is a queued version of [`ComponentsRegistrator::register_component_with_descriptor`].
/// This will reserve an id and queue the registration.
/// These registrations will be carried out at the next opportunity.
///
/// # Note
///
/// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later.
/// See type level docs for details.
#[inline]
pub fn queue_register_component_with_descriptor(
&self,
descriptor: ComponentDescriptor,
) -> ComponentId {
self.force_register_arbitrary_dynamic(descriptor, |registrator, id, descriptor| {
// SAFETY: Id uniqueness handled by caller.
unsafe {
registrator.register_component_inner(id, descriptor);
}
})
}
/// This is a queued version of [`ComponentsRegistrator::register_resource`].
/// This will reserve an id and queue the registration.
/// These registrations will be carried out at the next opportunity.
///
/// If this has already been registered or queued, this returns the previous [`ComponentId`].
///
/// # Note
///
/// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later.
/// See type level docs for details.
#[inline]
pub fn queue_register_resource<T: Resource>(&self) -> ComponentId {
let type_id = TypeId::of::<T>();
self.get_resource_id(type_id).unwrap_or_else(|| {
// SAFETY: We just checked that this type was not in the queue.
unsafe {
self.force_register_arbitrary_resource(
type_id,
ComponentDescriptor::new_resource::<T>(),
move |registrator, id, descriptor| {
// SAFETY: We just checked that this is not currently registered or queued, and if it was registered since, this would have been dropped from the queue.
// SAFETY: Id uniqueness handled by caller, and the type_id matches descriptor.
#[expect(unused_unsafe, reason = "More precise to specify.")]
unsafe {
registrator.register_resource_unchecked(type_id, id, descriptor);
}
},
)
}
})
}
/// This is a queued version of [`ComponentsRegistrator::register_non_send`].
/// This will reserve an id and queue the registration.
/// These registrations will be carried out at the next opportunity.
///
/// If this has already been registered or queued, this returns the previous [`ComponentId`].
///
/// # Note
///
/// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later.
/// See type level docs for details.
#[inline]
pub fn queue_register_non_send<T: Any>(&self) -> ComponentId {
let type_id = TypeId::of::<T>();
self.get_resource_id(type_id).unwrap_or_else(|| {
// SAFETY: We just checked that this type was not in the queue.
unsafe {
self.force_register_arbitrary_resource(
type_id,
ComponentDescriptor::new_non_send::<T>(StorageType::default()),
move |registrator, id, descriptor| {
// SAFETY: We just checked that this is not currently registered or queued, and if it was registered since, this would have been dropped from the queue.
// SAFETY: Id uniqueness handled by caller, and the type_id matches descriptor.
#[expect(unused_unsafe, reason = "More precise to specify.")]
unsafe {
registrator.register_resource_unchecked(type_id, id, descriptor);
}
},
)
}
})
}
/// This is a queued version of [`ComponentsRegistrator::register_resource_with_descriptor`].
/// This will reserve an id and queue the registration.
/// These registrations will be carried out at the next opportunity.
///
/// # Note
///
/// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later.
/// See type level docs for details.
#[inline]
pub fn queue_register_resource_with_descriptor(
&self,
descriptor: ComponentDescriptor,
) -> ComponentId {
self.force_register_arbitrary_dynamic(descriptor, |registrator, id, descriptor| {
// SAFETY: Id uniqueness handled by caller.
unsafe {
registrator.register_component_inner(id, descriptor);
}
})
}
}

View File

@ -0,0 +1,536 @@
use alloc::{format, vec::Vec};
use bevy_platform::{collections::HashMap, sync::Arc};
use bevy_ptr::OwningPtr;
use core::fmt::Debug;
use smallvec::SmallVec;
use thiserror::Error;
use crate::{
bundle::BundleInfo,
change_detection::MaybeLocation,
component::{Component, ComponentId, Components, ComponentsRegistrator, Tick},
entity::Entity,
query::DebugCheckedUnwrap as _,
storage::{SparseSets, Table, TableRow},
};
impl Components {
/// Registers the given component `R` and [required components] inherited from it as required by `T`.
///
/// When `T` is added to an entity, `R` will also be added if it was not already provided.
/// The given `constructor` will be used for the creation of `R`.
///
/// [required components]: Component#required-components
///
/// # Safety
///
/// The given component IDs `required` and `requiree` must be valid.
///
/// # Errors
///
/// Returns a [`RequiredComponentsError`] if the `required` component is already a directly required component for the `requiree`.
///
/// Indirect requirements through other components are allowed. In those cases, the more specific
/// registration will be used.
pub(crate) unsafe fn register_required_components<R: Component>(
&mut self,
requiree: ComponentId,
required: ComponentId,
constructor: fn() -> R,
) -> Result<(), RequiredComponentsError> {
// SAFETY: The caller ensures that the `requiree` is valid.
let required_components = unsafe {
self.get_required_components_mut(requiree)
.debug_checked_unwrap()
};
// Cannot directly require the same component twice.
if required_components
.0
.get(&required)
.is_some_and(|c| c.inheritance_depth == 0)
{
return Err(RequiredComponentsError::DuplicateRegistration(
requiree, required,
));
}
// Register the required component for the requiree.
// This is a direct requirement with a depth of `0`.
required_components.register_by_id(required, constructor, 0);
// Add the requiree to the list of components that require the required component.
// SAFETY: The component is in the list of required components, so it must exist already.
let required_by = unsafe { self.get_required_by_mut(required).debug_checked_unwrap() };
required_by.insert(requiree);
let mut required_components_tmp = RequiredComponents::default();
// SAFETY: The caller ensures that the `requiree` and `required` components are valid.
let inherited_requirements = unsafe {
self.register_inherited_required_components(
requiree,
required,
&mut required_components_tmp,
)
};
// SAFETY: The caller ensures that the `requiree` is valid.
let required_components = unsafe {
self.get_required_components_mut(requiree)
.debug_checked_unwrap()
};
required_components.0.extend(required_components_tmp.0);
// Propagate the new required components up the chain to all components that require the requiree.
if let Some(required_by) = self
.get_required_by(requiree)
.map(|set| set.iter().copied().collect::<SmallVec<[ComponentId; 8]>>())
{
// `required` is now required by anything that `requiree` was required by.
self.get_required_by_mut(required)
.unwrap()
.extend(required_by.iter().copied());
for &required_by_id in required_by.iter() {
// SAFETY: The component is in the list of required components, so it must exist already.
let required_components = unsafe {
self.get_required_components_mut(required_by_id)
.debug_checked_unwrap()
};
// Register the original required component in the "parent" of the requiree.
// The inheritance depth is 1 deeper than the `requiree` wrt `required_by_id`.
let depth = required_components.0.get(&requiree).expect("requiree is required by required_by_id, so its required_components must include requiree").inheritance_depth;
required_components.register_by_id(required, constructor, depth + 1);
for (component_id, component) in inherited_requirements.iter() {
// Register the required component.
// The inheritance depth of inherited components is whatever the requiree's
// depth is relative to `required_by_id`, plus the inheritance depth of the
// inherited component relative to the requiree, plus 1 to account for the
// requiree in between.
// SAFETY: Component ID and constructor match the ones on the original requiree.
// The original requiree is responsible for making sure the registration is safe.
unsafe {
required_components.register_dynamic_with(
*component_id,
component.inheritance_depth + depth + 1,
|| component.constructor.clone(),
);
};
}
}
}
Ok(())
}
/// Registers the components inherited from `required` for the given `requiree`,
/// returning the requirements in a list.
///
/// # Safety
///
/// The given component IDs `requiree` and `required` must be valid.
unsafe fn register_inherited_required_components(
&mut self,
requiree: ComponentId,
required: ComponentId,
required_components: &mut RequiredComponents,
) -> Vec<(ComponentId, RequiredComponent)> {
// Get required components inherited from the `required` component.
// SAFETY: The caller ensures that the `required` component is valid.
let required_component_info = unsafe { self.get_info(required).debug_checked_unwrap() };
let inherited_requirements: Vec<(ComponentId, RequiredComponent)> = required_component_info
.required_components()
.0
.iter()
.map(|(component_id, required_component)| {
(
*component_id,
RequiredComponent {
constructor: required_component.constructor.clone(),
// Add `1` to the inheritance depth since this will be registered
// for the component that requires `required`.
inheritance_depth: required_component.inheritance_depth + 1,
},
)
})
.collect();
// Register the new required components.
for (component_id, component) in inherited_requirements.iter() {
// Register the required component for the requiree.
// SAFETY: Component ID and constructor match the ones on the original requiree.
unsafe {
required_components.register_dynamic_with(
*component_id,
component.inheritance_depth,
|| component.constructor.clone(),
);
};
// Add the requiree to the list of components that require the required component.
// SAFETY: The caller ensures that the required components are valid.
let required_by = unsafe {
self.get_required_by_mut(*component_id)
.debug_checked_unwrap()
};
required_by.insert(requiree);
}
inherited_requirements
}
/// Registers the given component `R` and [required components] inherited from it as required by `T`,
/// and adds `T` to their lists of requirees.
///
/// The given `inheritance_depth` determines how many levels of inheritance deep the requirement is.
/// A direct requirement has a depth of `0`, and each level of inheritance increases the depth by `1`.
/// Lower depths are more specific requirements, and can override existing less specific registrations.
///
/// This method does *not* register any components as required by components that require `T`.
///
/// [required component]: Component#required-components
///
/// # Safety
///
/// The given component IDs `required` and `requiree` must be valid.
pub(crate) unsafe fn register_required_components_manual_unchecked<R: Component>(
&mut self,
requiree: ComponentId,
required: ComponentId,
required_components: &mut RequiredComponents,
constructor: fn() -> R,
inheritance_depth: u16,
) {
// Components cannot require themselves.
if required == requiree {
return;
}
// Register the required component `R` for the requiree.
required_components.register_by_id(required, constructor, inheritance_depth);
// Add the requiree to the list of components that require `R`.
// SAFETY: The caller ensures that the component ID is valid.
// Assuming it is valid, the component is in the list of required components, so it must exist already.
let required_by = unsafe { self.get_required_by_mut(required).debug_checked_unwrap() };
required_by.insert(requiree);
self.register_inherited_required_components(requiree, required, required_components);
}
}
impl<'w> ComponentsRegistrator<'w> {
// NOTE: This should maybe be private, but it is currently public so that `bevy_ecs_macros` can use it.
// We can't directly move this there either, because this uses `Components::get_required_by_mut`,
// which is private, and could be equally risky to expose to users.
/// Registers the given component `R` and [required components] inherited from it as required by `T`,
/// and adds `T` to their lists of requirees.
///
/// The given `inheritance_depth` determines how many levels of inheritance deep the requirement is.
/// A direct requirement has a depth of `0`, and each level of inheritance increases the depth by `1`.
/// Lower depths are more specific requirements, and can override existing less specific registrations.
///
/// The `recursion_check_stack` allows checking whether this component tried to register itself as its
/// own (indirect) required component.
///
/// This method does *not* register any components as required by components that require `T`.
///
/// Only use this method if you know what you are doing. In most cases, you should instead use [`World::register_required_components`],
/// or the equivalent method in `bevy_app::App`.
///
/// [required component]: Component#required-components
#[doc(hidden)]
pub fn register_required_components_manual<T: Component, R: Component>(
&mut self,
required_components: &mut RequiredComponents,
constructor: fn() -> R,
inheritance_depth: u16,
recursion_check_stack: &mut Vec<ComponentId>,
) {
let requiree = self.register_component_checked::<T>(recursion_check_stack);
let required = self.register_component_checked::<R>(recursion_check_stack);
// SAFETY: We just created the components.
unsafe {
self.register_required_components_manual_unchecked::<R>(
requiree,
required,
required_components,
constructor,
inheritance_depth,
);
}
}
}
/// An error returned when the registration of a required component fails.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum RequiredComponentsError {
/// The component is already a directly required component for the requiree.
#[error("Component {0:?} already directly requires component {1:?}")]
DuplicateRegistration(ComponentId, ComponentId),
/// An archetype with the component that requires other components already exists
#[error("An archetype with the component {0:?} that requires other components already exists")]
ArchetypeExists(ComponentId),
}
/// A Required Component constructor. See [`Component`] for details.
#[derive(Clone)]
pub struct RequiredComponentConstructor(
pub Arc<dyn Fn(&mut Table, &mut SparseSets, Tick, TableRow, Entity, MaybeLocation)>,
);
impl RequiredComponentConstructor {
/// # Safety
/// This is intended to only be called in the context of [`BundleInfo::write_components`] to initialized required components.
/// Calling it _anywhere else_ should be considered unsafe.
///
/// `table_row` and `entity` must correspond to a valid entity that currently needs a component initialized via the constructor stored
/// on this [`RequiredComponentConstructor`]. The stored constructor must correspond to a component on `entity` that needs initialization.
/// `table` and `sparse_sets` must correspond to storages on a world where `entity` needs this required component initialized.
///
/// Again, don't call this anywhere but [`BundleInfo::write_components`].
pub(crate) unsafe fn initialize(
&self,
table: &mut Table,
sparse_sets: &mut SparseSets,
change_tick: Tick,
table_row: TableRow,
entity: Entity,
caller: MaybeLocation,
) {
(self.0)(table, sparse_sets, change_tick, table_row, entity, caller);
}
}
/// Metadata associated with a required component. See [`Component`] for details.
#[derive(Clone)]
pub struct RequiredComponent {
/// The constructor used for the required component.
pub constructor: RequiredComponentConstructor,
/// The depth of the component requirement in the requirement hierarchy for this component.
/// This is used for determining which constructor is used in cases where there are duplicate requires.
///
/// For example, consider the inheritance tree `X -> Y -> Z`, where `->` indicates a requirement.
/// `X -> Y` and `Y -> Z` are direct requirements with a depth of 0, while `Z` is only indirectly
/// required for `X` with a depth of `1`.
///
/// In cases where there are multiple conflicting requirements with the same depth, a higher priority
/// will be given to components listed earlier in the `require` attribute, or to the latest added requirement
/// if registered at runtime.
pub inheritance_depth: u16,
}
/// The collection of metadata for components that are required for a given component.
///
/// For more information, see the "Required Components" section of [`Component`].
#[derive(Default, Clone)]
pub struct RequiredComponents(pub(crate) HashMap<ComponentId, RequiredComponent>);
impl Debug for RequiredComponents {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("RequiredComponents")
.field(&self.0.keys())
.finish()
}
}
impl RequiredComponents {
/// Registers a required component.
///
/// If the component is already registered, it will be overwritten if the given inheritance depth
/// is smaller than the depth of the existing registration. Otherwise, the new registration will be ignored.
///
/// # Safety
///
/// `component_id` must match the type initialized by `constructor`.
/// `constructor` _must_ initialize a component for `component_id` in such a way that
/// matches the storage type of the component. It must only use the given `table_row` or `Entity` to
/// initialize the storage for `component_id` corresponding to the given entity.
pub unsafe fn register_dynamic_with(
&mut self,
component_id: ComponentId,
inheritance_depth: u16,
constructor: impl FnOnce() -> RequiredComponentConstructor,
) {
let entry = self.0.entry(component_id);
match entry {
bevy_platform::collections::hash_map::Entry::Occupied(mut occupied) => {
let current = occupied.get_mut();
if current.inheritance_depth > inheritance_depth {
*current = RequiredComponent {
constructor: constructor(),
inheritance_depth,
}
}
}
bevy_platform::collections::hash_map::Entry::Vacant(vacant) => {
vacant.insert(RequiredComponent {
constructor: constructor(),
inheritance_depth,
});
}
}
}
/// Registers a required component.
///
/// If the component is already registered, it will be overwritten if the given inheritance depth
/// is smaller than the depth of the existing registration. Otherwise, the new registration will be ignored.
pub fn register<C: Component>(
&mut self,
components: &mut ComponentsRegistrator,
constructor: fn() -> C,
inheritance_depth: u16,
) {
let component_id = components.register_component::<C>();
self.register_by_id(component_id, constructor, inheritance_depth);
}
/// Registers the [`Component`] with the given ID as required if it exists.
///
/// If the component is already registered, it will be overwritten if the given inheritance depth
/// is smaller than the depth of the existing registration. Otherwise, the new registration will be ignored.
pub fn register_by_id<C: Component>(
&mut self,
component_id: ComponentId,
constructor: fn() -> C,
inheritance_depth: u16,
) {
let erased = || {
RequiredComponentConstructor({
// `portable-atomic-util` `Arc` is not able to coerce an unsized
// type like `std::sync::Arc` can. Creating a `Box` first does the
// coercion.
//
// This would be resolved by https://github.com/rust-lang/rust/issues/123430
#[cfg(not(target_has_atomic = "ptr"))]
use alloc::boxed::Box;
type Constructor = dyn for<'a, 'b> Fn(
&'a mut Table,
&'b mut SparseSets,
Tick,
TableRow,
Entity,
MaybeLocation,
);
#[cfg(not(target_has_atomic = "ptr"))]
type Intermediate<T> = Box<T>;
#[cfg(target_has_atomic = "ptr")]
type Intermediate<T> = Arc<T>;
let boxed: Intermediate<Constructor> = Intermediate::new(
move |table, sparse_sets, change_tick, table_row, entity, caller| {
OwningPtr::make(constructor(), |ptr| {
// SAFETY: This will only be called in the context of `BundleInfo::write_components`, which will
// pass in a valid table_row and entity requiring a C constructor
// C::STORAGE_TYPE is the storage type associated with `component_id` / `C`
// `ptr` points to valid `C` data, which matches the type associated with `component_id`
unsafe {
BundleInfo::initialize_required_component(
table,
sparse_sets,
change_tick,
table_row,
entity,
component_id,
C::STORAGE_TYPE,
ptr,
caller,
);
}
});
},
);
Arc::from(boxed)
})
};
// SAFETY:
// `component_id` matches the type initialized by the `erased` constructor above.
// `erased` initializes a component for `component_id` in such a way that
// matches the storage type of the component. It only uses the given `table_row` or `Entity` to
// initialize the storage corresponding to the given entity.
unsafe { self.register_dynamic_with(component_id, inheritance_depth, erased) };
}
/// Iterates the ids of all required components. This includes recursive required components.
pub fn iter_ids(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.0.keys().copied()
}
/// Removes components that are explicitly provided in a given [`Bundle`]. These components should
/// be logically treated as normal components, not "required components".
///
/// [`Bundle`]: crate::bundle::Bundle
pub(crate) fn remove_explicit_components(&mut self, components: &[ComponentId]) {
for component in components {
self.0.remove(component);
}
}
/// Merges `required_components` into this collection. This only inserts a required component
/// if it _did not already exist_ *or* if the required component is more specific than the existing one
/// (in other words, if the inheritance depth is smaller).
///
/// See [`register_dynamic_with`](Self::register_dynamic_with) for details.
pub(crate) fn merge(&mut self, required_components: &RequiredComponents) {
for (
component_id,
RequiredComponent {
constructor,
inheritance_depth,
},
) in required_components.0.iter()
{
// SAFETY: This exact registration must have been done on `required_components`, so safety is ensured by that caller.
unsafe {
self.register_dynamic_with(*component_id, *inheritance_depth, || {
constructor.clone()
});
}
}
}
}
// NOTE: This should maybe be private, but it is currently public so that `bevy_ecs_macros` can use it.
// This exists as a standalone function instead of being inlined into the component derive macro so as
// to reduce the amount of generated code.
#[doc(hidden)]
pub fn enforce_no_required_components_recursion(
components: &Components,
recursion_check_stack: &[ComponentId],
) {
if let Some((&requiree, check)) = recursion_check_stack.split_last() {
if let Some(direct_recursion) = check
.iter()
.position(|&id| id == requiree)
.map(|index| index == check.len() - 1)
{
panic!(
"Recursive required components detected: {}\nhelp: {}",
recursion_check_stack
.iter()
.map(|id| format!("{}", components.get_name(*id).unwrap().shortname()))
.collect::<Vec<_>>()
.join(""),
if direct_recursion {
format!(
"Remove require({}).",
components.get_name(requiree).unwrap().shortname()
)
} else {
"If this is intentional, consider merging the components.".into()
}
);
}
}
}

View File

@ -0,0 +1,199 @@
use bevy_ecs_macros::Event;
use bevy_ptr::UnsafeCellDeref;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use core::cell::UnsafeCell;
use crate::change_detection::MAX_CHANGE_AGE;
/// A value that tracks when a system ran relative to other systems.
/// This is used to power change detection.
///
/// *Note* that a system that hasn't been run yet has a `Tick` of 0.
#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
pub struct Tick {
tick: u32,
}
impl Tick {
/// The maximum relative age for a change tick.
/// The value of this is equal to [`MAX_CHANGE_AGE`].
///
/// Since change detection will not work for any ticks older than this,
/// ticks are periodically scanned to ensure their relative values are below this.
pub const MAX: Self = Self::new(MAX_CHANGE_AGE);
/// Creates a new [`Tick`] wrapping the given value.
#[inline]
pub const fn new(tick: u32) -> Self {
Self { tick }
}
/// Gets the value of this change tick.
#[inline]
pub const fn get(self) -> u32 {
self.tick
}
/// Sets the value of this change tick.
#[inline]
pub fn set(&mut self, tick: u32) {
self.tick = tick;
}
/// Returns `true` if this `Tick` occurred since the system's `last_run`.
///
/// `this_run` is the current tick of the system, used as a reference to help deal with wraparound.
#[inline]
pub fn is_newer_than(self, last_run: Tick, this_run: Tick) -> bool {
// This works even with wraparound because the world tick (`this_run`) is always "newer" than
// `last_run` and `self.tick`, and we scan periodically to clamp `ComponentTicks` values
// so they never get older than `u32::MAX` (the difference would overflow).
//
// The clamp here ensures determinism (since scans could differ between app runs).
let ticks_since_insert = this_run.relative_to(self).tick.min(MAX_CHANGE_AGE);
let ticks_since_system = this_run.relative_to(last_run).tick.min(MAX_CHANGE_AGE);
ticks_since_system > ticks_since_insert
}
/// Returns a change tick representing the relationship between `self` and `other`.
#[inline]
pub(crate) fn relative_to(self, other: Self) -> Self {
let tick = self.tick.wrapping_sub(other.tick);
Self { tick }
}
/// Wraps this change tick's value if it exceeds [`Tick::MAX`].
///
/// Returns `true` if wrapping was performed. Otherwise, returns `false`.
#[inline]
pub fn check_tick(&mut self, check: CheckChangeTicks) -> bool {
let age = check.present_tick().relative_to(*self);
// This comparison assumes that `age` has not overflowed `u32::MAX` before, which will be true
// so long as this check always runs before that can happen.
if age.get() > Self::MAX.get() {
*self = check.present_tick().relative_to(Self::MAX);
true
} else {
false
}
}
}
/// An observer [`Event`] that can be used to maintain [`Tick`]s in custom data structures, enabling to make
/// use of bevy's periodic checks that clamps ticks to a certain range, preventing overflows and thus
/// keeping methods like [`Tick::is_newer_than`] reliably return `false` for ticks that got too old.
///
/// # Example
///
/// Here a schedule is stored in a custom resource. This way the systems in it would not have their change
/// ticks automatically updated via [`World::check_change_ticks`](crate::world::World::check_change_ticks),
/// possibly causing `Tick`-related bugs on long-running apps.
///
/// To fix that, add an observer for this event that calls the schedule's
/// [`Schedule::check_change_ticks`](bevy_ecs::schedule::Schedule::check_change_ticks).
///
/// ```
/// use bevy_ecs::prelude::*;
/// use bevy_ecs::component::CheckChangeTicks;
///
/// #[derive(Resource)]
/// struct CustomSchedule(Schedule);
///
/// # let mut world = World::new();
/// world.add_observer(|check: On<CheckChangeTicks>, mut schedule: ResMut<CustomSchedule>| {
/// schedule.0.check_change_ticks(*check);
/// });
/// ```
#[derive(Debug, Clone, Copy, Event)]
pub struct CheckChangeTicks(pub(crate) Tick);
impl CheckChangeTicks {
/// Get the present `Tick` that other ticks get compared to.
pub fn present_tick(self) -> Tick {
self.0
}
}
/// Interior-mutable access to the [`Tick`]s for a single component or resource.
#[derive(Copy, Clone, Debug)]
pub struct TickCells<'a> {
/// The tick indicating when the value was added to the world.
pub added: &'a UnsafeCell<Tick>,
/// The tick indicating the last time the value was modified.
pub changed: &'a UnsafeCell<Tick>,
}
impl<'a> TickCells<'a> {
/// # Safety
/// All cells contained within must uphold the safety invariants of [`UnsafeCellDeref::read`].
#[inline]
pub(crate) unsafe fn read(&self) -> ComponentTicks {
ComponentTicks {
// SAFETY: The callers uphold the invariants for `read`.
added: unsafe { self.added.read() },
// SAFETY: The callers uphold the invariants for `read`.
changed: unsafe { self.changed.read() },
}
}
}
/// Records when a component or resource was added and when it was last mutably dereferenced (or added).
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))]
pub struct ComponentTicks {
/// Tick recording the time this component or resource was added.
pub added: Tick,
/// Tick recording the time this component or resource was most recently changed.
pub changed: Tick,
}
impl ComponentTicks {
/// Returns `true` if the component or resource was added after the system last ran
/// (or the system is running for the first time).
#[inline]
pub fn is_added(&self, last_run: Tick, this_run: Tick) -> bool {
self.added.is_newer_than(last_run, this_run)
}
/// Returns `true` if the component or resource was added or mutably dereferenced after the system last ran
/// (or the system is running for the first time).
#[inline]
pub fn is_changed(&self, last_run: Tick, this_run: Tick) -> bool {
self.changed.is_newer_than(last_run, this_run)
}
/// Creates a new instance with the same change tick for `added` and `changed`.
pub fn new(change_tick: Tick) -> Self {
Self {
added: change_tick,
changed: change_tick,
}
}
/// Manually sets the change tick.
///
/// This is normally done automatically via the [`DerefMut`](core::ops::DerefMut) implementation
/// on [`Mut<T>`](crate::change_detection::Mut), [`ResMut<T>`](crate::change_detection::ResMut), etc.
/// However, components and resources that make use of interior mutability might require manual updates.
///
/// # Example
/// ```no_run
/// # use bevy_ecs::{world::World, component::ComponentTicks};
/// let world: World = unimplemented!();
/// let component_ticks: ComponentTicks = unimplemented!();
///
/// component_ticks.set_changed(world.read_change_tick());
/// ```
#[inline]
pub fn set_changed(&mut self, change_tick: Tick) {
self.changed = change_tick;
}
}

View File

@ -731,42 +731,16 @@ impl<'de> Deserialize<'de> for Entity {
}
}
/// Outputs the full entity identifier, including the index, generation, and the raw bits.
/// Outputs the short entity identifier, including the index and generation.
///
/// This takes the format: `{index}v{generation}#{bits}`.
/// This takes the format: `{index}v{generation}`.
///
/// For [`Entity::PLACEHOLDER`], this outputs `PLACEHOLDER`.
///
/// # Usage
///
/// Prefer to use this format for debugging and logging purposes. Because the output contains
/// the raw bits, it is easy to check it against serialized scene data.
///
/// Example serialized scene data:
/// ```text
/// (
/// ...
/// entities: {
/// 4294967297: ( <--- Raw Bits
/// components: {
/// ...
/// ),
/// ...
/// )
/// ```
/// For a unique [`u64`] representation, use [`Entity::to_bits`].
impl fmt::Debug for Entity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self == &Self::PLACEHOLDER {
write!(f, "PLACEHOLDER")
} else {
write!(
f,
"{}v{}#{}",
self.index(),
self.generation(),
self.to_bits()
)
}
fmt::Display::fmt(self, f)
}
}
@ -1585,7 +1559,7 @@ mod tests {
fn entity_debug() {
let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));
let string = format!("{entity:?}");
assert_eq!(string, "42v0#4294967253");
assert_eq!(string, "42v0");
let entity = Entity::PLACEHOLDER;
let string = format!("{entity:?}");

View File

@ -746,11 +746,11 @@ fn early_sweep_material_instances<M>(
/// preparation for a new frame.
pub(crate) fn late_sweep_material_instances(
mut material_instances: ResMut<RenderMaterialInstances>,
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
) {
let last_change_tick = material_instances.current_change_tick;
for entity in removed_visibilities_query.read() {
for entity in removed_meshes_query.read() {
if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
// Only sweep the entry if it wasn't updated this frame. It's
// possible that a `ViewVisibility` component was removed and

View File

@ -1452,8 +1452,6 @@ pub fn extract_meshes_for_gpu_building(
>,
>,
all_meshes_query: Extract<Query<GpuMeshExtractionQuery>>,
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
mut removed_global_transforms_query: Extract<RemovedComponents<GlobalTransform>>,
mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
gpu_culling_query: Extract<Query<(), (With<Camera>, Without<NoIndirectDrawing>)>>,
meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
@ -1509,11 +1507,7 @@ pub fn extract_meshes_for_gpu_building(
}
// Also record info about each mesh that became invisible.
for entity in removed_visibilities_query
.read()
.chain(removed_global_transforms_query.read())
.chain(removed_meshes_query.read())
{
for entity in removed_meshes_query.read() {
// Only queue a mesh for removal if we didn't pick it up above.
// It's possible that a necessary component was removed and re-added in
// the same frame.

View File

@ -62,6 +62,22 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: v
return sample_shadow_cubemap(frag_ls * flip_z, distance_to_light, depth, light_id);
}
// this method of constructing a basis from a vec3 is used by glam::Vec3::any_orthonormal_pair
// so we reproduce it here to avoid a mismatch if glam changes. we also switch the handedness
// the construction of the orthonormal basis up and right vectors needs to precisely mirror the code
// in bevy_light/spot_light.rs:spot_light_world_from_view
fn spot_light_world_from_view(fwd: vec3<f32>) -> mat3x3<f32> {
var sign = -1.0;
if (fwd.z >= 0.0) {
sign = 1.0;
}
let a = -1.0 / (fwd.z + sign);
let b = fwd.x * fwd.y * a;
let up_dir = vec3<f32>(1.0 + sign * fwd.x * fwd.x * a, sign * b, -sign * fwd.x);
let right_dir = vec3<f32>(-b, -sign - fwd.y * fwd.y * a, fwd.y);
return mat3x3<f32>(right_dir, up_dir, fwd);
}
fn fetch_spot_shadow(
light_id: u32,
frag_position: vec4<f32>,
@ -88,17 +104,7 @@ fn fetch_spot_shadow(
+ ((*light).shadow_depth_bias * normalize(surface_to_light))
+ (surface_normal.xyz * (*light).shadow_normal_bias) * distance_to_light;
// the construction of the up and right vectors needs to precisely mirror the code
// in render/light.rs:spot_light_view_matrix
var sign = -1.0;
if (fwd.z >= 0.0) {
sign = 1.0;
}
let a = -1.0 / (fwd.z + sign);
let b = fwd.x * fwd.y * a;
let up_dir = vec3<f32>(1.0 + sign * fwd.x * fwd.x * a, sign * b, -sign * fwd.x);
let right_dir = vec3<f32>(-b, -sign - fwd.y * fwd.y * a, fwd.y);
let light_inv_rot = mat3x3<f32>(right_dir, up_dir, fwd);
let light_inv_rot = spot_light_world_from_view(fwd);
// because the matrix is a pure rotation matrix, the inverse is just the transpose, and to calculate
// the product of the transpose with a vector we can just post-multiply instead of pre-multiplying.

View File

@ -309,7 +309,6 @@ pub fn extract_skins(
skinned_mesh_inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
changed_transforms: Extract<Query<(Entity, &GlobalTransform), Changed<GlobalTransform>>>,
joints: Extract<Query<&GlobalTransform>>,
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
mut removed_skinned_meshes_query: Extract<RemovedComponents<SkinnedMesh>>,
) {
let skin_uniforms = skin_uniforms.into_inner();
@ -335,10 +334,7 @@ pub fn extract_skins(
);
// Delete skins that became invisible.
for skinned_mesh_entity in removed_visibilities_query
.read()
.chain(removed_skinned_meshes_query.read())
{
for skinned_mesh_entity in removed_skinned_meshes_query.read() {
// Only remove a skin if we didn't pick it up in `add_or_delete_skins`.
// It's possible that a necessary component was removed and re-added in
// the same frame.

View File

@ -2,7 +2,7 @@
//! Provides raytraced lighting.
//!
//! See [`SolariPlugin`] for more info.
//! See [`SolariPlugins`] for more info.
//!
//! ![`bevy_solari` logo](https://raw.githubusercontent.com/bevyengine/bevy/assets/branding/bevy_solari.svg)
pub mod pathtracer;
@ -13,33 +13,35 @@ pub mod scene;
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
pub use super::SolariPlugin;
pub use super::SolariPlugins;
pub use crate::realtime::SolariLighting;
pub use crate::scene::RaytracingMesh3d;
}
use crate::realtime::SolariLightingPlugin;
use crate::scene::RaytracingScenePlugin;
use bevy_app::{App, Plugin};
use bevy_app::{PluginGroup, PluginGroupBuilder};
use bevy_render::settings::WgpuFeatures;
/// An experimental plugin for raytraced lighting.
/// An experimental set of plugins for raytraced lighting.
///
/// This plugin provides:
/// This plugin group provides:
/// * [`SolariLightingPlugin`] - Raytraced direct and indirect lighting (indirect lighting not yet implemented).
/// * [`RaytracingScenePlugin`] - BLAS building, resource and lighting binding.
/// * [`pathtracer::PathtracingPlugin`] - A non-realtime pathtracer for validation purposes.
/// * [`pathtracer::PathtracingPlugin`] - A non-realtime pathtracer for validation purposes (not added by default).
///
/// To get started, add `RaytracingMesh3d` and `MeshMaterial3d::<StandardMaterial>` to your entities.
pub struct SolariPlugin;
pub struct SolariPlugins;
impl Plugin for SolariPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((RaytracingScenePlugin, SolariLightingPlugin));
impl PluginGroup for SolariPlugins {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(RaytracingScenePlugin)
.add(SolariLightingPlugin)
}
}
impl SolariPlugin {
impl SolariPlugins {
/// [`WgpuFeatures`] required for this plugin to function.
pub fn required_wgpu_features() -> WgpuFeatures {
WgpuFeatures::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE

View File

@ -2,7 +2,7 @@ mod extract;
mod node;
mod prepare;
use crate::SolariPlugin;
use crate::SolariPlugins;
use bevy_app::{App, Plugin};
use bevy_asset::embedded_asset;
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
@ -37,10 +37,10 @@ impl Plugin for PathtracingPlugin {
let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugin::required_wgpu_features()) {
if !features.contains(SolariPlugins::required_wgpu_features()) {
warn!(
"PathtracingPlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugin::required_wgpu_features().difference(features)
SolariPlugins::required_wgpu_features().difference(features)
);
return;
}

View File

@ -2,7 +2,7 @@ mod extract;
mod node;
mod prepare;
use crate::SolariPlugin;
use crate::SolariPlugins;
use bevy_app::{App, Plugin};
use bevy_asset::embedded_asset;
use bevy_core_pipeline::{
@ -38,10 +38,10 @@ impl Plugin for SolariLightingPlugin {
let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugin::required_wgpu_features()) {
if !features.contains(SolariPlugins::required_wgpu_features()) {
warn!(
"SolariLightingPlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugin::required_wgpu_features().difference(features)
SolariPlugins::required_wgpu_features().difference(features)
);
return;
}

View File

@ -6,7 +6,7 @@ mod types;
pub use binder::RaytracingSceneBindings;
pub use types::RaytracingMesh3d;
use crate::SolariPlugin;
use crate::SolariPlugins;
use bevy_app::{App, Plugin};
use bevy_ecs::schedule::IntoScheduleConfigs;
use bevy_render::{
@ -41,10 +41,10 @@ impl Plugin for RaytracingScenePlugin {
let render_app = app.sub_app_mut(RenderApp);
let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugin::required_wgpu_features()) {
if !features.contains(SolariPlugins::required_wgpu_features()) {
warn!(
"RaytracingScenePlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugin::required_wgpu_features().difference(features)
SolariPlugins::required_wgpu_features().difference(features)
);
return;
}

View File

@ -331,7 +331,6 @@ pub fn extract_mesh_materials_2d<M: Material2d>(
Or<(Changed<ViewVisibility>, Changed<MeshMaterial2d<M>>)>,
>,
>,
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
mut removed_materials_query: Extract<RemovedComponents<MeshMaterial2d<M>>>,
) {
for (entity, view_visibility, material) in &changed_meshes_query {
@ -342,10 +341,7 @@ pub fn extract_mesh_materials_2d<M: Material2d>(
}
}
for entity in removed_visibilities_query
.read()
.chain(removed_materials_query.read())
{
for entity in removed_materials_query.read() {
// Only queue a mesh for removal if we didn't pick it up above.
// It's possible that a necessary component was removed and re-added in
// the same frame.

View File

@ -13,41 +13,30 @@ fn main() {
fn draw_cursor(
camera_query: Single<(&Camera, &GlobalTransform)>,
ground: Single<&GlobalTransform, With<Ground>>,
windows: Query<&Window>,
window: Single<&Window>,
mut gizmos: Gizmos,
) {
let Ok(windows) = windows.single() else {
return;
};
let (camera, camera_transform) = *camera_query;
let Some(cursor_position) = windows.cursor_position() else {
return;
};
if let Some(cursor_position) = window.cursor_position()
// Calculate a ray pointing from the camera into the world based on the cursor's position.
&& let Ok(ray) = camera.viewport_to_world(camera_transform, cursor_position)
// Calculate if and at what distance the ray is hitting the ground plane.
&& let Some(distance) =
ray.intersect_plane(ground.translation(), InfinitePlane3d::new(ground.up()))
{
let point = ray.get_point(distance);
// Calculate a ray pointing from the camera into the world based on the cursor's position.
let Ok(ray) = camera.viewport_to_world(camera_transform, cursor_position) else {
return;
};
// Calculate if and where the ray is hitting the ground plane.
let Some(distance) =
ray.intersect_plane(ground.translation(), InfinitePlane3d::new(ground.up()))
else {
return;
};
let point = ray.get_point(distance);
// Draw a circle just above the ground plane at that position.
gizmos.circle(
Isometry3d::new(
point + ground.up() * 0.01,
Quat::from_rotation_arc(Vec3::Z, ground.up().as_vec3()),
),
0.2,
Color::WHITE,
);
// Draw a circle just above the ground plane at that position.
gizmos.circle(
Isometry3d::new(
point + ground.up() * 0.01,
Quat::from_rotation_arc(Vec3::Z, ground.up().as_vec3()),
),
0.2,
Color::WHITE,
);
}
}
#[derive(Component)]

View File

@ -10,7 +10,7 @@ use bevy::{
scene::SceneInstanceReady,
solari::{
pathtracer::{Pathtracer, PathtracingPlugin},
prelude::{RaytracingMesh3d, SolariLighting, SolariPlugin},
prelude::{RaytracingMesh3d, SolariLighting, SolariPlugins},
},
};
use camera_controller::{CameraController, CameraControllerPlugin};
@ -28,7 +28,7 @@ fn main() {
let args: Args = argh::from_env();
let mut app = App::new();
app.add_plugins((DefaultPlugins, SolariPlugin, CameraControllerPlugin))
app.add_plugins((DefaultPlugins, SolariPlugins, CameraControllerPlugin))
.insert_resource(args)
.add_systems(Startup, setup);

View File

@ -1,7 +1,7 @@
//! Showcase how to use and configure FPS overlay.
use bevy::{
dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin},
dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin, FrameTimeGraphConfig},
prelude::*,
text::FontSmoothing,
};
@ -33,6 +33,13 @@ fn main() {
// We can also set the refresh interval for the FPS counter
refresh_interval: core::time::Duration::from_millis(100),
enabled: true,
frame_time_graph_config: FrameTimeGraphConfig {
enabled: true,
// The minimum acceptable fps
min_fps: 30.0,
// The target fps
target_fps: 144.0,
},
},
},
))
@ -52,7 +59,8 @@ fn setup(mut commands: Commands) {
"Press 1 to toggle the overlay color.\n",
"Press 2 to decrease the overlay size.\n",
"Press 3 to increase the overlay size.\n",
"Press 4 to toggle the overlay visibility."
"Press 4 to toggle the text visibility.\n",
"Press 5 to toggle the frame time graph."
)),
Node {
position_type: PositionType::Absolute,
@ -81,4 +89,7 @@ fn customize_config(input: Res<ButtonInput<KeyCode>>, mut overlay: ResMut<FpsOve
if input.just_pressed(KeyCode::Digit4) {
overlay.enabled = !overlay.enabled;
}
if input.just_released(KeyCode::Digit5) {
overlay.frame_time_graph_config.enabled = !overlay.frame_time_graph_config.enabled;
}
}

View File

@ -4,7 +4,7 @@ use bevy::{
color::palettes::basic::*,
core_widgets::{
Callback, CoreButton, CoreCheckbox, CoreRadio, CoreRadioGroup, CoreSlider,
CoreSliderDragState, CoreSliderThumb, CoreWidgetsPlugin, SliderRange, SliderValue,
CoreSliderDragState, CoreSliderThumb, CoreWidgetsPlugins, SliderRange, SliderValue,
TrackClick,
},
input_focus::{
@ -21,7 +21,7 @@ fn main() {
App::new()
.add_plugins((
DefaultPlugins,
CoreWidgetsPlugin,
CoreWidgetsPlugins,
InputDispatchPlugin,
TabNavigationPlugin,
))

View File

@ -3,7 +3,7 @@
use bevy::{
color::palettes::basic::*,
core_widgets::{
Callback, CoreButton, CoreCheckbox, CoreSlider, CoreSliderThumb, CoreWidgetsPlugin,
Callback, CoreButton, CoreCheckbox, CoreSlider, CoreSliderThumb, CoreWidgetsPlugins,
SliderRange, SliderValue,
},
ecs::system::SystemId,
@ -21,7 +21,7 @@ fn main() {
App::new()
.add_plugins((
DefaultPlugins,
CoreWidgetsPlugin,
CoreWidgetsPlugins,
InputDispatchPlugin,
TabNavigationPlugin,
))

View File

@ -1,7 +1,9 @@
//! This example shows off the various Bevy Feathers widgets.
use bevy::{
core_widgets::{Callback, CoreRadio, CoreRadioGroup, CoreWidgetsPlugin, SliderStep},
core_widgets::{
Callback, CoreRadio, CoreRadioGroup, CoreWidgetsPlugins, SliderPrecision, SliderStep,
},
feathers::{
controls::{
button, checkbox, radio, slider, toggle_switch, ButtonProps, ButtonVariant,
@ -25,7 +27,7 @@ fn main() {
App::new()
.add_plugins((
DefaultPlugins,
CoreWidgetsPlugin,
CoreWidgetsPlugins,
InputDispatchPlugin,
TabNavigationPlugin,
FeathersPlugin,
@ -259,7 +261,7 @@ fn demo_root(commands: &mut Commands) -> impl Bundle {
value: 20.0,
..default()
},
SliderStep(10.)
(SliderStep(10.), SliderPrecision(2)),
),
]
),],

View File

@ -0,0 +1,116 @@
---
title: Many render resources now initialized in `RenderStartup`
pull_requests: [19841, 19926, 19885, 19886, 19897, 19898, 19901]
---
Many render resources are **no longer present** during `Plugin::finish`. Instead they are
initialized during `RenderStartup` (which occurs once the app starts running). If you only access
the resource during the `Render` schedule, then there should be no change. However, if you need one
of these render resources to initialize your own resource, you will need to convert your resource
initialization into a system.
The following are the (public) resources that are now initialized in `RenderStartup`.
- `CasPipeline`
- `FxaaPipeline`
- `SmaaPipelines`
- `TaaPipeline`
- `BoxShadowPipeline`
- `GradientPipeline`
- `UiPipeline`
- `UiMaterialPipeline<M>`
- `UiTextureSlicePipeline`
The vast majority of cases for initializing render resources look like so (in Bevy 0.16):
```rust
impl Plugin for MyRenderingPlugin {
fn build(&self, app: &mut App) {
// Do nothing??
}
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<MyRenderResource>();
render_app.add_systems(Render, my_render_system);
}
}
pub struct MyRenderResource {
...
}
impl FromWorld for MyRenderResource {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let render_adapter = world.resource::<RenderAdapter>();
let asset_server = world.resource::<AssetServer>();
MyRenderResource {
...
}
}
}
```
The two main things to focus on are:
1. The resource implements the `FromWorld` trait which collects all its dependent resources (most
commonly, `RenderDevice`), and then creates an instance of the resource.
2. The plugin adds its systems and resources in `Plugin::finish`.
First, we need to rewrite our `FromWorld` implementation as a system. This generally means
converting calls to `World::resource` into system params, and then using `Commands` to insert the
resource. In the above case, that would look like:
```rust
// Just a regular old system!!
fn init_my_resource(
mut commands: Commands,
render_device: Res<RenderDevice>,
render_adapter: Res<RenderAdapter>,
asset_server: Res<AssetServer>,
) {
commands.insert_resource(MyRenderResource {
...
});
}
```
Each case will be slightly different. Two notes to be wary of:
1. Functions that accept `&RenderDevice` for example may no longer compile after switching to
`Res<RenderDevice>`. This can be resolved by passing `&render_device` instead of
`render_device`.
2. If you are using `load_embedded_asset(world, "my_asset.png")`, you may need to first add
`asset_server` as a system param, then change this to
`load_embedded_asset(asset_server.as_ref(), "my_asset.png")`.
Now that we have our initialization system, we just need to add the system to `RenderStartup`:
```rust
impl Plugin for MyRenderingPlugin {
fn build(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.add_systems(RenderStartup, init_my_resource)
.add_systems(Render, my_render_system);
}
// No more finish!!
}
```
In addition, if your resource requires one of the affected systems above, you will need to use
system ordering to ensure your resource initializes after the other system. For example, if your
system uses `Res<UiPipeline>`, you will need to add an ordering like:
```rust
render_app.add_systems(RenderStartup, init_my_resource.after(init_ui_pipeline));
```

View File

@ -0,0 +1,18 @@
---
title: Frame Time Graph
authors: ["@IceSentry", "@Zeophlite"]
pull_requests: [12561, 19277]
---
(TODO: Embed frame time graph gif from 12561)
Frame time is often more important to know than FPS but because of the temporal nature of it, just seeing a number is not enough.
Seeing a graph that shows the history makes it easier to reason about performance.
Enable the `bevy_dev_tools` feature, and add in `FpsOverlayPlugin` to add a bar graph of the frame time history.
Each bar is scaled based on the frame time where a bigger frame time will give a taller and wider bar.
The color also scales with that frame time where red is at or bellow the minimum target fps and green is at or above the target maximum frame rate.
Anything between those 2 values will be interpolated between green and red based on the frame time.
The algorithm is highly inspired by [Adam Sawicki's article on visualizing frame times](https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times).

View File

@ -1,7 +1,7 @@
---
title: Headless Widgets
authors: ["@viridia", "@ickshonpe", "@alice-i-cecile"]
pull_requests: [19366, 19584, 19665, 19778, 19803]
pull_requests: [19366, 19584, 19665, 19778, 19803, 20032, 20036]
---
Bevy's `Button` and `Interaction` components have been around for a long time. Unfortunately

View File

@ -0,0 +1,103 @@
---
title: `RenderStartup` and making the renderer my ECS-y
authors: ["@IceSentry", "@andriyDev"]
pull_requests: [19841, 19926, 19885, 19886, 19897, 19898, 19901]
---
Previous rendering code looked quite different from other Bevy code. In general, resources were
initialized with the `FromWorld` trait (where most Bevy code only uses the `Default` trait for most
resources) and systems/resources were added in `Plugin::finish` (where nearly all Bevy code does not
use `Plugin::finish` at all). This difference with Bevy code can make it harder for new developers
to learn rendering, and can result in "cargo cult" copying of rendering code (e.g., "is it important
to be using `FromWorld` here? Better to be safe and just do what the rendering code is doing!").
As a step towards making the renderer more accessible (and maintainable), we have introduced the
`RenderStartup` schedule and ported many rendering resources to be initialized in `RenderStartup`
with systems! This has several benefits:
1. Creating resources in systems makes it clearer that rendering resources **are just regular
resources**. Hopefully, this better communicates that how you initialize these resources is
totally up to you!
2. We can now use the system ordering API to ensure that resources are initialized in the correct
order. For example, we can do `init_material_pipeline.after(init_mesh_pipeline)` if we need the
mesh pipeline to initialize the material pipeline.
3. These initialization systems clearly describe what resources they require through their argument
list. If a system has an argument of `deferred_lighting_layout: Res<DeferredLightingLayout>`, it
clearly documents that this system needs to be run **after** we initialize the
`DeferredLightingLayout`.
We want developers to become more familiar and comfortable with Bevy's rendering stack, and hope
that bringing the renderer closer to regular ECS code will encourage that. Code that previously looked
like this (in Bevy 0.16):
```rust
impl Plugin for MyRenderingPlugin {
fn build(&self, app: &mut App) {
// Do nothing??
}
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<MyRenderResource>();
}
}
pub struct MyRenderResource {
...
}
impl FromWorld for MyRenderResource {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let render_adapter = world.resource::<RenderAdapter>();
let asset_server = world.resource::<AssetServer>();
MyRenderResource {
...
}
}
}
```
Can now be written like:
```rust
impl Plugin for MyRenderingPlugin {
fn build(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.add_systems(RenderStartup, init_my_resource);
}
// No more finish!!
}
pub struct MyRenderResource {
...
}
// Just a regular old system!!
fn init_my_resource(
mut commands: Commands,
render_device: Res<RenderDevice>,
render_adapter: Res<RenderAdapter>,
asset_server: Res<AssetServer>,
) {
commands.insert_resource(MyRenderResource {
...
});
}
```
We highly encourage users to port their own rendering resources to this new system approach (and for
resources whose initialization depends on a Bevy core resource, it may be required). In fact, we
encourage users to abandon `Plugin::finish` entirely and move all their system and resource
initializations for rendering into `Plugin::build` instead.
As stated before, we've ported many resources to be initialized in `RenderStartup`. See the
migration guide for a full list of affected resources.