Precache/early kick off of op work for non-shadow ops.

bug:26562703
bug:27052145

Change-Id: Ic452bfe75da849ffdd47fecdd6eb1472fd0c806e
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index dc967e0..a992af6 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -34,7 +34,7 @@
 FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
         uint32_t viewportWidth, uint32_t viewportHeight,
         const std::vector< sp<RenderNode> >& nodes,
-        const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches* caches)
+        const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches)
         : mCanvasState(*this)
         , mCaches(caches)
         , mLightRadius(lightGeometry.radius) {
@@ -364,15 +364,13 @@
         casterPath = frameAllocatedPath;
     }
 
-
     if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) {
         Matrix4 shadowMatrixXY(casterNodeOp.localMatrix);
         Matrix4 shadowMatrixZ(casterNodeOp.localMatrix);
         node.applyViewPropertyTransforms(shadowMatrixXY, false);
         node.applyViewPropertyTransforms(shadowMatrixZ, true);
 
-        LOG_ALWAYS_FATAL_IF(!mCaches, "Caches needed for shadows");
-        sp<TessellationCache::ShadowTask> task = mCaches->tessellationCache.getShadowTask(
+        sp<TessellationCache::ShadowTask> task = mCaches.tessellationCache.getShadowTask(
                 mCanvasState.currentTransform(),
                 mCanvasState.getLocalClipBounds(),
                 casterAlpha >= 1.0f,
@@ -483,13 +481,14 @@
  * Defers an unmergeable, strokeable op, accounting correctly
  * for paint's style on the bounds being computed.
  */
-void FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
         BakedOpState::StrokeBehavior strokeBehavior) {
     // Note: here we account for stroke when baking the op
     BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
             mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior);
-    if (!bakedState) return; // quick rejected
+    if (!bakedState) return nullptr; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
+    return bakedState;
 }
 
 /**
@@ -607,7 +606,10 @@
 }
 
 void FrameBuilder::deferPathOp(const PathOp& op) {
-    deferStrokeableOp(op, OpBatchType::Bitmap);
+    auto state = deferStrokeableOp(op, OpBatchType::AlphaMaskTexture);
+    if (CC_LIKELY(state)) {
+        mCaches.pathCache.precache(op.path, op.paint);
+    }
 }
 
 void FrameBuilder::deferPointsOp(const PointsOp& op) {
@@ -620,7 +622,12 @@
 }
 
 void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
-    deferStrokeableOp(op, tessBatchId(op));
+    auto state = deferStrokeableOp(op, tessBatchId(op));
+    if (CC_LIKELY(state && !op.paint->getPathEffect())) {
+        // TODO: consider storing tessellation task in BakedOpState
+        mCaches.tessellationCache.precacheRoundRect(state->computedState.transform, *(op.paint),
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
+    }
 }
 
 void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
@@ -660,12 +667,28 @@
     } else {
         currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
     }
+
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
+    auto& totalTransform = bakedState->computedState.transform;
+    if (totalTransform.isPureTranslate() || totalTransform.isPerspective()) {
+        fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::I());
+    } else {
+        // Partial transform case, see BakedOpDispatcher::renderTextOp
+        float sx, sy;
+        totalTransform.decomposeScale(sx, sy);
+        fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::MakeScale(
+                roundf(std::max(1.0f, sx)),
+                roundf(std::max(1.0f, sy))));
+    }
 }
 
 void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
     BakedOpState* bakedState = tryBakeUnboundedOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
+
+    mCaches.fontRenderer.getFontRenderer().precache(
+            op.paint, op.glyphs, op.glyphCount, SkMatrix::I());
 }
 
 void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
@@ -826,5 +849,9 @@
     }
 }
 
+void FrameBuilder::finishDefer() {
+    mCaches.fontRenderer.endPrecaching();
+}
+
 } // namespace uirenderer
 } // namespace android
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index 8a00d33..0b7a606 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -65,14 +65,16 @@
             uint32_t viewportWidth, uint32_t viewportHeight,
             const std::vector< sp<RenderNode> >& nodes,
             const LightGeometry& lightGeometry,
-            Caches* caches)
-            : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightGeometry, Rect(), caches) {}
+            Caches& caches)
+            : FrameBuilder(layers, clip, viewportWidth, viewportHeight,
+                    nodes, lightGeometry, Rect(), caches) {}
 
     FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
             uint32_t viewportWidth, uint32_t viewportHeight,
             const std::vector< sp<RenderNode> >& nodes,
             const LightGeometry& lightGeometry,
-            const Rect &contentDrawBounds, Caches* caches);
+            const Rect &contentDrawBounds,
+            Caches& caches);
 
     virtual ~FrameBuilder() {}
 
@@ -81,10 +83,10 @@
      *
      * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use
      * state->op->opId to lookup a receiver that will be called when the op is replayed.
-     *
      */
     template <typename StaticDispatcher, typename Renderer>
     void replayBakedOps(Renderer& renderer) {
+        finishDefer();
         /**
          * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to
          * dispatch the op via a method on a static dispatcher when the op is replayed.
@@ -157,6 +159,7 @@
     virtual GLuint getTargetFbo() const override { return 0; }
 
 private:
+    void finishDefer();
     enum class ChildrenSelectMode {
         Negative,
         Positive
@@ -198,7 +201,7 @@
         return mAllocator.create<SkPath>();
     }
 
-    void deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+    const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
             BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
 
     /**
@@ -230,7 +233,7 @@
 
     CanvasState mCanvasState;
 
-    Caches* mCaches = nullptr;
+    Caches& mCaches;
 
     float mLightRadius;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 31eec8c..eee5278 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -345,10 +345,10 @@
     mEglManager.damageFrame(frame, dirty);
 
 #if HWUI_NEW_OPS
+    auto& caches = Caches::getInstance();
     FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
-            mRenderNodes, mLightGeometry, mContentDrawBounds, &Caches::getInstance());
+            mRenderNodes, mLightGeometry, mContentDrawBounds, caches);
     mLayerUpdateQueue.clear();
-    auto&& caches = Caches::getInstance();
     BakedOpRenderer renderer(caches, mRenderThread.renderState(),
             mOpaque, mLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
new file mode 100644
index 0000000..52039ef
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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 "TestSceneBase.h"
+#include "utils/Color.h"
+
+#include <minikin/Layout.h>
+#include <hwui/Paint.h>
+
+#include <cstdio>
+
+class GlyphStressAnimation;
+
+static TestScene::Registrar _GlyphStress(TestScene::Info{
+    "glyphstress",
+    "A stress test for both the glyph cache, and glyph rendering.",
+    TestScene::simpleCreateScene<GlyphStressAnimation>
+});
+
+class GlyphStressAnimation : public TestScene {
+public:
+    sp<RenderNode> container;
+    void createContent(int width, int height, TestCanvas& canvas) override {
+        container = TestUtils::createNode(0, 0, width, height, nullptr);
+        doFrame(0); // update container
+
+        canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+        canvas.drawRenderNode(container.get());
+    }
+
+    void doFrame(int frameNr) override {
+        std::unique_ptr<uint16_t[]> text = TestUtils::utf8ToUtf16(
+                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+        ssize_t textLength = 26 * 2;
+
+        TestCanvas canvas(
+                container->stagingProperties().getWidth(),
+                container->stagingProperties().getHeight());
+        Paint paint;
+        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        paint.setAntiAlias(true);
+        paint.setColor(Color::Black);
+        for (int i = 0; i < 5; i++) {
+            paint.setTextSize(10 + (frameNr % 20) + i * 20);
+            canvas.drawText(text.get(), 0, textLength, textLength,
+                    0, 100 * (i + 2), kBidi_Force_LTR, paint, nullptr);
+        }
+
+        container->setStagingDisplayList(canvas.finishRecording());
+    }
+};
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 7816f0f..9daf633 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -65,7 +65,7 @@
     auto nodes = createTestNodeList();
     while (state.KeepRunning()) {
         FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-                nodes, sLightGeometry, nullptr);
+                nodes, sLightGeometry, Caches::getInstance());
         benchmark::DoNotOptimize(&frameBuilder);
     }
 }
@@ -80,7 +80,7 @@
 
         while (state.KeepRunning()) {
             FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-                    nodes, sLightGeometry, nullptr);
+                    nodes, sLightGeometry, Caches::getInstance());
 
             BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
             frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -119,7 +119,7 @@
     while (state.KeepRunning()) {
         FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
                 SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
-                nodes, sLightGeometry, nullptr);
+                nodes, sLightGeometry, Caches::getInstance());
         benchmark::DoNotOptimize(&frameBuilder);
     }
 }
@@ -137,7 +137,7 @@
         while (state.KeepRunning()) {
             FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
                     SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
-                    nodes, sLightGeometry, nullptr);
+                    nodes, sLightGeometry, Caches::getInstance());
 
             BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
             frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index a467b5c..0aabfb1 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -107,7 +107,7 @@
 
 class FailRenderer : public TestRendererBase {};
 
-TEST(FrameBuilder, simple) {
+RENDERTHREAD_TEST(FrameBuilder, simple) {
     class SimpleTestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -133,13 +133,13 @@
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
-TEST(FrameBuilder, simpleStroke) {
+RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
     class SimpleStrokeTestRenderer : public TestRendererBase {
     public:
         void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -159,13 +159,13 @@
         canvas.drawPoint(50, 50, strokedPaint);
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SimpleStrokeTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(FrameBuilder, simpleRejection) {
+RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
@@ -174,13 +174,13 @@
         canvas.restore();
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
 
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(FrameBuilder, simpleBatching) {
+RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
     const int LOOPS = 5;
     class SimpleBatchingTestRenderer : public TestRendererBase {
     public:
@@ -209,14 +209,14 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SimpleBatchingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
             << "Expect number of ops = 2 * loop count";
 }
 
-TEST(FrameBuilder, clippedMerging) {
+RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
     class ClippedMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -250,13 +250,13 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     ClippedMergingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, textMerging) {
+RENDERTHREAD_TEST(FrameBuilder, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -278,13 +278,13 @@
         TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     TextMergingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
 }
 
-TEST(FrameBuilder, textStrikethrough) {
+RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
     const int LOOPS = 5;
     class TextStrikethroughTestRenderer : public TestRendererBase {
     public:
@@ -309,7 +309,7 @@
         }
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     TextStrikethroughTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
@@ -319,7 +319,7 @@
 static auto styles = {
         SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
 
-TEST(FrameBuilder, textStyle) {
+RENDERTHREAD_TEST(FrameBuilder, textStyle) {
     class TextStyleTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -365,7 +365,7 @@
         }
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     TextStyleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
@@ -398,13 +398,13 @@
         canvas.restore();
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     TextureLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(FrameBuilder, functor_reject) {
+RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
     class FunctorTestRenderer : public TestRendererBase {
     public:
         void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
@@ -421,13 +421,14 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(scrolledFunctorView), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(scrolledFunctorView),
+            sLightGeometry, Caches::getInstance());
     FunctorTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
 }
 
-TEST(FrameBuilder, renderNode) {
+RENDERTHREAD_TEST(FrameBuilder, renderNode) {
     class RenderNodeTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -466,13 +467,13 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
     RenderNodeTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(FrameBuilder, clipped) {
+RENDERTHREAD_TEST(FrameBuilder, clipped) {
     class ClippedTestRenderer : public TestRendererBase {
     public:
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -491,12 +492,12 @@
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
             SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
-            200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     ClippedTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(FrameBuilder, saveLayer_simple) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
     class SaveLayerSimpleTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -533,13 +534,13 @@
         canvas.restore();
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SaveLayerSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, saveLayer_nested) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
     /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
      * - startTemporaryLayer2, rect2 endLayer2
      * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -605,13 +606,13 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SaveLayerNestedTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 }
 
-TEST(FrameBuilder, saveLayer_contentRejection) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
         auto node = TestUtils::createNode(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
@@ -625,14 +626,14 @@
         canvas.restore();
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
 
     FailRenderer renderer;
     // should see no ops, even within the layer, since the layer should be rejected
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(FrameBuilder, saveLayerUnclipped_simple) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
     class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -668,13 +669,13 @@
         canvas.restore();
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SaveLayerUnclippedSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
     class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -722,14 +723,14 @@
         canvas.restoreToCount(restoreTo);
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SaveLayerUnclippedMergedClearsTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex())
             << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
 }
 
-TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
     class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -763,13 +764,13 @@
 
     // draw with partial screen dirty, and assert we see that rect later
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SaveLayerUnclippedClearClipTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, saveLayerUnclipped_reject) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // unclipped savelayer + rect both in area that won't intersect with dirty
@@ -780,7 +781,7 @@
 
     // draw with partial screen dirty that doesn't intersect with savelayer
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -789,7 +790,7 @@
  * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
  * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
  */
-TEST(FrameBuilder, saveLayerUnclipped_complex) {
+RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
     class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -840,7 +841,7 @@
         canvas.restore();
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SaveLayerUnclippedComplexTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(12, renderer.getIndex());
@@ -898,7 +899,7 @@
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
     FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedNodeList, sLightGeometry, nullptr);
+            syncedNodeList, sLightGeometry, Caches::getInstance());
     HwLayerSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -999,7 +1000,7 @@
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
 
     FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedList, sLightGeometry, nullptr);
