[sksg] More inval fiddling

Node subclasses can now control whether their bounds (changes)
contribute to damage.

Tristate:

  * Default:   The node bounds contribute to damage if the node itself was
               invalidated, observing hasSelfInval().  This is the default
               behavior.

  * ForceSelf: The node bounds contribute to damage, regardless of
               hasSelfInval().  Used for domain-boundary nodes (e.g. Draw),
               which gate blocked fragments (e.g. geometry, paint nodes).

  * BlockSelf: The node bounds do not contribute to damage, regardless of
               hasSelfInval().  Used for nodes which do not contribute
               damage directly (e.g. paints, geometry).

TBR=
Change-Id: I7c941c7ea12e14b008d846ec13108e66e34dbc73
Reviewed-on: https://skia-review.googlesource.com/91104
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/experimental/sksg/SkSGDraw.cpp b/experimental/sksg/SkSGDraw.cpp
index c8e6218..56415fc 100644
--- a/experimental/sksg/SkSGDraw.cpp
+++ b/experimental/sksg/SkSGDraw.cpp
@@ -29,14 +29,16 @@
     fGeometry->draw(canvas, fPaint->makePaint());
 }
 
-SkRect Draw::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+Node::RevalidationResult Draw::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
     SkASSERT(this->hasInval());
 
-    // TODO: paint bounds extents
+    // TODO: adjust bounds for paint
     const auto bounds = fGeometry->revalidate(ic, ctm);
     fPaint->revalidate(ic, ctm);
 
-    return bounds;
+    // Neither paint nor geometry contribute to damage directly; instead we generate
+    // damage here, at the binding point.
+    return { bounds, Damage::kForceSelf };
 }
 
 } // namespace sksg
diff --git a/experimental/sksg/SkSGDraw.h b/experimental/sksg/SkSGDraw.h
index 20ead3d..ad2b8fd 100644
--- a/experimental/sksg/SkSGDraw.h
+++ b/experimental/sksg/SkSGDraw.h
@@ -34,7 +34,7 @@
 
     void onRender(SkCanvas*) const override;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
 
 private:
     sk_sp<GeometryNode> fGeometry;
diff --git a/experimental/sksg/SkSGEffectNode.cpp b/experimental/sksg/SkSGEffectNode.cpp
index 1ecf7c7..4a1c8ad 100644
--- a/experimental/sksg/SkSGEffectNode.cpp
+++ b/experimental/sksg/SkSGEffectNode.cpp
@@ -22,10 +22,10 @@
     fChild->render(canvas);
 }
 
-SkRect EffectNode::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+Node::RevalidationResult EffectNode::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
     SkASSERT(this->hasInval());
 
-    return fChild->revalidate(ic, ctm);
+    return { fChild->revalidate(ic, ctm), Damage::kDefault };
 }
 
 } // namespace sksg
diff --git a/experimental/sksg/SkSGEffectNode.h b/experimental/sksg/SkSGEffectNode.h
index ab0968e..0fd71bc 100644
--- a/experimental/sksg/SkSGEffectNode.h
+++ b/experimental/sksg/SkSGEffectNode.h
@@ -25,7 +25,7 @@
 
     void onRender(SkCanvas*) const override;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
 
 private:
     sk_sp<RenderNode> fChild;
diff --git a/experimental/sksg/SkSGGroup.cpp b/experimental/sksg/SkSGGroup.cpp
index fa9ae1e..9f40d6a 100644
--- a/experimental/sksg/SkSGGroup.cpp
+++ b/experimental/sksg/SkSGGroup.cpp
@@ -51,16 +51,16 @@
     }
 }
 
-SkRect Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+Node::RevalidationResult Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
     SkASSERT(this->hasInval());
 
-    SkRect bounds = SkRect::MakeEmpty();
+    RevalidationResult result =  { SkRect::MakeEmpty(), Damage::kDefault };
 
     for (const auto& child : fChildren) {
-        bounds.join(child->revalidate(ic, ctm));
+        result.fBounds.join(child->revalidate(ic, ctm));
     }
 
-    return bounds;
+    return result;
 }
 
 } // namespace sksg
diff --git a/experimental/sksg/SkSGGroup.h b/experimental/sksg/SkSGGroup.h
index f9126ea..3d8b1ad 100644
--- a/experimental/sksg/SkSGGroup.h
+++ b/experimental/sksg/SkSGGroup.h
@@ -31,7 +31,7 @@
     ~Group() override;
 
     void onRender(SkCanvas*) const override;
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
 
 private:
     SkTArray<sk_sp<RenderNode>, true> fChildren;
diff --git a/experimental/sksg/SkSGNode.cpp b/experimental/sksg/SkSGNode.cpp
index d3e8040..13e9864 100644
--- a/experimental/sksg/SkSGNode.cpp
+++ b/experimental/sksg/SkSGNode.cpp
@@ -127,20 +127,20 @@
         return fBounds;
     }
 
