Skip to content

Commit

Permalink
Implement bindless lightmaps. (#16653)
Browse files Browse the repository at this point in the history
This commit allows Bevy to bind 16 lightmaps at a time, if the current
platform supports bindless textures. Naturally, if bindless textures
aren't supported, Bevy falls back to binding only a single lightmap at a
time. As lightmaps are usually heavily atlased, I doubt many scenes will
use more than 16 lightmap textures.

This has little performance impact now, but it's desirable for us to
reap the benefits of multidraw and bindless textures on scenes that use
lightmaps. Otherwise, we might have to break batches in order to switch
those lightmaps.

Additionally, this PR slightly reduces the cost of binning because it
makes the lightmap index in `Opaque3dBinKey` 32 bits instead of an
`AssetId`.

## Migration Guide

* The `Opaque3dBinKey::lightmap_image` field is now
`Opaque3dBinKey::lightmap_slab`, which is a lightweight identifier for
an entire binding array of lightmaps.
  • Loading branch information
pcwalton authored Dec 16, 2024
1 parent 26bd160 commit 35826be
Show file tree
Hide file tree
Showing 16 changed files with 524 additions and 117 deletions.
2 changes: 1 addition & 1 deletion assets/shaders/bindless_material.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct Color {
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
#ifdef BINDLESS
let slot = mesh[in.instance_index].material_bind_group_slot;
let slot = mesh[in.instance_index].material_and_lightmap_bind_group_slot & 0xffffu;
let base_color = material_color[slot].base_color;
#else // BINDLESS
let base_color = material_color.base_color;
Expand Down
10 changes: 6 additions & 4 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ pub use main_opaque_pass_3d_node::*;
pub use main_transparent_pass_3d_node::*;

use bevy_app::{App, Plugin, PostUpdate};
use bevy_asset::{AssetId, UntypedAssetId};
use bevy_asset::UntypedAssetId;
use bevy_color::LinearRgba;
use bevy_ecs::{entity::EntityHashSet, prelude::*};
use bevy_image::{BevyDefault, Image};
use bevy_image::BevyDefault;
use bevy_math::FloatOrd;
use bevy_render::{
camera::{Camera, ExtractedCamera},
Expand All @@ -102,6 +102,7 @@ use bevy_render::{
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
use bevy_utils::{tracing::warn, HashMap};
use nonmax::NonMaxU32;

use crate::{
core_3d::main_transmissive_pass_3d_node::MainTransmissivePass3dNode,
Expand Down Expand Up @@ -258,8 +259,9 @@ pub struct Opaque3dBatchSetKey {
/// For non-mesh items, you can safely fill this with `None`.
pub index_slab: Option<SlabId>,

/// The lightmap, if present.
pub lightmap_image: Option<AssetId<Image>>,
/// Index of the slab that the lightmap resides in, if a lightmap is
/// present.
pub lightmap_slab: Option<NonMaxU32>,
}

/// Data that must be identical in order to *batch* phase items together.
Expand Down
18 changes: 17 additions & 1 deletion crates/bevy_pbr/src/lightmap/lightmap.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

#import bevy_pbr::mesh_bindings::mesh

#ifdef MULTIPLE_LIGHTMAPS_IN_ARRAY
@group(1) @binding(4) var lightmaps_textures: binding_array<texture_2d<f32>>;
@group(1) @binding(5) var lightmaps_samplers: binding_array<sampler>;
#else // MULTIPLE_LIGHTMAPS_IN_ARRAY
@group(1) @binding(4) var lightmaps_texture: texture_2d<f32>;
@group(1) @binding(5) var lightmaps_sampler: sampler;
#endif // MULTIPLE_LIGHTMAPS_IN_ARRAY

// Samples the lightmap, if any, and returns indirect illumination from it.
fn lightmap(uv: vec2<f32>, exposure: f32, instance_index: u32) -> vec3<f32> {
Expand All @@ -21,9 +26,20 @@ fn lightmap(uv: vec2<f32>, exposure: f32, instance_index: u32) -> vec3<f32> {
// control flow uniformity problems.
//
// TODO(pcwalton): Consider bicubic filtering.
#ifdef MULTIPLE_LIGHTMAPS_IN_ARRAY
let lightmap_slot = mesh[instance_index].material_and_lightmap_bind_group_slot >> 16u;
return textureSampleLevel(
lightmaps_textures[lightmap_slot],
lightmaps_samplers[lightmap_slot],
lightmap_uv,
0.0
).rgb * exposure;
#else // MULTIPLE_LIGHTMAPS_IN_ARRAY
return textureSampleLevel(
lightmaps_texture,
lightmaps_sampler,
lightmap_uv,
0.0).rgb * exposure;
0.0
).rgb * exposure;
#endif // MULTIPLE_LIGHTMAPS_IN_ARRAY
}
Loading

0 comments on commit 35826be

Please sign in to comment.