+            syncedList, sLightGeometry, Caches::getInstance());
     HwLayerComplexTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(13, renderer.getIndex());
@@ -1023,7 +1024,7 @@
     node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
     canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
 }
-TEST(FrameBuilder, zReorder) {
+RENDERTHREAD_TEST(FrameBuilder, zReorder) {
     class ZReorderTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -1048,13 +1049,13 @@
         drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
     ZReorderTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 };
 
-TEST(FrameBuilder, projectionReorder) {
+RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1139,7 +1140,7 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
     ProjectionReorderTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex());
@@ -1222,7 +1223,7 @@
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
     FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            syncedList, sLightGeometry, nullptr);
+            syncedList, sLightGeometry, Caches::getInstance());
     ProjectionHwLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -1278,7 +1279,7 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
     ProjectionChildScrollTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1321,7 +1322,7 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, &Caches::getInstance());
+            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
     ShadowTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1363,8 +1364,7 @@
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             TestUtils::createSyncedNodeList(parent),
-            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50},
-            &Caches::getInstance());
+            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
     ShadowSaveLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
@@ -1416,8 +1416,7 @@
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
     FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedList,
-            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30},
-            &Caches::getInstance());
+            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
     ShadowHwLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
@@ -1426,7 +1425,7 @@
     *layerHandle = nullptr;
 }
 
