Fix hw layer overdraw/update visualization

Fixes: 28748255

Change-Id: I83b531cdf5e4407fd17edd72d96e6189924926fa
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 8857c41..faf6a52 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -259,6 +259,7 @@
     tests/unit/SkiaBehaviorTests.cpp \
     tests/unit/SnapshotTests.cpp \
     tests/unit/StringUtilsTests.cpp \
+    tests/unit/TestUtilsTests.cpp \
     tests/unit/TextDropShadowCacheTests.cpp \
     tests/unit/VectorDrawableTests.cpp
 
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 2ec7b75..59c1065 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -791,6 +791,16 @@
     renderer.renderGlop(state, glop);
 }
 
+void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state,
+        int color, SkXfermode::Mode mode, SkColorFilter* colorFilter) {
+    SkPaint paint;
+    paint.setColor(color);
+    paint.setXfermodeMode(mode);
+    paint.setColorFilter(colorFilter);
+    RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
+    BakedOpDispatcher::onRectOp(renderer, rectOp, state);
+}
+
 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
     // Note that we don't use op->paint in this function - it's never set on a LayerOp
     OffscreenBuffer* buffer = *op.layerHandle;
@@ -798,12 +808,9 @@
     if (CC_UNLIKELY(!buffer)) {
         // Layer was not allocated, which can occur if there were no draw ops inside. We draw the
         // equivalent by drawing a rect with the same layer properties (alpha/xfer/filter).
-        SkPaint paint;
-        paint.setAlpha(op.alpha * 255);
-        paint.setXfermodeMode(op.mode);
-        paint.setColorFilter(op.colorFilter);
-        RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
-        BakedOpDispatcher::onRectOp(renderer, rectOp, state);
+        int color = SkColorSetA(SK_ColorTRANSPARENT, op.alpha * 255);
+        renderRectForLayer(renderer, op, state,
+                color, op.mode, op.colorFilter);
     } else {
         float layerAlpha = op.alpha * state.alpha;
         Glop glop;
@@ -817,6 +824,19 @@
                 .build();
         renderer.renderGlop(state, glop);
     }
+
+    if (buffer && !buffer->hasRenderedSinceRepaint) {
+        buffer->hasRenderedSinceRepaint = true;
+        if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
+            // render debug layer highlight
+            renderRectForLayer(renderer, op, state,
+                    0x7f00ff00, SkXfermode::Mode::kSrcOver_Mode, nullptr);
+        } else if (CC_UNLIKELY(Properties::debugOverdraw)) {
+            // render transparent to increment overdraw for repaint area
+            renderRectForLayer(renderer, op, state,
+                    SK_ColorTRANSPARENT, SkXfermode::Mode::kSrcOver_Mode, nullptr);
+        }
+    }
 }
 
 void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 3c302b3..eed0126 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -55,6 +55,7 @@
     }
 
     mRenderTarget.offscreenBuffer = offscreenBuffer;
+    mRenderTarget.offscreenBuffer->hasRenderedSinceRepaint = false;
 
     // create and bind framebuffer
     mRenderTarget.frameBufferId = mRenderState.createFramebuffer();
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 84c8a74..746e99a 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -81,7 +81,8 @@
         // only schedule repaint if node still on layer - possible it may have been
         // removed during a dropped frame, but layers may still remain scheduled so
         // as not to lose info on what portion is damaged
-        if (CC_LIKELY(layerNode->getLayer() != nullptr)) {
+        OffscreenBuffer* layer = layerNode->getLayer();
+        if (CC_LIKELY(layer)) {
             ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u",
                     layerNode->getName(), layerNode->getWidth(), layerNode->getHeight());
 
@@ -90,7 +91,7 @@
 
             // map current light center into RenderNode's coordinate space
             Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
-            layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter);
+            layer->inverseTransformInWindow.mapPoint3d(lightCenter);
 
             saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
                     layerDamage, lightCenter, nullptr, layerNode);
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
index 089f131..73a3392 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -77,6 +77,8 @@
     // vbo / size of mesh
     GLsizei elementCount = 0;
     GLuint vbo = 0;
+
+    bool hasRenderedSinceRepaint;
 };
 
 /**
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index dbaefa4..4536bef 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -74,6 +74,28 @@
     }; \
     void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
 
+/**
+ * Sets a property value temporarily, generally for the duration of a test, restoring the previous
+ * value when going out of scope.
+ *
+ * Can be used e.g. to test behavior only active while Properties::debugOverdraw is enabled.
+ */
+template <typename T>
+class ScopedProperty {
+public:
+    ScopedProperty(T& property, T newValue)
+        : mPropertyPtr(&property)
+        , mOldValue(property) {
+        property = newValue;
+    }
+    ~ScopedProperty() {
+        *mPropertyPtr = mOldValue;
+    }
+private:
+    T* mPropertyPtr;
+    T mOldValue;
+};
+
 class TestUtils {
 public:
     class SignalingDtor {
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 5613f9f..01d3d70 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -16,21 +16,22 @@
 
 #include <gtest/gtest.h>
 
-#include <RecordedOp.h>
 #include <BakedOpDispatcher.h>
 #include <BakedOpRenderer.h>
 #include <FrameBuilder.h>
-#include <SkBlurDrawLooper.h>
+#include <LayerUpdateQueue.h>
 #include <hwui/Paint.h>
+#include <RecordedOp.h>
 #include <tests/common/TestUtils.h>
+#include <utils/Color.h>
 
+#include <SkBlurDrawLooper.h>
 #include <SkDashPathEffect.h>
 
 using namespace android::uirenderer;
 
 static BakedOpRenderer::LightInfo sLightInfo;
 const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
-static Rect sBaseClip(100, 100);
 
 class ValidatingBakedOpRenderer : public BakedOpRenderer {
 public:
@@ -55,7 +56,7 @@
         std::function<void(const Glop& glop)> glopVerifier) {
     // Create op, and wrap with basic state.
     LinearAllocator allocator;
-    auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), sBaseClip);
+    auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 100));
     auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
     ASSERT_NE(nullptr, state);
 
