Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/renderer/render_context.rs
9328 views
1
use super::WgpuWrapper;
2
use crate::diagnostic::internal::DiagnosticsRecorder;
3
use crate::render_phase::TrackedRenderPass;
4
use crate::render_resource::{CommandEncoder, RenderPassDescriptor};
5
use crate::renderer::RenderDevice;
6
use bevy_derive::{Deref, DerefMut};
7
use bevy_ecs::change_detection::Tick;
8
use bevy_ecs::component::ComponentId;
9
use bevy_ecs::prelude::*;
10
use bevy_ecs::query::{FilteredAccessSet, QueryData, QueryFilter, QueryState};
11
use bevy_ecs::system::{
12
Deferred, SystemBuffer, SystemMeta, SystemParam, SystemParamValidationError,
13
};
14
use bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell;
15
use bevy_ecs::world::DeferredWorld;
16
use bevy_log::info_span;
17
use core::marker::PhantomData;
18
use wgpu::CommandBuffer;
19
20
#[derive(Default)]
21
struct PendingCommandBuffersInner {
22
buffers: Vec<CommandBuffer>,
23
encoders: Vec<CommandEncoder>,
24
}
25
26
/// A resource that holds command buffers and encoders that are pending submission to the render queue.
27
#[derive(Resource)]
28
pub struct PendingCommandBuffers(WgpuWrapper<PendingCommandBuffersInner>);
29
30
impl Default for PendingCommandBuffers {
31
fn default() -> Self {
32
Self(WgpuWrapper::new(PendingCommandBuffersInner::default()))
33
}
34
}
35
36
impl PendingCommandBuffers {
37
pub fn push(&mut self, buffers: impl IntoIterator<Item = CommandBuffer>) {
38
self.0.buffers.extend(buffers);
39
}
40
41
pub fn push_encoder(&mut self, encoder: CommandEncoder) {
42
self.0.encoders.push(encoder);
43
}
44
45
pub fn take(&mut self) -> Vec<CommandBuffer> {
46
let encoders: Vec<_> = self.0.encoders.drain(..).collect();
47
for encoder in encoders {
48
self.0.buffers.push(encoder.finish());
49
}
50
core::mem::take(&mut self.0.buffers)
51
}
52
53
pub fn is_empty(&self) -> bool {
54
self.0.buffers.is_empty() && self.0.encoders.is_empty()
55
}
56
57
pub fn len(&self) -> usize {
58
self.0.buffers.len() + self.0.encoders.len()
59
}
60
}
61
62
#[derive(Default)]
63
struct RenderContextStateInner {
64
command_encoder: Option<CommandEncoder>,
65
command_buffers: Vec<CommandBuffer>,
66
render_device: Option<RenderDevice>,
67
}
68
69
/// A resource that holds the current render context state, including command encoder and command buffers.
70
/// This is used internally by the [`RenderContext`] system parameter. Implements [`SystemBuffer`] to flush
71
/// command buffers at the end of each render system in topological system order.
72
pub struct RenderContextState(WgpuWrapper<RenderContextStateInner>);
73
74
impl Default for RenderContextState {
75
fn default() -> Self {
76
Self(WgpuWrapper::new(RenderContextStateInner::default()))
77
}
78
}
79
80
impl RenderContextState {
81
fn flush_encoder(&mut self) {
82
if let Some(encoder) = self.0.command_encoder.take() {
83
self.0.command_buffers.push(encoder.finish());
84
}
85
}
86
87
fn command_encoder(&mut self) -> &mut CommandEncoder {
88
let render_device = self
89
.0
90
.render_device
91
.clone()
92
.expect("RenderDevice must be set before accessing command_encoder");
93
94
self.0.command_encoder.get_or_insert_with(|| {
95
render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default())
96
})
97
}
98
99
pub fn finish(&mut self) -> Vec<CommandBuffer> {
100
self.flush_encoder();
101
core::mem::take(&mut self.0.command_buffers)
102
}
103
}
104
105
impl SystemBuffer for RenderContextState {
106
fn queue(&mut self, system_meta: &SystemMeta, mut world: DeferredWorld) {
107
let _span = info_span!("RenderContextState::apply", system = %system_meta.name()).entered();
108
109
let inner = &mut *self.0;
110
111
// flush to ensure correct submission order
112
if let Some(encoder) = inner.command_encoder.take() {
113
inner.command_buffers.push(encoder.finish());
114
}
115
116
if !inner.command_buffers.is_empty() {
117
let mut pending = world.resource_mut::<PendingCommandBuffers>();
118
pending.push(core::mem::take(&mut inner.command_buffers));
119
}
120
121
inner.render_device = None;
122
}
123
}
124
125
/// A system parameter that provides access to a command encoder and render device for issuing
126
/// rendering commands inside any system running beneath the root [`super::RenderGraph`] schedule in the
127
/// [`super::render_system`] system.
128
#[derive(SystemParam)]
129
pub struct RenderContext<'w, 's> {
130
state: Deferred<'s, RenderContextState>,
131
render_device: Res<'w, RenderDevice>,
132
diagnostics_recorder: Option<Res<'w, DiagnosticsRecorder>>,
133
}
134
135
impl<'w, 's> RenderContext<'w, 's> {
136
fn ensure_device(&mut self) {
137
if self.state.0.render_device.is_none() {
138
self.state.0.render_device = Some(self.render_device.clone());
139
}
140
}
141
142
/// Returns the render device associated with this render context.
143
pub fn render_device(&self) -> &RenderDevice {
144
&self.render_device
145
}
146
147
/// Returns the diagnostics recorder, if available.
148
pub fn diagnostic_recorder(&self) -> Option<Res<'w, DiagnosticsRecorder>> {
149
self.diagnostics_recorder.as_ref().map(Res::clone)
150
}
151
152
/// Returns the current command encoder, creating one if it does not already exist.
153
pub fn command_encoder(&mut self) -> &mut CommandEncoder {
154
self.ensure_device();
155
self.state.command_encoder()
156
}
157
158
/// Begins a tracked render pass with the given descriptor.
159
pub fn begin_tracked_render_pass<'a>(
160
&'a mut self,
161
descriptor: RenderPassDescriptor<'_>,
162
) -> TrackedRenderPass<'a> {
163
self.ensure_device();
164
165
let command_encoder = self.state.0.command_encoder.get_or_insert_with(|| {
166
self.render_device
167
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default())
168
});
169
170
let render_pass = command_encoder.begin_render_pass(&descriptor);
171
TrackedRenderPass::new(&self.render_device, render_pass)
172
}
173
174
/// Adds a finished command buffer to be submitted later.
175
pub fn add_command_buffer(&mut self, command_buffer: CommandBuffer) {
176
self.state.flush_encoder();
177
self.state.0.command_buffers.push(command_buffer);
178
}
179
}
180
181
/// A system parameter that can be used to explicitly flush pending command buffers to the render queue.
182
/// This is typically not necessary, as command buffers are automatically flushed at the end of each
183
/// render system. However, in some cases it may be useful to flush command buffers earlier.
184
#[derive(SystemParam)]
185
pub struct FlushCommands<'w> {
186
pending: ResMut<'w, PendingCommandBuffers>,
187
queue: Res<'w, super::RenderQueue>,
188
}
189
190
impl<'w> FlushCommands<'w> {
191
/// Flushes all pending command buffers to the render queue.
192
pub fn flush(&mut self) {
193
let buffers = self.pending.take();
194
if !buffers.is_empty() {
195
self.queue.submit(buffers);
196
}
197
}
198
}
199
200
/// The entity corresponding to the current view being rendered.
201
#[derive(Resource, Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut)]
202
pub struct CurrentView(pub Entity);
203
204
/// A query that fetches components for the entity corresponding to the current view being rendered,
205
/// as defined by the [`CurrentView`] resource, equivalent to `query.get(current_view.entity())`.
206
pub struct ViewQuery<'w, 's, D: QueryData, F: QueryFilter = ()> {
207
entity: Entity,
208
item: D::Item<'w, 's>,
209
_filter: PhantomData<F>,
210
}
211
212
impl<'w, 's, D: QueryData, F: QueryFilter> ViewQuery<'w, 's, D, F> {
213
#[inline]
214
pub fn entity(&self) -> Entity {
215
self.entity
216
}
217
218
#[inline]
219
pub fn into_inner(self) -> D::Item<'w, 's> {
220
self.item
221
}
222
}
223
224
pub struct ViewQueryState<D: QueryData, F: QueryFilter> {
225
resource_id: ComponentId,
226
query_state: QueryState<D, F>,
227
}
228
229
// SAFETY: ViewQuery accesses the CurrentView resource (read) and query components.
230
// Access is properly registered in init_access.
231
unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
232
for ViewQuery<'a, '_, D, F>
233
{
234
type State = ViewQueryState<D, F>;
235
type Item<'w, 's> = ViewQuery<'w, 's, D, F>;
236
237
fn init_state(world: &mut World) -> Self::State {
238
ViewQueryState {
239
resource_id: world
240
.components_registrator()
241
.register_resource::<CurrentView>(),
242
query_state: QueryState::new(world),
243
}
244
}
245
246
fn init_access(
247
state: &Self::State,
248
system_meta: &mut SystemMeta,
249
component_access_set: &mut FilteredAccessSet,
250
world: &mut World,
251
) {
252
component_access_set.add_unfiltered_resource_read(state.resource_id);
253
254
<Query<'_, '_, D, F> as SystemParam>::init_access(
255
&state.query_state,
256
system_meta,
257
component_access_set,
258
world,
259
);
260
}
261
262
#[inline]
263
unsafe fn validate_param(
264
state: &mut Self::State,
265
_system_meta: &SystemMeta,
266
world: UnsafeWorldCell,
267
) -> Result<(), SystemParamValidationError> {
268
// SAFETY: We have registered resource read access in init_access
269
let current_view = unsafe { world.get_resource::<CurrentView>() };
270
271
let Some(current_view) = current_view else {
272
return Err(SystemParamValidationError::skipped::<Self>(
273
"CurrentView resource not present",
274
));
275
};
276
277
let entity = current_view.entity();
278
279
// SAFETY: Query state access is properly registered in init_access.
280
// The caller ensures the world matches the one used in init_state.
281
let result = unsafe { state.query_state.get_unchecked(world, entity) };
282
283
if result.is_err() {
284
return Err(SystemParamValidationError::skipped::<Self>(
285
"Current view entity does not match query",
286
));
287
}
288
289
Ok(())
290
}
291
292
#[inline]
293
unsafe fn get_param<'w, 's>(
294
state: &'s mut Self::State,
295
_system_meta: &SystemMeta,
296
world: UnsafeWorldCell<'w>,
297
_change_tick: Tick,
298
) -> Self::Item<'w, 's> {
299
// SAFETY: We have registered resource read access and validate_param succeeded
300
let current_view = unsafe {
301
world
302
.get_resource::<CurrentView>()
303
.expect("CurrentView must exist")
304
};
305
306
let entity = current_view.entity();
307
308
// SAFETY: Query state access is properly registered in init_access.
309
// validate_param verified the entity matches.
310
let item = unsafe {
311
state
312
.query_state
313
.get_unchecked(world, entity)
314
.expect("view entity must match query")
315
};
316
317
ViewQuery {
318
entity,
319
item,
320
_filter: PhantomData,
321
}
322
}
323
}
324
325
// SAFETY: ViewQuery with ReadOnlyQueryData only reads from the world.
326
unsafe impl<'w, 's, D: bevy_ecs::query::ReadOnlyQueryData + 'static, F: QueryFilter + 'static>
327
bevy_ecs::system::ReadOnlySystemParam for ViewQuery<'w, 's, D, F>
328
{
329
}
330
331