Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/extract_component.rs
9299 views
1
use crate::{
2
render_resource::{encase::internal::WriteInto, DynamicUniformBuffer, ShaderType},
3
renderer::{RenderDevice, RenderQueue},
4
sync_component::{SyncComponent, SyncComponentPlugin},
5
sync_world::RenderEntity,
6
Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
7
};
8
use bevy_app::{App, Plugin};
9
use bevy_camera::visibility::ViewVisibility;
10
use bevy_ecs::{
11
component::Component,
12
prelude::*,
13
query::{QueryFilter, QueryItem, ReadOnlyQueryData},
14
};
15
use core::{marker::PhantomData, ops::Deref};
16
17
pub use bevy_render_macros::ExtractComponent;
18
19
/// Stores the index of a uniform inside of [`ComponentUniforms`].
20
#[derive(Component)]
21
pub struct DynamicUniformIndex<C: Component> {
22
index: u32,
23
marker: PhantomData<C>,
24
}
25
26
impl<C: Component> DynamicUniformIndex<C> {
27
#[inline]
28
pub fn index(&self) -> u32 {
29
self.index
30
}
31
}
32
33
/// Describes how a component gets extracted for rendering.
34
///
35
/// Therefore the component is transferred from the "app world" into the "render
36
/// world" in the [`ExtractSchedule`] step. This functionality is enabled by
37
/// adding [`ExtractComponentPlugin`] with the component type.
38
///
39
/// The Out type is defined in [`SyncComponent`].
40
///
41
/// The marker type is only used as a way to bypass the orphan rules. To
42
/// implement the trait for a foreign type you can use a local type as the
43
/// marker, e.g. the type of the plugin that calls [`ExtractComponentPlugin`].
44
pub trait ExtractComponent<Marker = ()>: SyncComponent {
45
/// ECS [`ReadOnlyQueryData`] to fetch the components to extract.
46
type QueryData: ReadOnlyQueryData;
47
/// Filters the entities with additional constraints.
48
type QueryFilter: QueryFilter;
49
50
/// Defines how the component is transferred into the "render world".
51
fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out>;
52
}
53
54
/// This plugin prepares the components of the corresponding type for the GPU
55
/// by transforming them into uniforms.
56
///
57
/// They can then be accessed from the [`ComponentUniforms`] resource.
58
/// For referencing the newly created uniforms a [`DynamicUniformIndex`] is inserted
59
/// for every processed entity.
60
///
61
/// Therefore it sets up the [`RenderSystems::Prepare`] step
62
/// for the specified [`ExtractComponent`].
63
pub struct UniformComponentPlugin<C>(PhantomData<fn() -> C>);
64
65
impl<C> Default for UniformComponentPlugin<C> {
66
fn default() -> Self {
67
Self(PhantomData)
68
}
69
}
70
71
impl<C: Component + ShaderType + WriteInto + Clone> Plugin for UniformComponentPlugin<C> {
72
fn build(&self, app: &mut App) {
73
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
74
render_app
75
.insert_resource(ComponentUniforms::<C>::default())
76
.add_systems(
77
Render,
78
prepare_uniform_components::<C>.in_set(RenderSystems::PrepareResources),
79
);
80
}
81
}
82
}
83
84
/// Stores all uniforms of the component type.
85
#[derive(Resource)]
86
pub struct ComponentUniforms<C: Component + ShaderType> {
87
uniforms: DynamicUniformBuffer<C>,
88
}
89
90
impl<C: Component + ShaderType> Deref for ComponentUniforms<C> {
91
type Target = DynamicUniformBuffer<C>;
92
93
#[inline]
94
fn deref(&self) -> &Self::Target {
95
&self.uniforms
96
}
97
}
98
99
impl<C: Component + ShaderType> ComponentUniforms<C> {
100
#[inline]
101
pub fn uniforms(&self) -> &DynamicUniformBuffer<C> {
102
&self.uniforms
103
}
104
}
105
106
impl<C: Component + ShaderType> Default for ComponentUniforms<C> {
107
fn default() -> Self {
108
Self {
109
uniforms: Default::default(),
110
}
111
}
112
}
113
114
/// This system prepares all components of the corresponding component type.
115
/// They are transformed into uniforms and stored in the [`ComponentUniforms`] resource.
116
fn prepare_uniform_components<C>(
117
mut commands: Commands,
118
render_device: Res<RenderDevice>,
119
render_queue: Res<RenderQueue>,
120
mut component_uniforms: ResMut<ComponentUniforms<C>>,
121
components: Query<(Entity, &C)>,
122
) where
123
C: Component + ShaderType + WriteInto + Clone,
124
{
125
let components_iter = components.iter();
126
let count = components_iter.len();
127
let Some(mut writer) =
128
component_uniforms
129
.uniforms
130
.get_writer(count, &render_device, &render_queue)
131
else {
132
return;
133
};
134
let entities = components_iter
135
.map(|(entity, component)| {
136
(
137
entity,
138
DynamicUniformIndex::<C> {
139
index: writer.write(component),
140
marker: PhantomData,
141
},
142
)
143
})
144
.collect::<Vec<_>>();
145
commands.try_insert_batch(entities);
146
}
147
148
/// This plugin extracts the components into the render world for synced
149
/// entities. To do so, it sets up the [`ExtractSchedule`] step for the
150
/// specified [`ExtractComponent`].
151
///
152
/// It also registers [`SyncComponentPlugin`] to ensure the extracted components
153
/// are deleted if the main world components are removed.
154
pub struct ExtractComponentPlugin<C, F = ()> {
155
only_extract_visible: bool,
156
marker: PhantomData<fn() -> (C, F)>,
157
}
158
159
impl<C, F> Default for ExtractComponentPlugin<C, F> {
160
fn default() -> Self {
161
Self {
162
only_extract_visible: false,
163
marker: PhantomData,
164
}
165
}
166
}
167
168
impl<C, F> ExtractComponentPlugin<C, F> {
169
pub fn extract_visible() -> Self {
170
Self {
171
only_extract_visible: true,
172
marker: PhantomData,
173
}
174
}
175
}
176
177
impl<C: ExtractComponent<Marker>, Marker: 'static> Plugin for ExtractComponentPlugin<C, Marker> {
178
fn build(&self, app: &mut App) {
179
app.add_plugins(SyncComponentPlugin::<C>::default());
180
181
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
182
if self.only_extract_visible {
183
render_app.add_systems(ExtractSchedule, extract_visible_components::<C, Marker>);
184
} else {
185
render_app.add_systems(ExtractSchedule, extract_components::<C, Marker>);
186
}
187
}
188
}
189
}
190
191
/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToRenderWorld`].
192
fn extract_components<C: ExtractComponent<Marker>, Marker>(
193
mut commands: Commands,
194
mut previous_len: Local<usize>,
195
query: Extract<Query<(RenderEntity, C::QueryData), C::QueryFilter>>,
196
) {
197
let mut values = Vec::with_capacity(*previous_len);
198
for (entity, query_item) in &query {
199
if let Some(component) = C::extract_component(query_item) {
200
values.push((entity, component));
201
} else {
202
commands.entity(entity).remove::<C::Out>();
203
}
204
}
205
*previous_len = values.len();
206
commands.try_insert_batch(values);
207
}
208
209
/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are visible and synced via [`crate::sync_world::SyncToRenderWorld`].
210
fn extract_visible_components<C: ExtractComponent<Marker>, Marker>(
211
mut commands: Commands,
212
mut previous_len: Local<usize>,
213
query: Extract<Query<(RenderEntity, &ViewVisibility, C::QueryData), C::QueryFilter>>,
214
) {
215
let mut values = Vec::with_capacity(*previous_len);
216
for (entity, view_visibility, query_item) in &query {
217
if view_visibility.get() {
218
if let Some(component) = C::extract_component(query_item) {
219
values.push((entity, component));
220
} else {
221
commands.entity(entity).remove::<C::Out>();
222
}
223
}
224
}
225
*previous_len = values.len();
226
commands.try_insert_batch(values);
227
}
228
229