Browse Source

LibGfx+LibWeb: Use ref-counted object to store glyph run

...to avoid allocating a copy of glyph run for painting commands. We
can't simply save pointers to a glyph run in layout/paintable tree
because it should be safe to deallocate layout and paintable trees
after painting commands are recorded, if in the future we decide to
move command execution to a separate thread.
Aliaksandr Kalenik 1 year ago
parent
commit
06c176bbfb

+ 17 - 0
Userland/Libraries/LibGfx/TextLayout.h

@@ -100,6 +100,23 @@ struct DrawEmoji {
 
 using DrawGlyphOrEmoji = Variant<DrawGlyph, DrawEmoji>;
 
+class GlyphRun : public RefCounted<GlyphRun> {
+public:
+    GlyphRun() = default;
+    GlyphRun(Vector<Gfx::DrawGlyphOrEmoji>&& glyphs)
+        : m_glyphs(move(glyphs))
+    {
+    }
+
+    [[nodiscard]] Vector<Gfx::DrawGlyphOrEmoji> const& glyphs() const { return m_glyphs; }
+    [[nodiscard]] bool is_empty() const { return m_glyphs.is_empty(); }
+
+    void append(Gfx::DrawGlyphOrEmoji glyph) { m_glyphs.append(glyph); }
+
+private:
+    Vector<Gfx::DrawGlyphOrEmoji> m_glyphs;
+};
+
 Variant<DrawGlyph, DrawEmoji> prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIterator& it, Font const& font);
 
 template<typename Callback>

+ 1 - 1
Userland/Libraries/LibWeb/Layout/LineBox.cpp

@@ -26,7 +26,7 @@ void LineBox::add_fragment(Node const& layout_node, int start, int length, CSSPi
         m_fragments.last().set_width(m_fragments.last().width() + content_width);
         for (auto glyph : glyph_run) {
             glyph.visit([&](auto& glyph) { glyph.position.translate_by(fragment_width.to_float(), 0); });
-            m_fragments.last().m_glyph_run.append(glyph);
+            m_fragments.last().m_glyph_run->append(glyph);
         }
     } else {
         Vector<Gfx::DrawGlyphOrEmoji> glyph_run_copy { glyph_run };

+ 4 - 4
Userland/Libraries/LibWeb/Layout/LineBoxFragment.h

@@ -19,7 +19,7 @@ class LineBoxFragment {
     friend class LineBox;
 
 public:
-    LineBoxFragment(Node const& layout_node, int start, int length, CSSPixelPoint offset, CSSPixelSize size, CSSPixels border_box_top, CSSPixels border_box_bottom, Vector<Gfx::DrawGlyphOrEmoji> glyph_run = {})
+    LineBoxFragment(Node const& layout_node, int start, int length, CSSPixelPoint offset, CSSPixelSize size, CSSPixels border_box_top, CSSPixels border_box_bottom, Vector<Gfx::DrawGlyphOrEmoji> glyphs)
         : m_layout_node(layout_node)
         , m_start(start)
         , m_length(length)
@@ -27,7 +27,7 @@ public:
         , m_size(size)
         , m_border_box_top(border_box_top)
         , m_border_box_bottom(border_box_bottom)
-        , m_glyph_run(move(glyph_run))
+        , m_glyph_run(adopt_ref(*new Gfx::GlyphRun(move(glyphs))))
     {
     }
 
@@ -60,7 +60,7 @@ public:
 
     bool is_atomic_inline() const;
 
-    Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run() const { return m_glyph_run; }
+    Gfx::GlyphRun const& glyph_run() const { return *m_glyph_run; }
 
 private:
     JS::NonnullGCPtr<Node const> m_layout_node;
@@ -71,7 +71,7 @@ private:
     CSSPixels m_border_box_top { 0 };
     CSSPixels m_border_box_bottom { 0 };
     CSSPixels m_baseline { 0 };
-    Vector<Gfx::DrawGlyphOrEmoji> m_glyph_run;
+    NonnullRefPtr<Gfx::GlyphRun> m_glyph_run;
 };
 
 }

+ 1 - 1
Userland/Libraries/LibWeb/Painting/Command.h

