Cache glyphs (using display lists) when painting at high frequency
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Apr 2018 20:29:49 +0000 (20:29 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Apr 2018 20:29:49 +0000 (20:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178750
<rdar://problem/35201729>

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2018-04-11
Reviewed by Antti Koivisto.

This patch adds support for caching of glyph drawing commands when painting
at high frequency. Caching the glyph drawing will be done using DisplayLists.

GlyphDisplayListCache is basically a hash map which maps InlineTextBox
or SimpleLineLayout::Run to DisplayList. Before adding a new entry to it
we have to check whether the conditions for caching the glyph DisplayList
are met or not. If no entry is found for a given run, a new DisplayList
is created and a new entry is add to the cache.

* WebCore.xcodeproj/project.pbxproj:
* page/MemoryRelease.cpp:
(WebCore::releaseNoncriticalMemory): Make GlyphDisplayListCache respond
to memory pressure.

* platform/graphics/FontCascade.cpp:
(WebCore::FontCascade::displayListForTextRun const):
* platform/graphics/FontCascade.h:
(WebCore::FontCascade::displayListForTextRun):
Record the drawing of a glyph run into a DisplayList.

* rendering/GlyphDisplayListCache.h: Added.
(WebCore::GlyphDisplayListCache::singleton):
(WebCore::GlyphDisplayListCache::get):
(WebCore::GlyphDisplayListCache::remove):
(WebCore::GlyphDisplayListCache::clear):
(WebCore::GlyphDisplayListCache::size const):
(WebCore::GlyphDisplayListCache::sizeInBytes const):
A simple cache for the TextRun DisplayList. Adding a new entry in the
cache happens under restricted conditions. So this cache is not expected
to grow much.

* rendering/InlineTextBox.cpp:
(WebCore::InlineTextBox::~InlineTextBox):
(WebCore::InlineTextBox::paint):
(WebCore::InlineTextBox::paintMarkedTexts):
(WebCore::InlineTextBox::paintMarkedTextBackground):
(WebCore::InlineTextBox::paintMarkedTextForeground):
(WebCore::InlineTextBox::paintMarkedTextDecoration):
(WebCore::InlineTextBox::paintCompositionBackground):
(WebCore::InlineTextBox::paintCompositionUnderlines const):
(WebCore::InlineTextBox::paintCompositionUnderline const):
* rendering/InlineTextBox.h:
(WebCore::InlineTextBox::paintMarkedTexts):
InlineTextBox::paintMarkedTextForeground() now requires PaintInfo to know
whether the entry in the GlyphDisplayListCache should be removed or not.
Change all the GraphicsContext arguments to be PaintInfo.

* rendering/SimpleLineLayout.cpp:
(WebCore::SimpleLineLayout::Layout::~Layout):
* rendering/SimpleLineLayout.h:
* rendering/SimpleLineLayoutFunctions.cpp:
(WebCore::SimpleLineLayout::paintFlow):
(WebCore::SimpleLineLayout::simpleLineLayoutWillBeDeleted):
* rendering/SimpleLineLayoutFunctions.h:
* rendering/SimpleLineLayoutResolver.h:
(WebCore::SimpleLineLayout::RunResolver::Run::simpleRun const):
Implement the glyph caching for SimpleLineLayout::Run.

* rendering/TextPainter.cpp:
(WebCore::TextPainter::paintTextOrEmphasisMarks): If the DisplayList is
available, replay it back into the GraphicsContext. Make sure to reset to
the DisplayList pointer to nullptr after painting.

(WebCore::TextPainter::clearGlyphDisplayLists):
(WebCore::TextPainter::shouldUseGlyphDisplayList): Check whether we should
use DisplayList to the draw glyph run.

* rendering/TextPainter.h:
(WebCore::TextPainter::setGlyphDisplayListIfNeeded): Check whether we should
should use DisplayList to the draw glyph run and if we should, ensure first
the DisplayList is cached and set it in the TextPainter so it uses it when
the run is painted.

(WebCore::TextPainter::removeGlyphDisplayList): Remove the cached DisplayList
entry for a glyph layout run.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230544 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/page/MemoryRelease.cpp
Source/WebCore/platform/graphics/FontCascade.cpp
Source/WebCore/platform/graphics/FontCascade.h
Source/WebCore/rendering/GlyphDisplayListCache.h [new file with mode: 0644]
Source/WebCore/rendering/InlineTextBox.cpp
Source/WebCore/rendering/InlineTextBox.h
Source/WebCore/rendering/SimpleLineLayout.cpp
Source/WebCore/rendering/SimpleLineLayout.h
Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp
Source/WebCore/rendering/SimpleLineLayoutFunctions.h
Source/WebCore/rendering/SimpleLineLayoutResolver.h
Source/WebCore/rendering/TextPainter.cpp
Source/WebCore/rendering/TextPainter.h

index 16a75bd..b899f37 100644 (file)
@@ -1,3 +1,87 @@
+2018-04-11  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Cache glyphs (using display lists) when painting at high frequency
+        https://bugs.webkit.org/show_bug.cgi?id=178750
+        <rdar://problem/35201729>
+
+        Reviewed by Antti Koivisto.
+
+        This patch adds support for caching of glyph drawing commands when painting
+        at high frequency. Caching the glyph drawing will be done using DisplayLists.
+
+        GlyphDisplayListCache is basically a hash map which maps InlineTextBox
+        or SimpleLineLayout::Run to DisplayList. Before adding a new entry to it
+        we have to check whether the conditions for caching the glyph DisplayList
+        are met or not. If no entry is found for a given run, a new DisplayList
+        is created and a new entry is add to the cache.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * page/MemoryRelease.cpp:
+        (WebCore::releaseNoncriticalMemory): Make GlyphDisplayListCache respond
+        to memory pressure.
+
+        * platform/graphics/FontCascade.cpp:
+        (WebCore::FontCascade::displayListForTextRun const):
+        * platform/graphics/FontCascade.h:
+        (WebCore::FontCascade::displayListForTextRun):
+        Record the drawing of a glyph run into a DisplayList.
+
+        * rendering/GlyphDisplayListCache.h: Added.
+        (WebCore::GlyphDisplayListCache::singleton):
+        (WebCore::GlyphDisplayListCache::get):
+        (WebCore::GlyphDisplayListCache::remove):
+        (WebCore::GlyphDisplayListCache::clear):
+        (WebCore::GlyphDisplayListCache::size const):
+        (WebCore::GlyphDisplayListCache::sizeInBytes const):
+        A simple cache for the TextRun DisplayList. Adding a new entry in the
+        cache happens under restricted conditions. So this cache is not expected
+        to grow much.
+
+        * rendering/InlineTextBox.cpp:
+        (WebCore::InlineTextBox::~InlineTextBox):
+        (WebCore::InlineTextBox::paint):
+        (WebCore::InlineTextBox::paintMarkedTexts):
+        (WebCore::InlineTextBox::paintMarkedTextBackground):
+        (WebCore::InlineTextBox::paintMarkedTextForeground):
+        (WebCore::InlineTextBox::paintMarkedTextDecoration):
+        (WebCore::InlineTextBox::paintCompositionBackground):
+        (WebCore::InlineTextBox::paintCompositionUnderlines const):
+        (WebCore::InlineTextBox::paintCompositionUnderline const):
+        * rendering/InlineTextBox.h:
+        (WebCore::InlineTextBox::paintMarkedTexts):
+        InlineTextBox::paintMarkedTextForeground() now requires PaintInfo to know
+        whether the entry in the GlyphDisplayListCache should be removed or not.
+        Change all the GraphicsContext arguments to be PaintInfo.
+
+        * rendering/SimpleLineLayout.cpp:
+        (WebCore::SimpleLineLayout::Layout::~Layout):
+        * rendering/SimpleLineLayout.h:
+        * rendering/SimpleLineLayoutFunctions.cpp:
+        (WebCore::SimpleLineLayout::paintFlow):
+        (WebCore::SimpleLineLayout::simpleLineLayoutWillBeDeleted):
+        * rendering/SimpleLineLayoutFunctions.h:
+        * rendering/SimpleLineLayoutResolver.h:
+        (WebCore::SimpleLineLayout::RunResolver::Run::simpleRun const):
+        Implement the glyph caching for SimpleLineLayout::Run.
+
+        * rendering/TextPainter.cpp:
+        (WebCore::TextPainter::paintTextOrEmphasisMarks): If the DisplayList is
+        available, replay it back into the GraphicsContext. Make sure to reset to
+        the DisplayList pointer to nullptr after painting.
+
+        (WebCore::TextPainter::clearGlyphDisplayLists):
+        (WebCore::TextPainter::shouldUseGlyphDisplayList): Check whether we should
+        use DisplayList to the draw glyph run.
+
+        * rendering/TextPainter.h:
+        (WebCore::TextPainter::setGlyphDisplayListIfNeeded): Check whether we should
+        should use DisplayList to the draw glyph run and if we should, ensure first
+        the DisplayList is cached and set it in the TextPainter so it uses it when
+        the run is painted.
+
+        (WebCore::TextPainter::removeGlyphDisplayList): Remove the cached DisplayList
+        entry for a glyph layout run.
+
 2018-04-11  Brent Fulgham  <bfulgham@apple.com>
 
         GraphicsLayerCA::createPlatformCALayer always disables extended color in its backing store
index 2ae0c8a..92034be 100644 (file)
                555B87EB1CAAF0AB00349425 /* ImageDecoderCG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageDecoderCG.h; sourceTree = "<group>"; };
                5576A5621D88A70800CCC04C /* ImageFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFrame.cpp; sourceTree = "<group>"; };
                5576A5631D88A70800CCC04C /* ImageFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFrame.h; sourceTree = "<group>"; };
+               5597FCCB2076C06800D35BB0 /* GlyphDisplayListCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GlyphDisplayListCache.h; sourceTree = "<group>"; };
                55A336F61D8209F40022C4C7 /* NativeImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeImage.h; sourceTree = "<group>"; };
                55A336F81D821E3C0022C4C7 /* ImageBackingStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageBackingStore.h; sourceTree = "<group>"; };
                55AF14E31EAAC59B0026EEAA /* UTIRegistry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UTIRegistry.cpp; sourceTree = "<group>"; };
                                9A528E8117D7F52F00AA9518 /* FloatingObjects.cpp */,
                                9A528E8217D7F52F00AA9518 /* FloatingObjects.h */,
                                935C477409AC4D8D00A6AAB4 /* GapRects.h */,
+                               5597FCCB2076C06800D35BB0 /* GlyphDisplayListCache.h */,
                                E112F4701E3A85F200D6CDFD /* Grid.cpp */,
                                E112F46F1E3A85D800D6CDFD /* Grid.h */,
                                E12DE7151E4B748700F9ACCF /* GridTrackSizingAlgorithm.cpp */,
index 70385ae..57778bf 100644 (file)
@@ -46,6 +46,7 @@
 #include "ScrollingThread.h"
 #include "StyleScope.h"
 #include "StyledElement.h"
+#include "TextPainter.h"
 #include "WorkerThread.h"
 #include <wtf/FastMalloc.h>
 #include <wtf/SystemTracing.h>
@@ -64,6 +65,7 @@ static void releaseNoncriticalMemory()
     FontDescription::invalidateCaches();
 
     clearWidthCaches();
+    TextPainter::clearGlyphDisplayLists();
 
     for (auto* document : Document::allDocuments())
         document->clearSelectorQueryCache();
index d3f3417..847861f 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "CharacterProperties.h"
 #include "ComplexTextController.h"
+#include "DisplayListRecorder.h"
 #include "FloatRect.h"
 #include "FontCache.h"
 #include "GlyphBuffer.h"
@@ -305,6 +306,32 @@ void FontCascade::drawEmphasisMarks(GraphicsContext& context, const TextRun& run
         drawEmphasisMarksForComplexText(context, run, mark, point, from, destination);
 }
 
+std::unique_ptr<DisplayList::DisplayList> FontCascade::displayListForTextRun(GraphicsContext& context, const TextRun& run, unsigned from, std::optional<unsigned> to, CustomFontNotReadyAction customFontNotReadyAction) const
+{
+    ASSERT(!context.paintingDisabled());
+    unsigned destination = to.value_or(run.length());
+    
+    // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
+    CodePath codePathToUse = codePath(run);
+    if (codePathToUse != Complex && (enableKerning() || requiresShaping()) && (from || destination != run.length()))
+        codePathToUse = Complex;
+    
+    GlyphBuffer glyphBuffer;
+    float startX = glyphBufferForTextRun(codePathToUse, run, from, destination, glyphBuffer);
+    // We couldn't generate any glyphs for the run. Give up.
+    if (glyphBuffer.isEmpty())
+        return nullptr;
+    
+    std::unique_ptr<DisplayList::DisplayList> displayList = std::make_unique<DisplayList::DisplayList>();
+    GraphicsContext recordingContext([&](GraphicsContext& displayListContext) {
+        return std::make_unique<DisplayList::Recorder>(displayListContext, *displayList, context.state(), FloatRect(), AffineTransform());
+    });
+    
+    FloatPoint startPoint(startX, 0);
+    drawGlyphBuffer(recordingContext, glyphBuffer, startPoint, customFontNotReadyAction);
+    return displayList;
+}
+    
 float FontCascade::widthOfTextRange(const TextRun& run, unsigned from, unsigned to, HashSet<const Font*>* fallbackFonts, float* outWidthBeforeRange, float* outWidthAfterRange) const
 {
     ASSERT(from <= to);
index 22443e3..e1e1b52 100644 (file)
@@ -48,6 +48,10 @@ class RenderText;
 class TextLayout;
 class TextRun;
 
+namespace DisplayList {
+class DisplayList;
+}
+    
 struct GlyphData;
 
 struct GlyphOverflow {
@@ -197,6 +201,8 @@ public:
 
     WeakPtr<FontCascade> createWeakPtr() const { return m_weakPtrFactory.createWeakPtr(*const_cast<FontCascade*>(this)); }
 
+    std::unique_ptr<DisplayList::DisplayList> displayListForTextRun(GraphicsContext&, const TextRun&, unsigned from = 0, std::optional<unsigned> to = { }, CustomFontNotReadyAction = CustomFontNotReadyAction::DoNotPaintIfFontNotReady) const;
+    
 private:
     enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis };
 
diff --git a/Source/WebCore/rendering/GlyphDisplayListCache.h b/Source/WebCore/rendering/GlyphDisplayListCache.h
new file mode 100644 (file)
index 0000000..2b2f223
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "DisplayList.h"
+#include "FontCascade.h"
+#include "Logging.h"
+#include "TextRun.h"
+#include <wtf/HashMap.h>
+#include <wtf/MemoryPressureHandler.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+template<typename LayoutRun>
+class GlyphDisplayListCache {
+public:
+    GlyphDisplayListCache() = default;
+
+    static GlyphDisplayListCache& singleton()
+    {
+        static NeverDestroyed<GlyphDisplayListCache> cache;
+        return cache;
+    }
+
+    DisplayList::DisplayList* get(const LayoutRun& run, const FontCascade& font, GraphicsContext& context, const TextRun& textRun)
+    {
+        if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
+            if (!m_glyphRunMap.isEmpty()) {
+                LOG(MemoryPressure, "GlyphDisplayListCache::%s - Under memory pressure - size: %d - sizeInBytes: %ld", __FUNCTION__, size(), sizeInBytes());
+                clear();
+            }
+            return nullptr;
+        }
+
+        if (auto displayList = m_glyphRunMap.get(&run))
+            return displayList;
+
+        if (auto displayList = font.displayListForTextRun(context, textRun))
+            return m_glyphRunMap.add(&run, WTFMove(displayList)).iterator->value.get();
+
+        return nullptr;
+    }
+
+    void remove(const LayoutRun& run)
+    {
+        m_glyphRunMap.remove(&run);
+    }
+
+    void clear()
+    {
+        m_glyphRunMap.clear();
+    }
+
+    unsigned size() const
+    {
+        return m_glyphRunMap.size();
+    }
+    
+    size_t sizeInBytes() const
+    {
+        size_t sizeInBytes = 0;
+        for (const auto& entry : m_glyphRunMap)
+            sizeInBytes += entry.value->sizeInBytes();
+        return sizeInBytes;
+    }
+    
+private:
+    using GlyphRunMap = HashMap<const LayoutRun*, std::unique_ptr<DisplayList::DisplayList>>;
+    GlyphRunMap m_glyphRunMap;
+};
+    
+}
index 11f58b1..9ee2515 100644 (file)
@@ -73,6 +73,7 @@ InlineTextBox::~InlineTextBox()
 {
     if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
         gTextBoxesWithOverflow->remove(this);
+    TextPainter::removeGlyphDisplayList(*this);
 }
 
 void InlineTextBox::markDirty(bool dirty)
@@ -503,7 +504,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
     // and composition underlines.
     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
         if (containsComposition && !useCustomUnderlines)
-            paintCompositionBackground(context, boxOrigin);
+            paintCompositionBackground(paintInfo, boxOrigin);
 
         Vector<MarkedText> markedTexts = collectMarkedTextsForDocumentMarkers(TextPaintPhase::Background);
 #if ENABLE(TEXT_SELECTION)
@@ -518,7 +519,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
         // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
         auto coalescedStyledMarkedTexts = coalesceAdjacentMarkedTexts(styledMarkedTexts, &MarkedTextStyle::areBackgroundMarkedTextStylesEqual);
 
-        paintMarkedTexts(context, TextPaintPhase::Background, boxRect, coalescedStyledMarkedTexts);
+        paintMarkedTexts(paintInfo, TextPaintPhase::Background, boxRect, coalescedStyledMarkedTexts);
     }
 
     // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
