Explorar el Código

app/text2: modify text rendering so we build a single atlas for an entire parley Layout. this is handy if a layout does not change (text/styles) just the wrapping. then we can reuse the atlas.

jkds hace 7 meses
padre
commit
6c0dafbceb
Se han modificado 2 ficheros con 90 adiciones y 33 borrados
  1. 27 21
      bin/app/src/text2/atlas.rs
  2. 63 12
      bin/app/src/text2/render.rs

+ 27 - 21
bin/app/src/text2/atlas.rs

@@ -34,7 +34,8 @@ pub fn make_texture_atlas(render_api: &RenderApi, glyphs: &Vec<Glyph>) -> Render
 }
 */
 
-//pub struct Sprite(swash::scale::image::Image);
+pub(super) type RunIdx = usize;
+type GlyphKey = (swash::GlyphId, RunIdx);
 
 /// Responsible for aggregating glyphs, and then producing a single software
 /// blitted texture usable in a single draw call.
@@ -42,14 +43,13 @@ pub fn make_texture_atlas(render_api: &RenderApi, glyphs: &Vec<Glyph>) -> Render
 ///
 /// ```rust
 ///     let mut atlas = Atlas::new(&render_api);
-///     atlas.push(&glyphs);    // repeat as needed for shaped lines
+///     atlas.push_glyph(glyph, run_idx, &mut scaler);
 ///     let atlas = atlas.make().unwrap();
-///     let uv = atlas.fetch_uv(glyph_id).unwrap();
+///     let uv = atlas.fetch_uv(glyph_id, run_idx).unwrap();
 ///     let atlas_texture_id = atlas.texture_id;
 /// ```
 pub struct Atlas<'a> {
-    scaler: swash::scale::Scaler<'a>,
-    glyph_ids: Vec<swash::GlyphId>,
+    glyph_keys: Vec<GlyphKey>,
     sprites: Vec<swash::scale::image::Image>,
     // LHS x pos of glyph
     x_pos: Vec<usize>,
