Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Use storage buffers for clustered forward point lights #3989

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,10 @@ min_sdk_version = 16
target_sdk_version = 29

# Stress Tests
[[example]]
name = "many_lights"
path = "examples/stress_tests/many_lights.rs"

[[example]]
name = "transform_hierarchy"
path = "examples/stress_tests/transform_hierarchy.rs"

10 changes: 4 additions & 6 deletions crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,10 @@ impl Plugin for PbrPlugin {
)
.add_system_to_stage(
RenderStage::Prepare,
// this is added as an exclusive system because it contributes new views. it must run (and have Commands applied)
// _before_ the `prepare_views()` system is run. ideally this becomes a normal system when "stageless" features come out
render::prepare_clusters
.exclusive_system()
.label(RenderLightSystems::PrepareClusters)
.after(RenderLightSystems::PrepareLights),
// NOTE: This needs to run after prepare_lights. As prepare_lights is an exclusive system,
// just adding it to the non-exclusive systems in the Prepare stage means it runs after
// prepare_lights.
render::prepare_clusters.label(RenderLightSystems::PrepareClusters),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this label shouldn't be needed since #4224

)
.add_system_to_stage(
RenderStage::Queue,
Expand Down
25 changes: 19 additions & 6 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use bevy_render::{
color::Color,
prelude::Image,
primitives::{Aabb, CubemapFrusta, Frustum, Sphere},
render_resource::BufferBindingType,
renderer::RenderDevice,
view::{ComputedVisibility, RenderLayers, Visibility, VisibleEntities},
};
use bevy_transform::components::GlobalTransform;
Expand All @@ -17,7 +19,8 @@ use bevy_window::Windows;

use crate::{
calculate_cluster_factors, CubeMapFace, CubemapVisibleEntities, ViewClusterBindings,
CUBE_MAP_FACES, MAX_POINT_LIGHTS, POINT_LIGHT_NEAR_Z,
CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, CUBE_MAP_FACES, MAX_UNIFORM_BUFFER_POINT_LIGHTS,
POINT_LIGHT_NEAR_Z,
};

/// A light that emits light in all directions from a central point.
Expand Down Expand Up @@ -709,6 +712,7 @@ pub(crate) fn assign_lights_to_clusters(
lights_query: Query<(Entity, &GlobalTransform, &PointLight, &Visibility)>,
mut lights: Local<Vec<PointLightAssignmentData>>,
mut max_point_lights_warning_emitted: Local<bool>,
render_device: Res<RenderDevice>,
) {
global_lights.entities.clear();
lights.clear();
Expand All @@ -727,7 +731,13 @@ pub(crate) fn assign_lights_to_clusters(
),
);

if lights.len() > MAX_POINT_LIGHTS {
let clustered_forward_buffer_binding_type =
render_device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
let supports_storage_buffers = matches!(
clustered_forward_buffer_binding_type,
BufferBindingType::Storage { .. }
);
if lights.len() > MAX_UNIFORM_BUFFER_POINT_LIGHTS && !supports_storage_buffers {
lights.sort_by(|light_1, light_2| {
point_light_order(
(&light_1.entity, &light_1.shadows_enabled),
Expand All @@ -743,7 +753,7 @@ pub(crate) fn assign_lights_to_clusters(
let mut lights_in_view_count = 0;
lights.retain(|light| {
// take one extra light to check if we should emit the warning
if lights_in_view_count == MAX_POINT_LIGHTS + 1 {
if lights_in_view_count == MAX_UNIFORM_BUFFER_POINT_LIGHTS + 1 {
false
} else {
let light_sphere = Sphere {
Expand All @@ -763,12 +773,15 @@ pub(crate) fn assign_lights_to_clusters(
}
});

if lights.len() > MAX_POINT_LIGHTS && !*max_point_lights_warning_emitted {
warn!("MAX_POINT_LIGHTS ({}) exceeded", MAX_POINT_LIGHTS);
if lights.len() > MAX_UNIFORM_BUFFER_POINT_LIGHTS && !*max_point_lights_warning_emitted {
warn!(
"MAX_UNIFORM_BUFFER_POINT_LIGHTS ({}) exceeded",
MAX_UNIFORM_BUFFER_POINT_LIGHTS
);
*max_point_lights_warning_emitted = true;
}

lights.truncate(MAX_POINT_LIGHTS);
lights.truncate(MAX_UNIFORM_BUFFER_POINT_LIGHTS);
}

for (view_entity, camera_transform, camera, frustum, config, clusters, mut visible_lights) in
Expand Down
11 changes: 7 additions & 4 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use std::marker::PhantomData;
/// way to render [`Mesh`] entities with custom shader logic. For materials that can specialize their [`RenderPipelineDescriptor`]
/// based on specific material values, see [`SpecializedMaterial`]. [`Material`] automatically implements [`SpecializedMaterial`]
/// and can be used anywhere that type is used (such as [`MaterialPlugin`]).
pub trait Material: Asset + RenderAsset {
pub trait Material: Asset + RenderAsset + Sized {
/// Returns this material's [`BindGroup`]. This should match the layout returned by [`Material::bind_group_layout`].
fn bind_group(material: &<Self as RenderAsset>::PreparedAsset) -> &BindGroup;

Expand Down Expand Up @@ -78,6 +78,7 @@ pub trait Material: Asset + RenderAsset {
#[allow(unused_variables)]
#[inline]
fn specialize(
pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout,
) -> Result<(), SpecializedMeshPipelineError> {
Expand All @@ -93,11 +94,12 @@ impl<M: Material> SpecializedMaterial for M {

#[inline]
fn specialize(
pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
_key: Self::Key,
layout: &MeshVertexBufferLayout,
) -> Result<(), SpecializedMeshPipelineError> {
<M as Material>::specialize(descriptor, layout)
<M as Material>::specialize(pipeline, descriptor, layout)
}

#[inline]
Expand Down Expand Up @@ -137,7 +139,7 @@ impl<M: Material> SpecializedMaterial for M {
/// way to render [`Mesh`] entities with custom shader logic. [`SpecializedMaterials`](SpecializedMaterial) use their [`SpecializedMaterial::Key`]
/// to customize their [`RenderPipelineDescriptor`] based on specific material values. The slightly simpler [`Material`] trait
/// should be used for materials that do not need specialization. [`Material`] types automatically implement [`SpecializedMaterial`].
pub trait SpecializedMaterial: Asset + RenderAsset {
pub trait SpecializedMaterial: Asset + RenderAsset + Sized {
/// The key used to specialize this material's [`RenderPipelineDescriptor`].
type Key: PartialEq + Eq + Hash + Clone + Send + Sync;

Expand All @@ -148,6 +150,7 @@ pub trait SpecializedMaterial: Asset + RenderAsset {

/// Specializes the given `descriptor` according to the given `key`.
fn specialize(
pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
key: Self::Key,
layout: &MeshVertexBufferLayout,
Expand Down Expand Up @@ -251,7 +254,7 @@ impl<M: SpecializedMaterial> SpecializedMeshPipeline for MaterialPipeline<M> {
let descriptor_layout = descriptor.layout.as_mut().unwrap();
descriptor_layout.insert(1, self.material_layout.clone());

M::specialize(&mut descriptor, key.material_key, layout)?;
M::specialize(self, &mut descriptor, key.material_key, layout)?;
Ok(descriptor)
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/pbr_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ impl SpecializedMaterial for StandardMaterial {
}

fn specialize(
_pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
key: Self::Key,
_layout: &MeshVertexBufferLayout,
Expand Down
Loading