@@ -564,7 +565,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
     // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
     auto coalescedStyledMarkedTexts = coalesceAdjacentMarkedTexts(styledMarkedTexts, &MarkedTextStyle::areForegroundMarkedTextStylesEqual);
 
-    paintMarkedTexts(context, TextPaintPhase::Foreground, boxRect, coalescedStyledMarkedTexts);
+    paintMarkedTexts(paintInfo, TextPaintPhase::Foreground, boxRect, coalescedStyledMarkedTexts);
 
     // Paint decorations
     TextDecoration textDecorations = lineStyle.textDecorationsInEffect();
@@ -601,7 +602,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
         // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
         auto coalescedStyledMarkedTexts = coalesceAdjacentMarkedTexts(styledMarkedTexts, &MarkedTextStyle::areDecorationMarkedTextStylesEqual);
 
-        paintMarkedTexts(context, TextPaintPhase::Decoration, boxRect, coalescedStyledMarkedTexts, textDecorationSelectionClipOutRect);
+        paintMarkedTexts(paintInfo, TextPaintPhase::Decoration, boxRect, coalescedStyledMarkedTexts, textDecorationSelectionClipOutRect);
     }
 
     // 3. Paint fancy decorations, including composition underlines and platform-specific underlines for spelling errors, grammar errors, et cetera.
