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:
Chris Craik
2016-04-07 23:30:19 +00:00
committed by android-build-merger
12 changed files with 163 additions and 13 deletions

View File

@@ -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 += \

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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";
}
}
}

View File

@@ -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
///////////////////////////////////////////////////////////////////////////////

View File

@@ -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.
*/

View File

@@ -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";

View File

@@ -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

View File

@@ -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());

View 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());
}
}