Title: [274461] trunk/Source/WebCore
Revision
274461
Author
simon.fra...@apple.com
Date
2021-03-15 20:07:28 -0700 (Mon, 15 Mar 2021)

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

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

Reply via email to