FrameBuilderTests.cpp revision 6246d27813f25b85f6e4b5cb1121fe8484bcce2d
1f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan/* 2f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Copyright (C) 2016 The Android Open Source Project 3f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * 4f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Licensed under the Apache License, Version 2.0 (the "License"); 5f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * you may not use this file except in compliance with the License. 6f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * You may obtain a copy of the License at 7f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * 8f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * http://www.apache.org/licenses/LICENSE-2.0 9f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * 10f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Unless required by applicable law or agreed to in writing, software 11f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * distributed under the License is distributed on an "AS IS" BASIS, 12f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * See the License for the specific language governing permissions and 14f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * limitations under the License. 15f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan */ 16f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 17f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#include <gtest/gtest.h> 18d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 19d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <BakedOpState.h> 20d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <DeferredLayerUpdater.h> 21f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#include <FrameBuilder.h> 22d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <LayerUpdateQueue.h> 23f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#include <RecordedOp.h> 24d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <RecordingCanvas.h> 25d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <tests/common/TestUtils.h> 26d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 27d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <unordered_map> 28d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 29d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloannamespace android { 30d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloannamespace uirenderer { 31d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 32f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanconst LayerUpdateQueue sEmptyLayerUpdateQueue; 33d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanconst std::vector< sp<RenderNode> > sEmptyNodeList; 34f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanconst FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; 35a12bf4695c4916207f946efafd9728eb941e3f0aRobert Sloan 36d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 37a12bf4695c4916207f946efafd9728eb941e3f0aRobert Sloan/** 38d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * Virtual class implemented by each test to redirect static operation / state transitions to 39d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * virtual methods. 40d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * 41d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * Virtual dispatch allows for default behaviors to be specified (very common case in below tests), 42d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * and allows Renderer vs Dispatching behavior to be merged. 43d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * 44d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * onXXXOp methods fail by default - tests should override ops they expect 45d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * startRepaintLayer fails by default - tests should override if expected 46d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * startFrame/endFrame do nothing by default - tests should override to intercept 47d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan */ 48d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanclass TestRendererBase { 49d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanpublic: 50d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan virtual ~TestRendererBase() {} 51d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) { 52f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan ADD_FAILURE() << "Layer creation not expected in this test"; 53d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan return nullptr; 54f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 55d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) { 56f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan ADD_FAILURE() << "Layer repaint not expected in this test"; 57d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 58f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan virtual void endLayer() { 59f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan ADD_FAILURE() << "Layer updates not expected in this test"; 60f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 61f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {} 62f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan virtual void endFrame(const Rect& repaintRect) {} 63f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 64f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan // define virtual defaults for single draw methods 65f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \ 66f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan virtual void on##Type(const Type&, const BakedOpState&) { \ 67f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan ADD_FAILURE() << #Type " not expected in this test"; \ 68f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 69f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan MAP_RENDERABLE_OPS(X) 70f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X 71f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 72f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan // define virtual defaults for merged draw methods 73f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \ 74f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \ 75f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \ 76f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 77f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan MAP_MERGEABLE_OPS(X) 78f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X 79f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 80f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan int getIndex() { return mIndex; } 81f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 82f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanprotected: 83f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan int mIndex = 0; 84f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan}; 85f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 86f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan/** 87f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Dispatches all static methods to similar formed methods on renderer, which fail by default but 88f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * are overridden by subclasses per test. 89f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan */ 90f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanclass TestDispatcher { 91f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanpublic: 92f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan // define single op methods, which redirect to TestRendererBase 93f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \ 94f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \ 95f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan renderer.on##Type(op, state); \ 96f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 97f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan MAP_RENDERABLE_OPS(X); 98f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X 99f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 100f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan // define merged op methods, which redirect to TestRendererBase 101f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \ 102f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \ 103f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan renderer.onMerged##Type##s(opList); \ 104f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 105f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan MAP_MERGEABLE_OPS(X); 106f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X 107f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan}; 108f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 109f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanclass FailRenderer : public TestRendererBase {}; 110f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 111f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simple) { 112f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan class SimpleTestRenderer : public TestRendererBase { 113f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan public: 114f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 115f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(0, mIndex++); 116f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(100u, width); 117f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(200u, height); 118f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 119f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void onRectOp(const RectOp& op, const BakedOpState& state) override { 120f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(1, mIndex++); 121f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 122f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 123f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(2, mIndex++); 124f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 125d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void endFrame(const Rect& repaintRect) override { 126f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(3, mIndex++); 127f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 128f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan }; 129f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 130f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan auto node = TestUtils::createNode(0, 0, 100, 200, 131f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 132f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan SkBitmap bitmap = TestUtils::createSkBitmap(25, 25); 133f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.drawRect(0, 0, 100, 200, SkPaint()); 134f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.drawBitmap(bitmap, 10, 10, nullptr); 135f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan }); 136f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, 137f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 138f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan SimpleTestRenderer renderer; 139f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 140f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end 141f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan} 142f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 143f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simpleStroke) { 144f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan class SimpleStrokeTestRenderer : public TestRendererBase { 145f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan public: 146f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void onPointsOp(const PointsOp& op, const BakedOpState& state) override { 147f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(0, mIndex++); 148f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan // even though initial bounds are empty... 149f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_TRUE(op.unmappedBounds.isEmpty()) 150f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan << "initial bounds should be empty, since they're unstroked"; 151f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds) 152f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan << "final bounds should account for stroke"; 153f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 154f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan }; 155f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 156f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan auto node = TestUtils::createNode(0, 0, 100, 200, 157f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 158f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan SkPaint strokedPaint; 159f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan strokedPaint.setStrokeWidth(10); 160f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.drawPoint(50, 50, strokedPaint); 161f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan }); 162f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, 163f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 164f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan SimpleStrokeTestRenderer renderer; 165f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 166f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(1, renderer.getIndex()); 167f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan} 168f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 169f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simpleRejection) { 170f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan auto node = TestUtils::createNode(0, 0, 200, 200, 171f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 172f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.save(SaveFlags::MatrixClip); 173f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty 174f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.drawRect(0, 0, 400, 400, SkPaint()); 175f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.restore(); 176f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan }); 177f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 178f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 179f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 180f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan FailRenderer renderer; 181f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 182f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan} 183f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 184f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simpleBatching) { 185f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan const int LOOPS = 5; 186f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan class SimpleBatchingTestRenderer : public TestRendererBase { 187f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan public: 188f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 189f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects"; 190f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 191f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void onRectOp(const RectOp& op, const BakedOpState& state) override { 192f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps"; 193f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 194f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan }; 195f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 196f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan auto node = TestUtils::createNode(0, 0, 200, 200, 197f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 198f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan SkBitmap bitmap = TestUtils::createSkBitmap(10, 10, 199f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap 200f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 201f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. 202f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. 203f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.save(SaveFlags::MatrixClip); 204f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan for (int i = 0; i < LOOPS; i++) { 205f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.translate(0, 10); 206f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.drawRect(0, 0, 10, 10, SkPaint()); 207f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.drawBitmap(bitmap, 5, 0, nullptr); 208f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 209f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan canvas.restore(); 210f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan }); 211f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 212f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 213f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 214f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan SimpleBatchingTestRenderer renderer; 215f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 216f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 217f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan << "Expect number of ops = 2 * loop count"; 218f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan} 219f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan 220f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { 221f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan class EmptyNoFbo0TestRenderer : public TestRendererBase { 222f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan public: 223f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 224f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan ADD_FAILURE() << "Primary frame draw not expected in this test"; 225f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan } 226f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan void endFrame(const Rect& repaintRect) override { 227f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan ADD_FAILURE() << "Primary frame draw not expected in this test"; 228d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 229d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 230d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 231d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // Pass empty node list, so no work is enqueued for Fbo0 232d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 233d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan sEmptyNodeList, sLightGeometry, Caches::getInstance()); 234d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EmptyNoFbo0TestRenderer renderer; 235d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 236d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 237d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 238d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { 239d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class EmptyWithFbo0TestRenderer : public TestRendererBase { 240d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 241d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 242d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(0, mIndex++); 243d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 244d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void endFrame(const Rect& repaintRect) override { 245d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(1, mIndex++); 246d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 247d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 248d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(10, 10, 110, 110, 249d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 250d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // no drawn content 251d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 252d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto syncedNodeList = TestUtils::createSyncedNodeList(node); 253d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 254d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // Draw, but pass empty node list, so no work is done for primary frame 255d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 256d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan syncedNodeList, sLightGeometry, Caches::getInstance()); 257d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EmptyWithFbo0TestRenderer renderer; 258d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 259d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," 260d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan " but fbo0 update lifecycle should still be observed"; 261d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 262d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 263d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { 264d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class AvoidOverdrawRectsTestRenderer : public TestRendererBase { 265d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 266d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onRectOp(const RectOp& op, const BakedOpState& state) override { 267d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(mIndex++, 0) << "Should be one rect"; 268d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds) 269d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan << "Last rect should occlude others."; 270d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 271d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 272d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(0, 0, 200, 200, 273d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 274d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawRect(0, 0, 200, 200, SkPaint()); 275d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawRect(0, 0, 200, 200, SkPaint()); 276d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawRect(10, 10, 190, 190, SkPaint()); 277d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 278d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 279d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // Damage (and therefore clip) is same as last draw, subset of renderable area. 280d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // This means last op occludes other contents, and they'll be rejected to avoid overdraw. 281d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190); 282d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200, 283d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 284d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 285d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) 286d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan << "Recording must not have rejected ops, in order for this test to be valid"; 287d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 288d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan AvoidOverdrawRectsTestRenderer renderer; 289d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 290d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op"; 291d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 292d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 293d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { 294d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50, 295d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkColorType::kRGB_565_SkColorType); 296d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50, 297d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkColorType::kAlpha_8_SkColorType); 298d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { 299d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 300d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 301d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_LT(mIndex++, 2) << "Should be two bitmaps"; 302d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan switch(mIndex++) { 303d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan case 0: 304d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef()); 305d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan break; 306d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan case 1: 307d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef()); 308d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan break; 309d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan default: 310d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan ADD_FAILURE() << "Only two ops expected."; 311d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 312d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 313d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 314d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 315d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(0, 0, 50, 50, 316d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 317d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawRect(0, 0, 50, 50, SkPaint()); 318d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawRect(0, 0, 50, 50, SkPaint()); 319d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawBitmap(transpBitmap, 0, 0, nullptr); 320d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 321d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // only the below draws should remain, since they're 322d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr); 323d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawBitmap(transpBitmap, 0, 0, nullptr); 324d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 325d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 326d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50, 327d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 328d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 329d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) 330d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan << "Recording must not have rejected ops, in order for this test to be valid"; 331d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 332d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan AvoidOverdrawBitmapsTestRenderer renderer; 333d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 334d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly one op"; 335d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 336d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 337d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, clippedMerging) { 338d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class ClippedMergingTestRenderer : public TestRendererBase { 339d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 340d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onMergedBitmapOps(const MergedBakedOpList& opList) override { 341d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(0, mIndex); 342d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan mIndex += opList.count; 343d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(4u, opList.count); 344d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip); 345d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right, 346d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan opList.clipSideFlags); 347d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 348d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 349d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(0, 0, 100, 100, 350d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [](RenderProperties& props, TestCanvas& canvas) { 351d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkBitmap bitmap = TestUtils::createSkBitmap(20, 20); 352d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 353d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // left side clipped (to inset left half) 354d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op); 355d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawBitmap(bitmap, 0, 40, nullptr); 356d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 357d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // top side clipped (to inset top half) 358d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op); 359d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawBitmap(bitmap, 40, 0, nullptr); 360d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 361d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // right side clipped (to inset right half) 362d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op); 363d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawBitmap(bitmap, 80, 40, nullptr); 364d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 365d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // bottom not clipped, just abutting (inset bottom half) 366d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op); 367d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawBitmap(bitmap, 40, 70, nullptr); 368d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 369d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 370d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 371d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 372d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan ClippedMergingTestRenderer renderer; 373d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 374d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(4, renderer.getIndex()); 375d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 376d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 377d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textMerging) { 378d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class TextMergingTestRenderer : public TestRendererBase { 379d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 380d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onMergedTextOps(const MergedBakedOpList& opList) override { 381d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(0, mIndex); 382d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan mIndex += opList.count; 383d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(2u, opList.count); 384d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags); 385d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags); 386d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags); 387d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 388d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 389d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(0, 0, 400, 400, 390d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [](RenderProperties& props, TestCanvas& canvas) { 391d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkPaint paint; 392d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 393d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setAntiAlias(true); 394d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setTextSize(50); 395d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped 396d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped 397d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 398d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 399d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 400d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TextMergingTestRenderer renderer; 401d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 402d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; 403d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 404d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 405d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { 406d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan const int LOOPS = 5; 407d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class TextStrikethroughTestRenderer : public TestRendererBase { 408d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 409d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onRectOp(const RectOp& op, const BakedOpState& state) override { 410d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text"; 411d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 412d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onMergedTextOps(const MergedBakedOpList& opList) override { 413d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(0, mIndex); 414d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan mIndex += opList.count; 415d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(5u, opList.count); 416d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 417d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 418d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(0, 0, 200, 2000, 419d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [](RenderProperties& props, RecordingCanvas& canvas) { 420d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkPaint textPaint; 421d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan textPaint.setAntiAlias(true); 422d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan textPaint.setTextSize(20); 423d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan textPaint.setStrikeThruText(true); 424d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 425d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan for (int i = 0; i < LOOPS; i++) { 426d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); 427d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 428d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 429d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000, 430d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 431d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TextStrikethroughTestRenderer renderer; 432d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 433d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 434d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan << "Expect number of ops = 2 * loop count"; 435d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 436d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 437d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanstatic auto styles = { 438d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style }; 439d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 440d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textStyle) { 441d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class TextStyleTestRenderer : public TestRendererBase { 442d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 443d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onMergedTextOps(const MergedBakedOpList& opList) override { 444d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan ASSERT_EQ(0, mIndex); 445d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan ASSERT_EQ(3u, opList.count); 446d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan mIndex += opList.count; 447d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 448d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan int index = 0; 449d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan for (auto style : styles) { 450d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto state = opList.states[index++]; 451d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan ASSERT_EQ(style, state->op->paint->getStyle()) 452d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan << "Remainder of validation relies upon stable merged order"; 453d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan ASSERT_EQ(0, state->computedState.clipSideFlags) 454d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan << "Clipped bounds validation requires unclipped ops"; 455d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 456d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 457d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan Rect fill = opList.states[0]->computedState.clippedBounds; 458d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan Rect stroke = opList.states[1]->computedState.clippedBounds; 459d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds) 460d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan << "Stroke+Fill should be same as stroke"; 461d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 462d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_TRUE(stroke.contains(fill)); 463d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_FALSE(fill.contains(stroke)); 464d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 465d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // outset by half the stroke width 466d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan Rect outsetFill(fill); 467d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan outsetFill.outset(5); 468d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(stroke, outsetFill); 469d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 470d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 471d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(0, 0, 400, 400, 472d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [](RenderProperties& props, TestCanvas& canvas) { 473d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkPaint paint; 474d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 475d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setAntiAlias(true); 476d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setTextSize(50); 477d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setStrokeWidth(10); 478d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 479d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // draw 3 copies of the same text overlapping, each with a different style. 480d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan // They'll get merged, but with 481d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan for (auto style : styles) { 482d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan paint.setStyle(style); 483d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); 484d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 485d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 486d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 487d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 488d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan TextStyleTestRenderer renderer; 489d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan frameBuilder.replayBakedOps<TestDispatcher>(renderer); 490d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops"; 491d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan} 492d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 493d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { 494d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase { 495d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan public: 496d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 497d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(0, mIndex++); 498d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect()); 499d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds); 500d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 501d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan Matrix4 expected; 502d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan expected.loadTranslate(5, 5, 0); 503d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 504d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan } 505d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }; 506d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 507d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 508d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan SkMatrix::MakeTrans(5, 5)); 509d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan 510d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan auto node = TestUtils::createNode(0, 0, 200, 200, 511d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 512d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.save(SaveFlags::MatrixClip); 513d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op); 514d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.drawLayer(layerUpdater.get()); 515d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan canvas.restore(); 516d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan }); 517f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 518f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 519 TextureLayerClipLocalMatrixTestRenderer renderer; 520 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 521 EXPECT_EQ(1, renderer.getIndex()); 522} 523 524RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) { 525 class TextureLayerCombineMatricesTestRenderer : public TestRendererBase { 526 public: 527 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 528 EXPECT_EQ(0, mIndex++); 529 530 Matrix4 expected; 531 expected.loadTranslate(35, 45, 0); 532 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 533 } 534 }; 535 536 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 537 SkMatrix::MakeTrans(5, 5)); 538 539 auto node = TestUtils::createNode(0, 0, 200, 200, 540 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 541 canvas.save(SaveFlags::MatrixClip); 542 canvas.translate(30, 40); 543 canvas.drawLayer(layerUpdater.get()); 544 canvas.restore(); 545 }); 546 547 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 548 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 549 TextureLayerCombineMatricesTestRenderer renderer; 550 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 551 EXPECT_EQ(1, renderer.getIndex()); 552} 553 554RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) { 555 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 556 SkMatrix::MakeTrans(5, 5)); 557 layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected 558 559 auto node = TestUtils::createNode(0, 0, 200, 200, 560 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 561 canvas.drawLayer(layerUpdater.get()); 562 }); 563 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 564 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 565 FailRenderer renderer; 566 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 567} 568 569RENDERTHREAD_TEST(FrameBuilder, functor_reject) { 570 class FunctorTestRenderer : public TestRendererBase { 571 public: 572 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override { 573 EXPECT_EQ(0, mIndex++); 574 } 575 }; 576 Functor noopFunctor; 577 578 // 1 million pixel tall view, scrolled down 80% 579 auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000, 580 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { 581 canvas.translate(0, -800000); 582 canvas.callDrawGLFunction(&noopFunctor); 583 }); 584 585 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 586 TestUtils::createSyncedNodeList(scrolledFunctorView), 587 sLightGeometry, Caches::getInstance()); 588 FunctorTestRenderer renderer; 589 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 590 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected"; 591} 592 593RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) { 594 class ColorTestRenderer : public TestRendererBase { 595 public: 596 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 597 EXPECT_EQ(0, mIndex++); 598 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds) 599 << "Color op should be expanded to bounds of surrounding"; 600 } 601 }; 602 603 auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10, 604 [](RenderProperties& props, RecordingCanvas& canvas) { 605 props.setClipToBounds(false); 606 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); 607 }); 608 609 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 610 TestUtils::createSyncedNodeList(unclippedColorView), 611 sLightGeometry, Caches::getInstance()); 612 ColorTestRenderer renderer; 613 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 614 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected"; 615} 616 617TEST(FrameBuilder, renderNode) { 618 class RenderNodeTestRenderer : public TestRendererBase { 619 public: 620 void onRectOp(const RectOp& op, const BakedOpState& state) override { 621 switch(mIndex++) { 622 case 0: 623 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 624 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 625 break; 626 case 1: 627 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds); 628 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 629 break; 630 default: 631 ADD_FAILURE(); 632 } 633 } 634 }; 635 636 auto child = TestUtils::createNode(10, 10, 110, 110, 637 [](RenderProperties& props, RecordingCanvas& canvas) { 638 SkPaint paint; 639 paint.setColor(SK_ColorWHITE); 640 canvas.drawRect(0, 0, 100, 100, paint); 641 }); 642 643 auto parent = TestUtils::createNode(0, 0, 200, 200, 644 [&child](RenderProperties& props, RecordingCanvas& canvas) { 645 SkPaint paint; 646 paint.setColor(SK_ColorDKGRAY); 647 canvas.drawRect(0, 0, 200, 200, paint); 648 649 canvas.save(SaveFlags::MatrixClip); 650 canvas.translate(40, 40); 651 canvas.drawRenderNode(child.get()); 652 canvas.restore(); 653 }); 654 655 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 656 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 657 RenderNodeTestRenderer renderer; 658 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 659 EXPECT_EQ(2, renderer.getIndex()); 660} 661 662RENDERTHREAD_TEST(FrameBuilder, clipped) { 663 class ClippedTestRenderer : public TestRendererBase { 664 public: 665 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 666 EXPECT_EQ(0, mIndex++); 667 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds); 668 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect()); 669 EXPECT_TRUE(state.computedState.transform.isIdentity()); 670 } 671 }; 672 673 auto node = TestUtils::createNode(0, 0, 200, 200, 674 [](RenderProperties& props, RecordingCanvas& canvas) { 675 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200); 676 canvas.drawBitmap(bitmap, 0, 0, nullptr); 677 }); 678 679 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, 680 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver 681 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 682 ClippedTestRenderer renderer; 683 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 684} 685 686RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { 687 class SaveLayerSimpleTestRenderer : public TestRendererBase { 688 public: 689 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 690 EXPECT_EQ(0, mIndex++); 691 EXPECT_EQ(180u, width); 692 EXPECT_EQ(180u, height); 693 return nullptr; 694 } 695 void endLayer() override { 696 EXPECT_EQ(2, mIndex++); 697 } 698 void onRectOp(const RectOp& op, const BakedOpState& state) override { 699 EXPECT_EQ(1, mIndex++); 700 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds); 701 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds); 702 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect()); 703 704 Matrix4 expectedTransform; 705 expectedTransform.loadTranslate(-10, -10, 0); 706 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform); 707 } 708 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 709 EXPECT_EQ(3, mIndex++); 710 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 711 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 712 EXPECT_TRUE(state.computedState.transform.isIdentity()); 713 } 714 }; 715 716 auto node = TestUtils::createNode(0, 0, 200, 200, 717 [](RenderProperties& props, RecordingCanvas& canvas) { 718 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); 719 canvas.drawRect(10, 10, 190, 190, SkPaint()); 720 canvas.restore(); 721 }); 722 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 723 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 724 SaveLayerSimpleTestRenderer renderer; 725 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 726 EXPECT_EQ(4, renderer.getIndex()); 727} 728 729RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { 730 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as: 731 * - startTemporaryLayer2, rect2 endLayer2 732 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1 733 * - startFrame, layerOp1, endFrame 734 */ 735 class SaveLayerNestedTestRenderer : public TestRendererBase { 736 public: 737 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 738 const int index = mIndex++; 739 if (index == 0) { 740 EXPECT_EQ(400u, width); 741 EXPECT_EQ(400u, height); 742 return (OffscreenBuffer*) 0x400; 743 } else if (index == 3) { 744 EXPECT_EQ(800u, width); 745 EXPECT_EQ(800u, height); 746 return (OffscreenBuffer*) 0x800; 747 } else { ADD_FAILURE(); } 748 return (OffscreenBuffer*) nullptr; 749 } 750 void endLayer() override { 751 int index = mIndex++; 752 EXPECT_TRUE(index == 2 || index == 6); 753 } 754 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 755 EXPECT_EQ(7, mIndex++); 756 } 757 void endFrame(const Rect& repaintRect) override { 758 EXPECT_EQ(9, mIndex++); 759 } 760 void onRectOp(const RectOp& op, const BakedOpState& state) override { 761 const int index = mIndex++; 762 if (index == 1) { 763 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect 764 } else if (index == 4) { 765 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect 766 } else { ADD_FAILURE(); } 767 } 768 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 769 const int index = mIndex++; 770 if (index == 5) { 771 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle); 772 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer 773 } else if (index == 8) { 774 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle); 775 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer 776 } else { ADD_FAILURE(); } 777 } 778 }; 779 780 auto node = TestUtils::createNode(0, 0, 800, 800, 781 [](RenderProperties& props, RecordingCanvas& canvas) { 782 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); 783 { 784 canvas.drawRect(0, 0, 800, 800, SkPaint()); 785 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); 786 { 787 canvas.drawRect(0, 0, 400, 400, SkPaint()); 788 } 789 canvas.restore(); 790 } 791 canvas.restore(); 792 }); 793 794 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800, 795 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 796 SaveLayerNestedTestRenderer renderer; 797 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 798 EXPECT_EQ(10, renderer.getIndex()); 799} 800 801RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { 802 auto node = TestUtils::createNode(0, 0, 200, 200, 803 [](RenderProperties& props, RecordingCanvas& canvas) { 804 canvas.save(SaveFlags::MatrixClip); 805 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); 806 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); 807 808 // draw within save layer may still be recorded, but shouldn't be drawn 809 canvas.drawRect(200, 200, 400, 400, SkPaint()); 810 811 canvas.restore(); 812 canvas.restore(); 813 }); 814 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 815 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 816 817 FailRenderer renderer; 818 // should see no ops, even within the layer, since the layer should be rejected 819 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 820} 821 822RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { 823 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase { 824 public: 825 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 826 EXPECT_EQ(0, mIndex++); 827 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 828 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 829 EXPECT_TRUE(state.computedState.transform.isIdentity()); 830 } 831 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 832 EXPECT_EQ(1, mIndex++); 833 ASSERT_NE(nullptr, op.paint); 834 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); 835 } 836 void onRectOp(const RectOp& op, const BakedOpState& state) override { 837 EXPECT_EQ(2, mIndex++); 838 EXPECT_EQ(Rect(200, 200), op.unmappedBounds); 839 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 840 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 841 EXPECT_TRUE(state.computedState.transform.isIdentity()); 842 } 843 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 844 EXPECT_EQ(3, mIndex++); 845 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 846 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 847 EXPECT_TRUE(state.computedState.transform.isIdentity()); 848 } 849 }; 850 851 auto node = TestUtils::createNode(0, 0, 200, 200, 852 [](RenderProperties& props, RecordingCanvas& canvas) { 853 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 854 canvas.drawRect(0, 0, 200, 200, SkPaint()); 855 canvas.restore(); 856 }); 857 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 858 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 859 SaveLayerUnclippedSimpleTestRenderer renderer; 860 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 861 EXPECT_EQ(4, renderer.getIndex()); 862} 863 864RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { 865 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase { 866 public: 867 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 868 int index = mIndex++; 869 EXPECT_GT(4, index); 870 EXPECT_EQ(5, op.unmappedBounds.getWidth()); 871 EXPECT_EQ(5, op.unmappedBounds.getHeight()); 872 if (index == 0) { 873 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds); 874 } else if (index == 1) { 875 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds); 876 } else if (index == 2) { 877 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds); 878 } else if (index == 3) { 879 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds); 880 } 881 } 882 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 883 EXPECT_EQ(4, mIndex++); 884 ASSERT_EQ(op.vertexCount, 16u); 885 for (size_t i = 0; i < op.vertexCount; i++) { 886 auto v = op.vertices[i]; 887 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200); 888 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200); 889 } 890 } 891 void onRectOp(const RectOp& op, const BakedOpState& state) override { 892 EXPECT_EQ(5, mIndex++); 893 } 894 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 895 EXPECT_LT(5, mIndex++); 896 } 897 }; 898 899 auto node = TestUtils::createNode(0, 0, 200, 200, 900 [](RenderProperties& props, RecordingCanvas& canvas) { 901 902 int restoreTo = canvas.save(SaveFlags::MatrixClip); 903 canvas.scale(2, 2); 904 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); 905 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); 906 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); 907 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); 908 canvas.drawRect(0, 0, 100, 100, SkPaint()); 909 canvas.restoreToCount(restoreTo); 910 }); 911 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 912 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 913 SaveLayerUnclippedMergedClearsTestRenderer renderer; 914 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 915 EXPECT_EQ(10, renderer.getIndex()) 916 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect."; 917} 918 919RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { 920 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase { 921 public: 922 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 923 EXPECT_EQ(0, mIndex++); 924 } 925 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 926 EXPECT_EQ(1, mIndex++); 927 ASSERT_NE(nullptr, op.paint); 928 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); 929 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds) 930 << "Expect dirty rect as clip"; 931 ASSERT_NE(nullptr, state.computedState.clipState); 932 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect); 933 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 934 } 935 void onRectOp(const RectOp& op, const BakedOpState& state) override { 936 EXPECT_EQ(2, mIndex++); 937 } 938 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 939 EXPECT_EQ(3, mIndex++); 940 } 941 }; 942 943 auto node = TestUtils::createNode(0, 0, 200, 200, 944 [](RenderProperties& props, RecordingCanvas& canvas) { 945 // save smaller than clip, so we get unclipped behavior 946 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 947 canvas.drawRect(0, 0, 200, 200, SkPaint()); 948 canvas.restore(); 949 }); 950 951 // draw with partial screen dirty, and assert we see that rect later 952 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, 953 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 954 SaveLayerUnclippedClearClipTestRenderer renderer; 955 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 956 EXPECT_EQ(4, renderer.getIndex()); 957} 958 959RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { 960 auto node = TestUtils::createNode(0, 0, 200, 200, 961 [](RenderProperties& props, RecordingCanvas& canvas) { 962 // unclipped savelayer + rect both in area that won't intersect with dirty 963 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); 964 canvas.drawRect(100, 100, 200, 200, SkPaint()); 965 canvas.restore(); 966 }); 967 968 // draw with partial screen dirty that doesn't intersect with savelayer 969 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, 970 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 971 FailRenderer renderer; 972 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 973} 974 975/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as: 976 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer 977 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe 978 */ 979RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { 980 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { 981 public: 982 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 983 EXPECT_EQ(0, mIndex++); // savelayer first 984 return (OffscreenBuffer*)0xabcd; 985 } 986 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 987 int index = mIndex++; 988 EXPECT_TRUE(index == 1 || index == 7); 989 } 990 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 991 int index = mIndex++; 992 EXPECT_TRUE(index == 2 || index == 8); 993 } 994 void onRectOp(const RectOp& op, const BakedOpState& state) override { 995 EXPECT_EQ(3, mIndex++); 996 Matrix4 expected; 997 expected.loadTranslate(-100, -100, 0); 998 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds); 999 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 1000 } 1001 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1002 int index = mIndex++; 1003 EXPECT_TRUE(index == 4 || index == 10); 1004 } 1005 void endLayer() override { 1006 EXPECT_EQ(5, mIndex++); 1007 } 1008 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1009 EXPECT_EQ(6, mIndex++); 1010 } 1011 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1012 EXPECT_EQ(9, mIndex++); 1013 } 1014 void endFrame(const Rect& repaintRect) override { 1015 EXPECT_EQ(11, mIndex++); 1016 } 1017 }; 1018 1019 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping 1020 [](RenderProperties& props, RecordingCanvas& canvas) { 1021 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped 1022 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped 1023 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped 1024 canvas.drawRect(200, 200, 300, 300, SkPaint()); 1025 canvas.restore(); 1026 canvas.restore(); 1027 canvas.restore(); 1028 }); 1029 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600, 1030 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 1031 SaveLayerUnclippedComplexTestRenderer renderer; 1032 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1033 EXPECT_EQ(12, renderer.getIndex()); 1034} 1035 1036RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { 1037 class HwLayerSimpleTestRenderer : public TestRendererBase { 1038 public: 1039 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1040 EXPECT_EQ(0, mIndex++); 1041 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1042 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1043 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1044 } 1045 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1046 EXPECT_EQ(1, mIndex++); 1047 1048 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1049 << "Transform should be reset within layer"; 1050 1051 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1052 << "Damage rect should be used to clip layer content"; 1053 } 1054 void endLayer() override { 1055 EXPECT_EQ(2, mIndex++); 1056 } 1057 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1058 EXPECT_EQ(3, mIndex++); 1059 } 1060 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1061 EXPECT_EQ(4, mIndex++); 1062 } 1063 void endFrame(const Rect& repaintRect) override { 1064 EXPECT_EQ(5, mIndex++); 1065 } 1066 }; 1067 1068 auto node = TestUtils::createNode(10, 10, 110, 110, 1069 [](RenderProperties& props, RecordingCanvas& canvas) { 1070 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1071 SkPaint paint; 1072 paint.setColor(SK_ColorWHITE); 1073 canvas.drawRect(0, 0, 100, 100, paint); 1074 }); 1075 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1076 1077 // create RenderNode's layer here in same way prepareTree would 1078 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1079 *layerHandle = &layer; 1080 1081 auto syncedNodeList = TestUtils::createSyncedNodeList(node); 1082 1083 // only enqueue partial damage 1084 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1085 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1086 1087 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1088 syncedNodeList, sLightGeometry, Caches::getInstance()); 1089 HwLayerSimpleTestRenderer renderer; 1090 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1091 EXPECT_EQ(6, renderer.getIndex()); 1092 1093 // clean up layer pointer, so we can safely destruct RenderNode 1094 *layerHandle = nullptr; 1095} 1096 1097RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { 1098 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: 1099 * - startRepaintLayer(child), rect(grey), endLayer 1100 * - startTemporaryLayer, drawLayer(child), endLayer 1101 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer 1102 * - startFrame, drawLayer(parent), endLayerb 1103 */ 1104 class HwLayerComplexTestRenderer : public TestRendererBase { 1105 public: 1106 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1107 EXPECT_EQ(3, mIndex++); // savelayer first 1108 return (OffscreenBuffer*)0xabcd; 1109 } 1110 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1111 int index = mIndex++; 1112 if (index == 0) { 1113 // starting inner layer 1114 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1115 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1116 } else if (index == 6) { 1117 // starting outer layer 1118 EXPECT_EQ(200u, offscreenBuffer->viewportWidth); 1119 EXPECT_EQ(200u, offscreenBuffer->viewportHeight); 1120 } else { ADD_FAILURE(); } 1121 } 1122 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1123 int index = mIndex++; 1124 if (index == 1) { 1125 // inner layer's rect (white) 1126 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1127 } else if (index == 7) { 1128 // outer layer's rect (grey) 1129 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1130 } else { ADD_FAILURE(); } 1131 } 1132 void endLayer() override { 1133 int index = mIndex++; 1134 EXPECT_TRUE(index == 2 || index == 5 || index == 9); 1135 } 1136 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1137 EXPECT_EQ(10, mIndex++); 1138 } 1139 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1140 OffscreenBuffer* layer = *op.layerHandle; 1141 int index = mIndex++; 1142 if (index == 4) { 1143 EXPECT_EQ(100u, layer->viewportWidth); 1144 EXPECT_EQ(100u, layer->viewportHeight); 1145 } else if (index == 8) { 1146 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1147 } else if (index == 11) { 1148 EXPECT_EQ(200u, layer->viewportWidth); 1149 EXPECT_EQ(200u, layer->viewportHeight); 1150 } else { ADD_FAILURE(); } 1151 } 1152 void endFrame(const Rect& repaintRect) override { 1153 EXPECT_EQ(12, mIndex++); 1154 } 1155 }; 1156 1157 auto child = TestUtils::createNode(50, 50, 150, 150, 1158 [](RenderProperties& props, RecordingCanvas& canvas) { 1159 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1160 SkPaint paint; 1161 paint.setColor(SK_ColorWHITE); 1162 canvas.drawRect(0, 0, 100, 100, paint); 1163 }); 1164 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1165 *(child->getLayerHandle()) = &childLayer; 1166 1167 RenderNode* childPtr = child.get(); 1168 auto parent = TestUtils::createNode(0, 0, 200, 200, 1169 [childPtr](RenderProperties& props, RecordingCanvas& canvas) { 1170 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1171 SkPaint paint; 1172 paint.setColor(SK_ColorDKGRAY); 1173 canvas.drawRect(0, 0, 200, 200, paint); 1174 1175 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); 1176 canvas.drawRenderNode(childPtr); 1177 canvas.restore(); 1178 }); 1179 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1180 *(parent->getLayerHandle()) = &parentLayer; 1181 1182 auto syncedList = TestUtils::createSyncedNodeList(parent); 1183 1184 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1185 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); 1186 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); 1187 1188 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1189 syncedList, sLightGeometry, Caches::getInstance()); 1190 HwLayerComplexTestRenderer renderer; 1191 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1192 EXPECT_EQ(13, renderer.getIndex()); 1193 1194 // clean up layer pointers, so we can safely destruct RenderNodes 1195 *(child->getLayerHandle()) = nullptr; 1196 *(parent->getLayerHandle()) = nullptr; 1197} 1198 1199 1200RENDERTHREAD_TEST(FrameBuilder, buildLayer) { 1201 class BuildLayerTestRenderer : public TestRendererBase { 1202 public: 1203 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1204 EXPECT_EQ(0, mIndex++); 1205 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1206 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1207 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1208 } 1209 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 1210 EXPECT_EQ(1, mIndex++); 1211 1212 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1213 << "Transform should be reset within layer"; 1214 1215 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1216 << "Damage rect should be used to clip layer content"; 1217 } 1218 void endLayer() override { 1219 EXPECT_EQ(2, mIndex++); 1220 } 1221 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1222 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1223 } 1224 void endFrame(const Rect& repaintRect) override { 1225 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1226 } 1227 }; 1228 1229 auto node = TestUtils::createNode(10, 10, 110, 110, 1230 [](RenderProperties& props, RecordingCanvas& canvas) { 1231 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1232 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); 1233 }); 1234 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1235 1236 // create RenderNode's layer here in same way prepareTree would 1237 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1238 *layerHandle = &layer; 1239 1240 auto syncedNodeList = TestUtils::createSyncedNodeList(node); 1241 1242 // only enqueue partial damage 1243 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1244 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1245 1246 // Draw, but pass empty node list, so no work is done for primary frame 1247 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1, 1248 sEmptyNodeList, sLightGeometry, Caches::getInstance()); 1249 BuildLayerTestRenderer renderer; 1250 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1251 EXPECT_EQ(3, renderer.getIndex()); 1252 1253 // clean up layer pointer, so we can safely destruct RenderNode 1254 *layerHandle = nullptr; 1255} 1256 1257static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { 1258 SkPaint paint; 1259 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel 1260 canvas->drawRect(0, 0, 100, 100, paint); 1261} 1262static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { 1263 auto node = TestUtils::createNode(0, 0, 100, 100, 1264 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { 1265 drawOrderedRect(&canvas, expectedDrawOrder); 1266 }); 1267 node->mutateStagingProperties().setTranslationZ(z); 1268 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); 1269 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1270} 1271RENDERTHREAD_TEST(FrameBuilder, zReorder) { 1272 class ZReorderTestRenderer : public TestRendererBase { 1273 public: 1274 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1275 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel 1276 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 1277 } 1278 }; 1279 1280 auto parent = TestUtils::createNode(0, 0, 100, 100, 1281 [](RenderProperties& props, RecordingCanvas& canvas) { 1282 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 1283 drawOrderedRect(&canvas, 1); 1284 canvas.insertReorderBarrier(true); 1285 drawOrderedNode(&canvas, 6, 2.0f); 1286 drawOrderedRect(&canvas, 3); 1287 drawOrderedNode(&canvas, 4, 0.0f); 1288 drawOrderedRect(&canvas, 5); 1289 drawOrderedNode(&canvas, 2, -2.0f); 1290 drawOrderedNode(&canvas, 7, 2.0f); 1291 canvas.insertReorderBarrier(false); 1292 drawOrderedRect(&canvas, 8); 1293 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 1294 }); 1295 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 1296 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1297 ZReorderTestRenderer renderer; 1298 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1299 EXPECT_EQ(10, renderer.getIndex()); 1300}; 1301 1302RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { 1303 static const int scrollX = 5; 1304 static const int scrollY = 10; 1305 class ProjectionReorderTestRenderer : public TestRendererBase { 1306 public: 1307 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1308 const int index = mIndex++; 1309 1310 Matrix4 expectedMatrix; 1311 switch (index) { 1312 case 0: 1313 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 1314 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1315 expectedMatrix.loadIdentity(); 1316 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1317 break; 1318 case 1: 1319 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 1320 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1321 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); 1322 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1323 EXPECT_EQ(Rect(-35, -30, 45, 50), 1324 Rect(state.computedState.localProjectionPathMask->getBounds())); 1325 break; 1326 case 2: 1327 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 1328 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 1329 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 1330 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1331 break; 1332 default: 1333 ADD_FAILURE(); 1334 } 1335 EXPECT_EQ(expectedMatrix, state.computedState.transform); 1336 } 1337 }; 1338 1339 /** 1340 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 1341 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 1342 * draw, but because it is projected backwards, it's drawn in between B and C. 1343 * 1344 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 1345 * (which isn't affected by scroll). 1346 */ 1347 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100, 1348 [](RenderProperties& properties, RecordingCanvas& canvas) { 1349 properties.setProjectionReceiver(true); 1350 // scroll doesn't apply to background, so undone via translationX/Y 1351 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1352 properties.setTranslationX(scrollX); 1353 properties.setTranslationY(scrollY); 1354 1355 SkPaint paint; 1356 paint.setColor(SK_ColorWHITE); 1357 canvas.drawRect(0, 0, 100, 100, paint); 1358 }); 1359 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50, 1360 [](RenderProperties& properties, RecordingCanvas& canvas) { 1361 properties.setProjectBackwards(true); 1362 properties.setClipToBounds(false); 1363 SkPaint paint; 1364 paint.setColor(SK_ColorDKGRAY); 1365 canvas.drawRect(-10, -10, 60, 60, paint); 1366 }); 1367 auto child = TestUtils::createNode(0, 50, 100, 100, 1368 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1369 SkPaint paint; 1370 paint.setColor(SK_ColorBLUE); 1371 canvas.drawRect(0, 0, 100, 50, paint); 1372 canvas.drawRenderNode(projectingRipple.get()); 1373 }); 1374 auto parent = TestUtils::createNode(0, 0, 100, 100, 1375 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1376 // Set a rect outline for the projecting ripple to be masked against. 1377 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 1378 1379 canvas.save(SaveFlags::MatrixClip); 1380 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1381 canvas.drawRenderNode(receiverBackground.get()); 1382 canvas.drawRenderNode(child.get()); 1383 canvas.restore(); 1384 }); 1385 1386 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 1387 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1388 ProjectionReorderTestRenderer renderer; 1389 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1390 EXPECT_EQ(3, renderer.getIndex()); 1391} 1392 1393RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { 1394 static const int scrollX = 5; 1395 static const int scrollY = 10; 1396 class ProjectionHwLayerTestRenderer : public TestRendererBase { 1397 public: 1398 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1399 EXPECT_EQ(0, mIndex++); 1400 } 1401 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 1402 EXPECT_EQ(1, mIndex++); 1403 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1404 } 1405 void endLayer() override { 1406 EXPECT_EQ(2, mIndex++); 1407 } 1408 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1409 EXPECT_EQ(3, mIndex++); 1410 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1411 } 1412 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1413 EXPECT_EQ(4, mIndex++); 1414 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1415 Matrix4 expected; 1416 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); 1417 EXPECT_EQ(expected, state.computedState.transform); 1418 EXPECT_EQ(Rect(-85, -80, 295, 300), 1419 Rect(state.computedState.localProjectionPathMask->getBounds())); 1420 } 1421 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1422 EXPECT_EQ(5, mIndex++); 1423 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1424 } 1425 }; 1426 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1427 [](RenderProperties& properties, RecordingCanvas& canvas) { 1428 properties.setProjectionReceiver(true); 1429 // scroll doesn't apply to background, so undone via translationX/Y 1430 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1431 properties.setTranslationX(scrollX); 1432 properties.setTranslationY(scrollY); 1433 1434 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1435 }); 1436 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1437 [](RenderProperties& properties, RecordingCanvas& canvas) { 1438 properties.setProjectBackwards(true); 1439 properties.setClipToBounds(false); 1440 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 1441 }); 1442 auto child = TestUtils::createNode(100, 100, 300, 300, 1443 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1444 properties.mutateLayerProperties().setType(LayerType::RenderLayer); 1445 canvas.drawRenderNode(projectingRipple.get()); 1446 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); 1447 }); 1448 auto parent = TestUtils::createNode(0, 0, 400, 400, 1449 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1450 // Set a rect outline for the projecting ripple to be masked against. 1451 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 1452 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1453 canvas.drawRenderNode(receiverBackground.get()); 1454 canvas.drawRenderNode(child.get()); 1455 }); 1456 1457 OffscreenBuffer** layerHandle = child->getLayerHandle(); 1458 1459 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1460 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1461 Matrix4 windowTransform; 1462 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin 1463 layer.setWindowTransform(windowTransform); 1464 *layerHandle = &layer; 1465 1466 auto syncedList = TestUtils::createSyncedNodeList(parent); 1467 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1468 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); 1469 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 1470 syncedList, sLightGeometry, Caches::getInstance()); 1471 ProjectionHwLayerTestRenderer renderer; 1472 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1473 EXPECT_EQ(6, renderer.getIndex()); 1474 1475 // clean up layer pointer, so we can safely destruct RenderNode 1476 *layerHandle = nullptr; 1477} 1478 1479RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { 1480 static const int scrollX = 500000; 1481 static const int scrollY = 0; 1482 class ProjectionChildScrollTestRenderer : public TestRendererBase { 1483 public: 1484 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1485 EXPECT_EQ(0, mIndex++); 1486 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1487 } 1488 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1489 EXPECT_EQ(1, mIndex++); 1490 ASSERT_NE(nullptr, state.computedState.clipState); 1491 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1492 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect); 1493 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1494 } 1495 }; 1496 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1497 [](RenderProperties& properties, RecordingCanvas& canvas) { 1498 properties.setProjectionReceiver(true); 1499 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1500 }); 1501 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1502 [](RenderProperties& properties, RecordingCanvas& canvas) { 1503 // scroll doesn't apply to background, so undone via translationX/Y 1504 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1505 properties.setTranslationX(scrollX); 1506 properties.setTranslationY(scrollY); 1507 properties.setProjectBackwards(true); 1508 properties.setClipToBounds(false); 1509 canvas.drawOval(0, 0, 200, 200, SkPaint()); 1510 }); 1511 auto child = TestUtils::createNode(0, 0, 400, 400, 1512 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1513 // Record time clip will be ignored by projectee 1514 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op); 1515 1516 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1517 canvas.drawRenderNode(projectingRipple.get()); 1518 }); 1519 auto parent = TestUtils::createNode(0, 0, 400, 400, 1520 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1521 canvas.drawRenderNode(receiverBackground.get()); 1522 canvas.drawRenderNode(child.get()); 1523 }); 1524 1525 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 1526 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1527 ProjectionChildScrollTestRenderer renderer; 1528 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1529 EXPECT_EQ(2, renderer.getIndex()); 1530} 1531 1532// creates a 100x100 shadow casting node with provided translationZ 1533static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1534 return TestUtils::createNode(0, 0, 100, 100, 1535 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1536 properties.setTranslationZ(translationZ); 1537 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1538 SkPaint paint; 1539 paint.setColor(SK_ColorWHITE); 1540 canvas.drawRect(0, 0, 100, 100, paint); 1541 }); 1542} 1543 1544RENDERTHREAD_TEST(FrameBuilder, shadow) { 1545 class ShadowTestRenderer : public TestRendererBase { 1546 public: 1547 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1548 EXPECT_EQ(0, mIndex++); 1549 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1550 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr)); 1551 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY); 1552 1553 Matrix4 expectedZ; 1554 expectedZ.loadTranslate(0, 0, 5); 1555 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ); 1556 } 1557 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1558 EXPECT_EQ(1, mIndex++); 1559 } 1560 }; 1561 1562 auto parent = TestUtils::createNode(0, 0, 200, 200, 1563 [](RenderProperties& props, RecordingCanvas& canvas) { 1564 canvas.insertReorderBarrier(true); 1565 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1566 }); 1567 1568 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1569 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1570 ShadowTestRenderer renderer; 1571 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1572 EXPECT_EQ(2, renderer.getIndex()); 1573} 1574 1575RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { 1576 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1577 public: 1578 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1579 EXPECT_EQ(0, mIndex++); 1580 return nullptr; 1581 } 1582 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1583 EXPECT_EQ(1, mIndex++); 1584 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1585 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1586 } 1587 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1588 EXPECT_EQ(2, mIndex++); 1589 } 1590 void endLayer() override { 1591 EXPECT_EQ(3, mIndex++); 1592 } 1593 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1594 EXPECT_EQ(4, mIndex++); 1595 } 1596 }; 1597 1598 auto parent = TestUtils::createNode(0, 0, 200, 200, 1599 [](RenderProperties& props, RecordingCanvas& canvas) { 1600 // save/restore outside of reorderBarrier, so they don't get moved out of place 1601 canvas.translate(20, 10); 1602 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); 1603 canvas.insertReorderBarrier(true); 1604 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1605 canvas.insertReorderBarrier(false); 1606 canvas.restoreToCount(count); 1607 }); 1608 1609 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1610 TestUtils::createSyncedNodeList(parent), 1611 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1612 ShadowSaveLayerTestRenderer renderer; 1613 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1614 EXPECT_EQ(5, renderer.getIndex()); 1615} 1616 1617RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { 1618 class ShadowHwLayerTestRenderer : public TestRendererBase { 1619 public: 1620 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1621 EXPECT_EQ(0, mIndex++); 1622 } 1623 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1624 EXPECT_EQ(1, mIndex++); 1625 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1626 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1627 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius); 1628 } 1629 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1630 EXPECT_EQ(2, mIndex++); 1631 } 1632 void endLayer() override { 1633 EXPECT_EQ(3, mIndex++); 1634 } 1635 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1636 EXPECT_EQ(4, mIndex++); 1637 } 1638 }; 1639 1640 auto parent = TestUtils::createNode(50, 60, 150, 160, 1641 [](RenderProperties& props, RecordingCanvas& canvas) { 1642 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1643 canvas.insertReorderBarrier(true); 1644 canvas.save(SaveFlags::MatrixClip); 1645 canvas.translate(20, 10); 1646 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1647 canvas.restore(); 1648 }); 1649 OffscreenBuffer** layerHandle = parent->getLayerHandle(); 1650 1651 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1652 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1653 Matrix4 windowTransform; 1654 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin 1655 layer.setWindowTransform(windowTransform); 1656 *layerHandle = &layer; 1657 1658 auto syncedList = TestUtils::createSyncedNodeList(parent); 1659 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1660 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); 1661 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1662 syncedList, 1663 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance()); 1664 ShadowHwLayerTestRenderer renderer; 1665 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1666 EXPECT_EQ(5, renderer.getIndex()); 1667 1668 // clean up layer pointer, so we can safely destruct RenderNode 1669 *layerHandle = nullptr; 1670} 1671 1672RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { 1673 class ShadowLayeringTestRenderer : public TestRendererBase { 1674 public: 1675 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1676 int index = mIndex++; 1677 EXPECT_TRUE(index == 0 || index == 1); 1678 } 1679 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1680 int index = mIndex++; 1681 EXPECT_TRUE(index == 2 || index == 3); 1682 } 1683 }; 1684 auto parent = TestUtils::createNode(0, 0, 200, 200, 1685 [](RenderProperties& props, RecordingCanvas& canvas) { 1686 canvas.insertReorderBarrier(true); 1687 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1688 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 1689 }); 1690 1691 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1692 TestUtils::createSyncedNodeList(parent), 1693 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1694 ShadowLayeringTestRenderer renderer; 1695 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1696 EXPECT_EQ(4, renderer.getIndex()); 1697} 1698 1699static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, 1700 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 1701 class PropertyTestRenderer : public TestRendererBase { 1702 public: 1703 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) 1704 : mCallback(callback) {} 1705 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1706 EXPECT_EQ(mIndex++, 0); 1707 mCallback(op, state); 1708 } 1709 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 1710 }; 1711 1712 auto node = TestUtils::createNode(0, 0, 100, 100, 1713 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 1714 propSetupCallback(props); 1715 SkPaint paint; 1716 paint.setColor(SK_ColorWHITE); 1717 canvas.drawRect(0, 0, 100, 100, paint); 1718 }); 1719 1720 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, 1721 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 1722 PropertyTestRenderer renderer(opValidateCallback); 1723 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1724 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 1725} 1726 1727RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 1728 testProperty([](RenderProperties& properties) { 1729 properties.setAlpha(0.5f); 1730 properties.setHasOverlappingRendering(false); 1731 }, [](const RectOp& op, const BakedOpState& state) { 1732 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 1733 }); 1734} 1735 1736RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { 1737 testProperty([](RenderProperties& properties) { 1738 properties.setClipToBounds(true); 1739 properties.setClipBounds(Rect(10, 20, 300, 400)); 1740 }, [](const RectOp& op, const BakedOpState& state) { 1741 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 1742 << "Clip rect should be intersection of node bounds and clip bounds"; 1743 }); 1744} 1745 1746RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { 1747 testProperty([](RenderProperties& properties) { 1748 properties.mutableRevealClip().set(true, 50, 50, 25); 1749 }, [](const RectOp& op, const BakedOpState& state) { 1750 ASSERT_NE(nullptr, state.roundRectClipState); 1751 EXPECT_TRUE(state.roundRectClipState->highPriority); 1752 EXPECT_EQ(25, state.roundRectClipState->radius); 1753 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 1754 }); 1755} 1756 1757RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { 1758 testProperty([](RenderProperties& properties) { 1759 properties.mutableOutline().setShouldClip(true); 1760 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 1761 }, [](const RectOp& op, const BakedOpState& state) { 1762 ASSERT_NE(nullptr, state.roundRectClipState); 1763 EXPECT_FALSE(state.roundRectClipState->highPriority); 1764 EXPECT_EQ(5, state.roundRectClipState->radius); 1765 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 1766 }); 1767} 1768 1769RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) { 1770 testProperty([](RenderProperties& properties) { 1771 properties.setLeftTopRightBottom(10, 10, 110, 110); 1772 1773 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 1774 properties.setStaticMatrix(&staticMatrix); 1775 1776 // ignored, since static overrides animation 1777 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 1778 properties.setAnimationMatrix(&animationMatrix); 1779 1780 properties.setTranslationX(10); 1781 properties.setTranslationY(20); 1782 properties.setScaleX(0.5f); 1783 properties.setScaleY(0.7f); 1784 }, [](const RectOp& op, const BakedOpState& state) { 1785 Matrix4 matrix; 1786 matrix.loadTranslate(10, 10, 0); // left, top 1787 matrix.scale(1.2f, 1.2f, 1); // static matrix 1788 // ignore animation matrix, since static overrides it 1789 1790 // translation xy 1791 matrix.translate(10, 20); 1792 1793 // scale xy (from default pivot - center) 1794 matrix.translate(50, 50); 1795 matrix.scale(0.5f, 0.7f, 1); 1796 matrix.translate(-50, -50); 1797 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 1798 << "Op draw matrix must match expected combination of transformation properties"; 1799 }); 1800} 1801 1802struct SaveLayerAlphaData { 1803 uint32_t layerWidth = 0; 1804 uint32_t layerHeight = 0; 1805 Rect rectClippedBounds; 1806 Matrix4 rectMatrix; 1807}; 1808/** 1809 * Constructs a view to hit the temporary layer alpha property implementation: 1810 * a) 0 < alpha < 1 1811 * b) too big for layer (larger than maxTextureSize) 1812 * c) overlapping rendering content 1813 * returning observed data about layer size and content clip/transform. 1814 * 1815 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 1816 * (for efficiency, and to fit in layer size constraints) based on parent clip. 1817 */ 1818void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 1819 std::function<void(RenderProperties&)> propSetupCallback) { 1820 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 1821 public: 1822 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) 1823 : mOutData(outData) {} 1824 1825 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1826 EXPECT_EQ(0, mIndex++); 1827 mOutData->layerWidth = width; 1828 mOutData->layerHeight = height; 1829 return nullptr; 1830 } 1831 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1832 EXPECT_EQ(1, mIndex++); 1833 1834 mOutData->rectClippedBounds = state.computedState.clippedBounds; 1835 mOutData->rectMatrix = state.computedState.transform; 1836 } 1837 void endLayer() override { 1838 EXPECT_EQ(2, mIndex++); 1839 } 1840 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1841 EXPECT_EQ(3, mIndex++); 1842 } 1843 private: 1844 SaveLayerAlphaData* mOutData; 1845 }; 1846 1847 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 1848 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 1849 auto node = TestUtils::createNode(0, 0, 10000, 10000, 1850 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 1851 properties.setHasOverlappingRendering(true); 1852 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 1853 // apply other properties 1854 propSetupCallback(properties); 1855 1856 SkPaint paint; 1857 paint.setColor(SK_ColorWHITE); 1858 canvas.drawRect(0, 0, 10000, 10000, paint); 1859 }); 1860 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height 1861 1862 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1863 nodes, sLightGeometry, Caches::getInstance()); 1864 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 1865 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1866 1867 // assert, since output won't be valid if we haven't seen a save layer triggered 1868 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 1869} 1870 1871RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 1872 SaveLayerAlphaData observedData; 1873 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1874 properties.setTranslationX(10); // offset rendering content 1875 properties.setTranslationY(-2000); // offset rendering content 1876 }); 1877 EXPECT_EQ(190u, observedData.layerWidth); 1878 EXPECT_EQ(200u, observedData.layerHeight); 1879 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 1880 << "expect content to be clipped to screen area"; 1881 Matrix4 expected; 1882 expected.loadTranslate(0, -2000, 0); 1883 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 1884 << "expect content to be translated as part of being clipped"; 1885} 1886 1887RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 1888 SaveLayerAlphaData observedData; 1889 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1890 // Translate and rotate the view so that the only visible part is the top left corner of 1891 // the view. It will form an isosceles right triangle with a long side length of 200 at the 1892 // bottom of the viewport. 1893 properties.setTranslationX(100); 1894 properties.setTranslationY(100); 1895 properties.setPivotX(0); 1896 properties.setPivotY(0); 1897 properties.setRotation(45); 1898 }); 1899 // ceil(sqrt(2) / 2 * 200) = 142 1900 EXPECT_EQ(142u, observedData.layerWidth); 1901 EXPECT_EQ(142u, observedData.layerHeight); 1902 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 1903 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1904} 1905 1906RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 1907 SaveLayerAlphaData observedData; 1908 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1909 properties.setPivotX(0); 1910 properties.setPivotY(0); 1911 properties.setScaleX(2); 1912 properties.setScaleY(0.5f); 1913 }); 1914 EXPECT_EQ(100u, observedData.layerWidth); 1915 EXPECT_EQ(400u, observedData.layerHeight); 1916 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 1917 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1918} 1919 1920} // namespace uirenderer 1921} // namespace android 1922