Use 4-byte LightmapSlabIndex for batching instead of 16-byte AssetId<Image> (#18326)

Less data accessed and compared gives better batching performance.

# Objective

- Use a smaller id to represent the lightmap in batch data to enable a
faster implementation of draw streams.
- Improve batching performance for 3D sorted render phases.

## Solution

- 3D batching can use `LightmapSlabIndex` (a `NonMaxU32` which is 4
bytes) instead of the lightmap `AssetId<Image>` (an enum where the
largest variant is a 16-byte UUID) to discern the ability to batch.

## Testing

Tested main (yellow) vs this PR (red) on an M4 Max using the
`many_cubes` example with `WGPU_SETTINGS_PRIO=webgl2` to avoid
GPU-preprocessing, and modifying the materials in `many_cubes` to have
`AlphaMode::Blend` so that they would rely on the less efficient sorted
render phase batching.
<img width="1500" alt="Screenshot_2025-03-15_at_12 17 21"
src="https://github.com/user-attachments/assets/14709bd3-6d06-40fb-aa51-e1d2d606ebe3"
/>
A 44.75us or 7.5% reduction in median execution time of the batch and
prepare sorted render phase system for the `Transparent3d` phase
(handling 160k cubes).

---

## Migration Guide

- Changed: `RenderLightmap::new()` no longer takes an `AssetId<Image>`
argument for the asset id of the lightmap image.
This commit is contained in:
Robert Swain 2025-03-15 15:16:32 +01:00 committed by GitHub
parent b462f47864
commit bc7416aa22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 3 additions and 9 deletions

View File

@ -116,9 +116,6 @@ pub struct Lightmap {
/// There is one of these per visible lightmapped mesh instance.
#[derive(Debug)]
pub(crate) struct RenderLightmap {
/// The ID of the lightmap texture.
pub(crate) image: AssetId<Image>,
/// The rectangle within the lightmap texture that the UVs are relative to.
///
/// The top left coordinate is the `min` part of the rect, and the bottom
@ -245,7 +242,6 @@ fn extract_lightmaps(
render_lightmaps.render_lightmaps.insert(
entity.into(),
RenderLightmap::new(
lightmap.image.id(),
lightmap.uv_rect,
slab_index,
slot_index,
@ -305,14 +301,12 @@ impl RenderLightmap {
/// Creates a new lightmap from a texture, a UV rect, and a slab and slot
/// index pair.
fn new(
image: AssetId<Image>,
uv_rect: Rect,
slab_index: LightmapSlabIndex,
slot_index: LightmapSlotIndex,
bicubic_sampling: bool,
) -> Self {
Self {
image,
uv_rect,
slab_index,
slot_index,

View File

@ -1905,7 +1905,7 @@ impl GetBatchData for MeshPipeline {
type CompareData = (
MaterialBindGroupIndex,
AssetId<Mesh>,
Option<AssetId<Image>>,
Option<LightmapSlabIndex>,
);
type BufferData = MeshUniform;
@ -1946,7 +1946,7 @@ impl GetBatchData for MeshPipeline {
mesh_instance.should_batch().then_some((
material_bind_group_index.group,
mesh_instance.mesh_asset_id,
maybe_lightmap.map(|lightmap| lightmap.image),
maybe_lightmap.map(|lightmap| lightmap.slab_index),
)),
))
}
@ -1976,7 +1976,7 @@ impl GetFullBatchData for MeshPipeline {
mesh_instance.should_batch().then_some((
mesh_instance.material_bindings_index.group,
mesh_instance.mesh_asset_id,
maybe_lightmap.map(|lightmap| lightmap.image),
maybe_lightmap.map(|lightmap| lightmap.slab_index),
)),
))
}