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,
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 sort_for_opaque_phase(drawables: &mut [Self]) {
drawables.sort_by_key(|drawable| {
((drawable.draw_data_plus_rendering_key.0 as u64) << 32)
| (drawable.distance_sort_key.to_bits() as u64)
});
}
#[inline]
fn sort_for_transparent_phase(drawables: &mut [Self]) {
drawables.sort_by_key(|drawable| !drawable.distance_sort_key.to_bits());
}
}
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) {
re_tracing::profile_function!();
for phase in self.active_phases {
if phase == DrawPhase::Transparent {
Drawable::sort_for_transparent_phase(&mut self.drawables[phase as usize]);
} else {
Drawable::sort_for_opaque_phase(&mut self.drawables[phase as usize]);
}
}
}
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,
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 TEST_DRAWABLES: [Drawable; 7] = [
Drawable {
distance_sort_key: 0.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 1.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 1),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 1),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::MAX,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::INFINITY,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_0, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(RENDERER_2, 0),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
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,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::MAX,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::INFINITY,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 1.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_2, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
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);
assert_eq!(drawables, expected);
}
{
let mut drawables = TEST_DRAWABLES.to_vec();
drawables.reverse();
drawables.swap(0, 1);
Drawable::sort_for_opaque_phase(&mut drawables);
assert_eq!(drawables, expected);
}
}
#[test]
fn test_sort_for_transparent_phase() {
let expected = vec![
Drawable {
distance_sort_key: f32::INFINITY,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: f32::MAX,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_2, 0,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 2.0001,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_2, 0,
),
draw_data_payload: 1, },
Drawable {
distance_sort_key: 2.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_sort_key: 1.0,
draw_data_plus_rendering_key: PackedRenderingKeyAndDrawDataIndex::new(
RENDERER_0, 1,
),
draw_data_payload: 0,
},
Drawable {
distance_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);
}
}
}