bevy_render/
extract_instances.rs

1//! Convenience logic for turning components from the main world into extracted
2//! instances in the render world.
3//!
4//! This is essentially the same as the `extract_component` module, but
5//! higher-performance because it avoids the ECS overhead.
6
7use core::marker::PhantomData;
8
9use bevy_app::{App, Plugin};
10use bevy_camera::visibility::ViewVisibility;
11use bevy_derive::{Deref, DerefMut};
12use bevy_ecs::{
13    prelude::Entity,
14    query::{QueryFilter, QueryItem, ReadOnlyQueryData},
15    resource::Resource,
16    system::{Query, ResMut},
17};
18
19use crate::sync_world::MainEntityHashMap;
20use crate::{Extract, ExtractSchedule, RenderApp};
21
22/// Describes how to extract data needed for rendering from a component or
23/// components.
24///
25/// Before rendering, any applicable components will be transferred from the
26/// main world to the render world in the [`ExtractSchedule`] step.
27///
28/// This is essentially the same as
29/// [`ExtractComponent`](crate::extract_component::ExtractComponent), but
30/// higher-performance because it avoids the ECS overhead.
31pub trait ExtractInstance: Send + Sync + Sized + 'static {
32    /// ECS [`ReadOnlyQueryData`] to fetch the components to extract.
33    type QueryData: ReadOnlyQueryData;
34    /// Filters the entities with additional constraints.
35    type QueryFilter: QueryFilter;
36
37    /// Defines how the component is transferred into the "render world".
38    fn extract(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self>;
39}
40
41/// This plugin extracts one or more components into the "render world" as
42/// extracted instances.
43///
44/// Therefore it sets up the [`ExtractSchedule`] step for the specified
45/// [`ExtractedInstances`].
46#[derive(Default)]
47pub struct ExtractInstancesPlugin<EI>
48where
49    EI: ExtractInstance,
50{
51    only_extract_visible: bool,
52    marker: PhantomData<fn() -> EI>,
53}
54
55/// Stores all extract instances of a type in the render world.
56#[derive(Resource, Deref, DerefMut)]
57pub struct ExtractedInstances<EI>(MainEntityHashMap<EI>)
58where
59    EI: ExtractInstance;
60
61impl<EI> Default for ExtractedInstances<EI>
62where
63    EI: ExtractInstance,
64{
65    fn default() -> Self {
66        Self(Default::default())
67    }
68}
69
70impl<EI> ExtractInstancesPlugin<EI>
71where
72    EI: ExtractInstance,
73{
74    /// Creates a new [`ExtractInstancesPlugin`] that unconditionally extracts to
75    /// the render world, whether the entity is visible or not.
76    pub fn new() -> Self {
77        Self {
78            only_extract_visible: false,
79            marker: PhantomData,
80        }
81    }
82
83    /// Creates a new [`ExtractInstancesPlugin`] that extracts to the render world
84    /// if and only if the entity it's attached to is visible.
85    pub fn extract_visible() -> Self {
86        Self {
87            only_extract_visible: true,
88            marker: PhantomData,
89        }
90    }
91}
92
93impl<EI> Plugin for ExtractInstancesPlugin<EI>
94where
95    EI: ExtractInstance,
96{
97    fn build(&self, app: &mut App) {
98        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
99            render_app.init_resource::<ExtractedInstances<EI>>();
100            if self.only_extract_visible {
101                render_app.add_systems(ExtractSchedule, extract_visible::<EI>);
102            } else {
103                render_app.add_systems(ExtractSchedule, extract_all::<EI>);
104            }
105        }
106    }
107}
108
109fn extract_all<EI>(
110    mut extracted_instances: ResMut<ExtractedInstances<EI>>,
111    query: Extract<Query<(Entity, EI::QueryData), EI::QueryFilter>>,
112) where
113    EI: ExtractInstance,
114{
115    extracted_instances.clear();
116    for (entity, other) in &query {
117        if let Some(extract_instance) = EI::extract(other) {
118            extracted_instances.insert(entity.into(), extract_instance);
119        }
120    }
121}
122
123fn extract_visible<EI>(
124    mut extracted_instances: ResMut<ExtractedInstances<EI>>,
125    query: Extract<Query<(Entity, &ViewVisibility, EI::QueryData), EI::QueryFilter>>,
126) where
127    EI: ExtractInstance,
128{
129    extracted_instances.clear();
130    for (entity, view_visibility, other) in &query {
131        if view_visibility.get()
132            && let Some(extract_instance) = EI::extract(other)
133        {
134            extracted_instances.insert(entity.into(), extract_instance);
135        }
136    }
137}