Skip to content

Commit 4f5931f

Browse files
committed
bevy_pbr: Use storage buffers for clustered forward rendering bindings
This enables using many more than 256 point lights in a scene. On an M1 Max MacBook Pro, it can maintain 60fps with about 2150 point lights. With non-pipelined rendering, the bottleneck seems to be assign_lights_to_clusters, which was expected. There are possibly optimisations that can be done to reduce the bottleneck CPU-side, but I have seen papers and presentations supporting millions of lights when leveraging compute shaders for the light assignment to clusters.
1 parent 98938a8 commit 4f5931f

File tree

9 files changed

+468
-90
lines changed

9 files changed

+468
-90
lines changed

crates/bevy_pbr/src/lib.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
3737
use bevy_ecs::prelude::*;
3838
use bevy_reflect::TypeUuid;
3939
use bevy_render::{
40+
options::WgpuOptions,
4041
prelude::Color,
4142
render_graph::RenderGraph,
4243
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions},
@@ -135,6 +136,16 @@ impl Plugin for PbrPlugin {
135136
},
136137
);
137138

139+
// FIXME: What would be a good way of managing storage buffer limitations per
140+
// stage?
141+
let use_storage_buffers = app
142+
.world
143+
.get_resource::<WgpuOptions>()
144+
.unwrap()
145+
.limits
146+
.max_storage_buffers_per_shader_stage
147+
>= 3;
148+
138149
let render_app = match app.get_sub_app_mut(RenderApp) {
139150
Ok(render_app) => render_app,
140151
Err(_) => return,
@@ -175,7 +186,7 @@ impl Plugin for PbrPlugin {
175186
.init_resource::<ShadowPipeline>()
176187
.init_resource::<DrawFunctions<Shadow>>()
177188
.init_resource::<LightMeta>()
178-
.init_resource::<GlobalLightMeta>()
189+
.insert_resource(GlobalLightMeta::new(use_storage_buffers))
179190
.init_resource::<SpecializedPipelines<ShadowPipeline>>();
180191

181192
let shadow_pass_node = ShadowPassNode::new(&mut render_app.world);

crates/bevy_pbr/src/material.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use bevy_ecs::{
1616
};
1717
use bevy_render::{
1818
mesh::Mesh,
19+
options::WgpuOptions,
1920
render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets},
2021
render_component::ExtractComponentPlugin,
2122
render_phase::{
@@ -78,7 +79,11 @@ impl<M: Material> SpecializedMaterial for M {
7879
type Key = ();
7980

8081
#[inline]
81-
fn key(_material: &<Self as RenderAsset>::PreparedAsset) -> Self::Key {}
82+
fn key(
83+
_wgpu_options: &WgpuOptions,
84+
_material: &<Self as RenderAsset>::PreparedAsset,
85+
) -> Self::Key {
86+
}
8287

8388
#[inline]
8489
fn specialize(_key: Self::Key, _descriptor: &mut RenderPipelineDescriptor) {}
@@ -127,7 +132,10 @@ pub trait SpecializedMaterial: Asset + RenderAsset {
127132
/// Extract the [`SpecializedMaterial::Key`] for the "prepared" version of this material. This key will be
128133
/// passed in to the [`SpecializedMaterial::specialize`] function when compiling the [`RenderPipeline`](bevy_render::render_resource::RenderPipeline)
129134
/// for a given entity's material.
130-
fn key(material: &<Self as RenderAsset>::PreparedAsset) -> Self::Key;
135+
fn key(
136+
wgpu_options: &WgpuOptions,
137+
material: &<Self as RenderAsset>::PreparedAsset,
138+
) -> Self::Key;
131139

132140
/// Specializes the given `descriptor` according to the given `key`.
133141
fn specialize(key: Self::Key, descriptor: &mut RenderPipelineDescriptor);
@@ -271,6 +279,7 @@ impl<M: SpecializedMaterial, const I: usize> EntityRenderCommand for SetMaterial
271279

272280
#[allow(clippy::too_many_arguments)]
273281
pub fn queue_material_meshes<M: SpecializedMaterial>(
282+
wgpu_options: Res<WgpuOptions>,
274283
opaque_draw_functions: Res<DrawFunctions<Opaque3d>>,
275284
alpha_mask_draw_functions: Res<DrawFunctions<AlphaMask3d>>,
276285
transparent_draw_functions: Res<DrawFunctions<Transparent3d>>,
@@ -289,6 +298,7 @@ pub fn queue_material_meshes<M: SpecializedMaterial>(
289298
&mut RenderPhase<Transparent3d>,
290299
)>,
291300
) {
301+
let wgpu_options = wgpu_options.into_inner();
292302
for (view, visible_entities, mut opaque_phase, mut alpha_mask_phase, mut transparent_phase) in
293303
views.iter_mut()
294304
{
@@ -327,7 +337,7 @@ pub fn queue_material_meshes<M: SpecializedMaterial>(
327337
mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS;
328338
}
329339

330-
let specialized_key = M::key(material);
340+
let specialized_key = M::key(wgpu_options, material);
331341
let pipeline_id = pipelines.specialize(
332342
&mut pipeline_cache,
333343
&material_pipeline,

crates/bevy_pbr/src/pbr_material.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use bevy_math::Vec4;
55
use bevy_reflect::TypeUuid;
66
use bevy_render::{
77
color::Color,
8+
options::WgpuOptions,
89
prelude::Shader,
910
render_asset::{PrepareAssetError, RenderAsset, RenderAssets},
1011
render_resource::{
@@ -327,14 +328,19 @@ impl RenderAsset for StandardMaterial {
327328
#[derive(Clone, PartialEq, Eq, Hash)]
328329
pub struct StandardMaterialKey {
329330
normal_map: bool,
331+
storage_buffers: bool,
330332
}
331333

332334
impl SpecializedMaterial for StandardMaterial {
333335
type Key = StandardMaterialKey;
334336

335-
fn key(render_asset: &<Self as RenderAsset>::PreparedAsset) -> Self::Key {
337+
fn key(
338+
wgpu_options: &WgpuOptions,
339+
render_asset: &<Self as RenderAsset>::PreparedAsset,
340+
) -> Self::Key {
336341
StandardMaterialKey {
337342
normal_map: render_asset.has_normal_map,
343+
storage_buffers: wgpu_options.limits.max_storage_buffers_per_shader_stage >= 3,
338344
}
339345
}
340346

@@ -347,6 +353,14 @@ impl SpecializedMaterial for StandardMaterial {
347353
.shader_defs
348354
.push(String::from("STANDARDMATERIAL_NORMAL_MAP"));
349355
}
356+
if !key.storage_buffers {
357+
descriptor
358+
.fragment
359+
.as_mut()
360+
.unwrap()
361+
.shader_defs
362+
.push(String::from("NO_STORAGE_BUFFERS_SUPPORT"));
363+
}
350364
if let Some(label) = &mut descriptor.label {
351365
*label = format!("pbr_{}", *label).into();
352366
}

0 commit comments

Comments
 (0)