@@ -38,7 +38,7 @@
 namespace Web::Painting {
 
 struct DrawGlyphRun {
-    Vector<Gfx::DrawGlyphOrEmoji> glyph_run;
+    NonnullRefPtr<Gfx::GlyphRun> glyph_run;
     Color color;
     Gfx::IntRect rect;
     Gfx::FloatPoint translation;

+ 2 - 2
Userland/Libraries/LibWeb/Painting/CommandList.cpp

@@ -49,7 +49,7 @@ void CommandList::execute(CommandExecutor& executor)
             auto& command = command_with_scroll_id.command;
             if (command.has<DrawGlyphRun>()) {
                 auto scale = command.get<DrawGlyphRun>().scale;
-                for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run) {
+                for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run->glyphs()) {
                     if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
                         auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
                         auto const& font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(scale));
@@ -89,7 +89,7 @@ void CommandList::execute(CommandExecutor& executor)
 
         auto result = command.visit(
             [&](DrawGlyphRun const& command) {
-                return executor.draw_glyph_run(command.glyph_run, command.color, command.translation, command.scale);
+                return executor.draw_glyph_run(command.glyph_run->glyphs(), command.color, command.translation, command.scale);
             },
             [&](DrawText const& command) {
                 return executor.draw_text(command.rect, command.raw_text, command.alignment, command.color,

+ 2 - 2
Userland/Libraries/LibWeb/Painting/PaintableFragment.h

@@ -38,7 +38,7 @@ public:
 
     CSSPixelRect const absolute_rect() const;
 
-    Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run() const { return m_glyph_run; }
+    Gfx::GlyphRun const& glyph_run() const { return *m_glyph_run; }
 
     CSSPixelRect selection_rect(Gfx::Font const&) const;
 
@@ -55,7 +55,7 @@ private:
     int m_start;
     int m_length;
     Painting::BorderRadiiData m_border_radii_data;
-    Vector<Gfx::DrawGlyphOrEmoji> m_glyph_run;
+    NonnullRefPtr<Gfx::GlyphRun> m_glyph_run;
     Vector<ShadowData> m_shadows;
     bool m_contained_by_inline_node { false };
 };

+ 2 - 2
Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp

@@ -206,11 +206,11 @@ void RecordingPainter::draw_signed_distance_field(Gfx::IntRect const& dst_rect,
     });
 }
 
-void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, Gfx::IntRect const& rect, double scale)
+void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Gfx::GlyphRun const& glyph_run, Color color, Gfx::IntRect const& rect, double scale)
 {
     auto transformed_baseline_start = state().translation.map(baseline_start).to_type<float>();
     append(DrawGlyphRun {
-        .glyph_run = Vector<Gfx::DrawGlyphOrEmoji> { glyph_run },
+        .glyph_run = glyph_run,
         .color = color,
         .rect = state().translation.map(rect),
         .translation = transformed_baseline_start,

+ 1 - 1
Userland/Libraries/LibWeb/Painting/RecordingPainter.h

@@ -100,7 +100,7 @@ public:
     void draw_signed_distance_field(Gfx::IntRect const& dst_rect, Color color, Gfx::GrayscaleBitmap const& sdf, float smoothing);
 
     // Streamlined text drawing routine that does no wrapping/elision/alignment.
-    void draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, Gfx::IntRect const& rect, double scale);
+    void draw_text_run(Gfx::IntPoint baseline_start, Gfx::GlyphRun const& glyph_run, Color color, Gfx::IntRect const& rect, double scale);
 
     void add_clip_rect(Gfx::IntRect const& rect);
 

+ 2 - 2
Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp

@@ -590,8 +590,8 @@ void paint_text_shadow(PaintContext& context, PaintableFragment const& fragment,
     auto fragment_baseline = context.rounded_device_pixels(fragment.baseline()).value();
 
     Vector<Gfx::DrawGlyphOrEmoji> scaled_glyph_run;
-    scaled_glyph_run.ensure_capacity(fragment.glyph_run().size());
-    for (auto glyph : fragment.glyph_run()) {
+    scaled_glyph_run.ensure_capacity(fragment.glyph_run().glyphs().size());
+    for (auto glyph : fragment.glyph_run().glyphs()) {
         glyph.visit([&](auto& glyph) {
             glyph.font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(context.device_pixels_per_css_pixel()));
             glyph.position = glyph.position.scaled(context.device_pixels_per_css_pixel());