use crate::{ Axis, Len, MoveOffset, PaintId, RegionAlign, RenderedText, Size, StrongWidget, TextHandle, TextResources, TextureHandle, UiData, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, WidgetId, render::{ Drawn, GlyphPrimitive, IMAGE_BINDING, Mask, MaskIdx, MoveIdx, NOT_DRAWN, Primitive, PrimitiveHandle, PrimitiveInst, RectPrimitive, }, ui::render_state::Retained, util::Vec2, }; use std::{cell::RefCell, rc::Rc}; pub struct Painter<'a> { pub(super) render_state: &'a mut UiRenderState, pub(super) rsc: &'a mut dyn UiRsc, pub(super) region: UiRegion, pub(super) mask: MaskIdx, pub(super) move_slot: MoveIdx, pub(super) child_move_slot: Option, pub(super) own_mask: MaskIdx, pub(super) textures: Vec, pub(super) paints: Vec, pub(super) primitives: Vec, pub(super) recycle: std::iter::Peekable>, pub(super) children: Vec, pub(super) size_dependencies: Vec, pub(super) size: Option, /// Whether a retained child's length on each axis is still valid. A /// child's length may change when the parent's orthogonal extent changes /// (most importantly, wrapped text gets taller when it gets narrower), /// but not merely because a content-sized parent grew along that same /// axis around one of its siblings. pub(super) reuse_child_sizes: [bool; 2], pub layer: usize, pub(super) id: WidgetId, } pub struct DrawResult<'p, 'a> { painter: &'p mut Painter<'a>, child: WidgetId, } impl DrawResult<'_, '_> { pub fn size(self) -> Size { if !self.painter.size_dependencies.contains(&self.child) { self.painter.size_dependencies.push(self.child); } self.painter.render_state.active[&self.child].size } } impl<'a> Painter<'a> { pub fn set_size(&mut self, size: Size) { assert!( self.size.replace(size).is_none(), "a widget set its size more than once during one draw" ); } fn primitive_at(&mut self, primitive: P, region: UiRegion) { self.write_primitive(primitive, region, Drawn::Yes); } /// **Consumed strictly in order, and one mismatch ends recycling for /// the rest of the draw.** A widget's `draw` is a function of its own /// state, so a redraw writes the same sequence of primitives in the /// same order in the overwhelmingly common case; searching the /// remainder for a match would turn an O(1) step into an O(primitives) /// one to rescue a case that means the widget's content changed shape /// anyway. Stopping is also what keeps the invariant simple: every /// handle from `recycled` on is untouched and gets freed together. fn take_recycled(&mut self, binding: u32, drawn: Drawn) -> Option { let h = self.recycle.peek()?; let drawn_matches = (h.pos == NOT_DRAWN) == (drawn == Drawn::No); if h.binding != binding || h.layer != self.layer || !drawn_matches { return None; } self.recycle.next() } fn write_primitive( &mut self, primitive: P, region: UiRegion, drawn: Drawn, ) -> u32 { let inst = PrimitiveInst { id: self.id, primitive, region, mask_idx: self.mask, move_idx: self.move_slot, }; let h = match self.take_recycled(P::BINDING, drawn) { Some(h) => { self.render_state.primitives.recycle(&h, inst); h } None => self.render_state.write_primitive(self.layer, drawn, inst), }; if self.mask != MaskIdx::NONE { self.rsc.ui_mut().masks.push_ref(self.mask); } let slot = h.slot; self.own(h); slot } /// Take ownership of a handle this widget just wrote. fn own(&mut self, h: PrimitiveHandle) { self.render_state .primitives .set_handle_index(h.slot, self.primitives.len() as u32); self.primitives.push(h); } pub fn primitive(&mut self, primitive: P) { self.primitive_at(primitive, self.region) } /// Resolves a public paint handle to the compact index stored by a GPU /// primitive and retains the handle for exactly as long as that draw. pub fn paint(&mut self, paint: &PaintId) -> u32 { if !self.paints.contains(paint) { self.paints.push(paint.clone()); } paint.slot() } pub fn paint_value(&mut self, paint: &mut crate::PaintValue) -> u32 { let paint = paint.resolve(&mut self.rsc.ui_mut().paints).clone(); self.paint(&paint) } pub fn primitive_within(&mut self, primitive: P, region: UiRegion) { self.primitive_at(primitive, region.within(&self.region)); } /// The slot is allocated once and **rewritten in place** on every /// later draw rather than pushed again, because a descendant whose own /// region did not change is not redrawn (`draw_inner`'s fast path) and /// so keeps pointing at whichever slot it was drawn under. See /// `ActiveData::own_mask` for what pushing a fresh one cost. pub fn set_mask(&mut self, region: UiRegion) { let paint = self.paint(&PaintId::NONE); let shape = self.write_primitive(RectPrimitive::color(paint), region, Drawn::No); self.set_mask_to(shape); } /// Clip everything this widget draws after this call to `shape`'s /// own shape -- the first primitive `shape`'s subtree drew, which /// must already have been drawn this frame /// (`UiRenderState::first_primitive`). What `.masked_by()` uses to /// clip a container's content to the rounded background it draws, /// with no radius argument anywhere that could fall out of step with /// the one being drawn. pub fn set_mask_to_widget(&mut self, shape: &StrongWidget) { let slot = self.render_state.first_primitive(shape.id()).unwrap_or_else(|| { panic!( "'{}' was given as a mask's shape but drew no primitive, so there is nothing to \ clip to", self.rsc.widgets().label(shape.id()), ) }); self.set_mask_to(slot); } fn set_mask_to(&mut self, shape: u32) { assert!( self.own_mask == MaskIdx::NONE || self.mask != self.own_mask, "set_mask called twice while drawing one widget: the second would replace the first \ rather than nest inside it", ); let binding = self.render_state.primitives.instance(shape).binding; assert_eq!( binding, RectPrimitive::BINDING, "a mask's shape must be a rect primitive; primitive {shape} is binding {binding}", ); let parent = self.mask; let mask = Mask { primitive: shape, parent, }; let old_parent = if self.own_mask == MaskIdx::NONE { let slot = self.rsc.ui_mut().masks.push(mask); self.rsc.ui_mut().masks.push_ref(slot); self.own_mask = slot; MaskIdx::NONE } else { let old = self.rsc.ui().masks[self.own_mask.idx()].parent; *self.rsc.ui_mut().masks.get_mut(self.own_mask) = mask; old }; if old_parent != parent { if parent != MaskIdx::NONE { self.rsc.ui_mut().masks.push_ref(parent); } if old_parent != MaskIdx::NONE { self.rsc.ui_mut().masks.remove(old_parent); } } self.mask = self.own_mask; } pub fn widget<'p, W: ?Sized>(&'p mut self, id: &StrongWidget) -> DrawResult<'p, 'a> { self.widget_at(id, self.region) } pub fn widget_within<'p, W: ?Sized>( &'p mut self, id: &StrongWidget, region: UiRegion, ) -> DrawResult<'p, 'a> { self.widget_at(id, region.within(&self.region)) } /// Translate this widget's children as one retained subtree, in output /// pixels. The first call must happen before drawing a child, because the /// slot becomes the parent of every direct child's ordinary move slot. /// Once retained, it may be updated later in a redraw (for example after /// measuring a changed child). All deeper descendants inherit it and the /// CPU hit-test walk resolves the same translation as the shader. pub fn set_child_offset(&mut self, offset: Vec2) { let slot = match self.child_move_slot { Some(slot) => slot, None => { assert!( self.children.is_empty(), "a child offset must be created before drawing a child" ); let parent = self.move_slot.idx() as u32; let slot = self .rsc .ui_mut() .move_offsets .push(MoveOffset::new([offset.x, offset.y], parent)); // One ref for this widget's ownership and one on the // up-link. Direct children take their own refs when their // move slots are allocated. self.rsc.ui_mut().move_offsets.push_ref(slot); self.rsc.ui_mut().move_offsets.push_ref(self.move_slot); self.child_move_slot = Some(slot); return; } }; let next = [offset.x, offset.y]; if self.rsc.ui().move_offsets[slot.idx()].delta != next { self.rsc.ui_mut().move_offsets.get_mut(slot).delta = next; self.render_state.note_move(); } } pub fn known_len(&mut self, id: &StrongWidget, axis: Axis) -> Option { let len = if let Some(len) = self.rsc.widgets().get_dyn(id.id())?.size_hint(axis) { Some(len.fold_dp(self.density())) } else if !self.reuse_child_sizes[match axis { Axis::X => 0, Axis::Y => 1, }] || self.rsc.widgets().needs_redraw.contains(&id.id()) { None } else { self.render_state .active .get(&id.id()) .map(|a| a.size.axis(axis)) }; if len.is_some() && !self.size_dependencies.contains(&id.id()) { self.size_dependencies.push(id.id()); } len } fn widget_at<'p, W: ?Sized>( &'p mut self, id: &StrongWidget, region: UiRegion, ) -> DrawResult<'p, 'a> { self.children.push(id.id()); let parent_move_slot = self.child_move_slot.unwrap_or(self.move_slot); self.render_state.draw_inner( self.layer, id.id(), region, Some(self.id), parent_move_slot.idx() as u32, self.mask, Retained::default(), self.rsc, ); DrawResult { painter: self, child: id.id(), } } pub fn place<'p, W: ?Sized>( &'p mut self, id: &StrongWidget, region: UiRegion, ) -> DrawResult<'p, 'a> { let region = region.within(&self.region); let retained = self .render_state .active .get(&id.id()) .map(|active| (active.layer, active.mask)); if self.render_state.place(id.id(), region, self.rsc).is_some() { } else if let Some((layer, mask)) = retained { self.children.push(id.id()); self.rsc.widgets_mut().needs_redraw.insert(id.id()); let parent_move_slot = self.child_move_slot.unwrap_or(self.move_slot); self.render_state.draw_inner( layer, id.id(), region, Some(self.id), parent_move_slot.idx() as u32, mask, Retained::default(), self.rsc, ); } else { self.children.push(id.id()); let parent_move_slot = self.child_move_slot.unwrap_or(self.move_slot); self.render_state.draw_inner( self.layer, id.id(), region, Some(self.id), parent_move_slot.idx() as u32, self.mask, Retained::default(), self.rsc, ); } DrawResult { painter: self, child: id.id(), } } pub fn place_used( &mut self, id: &StrongWidget, used: Size, within: UiRegion, ) -> DrawResult<'_, 'a> { let region = self.fit_region(used, within); self.place(id, region) } pub fn fit_region(&mut self, used: Size, mut within: UiRegion) -> UiRegion { let mut region = used .to_uivec2(self.density()) .align(RegionAlign::TOP_LEFT) .within(&within); let output = self.output_size(); for axis in [Axis::X, Axis::Y] { let mut actual = region.within(&self.region); let mut available = within.within(&self.region); if actual.axis(axis).len().to_abs(output.axis(axis)) > available.axis(axis).len().to_abs(output.axis(axis)) { *region.axis_mut(axis) = *within.axis(axis); } } region } pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) { self.textures.push(handle.clone()); self.write_image(handle.image_index(), region.within(&self.region)); } pub fn texture(&mut self, handle: &TextureHandle) { self.textures.push(handle.clone()); self.write_image(handle.image_index(), self.region); } pub fn texture_at(&mut self, handle: &TextureHandle, region: UiRegion) { self.textures.push(handle.clone()); self.write_image(handle.image_index(), region); } fn write_image(&mut self, texture_idx: u32, region: UiRegion) { let h = match self.take_recycled(IMAGE_BINDING, Drawn::Yes) { Some(h) => { self.render_state.primitives.recycle_image( &h, self.id, texture_idx, region, self.mask, self.move_slot, ); h } None => self.render_state.write_image( self.layer, self.id, texture_idx, region, self.mask, self.move_slot, ), }; if self.mask != MaskIdx::NONE { self.rsc.ui_mut().masks.push_ref(self.mask); } self.own(h); } pub fn render_text(&mut self, text: &TextHandle, width: Option) -> RenderedText { let density = self.render_state.density; let ui: &mut UiData = self.rsc.ui_mut(); let (rendered, prepared) = text.render(width, self.id, &mut ui.textures, density); self.render_state.shape_count += u64::from(prepared); rendered } fn atlas_generation(&self) -> u64 { self.rsc.ui().text.borrow().atlas.generation() } pub fn glyphs(&mut self, text: &RenderedText, origin: UiRegion) { // A caller re-emitting quads placed against an atlas that has since // been cleared draws every glyph from coordinates now holding // something else. Caught at the submission rather than on screen, // where it reads as fragments of unrelated letters. `assert_eq!` // for R1's reason: two integers per laid-out string, not per // glyph, and the failure is unreadable text on a release build. assert_eq!( text.generation, self.atlas_generation(), "glyphs placed against atlas generation {} submitted against {}: the holder did not \ re-render after the atlas was cleared", text.generation, self.atlas_generation(), ); let flags_for = |is_color| { if is_color { GlyphPrimitive::IS_COLOR } else { 0 } }; for paint in text.paints.iter() { self.paint(paint); } for glyph in text.glyphs.iter() { let mut region = origin; region.x.end = region.x.start; region.y.end = region.y.start; let mut region = region.offset(UiVec2::abs(glyph.offset)); region.x.end = region.x.start + UiScalar::abs(glyph.entry.width as f32); region.y.end = region.y.start + UiScalar::abs(glyph.entry.height as f32); self.primitive_at( GlyphPrimitive::new( glyph.entry.uv_min, glyph.entry.uv_max, glyph.entry.layer, glyph.paint, flags_for(glyph.entry.is_color), ), region, ); } } pub fn region(&self) -> UiRegion { self.region } pub fn output_size(&self) -> Vec2 { self.render_state.output_size } /// Physical pixels per `dp` -- see `UiRenderState::density`'s field /// doc. What `Len::dp`'s `apply_rest` call resolves against. pub fn density(&self) -> f32 { self.render_state.density } pub fn px_size(&mut self) -> Vec2 { self.region.size().to_abs(self.render_state.output_size) } pub fn text_resources(&mut self) -> Rc> { self.rsc.ui().text.clone() } pub fn child_layer(&mut self) { self.layer = self.render_state.layers.child(self.layer); } pub fn next_layer(&mut self) { self.layer = self.render_state.layers.next(self.layer); } pub fn label(&self) -> &str { &self.rsc.widgets().data(self.id).unwrap().label } pub fn id(&self) -> &WidgetId { &self.id } }