-    SkRect prevBounds;
-    if (this->hasSelfInval()) {
-        prevBounds = fBounds;
-    }
+    const auto result     = this->onRevalidate(ic, ctm);
+    const auto selfDamage = result.fReval == Damage::kForceSelf ||
+                            (this->hasSelfInval() && result.fReval != Damage::kBlockSelf);
 
-    fBounds = this->onRevalidate(ic, ctm);
-
-    if (this->hasSelfInval()) {
-        ic->inval(prevBounds, ctm);
-        if (fBounds != prevBounds) {
-            ic->inval(fBounds, ctm);
+    if (selfDamage) {
+        // old bounds
+        ic->inval(fBounds, ctm);
+        if (result.fBounds != fBounds) {
+            // new bounds
+            ic->inval(result.fBounds, ctm);
         }
     }
 
+    fBounds = result.fBounds;
     fFlags &= ~(kInvalSelf_Flag | kInvalDescendant_Flag);
 
     return fBounds;
diff --git a/experimental/sksg/SkSGNode.h b/experimental/sksg/SkSGNode.h
index 7758eca..58456cf 100644
--- a/experimental/sksg/SkSGNode.h
+++ b/experimental/sksg/SkSGNode.h
@@ -47,7 +47,16 @@
 
     // Dispatched on revalidation.  Subclasses are expected to recompute/cache their properties
     // and return their bounding box in local coordinates.
-    virtual SkRect onRevalidate(InvalidationController*, const SkMatrix& ctm) = 0;
+    enum class Damage {
+        kDefault,    // respects the local kInvalSelf_Flag
+        kForceSelf,  // forces self revalidation regardless of kInvalSelf_Flag
+        kBlockSelf,  // blocks self revalidation regardless of kInvalSelf_Flag
+    };
+    struct RevalidationResult {
+        SkRect  fBounds;
+        Damage  fReval;
+    };
+    virtual RevalidationResult onRevalidate(InvalidationController*, const SkMatrix& ctm) = 0;
 
 private:
     void addInvalReceiver(Node*);
diff --git a/experimental/sksg/SkSGPaintNode.cpp b/experimental/sksg/SkSGPaintNode.cpp
index fbdfae5..be8edac 100644
--- a/experimental/sksg/SkSGPaintNode.cpp
+++ b/experimental/sksg/SkSGPaintNode.cpp
@@ -17,7 +17,7 @@
     return fPaint;
 }
 
-SkRect PaintNode::onRevalidate(InvalidationController*, const SkMatrix&) {
+Node::RevalidationResult PaintNode::onRevalidate(InvalidationController*, const SkMatrix&) {
     SkASSERT(this->hasInval());
 
     if (this->hasSelfInval()) {
@@ -32,7 +32,8 @@
         this->onApplyToPaint(&fPaint);
     }
 
-    return SkRect::MakeEmpty();
+    // Paints have no bounds and don't contribute to damage.
+    return { SkRect::MakeEmpty(), Damage::kBlockSelf };
 }
 
 } // namespace sksg
diff --git a/experimental/sksg/SkSGPaintNode.h b/experimental/sksg/SkSGPaintNode.h
index a2fbada..0dac92e 100644
--- a/experimental/sksg/SkSGPaintNode.h
+++ b/experimental/sksg/SkSGPaintNode.h
@@ -36,7 +36,7 @@
 
     virtual void onApplyToPaint(SkPaint*) const = 0;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) final;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) final;
 
 private:
     SkPaint        fPaint;
diff --git a/experimental/sksg/effects/SkSGTransform.cpp b/experimental/sksg/effects/SkSGTransform.cpp
index ca4c5cd..dc31623 100644
--- a/experimental/sksg/effects/SkSGTransform.cpp
+++ b/experimental/sksg/effects/SkSGTransform.cpp
@@ -21,13 +21,13 @@
     this->INHERITED::onRender(canvas);
 }
 
-SkRect Transform::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+Node::RevalidationResult Transform::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
     SkASSERT(this->hasInval());
 
-    auto bounds = this->INHERITED::onRevalidate(ic, SkMatrix::Concat(ctm, fMatrix));
-    fMatrix.mapRect(&bounds);
+    auto result = this->INHERITED::onRevalidate(ic, SkMatrix::Concat(ctm, fMatrix));
+    fMatrix.mapRect(&result.fBounds);
 
-    return bounds;
+    return result;
 }
 
 } // namespace sksg
diff --git a/experimental/sksg/effects/SkSGTransform.h b/experimental/sksg/effects/SkSGTransform.h
index a32f83d..8a97a67 100644
--- a/experimental/sksg/effects/SkSGTransform.h
+++ b/experimental/sksg/effects/SkSGTransform.h
@@ -30,7 +30,7 @@
 
     void onRender(SkCanvas*) const override;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
 
 private:
     SkMatrix fMatrix;
diff --git a/experimental/sksg/geometry/SkSGMerge.cpp b/experimental/sksg/geometry/SkSGMerge.cpp
index a9f06d4..47429a5 100644
--- a/experimental/sksg/geometry/SkSGMerge.cpp
+++ b/experimental/sksg/geometry/SkSGMerge.cpp
@@ -54,7 +54,7 @@
     return kUnion_SkPathOp;
 }
 
