Add overdraw debugging feature to Skia pipelines

Test: Compared to OpenGL pipeline and sanity checked
with understanding of the drawing pipeline.  Also
wrote a unit test.

BUG:32370375

Change-Id: Iab397d21f0def725fa89551d48c764c67fd2bda8
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index a03ded6..a5443d9 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -188,8 +188,9 @@
     ATRACE_CALL();
     LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
 
-    // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer.
-    bool functorsNeedLayer = Properties::debugOverdraw;
+    // The OpenGL renderer reserves the stencil buffer for overdraw debugging.  Functors
+    // will need to be drawn in a layer.
+    bool functorsNeedLayer = Properties::debugOverdraw && !Properties::isSkiaEnabled();
 
     prepareTreeImpl(info, functorsNeedLayer);
 }
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 7dcbbd0..adb7e26 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -165,13 +165,22 @@
             }
             renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
 
-            if (CC_UNLIKELY(Properties::debugLayersUpdates
-                    && !renderNode->getSkiaLayer()->hasRenderedSinceRepaint)) {
+            if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
-                SkPaint layerPaint;
-                layerPaint.setColor(0x7f00ff00);
-                canvas->drawRect(bounds, layerPaint);
+                if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
+                    SkPaint layerPaint;
+                    layerPaint.setColor(0x7f00ff00);
+                    canvas->drawRect(bounds, layerPaint);
+                } else if (CC_UNLIKELY(Properties::debugOverdraw)) {
+                    // Render transparent rect to increment overdraw for repaint area.
+                    // This can be "else if" because flashing green on layer updates
+                    // will also increment the overdraw if it happens to be turned on.
+                    SkPaint transparentPaint;
+                    transparentPaint.setColor(SK_ColorTRANSPARENT);
+                    canvas->drawRect(bounds, transparentPaint);
+                }
             }
+
         // composing a software layer with alpha
         } else if (properties.effectiveLayerType() == LayerType::Software) {
             SkPaint paint;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index c5a40d4..0f2d09d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -18,6 +18,8 @@
 
 #include "utils/TraceUtils.h"
 #include <SkOSFile.h>
+#include <SkOverdrawCanvas.h>
+#include <SkOverdrawColorFilter.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
 #include <SkPixelSerializer.h>
@@ -192,6 +194,34 @@
         }
     }
 
+    renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
+
+    if (skpCaptureEnabled() && recordingPicture) {
+        sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
+        if (picture->approximateOpCount() > 0) {
+            SkFILEWStream stream(prop);
+            if (stream.isValid()) {
+                PngPixelSerializer serializer;
+                picture->serialize(&stream, &serializer);
+                stream.flush();
+                SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
+            }
+        }
+        surface->getCanvas()->drawPicture(picture);
+    }
+
+    if (CC_UNLIKELY(Properties::debugOverdraw)) {
+        renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
+    }
+
+    ATRACE_NAME("flush commands");
+    canvas->flush();
+}
+
+void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
+        const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
+        SkCanvas* canvas) {
+
     canvas->clipRect(clip, SkRegion::kReplace_Op);
 
     if (!opaque) {
@@ -250,23 +280,6 @@
         canvas->restoreToCount(count);
         layer++;
     }
-
-    if (skpCaptureEnabled() && recordingPicture) {
-        sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
-        if (picture->approximateOpCount() > 0) {
-            SkFILEWStream stream(prop);
-            if (stream.isValid()) {
-                PngPixelSerializer serializer;
-                picture->serialize(&stream, &serializer);
-                stream.flush();
-                SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
-            }
-        }
-        surface->getCanvas()->drawPicture(picture);
-    }
-
-    ATRACE_NAME("flush commands");
-    canvas->flush();
 }
 
 void SkiaPipeline::dumpResourceCacheUsage() const {
@@ -283,6 +296,40 @@
     ALOGD("%s", log.c_str());
 }
 
+// Overdraw debugging
+
+// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
+// This implementation:
+// (1) Requires transparent entries for "no overdraw" and "single draws".
+// (2) Requires premul colors (instead of unpremul).
+// (3) Requires RGBA colors (instead of BGRA).
+static const uint32_t kOverdrawColors[2][6] = {
+        { 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, },
+        { 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, },
+};
+
+void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
+        const std::vector<sp<RenderNode>>& nodes, const Rect &contentDrawBounds,
+        sk_sp<SkSurface> surface) {
+    // Set up the overdraw canvas.
+    SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
+    sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
+    SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
+
+    // Fake a redraw to replay the draw commands.  This will increment the alpha channel
+    // each time a pixel would have been drawn.
+    // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
+    // initialized.
+    renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas);
+    sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
+
+    // Draw overdraw colors to the canvas.  The color filter will convert counts to colors.
+    SkPaint paint;
+    const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
+    paint.setColorFilter(SkOverdrawColorFilter::Make(colors));
+    surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
+}
+
 } /* namespace skiapipeline */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index c1c8cbe..c58fedf 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -107,6 +107,18 @@
     renderthread::RenderThread& mRenderThread;
 
 private:
+    void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
+            const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
+            SkCanvas* canvas);
+
+    /**
+     *  Debugging feature.  Draws a semi-transparent overlay on each pixel, indicating
+     *  how many times it has been drawn.
+     */
+    void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
+            const std::vector< sp<RenderNode> >& nodes, const Rect &contentDrawBounds,
+            sk_sp<SkSurface>);
+
     TaskManager mTaskManager;
     std::vector<sk_sp<SkImage>> mPinnedImages;
     static float mLightRadius;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 7a2fa57..17801af 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -179,3 +179,53 @@
     redNode->setLayerSurface(sk_sp<SkSurface>());
     blueNode->setLayerSurface(sk_sp<SkSurface>());
 }
+
+RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
+    ScopedProperty<bool> prop(Properties::debugOverdraw, true);
+
+    auto whiteNode = TestUtils::createSkiaNode(0, 0, 1, 1,
+        [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+            canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+        });
+    LayerUpdateQueue layerUpdateQueue;
+    SkRect dirty = SkRect::MakeXYWH(0, 0, 1, 1);
+    std::vector<sp<RenderNode>> renderNodes;
+    renderNodes.push_back(whiteNode);
+    bool opaque = true;
+    android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
+    auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+
+    // Initialize the canvas to blue.
+    surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    // Single draw, should be white.
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
+
+    // 1 Overdraw, should be blue blended onto white.
+    renderNodes.push_back(whiteNode);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0d0ff);
+
+    // 2 Overdraw, should be green blended onto white
+    renderNodes.push_back(whiteNode);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0ffd0);
+
+    // 3 Overdraw, should be pink blended onto white.
+    renderNodes.push_back(whiteNode);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffffc0c0);
+
+    // 4 Overdraw, should be red blended onto white.
+    renderNodes.push_back(whiteNode);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080);
+
+    // 5 Overdraw, should be red blended onto white.
+    renderNodes.push_back(whiteNode);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080);
+}