Merge "Add more RenderNode property support in OpReorderer path"
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 163f7cc..c6b1609 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -359,7 +359,7 @@
void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
- if (node.applyViewProperties(mCanvasState)) {
+ if (node.applyViewProperties(mCanvasState, mAllocator)) {
// not rejected so render
if (node.getLayer()) {
// HW layer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 0601944..15ca718 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -282,9 +282,11 @@
damageSelf(info);
transformUpdateNeeded = true;
#if HWUI_NEW_OPS
- } else if (mLayer->viewportWidth != getWidth() || mLayer->viewportHeight != getHeight()) {
- // TODO: allow it to grow larger
- if (getWidth() > mLayer->texture.width || getHeight() > mLayer->texture.height) {
+ } else if (mLayer->viewportWidth != (uint32_t) getWidth()
+ || mLayer->viewportHeight != (uint32_t)getHeight()) {
+ // TODO: allow node's layer to grow larger
+ if ((uint32_t)getWidth() > mLayer->texture.width
+ || (uint32_t)getHeight() > mLayer->texture.height) {
#else
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
@@ -304,7 +306,7 @@
if (info.errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << getName();
- const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
+ const int maxTextureSize = Caches::getInstance().maxTextureSize;
if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
err << ", size " << getWidth() << "x" << getHeight()
<< " exceeds max size " << maxTextureSize;
@@ -518,7 +520,7 @@
}
}
-bool RenderNode::applyViewProperties(CanvasState& canvasState) const {
+bool RenderNode::applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const {
const Outline& outline = properties().getOutline();
if (properties().getAlpha() <= 0
|| (outline.getShouldClip() && outline.isEmpty())
@@ -542,6 +544,48 @@
canvasState.concatMatrix(*properties().getTransformMatrix());
}
}
+
+ const bool isLayer = properties().effectiveLayerType() != LayerType::None;
+ int clipFlags = properties().getClippingFlags();
+ if (properties().getAlpha() < 1) {
+ if (isLayer) {
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+ }
+ if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) {
+ // simply scale rendering content's alpha
+ canvasState.scaleAlpha(properties().getAlpha());
+ } else {
+ // savelayer needed to create an offscreen buffer
+ Rect layerBounds(0, 0, getWidth(), getHeight());
+ if (clipFlags) {
+ properties().getClippingRectForFlags(clipFlags, &layerBounds);
+ clipFlags = 0; // all clipping done by savelayer
+ }
+ LOG_ALWAYS_FATAL("TODO: savelayer");
+ }
+
+ if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) {
+ // pretend alpha always causes savelayer to warn about
+ // performance problem affecting old versions
+ ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), getWidth(), getHeight());
+ }
+ }
+ if (clipFlags) {
+ Rect clipRect;
+ properties().getClippingRectForFlags(clipFlags, &clipRect);
+ canvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+ SkRegion::kIntersect_Op);
+ }
+
+ // TODO: support nesting round rect clips
+ if (mProperties.getRevealClip().willClip()) {
+ Rect bounds;
+ mProperties.getRevealClip().getBounds(&bounds);
+ canvasState.setClippingRoundRect(allocator,
+ bounds, mProperties.getRevealClip().getRadius());
+ } else if (mProperties.getOutline().willClip()) {
+ canvasState.setClippingOutline(allocator, &(mProperties.getOutline()));
+ }
return !canvasState.quickRejectConservative(
0, 0, properties().getWidth(), properties().getHeight());
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 3500cb2..2c25751 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -171,11 +171,11 @@
return mStagingProperties;
}
- uint32_t getWidth() {
+ int getWidth() const {
return properties().getWidth();
}
- uint32_t getHeight() {
+ int getHeight() const {
return properties().getHeight();
}
@@ -188,7 +188,7 @@
AnimatorManager& animators() { return mAnimatorManager; }
// Returns false if the properties dictate the subtree contained in this RenderNode won't render
- bool applyViewProperties(CanvasState& canvasState) const;
+ bool applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const;
void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index abef806..ca7789e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -16,23 +16,24 @@
#ifndef RENDERNODEPROPERTIES_H
#define RENDERNODEPROPERTIES_H
-#include <algorithm>
-#include <stddef.h>
-#include <vector>
-#include <cutils/compiler.h>
-#include <androidfw/ResourceTypes.h>
-#include <utils/Log.h>
+#include "Caches.h"
+#include "DeviceInfo.h"
+#include "Rect.h"
+#include "RevealClip.h"
+#include "Outline.h"
+#include "utils/MathUtils.h"
#include <SkCamera.h>
#include <SkMatrix.h>
#include <SkRegion.h>
#include <SkXfermode.h>
-#include "Caches.h"
-#include "Rect.h"
-#include "RevealClip.h"
-#include "Outline.h"
-#include "utils/MathUtils.h"
+#include <algorithm>
+#include <stddef.h>
+#include <vector>
+#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
class SkBitmap;
class SkColorFilter;
@@ -608,10 +609,11 @@
}
bool promotedToLayer() const {
- const int maxTextureSize = Caches::getInstance().maxTextureSize;
+ const DeviceInfo* deviceInfo = DeviceInfo::get();
+ LOG_ALWAYS_FATAL_IF(!deviceInfo, "DeviceInfo uninitialized");
return mLayerProperties.mType == LayerType::None
- && mPrimitiveFields.mWidth <= maxTextureSize
- && mPrimitiveFields.mHeight <= maxTextureSize
+ && mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
+ && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize()
&& (mComputedFields.mNeedLayerForFunctors
|| (!MathUtils::isZero(mPrimitiveFields.mAlpha)
&& mPrimitiveFields.mAlpha < 1
diff --git a/libs/hwui/unit_tests/DeviceInfoTests.cpp b/libs/hwui/unit_tests/DeviceInfoTests.cpp
index c3c68ae..17236bd 100644
--- a/libs/hwui/unit_tests/DeviceInfoTests.cpp
+++ b/libs/hwui/unit_tests/DeviceInfoTests.cpp
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-
-#include "DeviceInfo.h"
+#include <DeviceInfo.h>
#include <gtest/gtest.h>
@@ -23,10 +22,9 @@
using namespace android::uirenderer;
TEST(DeviceInfo, basic) {
- const DeviceInfo* di = DeviceInfo::get();
- EXPECT_EQ(nullptr, di) << "DeviceInfo was already initialized?";
+ // can't assert state before init - another test may have initialized the singleton
DeviceInfo::initialize();
- di = DeviceInfo::get();
+ const DeviceInfo* di = DeviceInfo::get();
ASSERT_NE(nullptr, di) << "DeviceInfo initialization failed";
EXPECT_EQ(2048, di->maxTextureSize()) << "Max texture size didn't match";
}
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index 09b10c3..50f210f 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -527,5 +527,120 @@
*(parent->getLayerHandle()) = nullptr;
}
+
+class PropertyTestRenderer : public TestRendererBase {
+public:
+ 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;
+};
+
+static void testProperty(
+ std::function<int(RenderProperties&)> propSetupCallback,
+ std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+ node->setPropertyFieldsDirty(propSetupCallback(node->mutateStagingProperties()));
+ TestUtils::syncNodePropertiesAndDisplayList(node);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(node.get());
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ SkRect::MakeWH(100, 100), 200, 200, nodes);
+
+ PropertyTestRenderer renderer(opValidateCallback);
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
+}
+
+TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
+ testProperty([](RenderProperties& properties) {
+ properties.setAlpha(0.5f);
+ properties.setHasOverlappingRendering(false);
+ return RenderNode::ALPHA | RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
+ });
+}
+
+TEST(OpReorderer, renderPropClipping) {
+ testProperty([](RenderProperties& properties) {
+ properties.setClipToBounds(true);
+ properties.setClipBounds(Rect(10, 20, 300, 400));
+ return RenderNode::GENERIC;
+ }, [](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";
+ });
+}
+
+TEST(OpReorderer, renderPropRevealClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableRevealClip().set(true, 50, 50, 25);
+ return RenderNode::GENERIC;
+ }, [](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);
+ });
+}
+
+TEST(OpReorderer, renderPropOutlineClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableOutline().setShouldClip(true);
+ properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
+ return RenderNode::GENERIC;
+ }, [](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);
+ });
+}
+
+TEST(OpReorderer, 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);
+ return RenderNode::GENERIC
+ | RenderNode::TRANSLATION_X | RenderNode::TRANSLATION_Y
+ | RenderNode::SCALE_X | RenderNode::SCALE_Y;
+ }, [](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";
+ });
+}
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index 770f413..0cf8040 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -16,6 +16,7 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
+#include <DeviceInfo.h>
#include <Matrix.h>
#include <Rect.h>
#include <RenderNode.h>
@@ -90,6 +91,10 @@
}
static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
+ // if RenderNodes are being sync'd/used, device info will be needed, since
+ // DeviceInfo::maxTextureSize() affects layer property
+ DeviceInfo::initialize();
+
sp<RenderNode> node = new RenderNode();
node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);