@@ -194,4 +195,84 @@
 
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
     ASSERT_EQ(3, glopCount) << "Exactly three glops expected";
-}
\ No newline at end of file
+}
+
+static void validateLayerDraw(renderthread::RenderThread& renderThread,
+        std::function<void(const Glop& glop)> validator) {
+    auto node = TestUtils::createNode(0, 0, 100, 100,
+            [](RenderProperties& props, TestCanvas& canvas) {
+        props.mutateLayerProperties().setType(LayerType::RenderLayer);
+
+        // provide different blend mode, so decoration draws contrast
+        props.mutateLayerProperties().setXferMode(SkXfermode::Mode::kSrc_Mode);
+        canvas.drawColor(Color::Black, SkXfermode::Mode::kSrcOver_Mode);
+    });
+    OffscreenBuffer** layerHandle = node->getLayerHandle();
+
+    auto syncedNode = TestUtils::getSyncedNode(node);
+
+    // create RenderNode's layer here in same way prepareTree would
+    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+    *layerHandle = &layer;
+    {
+        LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
+        layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(0, 0, 100, 100));
+
+        ValidatingBakedOpRenderer renderer(renderThread.renderState(), validator);
+        FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+                sLightGeometry, Caches::getInstance());
+        frameBuilder.deferLayers(layerUpdateQueue);
+        frameBuilder.deferRenderNode(*syncedNode);
+        frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+    }
+
+    // clean up layer pointer, so we can safely destruct RenderNode
+    *layerHandle = nullptr;
+}
+
+static FloatColor makeFloatColor(uint32_t color) {
+    FloatColor c;
+    c.set(color);
+    return c;
+}
+
+RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) {
+    for (bool debugOverdraw : { false, true }) {
+        for (bool debugLayersUpdates : { false, true }) {
+            ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
+            ScopedProperty<bool> lupProp(Properties::debugLayersUpdates, debugLayersUpdates);
+
+            int glopCount = 0;
+            validateLayerDraw(renderThread, [&glopCount, &debugLayersUpdates](const Glop& glop) {
+                if (glopCount == 0) {
+                    // 0 - Black layer fill
+                    EXPECT_TRUE(glop.fill.colorEnabled);
+                    EXPECT_EQ(makeFloatColor(Color::Black), glop.fill.color);
+                } else if (glopCount == 1) {
+                    // 1 - Uncolored (textured) layer draw
+                    EXPECT_FALSE(glop.fill.colorEnabled);
+                } else if (glopCount == 2) {
+                    // 2 - layer overlay, if present
+                    EXPECT_TRUE(glop.fill.colorEnabled);
+                    // blend srcover, different from that of layer
+                    EXPECT_EQ(GLenum(GL_ONE), glop.blend.src);
+                    EXPECT_EQ(GLenum(GL_ONE_MINUS_SRC_ALPHA), glop.blend.dst);
+                    EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0),
+                            glop.fill.color) << "Should be transparent green if debugLayersUpdates";
+                } else if (glopCount < 7) {
+                    // 3 - 6 - overdraw indicator overlays, if present
+                    EXPECT_TRUE(glop.fill.colorEnabled);
+                    uint32_t expectedColor = Caches::getInstance().getOverdrawColor(glopCount - 2);
+                    ASSERT_EQ(makeFloatColor(expectedColor), glop.fill.color);
+                } else {
+                    ADD_FAILURE() << "Too many glops observed";
+                }
+                glopCount++;
+            });
+            int expectedCount = 2;
+            if (debugLayersUpdates || debugOverdraw) expectedCount++;
+            if (debugOverdraw) expectedCount += 4;
+            EXPECT_EQ(expectedCount, glopCount);
+        }
+    }
+}
diff --git a/libs/hwui/tests/unit/TestUtilsTests.cpp b/libs/hwui/tests/unit/TestUtilsTests.cpp
new file mode 100644
index 0000000..4905957
--- /dev/null
+++ b/libs/hwui/tests/unit/TestUtilsTests.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tests/common/TestUtils.h"
+#include "Properties.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::uirenderer;
+
+TEST(ScopedProperty, simpleBool) {
+    bool previous = Properties::debugOverdraw;
+    {
+        ScopedProperty<bool> debugOverdraw(Properties::debugOverdraw, true);
+        EXPECT_TRUE(Properties::debugOverdraw);
+    }
+    EXPECT_EQ(previous, Properties::debugOverdraw);
+    {
+        ScopedProperty<bool> debugOverdraw(Properties::debugOverdraw, false);
+        EXPECT_FALSE(Properties::debugOverdraw);
+    }
+    EXPECT_EQ(previous, Properties::debugOverdraw);
+}