From 262f471934c99d77e8767275d95a7b4dbaf6a7c7 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:29:11 +0000 Subject: [PATCH] move mesh uniform construction out of Core3d (#16188) # Objective gpu based mesh uniform construction in the `GpuPreprocessNode` is currently in `Core3d`. The node iterates all views and schedules the uniform construction for each. so - when there are multiple 3d cameras, it runs multiple times on each view - if a view wants to render meshes but doesn't use the `Core3d` graph, the camera must run later than at least one `Core3d`-based camera (or add the node to its own graph, duplicating the work) - If views want to share mesh uniforms there is no way to avoid running the preprocessing for every view ## Solution - move the node to the top level of the rendergraph, before the camera driver node - make the `PreprocessBindGroup` `clone`able, and add a `SkipGpuPreprocessing` component to allow opting out per view --- crates/bevy_pbr/src/render/gpu_preprocess.rs | 38 ++++++++++++-------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/crates/bevy_pbr/src/render/gpu_preprocess.rs b/crates/bevy_pbr/src/render/gpu_preprocess.rs index 3db9508c17..ee28005a8c 100644 --- a/crates/bevy_pbr/src/render/gpu_preprocess.rs +++ b/crates/bevy_pbr/src/render/gpu_preprocess.rs @@ -10,11 +10,10 @@ use core::num::NonZero; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle}; -use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d}; use bevy_ecs::{ component::Component, entity::Entity, - query::{Has, QueryState}, + query::{Has, QueryState, Without}, schedule::{common_conditions::resource_exists, IntoSystemConfigs as _}, system::{lifetimeless::Read, Commands, Res, ResMut, Resource}, world::{FromWorld, World}, @@ -24,7 +23,8 @@ use bevy_render::{ BatchedInstanceBuffers, GpuPreprocessingSupport, IndirectParameters, IndirectParametersBuffer, PreprocessWorkItem, }, - render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext}, + graph::CameraDriverLabel, + render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext}, render_resource::{ binding_types::{storage_buffer, storage_buffer_read_only, uniform_buffer}, BindGroup, BindGroupEntries, BindGroupLayout, BindingResource, BufferBinding, @@ -65,12 +65,15 @@ pub struct GpuMeshPreprocessPlugin { /// The render node for the mesh uniform building pass. pub struct GpuPreprocessNode { - view_query: QueryState<( - Entity, - Read, - Read, - Has, - )>, + view_query: QueryState< + ( + Entity, + Read, + Read, + Has, + ), + Without, + >, } /// The compute shader pipelines for the mesh uniform building pass. @@ -108,9 +111,14 @@ bitflags! { /// The compute shader bind group for the mesh uniform building pass. /// /// This goes on the view. -#[derive(Component)] +#[derive(Component, Clone)] pub struct PreprocessBindGroup(BindGroup); +/// Stops the `GpuPreprocessNode` attempting to generate the buffer for this view +/// useful to avoid duplicating effort if the bind group is shared between views +#[derive(Component)] +pub struct SkipGpuPreprocess; + impl Plugin for GpuMeshPreprocessPlugin { fn build(&self, app: &mut App) { load_internal_asset!( @@ -136,10 +144,12 @@ impl Plugin for GpuMeshPreprocessPlugin { } // Stitch the node in. + let gpu_preprocess_node = GpuPreprocessNode::from_world(render_app.world_mut()); + let mut render_graph = render_app.world_mut().resource_mut::(); + render_graph.add_node(NodePbr::GpuPreprocess, gpu_preprocess_node); + render_graph.add_node_edge(NodePbr::GpuPreprocess, CameraDriverLabel); + render_app - .add_render_graph_node::(Core3d, NodePbr::GpuPreprocess) - .add_render_graph_edges(Core3d, (NodePbr::GpuPreprocess, Node3d::Prepass)) - .add_render_graph_edges(Core3d, (NodePbr::GpuPreprocess, NodePbr::ShadowPass)) .init_resource::() .init_resource::>() .add_systems( @@ -200,7 +210,7 @@ impl Node for GpuPreprocessNode { // Grab the index buffer for this view. let Some(index_buffer) = index_buffers.get(&view) else { warn!("The preprocessing index buffer wasn't present"); - return Ok(()); + continue; }; // Select the right pipeline, depending on whether GPU culling is in