Skip to content

Commit e45a77a

Browse files
authored
Use RenderStartup for occlusion_culling example. (#20184)
# Objective - Progress towards #19887. - This example was non-trivial to port so was done as a separate PR. ## Solution - This situation is very weird. We are trying to get features of the renderer back into the main world. We use an ArcMutex to communicate between the two worlds. - We now insert the resource into both worlds, but only with Option::None. - The `RenderStartup` init system initializes the resource with the feature values populated. This does result in a "regression" where there is a single frame where the `update_status_text` system doesn't react properly, since it doesn't have the features from the render world yet. ## Testing - Ran the `occlusion_culling` example and it worked!
1 parent 6f3827e commit e45a77a

File tree

1 file changed

+62
-51
lines changed

1 file changed

+62
-51
lines changed

examples/3d/occlusion_culling.rs

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@ use bevy::{
2626
prelude::*,
2727
render::{
2828
batching::gpu_preprocessing::{
29-
GpuPreprocessingMode, GpuPreprocessingSupport, IndirectParametersBuffers,
30-
IndirectParametersIndexed,
29+
GpuPreprocessingSupport, IndirectParametersBuffers, IndirectParametersIndexed,
3130
},
3231
experimental::occlusion_culling::OcclusionCulling,
3332
render_graph::{self, NodeRunError, RenderGraphContext, RenderGraphExt, RenderLabel},
3433
render_resource::{Buffer, BufferDescriptor, BufferUsages, MapMode},
3534
renderer::{RenderContext, RenderDevice},
3635
settings::WgpuFeatures,
37-
Render, RenderApp, RenderDebugFlags, RenderPlugin, RenderSystems,
36+
Render, RenderApp, RenderDebugFlags, RenderPlugin, RenderStartup, RenderSystems,
3837
},
3938
};
4039
use bytemuck::Pod;
@@ -111,7 +110,7 @@ struct IndirectParametersStagingBuffers {
111110
/// really care how up-to-date the counter of culled meshes is. If it's off by a
112111
/// few frames, that's no big deal.
113112
#[derive(Clone, Resource, Deref, DerefMut)]
114-
struct SavedIndirectParameters(Arc<Mutex<SavedIndirectParametersData>>);
113+
struct SavedIndirectParameters(Arc<Mutex<Option<SavedIndirectParametersData>>>);
115114

116115
/// A CPU-side copy of the GPU buffer that stores the indirect draw parameters.
117116
///
@@ -138,27 +137,31 @@ struct SavedIndirectParametersData {
138137
occlusion_culling_introspection_supported: bool,
139138
}
140139

141-
impl FromWorld for SavedIndirectParameters {
142-
fn from_world(world: &mut World) -> SavedIndirectParameters {
143-
let render_device = world.resource::<RenderDevice>();
144-
SavedIndirectParameters(Arc::new(Mutex::new(SavedIndirectParametersData {
145-
data: vec![],
146-
count: 0,
147-
// This gets set to false in `readback_indirect_buffers` if we don't
148-
// support GPU preprocessing.
149-
occlusion_culling_supported: true,
150-
// In order to determine how many meshes were culled, we look at the
151-
// indirect count buffer that Bevy only populates if the platform
152-
// supports `multi_draw_indirect_count`. So, if we don't have that
153-
// feature, then we don't bother to display how many meshes were
154-
// culled.
155-
occlusion_culling_introspection_supported: render_device
156-
.features()
157-
.contains(WgpuFeatures::MULTI_DRAW_INDIRECT_COUNT),
158-
})))
140+
impl SavedIndirectParameters {
141+
fn new() -> Self {
142+
Self(Arc::new(Mutex::new(None)))
159143
}
160144
}
161145

146+
fn init_saved_indirect_parameters(
147+
render_device: Res<RenderDevice>,
148+
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
149+
saved_indirect_parameters: Res<SavedIndirectParameters>,
150+
) {
151+
let mut saved_indirect_parameters = saved_indirect_parameters.0.lock().unwrap();
152+
*saved_indirect_parameters = Some(SavedIndirectParametersData {
153+
data: vec![],
154+
count: 0,
155+
occlusion_culling_supported: gpu_preprocessing_support.is_culling_supported(),
156+
// In order to determine how many meshes were culled, we look at the indirect count buffer
157+
// that Bevy only populates if the platform supports `multi_draw_indirect_count`. So, if we
158+
// don't have that feature, then we don't bother to display how many meshes were culled.
159+
occlusion_culling_introspection_supported: render_device
160+
.features()
161+
.contains(WgpuFeatures::MULTI_DRAW_INDIRECT_COUNT),
162+
});
163+
}
164+
162165
/// The demo's current settings.
163166
#[derive(Resource)]
164167
struct AppStatus {
@@ -210,12 +213,25 @@ fn main() {
210213

211214
impl Plugin for ReadbackIndirectParametersPlugin {
212215
fn build(&self, app: &mut App) {
216+
// Create the `SavedIndirectParameters` resource that we're going to use
217+
// to communicate between the thread that the GPU-to-CPU readback
218+
// callback runs on and the main application threads. This resource is
219+
// atomically reference counted. We store one reference to the
220+
// `SavedIndirectParameters` in the main app and another reference in
221+
// the render app.
222+
let saved_indirect_parameters = SavedIndirectParameters::new();
223+
app.insert_resource(saved_indirect_parameters.clone());
224+
213225
// Fetch the render app.
214226
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
215227
return;
216228
};
217229

218230
render_app
231+
// Insert another reference to the `SavedIndirectParameters`.
232+
.insert_resource(saved_indirect_parameters)
233+
// Setup the parameters in RenderStartup.
234+
.add_systems(RenderStartup, init_saved_indirect_parameters)
219235
.init_resource::<IndirectParametersStagingBuffers>()
220236
.add_systems(ExtractSchedule, readback_indirect_parameters)
221237
.add_systems(
@@ -245,26 +261,6 @@ impl Plugin for ReadbackIndirectParametersPlugin {
245261
),
246262
);
247263
}
248-
249-
fn finish(&self, app: &mut App) {
250-
// Create the `SavedIndirectParameters` resource that we're going to use
251-
// to communicate between the thread that the GPU-to-CPU readback
252-
// callback runs on and the main application threads. This resource is
253-
// atomically reference counted. We store one reference to the
254-
// `SavedIndirectParameters` in the main app and another reference in
255-
// the render app.
256-
let saved_indirect_parameters = SavedIndirectParameters::from_world(app.world_mut());
257-
app.insert_resource(saved_indirect_parameters.clone());
258-
259-
// Fetch the render app.
260-
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
261-
return;
262-
};
263-
264-
render_app
265-
// Insert another reference to the `SavedIndirectParameters`.
266-
.insert_resource(saved_indirect_parameters);
267-
}
268264
}
269265

270266
/// Spawns all the objects in the scene.
@@ -550,6 +546,10 @@ fn update_status_text(
550546
occlusion_culling_introspection_supported,
551547
): (u32, bool, bool) = {
552548
let saved_indirect_parameters = saved_indirect_parameters.lock().unwrap();
549+
let Some(saved_indirect_parameters) = saved_indirect_parameters.as_ref() else {
550+
// Bail out early if the resource isn't initialized yet.
551+
return;
552+
};
553553
(
554554
saved_indirect_parameters
555555
.data
@@ -597,14 +597,15 @@ fn update_status_text(
597597
fn readback_indirect_parameters(
598598
mut indirect_parameters_staging_buffers: ResMut<IndirectParametersStagingBuffers>,
599599
saved_indirect_parameters: Res<SavedIndirectParameters>,
600-
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
601600
) {
602-
// If culling isn't supported on this platform, note that, and bail.
603-
if gpu_preprocessing_support.max_supported_mode != GpuPreprocessingMode::Culling {
604-
saved_indirect_parameters
605-
.lock()
606-
.unwrap()
607-
.occlusion_culling_supported = false;
601+
// If culling isn't supported on this platform, bail.
602+
if !saved_indirect_parameters
603+
.lock()
604+
.unwrap()
605+
.as_ref()
606+
.unwrap()
607+
.occlusion_culling_supported
608+
{
608609
return;
609610
}
610611

@@ -620,10 +621,20 @@ fn readback_indirect_parameters(
620621
let saved_indirect_parameters_0 = (**saved_indirect_parameters).clone();
621622
let saved_indirect_parameters_1 = (**saved_indirect_parameters).clone();
622623
readback_buffer::<IndirectParametersIndexed>(data_buffer, move |indirect_parameters| {
623-
saved_indirect_parameters_0.lock().unwrap().data = indirect_parameters.to_vec();
624+
saved_indirect_parameters_0
625+
.lock()
626+
.unwrap()
627+
.as_mut()
628+
.unwrap()
629+
.data = indirect_parameters.to_vec();
624630
});
625631
readback_buffer::<u32>(batch_sets_buffer, move |indirect_parameters_count| {
626-
saved_indirect_parameters_1.lock().unwrap().count = indirect_parameters_count[0];
632+
saved_indirect_parameters_1
633+
.lock()
634+
.unwrap()
635+
.as_mut()
636+
.unwrap()
637+
.count = indirect_parameters_count[0];
627638
});
628639
}
629640

0 commit comments

Comments
 (0)