Skip to content
Permalink
Browse files
Cache glyphs (using display lists) when painting at high frequency
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.

Canonical link: https://commits.webkit.org/200062@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230544 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Said Abou-Hallawa authored and webkit-commit-queue committed Apr 11, 2018
1 parent a48580b commit cdebd883b71af8ae0202e3cd94772d242c33268e
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 28 deletions.
@@ -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
@@ -8254,6 +8254,7 @@
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>"; };
@@ -25478,6 +25479,7 @@
9A528E8117D7F52F00AA9518 /* FloatingObjects.cpp */,
9A528E8217D7F52F00AA9518 /* FloatingObjects.h */,
935C477409AC4D8D00A6AAB4 /* GapRects.h */,
5597FCCB2076C06800D35BB0 /* GlyphDisplayListCache.h */,
E112F4701E3A85F200D6CDFD /* Grid.cpp */,
E112F46F1E3A85D800D6CDFD /* Grid.h */,
E12DE7151E4B748700F9ACCF /* GridTrackSizingAlgorithm.cpp */,
@@ -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();
@@ -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);
@@ -48,6 +48,10 @@ class RenderText;
class TextLayout;
class TextRun;

namespace DisplayList {
class DisplayList;
}

struct GlyphData;

struct GlyphOverflow {
@@ -197,6 +201,8 @@ class FontCascade {

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 };

@@ -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;
};

}

0 comments on commit cdebd88

Please sign in to comment.