FrameBuilderTests.cpp revision 919645aded88b7d9f82b6f05777cbe827f415d1a
18ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis/* 28ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Copyright (C) 2016 The Android Open Source Project 38ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * 48ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Licensed under the Apache License, Version 2.0 (the "License"); 58ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * you may not use this file except in compliance with the License. 68ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * You may obtain a copy of the License at 78ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * 88ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * http://www.apache.org/licenses/LICENSE-2.0 98ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * 108ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Unless required by applicable law or agreed to in writing, software 118ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * distributed under the License is distributed on an "AS IS" BASIS, 128ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * See the License for the specific language governing permissions and 148ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * limitations under the License. 158ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis */ 168ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis 172adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden#include <gtest/gtest.h> 182adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden 198ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <BakedOpState.h> 208ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <DeferredLayerUpdater.h> 218ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <FrameBuilder.h> 228ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <LayerUpdateQueue.h> 238ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <RecordedOp.h> 248ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <RecordingCanvas.h> 258ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <tests/common/TestUtils.h> 268ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis 278ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <unordered_map> 28f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall 298ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennisnamespace android { 308ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennisnamespace uirenderer { 315065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza 328ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennisconst FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; 338ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis 348ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis/** 358ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Virtual class implemented by each test to redirect static operation / state transitions to 36f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza * virtual methods. 37399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall * 38e3c697fb929c856b59fa56a8e05a2a7eba187c3dMathias Agopian * Virtual dispatch allows for default behaviors to be specified (very common case in below tests), 39eafabcdc1639fb96062d9e3c39b0ae27b0238ae1Mathias Agopian * and allows Renderer vs Dispatching behavior to be merged. 400273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFadden * 41466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden * onXXXOp methods fail by default - tests should override ops they expect 42466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden * startRepaintLayer fails by default - tests should override if expected 43466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden * startFrame/endFrame do nothing by default - tests should override to intercept 44466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden */ 45e3c697fb929c856b59fa56a8e05a2a7eba187c3dMathias Agopianclass TestRendererBase { 460273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFaddenpublic: 47466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden virtual ~TestRendererBase() {} 48466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) { 49466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden ADD_FAILURE() << "Temporary layers not expected in this test"; 50466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden return nullptr; 510273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFadden } 522adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden virtual void recycleTemporaryLayer(OffscreenBuffer*) { 530273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFadden ADD_FAILURE() << "Temporary layers not expected in this test"; 542adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden } 558ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) { 568ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis ADD_FAILURE() << "Layer repaint not expected in this test"; 572adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden } 588ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis virtual void endLayer() { 598072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian ADD_FAILURE() << "Layer updates not expected in this test"; 607d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin } 617d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {} 628072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian virtual void endFrame(const Rect& repaintRect) {} 637d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 647d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // define virtual defaults for single draw methods 658072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian#define X(Type) \ 668072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian virtual void on##Type(const Type&, const BakedOpState&) { \ 67a5c75c01620179ce00812354778a29a80d76e71fMathias Agopian ADD_FAILURE() << #Type " not expected in this test"; \ 688ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis } 692adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden MAP_RENDERABLE_OPS(X) 702adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden#undef X 718ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis 728ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis // define virtual defaults for merged draw methods 737d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin#define X(Type) \ 747d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \ 757d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \ 767d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin } 77583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos MAP_MERGEABLE_OPS(X) 78583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos#undef X 797d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 807d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin int getIndex() { return mIndex; } 817d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 827b305fffc39d0fe0926e7fd2d7f6a524fbce62b7Jamie Gennisprotected: 838ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis int mIndex = 0; 84fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos}; 8572daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos 8672daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos/** 8772daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos * Dispatches all static methods to similar formed methods on renderer, which fail by default but 8872daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos * are overridden by subclasses per test. 89fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos */ 90fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballosclass TestDispatcher { 91fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballospublic: 9272daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos // define single op methods, which redirect to TestRendererBase 9372daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos#define X(Type) \ 9472daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \ 95fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos renderer.on##Type(op, state); \ 96fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos } 97fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos MAP_RENDERABLE_OPS(X); 98fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos#undef X 99fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos 100fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos // define merged op methods, which redirect to TestRendererBase 101fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos#define X(Type) \ 102fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \ 103fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos renderer.onMerged##Type##s(opList); \ 10472daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos } 10572daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos MAP_MERGEABLE_OPS(X); 10672daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos#undef X 10772daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos}; 10823b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos 109fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballosclass FailRenderer : public TestRendererBase {}; 110fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos 111fa455354557f6283ff3a7d76979e52fd251c155fPablo CeballosRENDERTHREAD_TEST(FrameBuilder, simple) { 112fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos class SimpleTestRenderer : public TestRendererBase { 113fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos public: 114fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 115fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos EXPECT_EQ(0, mIndex++); 116fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos EXPECT_EQ(100u, width); 117fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos EXPECT_EQ(200u, height); 118fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos } 119fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos void onRectOp(const RectOp& op, const BakedOpState& state) override { 120fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos EXPECT_EQ(1, mIndex++); 12123b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos } 12223b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 123b687a2814ca9db576eb1ea33dea90ac35cd61bc1Pablo Ceballos EXPECT_EQ(2, mIndex++); 12423b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos } 125fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos void endFrame(const Rect& repaintRect) override { 126fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos EXPECT_EQ(3, mIndex++); 1278ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis } 1288ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis }; 1297d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 1307d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin auto node = TestUtils::createNode(0, 0, 100, 200, 1317d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin [](RenderProperties& props, RecordingCanvas& canvas) { 1327d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin SkBitmap bitmap = TestUtils::createSkBitmap(25, 25); 1337d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.drawRect(0, 0, 100, 200, SkPaint()); 1347d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.drawBitmap(bitmap, 10, 10, nullptr); 1357d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }); 1367d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, 1377d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin sLightGeometry, Caches::getInstance()); 1387d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1397d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 1407d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin SimpleTestRenderer renderer; 141a5c75c01620179ce00812354778a29a80d76e71fMathias Agopian frameBuilder.replayBakedOps<TestDispatcher>(renderer); 142f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end 1437d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin} 1447d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 1457d2d160cdc3cba9f4454f38433c94b68376cb843Igor MurashkinRENDERTHREAD_TEST(FrameBuilder, simpleStroke) { 146f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall class SimpleStrokeTestRenderer : public TestRendererBase { 147f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall public: 1487d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin void onPointsOp(const PointsOp& op, const BakedOpState& state) override { 149f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall EXPECT_EQ(0, mIndex++); 1507cdd786fa80cf03551291ae8feca7b77583be1c5Mathias Agopian // even though initial bounds are empty... 1517d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_TRUE(op.unmappedBounds.isEmpty()) 1527d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin << "initial bounds should be empty, since they're unstroked"; 1537d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds) 1547d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin << "final bounds should account for stroke"; 1557d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin } 1567d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }; 1573be1c6b60a188dc10025e2ce156c11fac050625dDan Stoza 1587d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin auto node = TestUtils::createNode(0, 0, 100, 200, 1597d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin [](RenderProperties& props, RecordingCanvas& canvas) { 1607d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin SkPaint strokedPaint; 1617d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin strokedPaint.setStrokeWidth(10); 1627d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.drawPoint(50, 50, strokedPaint); 1637d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }); 1647d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, 1657d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin sLightGeometry, Caches::getInstance()); 1667d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1677d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 1687d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin SimpleStrokeTestRenderer renderer; 1697d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1707d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(1, renderer.getIndex()); 1717d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin} 172583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos 173583b1b32191992d6ada58b3c61c71932a71c0c4bPablo CeballosRENDERTHREAD_TEST(FrameBuilder, simpleRejection) { 1749f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza auto node = TestUtils::createNode(0, 0, 200, 200, 1759f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza [](RenderProperties& props, RecordingCanvas& canvas) { 1769f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza canvas.save(SaveFlags::MatrixClip); 1779f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty 1789f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza canvas.drawRect(0, 0, 400, 400, SkPaint()); 1799f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza canvas.restore(); 1809f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza }); 1819f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1827d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin sLightGeometry, Caches::getInstance()); 1837d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1847d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 185127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza FailRenderer renderer; 186127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1877d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin} 1887d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 1897d2d160cdc3cba9f4454f38433c94b68376cb843Igor MurashkinRENDERTHREAD_TEST(FrameBuilder, simpleBatching) { 190567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos const int LOOPS = 5; 191567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos class SimpleBatchingTestRenderer : public TestRendererBase { 1928ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis public: 1939f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 1949f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects"; 1959f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza } 1969f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza void onRectOp(const RectOp& op, const BakedOpState& state) override { 1979f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps"; 1989f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza } 1999f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza }; 2009f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza 2019f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza auto node = TestUtils::createNode(0, 0, 200, 200, 2029f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza [](RenderProperties& props, RecordingCanvas& canvas) { 203583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos SkBitmap bitmap = TestUtils::createSkBitmap(10, 10, 204583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap 2059f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza 2069f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. 2079f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. 2089f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza canvas.save(SaveFlags::MatrixClip); 2099f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza for (int i = 0; i < LOOPS; i++) { 210d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza canvas.translate(0, 10); 211d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza canvas.drawRect(0, 0, 10, 10, SkPaint()); 212d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza canvas.drawBitmap(bitmap, 5, 0, nullptr); 213d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza } 214d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza canvas.restore(); 215d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza }); 216d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 217d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza sLightGeometry, Caches::getInstance()); 218d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 219d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza 220d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza SimpleBatchingTestRenderer renderer; 221d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza frameBuilder.replayBakedOps<TestDispatcher>(renderer); 222d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 223d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza << "Expect number of ops = 2 * loop count"; 224583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos} 225583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos 226d9822a3843017444364899afc3c23fb5be6b9cb9Dan StozaRENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) { 227d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase { 228d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza public: 229d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza void onRectOp(const RectOp& op, const BakedOpState& state) override { 230d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza EXPECT_EQ(0, mIndex++); 231d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds); 2329f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, 2339f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza state.computedState.clipSideFlags); 2349f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza } 2359f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza }; 2369f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza 2379f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza auto node = TestUtils::createNode(0, 0, 100, 100, 2389f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza [](RenderProperties& props, RecordingCanvas& canvas) { 2399f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza canvas.drawRect(0, 0, 100, 100, SkPaint()); 2409f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza }); 2419f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza 2429f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2439f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza sLightGeometry, Caches::getInstance()); 244583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node 245583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos *TestUtils::getSyncedNode(node)); 246812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza 247812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza DeferRenderNodeTranslateClipTestRenderer renderer; 248812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2499f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza EXPECT_EQ(1, renderer.getIndex()); 2509f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza} 2519f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza 2529f3053de78630815d60cf48a2cf2348cc5867c45Dan StozaRENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) { 2539f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza class DeferRenderNodeSceneTestRenderer : public TestRendererBase { 2549f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza public: 2559f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza void onRectOp(const RectOp& op, const BakedOpState& state) override { 2569f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza const Rect& clippedBounds = state.computedState.clippedBounds; 2579f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza Matrix4 expected; 258127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza switch (mIndex++) { 259127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza case 0: 2609f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza // background - left side 2619f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds); 2629f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza expected.loadTranslate(100, 100, 0); 2638ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis break; 2648ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis case 1: 2657d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // background - top side 2667d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds); 2677d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin expected.loadTranslate(100, 100, 0); 2687d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin break; 2697d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin case 2: 2707d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // content 2717d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds); 2727d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin expected.loadTranslate(-50, -50, 0); 2731d01a12e7150be569557b64da9b8663c62c13594Eino-Ville Talvala break; 27497c602c5af5f3ffd69009bf496d86347b71a2b4cMathias Agopian case 3: 2757d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // overlay 2767d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds); 2777d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin break; 2787d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin default: 2797cdd786fa80cf03551291ae8feca7b77583be1c5Mathias Agopian ADD_FAILURE() << "Too many rects observed"; 2807d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin } 281583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos EXPECT_EQ(expected, state.computedState.transform); 282583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos } 2837d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }; 2847d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 2857d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin std::vector<sp<RenderNode>> nodes; 2867d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin SkPaint transparentPaint; 2877d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin transparentPaint.setAlpha(128); 2887d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 2897d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // backdrop 2907d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin nodes.push_back(TestUtils::createNode(100, 100, 700, 500, // 600x400 2917d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 292f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian canvas.drawRect(0, 0, 600, 400, transparentPaint); 293e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian })); 294e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian 295c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall // content 2967d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin Rect contentDrawBounds(150, 150, 650, 450); // 500x300 2977d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin nodes.push_back(TestUtils::createNode(0, 0, 800, 600, 29882c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 2997d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.drawRect(0, 0, 800, 600, transparentPaint); 3007d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin })); 3017d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 3027d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // overlay 3037d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin nodes.push_back(TestUtils::createNode(0, 0, 800, 600, 3041681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 3051681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk canvas.drawRect(0, 0, 800, 200, transparentPaint); 3063c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden })); 30782c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala 308567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos for (auto& node : nodes) { 30982c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala TestUtils::syncHierarchyPropertiesAndDisplayList(node); 31082c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala } 311567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos 312567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, 3133c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden sLightGeometry, Caches::getInstance()); 31482c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds); 31582c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala 316567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos DeferRenderNodeSceneTestRenderer renderer; 3171681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk frameBuilder.replayBakedOps<TestDispatcher>(renderer); 318f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian EXPECT_EQ(4, renderer.getIndex()); 3193c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden} 32082c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala 321f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias AgopianRENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { 322f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian class EmptyNoFbo0TestRenderer : public TestRendererBase { 323f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian public: 324c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 3251681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk ADD_FAILURE() << "Primary frame draw not expected in this test"; 3261681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk } 3271681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk void endFrame(const Rect& repaintRect) override { 328f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian ADD_FAILURE() << "Primary frame draw not expected in this test"; 329c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall } 330e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian }; 331e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian 332e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian // Use layer update constructor, so no work is enqueued for Fbo0 333e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian LayerUpdateQueue emptyLayerUpdateQueue; 334e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance()); 335c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall EmptyNoFbo0TestRenderer renderer; 3365065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza frameBuilder.replayBakedOps<TestDispatcher>(renderer); 3375065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza} 3385065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza 339f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias AgopianRENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { 340f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian class EmptyWithFbo0TestRenderer : public TestRendererBase { 3413c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden public: 34282c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 343f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian EXPECT_EQ(0, mIndex++); 344f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian } 345f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian void endFrame(const Rect& repaintRect) override { 3461681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk EXPECT_EQ(1, mIndex++); 347c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall } 3485065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza }; 349f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian auto node = TestUtils::createNode(10, 10, 110, 110, 350f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian [](RenderProperties& props, RecordingCanvas& canvas) { 351f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian // no drawn content 3527d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }); 353f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian 3547d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // Draw, but pass node without draw content, so no work is done for primary frame 3557d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 3567d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin sLightGeometry, Caches::getInstance()); 3577d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 3587d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 359f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian EmptyWithFbo0TestRenderer renderer; 3602488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian frameBuilder.replayBakedOps<TestDispatcher>(renderer); 3612488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," 3622488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian " but fbo0 update lifecycle should still be observed"; 363f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian} 364f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian 365f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias AgopianRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { 3662488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian class AvoidOverdrawRectsTestRenderer : public TestRendererBase { 367f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian public: 368f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian void onRectOp(const RectOp& op, const BakedOpState& state) override { 3692488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian EXPECT_EQ(mIndex++, 0) << "Should be one rect"; 370f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds) 371f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian << "Last rect should occlude others."; 372f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian } 3732488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian }; 374f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian auto node = TestUtils::createNode(0, 0, 200, 200, 375f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian [](RenderProperties& props, RecordingCanvas& canvas) { 376f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian canvas.drawRect(0, 0, 200, 200, SkPaint()); 377f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian canvas.drawRect(0, 0, 200, 200, SkPaint()); 378f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian canvas.drawRect(10, 10, 190, 190, SkPaint()); 3792488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian }); 380f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian 381f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian // Damage (and therefore clip) is same as last draw, subset of renderable area. 382567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos // This means last op occludes other contents, and they'll be rejected to avoid overdraw. 383567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, 3848ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis sLightGeometry, Caches::getInstance()); 3858ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 3868ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis 3878ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) 3887d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin << "Recording must not have rejected ops, in order for this test to be valid"; 3897d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 3907d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin AvoidOverdrawRectsTestRenderer renderer; 391583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos frameBuilder.replayBakedOps<TestDispatcher>(renderer); 392583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op"; 3937d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin} 3947d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 395583b1b32191992d6ada58b3c61c71932a71c0c4bPablo CeballosRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { 396583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50, 397583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos SkColorType::kRGB_565_SkColorType); 398583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50, 399583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos SkColorType::kAlpha_8_SkColorType); 400583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { 401583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos public: 402583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 403583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos switch(mIndex++) { 4048ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis case 0: 405eafabcdc1639fb96062d9e3c39b0ae27b0238ae1Mathias Agopian EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef()); 4067d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin break; 4077d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin case 1: 4087d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef()); 4097d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin break; 4107d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin default: 411eafabcdc1639fb96062d9e3c39b0ae27b0238ae1Mathias Agopian ADD_FAILURE() << "Only two ops expected."; 4128072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian } 4132adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden } 4142adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden }; 4157d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 416fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis auto node = TestUtils::createNode(0, 0, 50, 50, 417fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis [](RenderProperties& props, RecordingCanvas& canvas) { 4182adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden canvas.drawRect(0, 0, 50, 50, SkPaint()); 4195bfc24515bb5c8ea7975f72d538df37753733a2fMathias Agopian canvas.drawRect(0, 0, 50, 50, SkPaint()); 420f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza canvas.drawBitmap(transpBitmap, 0, 0, nullptr); 421f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza 422f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza // only the below draws should remain, since they're 423f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr); 424f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza canvas.drawBitmap(transpBitmap, 0, 0, nullptr); 4257d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }); 4267d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, 4277d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin sLightGeometry, Caches::getInstance()); 4287d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 4297d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 4307d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) 4317d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin << "Recording must not have rejected ops, in order for this test to be valid"; 4327d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 4337d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin AvoidOverdrawBitmapsTestRenderer renderer; 4347d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin frameBuilder.replayBakedOps<TestDispatcher>(renderer); 4357d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops"; 4367d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin} 4377d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 4387d2d160cdc3cba9f4454f38433c94b68376cb843Igor MurashkinRENDERTHREAD_TEST(FrameBuilder, clippedMerging) { 4397d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin class ClippedMergingTestRenderer : public TestRendererBase { 4407d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin public: 4417d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin void onMergedBitmapOps(const MergedBakedOpList& opList) override { 4427d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin EXPECT_EQ(0, mIndex); 4437ea777f097784492f880623067becac1b276f884Igor Murashkin mIndex += opList.count; 44423b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos EXPECT_EQ(4u, opList.count); 44523b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip); 44623b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right, 4477d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin opList.clipSideFlags); 4487d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin } 4497d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }; 4507d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin auto node = TestUtils::createNode(0, 0, 100, 100, 451f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza [](RenderProperties& props, TestCanvas& canvas) { 452365857df8b94c959dea984a63013f6e7730ef976Mathias Agopian SkBitmap bitmap = TestUtils::createSkBitmap(20, 20); 453fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis 4542adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden // left side clipped (to inset left half) 4552adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op); 4562adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden canvas.drawBitmap(bitmap, 0, 40, nullptr); 4572adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden 4582adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden // top side clipped (to inset top half) 459fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op); 4602adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden canvas.drawBitmap(bitmap, 40, 0, nullptr); 461fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis 4627d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // right side clipped (to inset right half) 4637d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op); 4647d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.drawBitmap(bitmap, 80, 40, nullptr); 4657d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 4667d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin // bottom not clipped, just abutting (inset bottom half) 4677d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op); 4687d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin canvas.drawBitmap(bitmap, 40, 70, nullptr); 4697d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin }); 4707d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin 4717d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 4727d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin sLightGeometry, Caches::getInstance()); 473fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 474399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall 475399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall ClippedMergingTestRenderer renderer; 476399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall frameBuilder.replayBakedOps<TestDispatcher>(renderer); 477399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall EXPECT_EQ(4, renderer.getIndex()); 478399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall} 479399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall 480399184a4cd728ea1421fb0bc1722274a29e38f4aJesse HallRENDERTHREAD_TEST(FrameBuilder, textMerging) { 481399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall class TextMergingTestRenderer : public TestRendererBase { 482399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall public: 483399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall void onMergedTextOps(const MergedBakedOpList& opList) override { 484399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall EXPECT_EQ(0, mIndex); 485399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall mIndex += opList.count; 48629a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza EXPECT_EQ(2u, opList.count); 48729a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags); 48829a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags); 48929a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags); 49029a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza } 49129a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza }; 49229a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza auto node = TestUtils::createNode(0, 0, 400, 400, 49329a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza [](RenderProperties& props, TestCanvas& canvas) { 49429a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza SkPaint paint; 49529a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 49629a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza paint.setAntiAlias(true); 497567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos paint.setTextSize(50); 4983be1c6b60a188dc10025e2ce156c11fac050625dDan Stoza TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped 4999de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped 5009de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza }); 5019de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 5029de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza sLightGeometry, Caches::getInstance()); 5039de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 5049de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza 5059de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza TextMergingTestRenderer renderer; 5069de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza frameBuilder.replayBakedOps<TestDispatcher>(renderer); 5079de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; 5089de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza} 5099de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza 5109de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan StozaRENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { 511812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza const int LOOPS = 5; 512812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza class TextStrikethroughTestRenderer : public TestRendererBase { 513812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza public: 514812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza void onRectOp(const RectOp& op, const BakedOpState& state) override { 515812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text"; 516812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza } 517812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza void onMergedTextOps(const MergedBakedOpList& opList) override { 518812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza EXPECT_EQ(0, mIndex); 519812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza mIndex += opList.count; 520c6f30bdee1f634eb90d68cb76efe935b6535a1e8Dan Stoza EXPECT_EQ(5u, opList.count); 521c6f30bdee1f634eb90d68cb76efe935b6535a1e8Dan Stoza } 522c6f30bdee1f634eb90d68cb76efe935b6535a1e8Dan Stoza }; 5237dde599bf1a0dbef7390d91c2689d506371cdbd7Dan Stoza auto node = TestUtils::createNode(0, 0, 200, 2000, 5247dde599bf1a0dbef7390d91c2689d506371cdbd7Dan Stoza [](RenderProperties& props, RecordingCanvas& canvas) { 5257dde599bf1a0dbef7390d91c2689d506371cdbd7Dan Stoza SkPaint textPaint; 526ccdfd60d79a8b7f1ed6401d0f2e8e29166a10584Pablo Ceballos textPaint.setAntiAlias(true); 5273559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos textPaint.setTextSize(20); 528ccdfd60d79a8b7f1ed6401d0f2e8e29166a10584Pablo Ceballos textPaint.setStrikeThruText(true); 5293559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 530295a9fc8aa87daa2cded5c1a279b8cd24e9a9a9fPablo Ceballos for (int i = 0; i < LOOPS; i++) { 531295a9fc8aa87daa2cded5c1a279b8cd24e9a9a9fPablo Ceballos TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); 532295a9fc8aa87daa2cded5c1a279b8cd24e9a9a9fPablo Ceballos } 5333559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos }); 534127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza 535ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, 536ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos sLightGeometry, Caches::getInstance()); 5373559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 538ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos 539ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos TextStrikethroughTestRenderer renderer; 540ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos frameBuilder.replayBakedOps<TestDispatcher>(renderer); 541ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 542ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos << "Expect number of ops = 2 * loop count"; 543127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza} 544127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza 545127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stozastatic auto styles = { 546127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style }; 547127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza 548127fc63e8a15366b4395f1363e8e18eb058d1709Dan StozaRENDERTHREAD_TEST(FrameBuilder, textStyle) { 549127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza class TextStyleTestRenderer : public TestRendererBase { 550127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza public: 551127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza void onMergedTextOps(const MergedBakedOpList& opList) override { 552127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza ASSERT_EQ(0, mIndex); 553127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza ASSERT_EQ(3u, opList.count); 55423b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos mIndex += opList.count; 55523b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos 55623b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos int index = 0; 55723b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos for (auto style : styles) { 55823b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos auto state = opList.states[index++]; 559127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza ASSERT_EQ(style, state->op->paint->getStyle()) 5608ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis << "Remainder of validation relies upon stable merged order"; 5618ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis ASSERT_EQ(0, state->computedState.clipSideFlags) 5628ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis << "Clipped bounds validation requires unclipped ops"; 5638ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis } 5642adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden 5658ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis Rect fill = opList.states[0]->computedState.clippedBounds; 5668ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis Rect stroke = opList.states[1]->computedState.clippedBounds; 5678ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds) 5688ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis << "Stroke+Fill should be same as stroke"; 5698ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis 5708ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis EXPECT_TRUE(stroke.contains(fill)); 5718ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis EXPECT_FALSE(fill.contains(stroke)); 5728ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis 5738ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis // outset by half the stroke width 5748ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis Rect outsetFill(fill); 5758ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis outsetFill.outset(5); 5762adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden 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_round) { 1011 class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase { 1012 public: 1013 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1014 EXPECT_EQ(0, mIndex++); 1015 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds) 1016 << "Bounds rect should round out"; 1017 } 1018 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {} 1019 void onRectOp(const RectOp& op, const BakedOpState& state) override {} 1020 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1021 EXPECT_EQ(1, mIndex++); 1022 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds) 1023 << "Bounds rect should round out"; 1024 } 1025 }; 1026 1027 auto node = TestUtils::createNode(0, 0, 200, 200, 1028 [](RenderProperties& props, RecordingCanvas& canvas) { 1029 canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out 1030 128, (SaveFlags::Flags)(0)); 1031 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1032 canvas.restore(); 1033 }); 1034 1035 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1036 sLightGeometry, Caches::getInstance()); 1037 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1038 1039 SaveLayerUnclippedRoundTestRenderer renderer; 1040 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1041 EXPECT_EQ(2, renderer.getIndex()); 1042} 1043 1044RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { 1045 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase { 1046 public: 1047 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1048 int index = mIndex++; 1049 EXPECT_GT(4, index); 1050 EXPECT_EQ(5, op.unmappedBounds.getWidth()); 1051 EXPECT_EQ(5, op.unmappedBounds.getHeight()); 1052 if (index == 0) { 1053 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds); 1054 } else if (index == 1) { 1055 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds); 1056 } else if (index == 2) { 1057 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds); 1058 } else if (index == 3) { 1059 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds); 1060 } 1061 } 1062 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1063 EXPECT_EQ(4, mIndex++); 1064 ASSERT_EQ(op.vertexCount, 16u); 1065 for (size_t i = 0; i < op.vertexCount; i++) { 1066 auto v = op.vertices[i]; 1067 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200); 1068 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200); 1069 } 1070 } 1071 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1072 EXPECT_EQ(5, mIndex++); 1073 } 1074 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1075 EXPECT_LT(5, mIndex++); 1076 } 1077 }; 1078 1079 auto node = TestUtils::createNode(0, 0, 200, 200, 1080 [](RenderProperties& props, RecordingCanvas& canvas) { 1081 1082 int restoreTo = canvas.save(SaveFlags::MatrixClip); 1083 canvas.scale(2, 2); 1084 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); 1085 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); 1086 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); 1087 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); 1088 canvas.drawRect(0, 0, 100, 100, SkPaint()); 1089 canvas.restoreToCount(restoreTo); 1090 }); 1091 1092 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1093 sLightGeometry, Caches::getInstance()); 1094 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1095 1096 SaveLayerUnclippedMergedClearsTestRenderer renderer; 1097 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1098 EXPECT_EQ(10, renderer.getIndex()) 1099 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect."; 1100} 1101 1102RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { 1103 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase { 1104 public: 1105 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1106 EXPECT_EQ(0, mIndex++); 1107 } 1108 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1109 EXPECT_EQ(1, mIndex++); 1110 ASSERT_NE(nullptr, op.paint); 1111 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); 1112 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds) 1113 << "Expect dirty rect as clip"; 1114 ASSERT_NE(nullptr, state.computedState.clipState); 1115 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect); 1116 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1117 } 1118 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1119 EXPECT_EQ(2, mIndex++); 1120 } 1121 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1122 EXPECT_EQ(3, mIndex++); 1123 } 1124 }; 1125 1126 auto node = TestUtils::createNode(0, 0, 200, 200, 1127 [](RenderProperties& props, RecordingCanvas& canvas) { 1128 // save smaller than clip, so we get unclipped behavior 1129 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 1130 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1131 canvas.restore(); 1132 }); 1133 1134 // draw with partial screen dirty, and assert we see that rect later 1135 FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, 1136 sLightGeometry, Caches::getInstance()); 1137 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1138 1139 SaveLayerUnclippedClearClipTestRenderer renderer; 1140 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1141 EXPECT_EQ(4, renderer.getIndex()); 1142} 1143 1144RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { 1145 auto node = TestUtils::createNode(0, 0, 200, 200, 1146 [](RenderProperties& props, RecordingCanvas& canvas) { 1147 // unclipped savelayer + rect both in area that won't intersect with dirty 1148 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); 1149 canvas.drawRect(100, 100, 200, 200, SkPaint()); 1150 canvas.restore(); 1151 }); 1152 1153 // draw with partial screen dirty that doesn't intersect with savelayer 1154 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, 1155 sLightGeometry, Caches::getInstance()); 1156 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1157 1158 FailRenderer renderer; 1159 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1160} 1161 1162/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as: 1163 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer 1164 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe 1165 */ 1166RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { 1167 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { 1168 public: 1169 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1170 EXPECT_EQ(0, mIndex++); // savelayer first 1171 return (OffscreenBuffer*)0xabcd; 1172 } 1173 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1174 int index = mIndex++; 1175 EXPECT_TRUE(index == 1 || index == 7); 1176 } 1177 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1178 int index = mIndex++; 1179 EXPECT_TRUE(index == 2 || index == 8); 1180 } 1181 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1182 EXPECT_EQ(3, mIndex++); 1183 Matrix4 expected; 1184 expected.loadTranslate(-100, -100, 0); 1185 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds); 1186 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 1187 } 1188 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1189 int index = mIndex++; 1190 EXPECT_TRUE(index == 4 || index == 10); 1191 } 1192 void endLayer() override { 1193 EXPECT_EQ(5, mIndex++); 1194 } 1195 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1196 EXPECT_EQ(6, mIndex++); 1197 } 1198 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1199 EXPECT_EQ(9, mIndex++); 1200 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1201 } 1202 void endFrame(const Rect& repaintRect) override { 1203 EXPECT_EQ(11, mIndex++); 1204 } 1205 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1206 EXPECT_EQ(12, mIndex++); 1207 EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer); 1208 } 1209 }; 1210 1211 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping 1212 [](RenderProperties& props, RecordingCanvas& canvas) { 1213 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped 1214 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped 1215 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped 1216 canvas.drawRect(200, 200, 300, 300, SkPaint()); 1217 canvas.restore(); 1218 canvas.restore(); 1219 canvas.restore(); 1220 }); 1221 1222 FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, 1223 sLightGeometry, Caches::getInstance()); 1224 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1225 1226 SaveLayerUnclippedComplexTestRenderer renderer; 1227 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1228 EXPECT_EQ(13, renderer.getIndex()); 1229} 1230 1231RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { 1232 class HwLayerSimpleTestRenderer : public TestRendererBase { 1233 public: 1234 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1235 EXPECT_EQ(0, mIndex++); 1236 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1237 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1238 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1239 } 1240 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1241 EXPECT_EQ(1, mIndex++); 1242 1243 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1244 << "Transform should be reset within layer"; 1245 1246 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1247 << "Damage rect should be used to clip layer content"; 1248 } 1249 void endLayer() override { 1250 EXPECT_EQ(2, mIndex++); 1251 } 1252 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1253 EXPECT_EQ(3, mIndex++); 1254 } 1255 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1256 EXPECT_EQ(4, mIndex++); 1257 } 1258 void endFrame(const Rect& repaintRect) override { 1259 EXPECT_EQ(5, mIndex++); 1260 } 1261 }; 1262 1263 auto node = TestUtils::createNode(10, 10, 110, 110, 1264 [](RenderProperties& props, RecordingCanvas& canvas) { 1265 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1266 SkPaint paint; 1267 paint.setColor(SK_ColorWHITE); 1268 canvas.drawRect(0, 0, 100, 100, paint); 1269 }); 1270 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1271 1272 // create RenderNode's layer here in same way prepareTree would 1273 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1274 *layerHandle = &layer; 1275 1276 auto syncedNode = TestUtils::getSyncedNode(node); 1277 1278 // only enqueue partial damage 1279 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1280 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1281 1282 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1283 sLightGeometry, Caches::getInstance()); 1284 frameBuilder.deferLayers(layerUpdateQueue); 1285 frameBuilder.deferRenderNode(*syncedNode); 1286 1287 HwLayerSimpleTestRenderer renderer; 1288 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1289 EXPECT_EQ(6, renderer.getIndex()); 1290 1291 // clean up layer pointer, so we can safely destruct RenderNode 1292 *layerHandle = nullptr; 1293} 1294 1295RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { 1296 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: 1297 * - startRepaintLayer(child), rect(grey), endLayer 1298 * - startTemporaryLayer, drawLayer(child), endLayer 1299 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer 1300 * - startFrame, drawLayer(parent), endLayerb 1301 */ 1302 class HwLayerComplexTestRenderer : public TestRendererBase { 1303 public: 1304 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1305 EXPECT_EQ(3, mIndex++); // savelayer first 1306 return (OffscreenBuffer*)0xabcd; 1307 } 1308 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1309 int index = mIndex++; 1310 if (index == 0) { 1311 // starting inner layer 1312 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1313 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1314 } else if (index == 6) { 1315 // starting outer layer 1316 EXPECT_EQ(200u, offscreenBuffer->viewportWidth); 1317 EXPECT_EQ(200u, offscreenBuffer->viewportHeight); 1318 } else { ADD_FAILURE(); } 1319 } 1320 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1321 int index = mIndex++; 1322 if (index == 1) { 1323 // inner layer's rect (white) 1324 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1325 } else if (index == 7) { 1326 // outer layer's rect (grey) 1327 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1328 } else { ADD_FAILURE(); } 1329 } 1330 void endLayer() override { 1331 int index = mIndex++; 1332 EXPECT_TRUE(index == 2 || index == 5 || index == 9); 1333 } 1334 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1335 EXPECT_EQ(10, mIndex++); 1336 } 1337 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1338 OffscreenBuffer* layer = *op.layerHandle; 1339 int index = mIndex++; 1340 if (index == 4) { 1341 EXPECT_EQ(100u, layer->viewportWidth); 1342 EXPECT_EQ(100u, layer->viewportHeight); 1343 } else if (index == 8) { 1344 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1345 } else if (index == 11) { 1346 EXPECT_EQ(200u, layer->viewportWidth); 1347 EXPECT_EQ(200u, layer->viewportHeight); 1348 } else { ADD_FAILURE(); } 1349 } 1350 void endFrame(const Rect& repaintRect) override { 1351 EXPECT_EQ(12, mIndex++); 1352 } 1353 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1354 EXPECT_EQ(13, mIndex++); 1355 } 1356 }; 1357 1358 auto child = TestUtils::createNode(50, 50, 150, 150, 1359 [](RenderProperties& props, RecordingCanvas& canvas) { 1360 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1361 SkPaint paint; 1362 paint.setColor(SK_ColorWHITE); 1363 canvas.drawRect(0, 0, 100, 100, paint); 1364 }); 1365 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1366 *(child->getLayerHandle()) = &childLayer; 1367 1368 RenderNode* childPtr = child.get(); 1369 auto parent = TestUtils::createNode(0, 0, 200, 200, 1370 [childPtr](RenderProperties& props, RecordingCanvas& canvas) { 1371 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1372 SkPaint paint; 1373 paint.setColor(SK_ColorDKGRAY); 1374 canvas.drawRect(0, 0, 200, 200, paint); 1375 1376 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); 1377 canvas.drawRenderNode(childPtr); 1378 canvas.restore(); 1379 }); 1380 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1381 *(parent->getLayerHandle()) = &parentLayer; 1382 1383 auto syncedNode = TestUtils::getSyncedNode(parent); 1384 1385 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1386 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); 1387 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); 1388 1389 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1390 sLightGeometry, Caches::getInstance()); 1391 frameBuilder.deferLayers(layerUpdateQueue); 1392 frameBuilder.deferRenderNode(*syncedNode); 1393 1394 HwLayerComplexTestRenderer renderer; 1395 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1396 EXPECT_EQ(14, renderer.getIndex()); 1397 1398 // clean up layer pointers, so we can safely destruct RenderNodes 1399 *(child->getLayerHandle()) = nullptr; 1400 *(parent->getLayerHandle()) = nullptr; 1401} 1402 1403 1404RENDERTHREAD_TEST(FrameBuilder, buildLayer) { 1405 class BuildLayerTestRenderer : public TestRendererBase { 1406 public: 1407 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1408 EXPECT_EQ(0, mIndex++); 1409 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1410 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1411 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1412 } 1413 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 1414 EXPECT_EQ(1, mIndex++); 1415 1416 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1417 << "Transform should be reset within layer"; 1418 1419 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1420 << "Damage rect should be used to clip layer content"; 1421 } 1422 void endLayer() override { 1423 EXPECT_EQ(2, mIndex++); 1424 } 1425 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1426 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1427 } 1428 void endFrame(const Rect& repaintRect) override { 1429 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1430 } 1431 }; 1432 1433 auto node = TestUtils::createNode(10, 10, 110, 110, 1434 [](RenderProperties& props, RecordingCanvas& canvas) { 1435 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1436 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); 1437 }); 1438 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1439 1440 // create RenderNode's layer here in same way prepareTree would 1441 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1442 *layerHandle = &layer; 1443 1444 TestUtils::syncHierarchyPropertiesAndDisplayList(node); 1445 1446 // only enqueue partial damage 1447 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1448 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1449 1450 // Draw, but pass empty node list, so no work is done for primary frame 1451 FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance()); 1452 BuildLayerTestRenderer renderer; 1453 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1454 EXPECT_EQ(3, renderer.getIndex()); 1455 1456 // clean up layer pointer, so we can safely destruct RenderNode 1457 *layerHandle = nullptr; 1458} 1459 1460static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { 1461 SkPaint paint; 1462 // order put in blue channel, transparent so overlapped content doesn't get rejected 1463 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); 1464 canvas->drawRect(0, 0, 100, 100, paint); 1465} 1466static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { 1467 auto node = TestUtils::createNode(0, 0, 100, 100, 1468 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { 1469 drawOrderedRect(&canvas, expectedDrawOrder); 1470 }); 1471 node->mutateStagingProperties().setTranslationZ(z); 1472 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); 1473 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1474} 1475RENDERTHREAD_TEST(FrameBuilder, zReorder) { 1476 class ZReorderTestRenderer : public TestRendererBase { 1477 public: 1478 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1479 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel 1480 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 1481 } 1482 }; 1483 1484 auto parent = TestUtils::createNode(0, 0, 100, 100, 1485 [](RenderProperties& props, RecordingCanvas& canvas) { 1486 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 1487 drawOrderedRect(&canvas, 1); 1488 canvas.insertReorderBarrier(true); 1489 drawOrderedNode(&canvas, 6, 2.0f); 1490 drawOrderedRect(&canvas, 3); 1491 drawOrderedNode(&canvas, 4, 0.0f); 1492 drawOrderedRect(&canvas, 5); 1493 drawOrderedNode(&canvas, 2, -2.0f); 1494 drawOrderedNode(&canvas, 7, 2.0f); 1495 canvas.insertReorderBarrier(false); 1496 drawOrderedRect(&canvas, 8); 1497 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 1498 }); 1499 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1500 sLightGeometry, Caches::getInstance()); 1501 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1502 1503 ZReorderTestRenderer renderer; 1504 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1505 EXPECT_EQ(10, renderer.getIndex()); 1506}; 1507 1508RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { 1509 static const int scrollX = 5; 1510 static const int scrollY = 10; 1511 class ProjectionReorderTestRenderer : public TestRendererBase { 1512 public: 1513 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1514 const int index = mIndex++; 1515 1516 Matrix4 expectedMatrix; 1517 switch (index) { 1518 case 0: 1519 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 1520 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1521 expectedMatrix.loadIdentity(); 1522 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1523 break; 1524 case 1: 1525 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 1526 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1527 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); 1528 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1529 EXPECT_EQ(Rect(-35, -30, 45, 50), 1530 Rect(state.computedState.localProjectionPathMask->getBounds())); 1531 break; 1532 case 2: 1533 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 1534 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 1535 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 1536 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1537 break; 1538 default: 1539 ADD_FAILURE(); 1540 } 1541 EXPECT_EQ(expectedMatrix, state.computedState.transform); 1542 } 1543 }; 1544 1545 /** 1546 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 1547 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 1548 * draw, but because it is projected backwards, it's drawn in between B and C. 1549 * 1550 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 1551 * (which isn't affected by scroll). 1552 */ 1553 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100, 1554 [](RenderProperties& properties, RecordingCanvas& canvas) { 1555 properties.setProjectionReceiver(true); 1556 // scroll doesn't apply to background, so undone via translationX/Y 1557 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1558 properties.setTranslationX(scrollX); 1559 properties.setTranslationY(scrollY); 1560 1561 SkPaint paint; 1562 paint.setColor(SK_ColorWHITE); 1563 canvas.drawRect(0, 0, 100, 100, paint); 1564 }); 1565 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50, 1566 [](RenderProperties& properties, RecordingCanvas& canvas) { 1567 properties.setProjectBackwards(true); 1568 properties.setClipToBounds(false); 1569 SkPaint paint; 1570 paint.setColor(SK_ColorDKGRAY); 1571 canvas.drawRect(-10, -10, 60, 60, paint); 1572 }); 1573 auto child = TestUtils::createNode(0, 50, 100, 100, 1574 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1575 SkPaint paint; 1576 paint.setColor(SK_ColorBLUE); 1577 canvas.drawRect(0, 0, 100, 50, paint); 1578 canvas.drawRenderNode(projectingRipple.get()); 1579 }); 1580 auto parent = TestUtils::createNode(0, 0, 100, 100, 1581 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1582 // Set a rect outline for the projecting ripple to be masked against. 1583 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 1584 1585 canvas.save(SaveFlags::MatrixClip); 1586 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1587 canvas.drawRenderNode(receiverBackground.get()); 1588 canvas.drawRenderNode(child.get()); 1589 canvas.restore(); 1590 }); 1591 1592 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1593 sLightGeometry, Caches::getInstance()); 1594 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1595 1596 ProjectionReorderTestRenderer renderer; 1597 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1598 EXPECT_EQ(3, renderer.getIndex()); 1599} 1600 1601RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { 1602 static const int scrollX = 5; 1603 static const int scrollY = 10; 1604 class ProjectionHwLayerTestRenderer : public TestRendererBase { 1605 public: 1606 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1607 EXPECT_EQ(0, mIndex++); 1608 } 1609 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 1610 EXPECT_EQ(1, mIndex++); 1611 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1612 } 1613 void endLayer() override { 1614 EXPECT_EQ(2, mIndex++); 1615 } 1616 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1617 EXPECT_EQ(3, mIndex++); 1618 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1619 } 1620 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1621 EXPECT_EQ(4, mIndex++); 1622 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1623 Matrix4 expected; 1624 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); 1625 EXPECT_EQ(expected, state.computedState.transform); 1626 EXPECT_EQ(Rect(-85, -80, 295, 300), 1627 Rect(state.computedState.localProjectionPathMask->getBounds())); 1628 } 1629 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1630 EXPECT_EQ(5, mIndex++); 1631 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1632 } 1633 }; 1634 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1635 [](RenderProperties& properties, RecordingCanvas& canvas) { 1636 properties.setProjectionReceiver(true); 1637 // scroll doesn't apply to background, so undone via translationX/Y 1638 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1639 properties.setTranslationX(scrollX); 1640 properties.setTranslationY(scrollY); 1641 1642 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1643 }); 1644 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1645 [](RenderProperties& properties, RecordingCanvas& canvas) { 1646 properties.setProjectBackwards(true); 1647 properties.setClipToBounds(false); 1648 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 1649 }); 1650 auto child = TestUtils::createNode(100, 100, 300, 300, 1651 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1652 properties.mutateLayerProperties().setType(LayerType::RenderLayer); 1653 canvas.drawRenderNode(projectingRipple.get()); 1654 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); 1655 }); 1656 auto parent = TestUtils::createNode(0, 0, 400, 400, 1657 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1658 // Set a rect outline for the projecting ripple to be masked against. 1659 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 1660 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1661 canvas.drawRenderNode(receiverBackground.get()); 1662 canvas.drawRenderNode(child.get()); 1663 }); 1664 1665 OffscreenBuffer** layerHandle = child->getLayerHandle(); 1666 1667 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1668 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1669 Matrix4 windowTransform; 1670 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin 1671 layer.setWindowTransform(windowTransform); 1672 *layerHandle = &layer; 1673 1674 auto syncedNode = TestUtils::getSyncedNode(parent); 1675 1676 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1677 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); 1678 1679 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 1680 sLightGeometry, Caches::getInstance()); 1681 frameBuilder.deferLayers(layerUpdateQueue); 1682 frameBuilder.deferRenderNode(*syncedNode); 1683 1684 ProjectionHwLayerTestRenderer renderer; 1685 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1686 EXPECT_EQ(6, renderer.getIndex()); 1687 1688 // clean up layer pointer, so we can safely destruct RenderNode 1689 *layerHandle = nullptr; 1690} 1691 1692RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { 1693 static const int scrollX = 500000; 1694 static const int scrollY = 0; 1695 class ProjectionChildScrollTestRenderer : public TestRendererBase { 1696 public: 1697 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1698 EXPECT_EQ(0, mIndex++); 1699 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1700 } 1701 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1702 EXPECT_EQ(1, mIndex++); 1703 ASSERT_NE(nullptr, state.computedState.clipState); 1704 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1705 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect); 1706 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1707 } 1708 }; 1709 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1710 [](RenderProperties& properties, RecordingCanvas& canvas) { 1711 properties.setProjectionReceiver(true); 1712 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1713 }); 1714 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1715 [](RenderProperties& properties, RecordingCanvas& canvas) { 1716 // scroll doesn't apply to background, so undone via translationX/Y 1717 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1718 properties.setTranslationX(scrollX); 1719 properties.setTranslationY(scrollY); 1720 properties.setProjectBackwards(true); 1721 properties.setClipToBounds(false); 1722 canvas.drawOval(0, 0, 200, 200, SkPaint()); 1723 }); 1724 auto child = TestUtils::createNode(0, 0, 400, 400, 1725 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1726 // Record time clip will be ignored by projectee 1727 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op); 1728 1729 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1730 canvas.drawRenderNode(projectingRipple.get()); 1731 }); 1732 auto parent = TestUtils::createNode(0, 0, 400, 400, 1733 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1734 canvas.drawRenderNode(receiverBackground.get()); 1735 canvas.drawRenderNode(child.get()); 1736 }); 1737 1738 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 1739 sLightGeometry, Caches::getInstance()); 1740 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1741 1742 ProjectionChildScrollTestRenderer renderer; 1743 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1744 EXPECT_EQ(2, renderer.getIndex()); 1745} 1746 1747// creates a 100x100 shadow casting node with provided translationZ 1748static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1749 return TestUtils::createNode(0, 0, 100, 100, 1750 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1751 properties.setTranslationZ(translationZ); 1752 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1753 SkPaint paint; 1754 paint.setColor(SK_ColorWHITE); 1755 canvas.drawRect(0, 0, 100, 100, paint); 1756 }); 1757} 1758 1759RENDERTHREAD_TEST(FrameBuilder, shadow) { 1760 class ShadowTestRenderer : public TestRendererBase { 1761 public: 1762 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1763 EXPECT_EQ(0, mIndex++); 1764 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1765 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr)); 1766 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY); 1767 1768 Matrix4 expectedZ; 1769 expectedZ.loadTranslate(0, 0, 5); 1770 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ); 1771 } 1772 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1773 EXPECT_EQ(1, mIndex++); 1774 } 1775 }; 1776 1777 auto parent = TestUtils::createNode(0, 0, 200, 200, 1778 [](RenderProperties& props, RecordingCanvas& canvas) { 1779 canvas.insertReorderBarrier(true); 1780 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1781 }); 1782 1783 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1784 sLightGeometry, Caches::getInstance()); 1785 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1786 1787 ShadowTestRenderer renderer; 1788 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1789 EXPECT_EQ(2, renderer.getIndex()); 1790} 1791 1792RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { 1793 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1794 public: 1795 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1796 EXPECT_EQ(0, mIndex++); 1797 return nullptr; 1798 } 1799 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1800 EXPECT_EQ(1, mIndex++); 1801 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1802 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1803 } 1804 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1805 EXPECT_EQ(2, mIndex++); 1806 } 1807 void endLayer() override { 1808 EXPECT_EQ(3, mIndex++); 1809 } 1810 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1811 EXPECT_EQ(4, mIndex++); 1812 } 1813 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1814 EXPECT_EQ(5, mIndex++); 1815 } 1816 }; 1817 1818 auto parent = TestUtils::createNode(0, 0, 200, 200, 1819 [](RenderProperties& props, RecordingCanvas& canvas) { 1820 // save/restore outside of reorderBarrier, so they don't get moved out of place 1821 canvas.translate(20, 10); 1822 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); 1823 canvas.insertReorderBarrier(true); 1824 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1825 canvas.insertReorderBarrier(false); 1826 canvas.restoreToCount(count); 1827 }); 1828 1829 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1830 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1831 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1832 1833 ShadowSaveLayerTestRenderer renderer; 1834 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1835 EXPECT_EQ(6, renderer.getIndex()); 1836} 1837 1838RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { 1839 class ShadowHwLayerTestRenderer : public TestRendererBase { 1840 public: 1841 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1842 EXPECT_EQ(0, mIndex++); 1843 } 1844 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1845 EXPECT_EQ(1, mIndex++); 1846 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1847 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1848 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius); 1849 } 1850 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1851 EXPECT_EQ(2, mIndex++); 1852 } 1853 void endLayer() override { 1854 EXPECT_EQ(3, mIndex++); 1855 } 1856 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1857 EXPECT_EQ(4, mIndex++); 1858 } 1859 }; 1860 1861 auto parent = TestUtils::createNode(50, 60, 150, 160, 1862 [](RenderProperties& props, RecordingCanvas& canvas) { 1863 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1864 canvas.insertReorderBarrier(true); 1865 canvas.save(SaveFlags::MatrixClip); 1866 canvas.translate(20, 10); 1867 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1868 canvas.restore(); 1869 }); 1870 OffscreenBuffer** layerHandle = parent->getLayerHandle(); 1871 1872 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1873 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1874 Matrix4 windowTransform; 1875 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin 1876 layer.setWindowTransform(windowTransform); 1877 *layerHandle = &layer; 1878 1879 auto syncedNode = TestUtils::getSyncedNode(parent); 1880 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1881 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); 1882 1883 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1884 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance()); 1885 frameBuilder.deferLayers(layerUpdateQueue); 1886 frameBuilder.deferRenderNode(*syncedNode); 1887 1888 ShadowHwLayerTestRenderer renderer; 1889 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1890 EXPECT_EQ(5, renderer.getIndex()); 1891 1892 // clean up layer pointer, so we can safely destruct RenderNode 1893 *layerHandle = nullptr; 1894} 1895 1896RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { 1897 class ShadowLayeringTestRenderer : public TestRendererBase { 1898 public: 1899 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1900 int index = mIndex++; 1901 EXPECT_TRUE(index == 0 || index == 1); 1902 } 1903 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1904 int index = mIndex++; 1905 EXPECT_TRUE(index == 2 || index == 3); 1906 } 1907 }; 1908 auto parent = TestUtils::createNode(0, 0, 200, 200, 1909 [](RenderProperties& props, RecordingCanvas& canvas) { 1910 canvas.insertReorderBarrier(true); 1911 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1912 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 1913 }); 1914 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1915 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1916 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1917 1918 ShadowLayeringTestRenderer renderer; 1919 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1920 EXPECT_EQ(4, renderer.getIndex()); 1921} 1922 1923RENDERTHREAD_TEST(FrameBuilder, shadowClipping) { 1924 class ShadowClippingTestRenderer : public TestRendererBase { 1925 public: 1926 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1927 EXPECT_EQ(0, mIndex++); 1928 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect) 1929 << "Shadow must respect pre-barrier canvas clip value."; 1930 } 1931 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1932 EXPECT_EQ(1, mIndex++); 1933 } 1934 }; 1935 auto parent = TestUtils::createNode(0, 0, 100, 100, 1936 [](RenderProperties& props, RecordingCanvas& canvas) { 1937 // Apply a clip before the reorder barrier/shadow casting child is drawn. 1938 // This clip must be applied to the shadow cast by the child. 1939 canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op); 1940 canvas.insertReorderBarrier(true); 1941 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1942 }); 1943 1944 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1945 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1946 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1947 1948 ShadowClippingTestRenderer renderer; 1949 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1950 EXPECT_EQ(2, renderer.getIndex()); 1951} 1952 1953static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, 1954 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 1955 class PropertyTestRenderer : public TestRendererBase { 1956 public: 1957 explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) 1958 : mCallback(callback) {} 1959 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1960 EXPECT_EQ(mIndex++, 0); 1961 mCallback(op, state); 1962 } 1963 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 1964 }; 1965 1966 auto node = TestUtils::createNode(0, 0, 100, 100, 1967 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 1968 propSetupCallback(props); 1969 SkPaint paint; 1970 paint.setColor(SK_ColorWHITE); 1971 canvas.drawRect(0, 0, 100, 100, paint); 1972 }); 1973 1974 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, 1975 sLightGeometry, Caches::getInstance()); 1976 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1977 1978 PropertyTestRenderer renderer(opValidateCallback); 1979 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1980 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 1981} 1982 1983RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 1984 testProperty([](RenderProperties& properties) { 1985 properties.setAlpha(0.5f); 1986 properties.setHasOverlappingRendering(false); 1987 }, [](const RectOp& op, const BakedOpState& state) { 1988 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 1989 }); 1990} 1991 1992RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { 1993 testProperty([](RenderProperties& properties) { 1994 properties.setClipToBounds(true); 1995 properties.setClipBounds(Rect(10, 20, 300, 400)); 1996 }, [](const RectOp& op, const BakedOpState& state) { 1997 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 1998 << "Clip rect should be intersection of node bounds and clip bounds"; 1999 }); 2000} 2001 2002RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { 2003 testProperty([](RenderProperties& properties) { 2004 properties.mutableRevealClip().set(true, 50, 50, 25); 2005 }, [](const RectOp& op, const BakedOpState& state) { 2006 ASSERT_NE(nullptr, state.roundRectClipState); 2007 EXPECT_TRUE(state.roundRectClipState->highPriority); 2008 EXPECT_EQ(25, state.roundRectClipState->radius); 2009 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 2010 }); 2011} 2012 2013RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { 2014 testProperty([](RenderProperties& properties) { 2015 properties.mutableOutline().setShouldClip(true); 2016 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 2017 }, [](const RectOp& op, const BakedOpState& state) { 2018 ASSERT_NE(nullptr, state.roundRectClipState); 2019 EXPECT_FALSE(state.roundRectClipState->highPriority); 2020 EXPECT_EQ(5, state.roundRectClipState->radius); 2021 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 2022 }); 2023} 2024 2025RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) { 2026 testProperty([](RenderProperties& properties) { 2027 properties.setLeftTopRightBottom(10, 10, 110, 110); 2028 2029 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 2030 properties.setStaticMatrix(&staticMatrix); 2031 2032 // ignored, since static overrides animation 2033 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 2034 properties.setAnimationMatrix(&animationMatrix); 2035 2036 properties.setTranslationX(10); 2037 properties.setTranslationY(20); 2038 properties.setScaleX(0.5f); 2039 properties.setScaleY(0.7f); 2040 }, [](const RectOp& op, const BakedOpState& state) { 2041 Matrix4 matrix; 2042 matrix.loadTranslate(10, 10, 0); // left, top 2043 matrix.scale(1.2f, 1.2f, 1); // static matrix 2044 // ignore animation matrix, since static overrides it 2045 2046 // translation xy 2047 matrix.translate(10, 20); 2048 2049 // scale xy (from default pivot - center) 2050 matrix.translate(50, 50); 2051 matrix.scale(0.5f, 0.7f, 1); 2052 matrix.translate(-50, -50); 2053 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 2054 << "Op draw matrix must match expected combination of transformation properties"; 2055 }); 2056} 2057 2058struct SaveLayerAlphaData { 2059 uint32_t layerWidth = 0; 2060 uint32_t layerHeight = 0; 2061 Rect rectClippedBounds; 2062 Matrix4 rectMatrix; 2063 Matrix4 drawLayerMatrix; 2064}; 2065/** 2066 * Constructs a view to hit the temporary layer alpha property implementation: 2067 * a) 0 < alpha < 1 2068 * b) too big for layer (larger than maxTextureSize) 2069 * c) overlapping rendering content 2070 * returning observed data about layer size and content clip/transform. 2071 * 2072 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 2073 * (for efficiency, and to fit in layer size constraints) based on parent clip. 2074 */ 2075void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 2076 std::function<void(RenderProperties&)> propSetupCallback) { 2077 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 2078 public: 2079 explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) 2080 : mOutData(outData) {} 2081 2082 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 2083 EXPECT_EQ(0, mIndex++); 2084 mOutData->layerWidth = width; 2085 mOutData->layerHeight = height; 2086 return nullptr; 2087 } 2088 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2089 EXPECT_EQ(1, mIndex++); 2090 2091 mOutData->rectClippedBounds = state.computedState.clippedBounds; 2092 mOutData->rectMatrix = state.computedState.transform; 2093 } 2094 void endLayer() override { 2095 EXPECT_EQ(2, mIndex++); 2096 } 2097 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 2098 EXPECT_EQ(3, mIndex++); 2099 mOutData->drawLayerMatrix = state.computedState.transform; 2100 } 2101 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 2102 EXPECT_EQ(4, mIndex++); 2103 } 2104 private: 2105 SaveLayerAlphaData* mOutData; 2106 }; 2107 2108 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 2109 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 2110 auto node = TestUtils::createNode(0, 0, 10000, 10000, 2111 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 2112 properties.setHasOverlappingRendering(true); 2113 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 2114 // apply other properties 2115 propSetupCallback(properties); 2116 2117 SkPaint paint; 2118 paint.setColor(SK_ColorWHITE); 2119 canvas.drawRect(0, 0, 10000, 10000, paint); 2120 }); 2121 auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height 2122 2123 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 2124 sLightGeometry, Caches::getInstance()); 2125 frameBuilder.deferRenderNode(*syncedNode); 2126 2127 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 2128 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2129 2130 // assert, since output won't be valid if we haven't seen a save layer triggered 2131 ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 2132} 2133 2134RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 2135 SaveLayerAlphaData observedData; 2136 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2137 properties.setTranslationX(10); // offset rendering content 2138 properties.setTranslationY(-2000); // offset rendering content 2139 }); 2140 EXPECT_EQ(190u, observedData.layerWidth); 2141 EXPECT_EQ(200u, observedData.layerHeight); 2142 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 2143 << "expect content to be clipped to screen area"; 2144 Matrix4 expected; 2145 expected.loadTranslate(0, -2000, 0); 2146 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 2147 << "expect content to be translated as part of being clipped"; 2148 expected.loadTranslate(10, 0, 0); 2149 EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix) 2150 << "expect drawLayer to be translated as part of being clipped"; 2151} 2152 2153RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 2154 SaveLayerAlphaData observedData; 2155 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2156 // Translate and rotate the view so that the only visible part is the top left corner of 2157 // the view. It will form an isosceles right triangle with a long side length of 200 at the 2158 // bottom of the viewport. 2159 properties.setTranslationX(100); 2160 properties.setTranslationY(100); 2161 properties.setPivotX(0); 2162 properties.setPivotY(0); 2163 properties.setRotation(45); 2164 }); 2165 // ceil(sqrt(2) / 2 * 200) = 142 2166 EXPECT_EQ(142u, observedData.layerWidth); 2167 EXPECT_EQ(142u, observedData.layerHeight); 2168 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 2169 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2170} 2171 2172RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 2173 SaveLayerAlphaData observedData; 2174 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2175 properties.setPivotX(0); 2176 properties.setPivotY(0); 2177 properties.setScaleX(2); 2178 properties.setScaleY(0.5f); 2179 }); 2180 EXPECT_EQ(100u, observedData.layerWidth); 2181 EXPECT_EQ(400u, observedData.layerHeight); 2182 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 2183 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2184} 2185 2186RENDERTHREAD_TEST(FrameBuilder, clip_replace) { 2187 class ClipReplaceTestRenderer : public TestRendererBase { 2188 public: 2189 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 2190 EXPECT_EQ(0, mIndex++); 2191 EXPECT_TRUE(op.localClip->intersectWithRoot); 2192 EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect) 2193 << "Expect resolved clip to be intersection of viewport clip and clip op"; 2194 } 2195 }; 2196 auto node = TestUtils::createNode(20, 20, 30, 30, 2197 [](RenderProperties& props, RecordingCanvas& canvas) { 2198 canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op); 2199 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); 2200 }); 2201 2202 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, 2203 sLightGeometry, Caches::getInstance()); 2204 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 2205 2206 ClipReplaceTestRenderer renderer; 2207 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2208 EXPECT_EQ(1, renderer.getIndex()); 2209} 2210 2211} // namespace uirenderer 2212} // namespace android 2213