@@ -609,7 +610,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
         paintPlatformDocumentMarkers(context, boxOrigin);
 
         if (useCustomUnderlines)
-            paintCompositionUnderlines(context, boxOrigin);
+            paintCompositionUnderlines(paintInfo, boxOrigin);
     }
     
     if (shouldRotate)
@@ -943,29 +944,30 @@ FloatPoint InlineTextBox::textOriginFromBoxRect(const FloatRect& boxRect) const
     return textOrigin;
 }
 
-void InlineTextBox::paintMarkedTexts(GraphicsContext& context, TextPaintPhase phase, const FloatRect& boxRect, const Vector<StyledMarkedText>& markedTexts, const FloatRect& decorationClipOutRect)
+void InlineTextBox::paintMarkedTexts(PaintInfo& paintInfo, TextPaintPhase phase, const FloatRect& boxRect, const Vector<StyledMarkedText>& markedTexts, const FloatRect& decorationClipOutRect)
 {
     switch (phase) {
     case TextPaintPhase::Background:
         for (auto& markedText : markedTexts)
-            paintMarkedTextBackground(context, boxRect.location(), markedText.style.backgroundColor, markedText.startOffset, markedText.endOffset);
+            paintMarkedTextBackground(paintInfo, boxRect.location(), markedText.style.backgroundColor, markedText.startOffset, markedText.endOffset);
         return;
     case TextPaintPhase::Foreground:
         for (auto& markedText : markedTexts)
-            paintMarkedTextForeground(context, boxRect, markedText);
+            paintMarkedTextForeground(paintInfo, boxRect, markedText);
         return;
     case TextPaintPhase::Decoration:
         for (auto& markedText : markedTexts)
-            paintMarkedTextDecoration(context, boxRect, decorationClipOutRect, markedText);
+            paintMarkedTextDecoration(paintInfo, boxRect, decorationClipOutRect, markedText);
         return;
     }
 }
 
