summaryrefslogtreecommitdiff
path: root/libs/hwui/tests/unit/FrameBuilderTests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/tests/unit/FrameBuilderTests.cpp')
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp2705
1 files changed, 0 insertions, 2705 deletions
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
deleted file mode 100644
index 4eb77514f4ae..000000000000
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ /dev/null
@@ -1,2705 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <BakedOpState.h>
-#include <DeferredLayerUpdater.h>
-#include <FrameBuilder.h>
-#include <GlLayer.h>
-#include <LayerUpdateQueue.h>
-#include <RecordedOp.h>
-#include <RecordingCanvas.h>
-#include <tests/common/TestUtils.h>
-
-#include <unordered_map>
-
-namespace android {
-namespace uirenderer {
-
-const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50};
-
-/**
- * Virtual class implemented by each test to redirect static operation / state transitions to
- * virtual methods.
- *
- * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
- * and allows Renderer vs Dispatching behavior to be merged.
- *
- * onXXXOp methods fail by default - tests should override ops they expect
- * startRepaintLayer fails by default - tests should override if expected
- * startFrame/endFrame do nothing by default - tests should override to intercept
- */
-class TestRendererBase {
-public:
- virtual ~TestRendererBase() {}
- virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
- ADD_FAILURE() << "Temporary layers not expected in this test";
- return nullptr;
- }
- virtual void recycleTemporaryLayer(OffscreenBuffer*) {
- ADD_FAILURE() << "Temporary layers not expected in this test";
- }
- virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
- ADD_FAILURE() << "Layer repaint not expected in this test";
- }
- virtual void endLayer() { ADD_FAILURE() << "Layer updates not expected in this test"; }
- virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
- virtual void endFrame(const Rect& repaintRect) {}
-
-// define virtual defaults for single draw methods
-#define X(Type) \
- virtual void on##Type(const Type&, const BakedOpState&) { \
- ADD_FAILURE() << #Type " not expected in this test"; \
- }
- MAP_RENDERABLE_OPS(X)
-#undef X
-
-// define virtual defaults for merged draw methods
-#define X(Type) \
- virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
- ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
- }
- MAP_MERGEABLE_OPS(X)
-#undef X
-
- int getIndex() { return mIndex; }
-
-protected:
- int mIndex = 0;
-};
-
-/**
- * Dispatches all static methods to similar formed methods on renderer, which fail by default but
- * are overridden by subclasses per test.
- */
-class TestDispatcher {
-public:
-// define single op methods, which redirect to TestRendererBase
-#define X(Type) \
- static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
- renderer.on##Type(op, state); \
- }
- MAP_RENDERABLE_OPS(X);
-#undef X
-
-// define merged op methods, which redirect to TestRendererBase
-#define X(Type) \
- static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
- renderer.onMerged##Type##s(opList); \
- }
- MAP_MERGEABLE_OPS(X);
-#undef X
-};
-
-class FailRenderer : public TestRendererBase {};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) {
- class SimpleTestRenderer : public TestRendererBase {
- public:
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, width);
- EXPECT_EQ(200u, height);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(3, mIndex++); }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
- canvas.drawRect(0, 0, 100, 200, SkPaint());
- canvas.drawBitmap(*bitmap, 10, 10, nullptr);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) {
- class SimpleStrokeTestRenderer : public TestRendererBase {
- public:
- void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- // even though initial bounds are empty...
- EXPECT_TRUE(op.unmappedBounds.isEmpty())
- << "initial bounds should be empty, since they're unstroked";
- EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
- << "final bounds should account for stroke";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint strokedPaint;
- strokedPaint.setStrokeWidth(10);
- canvas.drawPoint(50, 50, strokedPaint);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SimpleStrokeTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, arcStrokeClip) {
- class ArcStrokeClipTestRenderer : public TestRendererBase {
- public:
- void onArcOp(const ArcOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(25, 25, 175, 175), op.unmappedBounds);
- EXPECT_EQ(Rect(25, 25, 175, 175), state.computedState.clippedBounds);
- EXPECT_EQ(OpClipSideFlags::Full, state.computedState.clipSideFlags)
- << "Arc op clipped conservatively, since path texture may be expanded";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.clipRect(25, 25, 175, 175, SkClipOp::kIntersect);
- SkPaint aaPaint;
- aaPaint.setAntiAlias(true);
- canvas.drawArc(25, 25, 175, 175, 40, 180, true, aaPaint);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ArcStrokeClipTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) {
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- canvas.restore();
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) {
- const int LOOPS = 5;
- class SimpleBatchingTestRenderer : public TestRendererBase {
- public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
-
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(
- 10, 10,
- kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap
-
- // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
- // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
- canvas.save(SaveFlags::MatrixClip);
- for (int i = 0; i < LOOPS; i++) {
- canvas.translate(0, 10);
- canvas.drawRect(0, 0, 10, 10, SkPaint());
- canvas.drawBitmap(*bitmap, 5, 0, nullptr);
- }
- canvas.restore();
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SimpleBatchingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) {
- class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
- EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
- state.computedState.clipSideFlags);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
- *TestUtils::getSyncedNode(node));
-
- DeferRenderNodeTranslateClipTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) {
- class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const Rect& clippedBounds = state.computedState.clippedBounds;
- Matrix4 expected;
- switch (mIndex++) {
- case 0:
- // background - left side
- EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
- expected.loadTranslate(100, 100, 0);
- break;
- case 1:
- // background - top side
- EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
- expected.loadTranslate(100, 100, 0);
- break;
- case 2:
- // content
- EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
- expected.loadTranslate(-50, -50, 0);
- break;
- case 3:
- // overlay
- EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
- break;
- default:
- ADD_FAILURE() << "Too many rects observed";
- }
- EXPECT_EQ(expected, state.computedState.transform);
- }
- };
-
- std::vector<sp<RenderNode>> nodes;
- SkPaint transparentPaint;
- transparentPaint.setAlpha(128);
-
- // backdrop
- nodes.push_back(TestUtils::createNode<RecordingCanvas>(
- 100, 100, 700, 500, // 600x400
- [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 600, 400, transparentPaint);
- }));
-
- // content
- Rect contentDrawBounds(150, 150, 650, 450); // 500x300
- nodes.push_back(TestUtils::createNode<RecordingCanvas>(
- 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 800, 600, transparentPaint);
- }));
-
- // overlay
- nodes.push_back(TestUtils::createNode<RecordingCanvas>(
- 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 800, 200, transparentPaint);
- }));
-
- for (auto& node : nodes) {
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- }
-
- {
- FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
-
- DeferRenderNodeSceneTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
- }
-
- for (auto& node : nodes) {
- EXPECT_TRUE(node->isValid());
- EXPECT_FALSE(node->nothingToDraw());
- node->setStagingDisplayList(nullptr);
- EXPECT_FALSE(node->isValid());
- EXPECT_FALSE(node->nothingToDraw());
- node->destroyHardwareResources();
- EXPECT_TRUE(node->nothingToDraw());
- EXPECT_FALSE(node->isValid());
- }
-
- {
- // Validate no crashes if any nodes are missing DisplayLists
- FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- }
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_noFbo0) {
- class EmptyNoFbo0TestRenderer : public TestRendererBase {
- public:
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- void endFrame(const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- };
-
- // Use layer update constructor, so no work is enqueued for Fbo0
- LayerUpdateQueue emptyLayerUpdateQueue;
- FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
- EmptyNoFbo0TestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) {
- class EmptyWithFbo0TestRenderer : public TestRendererBase {
- public:
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(1, mIndex++); }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- // no drawn content
- });
-
- // Draw, but pass node without draw content, so no work is done for primary frame
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- EmptyWithFbo0TestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
- " but fbo0 update lifecycle should still be observed";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) {
- class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(mIndex++, 0) << "Should be one rect";
- EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
- << "Last rect should occlude others.";
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.drawRect(10, 10, 190, 190, SkPaint());
- });
-
- // Damage (and therefore clip) is same as last draw, subset of renderable area.
- // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
- FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
- << "Recording must not have rejected ops, in order for this test to be valid";
-
- AvoidOverdrawRectsTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
- static sk_sp<Bitmap> opaqueBitmap(
- TestUtils::createBitmap(50, 50, SkColorType::kRGB_565_SkColorType));
- static sk_sp<Bitmap> transpBitmap(
- TestUtils::createBitmap(50, 50, SkColorType::kAlpha_8_SkColorType));
- class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
- public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- switch (mIndex++) {
- case 0:
- EXPECT_EQ(opaqueBitmap.get(), op.bitmap);
- break;
- case 1:
- EXPECT_EQ(transpBitmap.get(), op.bitmap);
- break;
- default:
- ADD_FAILURE() << "Only two ops expected.";
- }
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 50, 50, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 50, 50, SkPaint());
- canvas.drawRect(0, 0, 50, 50, SkPaint());
- canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
-
- // only the below draws should remain, since they're
- canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr);
- canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
- << "Recording must not have rejected ops, in order for this test to be valid";
-
- AvoidOverdrawBitmapsTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) {
- class ClippedMergingTestRenderer : public TestRendererBase {
- public:
- void onMergedBitmapOps(const MergedBakedOpList& opList) override {
- EXPECT_EQ(0, mIndex);
- mIndex += opList.count;
- EXPECT_EQ(4u, opList.count);
- EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
- EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
- opList.clipSideFlags);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
-
- // left side clipped (to inset left half)
- canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 0, 40, nullptr);
-
- // top side clipped (to inset top half)
- canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 40, 0, nullptr);
-
- // right side clipped (to inset right half)
- canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 80, 40, nullptr);
-
- // bottom not clipped, just abutting (inset bottom half)
- canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 40, 70, nullptr);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ClippedMergingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) {
- class RegionClipStopsMergeTestRenderer : public TestRendererBase {
- public:
- void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPath path;
- path.addCircle(200, 200, 200, SkPath::kCW_Direction);
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipPath(&path, SkClipOp::kIntersect);
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(50);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200);
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- RegionClipStopsMergeTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) {
- class TextMergingTestRenderer : public TestRendererBase {
- public:
- void onMergedTextOps(const MergedBakedOpList& opList) override {
- EXPECT_EQ(0, mIndex);
- mIndex += opList.count;
- EXPECT_EQ(2u, opList.count);
- EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
- EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
- EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(50);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextMergingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) {
- const int LOOPS = 5;
- class TextStrikethroughTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
- }
- void onMergedTextOps(const MergedBakedOpList& opList) override {
- EXPECT_EQ(0, mIndex);
- mIndex += opList.count;
- EXPECT_EQ(5u, opList.count);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 2000, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint textPaint;
- textPaint.setAntiAlias(true);
- textPaint.setTextSize(20);
- textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag);
- for (int i = 0; i < LOOPS; i++) {
- TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
- }
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextStrikethroughTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count";
-}
-
-static auto styles = {SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) {
- class TextStyleTestRenderer : public TestRendererBase {
- public:
- void onMergedTextOps(const MergedBakedOpList& opList) override {
- ASSERT_EQ(0, mIndex);
- ASSERT_EQ(3u, opList.count);
- mIndex += opList.count;
-
- int index = 0;
- for (auto style : styles) {
- auto state = opList.states[index++];
- ASSERT_EQ(style, state->op->paint->getStyle())
- << "Remainder of validation relies upon stable merged order";
- ASSERT_EQ(0, state->computedState.clipSideFlags)
- << "Clipped bounds validation requires unclipped ops";
- }
-
- Rect fill = opList.states[0]->computedState.clippedBounds;
- Rect stroke = opList.states[1]->computedState.clippedBounds;
- EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
- << "Stroke+Fill should be same as stroke";
-
- EXPECT_TRUE(stroke.contains(fill));
- EXPECT_FALSE(fill.contains(stroke));
-
- // outset by half the stroke width
- Rect outsetFill(fill);
- outsetFill.outset(5);
- EXPECT_EQ(stroke, outsetFill);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(50);
- paint.setStrokeWidth(10);
-
- // draw 3 copies of the same text overlapping, each with a different style.
- // They'll get merged, but with
- for (auto style : styles) {
- paint.setStyle(style);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
- }
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
- TextStyleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
- class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
- public:
- void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
- EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
-
- Matrix4 expected;
- expected.loadTranslate(5, 5, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
- }
- };
-
- auto layerUpdater =
- TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect);
- canvas.drawLayer(layerUpdater.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextureLayerClipLocalMatrixTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) {
- class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
- public:
- void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
-
- Matrix4 expected;
- expected.loadTranslate(35, 45, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
- }
- };
-
- auto layerUpdater =
- TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(30, 40);
- canvas.drawLayer(layerUpdater.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextureLayerCombineMatricesTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) {
- auto layerUpdater =
- TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
- EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi());
-
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- glLayer->setRenderTarget(GL_NONE); // Should be rejected
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawLayer(layerUpdater.get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) {
- class FunctorTestRenderer : public TestRendererBase {
- public:
- void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- }
- };
- Functor noopFunctor;
-
- // 1 million pixel tall view, scrolled down 80%
- auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 1000000, [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.translate(0, -800000);
- canvas.callDrawGLFunction(&noopFunctor, nullptr);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
-
- FunctorTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) {
- class ColorTestRenderer : public TestRendererBase {
- public:
- void onColorOp(const ColorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
- << "Color op should be expanded to bounds of surrounding";
- }
- };
-
- auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 10, 10, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setClipToBounds(false);
- canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
-
- ColorTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) {
- class RenderNodeTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- switch (mIndex++) {
- case 0:
- EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- break;
- case 1:
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- break;
- default:
- ADD_FAILURE();
- }
- }
- };
-
- auto child = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&child](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorDKGRAY);
- canvas.drawRect(0, 0, 200, 200, paint);
-
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(40, 40);
- canvas.drawRenderNode(child.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- RenderNodeTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) {
- class ClippedTestRenderer : public TestRendererBase {
- public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200));
- canvas.drawBitmap(*bitmap, 0, 0, nullptr);
- });
-
- // clip to small area, should see in receiver
- FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ClippedTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) {
- class SaveLayerSimpleTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(180u, width);
- EXPECT_EQ(180u, height);
- return nullptr;
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
- EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
-
- Matrix4 expectedTransform;
- expectedTransform.loadTranslate(-10, -10, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(4, mIndex++);
- EXPECT_EQ(nullptr, offscreenBuffer);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(10, 10, 190, 190, SkPaint());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerSimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) {
- /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
- * - startTemporaryLayer2, rect2 endLayer2
- * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
- * - startFrame, layerOp1, endFrame
- */
- class SaveLayerNestedTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- const int index = mIndex++;
- if (index == 0) {
- EXPECT_EQ(400u, width);
- EXPECT_EQ(400u, height);
- return (OffscreenBuffer*)0x400;
- } else if (index == 3) {
- EXPECT_EQ(800u, width);
- EXPECT_EQ(800u, height);
- return (OffscreenBuffer*)0x800;
- } else {
- ADD_FAILURE();
- }
- return (OffscreenBuffer*)nullptr;
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 6);
- }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(7, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(9, mIndex++); }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 1) {
- EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
- } else if (index == 4) {
- EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
- } else {
- ADD_FAILURE();
- }
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 5) {
- EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
- EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
- EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
- } else {
- ADD_FAILURE();
- }
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- const int index = mIndex++;
- // order isn't important, but we need to see both
- if (index == 10) {
- EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
- } else if (index == 11) {
- EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
- } else {
- ADD_FAILURE();
- }
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 800, 800, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
- {
- canvas.drawRect(0, 0, 800, 800, SkPaint());
- canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
- { canvas.drawRect(0, 0, 400, 400, SkPaint()); }
- canvas.restore();
- }
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerNestedTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(12, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_contentRejection) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect);
- canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
-
- // draw within save layer may still be recorded, but shouldn't be drawn
- canvas.drawRect(200, 200, 400, 400, SkPaint());
-
- canvas.restore();
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- // should see no ops, even within the layer, since the layer should be rejected
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) {
- class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_NE(nullptr, op.paint);
- ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
- EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedSimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) {
- class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
- << "Bounds rect should round out";
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {}
- void onRectOp(const RectOp& op, const BakedOpState& state) override {}
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
- << "Bounds rect should round out";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
- 128, (SaveFlags::Flags)(0));
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedRoundTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
- class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_GT(4, index);
- EXPECT_EQ(5, op.unmappedBounds.getWidth());
- EXPECT_EQ(5, op.unmappedBounds.getHeight());
- if (index == 0) {
- EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
- } else if (index == 1) {
- EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
- } else if (index == 2) {
- EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
- } else if (index == 3) {
- EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
- }
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- ASSERT_EQ(op.vertexCount, 16u);
- for (size_t i = 0; i < op.vertexCount; i++) {
- auto v = op.vertices[i];
- EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
- EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
- }
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(5, mIndex++);
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_LT(5, mIndex++);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
-
- int restoreTo = canvas.save(SaveFlags::MatrixClip);
- canvas.scale(2, 2);
- canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
- canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
- canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
- canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- canvas.restoreToCount(restoreTo);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedMergedClearsTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(10, renderer.getIndex())
- << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
- class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_NE(nullptr, op.paint);
- EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
- << "Expect dirty rect as clip";
- ASSERT_NE(nullptr, state.computedState.clipState);
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
- EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- // save smaller than clip, so we get unclipped behavior
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- });
-
- // draw with partial screen dirty, and assert we see that rect later
- FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedClearClipTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_reject) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- // unclipped savelayer + rect both in area that won't intersect with dirty
- canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
- canvas.drawRect(100, 100, 200, 200, SkPaint());
- canvas.restore();
- });
-
- // draw with partial screen dirty that doesn't intersect with savelayer
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
- * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
- * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
- */
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) {
- class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
- EXPECT_EQ(0, mIndex++); // savelayer first
- return (OffscreenBuffer*)0xabcd;
- }
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 1 || index == 7);
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 8);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- Matrix4 expected;
- expected.loadTranslate(-100, -100, 0);
- EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
- EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 4 || index == 10);
- }
- void endLayer() override { EXPECT_EQ(5, mIndex++); }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(6, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(9, mIndex++);
- EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(11, mIndex++); }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(12, mIndex++);
- EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 600, 600, // 500x500 triggers clipping
- [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
- canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
- canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
- canvas.drawRect(200, 200, 300, 300, SkPaint());
- canvas.restore();
- canvas.restore();
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedComplexTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(13, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) {
- class HwLayerSimpleTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
- EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
-
- EXPECT_TRUE(state.computedState.transform.isIdentity())
- << "Transform should be reset within layer";
-
- EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
- << "Damage rect should be used to clip layer content";
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(3, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(5, mIndex++); }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
- OffscreenBuffer** layerHandle = node->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- *layerHandle = &layer;
-
- auto syncedNode = TestUtils::getSyncedNode(node);
-
- // only enqueue partial damage
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- HwLayerSimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(6, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) {
- /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
- * - startRepaintLayer(child), rect(grey), endLayer
- * - startTemporaryLayer, drawLayer(child), endLayer
- * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
- * - startFrame, drawLayer(parent), endLayerb
- */
- class HwLayerComplexTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
- EXPECT_EQ(3, mIndex++); // savelayer first
- return (OffscreenBuffer*)0xabcd;
- }
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- int index = mIndex++;
- if (index == 0) {
- // starting inner layer
- EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
- } else if (index == 6) {
- // starting outer layer
- EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
- } else {
- ADD_FAILURE();
- }
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- if (index == 1) {
- // inner layer's rect (white)
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- } else if (index == 7) {
- // outer layer's rect (grey)
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- } else {
- ADD_FAILURE();
- }
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 5 || index == 9);
- }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(10, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- OffscreenBuffer* layer = *op.layerHandle;
- int index = mIndex++;
- if (index == 4) {
- EXPECT_EQ(100u, layer->viewportWidth);
- EXPECT_EQ(100u, layer->viewportHeight);
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
- } else if (index == 11) {
- EXPECT_EQ(200u, layer->viewportWidth);
- EXPECT_EQ(200u, layer->viewportHeight);
- } else {
- ADD_FAILURE();
- }
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(12, mIndex++); }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(13, mIndex++);
- }
- };
-
- auto child = TestUtils::createNode<RecordingCanvas>(
- 50, 50, 150, 150, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
- OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- *(child->getLayerHandle()) = &childLayer;
-
- RenderNode* childPtr = child.get();
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- SkPaint paint;
- paint.setColor(SK_ColorDKGRAY);
- canvas.drawRect(0, 0, 200, 200, paint);
-
- canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
- canvas.drawRenderNode(childPtr);
- canvas.restore();
- });
- OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
- *(parent->getLayerHandle()) = &parentLayer;
-
- auto syncedNode = TestUtils::getSyncedNode(parent);
-
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
- layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- HwLayerComplexTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(14, renderer.getIndex());
-
- // clean up layer pointers, so we can safely destruct RenderNodes
- *(child->getLayerHandle()) = nullptr;
- *(parent->getLayerHandle()) = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) {
- class BuildLayerTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
- EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
- }
- void onColorOp(const ColorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
-
- EXPECT_TRUE(state.computedState.transform.isIdentity())
- << "Transform should be reset within layer";
-
- EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
- << "Damage rect should be used to clip layer content";
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- void endFrame(const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- });
- OffscreenBuffer** layerHandle = node->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- *layerHandle = &layer;
-
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
-
- // only enqueue partial damage
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
-
- // Draw, but pass empty node list, so no work is done for primary frame
- FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
- BuildLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-namespace {
-
-static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
- SkPaint paint;
- // order put in blue channel, transparent so overlapped content doesn't get rejected
- paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
- canvas->drawRect(0, 0, 100, 100, paint);
-}
-static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedRect(&canvas, expectedDrawOrder);
- });
- node->mutateStagingProperties().setTranslationZ(z);
- node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
- canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
-}
-
-static void drawOrderedNode(
- Canvas* canvas, uint8_t expectedDrawOrder,
- std::function<void(RenderProperties& props, RecordingCanvas& canvas)> setup) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100,
- [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedRect(&canvas, expectedDrawOrder);
- if (setup) {
- setup(props, canvas);
- }
- });
- canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
-}
-
-class ZReorderTestRenderer : public TestRendererBase {
-public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
- EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
- }
-};
-
-} // end anonymous namespace
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, zReorder) {
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.insertReorderBarrier(true);
- canvas.insertReorderBarrier(false);
- drawOrderedNode(&canvas, 0,
- 10.0f); // in reorder=false at this point, so played inorder
- drawOrderedRect(&canvas, 1);
- canvas.insertReorderBarrier(true);
- drawOrderedNode(&canvas, 6, 2.0f);
- drawOrderedRect(&canvas, 3);
- drawOrderedNode(&canvas, 4, 0.0f);
- drawOrderedRect(&canvas, 5);
- drawOrderedNode(&canvas, 2, -2.0f);
- drawOrderedNode(&canvas, 7, 2.0f);
- canvas.insertReorderBarrier(false);
- drawOrderedRect(&canvas, 8);
- drawOrderedNode(&canvas, 9,
- -10.0f); // in reorder=false at this point, so played inorder
- canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op
- drawOrderedRect(&canvas, 11);
- drawOrderedNode(&canvas, 10, -1.0f);
- canvas.insertReorderBarrier(false);
- canvas.insertReorderBarrier(true); // test with two empty reorder sections
- canvas.insertReorderBarrier(true);
- canvas.insertReorderBarrier(false);
- drawOrderedRect(&canvas, 12);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(13, renderer.getIndex());
-};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) {
- static const int scrollX = 5;
- static const int scrollY = 10;
- class ProjectionReorderTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
-
- Matrix4 expectedMatrix;
- switch (index) {
- case 0:
- EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- expectedMatrix.loadIdentity();
- EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
- break;
- case 1:
- EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
- ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
- EXPECT_EQ(Rect(-35, -30, 45, 50),
- Rect(state.computedState.localProjectionPathMask->getBounds()));
- break;
- case 2:
- EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
- EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
- expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
- EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
- break;
- default:
- ADD_FAILURE();
- }
- EXPECT_EQ(expectedMatrix, state.computedState.transform);
- }
- };
-
- /**
- * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
- * with a projecting child (P) of its own. P would normally draw between B and C's "background"
- * draw, but because it is projected backwards, it's drawn in between B and C.
- *
- * The parent is scrolled by scrollX/scrollY, but this does not affect the background
- * (which isn't affected by scroll).
- */
- auto receiverBackground = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectionReceiver(true);
- // scroll doesn't apply to background, so undone via translationX/Y
- // NOTE: translationX/Y only! no other transform properties may be set for a proj
- // receiver!
- properties.setTranslationX(scrollX);
- properties.setTranslationY(scrollY);
-
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
- auto projectingRipple = TestUtils::createNode<RecordingCanvas>(
- 50, 0, 100, 50, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectBackwards(true);
- properties.setClipToBounds(false);
- SkPaint paint;
- paint.setColor(SK_ColorDKGRAY);
- canvas.drawRect(-10, -10, 60, 60, paint);
- });
- auto child = TestUtils::createNode<RecordingCanvas>(
- 0, 50, 100, 100,
- [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorBLUE);
- canvas.drawRect(0, 0, 100, 50, paint);
- canvas.drawRenderNode(projectingRipple.get());
- });
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100,
- [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
- // Set a rect outline for the projecting ripple to be masked against.
- properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
-
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(-scrollX,
- -scrollY); // Apply scroll (note: bg undoes this internally)
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ProjectionReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) {
- static const int scrollX = 5;
- static const int scrollY = 10;
- class ProjectionHwLayerTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- }
- void onArcOp(const ArcOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
- }
- void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
- Matrix4 expected;
- expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
- EXPECT_EQ(expected, state.computedState.transform);
- EXPECT_EQ(Rect(-85, -80, 295, 300),
- Rect(state.computedState.localProjectionPathMask->getBounds()));
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(5, mIndex++);
- ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
- }
- };
- auto receiverBackground = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectionReceiver(true);
- // scroll doesn't apply to background, so undone via translationX/Y
- // NOTE: translationX/Y only! no other transform properties may be set for a proj
- // receiver!
- properties.setTranslationX(scrollX);
- properties.setTranslationY(scrollY);
-
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- });
- auto projectingRipple = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectBackwards(true);
- properties.setClipToBounds(false);
- canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
- });
- auto child = TestUtils::createNode<RecordingCanvas>(
- 100, 100, 300, 300,
- [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawRenderNode(projectingRipple.get());
- canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
- });
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400,
- [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
- // Set a rect outline for the projecting ripple to be masked against.
- properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
- canvas.translate(-scrollX,
- -scrollY); // Apply scroll (note: bg undoes this internally)
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- });
-
- OffscreenBuffer** layerHandle = child->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would, setting windowTransform
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
- Matrix4 windowTransform;
- windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
- layer.setWindowTransform(windowTransform);
- *layerHandle = &layer;
-
- auto syncedNode = TestUtils::getSyncedNode(parent);
-
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- ProjectionHwLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(6, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) {
- static const int scrollX = 500000;
- static const int scrollY = 0;
- class ProjectionChildScrollTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_NE(nullptr, state.computedState.clipState);
- ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
- ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- };
- auto receiverBackground = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectionReceiver(true);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- });
- auto projectingRipple = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) {
- // scroll doesn't apply to background, so undone via translationX/Y
- // NOTE: translationX/Y only! no other transform properties may be set for a proj
- // receiver!
- properties.setTranslationX(scrollX);
- properties.setTranslationY(scrollY);
- properties.setProjectBackwards(true);
- properties.setClipToBounds(false);
- canvas.drawOval(0, 0, 200, 200, SkPaint());
- });
- auto child = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400,
- [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
- // Record time clip will be ignored by projectee
- canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
-
- canvas.translate(-scrollX,
- -scrollY); // Apply scroll (note: bg undoes this internally)
- canvas.drawRenderNode(projectingRipple.get());
- });
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400,
- [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ProjectionChildScrollTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-// creates a 100x100 shadow casting node with provided translationZ
-static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
- return TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setTranslationZ(translationZ);
- properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) {
- class ShadowTestRenderer : public TestRendererBase {
- public:
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
- EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
- EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
-
- Matrix4 expectedZ;
- expectedZ.loadTranslate(0, 0, 5);
- EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- };
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) {
- class ShadowSaveLayerTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- return nullptr;
- }
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
- EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endLayer() override { EXPECT_EQ(3, mIndex++); }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(5, mIndex++);
- }
- };
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- // save/restore outside of reorderBarrier, so they don't get moved out of place
- canvas.translate(20, 10);
- int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- canvas.insertReorderBarrier(false);
- canvas.restoreToCount(count);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 50},
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowSaveLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(6, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) {
- class ShadowHwLayerTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- }
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
- EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
- EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endLayer() override { EXPECT_EQ(3, mIndex++); }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- };
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 50, 60, 150, 160, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.insertReorderBarrier(true);
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(20, 10);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- canvas.restore();
- });
- OffscreenBuffer** layerHandle = parent->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would, setting windowTransform
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- Matrix4 windowTransform;
- windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
- layer.setWindowTransform(windowTransform);
- *layerHandle = &layer;
-
- auto syncedNode = TestUtils::getSyncedNode(parent);
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 30},
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- ShadowHwLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) {
- class ShadowLayeringTestRenderer : public TestRendererBase {
- public:
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 0 || index == 1);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 3);
- }
- };
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 50},
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowLayeringTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) {
- class ShadowClippingTestRenderer : public TestRendererBase {
- public:
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
- << "Shadow must respect pre-barrier canvas clip value.";
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- };
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- // Apply a clip before the reorder barrier/shadow casting child is drawn.
- // This clip must be applied to the shadow cast by the child.
- canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect);
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 50},
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowClippingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-static void testProperty(
- std::function<void(RenderProperties&)> propSetupCallback,
- std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
- class PropertyTestRenderer : public TestRendererBase {
- public:
- explicit PropertyTestRenderer(
- std::function<void(const RectOp&, const BakedOpState&)> callback)
- : mCallback(callback) {}
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(mIndex++, 0);
- mCallback(op, state);
- }
- std::function<void(const RectOp&, const BakedOpState&)> mCallback;
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
- propSetupCallback(props);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- PropertyTestRenderer renderer(opValidateCallback);
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
- testProperty(
- [](RenderProperties& properties) {
- properties.setAlpha(0.5f);
- properties.setHasOverlappingRendering(false);
- },
- [](const RectOp& op, const BakedOpState& state) {
- EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) {
- testProperty(
- [](RenderProperties& properties) {
- properties.setClipToBounds(true);
- properties.setClipBounds(Rect(10, 20, 300, 400));
- },
- [](const RectOp& op, const BakedOpState& state) {
- EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
- << "Clip rect should be intersection of node bounds and clip bounds";
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) {
- testProperty(
- [](RenderProperties& properties) {
- properties.mutableRevealClip().set(true, 50, 50, 25);
- },
- [](const RectOp& op, const BakedOpState& state) {
- ASSERT_NE(nullptr, state.roundRectClipState);
- EXPECT_TRUE(state.roundRectClipState->highPriority);
- EXPECT_EQ(25, state.roundRectClipState->radius);
- EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) {
- testProperty(
- [](RenderProperties& properties) {
- properties.mutableOutline().setShouldClip(true);
- properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
- },
- [](const RectOp& op, const BakedOpState& state) {
- ASSERT_NE(nullptr, state.roundRectClipState);
- EXPECT_FALSE(state.roundRectClipState->highPriority);
- EXPECT_EQ(5, state.roundRectClipState->radius);
- EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) {
- testProperty(
- [](RenderProperties& properties) {
- properties.setLeftTopRightBottom(10, 10, 110, 110);
-
- SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
- properties.setStaticMatrix(&staticMatrix);
-
- // ignored, since static overrides animation
- SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
- properties.setAnimationMatrix(&animationMatrix);
-
- properties.setTranslationX(10);
- properties.setTranslationY(20);
- properties.setScaleX(0.5f);
- properties.setScaleY(0.7f);
- },
- [](const RectOp& op, const BakedOpState& state) {
- Matrix4 matrix;
- matrix.loadTranslate(10, 10, 0); // left, top
- matrix.scale(1.2f, 1.2f, 1); // static matrix
- // ignore animation matrix, since static overrides it
-
- // translation xy
- matrix.translate(10, 20);
-
- // scale xy (from default pivot - center)
- matrix.translate(50, 50);
- matrix.scale(0.5f, 0.7f, 1);
- matrix.translate(-50, -50);
- EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
- << "Op draw matrix must match expected combination of transformation "
- "properties";
- });
-}
-
-struct SaveLayerAlphaData {
- uint32_t layerWidth = 0;
- uint32_t layerHeight = 0;
- Rect rectClippedBounds;
- Matrix4 rectMatrix;
- Matrix4 drawLayerMatrix;
-};
-/**
- * Constructs a view to hit the temporary layer alpha property implementation:
- * a) 0 < alpha < 1
- * b) too big for layer (larger than maxTextureSize)
- * c) overlapping rendering content
- * returning observed data about layer size and content clip/transform.
- *
- * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
- * (for efficiency, and to fit in layer size constraints) based on parent clip.
- */
-void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
- std::function<void(RenderProperties&)> propSetupCallback) {
- class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
- public:
- explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) : mOutData(outData) {}
-
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- mOutData->layerWidth = width;
- mOutData->layerHeight = height;
- return nullptr;
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
-
- mOutData->rectClippedBounds = state.computedState.clippedBounds;
- mOutData->rectMatrix = state.computedState.transform;
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- mOutData->drawLayerMatrix = state.computedState.transform;
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(4, mIndex++);
- }
-
- private:
- SaveLayerAlphaData* mOutData;
- };
-
- ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
- << "Node must be bigger than max texture size to exercise saveLayer codepath";
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 10000, 10000,
- [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setHasOverlappingRendering(true);
- properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
- // apply other properties
- propSetupCallback(properties);
-
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 10000, 10000, paint);
- });
- auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*syncedNode);
-
- SaveLayerAlphaClipTestRenderer renderer(outObservedData);
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-
- // assert, since output won't be valid if we haven't seen a save layer triggered
- ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
- SaveLayerAlphaData observedData;
- testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
- properties.setTranslationX(10); // offset rendering content
- properties.setTranslationY(-2000); // offset rendering content
- });
- EXPECT_EQ(190u, observedData.layerWidth);
- EXPECT_EQ(200u, observedData.layerHeight);
- EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
- << "expect content to be clipped to screen area";
- Matrix4 expected;
- expected.loadTranslate(0, -2000, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
- << "expect content to be translated as part of being clipped";
- expected.loadTranslate(10, 0, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix)
- << "expect drawLayer to be translated as part of being clipped";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
- SaveLayerAlphaData observedData;
- testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
- // Translate and rotate the view so that the only visible part is the top left corner of
- // the view. It will form an isosceles right triangle with a long side length of 200 at the
- // bottom of the viewport.
- properties.setTranslationX(100);
- properties.setTranslationY(100);
- properties.setPivotX(0);
- properties.setPivotY(0);
- properties.setRotation(45);
- });
- // ceil(sqrt(2) / 2 * 200) = 142
- EXPECT_EQ(142u, observedData.layerWidth);
- EXPECT_EQ(142u, observedData.layerHeight);
- EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
- EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
- SaveLayerAlphaData observedData;
- testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
- properties.setPivotX(0);
- properties.setPivotY(0);
- properties.setScaleX(2);
- properties.setScaleY(0.5f);
- });
- EXPECT_EQ(100u, observedData.layerWidth);
- EXPECT_EQ(400u, observedData.layerHeight);
- EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
- EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) {
- class ClipReplaceTestRenderer : public TestRendererBase {
- public:
- void onColorOp(const ColorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_TRUE(op.localClip->intersectWithRoot);
- EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect)
- << "Expect resolved clip to be intersection of viewport clip and clip op";
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 20, 20, 30, 30, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated);
- canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ClipReplaceTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
- /* R is backward projected on B
- A
- / \
- B C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) {
- /* R is backward projected on E
- A
- / | \
- / | \
- B C E
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 3, [](RenderProperties& props,
- RecordingCanvas& canvas) { // drawn as 2
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) { // drawn as 3
- props.setProjectionReceiver(true);
- }); // nodeE
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) {
- /* R is backward projected without receiver
- A
- / \
- B C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 255,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- // not having a projection receiver is an undefined behavior
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) {
- /* R is backward projected on C
- A
- / \
- B C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 255,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- // having a node that is projected on itself is an
- // undefined/unexpected behavior
- props.setProjectionReceiver(true);
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) {
- // TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a
- // bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical
- // tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling
- /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below),
- but for some reason it is drawn.
- A
- /|\
- / | \
- B C R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) {}); // nodeC
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) {
- /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
- A
- |
- G
- /|\
- / | \
- B C R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props,
- RecordingCanvas& canvas) { // G
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) { // C
- }); // nodeC
- drawOrderedNode(&canvas, 255,
- [](RenderProperties& props, RecordingCanvas& canvas) { // R
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeG
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
- /* R is backward projected on B
- A
- |
- B
- |
- C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeB
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) {
- /* B and G are receivables, R is backward projected
- A
- / \
- B C
- / \
- G R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) { // C
- drawOrderedNode(&canvas, 3,
- [](RenderProperties& props, RecordingCanvas& canvas) { // G
- props.setProjectionReceiver(true);
- }); // nodeG
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) { // R
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
- /* B and G are receivables, G is backward projected
- A
- / \
- B C
- / \
- G R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) { // C
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) { // G
- props.setProjectionReceiver(true);
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeG
- drawOrderedNode(&canvas, 3,
- [](RenderProperties& props, RecordingCanvas& canvas) { // R
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
- /* B and G are receivables, R is backward projected
- A
- / \
- B C
- / \
- G D
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props,
- RecordingCanvas& canvas) { // C
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) { // G
- props.setProjectionReceiver(true);
- }); // nodeG
- drawOrderedNode(
- &canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { // D
- drawOrderedNode(&canvas, 3, [](RenderProperties& props,
- RecordingCanvas& canvas) { // R
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeD
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
-}
-
-} // namespace uirenderer
-} // namespace android