diff options
author | 2016-04-07 13:51:07 -0700 | |
---|---|---|
committer | 2016-04-07 15:02:39 -0700 | |
commit | 04d46eb69fb4f4c4c332c36c6ae845da3b2ae848 (patch) | |
tree | e6a598b4e1d29bf25ec139a07e8a0867b8b745d9 | |
parent | f2cf5987a6aea262187208ad269ceb1534955704 (diff) |
Support replace op in new pipeline
bug:26562461
Change-Id: Ie48d2da30f5e9d9abe88a5cd973dfb26e38abf63
-rw-r--r-- | libs/hwui/Android.mk | 5 | ||||
-rw-r--r-- | libs/hwui/BakedOpState.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/ClipArea.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/ClipArea.h | 6 | ||||
-rw-r--r-- | libs/hwui/FrameBuilder.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/OpDumper.cpp | 7 | ||||
-rw-r--r-- | libs/hwui/Snapshot.cpp | 27 | ||||
-rw-r--r-- | libs/hwui/Snapshot.h | 4 | ||||
-rw-r--r-- | libs/hwui/tests/unit/ClipAreaTests.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/tests/unit/FrameBuilderTests.cpp | 23 | ||||
-rw-r--r-- | libs/hwui/tests/unit/RecordingCanvasTests.cpp | 13 | ||||
-rw-r--r-- | libs/hwui/tests/unit/SnapshotTests.cpp | 74 |
12 files changed, 163 insertions, 13 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index be816f78e4ff..0606b0b1a158 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -248,16 +248,17 @@ LOCAL_SRC_FILES += \ tests/unit/FatVectorTests.cpp \ tests/unit/GlopBuilderTests.cpp \ tests/unit/GpuMemoryTrackerTests.cpp \ + tests/unit/GradientCacheTests.cpp \ tests/unit/LayerUpdateQueueTests.cpp \ tests/unit/LinearAllocatorTests.cpp \ tests/unit/MatrixTests.cpp \ tests/unit/OffscreenBufferPoolTests.cpp \ tests/unit/RenderNodeTests.cpp \ tests/unit/SkiaBehaviorTests.cpp \ + tests/unit/SnapshotTests.cpp \ tests/unit/StringUtilsTests.cpp \ tests/unit/TextDropShadowCacheTests.cpp \ - tests/unit/VectorDrawableTests.cpp \ - tests/unit/GradientCacheTests.cpp + tests/unit/VectorDrawableTests.cpp ifeq (true, $(HWUI_NEW_OPS)) LOCAL_SRC_FILES += \ diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp index b70d5868c718..9f98241c6caa 100644 --- a/libs/hwui/BakedOpState.cpp +++ b/libs/hwui/BakedOpState.cpp @@ -50,7 +50,7 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s } // resolvedClipRect = intersect(parentMatrix * localClip, parentClip) - clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator, + clipState = snapshot.serializeIntersectedClip(allocator, recordedOp.localClip, *(snapshot.transform)); LOG_ALWAYS_FATAL_IF(!clipState, "must clip!"); @@ -85,8 +85,7 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, const Matrix4& localTransform, const ClipBase* localClip) { transform.loadMultiply(*snapshot.transform, localTransform); - clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator, - localClip, *(snapshot.transform)); + clipState = snapshot.serializeIntersectedClip(allocator, localClip, *(snapshot.transform)); clippedBounds = clipState->rect; clipSideFlags = OpClipSideFlags::Full; localProjectionPathMask = nullptr; diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp index f886dda86deb..35fe06dc5e21 100644 --- a/libs/hwui/ClipArea.cpp +++ b/libs/hwui/ClipArea.cpp @@ -217,6 +217,7 @@ void ClipArea::setClip(float left, float top, float right, float bottom) { void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { + if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; onClipUpdated(); switch (mMode) { @@ -233,6 +234,7 @@ void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, } void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { + if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; onClipUpdated(); enterRegionMode(); @@ -242,6 +244,7 @@ void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { + if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; onClipUpdated(); SkMatrix skTransform; @@ -379,6 +382,7 @@ const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) { serialization->rect.set(mClipRegion.getBounds()); break; } + serialization->intersectWithRoot = mReplaceOpObserved; // TODO: this is only done for draw time, should eventually avoid for record time serialization->rect.snapToPixelBoundaries(); mLastSerialization = serialization; diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index 1654eb8f02e4..6eb2eef5a5f9 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -103,6 +103,7 @@ struct ClipBase { : mode(ClipMode::Rectangle) , rect(rect) {} const ClipMode mode; + bool intersectWithRoot = false; // Bounds of the clipping area, used to define the scissor, and define which // portion of the stencil is updated/used Rect rect; @@ -173,8 +174,8 @@ public: return mMode == ClipMode::RectangleList; } - const ClipBase* serializeClip(LinearAllocator& allocator); - const ClipBase* serializeIntersectedClip(LinearAllocator& allocator, + WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator); + WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator, const ClipBase* recordedClip, const Matrix4& recordedClipTransform); void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform); @@ -214,6 +215,7 @@ private: ClipMode mMode; bool mPostViewportClipObserved = false; + bool mReplaceOpObserved = false; /** * If mLastSerialization is non-null, it represents an already serialized copy diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index 0401f2d44776..f12e52325af1 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -462,7 +462,7 @@ void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) { int count = mCanvasState.save(SaveFlags::MatrixClip); // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix) - mCanvasState.writableSnapshot()->mutateClipArea().applyClip(op.localClip, + mCanvasState.writableSnapshot()->applyClip(op.localClip, *mCanvasState.currentSnapshot()->transform); mCanvasState.concatMatrix(op.localMatrix); diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp index c34cfbe1b259..cab93e8faf6a 100644 --- a/libs/hwui/OpDumper.cpp +++ b/libs/hwui/OpDumper.cpp @@ -33,10 +33,15 @@ void OpDumper::dump(const RecordedOp& op, std::ostream& output, int level) { op.localMatrix.mapRect(localBounds); output << sOpNameLut[op.opId] << " " << localBounds; - if (op.localClip && !op.localClip->rect.contains(localBounds)) { + if (op.localClip + && (!op.localClip->rect.contains(localBounds) || op.localClip->intersectWithRoot)) { output << std::fixed << std::setprecision(0) << " clip=" << op.localClip->rect << " mode=" << (int)op.localClip->mode; + + if (op.localClip->intersectWithRoot) { + output << " iwr"; + } } } diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index d7842801fdd8..2c9c9d90f686 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -242,6 +242,33 @@ void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* p #endif } +static Snapshot* getClipRoot(Snapshot* target) { + while (target->previous && target->previous->previous) { + target = target->previous; + } + return target; +} + +const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator, + const ClipBase* recordedClip, const Matrix4& recordedClipTransform) { + auto target = this; + if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { + // Clip must be intersected with root, instead of current clip. + target = getClipRoot(this); + } + + return target->mClipArea->serializeIntersectedClip(allocator, + recordedClip, recordedClipTransform); +} + +void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) { + if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { + // current clip is being replaced, but must intersect with clip root + *mClipArea = *(getClipRoot(this)->mClipArea); + } + mClipArea->applyClip(recordedClip, transform); +} + /////////////////////////////////////////////////////////////////////////////// // Queries /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 3a01d049109c..d8f926ef3925 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -170,6 +170,10 @@ public: const ClipArea& getClipArea() const { return *mClipArea; } ClipArea& mutateClipArea() { return *mClipArea; } + WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator, + const ClipBase* recordedClip, const Matrix4& recordedClipTransform); + void applyClip(const ClipBase* clip, const Matrix4& transform); + /** * Resets the clip to the specified rect. */ diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp index 822d04f61eb5..b864703348b9 100644 --- a/libs/hwui/tests/unit/ClipAreaTests.cpp +++ b/libs/hwui/tests/unit/ClipAreaTests.cpp @@ -132,8 +132,7 @@ TEST(ClipArea, serializeClip) { auto serializedClip = area.serializeClip(allocator); ASSERT_NE(nullptr, serializedClip); ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode); - auto clipRect = reinterpret_cast<const ClipRect*>(serializedClip); - EXPECT_EQ(Rect(200, 200), clipRect->rect); + EXPECT_EQ(Rect(200, 200), serializedClip->rect); EXPECT_EQ(serializedClip, area.serializeClip(allocator)) << "Requery of clip on unmodified ClipArea must return same pointer."; } @@ -192,8 +191,7 @@ TEST(ClipArea, serializeIntersectedClip) { auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); ASSERT_NE(nullptr, resolvedClip); ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode); - EXPECT_EQ(Rect(100, 100, 200, 200), - reinterpret_cast<const ClipRect*>(resolvedClip)->rect); + EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect); EXPECT_EQ(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) << "Must return previous serialization, since input is same"; diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index 0ea246f00fb7..98774391fd30 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -1946,5 +1946,28 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); } +RENDERTHREAD_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(20, 20, 30, 30, + [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op); + canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); + }); + + FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, + TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + ClipReplaceTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(1, renderer.getIndex()); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index 58376c64d518..c49ff71c0af0 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -569,6 +569,19 @@ TEST(RecordingCanvas, firstClipWillReplace) { EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip); } +TEST(RecordingCanvas, replaceClipIntersectWithRoot) { + auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { + canvas.save(SaveFlags::MatrixClip); + canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op); + canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); + canvas.restore(); + }); + ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op"; + // first clip must be preserved, even if it extends beyond canvas bounds + EXPECT_CLIP_RECT(Rect(-10, -10, 110, 110), dl->getOps()[0]->localClip); + EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot); +} + TEST(RecordingCanvas, insertReorderBarrier) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.drawRect(0, 0, 400, 400, SkPaint()); diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp new file mode 100644 index 000000000000..11797a8fc1db --- /dev/null +++ b/libs/hwui/tests/unit/SnapshotTests.cpp @@ -0,0 +1,74 @@ +/* + * 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 <Snapshot.h> + +#include <tests/common/TestUtils.h> + +using namespace android::uirenderer; + +TEST(Snapshot, serializeIntersectedClip) { + auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100)); + auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90)); + auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); + root->previous = actualRoot.get(); + child->previous = root.get(); + + LinearAllocator allocator; + ClipRect rect(Rect(0, 0, 75, 75)); + { + auto intersectWithChild = child->serializeIntersectedClip(allocator, + &rect, Matrix4::identity()); + ASSERT_NE(nullptr, intersectWithChild); + EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child"; + } + + rect.intersectWithRoot = true; + { + auto intersectWithRoot = child->serializeIntersectedClip(allocator, + &rect, Matrix4::identity()); + ASSERT_NE(nullptr, intersectWithRoot); + EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root"; + } +} + +TEST(Snapshot, applyClip) { + auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100)); + auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90)); + root->previous = actualRoot.get(); + + ClipRect rect(Rect(0, 0, 75, 75)); + { + auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); + child->previous = root.get(); + child->applyClip(&rect, Matrix4::identity()); + + EXPECT_TRUE(child->getClipArea().isSimple()); + EXPECT_EQ(Rect(50, 50, 75, 75), child->getRenderTargetClip()); + } + + { + rect.intersectWithRoot = true; + auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); + child->previous = root.get(); + child->applyClip(&rect, Matrix4::identity()); + + EXPECT_TRUE(child->getClipArea().isSimple()); + EXPECT_EQ(Rect(10, 10, 75, 75), child->getRenderTargetClip()); + } +} |