Merge "Support replace op in new pipeline" into nyc-dev
am: d306065
* commit 'd30606575783acd8689cfac604cba51e537b6b77':
Support replace op in new pipeline
Change-Id: Iab37f13a5fca72b2e581a897f7e03c17f9ce0b84
This commit is contained in:
@@ -247,16 +247,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 += \
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
74
libs/hwui/tests/unit/SnapshotTests.cpp
Normal file
74
libs/hwui/tests/unit/SnapshotTests.cpp
Normal file
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user