-void InlineTextBox::paintMarkedTextBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& color, unsigned clampedStartOffset, unsigned clampedEndOffset)
+void InlineTextBox::paintMarkedTextBackground(PaintInfo& paintInfo, const FloatPoint& boxOrigin, const Color& color, unsigned clampedStartOffset, unsigned clampedEndOffset)
 {
     if (clampedStartOffset >= clampedEndOffset)
         return;
 
+    GraphicsContext& context = paintInfo.context();
     GraphicsContextStateSaver stateSaver { context };
     updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all!
 
@@ -989,11 +991,12 @@ void InlineTextBox::paintMarkedTextBackground(GraphicsContext& context, const Fl
     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color);
 }
 
-void InlineTextBox::paintMarkedTextForeground(GraphicsContext& context, const FloatRect& boxRect, const StyledMarkedText& markedText)
+void InlineTextBox::paintMarkedTextForeground(PaintInfo& paintInfo, const FloatRect& boxRect, const StyledMarkedText& markedText)
 {
     if (markedText.startOffset >= markedText.endOffset)
         return;
 
+    GraphicsContext& context = paintInfo.context();
     const FontCascade& font = lineFont();
     const RenderStyle& lineStyle = this->lineStyle();
 
@@ -1012,20 +1015,24 @@ void InlineTextBox::paintMarkedTextForeground(GraphicsContext& context, const Fl
         textPainter.setShadow(&markedText.style.textShadow.value());
     textPainter.setEmphasisMark(emphasisMark, emphasisMarkOffset, combinedText());
 
+    TextRun textRun = createTextRun();
+    textPainter.setGlyphDisplayListIfNeeded(*this, paintInfo, font, context, textRun);
+
     GraphicsContextStateSaver stateSaver { context, false };
     if (markedText.type == MarkedText::DraggedContent) {
         stateSaver.save();
         context.setAlpha(markedText.style.alpha);
     }
     // TextPainter wants the box rectangle and text origin of the entire line box.
-    textPainter.paintRange(createTextRun(), boxRect, textOriginFromBoxRect(boxRect), markedText.startOffset, markedText.endOffset);
+    textPainter.paintRange(textRun, boxRect, textOriginFromBoxRect(boxRect), markedText.startOffset, markedText.endOffset);
 }
 
-void InlineTextBox::paintMarkedTextDecoration(GraphicsContext& context, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkedText& markedText)
+void InlineTextBox::paintMarkedTextDecoration(PaintInfo& paintInfo, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkedText& markedText)
 {
     if (m_truncation == cFullTruncation)
         return;
 
+    GraphicsContext& context = paintInfo.context();
     updateGraphicsContext(context, markedText.style.textStyles);
 
     bool isCombinedText = combinedText();
@@ -1073,12 +1080,12 @@ void InlineTextBox::paintMarkedTextDecoration(GraphicsContext& context, const Fl
         context.concatCTM(rotation(boxRect, Counterclockwise));
 }
 
-void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin)
+void InlineTextBox::paintCompositionBackground(PaintInfo& paintInfo, const FloatPoint& boxOrigin)
 {
-    paintMarkedTextBackground(context, boxOrigin, Color::compositionFill, clampedOffset(renderer().frame().editor().compositionStart()), clampedOffset(renderer().frame().editor().compositionEnd()));
+    paintMarkedTextBackground(paintInfo, boxOrigin, Color::compositionFill, clampedOffset(renderer().frame().editor().compositionStart()), clampedOffset(renderer().frame().editor().compositionEnd()));
 }
 
-void InlineTextBox::paintCompositionUnderlines(GraphicsContext& context, const FloatPoint& boxOrigin) const
+void InlineTextBox::paintCompositionUnderlines(PaintInfo& paintInfo, const FloatPoint& boxOrigin) const
 {
     if (m_truncation == cFullTruncation)
         return;
@@ -1095,7 +1102,7 @@ void InlineTextBox::paintCompositionUnderlines(GraphicsContext& context, const F
             break; // Underline is completely after this run, bail. A later run will paint it.
 
         // Underline intersects this run. Paint it.
-        paintCompositionUnderline(context, boxOrigin, underline);
+        paintCompositionUnderline(paintInfo, boxOrigin, underline);
 
         if (underline.endOffset > end() + 1)
             break; // Underline also runs into the next run. Bail now, no more marker advancement.
@@ -1109,7 +1116,7 @@ static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction,
     start = logicalWidth - width - start;
 }
 
-void InlineTextBox::paintCompositionUnderline(GraphicsContext& context, const FloatPoint& boxOrigin, const CompositionUnderline& underline) const
+void InlineTextBox::paintCompositionUnderline(PaintInfo& paintInfo, const FloatPoint& boxOrigin, const CompositionUnderline& underline) const
 {
     if (m_truncation == cFullTruncation)
         return;
@@ -1150,6 +1157,7 @@ void InlineTextBox::paintCompositionUnderline(GraphicsContext& context, const Fl
     start += 1;
     width -= 2;
 
+    GraphicsContext& context = paintInfo.context();
     context.setStrokeColor(underline.compositionUnderlineColor == CompositionUnderlineColor::TextColor ? renderer().style().visitedDependentColor(CSSPropertyWebkitTextFillColor) : underline.color);
     context.setStrokeThickness(lineThickness);
     context.drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
index 4793ca8..1de69bc 100644 (file)
@@ -168,18 +168,18 @@ private:
 
     FloatPoint textOriginFromBoxRect(const FloatRect&) const;
 
-    void paintMarkedTexts(GraphicsContext&, TextPaintPhase, const FloatRect& boxRect, const Vector<StyledMarkedText>&, const FloatRect& decorationClipOutRect = { });
+    void paintMarkedTexts(PaintInfo&, TextPaintPhase, const FloatRect& boxRect, const Vector<StyledMarkedText>&, const FloatRect& decorationClipOutRect = { });
 
     void paintPlatformDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkedText&);
     void paintPlatformDocumentMarkers(GraphicsContext&, const FloatPoint& boxOrigin);
 
-    void paintCompositionBackground(GraphicsContext&, const FloatPoint& boxOrigin);
-    void paintCompositionUnderlines(GraphicsContext&, const FloatPoint& boxOrigin) const;
-    void paintCompositionUnderline(GraphicsContext&, const FloatPoint& boxOrigin, const CompositionUnderline&) const;
+    void paintCompositionBackground(PaintInfo&, const FloatPoint& boxOrigin);
+    void paintCompositionUnderlines(PaintInfo&, const FloatPoint& boxOrigin) const;
+    void paintCompositionUnderline(PaintInfo&, const FloatPoint& boxOrigin, const CompositionUnderline&) const;
 
-    void paintMarkedTextBackground(GraphicsContext&, const FloatPoint& boxOrigin, const Color&, unsigned clampedStartOffset, unsigned clampedEndOffset);
-    void paintMarkedTextForeground(GraphicsContext&, const FloatRect& boxRect, const StyledMarkedText&);
-    void paintMarkedTextDecoration(GraphicsContext&, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkedText&);
+    void paintMarkedTextBackground(PaintInfo&, const FloatPoint& boxOrigin, const Color&, unsigned clampedStartOffset, unsigned clampedEndOffset);
+    void paintMarkedTextForeground(PaintInfo&, const FloatRect& boxRect, const StyledMarkedText&);
+    void paintMarkedTextDecoration(PaintInfo&, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkedText&);
 
     const RenderCombineText* combinedText() const;
     const FontCascade& lineFont() const;
index 7bd5ff6..301300f 100644 (file)
@@ -978,5 +978,10 @@ Layout::Layout(const RunVector& runVector, unsigned lineCount)
     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
 }
 
+Layout::~Layout()
+{
+    simpleLineLayoutWillBeDeleted(*this);
+}
+
 }
 }