-SkRect Merge::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+Node::RevalidationResult Merge::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
     SkASSERT(this->hasInval());
 
     const auto op = mode_to_op(fMode);
@@ -75,7 +75,8 @@
         builder.resolve(&fMerged);
     }
 
-    return fMerged.computeTightBounds();
+    // Geometry does not contribute damage directly.
+    return { fMerged.computeTightBounds(), Damage::kBlockSelf };
 }
 
 } // namespace skotty
diff --git a/experimental/sksg/geometry/SkSGMerge.h b/experimental/sksg/geometry/SkSGMerge.h
index b0cb40d..f3e2087 100644
--- a/experimental/sksg/geometry/SkSGMerge.h
+++ b/experimental/sksg/geometry/SkSGMerge.h
@@ -45,7 +45,7 @@
 protected:
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
     SkPath onAsPath() const override;
 
 private:
diff --git a/experimental/sksg/geometry/SkSGPath.cpp b/experimental/sksg/geometry/SkSGPath.cpp
index a04dcf6..ce1ff39 100644
--- a/experimental/sksg/geometry/SkSGPath.cpp
+++ b/experimental/sksg/geometry/SkSGPath.cpp
@@ -18,10 +18,11 @@
     canvas->drawPath(fPath, paint);
 }
 
-SkRect Path::onRevalidate(InvalidationController*, const SkMatrix&) {
+Node::RevalidationResult Path::onRevalidate(InvalidationController*, const SkMatrix&) {
     SkASSERT(this->hasSelfInval());
 
-    return fPath.computeTightBounds();
+    // Geometry does not contribute damage directly.
+    return { fPath.computeTightBounds(), Damage::kBlockSelf };
 }
 
 SkPath Path::onAsPath() const {
diff --git a/experimental/sksg/geometry/SkSGPath.h b/experimental/sksg/geometry/SkSGPath.h
index 18caa10..32388c9 100644
--- a/experimental/sksg/geometry/SkSGPath.h
+++ b/experimental/sksg/geometry/SkSGPath.h
@@ -30,7 +30,7 @@
 protected:
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
     SkPath onAsPath() const override;
 
 private:
diff --git a/experimental/sksg/geometry/SkSGRect.cpp b/experimental/sksg/geometry/SkSGRect.cpp
index c2ac384..0184dac 100644
--- a/experimental/sksg/geometry/SkSGRect.cpp
+++ b/experimental/sksg/geometry/SkSGRect.cpp
@@ -19,10 +19,11 @@
     canvas->drawRect(fRect, paint);
 }
 
-SkRect Rect::onRevalidate(InvalidationController*, const SkMatrix&) {
+Node::RevalidationResult Rect::onRevalidate(InvalidationController*, const SkMatrix&) {
     SkASSERT(this->hasSelfInval());
 
-    return fRect;
+    // Geometry does not contribute damage directly.
+    return { fRect, Damage::kBlockSelf };
 }
 
 SkPath Rect::onAsPath() const {
@@ -37,10 +38,11 @@
     canvas->drawRRect(fRRect, paint);
 }
 
-SkRect RRect::onRevalidate(InvalidationController*, const SkMatrix&) {
+Node::RevalidationResult RRect::onRevalidate(InvalidationController*, const SkMatrix&) {
     SkASSERT(this->hasSelfInval());
 
-    return fRRect.getBounds();
+    // Geometry does not contribute damage directly.
+    return { fRRect.getBounds(), Damage::kBlockSelf };
 }
 
 SkPath RRect::onAsPath() const {
diff --git a/experimental/sksg/geometry/SkSGRect.h b/experimental/sksg/geometry/SkSGRect.h
index ad27910..4667b6b 100644
--- a/experimental/sksg/geometry/SkSGRect.h
+++ b/experimental/sksg/geometry/SkSGRect.h
@@ -34,7 +34,7 @@
 protected:
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
     SkPath onAsPath() const override;
 
 private:
@@ -56,7 +56,7 @@
 protected:
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
-    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+    RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override;
     SkPath onAsPath() const override;
 
 private:
diff --git a/tests/SGTest.cpp b/tests/SGTest.cpp
index c4b95a7..ca6c23d 100644
--- a/tests/SGTest.cpp
+++ b/tests/SGTest.cpp
@@ -81,13 +81,12 @@
 
     {
         // Update the common color.
-        // TODO: this doesn't work ATM as expected; fix and enable.
-//        color->setColor(0xffff0000);
-//        std::vector<SkRect> damage = { {0, 0, 100, 100}, { 200, 100, 300, 200} };
-//        check_inval(reporter, tr,
-//                    SkRect::MakeWH(300, 200),
-//                    SkRect::MakeWH(300, 200),
-//                    &damage);
+        color->setColor(0xffff0000);
+        std::vector<SkRect> damage = { {0, 0, 100, 100}, { 200, 100, 300, 200} };
+        check_inval(reporter, tr,
+                    SkRect::MakeWH(300, 200),
+                    SkRect::MakeWH(300, 200),
+                    &damage);
     }
 
     {