Log Message
Optimize canvas repaints https://bugs.webkit.org/show_bug.cgi?id=223056 Reviewed by Said Abou-Hallawa.
Currently many draw operations in a canvas trigger a repaint via the didDraw() code path which goes to HTMLCanvasElement::didDraw(). HTMLCanvasElement did track a dirty rect and short-circuited repaints which were already dirty, but we paid the cost of computing the dirty rect (e.g. getting the bounds of a path), and of the hash lookup in Document::prepareCanvasesForDisplayIfNeeded() every time. Optimize this by moving the dirty rect tracking into CanvasRenderingContext2DBase. If the entire canvas is already dirty, we can avoid all the work (this is common, since pages often clear the entire canvas at the start of every frame). Otherwise, accumulate into m_dirtyRect. m_dirtyRect is cleared when we paint the canvas. It would be nice to share more of the didDraw() code in CanvasRenderingContext2DBase functions, but we want to be able to compute the dirty rect only if it's used, and a WTF::function()-based approach benchmarked as slower. This patch is an up-to-10% progression on some canvas subtests in MotionMark. * html/HTMLCanvasElement.cpp: (WebCore::HTMLCanvasElement::didDraw): (WebCore::HTMLCanvasElement::paint): * html/HTMLCanvasElement.h: * html/canvas/CanvasRenderingContext.h: (WebCore::CanvasRenderingContext::clearAccumulatedDirtyRect): (WebCore::CanvasRenderingContext::paintRenderingResultsToCanvas): * html/canvas/CanvasRenderingContext2DBase.cpp: (WebCore::CanvasRenderingContext2DBase::fillInternal): (WebCore::CanvasRenderingContext2DBase::strokeInternal): (WebCore::CanvasRenderingContext2DBase::fillRect): (WebCore::CanvasRenderingContext2DBase::strokeRect): (WebCore::CanvasRenderingContext2DBase::drawImage): (WebCore::CanvasRenderingContext2DBase::didDraw): (WebCore::CanvasRenderingContext2DBase::clearAccumulatedDirtyRect): (WebCore::CanvasRenderingContext2DBase::isEntireBackingStoreDirty const): (WebCore::CanvasRenderingContext2DBase::putImageData): (WebCore::CanvasRenderingContext2DBase::drawTextUnchecked): * html/canvas/CanvasRenderingContext2DBase.h: (WebCore::CanvasRenderingContext2DBase::didDraw): (WebCore::CanvasRenderingContext2DBase::backingStoreBounds const):
Modified Paths
- trunk/Source/WebCore/ChangeLog
- trunk/Source/WebCore/html/HTMLCanvasElement.cpp
- trunk/Source/WebCore/html/HTMLCanvasElement.h
- trunk/Source/WebCore/html/canvas/CanvasRenderingContext.h
- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp
- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h
Diff
Modified: trunk/Source/WebCore/ChangeLog (274460 => 274461)
--- trunk/Source/WebCore/ChangeLog 2021-03-16 03:03:22 UTC (rev 274460)
+++ trunk/Source/WebCore/ChangeLog 2021-03-16 03:07:28 UTC (rev 274461)
@@ -1,3 +1,49 @@
+2021-03-15 Simon Fraser <simon.fra...@apple.com>
+
+ Optimize canvas repaints
+ https://bugs.webkit.org/show_bug.cgi?id=223056
+
+ Reviewed by Said Abou-Hallawa.
+
+ Currently many draw operations in a canvas trigger a repaint via the didDraw()
+ code path which goes to HTMLCanvasElement::didDraw(). HTMLCanvasElement did track
+ a dirty rect and short-circuited repaints which were already dirty, but we paid
+ the cost of computing the dirty rect (e.g. getting the bounds of a path), and of
+ the hash lookup in Document::prepareCanvasesForDisplayIfNeeded() every time.
+
+ Optimize this by moving the dirty rect tracking into CanvasRenderingContext2DBase.
+ If the entire canvas is already dirty, we can avoid all the work (this is common,
+ since pages often clear the entire canvas at the start of every frame). Otherwise,
+ accumulate into m_dirtyRect. m_dirtyRect is cleared when we paint the canvas.
+
+ It would be nice to share more of the didDraw() code in CanvasRenderingContext2DBase
+ functions, but we want to be able to compute the dirty rect only if it's used, and
+ a WTF::function()-based approach benchmarked as slower.
+
+ This patch is an up-to-10% progression on some canvas subtests in MotionMark.
+
+ * html/HTMLCanvasElement.cpp:
+ (WebCore::HTMLCanvasElement::didDraw):
+ (WebCore::HTMLCanvasElement::paint):
+ * html/HTMLCanvasElement.h:
+ * html/canvas/CanvasRenderingContext.h:
+ (WebCore::CanvasRenderingContext::clearAccumulatedDirtyRect):
+ (WebCore::CanvasRenderingContext::paintRenderingResultsToCanvas):
+ * html/canvas/CanvasRenderingContext2DBase.cpp:
+ (WebCore::CanvasRenderingContext2DBase::fillInternal):
+ (WebCore::CanvasRenderingContext2DBase::strokeInternal):
+ (WebCore::CanvasRenderingContext2DBase::fillRect):
+ (WebCore::CanvasRenderingContext2DBase::strokeRect):
+ (WebCore::CanvasRenderingContext2DBase::drawImage):
+ (WebCore::CanvasRenderingContext2DBase::didDraw):
+ (WebCore::CanvasRenderingContext2DBase::clearAccumulatedDirtyRect):
+ (WebCore::CanvasRenderingContext2DBase::isEntireBackingStoreDirty const):
+ (WebCore::CanvasRenderingContext2DBase::putImageData):
+ (WebCore::CanvasRenderingContext2DBase::drawTextUnchecked):
+ * html/canvas/CanvasRenderingContext2DBase.h:
+ (WebCore::CanvasRenderingContext2DBase::didDraw):
+ (WebCore::CanvasRenderingContext2DBase::backingStoreBounds const):
+
2021-03-15 Jer Noble <jer.no...@apple.com>
[WK2] Can get stuck in fullscreen mode if node is removed prior to receiving willEnterFullscreen()
Modified: trunk/Source/WebCore/html/HTMLCanvasElement.cpp (274460 => 274461)
--- trunk/Source/WebCore/html/HTMLCanvasElement.cpp 2021-03-16 03:03:22 UTC (rev 274460)
+++ trunk/Source/WebCore/html/HTMLCanvasElement.cpp 2021-03-16 03:07:28 UTC (rev 274461)
@@ -577,10 +577,8 @@
FloatRect r = mapRect(dirtyRect, FloatRect(0, 0, size().width(), size().height()), destRect);
r.intersect(destRect);
- if (!r.isEmpty() && !m_dirtyRect.contains(r)) {
- m_dirtyRect.unite(r);
- renderer->repaintRectangle(enclosingIntRect(m_dirtyRect));
- }
+ if (!r.isEmpty())
+ renderer->repaintRectangle(enclosingIntRect(r));
}
notifyObserversCanvasChanged(dirtyRect);
}
@@ -649,8 +647,8 @@
void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r)
{
- // Clear the dirty rect
- m_dirtyRect = FloatRect();
+ if (m_context)
+ m_context->clearAccumulatedDirtyRect();
if (!context.paintingDisabled()) {
bool shouldPaint = true;
Modified: trunk/Source/WebCore/html/HTMLCanvasElement.h (274460 => 274461)
--- trunk/Source/WebCore/html/HTMLCanvasElement.h 2021-03-16 03:03:22 UTC (rev 274460)
+++ trunk/Source/WebCore/html/HTMLCanvasElement.h 2021-03-16 03:07:28 UTC (rev 274461)
@@ -182,15 +182,13 @@
Node::InsertedIntoAncestorResult insertedIntoAncestor(InsertionType, ContainerNode&) final;
void removedFromAncestor(RemovalType, ContainerNode& oldParentOfRemovedTree) final;
- FloatRect m_dirtyRect;
+ std::unique_ptr<CanvasRenderingContext> m_context;
+ mutable RefPtr<Image> m_copiedImage; // FIXME: This is temporary for platforms that have to copy the image buffer to render (and for CSSCanvasValue).
- bool m_ignoreReset { false };
-
Optional<bool> m_usesDisplayListDrawing;
bool m_tracksDisplayListReplay { false };
- std::unique_ptr<CanvasRenderingContext> m_context;
-
+ bool m_ignoreReset { false };
// m_hasCreatedImageBuffer means we tried to malloc the buffer. We didn't necessarily get it.
mutable bool m_hasCreatedImageBuffer { false };
mutable bool m_didClearImageBuffer { false };
@@ -197,10 +195,7 @@
#if ENABLE(WEBGL)
bool m_hasRelevantWebGLEventListener { false };
#endif
-
bool m_isSnapshotting { false };
-
- mutable RefPtr<Image> m_copiedImage; // FIXME: This is temporary for platforms that have to copy the image buffer to render (and for CSSCanvasValue).
};
} // namespace WebCore
Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext.h (274460 => 274461)
--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext.h 2021-03-16 03:03:22 UTC (rev 274460)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext.h 2021-03-16 03:07:28 UTC (rev 274461)
@@ -71,10 +71,12 @@
virtual bool isOffscreen2d() const { return false; }
virtual bool isPaint() const { return false; }
+ virtual void clearAccumulatedDirtyRect() { }
+
// Called before paintRenderingResultsToCanvas if paintRenderingResultsToCanvas is
// used for compositing purposes.
virtual void prepareForDisplayWithPaint() { }
- virtual void paintRenderingResultsToCanvas() {}
+ virtual void paintRenderingResultsToCanvas() { }
virtual PlatformLayer* platformLayer() const { return 0; }
bool hasActiveInspectorCanvasCallTracer() const { return m_hasActiveInspectorCanvasCallTracer; }
Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp (274460 => 274461)
--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp 2021-03-16 03:03:22 UTC (rev 274460)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp 2021-03-16 03:07:28 UTC (rev 274461)
@@ -1045,26 +1045,33 @@
if (gradient && gradient->isZeroSize())
return;
- if (!path.isEmpty()) {
- auto savedFillRule = c->fillRule();
- c->setFillRule(toWindRule(windingRule));
+ if (path.isEmpty())
+ return;
- if (isFullCanvasCompositeMode(state().globalComposite)) {
- beginCompositeLayer();
- c->fillPath(path);
- endCompositeLayer();
- didDrawEntireCanvas();
- } else if (state().globalComposite == CompositeOperator::Copy) {
- clearCanvas();
- c->fillPath(path);
- didDrawEntireCanvas();
- } else {
- c->fillPath(path);
- didDraw(path.fastBoundingRect());
- }
-
- c->setFillRule(savedFillRule);
- }
+ auto savedFillRule = c->fillRule();
+ c->setFillRule(toWindRule(windingRule));
+
+ bool repaintEntireCanvas = false;
+ if (isFullCanvasCompositeMode(state().globalComposite)) {
+ beginCompositeLayer();
+ c->fillPath(path);
+ endCompositeLayer();
+ repaintEntireCanvas = true;
+ } else if (state().globalComposite == CompositeOperator::Copy) {
+ clearCanvas();
+ c->fillPath(path);
+ repaintEntireCanvas = true;
+ } else
+ c->fillPath(path);
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
+ didDrawEntireCanvas();
+ else
+ didDraw(path.fastBoundingRect());
+
+ c->setFillRule(savedFillRule);
}
void CanvasRenderingContext2DBase::strokeInternal(const Path& path)
@@ -1080,22 +1087,30 @@
if (gradient && gradient->isZeroSize())
return;
- if (!path.isEmpty()) {
- if (isFullCanvasCompositeMode(state().globalComposite)) {
- beginCompositeLayer();
- c->strokePath(path);
- endCompositeLayer();
- didDrawEntireCanvas();
- } else if (state().globalComposite == CompositeOperator::Copy) {
- clearCanvas();
- c->strokePath(path);
- didDrawEntireCanvas();
- } else {
- FloatRect dirtyRect = path.fastBoundingRect();
- inflateStrokeRect(dirtyRect);
- c->strokePath(path);
- didDraw(dirtyRect);
- }
+ if (path.isEmpty())
+ return;
+
+ bool repaintEntireCanvas = false;
+ if (isFullCanvasCompositeMode(state().globalComposite)) {
+ beginCompositeLayer();
+ c->strokePath(path);
+ endCompositeLayer();
+ repaintEntireCanvas = true;
+ } else if (state().globalComposite == CompositeOperator::Copy) {
+ clearCanvas();
+ c->strokePath(path);
+ repaintEntireCanvas = true;
+ } else
+ c->strokePath(path);
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
+ didDrawEntireCanvas();
+ else {
+ auto dirtyRect = path.fastBoundingRect();
+ inflateStrokeRect(dirtyRect);
+ didDraw(dirtyRect);
}
}
@@ -1242,22 +1257,28 @@
FloatRect rect(x, y, width, height);
+ bool repaintEntireCanvas = false;
if (rectContainsCanvas(rect)) {
c->fillRect(rect);
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (isFullCanvasCompositeMode(state().globalComposite)) {
beginCompositeLayer();
c->fillRect(rect);
endCompositeLayer();
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (state().globalComposite == CompositeOperator::Copy) {
clearCanvas();
c->fillRect(rect);
+ repaintEntireCanvas = true;
+ } else
+ c->fillRect(rect);
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
didDrawEntireCanvas();
- } else {
- c->fillRect(rect);
+ else
didDraw(rect);
- }
}
void CanvasRenderingContext2DBase::strokeRect(float x, float y, float width, float height)
@@ -1279,19 +1300,26 @@
return;
FloatRect rect(x, y, width, height);
+ bool repaintEntireCanvas = false;
if (isFullCanvasCompositeMode(state().globalComposite)) {
beginCompositeLayer();
c->strokeRect(rect, state().lineWidth);
endCompositeLayer();
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (state().globalComposite == CompositeOperator::Copy) {
clearCanvas();
c->strokeRect(rect, state().lineWidth);
+ repaintEntireCanvas = true;
+ } else
+ c->strokeRect(rect, state().lineWidth);
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
didDrawEntireCanvas();
- } else {
- FloatRect boundingRect = rect;
+ else {
+ auto boundingRect = rect;
boundingRect.inflate(state().lineWidth / 2);
- c->strokeRect(rect, state().lineWidth);
didDraw(boundingRect);
}
}
@@ -1551,21 +1579,27 @@
ImagePaintingOptions options = { op, blendMode, orientation };
+ bool repaintEntireCanvas = false;
if (rectContainsCanvas(normalizedDstRect)) {
c->drawImage(*image, normalizedDstRect, normalizedSrcRect, options);
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (isFullCanvasCompositeMode(op)) {
fullCanvasCompositedDrawImage(*image, normalizedDstRect, normalizedSrcRect, op);
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (op == CompositeOperator::Copy) {
clearCanvas();
c->drawImage(*image, normalizedDstRect, normalizedSrcRect, options);
+ repaintEntireCanvas = true;
+ } else
+ c->drawImage(*image, normalizedDstRect, normalizedSrcRect, options);
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
didDrawEntireCanvas();
- } else {
- c->drawImage(*image, normalizedDstRect, normalizedSrcRect, options);
+ else
didDraw(normalizedDstRect);
- }
-
+
if (image->isSVGImage())
image->setImageObserver(observer);
@@ -1600,12 +1634,13 @@
sourceCanvas.makeRenderingResultsAvailable();
+ bool repaintEntireCanvas = false;
if (rectContainsCanvas(dstRect)) {
c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (isFullCanvasCompositeMode(state().globalComposite)) {
fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (state().globalComposite == CompositeOperator::Copy) {
if (&sourceCanvas == &canvasBase()) {
if (auto copy = buffer->copyRectToBuffer(srcRect, DestinationColorSpace::SRGB, *c)) {
@@ -1616,11 +1651,16 @@
clearCanvas();
c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
}
+ repaintEntireCanvas = true;
+ } else
+ c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
didDrawEntireCanvas();
- } else {
- c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
+ else
didDraw(dstRect);
- }
return { };
}
@@ -1650,7 +1690,10 @@
#if USE(CG)
if (auto image = video.nativeImageForCurrentTime()) {
c->drawNativeImage(*image, FloatSize(video.videoWidth(), video.videoHeight()), dstRect, srcRect);
- if (rectContainsCanvas(dstRect))
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (rectContainsCanvas(dstRect))
didDrawEntireCanvas();
else
didDraw(dstRect);
@@ -1666,8 +1709,12 @@
c->translate(-srcRect.location());
video.paintCurrentFrameInContext(*c, FloatRect(FloatPoint(), size(video)));
stateSaver.restore();
- didDraw(dstRect);
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else
+ didDraw(dstRect);
+
return { };
}
@@ -1700,20 +1747,26 @@
checkOrigin(&imageBitmap);
+ bool repaintEntireCanvas = false;
if (rectContainsCanvas(dstRect)) {
c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (isFullCanvasCompositeMode(state().globalComposite)) {
fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (state().globalComposite == CompositeOperator::Copy) {
clearCanvas();
c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
+ repaintEntireCanvas = true;
+ } else
+ c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
didDrawEntireCanvas();
- } else {
- c->drawImageBuffer(*buffer, dstRect, srcRect, { state().globalComposite, state().globalBlend });
+ else
didDraw(dstRect);
- }
return { };
}
@@ -2003,21 +2056,29 @@
didDraw(backingStoreBounds(), DidDrawOption::ApplyClip);
}
-void CanvasRenderingContext2DBase::didDraw(const FloatRect& r, OptionSet<DidDrawOption> options)
+void CanvasRenderingContext2DBase::didDraw(Optional<FloatRect> rect, OptionSet<DidDrawOption> options)
{
if (!drawingContext())
return;
+
+ if (!rect) {
+ canvasBase().didDraw(WTF::nullopt);
+ return;
+ }
+
+ auto dirtyRect = rect.value();
+ if (dirtyRect.isEmpty())
+ return;
+
if (!state().hasInvertibleTransform)
return;
- FloatRect dirtyRect = r;
-
if (options.contains(DidDrawOption::ApplyTransform))
dirtyRect = state().transform.mapRect(dirtyRect);
if (options.contains(DidDrawOption::ApplyShadow) && state().shadowColor.isVisible()) {
// The shadow gets applied after transformation
- FloatRect shadowRect(dirtyRect);
+ auto shadowRect = dirtyRect;
shadowRect.move(state().shadowOffset);
shadowRect.inflate(state().shadowBlur);
dirtyRect.unite(shadowRect);
@@ -2025,9 +2086,24 @@
// FIXME: This does not apply the clip because we have no way of reading the clip out of the GraphicsContext.
- canvasBase().didDraw(dirtyRect);
+ if (m_dirtyRect.contains(dirtyRect))
+ canvasBase().didDraw(WTF::nullopt);
+ else {
+ m_dirtyRect.unite(dirtyRect);
+ canvasBase().didDraw(m_dirtyRect);
+ }
}
+void CanvasRenderingContext2DBase::clearAccumulatedDirtyRect()
+{
+ m_dirtyRect = { };
+}
+
+bool CanvasRenderingContext2DBase::isEntireBackingStoreDirty() const
+{
+ return m_dirtyRect == backingStoreBounds();
+}
+
const Vector<CanvasRenderingContext2DBase::State, 1>& CanvasRenderingContext2DBase::stateStack()
{
realizeSaves();
@@ -2189,7 +2265,7 @@
if (!sourceRect.isEmpty())
buffer->putImageData(AlphaPremultiplication::Unpremultiplied, data, sourceRect, IntPoint(destOffset));
- didDraw(destRect, { }); // ignore transform, shadow and clip
+ didDraw(FloatRect { destRect }, { }); // ignore transform, shadow and clip
}
void CanvasRenderingContext2DBase::inflateStrokeRect(FloatRect& rect) const
@@ -2452,19 +2528,25 @@
location = FloatPoint();
}
+ bool repaintEntireCanvas = false;
if (isFullCanvasCompositeMode(state().globalComposite)) {
beginCompositeLayer();
fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
endCompositeLayer();
- didDrawEntireCanvas();
+ repaintEntireCanvas = true;
} else if (state().globalComposite == CompositeOperator::Copy) {
clearCanvas();
fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
+ repaintEntireCanvas = true;
+ } else
+ fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
+
+ if (isEntireBackingStoreDirty())
+ didDraw(WTF::nullopt);
+ else if (repaintEntireCanvas)
didDrawEntireCanvas();
- } else {
- fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
+ else
didDraw(textRect);
- }
}
Ref<TextMetrics> CanvasRenderingContext2DBase::measureTextInternal(const String& text)
Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h (274460 => 274461)
--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h 2021-03-16 03:03:22 UTC (rev 274460)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h 2021-03-16 03:07:28 UTC (rev 274461)
@@ -316,15 +316,17 @@
ApplyShadow = 1 << 1,
ApplyClip = 1 << 2,
};
- void didDraw(const FloatRect&, OptionSet<DidDrawOption> = { DidDrawOption::ApplyTransform, DidDrawOption::ApplyShadow, DidDrawOption::ApplyClip });
+ void didDraw(Optional<FloatRect>, OptionSet<DidDrawOption> = { DidDrawOption::ApplyTransform, DidDrawOption::ApplyShadow, DidDrawOption::ApplyClip });
void didDrawEntireCanvas();
-
- FloatRect backingStoreBounds() const { return FloatRect { { }, FloatSize { canvasBase().size() } }; }
void paintRenderingResultsToCanvas() override;
bool needsPreparationForDisplay() const final;
void prepareForDisplay() final;
+ void clearAccumulatedDirtyRect() final;
+ bool isEntireBackingStoreDirty() const;
+ FloatRect backingStoreBounds() const { return FloatRect { { }, FloatSize { canvasBase().size() } }; }
+
void unwindStateStack();
void realizeSavesLoop();
@@ -388,6 +390,7 @@
static constexpr unsigned MaxSaveCount = 1024 * 16;
Vector<State, 1> m_stateStack;
+ FloatRect m_dirtyRect;
unsigned m_unrealizedSaveCount { 0 };
bool m_usesCSSCompatibilityParseMode;
bool m_usesDisplayListDrawing { false };
_______________________________________________ webkit-changes mailing list webkit-changes@lists.webkit.org https://lists.webkit.org/mailman/listinfo/webkit-changes