Support buildLayer in new pipeline
bug:26561995
bug:27620686
Change-Id: I6c39f9a077e7e6002d3c01b8888238fd17b0f02a
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index b18836f..9c46c00 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -37,7 +37,8 @@
const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches)
: mCanvasState(*this)
, mCaches(caches)
- , mLightRadius(lightGeometry.radius) {
+ , mLightRadius(lightGeometry.radius)
+ , mDrawFbo0(!nodes.empty()) {
ATRACE_NAME("prepare drawing commands");
mLayerBuilders.reserve(layers.entries().size());
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index 02c05cb..e418227 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -137,12 +137,14 @@
}
GL_CHECKPOINT(MODERATE);
- const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
- renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
- GL_CHECKPOINT(MODERATE);
- fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT(MODERATE);
- renderer.endFrame(fbo0.repaintRect);
+ if (CC_LIKELY(mDrawFbo0)) {
+ const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
+ renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
+ GL_CHECKPOINT(MODERATE);
+ fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
+ GL_CHECKPOINT(MODERATE);
+ renderer.endFrame(fbo0.repaintRect);
+ }
}
void dump() const {
@@ -239,6 +241,8 @@
// contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
LinearAllocator mAllocator;
+
+ const bool mDrawFbo0;
};
}; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index eee5278..6933b2f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -86,10 +86,12 @@
freePrefetechedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
+#if !HWUI_NEW_OPS
if (mCanvas) {
delete mCanvas;
mCanvas = nullptr;
}
+#endif
}
void CanvasContext::setSurface(Surface* surface) {
@@ -587,9 +589,11 @@
void CanvasContext::buildLayer(RenderNode* node) {
ATRACE_CALL();
- if (!mEglManager.hasEglContext() || !mCanvas) {
- return;
- }
+ if (!mEglManager.hasEglContext()) return;
+#if !HWUI_NEW_OPS
+ if (!mCanvas) return;
+#endif
+
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
@@ -609,7 +613,15 @@
node->setPropertyFieldsDirty(RenderNode::GENERIC);
#if HWUI_NEW_OPS
- // TODO: support buildLayer
+ static const std::vector< sp<RenderNode> > emptyNodeList;
+ auto& caches = Caches::getInstance();
+ FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
+ emptyNodeList, mLightGeometry, mContentDrawBounds, caches);
+ mLayerUpdateQueue.clear();
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+ mOpaque, mLightInfo);
+ LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
#else
mCanvas->markLayersAsBuildLayers();
mCanvas->flushLayerUpdates();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6706c30..6d0889e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -196,10 +196,11 @@
RingBuffer<SwapHistory, 3> mSwapHistory;
bool mOpaque;
- OpenGLRenderer* mCanvas = nullptr;
#if HWUI_NEW_OPS
BakedOpRenderer::LightInfo mLightInfo;
FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 };
+#else
+ OpenGLRenderer* mCanvas = nullptr;
#endif
bool mHaveNewSurface = false;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index ba22f91..7dfafb9 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -30,6 +30,7 @@
namespace uirenderer {
const LayerUpdateQueue sEmptyLayerUpdateQueue;
+const std::vector< sp<RenderNode> > sEmptyNodeList;
const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
@@ -216,6 +217,49 @@
<< "Expect number of ops = 2 * loop count";
}
+RENDERTHREAD_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";
+ }
+ };
+
+ // Pass empty node list, so no work is enqueued for Fbo0
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ sEmptyNodeList, sLightGeometry, Caches::getInstance());
+ EmptyNoFbo0TestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+}
+
+RENDERTHREAD_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(10, 10, 110, 110,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ // no drawn content
+ });
+ auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+
+ // Draw, but pass empty node list, so no work is done for primary frame
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedNodeList, sLightGeometry, Caches::getInstance());
+ EmptyWithFbo0TestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
+ " but fbo0 update lifecycle should still be observed";
+}
+
RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
public:
@@ -1152,6 +1196,64 @@
*(parent->getLayerHandle()) = nullptr;
}
+
+RENDERTHREAD_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(10, 10, 110, 110,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.mutateLayerProperties().setType(LayerType::RenderLayer);
+ canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ });
+ 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 syncedNodeList = TestUtils::createSyncedNodeList(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, SkRect::MakeWH(1, 1), 1, 1,
+ sEmptyNodeList, 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;
+}
+
static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
SkPaint paint;
paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel