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