use super::*;
pub trait DrawablePrimitive<'r> {
fn draw(self, _: u32, _: &Geom, _: &mut Renderer);
}
macro_rules! DRAWABLE {
($t: ty, $draw_spec: ident) => {
impl<'r> DrawablePrimitive<'r> for $t {
fn draw(self, obj_n: u32, crop: &Geom, r: &mut Renderer) {
use PrimImpl::$draw_spec as object;
if obj_n < u32(r.cache.objs.len()) {
let PrimCache { state, o } = r.cache.objs.at_mut(obj_n);
if let object(l) = o {
*state = self.compare(crop, l) | r.status;
if !state.contains(State::MISMATCH) {
if !state.is_empty() {
r.flush |= *state;
*o = self.obj(*crop).pipe(object);
}
return;
}
}
r.cache.shrink(obj_n);
}
r.flush |= State::FULL;
let p = PrimCache { state: State::MISMATCH, o: object(self.obj(*crop)) };
r.cache.objs.push(p);
}
}
};
}
#[derive(Default)]
pub struct RenderCache {
pub batches: Vec<Batch>,
pub objs: Vec<PrimCache>,
pub first_transparent: usize,
}
impl RenderCache {
pub fn b_box(&self, at: u32) -> Geom {
self.objs.at(at).o.obj().base().bound_box()
}
pub fn shrink(&mut self, to: u32) {
let Self { batches, objs, .. } = self;
batches.retain_mut(|b| !b.shrink_and_empty(objs, to));
objs.truncate(usize(to));
}
fn batch(&mut self) {
let Self { batches, objs, first_transparent } = self;
let overlaps = |o, z| {
batches.iter().position(|b| b.contains(objs, (o, z))).is_some_and(|b| {
let covered = || batches[..b].iter().any(|b| b.covered(objs, (o, z)));
let covers = || batches[b + 1..].iter().any(|b| b.covers(objs, (o, z)));
covered() || covers()
})
};
let Some(first_invalid) = objs
.iter()
.enumerate()
.find(|(n, PrimCache { state, o })| {
let overlapping = || state.contains(State::XYZW) && o.obj().ordered() && overlaps(o, u32(n));
state.contains(State::MISMATCH) || overlapping()
})
.map(|(n, _)| n)
else {
return;
};
batches.retain_mut(|b| !b.shrink_and_empty(objs, u32(first_invalid)));
objs.iter().enumerate().skip(first_invalid).for_each(|(z, o)| {
let (z, o) = (u32(z), &o.o);
for b in batches.iter_mut().rev() {
if b.try_add(objs, (o, z)) {
return;
}
if b.interferes(objs, o) {
break;
}
}
if o.obj().ordered() {
batches.push(Batch::new(z));
} else {
batches.insert(0, Batch::new(z));
}
});
*first_transparent = batches.iter().position(|b| b.typ(objs).obj().ordered()).unwrap_or(batches.len());
}
pub fn flush(
&mut self, aspect: Vec2, idxs: &mut Vec<u16>, xyzw: &mut Vec<f16>, rgba: &mut Vec<u8>, uv: &mut Vec<f16>,
) -> (Option<usize>, Option<usize>, Option<usize>, Option<usize>) {
self.batch();
let Self { batches, objs, .. } = self;
let MAXIDX = const { u16::MAX as usize };
batches
.iter_mut()
.fold(((None, None, None, None), State::empty(), 0, 0), |(mut flush, mut state, idx_start, batch_start), b| {
let (batch_size, s) = b.redraw(aspect, objs);
state |= s;
DEBUG!("Flushing state {state:?}");
if state.contains(State::BATCH_RESIZED) {
flush.0.get_or_insert(idx_start);
let mut indices = b.typ(objs).obj().gen_idxs(vec2((batch_start, batch_size)));
if idx_start + indices.len() > MAXIDX {
WARN!("GUI batch too saturated with polygons, dropping some");
let mut i = indices.into_vec();
i.truncate(MAXIDX - idx_start);
indices = i.into();
}
b.idx_range = vec2((idx_start, indices.len()));
idxs.truncate(usize(idx_start));
idxs.extend_from_slice(&indices);
}
fn update<T: Copy>(changed: bool, ordered: bool, dim: usize, v: &mut Vec<T>, at: usize, upd: &[T], flush: &mut Option<usize>) {
if changed {
flush.get_or_insert(at * dim);
v.truncate(at * dim);
if ordered {
v.extend_from_slice(upd);
} else {
v.extend(upd.chunks(dim).rev().flatten());
}
}
}
let ordered = b.typ(objs).obj().ordered();
update(state.contains(State::XYZW), ordered, 4, xyzw, batch_start, &b.xyzw, &mut flush.1);
update(state.contains(State::RGBA), ordered, 4, rgba, batch_start, &b.rgba, &mut flush.2);
update(state.contains(State::UV), ordered, 2, uv, batch_start, &b.uv, &mut flush.3);
(flush, state, ulVec2(b.idx_range).fold(|l, r| l + r).min(MAXIDX), batch_start + usize(batch_size))
})
.pipe(|(flush, ..)| flush)
}
pub fn draw_opaque_batches(&self, v: &VaoBind<u16>) {
let Self { batches, objs, first_transparent } = self;
batches.iter().take(*first_transparent).for_each(|b| b.typ(objs).obj().batch_draw(v, b.idx_range));
}
pub fn draw_translucent_batches(&self, v: &VaoBind<u16>) {
let Self { batches, objs, first_transparent } = self;
batches.iter().skip(*first_transparent).for_each(|b| b.typ(objs).obj().batch_draw(v, b.idx_range));
}
}
pub struct PrimCache {
pub state: State,
pub o: PrimImpl,
}
pub enum PrimImpl {
Rect(RectImpl),
ImgRGB(SpriteImpl<RGB>),
ImgRGBA(SpriteImpl<RGBA>),
Img9RGB(Sprite9Impl<RGB>),
Img9RGBA(Sprite9Impl<RGBA>),
Frame9(Frame9Impl),
Text(TextImpl),
}
impl PrimImpl {
pub fn obj(&self) -> &dyn Primitive {
match self {
Rect(r) => r,
ImgRGB(r) => r,
ImgRGBA(r) => r,
Img9RGB(r) => r,
Img9RGBA(r) => r,
Frame9(r) => r,
Text(r) => r,
}
}
pub fn batchable(&self, r: &Self) -> bool {
match (self, r) {
(Rect(l), Rect(r)) => l.batchable(r),
(ImgRGB(l), ImgRGB(r)) => l.batchable(r),
(Img9RGB(l), Img9RGB(r)) => l.batchable(r),
(ImgRGBA(l), ImgRGBA(r)) => l.batchable(r),
(Img9RGBA(l), Img9RGBA(r)) => l.batchable(r),
(Frame9(l), Frame9(r)) => l.batchable(r),
(Text(l), Text(r)) => l.batchable(r),
_ => false,
}
}
}
use PrimImpl::*;