index d58729e..527447d 100644 (file)
@@ -81,6 +81,8 @@ public:
     using SimpleLineStruts = Vector<SimpleLineStrut, 4>;
     static std::unique_ptr<Layout> create(const RunVector&, unsigned lineCount);
 
+    ~Layout();
+
     unsigned lineCount() const { return m_lineCount; }
 
     unsigned runCount() const { return m_runCount; }
index 7bed2ac..fb05761 100644 (file)
@@ -120,6 +120,8 @@ void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& pai
         TextRun textRun { run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior() };
         textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
         FloatPoint textOrigin { rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor) };
+
+        textPainter.setGlyphDisplayListIfNeeded(run.simpleRun(), paintInfo, style.fontCascade(), paintInfo.context(), textRun);
         textPainter.paint(textRun, rect, textOrigin);
         if (textDecorationPainter) {
             textDecorationPainter->setWidth(rect.width());
@@ -261,6 +263,12 @@ const RenderObject& rendererForPosition(const FlowContents& flowContents, unsign
     return flowContents.segmentForPosition(position).renderer;
 }
 
+void simpleLineLayoutWillBeDeleted(const Layout& layout)
+{
+    for (unsigned i = 0; i < layout.runCount(); ++i)
+        TextPainter::removeGlyphDisplayList(layout.runAt(i));
+}
+
 #if ENABLE(TREE_DEBUGGING)
 static void printPrefix(TextStream& stream, int& printedCharacters, int depth)
 {
index 3e8b87c..b97f16b 100644 (file)
@@ -66,6 +66,8 @@ LayoutUnit baselineFromFlow(const RenderBlockFlow&);
 
 const RenderObject& rendererForPosition(const FlowContents&, unsigned);
 
+void simpleLineLayoutWillBeDeleted(const Layout&);
+
 #if ENABLE(TREE_DEBUGGING)
 void outputLineLayoutForFlow(WTF::TextStream&, const RenderBlockFlow&, const Layout&, int depth);
 #endif
index cade8a2..ff56f4d 100644 (file)
@@ -56,6 +56,7 @@ public:
         String textWithHyphen() const;
         bool isEndOfLine() const;
         bool hasHyphen() const { return m_iterator.simpleRun().hasHyphen; }
+        const SimpleLineLayout::Run& simpleRun() const { return m_iterator.simpleRun(); }
 
         unsigned lineIndex() const;
 
index e707f76..e59dbf6 100644 (file)
 #include "config.h"
 #include "TextPainter.h"
 
+#include "DisplayListReplayer.h"
 #include "GraphicsContext.h"
 #include "InlineTextBox.h"
 #include "RenderCombineText.h"
+#include "RenderLayer.h"
 #include "ShadowData.h"
 #include <wtf/NeverDestroyed.h>
 
@@ -98,10 +100,18 @@ void TextPainter::paintTextOrEmphasisMarks(const FontCascade& font, const TextRu
     float emphasisMarkOffset, const FloatPoint& textOrigin, unsigned startOffset, unsigned endOffset)
 {
     ASSERT(startOffset < endOffset);
-    if (emphasisMark.isEmpty())
-        m_context.drawText(font, textRun, textOrigin, startOffset, endOffset);
-    else
+    if (!emphasisMark.isEmpty())
         m_context.drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + FloatSize(0, emphasisMarkOffset), startOffset, endOffset);
+    else if (startOffset || endOffset < textRun.length() || !m_glyphDisplayList)
+        m_context.drawText(font, textRun, textOrigin, startOffset, endOffset);
+    else {
+        // Replaying back a whole cached glyph run to the GraphicsContext.
+        m_context.translate(textOrigin);
+        DisplayList::Replayer replayer(m_context, *m_glyphDisplayList);
+        replayer.replay();
+        m_context.translate(-textOrigin);
+    }
+    m_glyphDisplayList = nullptr;
 }
 
 void TextPainter::paintTextWithShadows(const ShadowData* shadow, const FontCascade& font, const TextRun& textRun, const FloatRect& boxRect, const FloatPoint& textOrigin,
@@ -195,4 +205,15 @@ void TextPainter::paintRange(const TextRun& textRun, const FloatRect& boxRect, c
     paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, start, end, m_style, m_shadow);
 }
 
+void TextPainter::clearGlyphDisplayLists()
+{
+    GlyphDisplayListCache<InlineTextBox>::singleton().clear();
+    GlyphDisplayListCache<SimpleLineLayout::Run>::singleton().clear();
+}
+
+bool TextPainter::shouldUseGlyphDisplayList(const PaintInfo& paintInfo)
+{
+    return !paintInfo.context().paintingDisabled() && paintInfo.enclosingSelfPaintingLayer() && paintInfo.enclosingSelfPaintingLayer()->paintingFrequently();
+}
+
 } // namespace WebCore
