use enumset::__internal::EnumSetTypePrivate as _;
use enumset::EnumSet;
use re_log::{debug_assert, debug_panic};
use super::DrawPhase;
use crate::context::Renderers;
use crate::renderer::{
DrawDataDrawable, DrawDataDrawablePayload, DrawInstruction, DrawableCollectionViewInfo,
};
use crate::{GpuRenderPipelinePoolAccessor, QueueableDrawData, RenderContext, RendererTypeId};
type DrawDataIndex = u32;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct PackedRenderingKeyAndDrawDataIndex(u32);
impl PackedRenderingKeyAndDrawDataIndex {
#[inline]
const fn new(renderer_key: RendererTypeId, draw_data_index: DrawDataIndex) -> Self {
#![expect(clippy::disallowed_macros)] std::debug_assert!(draw_data_index < 0xFFFFFF);
Self(((renderer_key.bits() as u32) << 24) | draw_data_index)
}
#[inline]
const fn draw_data_index(&self) -> DrawDataIndex {
self.0 & 0x00FFFFFF
}
#[inline]
const fn renderer_key(&self) -> RendererTypeId {
RendererTypeId::from_bits(((self.0 & 0xFF000000) >> 24) as u8)
}
}
impl std::fmt::Debug for PackedRenderingKeyAndDrawDataIndex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PackedRenderingKeyAndDrawDataIndex")
.field("draw_data_index", &self.draw_data_index())
.field("renderer_key", &self.renderer_key())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Drawable {
pub distance_sort_key: f32,
pub secondary_sort_key: f32,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex,
pub draw_data_payload: DrawDataDrawablePayload,
}
impl Drawable {
#[inline]
fn draw_data_index(&self) -> DrawDataIndex {
self.draw_data_plus_rendering_key.draw_data_index()
}
#[inline]
fn renderer_key(&self) -> RendererTypeId {
self.draw_data_plus_rendering_key.renderer_key()
}
#[inline]
fn renderer_sort_key(&self, name_sort_remap: &[u8; 256]) -> u8 {
name_sort_remap[self.renderer_key().bits() as usize]
}
#[inline]
fn sort_for_opaque_phase(drawables: &mut [Self], name_sort_remap: &[u8; 256]) {
drawables.sort_by(|a, b| {
a.renderer_sort_key(name_sort_remap)
.cmp(&b.renderer_sort_key(name_sort_remap))
.then_with(|| a.draw_data_index().cmp(&b.draw_data_index()))
.then_with(|| a.distance_sort_key.total_cmp(&b.distance_sort_key))
.then_with(|| a.secondary_sort_key.total_cmp(&b.secondary_sort_key))
});
}
#[inline]
fn sort_for_transparent_phase(drawables: &mut [Self]) {
drawables.sort_by(|a, b| {
b.distance_sort_key
.total_cmp(&a.distance_sort_key)
.then_with(|| a.secondary_sort_key.total_cmp(&b.secondary_sort_key))
});
}
}
pub struct DrawPhaseManager {
active_phases: EnumSet<DrawPhase>,
drawables: [Vec<Drawable>; DrawPhase::VARIANT_COUNT as usize],
draw_data: Vec<QueueableDrawData>,
}
impl DrawPhaseManager {
pub fn new(active_phases: EnumSet<DrawPhase>) -> Self {
Self {
active_phases,
drawables: [const { Vec::new() }; DrawPhase::VARIANT_COUNT as usize],
draw_data: Vec::new(),
}
}
pub fn add_draw_data(
&mut self,
ctx: &RenderContext,
draw_data: QueueableDrawData,
view_info: &DrawableCollectionViewInfo,
) {
re_tracing::profile_function!();
let draw_data_index = self.draw_data.len() as _;
let renderer_key = draw_data.renderer_key(ctx);
{
let mut collector = DrawableCollector::new(self, draw_data_index, renderer_key);
re_tracing::profile_scope!("collect_drawables");
draw_data.collect_drawables(view_info, &mut collector);
}
self.draw_data.push(draw_data);
}
pub fn sort_drawables(&mut self, renderers: &Renderers) {
re_tracing::profile_function!();
let name_sort_remap = renderers.name_sort_remap();
for phase in self.active_phases {
if phase == DrawPhase::Transparent || phase == DrawPhase::OutlineMaskNoDepth {
Drawable::sort_for_transparent_phase(&mut self.drawables[phase as usize]);
} else {
Drawable::sort_for_opaque_phase(
&mut self.drawables[phase as usize],
&name_sort_remap,
);
}
}
}
pub fn draw(
&self,
renderers: &Renderers,
gpu_resources: &GpuRenderPipelinePoolAccessor<'_>,
phase: DrawPhase,
pass: &mut wgpu::RenderPass<'_>,
) {
re_tracing::profile_function!(format!("draw({phase:?})"));
debug_assert!(
self.active_phases.contains(phase),
"Phase {phase:?} not active",
);
let renderer_chunked_drawables =
self.drawables[phase as usize].chunk_by(|a, b| a.renderer_key() == b.renderer_key());
let mut draw_instructions = Vec::with_capacity(64.min(self.draw_data.len()));
for drawable_run_with_same_renderer in renderer_chunked_drawables {
let first = &drawable_run_with_same_renderer[0]; let renderer_key = first.renderer_key();
draw_instructions.clear();
draw_instructions.extend(
drawable_run_with_same_renderer
.chunk_by(|a, b| a.draw_data_index() == b.draw_data_index())
.map(|drawables| DrawInstruction {
draw_data: &self.draw_data[drawables[0].draw_data_index() as usize],
drawables,
}),
);
let Some(renderer) = renderers.get_by_key(renderer_key) else {
debug_panic!(
"Previously acquired renderer not found by key. Since renderers are never deleted this should be impossible."
);
continue;
};
let draw_result =
renderer.run_draw_instructions(gpu_resources, phase, pass, &draw_instructions);
if let Err(err) = draw_result {
re_log::error!("Error drawing with {}: {err}", renderer.name());
}
}
}
#[cfg(test)]
pub fn drawables_for_phase(&self, phase: DrawPhase) -> &[Drawable] {
&self.drawables[phase as usize]
}
}
pub struct DrawableCollector<'a> {
per_phase_drawables: &'a mut DrawPhaseManager,
draw_data_index: DrawDataIndex,
renderer_key: RendererTypeId,
}
impl<'a> DrawableCollector<'a> {
fn new(
per_phase_drawables: &'a mut DrawPhaseManager,
draw_data_index: DrawDataIndex,
renderer_key: RendererTypeId,
) -> Self {
Self {
per_phase_drawables,
draw_data_index,
renderer_key,
}
}
#[inline]
fn make_drawable(
info: DrawDataDrawable,
draw_data_index: DrawDataIndex,
renderer_key: RendererTypeId,
) -> Drawable {
Drawable {
distance_sort_key: info.distance_sort_key,
secondary_sort_key: info.secondary_sort_key,
draw_data_payload: info.draw_data_payload,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
renderer_key,
draw_data_index,
),
}
}
#[inline]
pub fn add_drawables(
&mut self,
phases: impl Into<EnumSet<DrawPhase>>,
drawables: &[DrawDataDrawable],
) {
let Self {
per_phase_drawables,
draw_data_index,
renderer_key,
} = self;
let phases = per_phase_drawables
.active_phases
.intersection(phases.into());
for phase in phases {
per_phase_drawables.drawables[phase.enum_into_u32() as usize].extend(
drawables
.iter()
.map(|info| Self::make_drawable(*info, *draw_data_index, *renderer_key)),
);
}
}
#[inline]
pub fn add_drawable(
&mut self,
phases: impl Into<EnumSet<DrawPhase>>,
drawable: DrawDataDrawable,
) {
self.add_drawables(phases, &[drawable]);
}
#[inline]
pub fn add_drawable_for_phase(&mut self, phase: DrawPhase, drawable: DrawDataDrawable) {
let Self {
per_phase_drawables,
draw_data_index,
renderer_key,
} = self;
if per_phase_drawables.active_phases.contains(phase) {
per_phase_drawables.drawables[phase.enum_into_u32() as usize].push(
Self::make_drawable(drawable, *draw_data_index, *renderer_key),
);
}
}
#[inline]
pub fn active_phases(&self) -> EnumSet<DrawPhase> {
self.per_phase_drawables.active_phases
}
}
#[cfg(test)]
mod tests {
use core::f32;
use super::*;
const RENDERER_0: RendererTypeId = RendererTypeId::from_bits(0);
const RENDERER_2: RendererTypeId = RendererTypeId::from_bits(2);
const IDENTITY_REMAP: [u8; 256] = {
let mut remap = [0u8; 256];
let mut i = 0;
while i < 256 {
remap[i] = i as u8;
i += 1;
}
remap
};
const TEST_DRAWABLES: [Drawable; 7] = [
Drawable {
distance_sort_key: 0.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 1.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 1),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 1),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::MAX,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::INFINITY,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_2, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_2, 0),
draw_data_payload: 1, },
];
#[test]
fn test_sort_for_opaque_phase() {
let expected = vec![
Drawable {
distance_sort_key: 0.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::MAX,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::INFINITY,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 1.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_2, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_2, 0,
),
draw_data_payload: 1, },
];
{
let mut drawables = TEST_DRAWABLES.to_vec();
Drawable::sort_for_opaque_phase(&mut drawables, &IDENTITY_REMAP);
assert_eq!(drawables, expected);
}
{
let mut drawables = TEST_DRAWABLES.to_vec();
drawables.reverse();
drawables.swap(0, 1);
Drawable::sort_for_opaque_phase(&mut drawables, &IDENTITY_REMAP);
assert_eq!(drawables, expected);
}
}
#[test]
fn test_sort_for_transparent_phase() {
let expected = vec![
Drawable {
distance_sort_key: f32::INFINITY,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::MAX,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_2, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_2, 0,
),
draw_data_payload: 1, },
Drawable {
distance_sort_key: 2.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 1.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 0.0,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
];
{
let mut drawables = TEST_DRAWABLES.to_vec();
Drawable::sort_for_transparent_phase(&mut drawables);
assert_eq!(drawables, expected);
}
{
let mut drawables = TEST_DRAWABLES.to_vec();
drawables.reverse();
drawables.swap(0, 1);
Drawable::sort_for_transparent_phase(&mut drawables);
assert_eq!(drawables, expected);
}
}
#[test]
fn test_sort_for_transparent_phase_uses_secondary_sort_key_for_equal_distances() {
let mut drawables = vec![
Drawable {
distance_sort_key: 4.0,
secondary_sort_key: 20.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 1,
},
Drawable {
distance_sort_key: 4.0,
secondary_sort_key: 10.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
];
Drawable::sort_for_transparent_phase(&mut drawables);
assert_eq!(drawables[0].draw_data_payload, 0);
assert_eq!(drawables[1].draw_data_payload, 1);
}
#[test]
fn test_sort_for_transparent_phase_keeps_distance_priority_before_secondary_sort_key() {
let mut drawables = vec![
Drawable {
distance_sort_key: 25.0,
secondary_sort_key: 20.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 1,
},
Drawable {
distance_sort_key: 4.0,
secondary_sort_key: 10.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
];
Drawable::sort_for_transparent_phase(&mut drawables);
assert_eq!(drawables[0].draw_data_payload, 1);
assert_eq!(drawables[1].draw_data_payload, 0);
}
#[test]
fn test_sort_for_transparent_phase_keeps_distance_priority_over_secondary_sort_key() {
let mut drawables = vec![
Drawable {
distance_sort_key: 4.1,
secondary_sort_key: 10.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 4.0,
secondary_sort_key: 20.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 1,
},
];
Drawable::sort_for_transparent_phase(&mut drawables);
assert_eq!(drawables[0].draw_data_payload, 0);
assert_eq!(drawables[1].draw_data_payload, 1);
}
#[test]
fn test_sort_for_opaque_phase_uses_secondary_sort_key_as_tie_breaker() {
let mut drawables = vec![
Drawable {
distance_sort_key: 4.0,
secondary_sort_key: 20.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 1,
},
Drawable {
distance_sort_key: 4.0,
secondary_sort_key: 10.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
];
Drawable::sort_for_opaque_phase(&mut drawables, &IDENTITY_REMAP);
assert_eq!(drawables[0].draw_data_payload, 0);
assert_eq!(drawables[1].draw_data_payload, 1);
}
#[test]
fn test_opaque_sort_is_invariant_under_remap_inversion() {
let key_a = RendererTypeId::from_bits(0);
let key_b = RendererTypeId::from_bits(1);
let make_drawable = |key: RendererTypeId, payload: DrawDataDrawablePayload| Drawable {
distance_sort_key: f32::MAX,
secondary_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(key, 0),
draw_data_payload: payload,
};
let mut swapped_remap = IDENTITY_REMAP;
swapped_remap[0] = 1;
swapped_remap[1] = 0;
let base = vec![
make_drawable(key_a, 0), make_drawable(key_b, 1), ];
{
let mut d = base.clone();
Drawable::sort_for_opaque_phase(&mut d, &IDENTITY_REMAP);
assert_eq!(d[0].draw_data_payload, 0);
assert_eq!(d[1].draw_data_payload, 1);
}
{
let mut d = base.clone();
Drawable::sort_for_opaque_phase(&mut d, &swapped_remap);
assert_eq!(d[0].draw_data_payload, 1);
assert_eq!(d[1].draw_data_payload, 0);
}
{
let mut d = vec![make_drawable(key_b, 1), make_drawable(key_a, 0)];
Drawable::sort_for_opaque_phase(&mut d, &swapped_remap);
assert_eq!(d[0].draw_data_payload, 1);
assert_eq!(d[1].draw_data_payload, 0);
}
}
}