-TEST(FrameBuilder, shadowLayering) {
+RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
     class ShadowLayeringTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1447,8 +1446,7 @@
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             TestUtils::createSyncedNodeList(parent),
-            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50},
-            &Caches::getInstance());
+            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
     ShadowLayeringTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -1476,13 +1474,13 @@
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     PropertyTestRenderer renderer(opValidateCallback);
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
 }
 
-TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
     testProperty([](RenderProperties& properties) {
         properties.setAlpha(0.5f);
         properties.setHasOverlappingRendering(false);
@@ -1491,7 +1489,7 @@
     });
 }
 
-TEST(FrameBuilder, renderPropClipping) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -1501,7 +1499,7 @@
     });
 }
 
-TEST(FrameBuilder, renderPropRevealClip) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const RectOp& op, const BakedOpState& state) {
@@ -1512,7 +1510,7 @@
     });
 }
 
-TEST(FrameBuilder, renderPropOutlineClip) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -1524,7 +1522,7 @@
     });
 }
 
-TEST(FrameBuilder, renderPropTransform) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
@@ -1618,7 +1616,7 @@
     auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            nodes, sLightGeometry, nullptr);
+            nodes, sLightGeometry, Caches::getInstance());
     SaveLayerAlphaClipTestRenderer renderer(outObservedData);
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 
@@ -1626,7 +1624,7 @@
     ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
 }
 
-TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setTranslationX(10); // offset rendering content
@@ -1642,7 +1640,7 @@
             << "expect content to be translated as part of being clipped";
 }
 
-TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         // Translate and rotate the view so that the only visible part is the top left corner of
@@ -1661,7 +1659,7 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
+RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setPivotX(0);
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index da786c7..9161f90 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -41,7 +41,7 @@
     Caches& caches = Caches::getInstance();
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometery, nullptr);
+            TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
     BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
 }