index afb2697..2f1b159 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "AffineTransform.h"
 #include "FloatRect.h"
+#include "GlyphDisplayListCache.h"
 #include "TextFlags.h"
 #include "TextPaintStyle.h"
 #include <wtf/text/AtomicString.h>
@@ -58,6 +59,21 @@ public:
     void paint(const TextRun&, const FloatRect& boxRect, const FloatPoint& textOrigin);
     void paintRange(const TextRun&, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned start, unsigned end);
 
+    template<typename LayoutRun>
+    void setGlyphDisplayListIfNeeded(const LayoutRun& run, const PaintInfo& paintInfo, const FontCascade& font, GraphicsContext& context, const TextRun& textRun)
+    {
+        if (!TextPainter::shouldUseGlyphDisplayList(paintInfo))
+            TextPainter::removeGlyphDisplayList(run);
+        else
+            m_glyphDisplayList = GlyphDisplayListCache<LayoutRun>::singleton().get(run, font, context, textRun);
+    }
+
+    template<typename LayoutRun>
+    static void removeGlyphDisplayList(const LayoutRun& run) { GlyphDisplayListCache<LayoutRun>::singleton().remove(run); }
+
+    static void clearGlyphDisplayLists();
+    static bool shouldUseGlyphDisplayList(const PaintInfo&);
+
 private:
     void paintTextOrEmphasisMarks(const FontCascade&, const TextRun&, const AtomicString& emphasisMark, float emphasisMarkOffset,
         const FloatPoint& textOrigin, unsigned startOffset, unsigned endOffset);
@@ -74,6 +90,7 @@ private:
     const RenderCombineText* m_combinedText { nullptr };
     float m_emphasisMarkOffset { 0 };
     bool m_textBoxIsHorizontal { true };
+    DisplayList::DisplayList* m_glyphDisplayList { nullptr };
 };
 
 inline void TextPainter::setEmphasisMark(const AtomicString& mark, float offset, const RenderCombineText* combinedText)