@@ -62,10 +62,9 @@ pub struct Atlas<'a> {
 }
 
 impl<'a> Atlas<'a> {
-    pub fn new(scaler: swash::scale::Scaler<'a>, render_api: &'a RenderApi, tag: DebugTag) -> Self {
+    pub fn new(render_api: &'a RenderApi, tag: DebugTag) -> Self {
         Self {
-            scaler,
-            glyph_ids: vec![],
+            glyph_keys: vec![],
             sprites: vec![],
             x_pos: vec![],
 
@@ -80,12 +79,18 @@ impl<'a> Atlas<'a> {
         }
     }
 
-    pub fn push_glyph(&mut self, glyph_id: swash::GlyphId) {
-        if self.glyph_ids.contains(&glyph_id) {
+    pub fn push_glyph(
+        &mut self,
+        glyph_id: swash::GlyphId,
+        run_idx: RunIdx,
+        scaler: &mut swash::scale::Scaler,
+    ) {
+        let glyph_key = (glyph_id, run_idx);
+        if self.glyph_keys.contains(&glyph_key) {
             return
         }
 
-        self.glyph_ids.push(glyph_id);
+        self.glyph_keys.push(glyph_key);
 
         let rendered_glyph = swash::scale::Render::new(
             // Select our source order
@@ -97,7 +102,7 @@ impl<'a> Atlas<'a> {
         )
         // Select the simple alpha (non-subpixel) format
         .format(zeno::Format::Alpha)
-        .render(&mut self.scaler, glyph_id)
+        .render(scaler, glyph_id)
         .unwrap();
 
         let glyph_width = rendered_glyph.placement.width as usize;
@@ -171,12 +176,12 @@ impl<'a> Atlas<'a> {
     /// `rendered_atlas.fetch_uv(my_glyph_id)`.
     /// The texture ID is a struct member: `rendered_atlas.texture_id`.
     pub fn make(self) -> RenderedAtlas {
-        //if self.glyph_ids.is_empty() {
+        //if self.glyph_keys.is_empty() {
         //    return Err(Error::AtlasIsEmpty)
         //}
 
-        assert_eq!(self.glyph_ids.len(), self.sprites.len());
-        assert_eq!(self.glyph_ids.len(), self.x_pos.len());
+        assert_eq!(self.glyph_keys.len(), self.sprites.len());
+        assert_eq!(self.glyph_keys.len(), self.x_pos.len());
 
         let atlas = self.render();
         let texture = self.render_api.new_texture(
@@ -188,7 +193,7 @@ impl<'a> Atlas<'a> {
         );
 
         let uv_rects = self.compute_uvs();
-        let glyph_ids = self.glyph_ids;
+        let glyph_keys = self.glyph_keys;
 
         let mut infos = Vec::with_capacity(self.sprites.len());
         for (uv_rect, sprite) in uv_rects.into_iter().zip(self.sprites.into_iter()) {
@@ -200,7 +205,7 @@ impl<'a> Atlas<'a> {
             infos.push(GlyphInfo { uv_rect, place: sprite.placement, is_color });
         }
 
-        RenderedAtlas { glyph_ids, infos, texture }
+        RenderedAtlas { glyph_keys, infos, texture }
     }
 }
 
@@ -269,7 +274,7 @@ pub struct GlyphInfo {
 /// Final result computed from `Atlas::make()`.
 #[derive(Clone)]
 pub struct RenderedAtlas {
-    glyph_ids: Vec<swash::GlyphId>,
+    glyph_keys: Vec<GlyphKey>,
     infos: Vec<GlyphInfo>,
     /// Allocated atlas texture.
     pub texture: ManagedTexturePtr,
@@ -277,12 +282,13 @@ pub struct RenderedAtlas {
 
 impl RenderedAtlas {
     /// Get UV coords for a glyph within the rendered atlas.
-    pub fn fetch_uv(&self, glyph_id: swash::GlyphId) -> Option<&GlyphInfo> {
-        let glyphs_len = self.glyph_ids.len();
+    pub fn fetch_uv(&self, glyph_id: swash::GlyphId, run_idx: RunIdx) -> Option<&GlyphInfo> {
+        let glyphs_len = self.glyph_keys.len();
         assert_eq!(glyphs_len, self.infos.len());
 
+        let glyph_key = (glyph_id, run_idx);
         for i in 0..glyphs_len {
-            if self.glyph_ids[i] == glyph_id {
+            if self.glyph_keys[i] == glyph_key {
                 return Some(&self.infos[i])
             }
         }

+ 63 - 12
bin/app/src/text2/render.rs

@@ -21,7 +21,7 @@ use crate::{
     mesh::{Color, MeshBuilder, COLOR_WHITE},
 };
 
-use super::atlas::{Atlas, RenderedAtlas};
+use super::atlas::{Atlas, RenderedAtlas, RunIdx};
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub struct DebugRenderOptions(u32);
@@ -63,15 +63,40 @@ pub fn render_layout_with_opts(
     render_api: &RenderApi,
     tag: DebugTag,
 ) -> Vec<DrawInstruction> {
-    let mut scale_cx = swash::scale::ScaleContext::new();
+    // First pass to create atlas
+    let mut scale_ctx = swash::scale::ScaleContext::new();
+    let mut atlas = Atlas::new(render_api, tag);
+    let mut run_idx = 0;
+    for line in layout.lines() {
+        for item in line.items() {
+            match item {
+                parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
+                    push_glyphs(&mut atlas, &glyph_run, run_idx, &mut scale_ctx, render_api, tag);
+                    run_idx += 1;
+                }
+                parley::PositionedLayoutItem::InlineBox(_) => {}
+            }
+        }
+    }
+    // Render the atlas
+    let atlas = atlas.make();
+
+    // Second pass to draw glyphs
     let mut run_idx = 0;
     let mut instrs = vec![];
     for line in layout.lines() {
         for item in line.items() {
             match item {
                 parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
-                    let mesh =
-                        render_glyph_run(&mut scale_cx, &glyph_run, run_idx, opts, render_api, tag);
+                    let mesh = render_glyph_run(
+                        &mut scale_ctx,
+                        &glyph_run,
+                        run_idx,
+                        opts,
+                        &atlas,
+                        render_api,
+                        tag,
+                    );
                     instrs.push(DrawInstruction::Draw(mesh));
                     run_idx += 1;
                 }
@@ -82,11 +107,38 @@ pub fn render_layout_with_opts(
     instrs
 }
 
+fn push_glyphs(
+    atlas: &mut Atlas,
+    glyph_run: &parley::GlyphRun<'_, Color>,
+    run_idx: RunIdx,
+    scale_ctx: &mut swash::scale::ScaleContext,
+    render_api: &RenderApi,
+    tag: DebugTag,
+) {
+    let run = glyph_run.run();
+    let font = run.font();
+    let font_size = run.font_size();
+    let normalized_coords = run.normalized_coords();
+    let font_ref = swash::FontRef::from_index(font.data.as_ref(), font.index as usize).unwrap();
+
+    let mut scaler = scale_ctx
+        .builder(font_ref)
+        .size(font_size)
+        .hint(true)
+        .normalized_coords(normalized_coords)
+        .build();
+
+    for glyph in glyph_run.glyphs() {
+        atlas.push_glyph(glyph.id as u16, run_idx, &mut scaler);
+    }
+}
+
 fn render_glyph_run(
     scale_ctx: &mut swash::scale::ScaleContext,
     glyph_run: &parley::GlyphRun<'_, Color>,
-    _run_idx: usize,
+    run_idx: usize,
     opts: DebugRenderOptions,
+    atlas: &RenderedAtlas,
     render_api: &RenderApi,
     tag: DebugTag,
 ) -> DrawMesh {
@@ -96,8 +148,6 @@ fn render_glyph_run(
     let color = style.brush;
     //trace!(target: "text::render", "render_glyph_run run_idx={run_idx} baseline={run_y}");
 
-    let atlas = create_atlas(scale_ctx, glyph_run, render_api, tag);
-
     let mut mesh = MeshBuilder::new(tag);
 
     if let Some(underline) = &style.underline {
@@ -105,7 +155,7 @@ fn render_glyph_run(
     }
 
     for glyph in glyph_run.glyphs() {
-        let glyph_inf = atlas.fetch_uv(glyph.id as u16).expect("missing glyph UV rect");
+        let glyph_inf = atlas.fetch_uv(glyph.id as u16, run_idx).expect("missing glyph UV rect");
 
         let glyph_x = run_x + glyph.x;
         let glyph_y = run_y - glyph.y;
@@ -133,7 +183,7 @@ fn render_glyph_run(
         );
     }
 
-    mesh.alloc(render_api).draw_with_textures(vec![atlas.texture])
+    mesh.alloc(render_api).draw_with_textures(vec![atlas.texture.clone()])
 }
 
 fn render_underline(
@@ -170,6 +220,7 @@ fn render_underline(
 fn create_atlas(
     scale_ctx: &mut swash::scale::ScaleContext,
     glyph_run: &parley::GlyphRun<'_, Color>,
+    run_idx: usize,
     render_api: &RenderApi,
     tag: DebugTag,
 ) -> RenderedAtlas {
@@ -179,16 +230,16 @@ fn create_atlas(
     let normalized_coords = run.normalized_coords();
     let font_ref = swash::FontRef::from_index(font.data.as_ref(), font.index as usize).unwrap();
 
-    let scaler = scale_ctx
+    let mut scaler = scale_ctx
         .builder(font_ref)
         .size(font_size)
         .hint(true)
         .normalized_coords(normalized_coords)
         .build();
 
-    let mut atlas = Atlas::new(scaler, render_api, tag);
+    let mut atlas = Atlas::new(render_api, tag);
     for glyph in glyph_run.glyphs() {
-        atlas.push_glyph(glyph.id as u16);
+        atlas.push_glyph(glyph.id as u16, run_idx, &mut scaler);
     }
     //atlas.dump(&format!("/tmp/atlas_{run_idx}.png"));
     atlas.make()