Skip to content

Commit

Permalink
Add cache for SVGPathElement "d" attribute parsing
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=263195
rdar://110970184

Reviewed by Ryosuke Niwa.

Add cache for SVGPathElement "d" attribute parsing to avoid spending CPU time
re-parsing the same "d" attribute values.

* Source/WebCore/page/MemoryRelease.cpp:
(WebCore::releaseNoncriticalMemory):
* Source/WebCore/svg/SVGPathByteStream.h:
(WebCore::SVGPathByteStream::setData):
* Source/WebCore/svg/SVGPathElement.cpp:
(WebCore::pathSegListCache):
(WebCore::SVGPathElement::attributeChanged):
(WebCore::SVGPathElement::clearCache):
* Source/WebCore/svg/SVGPathElement.h:
* Source/WebCore/svg/SVGPathSegList.h:

Canonical link: https://commits.webkit.org/269372@main
  • Loading branch information
cdumez committed Oct 16, 2023
1 parent 722fe4f commit 75b8829
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Source/WebCore/page/MemoryRelease.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "Page.h"
#include "PerformanceLogging.h"
#include "RenderTheme.h"
#include "SVGPathElement.h"
#include "ScrollingThread.h"
#include "SelectorQuery.h"
#include "StyleScope.h"
Expand Down Expand Up @@ -89,6 +90,7 @@ static void releaseNoncriticalMemory(MaintainMemoryCache maintainMemoryCache)
InlineStyleSheetOwner::clearCache();
HTMLNameCache::clear();
ImmutableStyleProperties::clearDeduplicationMap();
SVGPathElement::clearCache();
}

static void releaseCriticalMemory(Synchronous synchronous, MaintainBackForwardCache maintainBackForwardCache, MaintainMemoryCache maintainMemoryCache)
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/svg/SVGPathByteStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class SVGPathByteStream {
void shrinkToFit() { m_data.shrinkToFit(); }

const Data& data() const { return m_data; }
void setData(const Data& data) { m_data = data; }

private:
Data m_data;
Expand Down
35 changes: 33 additions & 2 deletions Source/WebCore/svg/SVGPathElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ namespace WebCore {

WTF_MAKE_ISO_ALLOCATED_IMPL(SVGPathElement);

static constexpr uint64_t maxPathSegListCacheSize = 150 * 1024; // 150 Kb.
static uint64_t pathSegListCacheSize = 0;

static HashMap<AtomString, SVGPathByteStream::Data>& pathSegListCache()
{
static MainThreadNeverDestroyed<HashMap<AtomString, SVGPathByteStream::Data>> cache;
return cache;
}

inline SVGPathElement::SVGPathElement(const QualifiedName& tagName, Document& document)
: SVGGeometryElement(tagName, document, makeUniqueRef<PropertyRegistry>(*this))
{
Expand All @@ -56,13 +65,35 @@ Ref<SVGPathElement> SVGPathElement::create(const QualifiedName& tagName, Documen
void SVGPathElement::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason attributeModificationReason)
{
if (name == SVGNames::dAttr) {
if (!m_pathSegList->baseVal()->parse(newValue))
document().accessSVGExtensions().reportError("Problem parsing d=\"" + newValue + "\"");
auto& cache = pathSegListCache();
if (auto it = cache.find(newValue); it != cache.end())
m_pathSegList->baseVal()->updateByteStreamData(it->value);
else {
if (m_pathSegList->baseVal()->parse(newValue)) {
size_t newDataSize = m_pathSegList->baseVal()->existingPathByteStream().data().size();
if (LIKELY(newDataSize <= (maxPathSegListCacheSize / 2))) {
pathSegListCacheSize += newDataSize;
while (pathSegListCacheSize > maxPathSegListCacheSize) {
auto iteratorToRemove = cache.random();
ASSERT(pathSegListCacheSize >= iteratorToRemove->value.size());
pathSegListCacheSize -= iteratorToRemove->value.size();
cache.remove(iteratorToRemove);
}
cache.add(newValue, m_pathSegList->baseVal()->existingPathByteStream().data());
}
} else
document().accessSVGExtensions().reportError("Problem parsing d=\"" + newValue + "\"");
}
}

SVGGeometryElement::attributeChanged(name, oldValue, newValue, attributeModificationReason);
}

void SVGPathElement::clearCache()
{
pathSegListCache().clear();
}

void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
{
if (PropertyRegistry::isKnownAttribute(attrName)) {
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/svg/SVGPathElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ class SVGPathElement final : public SVGGeometryElement {
Path path() const { return m_pathSegList->currentPath(); }
size_t approximateMemoryCost() const final { return m_pathSegList->approximateMemoryCost(); }

static void clearCache();

private:
SVGPathElement(const QualifiedName&, Document&);

Expand Down
8 changes: 8 additions & 0 deletions Source/WebCore/svg/SVGPathSegList.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ class SVGPathSegList final : public SVGPropertyList<SVGPathSeg> {
return { };
}

void updateByteStreamData(const SVGPathByteStream::Data& byteStreamData)
{
pathByteStreamWillChange();
m_pathByteStream.setData(byteStreamData);
}

const SVGPathByteStream& existingPathByteStream() const { return m_pathByteStream; }

const SVGPathByteStream& pathByteStream() const { return const_cast<SVGPathSegList*>(this)->pathByteStream(); }
SVGPathByteStream& pathByteStream()
{
Expand Down

0 comments on commit 75b8829

Please sign in to comment.