OSDN Git Service

Initial HW layer support in new reorderer/renderer
authorChris Craik <ccraik@google.com>
Wed, 28 Oct 2015 23:50:44 +0000 (16:50 -0700)
committerChris Craik <ccraik@google.com>
Mon, 2 Nov 2015 20:59:38 +0000 (12:59 -0800)
Shares vast majority of clipped savelayer code, with only minor
differences in lifecycle.

Doesn't yet handle fill region, resize, or window transform.

Change-Id: Iabdd71811590d2b937eb11e1b01ce556ade54a5a

21 files changed:
libs/hwui/Android.mk
libs/hwui/BakedOpRenderer.cpp
libs/hwui/BakedOpRenderer.h
libs/hwui/DisplayList.h
libs/hwui/LayerUpdateQueue.cpp [new file with mode: 0644]
libs/hwui/LayerUpdateQueue.h [new file with mode: 0644]
libs/hwui/OpReorderer.cpp
libs/hwui/OpReorderer.h
libs/hwui/RecordedOp.h
libs/hwui/RecordingCanvas.cpp
libs/hwui/RecordingCanvas.h
libs/hwui/RenderNode.cpp
libs/hwui/RenderNode.h
libs/hwui/TreeInfo.h
libs/hwui/microbench/OpReordererBench.cpp
libs/hwui/renderthread/CanvasContext.cpp
libs/hwui/renderthread/CanvasContext.h
libs/hwui/tests/TreeContentAnimation.cpp
libs/hwui/unit_tests/LayerUpdateQueueTests.cpp [new file with mode: 0644]
libs/hwui/unit_tests/OpReordererTests.cpp
libs/hwui/unit_tests/TestUtils.h

index d94c91d..ae5fa6c 100644 (file)
@@ -56,6 +56,7 @@ hwui_src_files := \
     Layer.cpp \
     LayerCache.cpp \
     LayerRenderer.cpp \
+    LayerUpdateQueue.cpp \
     Matrix.cpp \
     OpenGLRenderer.cpp \
     Patch.cpp \
@@ -204,6 +205,7 @@ LOCAL_SRC_FILES += \
     unit_tests/ClipAreaTests.cpp \
     unit_tests/DamageAccumulatorTests.cpp \
     unit_tests/FatVectorTests.cpp \
+    unit_tests/LayerUpdateQueueTests.cpp \
     unit_tests/LinearAllocatorTests.cpp \
     unit_tests/StringUtilsTests.cpp
 
index 0868853..7055839 100644 (file)
@@ -31,7 +31,9 @@ namespace uirenderer {
 
 OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
         uint32_t viewportWidth, uint32_t viewportHeight)
-        : texture(caches)
+        : viewportWidth(viewportWidth)
+        , viewportHeight(viewportHeight)
+        , texture(caches)
         , texCoords(0, viewportHeight / float(textureHeight), viewportWidth / float(textureWidth), 0) {
     texture.width = textureWidth;
     texture.height = textureHeight;
@@ -52,12 +54,29 @@ OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t
 // BakedOpRenderer
 ////////////////////////////////////////////////////////////////////////////////
 
-OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
+OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(uint32_t width, uint32_t height) {
+    // TODO: get from cache!
+    return new OffscreenBuffer(Caches::getInstance(), width, height, width, height);
+}
+
+void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
+    // destroy and delete, since each clipped saveLayer is only drawn once.
+    offscreenBuffer->texture.deleteTexture();
+
+    // TODO: return texture/offscreenbuffer to cache!
+    delete offscreenBuffer;
+}
+
+OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
     LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
 
-    // TODO: really should be caching these!
-    OffscreenBuffer* buffer = new OffscreenBuffer(mCaches, width, height, width, height);
-    mRenderTarget.offscreenBuffer = buffer;
+    OffscreenBuffer* buffer = createOffscreenBuffer(width, height);
+    startLayer(buffer);
+    return buffer;
+}
+
+void BakedOpRenderer::startLayer(OffscreenBuffer* offscreenBuffer) {
+    mRenderTarget.offscreenBuffer = offscreenBuffer;
 
     // create and bind framebuffer
     mRenderTarget.frameBufferId = mRenderState.genFramebuffer();
@@ -65,7 +84,7 @@ OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
 
     // attach the texture to the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-            buffer->texture.id, 0);
+            offscreenBuffer->texture.id, 0);
     LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
     LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
             "framebuffer incomplete!");
@@ -75,8 +94,7 @@ OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
     glClear(GL_COLOR_BUFFER_BIT);
 
     // Change the viewport & ortho projection
-    setViewport(width, height);
-    return buffer;
+    setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
 }
 
 void BakedOpRenderer::endLayer() {
@@ -212,23 +230,21 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
 
     // TODO: extend this to handle HW layers & paint properties which
     // reside in node.properties().layerProperties()
-    float layerAlpha = (op.paint->getAlpha() / 255.0f) * state.alpha;
+    float layerAlpha = op.alpha * state.alpha;
     const bool tryToSnap = state.computedState.transform.isPureTranslate();
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
             .setMeshTexturedUvQuad(nullptr, buffer->texCoords)
-            .setFillLayer(buffer->texture, op.paint->getColorFilter(), layerAlpha, PaintUtils::getXfermodeDirect(op.paint), Blend::ModeOrderSwap::NoSwap)
+            .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
             .build();
     renderer.renderGlop(state, glop);
 
-    // destroy and delete, since each clipped saveLayer is only drawn once.
-    buffer->texture.deleteTexture();
-
-    // TODO: return texture/offscreenbuffer to cache!
-    delete buffer;
+    if (op.destroy) {
+        BakedOpRenderer::destroyOffscreenBuffer(buffer);
+    }
 }
 
 } // namespace uirenderer
index 16afad4..722bf02 100644 (file)
@@ -37,7 +37,8 @@ class OffscreenBuffer {
 public:
     OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
             uint32_t viewportWidth, uint32_t viewportHeight);
-
+    uint32_t viewportWidth;
+    uint32_t viewportHeight;
     Texture texture;
     Rect texCoords;
     Region region;
@@ -60,12 +61,16 @@ public:
             , mOpaque(opaque) {
     }
 
+    static OffscreenBuffer* createOffscreenBuffer(uint32_t width, uint32_t height);
+    static void destroyOffscreenBuffer(OffscreenBuffer*);
+
     RenderState& renderState() { return mRenderState; }
     Caches& caches() { return mCaches; }
 
     void startFrame(uint32_t width, uint32_t height);
     void endFrame();
-    OffscreenBuffer* startLayer(uint32_t width, uint32_t height);
+    OffscreenBuffer* createLayer(uint32_t width, uint32_t height);
+    void startLayer(OffscreenBuffer* offscreenBuffer);
     void endLayer();
 
     Texture* getTexture(const SkBitmap* bitmap);
index 86796c5..00c4e2d 100644 (file)
@@ -154,7 +154,11 @@ public:
         return allocator.usedSize();
     }
     bool isEmpty() {
+#if HWUI_NEW_OPS
+        return ops.empty();
+#else
         return !hasDrawOps;
+#endif
     }
 
 private:
@@ -179,7 +183,7 @@ private:
     // List of functors
     LsaVector<Functor*> functors;
 
-    bool hasDrawOps;
+    bool hasDrawOps; // only used if !HWUI_NEW_OPS
 
     void cleanupResources();
 };
diff --git a/libs/hwui/LayerUpdateQueue.cpp b/libs/hwui/LayerUpdateQueue.cpp
new file mode 100644 (file)
index 0000000..db5f676
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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 "LayerUpdateQueue.h"
+
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+void LayerUpdateQueue::clear() {
+    mEntries.clear();
+}
+
+void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damage) {
+    damage.doIntersect(0, 0, renderNode->getWidth(), renderNode->getHeight());
+    if (!damage.isEmpty()) {
+        for (Entry& entry : mEntries) {
+            if (CC_UNLIKELY(entry.renderNode == renderNode)) {
+                entry.damage.unionWith(damage);
+                return;
+            }
+        }
+        mEntries.emplace_back(renderNode, damage);
+    }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
new file mode 100644 (file)
index 0000000..be612d2
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
+#define ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
+
+#include "Rect.h"
+#include "utils/Macros.h"
+
+#include <vector>
+#include <unordered_map>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+
+class LayerUpdateQueue {
+    PREVENT_COPY_AND_ASSIGN(LayerUpdateQueue);
+public:
+    struct Entry {
+        Entry(RenderNode* renderNode, const Rect& damage)
+                : renderNode(renderNode)
+                , damage(damage) {}
+        RenderNode* renderNode;
+        Rect damage;
+    };
+
+    LayerUpdateQueue() {}
+    void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
+    void clear();
+    const std::vector<Entry> entries() const { return mEntries; }
+private:
+    std::vector<Entry> mEntries;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
index ddeb336..163f7cc 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "utils/PaintUtils.h"
 #include "RenderNode.h"
+#include "LayerUpdateQueue.h"
 
 #include "SkCanvas.h"
 #include "utils/Trace.h"
@@ -202,6 +203,14 @@ private:
     Rect mClipRect;
 };
 
+OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+        const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+        : width(width)
+        , height(height)
+        , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
+        , beginLayerOp(beginLayerOp)
+        , renderNode(renderNode) {}
+
 // iterate back toward target to see if anything drawn since should overlap the new op
 // if no target, merging ops still iterate to find similar batch to insert after
 void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
@@ -288,33 +297,48 @@ void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpDispatche
 }
 
 void OpReorderer::LayerReorderer::dump() const {
+    ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+            this, width, height, offscreenBuffer, beginLayerOp, renderNode);
     for (const BatchBase* batch : mBatches) {
         batch->dump();
     }
 }
 
-OpReorderer::OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
+OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+        uint32_t viewportWidth, uint32_t viewportHeight,
         const std::vector< sp<RenderNode> >& nodes)
         : mCanvasState(*this) {
     ATRACE_NAME("prepare drawing commands");
-
     mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
-    mLayerStack.push_back(0);
+        mLayerStack.push_back(0);
 
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
             clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
             Vector3());
+
+    // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
+    // updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
+    for (int i = layers.entries().size() - 1; i >= 0; i--) {
+        RenderNode* layerNode = layers.entries()[i].renderNode;
+        const Rect& layerDamage = layers.entries()[i].damage;
+
+        saveForLayer(layerNode->getWidth(), layerNode->getHeight(), nullptr, layerNode);
+        mCanvasState.writableSnapshot()->setClip(
+                layerDamage.left, layerDamage.top, layerDamage.right, layerDamage.bottom);
+
+        if (layerNode->getDisplayList()) {
+            deferImpl(*(layerNode->getDisplayList()));
+        }
+        restoreForLayer();
+    }
+
+    // Defer Fbo0
     for (const sp<RenderNode>& node : nodes) {
         if (node->nothingToDraw()) continue;
 
-        // TODO: dedupe this code with onRenderNode()
-        mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
-        if (node->applyViewProperties(mCanvasState)) {
-            // not rejected do ops...
-            const DisplayList& displayList = node->getDisplayList();
-            deferImpl(displayList);
-        }
-        mCanvasState.restore();
+        int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+        deferNodePropsAndOps(*node);
+        mCanvasState.restoreToCount(count);
     }
 }
 
@@ -334,6 +358,23 @@ void OpReorderer::onViewportInitialized() {}
 
 void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
 
+void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
+    if (node.applyViewProperties(mCanvasState)) {
+        // not rejected so render
+        if (node.getLayer()) {
+            // HW layer
+            LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
+            BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
+            if (bakedOpState) {
+                // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
+                currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
+            }
+        } else {
+            deferImpl(*(node.getDisplayList()));
+        }
+    }
+}
+
 /**
  * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
  *
@@ -365,11 +406,9 @@ void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
     mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top,
             op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op);
 
-    // apply RenderProperties state
-    if (op.renderNode->applyViewProperties(mCanvasState)) {
-        // if node not rejected based on properties, do ops...
-        deferImpl(op.renderNode->getDisplayList());
-    }
+    // then apply state from node properties, and defer ops
+    deferNodePropsAndOps(*op.renderNode);
+
     mCanvasState.restoreToCount(count);
 }
 
@@ -400,10 +439,8 @@ void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
     currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
 }
 
-// TODO: test rejection at defer time, where the bounds become empty
-void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
-    const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
-    const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+        const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
 
     mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
     mCanvasState.writableSnapshot()->transform->loadIdentity();
@@ -412,18 +449,27 @@ void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
 
     // create a new layer, and push its index on the stack
     mLayerStack.push_back(mLayerReorderers.size());
-    mLayerReorderers.emplace_back(layerWidth, layerHeight);
-    mLayerReorderers.back().beginLayerOp = &op;
+    mLayerReorderers.emplace_back(layerWidth, layerHeight, beginLayerOp, renderNode);
 }
 
-void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+void OpReorderer::restoreForLayer() {
+    // restore canvas, and pop finished layer off of the stack
     mCanvasState.restore();
+    mLayerStack.pop_back();
+}
 
-    const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
+// TODO: test rejection at defer time, where the bounds become empty
+void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+    const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
+    const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+    saveForLayer(layerWidth, layerHeight, &op, nullptr);
+}
 
-    // pop finished layer off of the stack
+void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+    const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
     int finishedLayerIndex = mLayerStack.back();
-    mLayerStack.pop_back();
+
+    restoreForLayer();
 
     // record the draw operation into the previous layer's list of draw commands
     // uses state from the associated beginLayerOp, since it has all the state needed for drawing
index 927ecfa..77be402 100644 (file)
@@ -32,6 +32,7 @@ namespace uirenderer {
 
 class BakedOpState;
 class BatchBase;
+class LayerUpdateQueue;
 class MergingOpBatch;
 class OffscreenBuffer;
 class OpBatch;
@@ -64,9 +65,14 @@ class OpReorderer : public CanvasStateClient {
      */
     class LayerReorderer {
     public:
+        // Create LayerReorderer for Fbo0
         LayerReorderer(uint32_t width, uint32_t height)
-                : width(width)
-                , height(height) {}
+                : LayerReorderer(width, height, nullptr, nullptr) {};
+
+        // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+        // saveLayer, renderNode is present for a HW layer.
+        LayerReorderer(uint32_t width, uint32_t height,
+                const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
 
         // iterate back toward target to see if anything drawn since should overlap the new op
         // if no target, merging ops still iterate to find similar batch to insert after
@@ -92,12 +98,12 @@ class OpReorderer : public CanvasStateClient {
 
         void dump() const;
 
-        OffscreenBuffer* offscreenBuffer = nullptr;
-        const BeginLayerOp* beginLayerOp = nullptr;
         const uint32_t width;
         const uint32_t height;
+        OffscreenBuffer* offscreenBuffer;
+        const BeginLayerOp* beginLayerOp;
+        const RenderNode* renderNode;
     private:
-
         std::vector<BatchBase*> mBatches;
 
         /**
@@ -112,8 +118,8 @@ class OpReorderer : public CanvasStateClient {
 
     };
 public:
-    // TODO: not final, just presented this way for simplicity. Layers too?
-    OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
+    OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+            uint32_t viewportWidth, uint32_t viewportHeight,
             const std::vector< sp<RenderNode> >& nodes);
 
     OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
@@ -144,8 +150,13 @@ public:
         // later in the list will be drawn by earlier ones
         for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
             LayerReorderer& layer = mLayerReorderers[i];
-            if (!layer.empty()) {
-                layer.offscreenBuffer = renderer.startLayer(layer.width, layer.height);
+            if (layer.renderNode) {
+                // cached HW layer - can't skip layer if empty
+                renderer.startLayer(layer.offscreenBuffer);
+                layer.replayBakedOpsImpl((void*)&renderer, receivers);
+                renderer.endLayer();
+            } else if (!layer.empty()) { // save layer - skip entire layer if empty
+                layer.offscreenBuffer = renderer.createLayer(layer.width, layer.height);
                 layer.replayBakedOpsImpl((void*)&renderer, receivers);
                 renderer.endLayer();
             }
@@ -171,12 +182,19 @@ public:
     virtual GLuint getTargetFbo() const override { return 0; }
 
 private:
+    void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+            const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+    void restoreForLayer();
+
     LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
 
     BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
         return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
     }
 
+    // should always be surrounded by a save/restore pair
+    void deferNodePropsAndOps(RenderNode& node);
+
     void deferImpl(const DisplayList& displayList);
 
     void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
index 7874d85..9ae868a 100644 (file)
@@ -20,6 +20,7 @@
 #include "utils/LinearAllocator.h"
 #include "Rect.h"
 #include "Matrix.h"
+#include "RenderNode.h"
 
 #include "SkXfermode.h"
 
@@ -136,13 +137,42 @@ struct EndLayerOp : RecordedOp {
             : RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
 };
 
+/**
+ * Draws an OffscreenBuffer.
+ *
+ * Alpha, mode, and colorfilter are embedded, since LayerOps are always dynamically generated,
+ * when creating/tracking a SkPaint* during defer isn't worth the bother.
+ */
 struct LayerOp : RecordedOp {
+    // Records a one-use (saveLayer) layer for drawing. Once drawn, the layer will be destroyed.
     LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
-            : SUPER(LayerOp)
-            , layerHandle(layerHandle) {}
+            : SUPER_PAINTLESS(LayerOp)
+            , layerHandle(layerHandle)
+            , alpha(paint->getAlpha() / 255.0f)
+            , mode(PaintUtils::getXfermodeDirect(paint))
+            , colorFilter(paint->getColorFilter())
+            , destroy(true) {}
+
+    LayerOp(RenderNode& node)
+        : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), Rect(node.getWidth(), node.getHeight()), nullptr)
+        , layerHandle(node.getLayerHandle())
+        , alpha(node.properties().layerProperties().alpha() / 255.0f)
+        , mode(node.properties().layerProperties().xferMode())
+        , colorFilter(node.properties().layerProperties().colorFilter())
+        , destroy(false) {}
+
     // Records a handle to the Layer object, since the Layer itself won't be
     // constructed until after this operation is constructed.
     OffscreenBuffer** layerHandle;
+    const float alpha;
+    const SkXfermode::Mode mode;
+
+    // pointer to object owned by either LayerProperties, or a recorded Paint object in a
+    // BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
+    SkColorFilter* colorFilter;
+
+    // whether to destroy the layer, once rendered
+    const bool destroy;
 };
 
 }; // namespace uirenderer
index 273af3a..7c460b1 100644 (file)
@@ -77,7 +77,6 @@ SkCanvas* RecordingCanvas::asSkCanvas() {
 // ----------------------------------------------------------------------------
 
 void RecordingCanvas::onViewportInitialized() {
-
 }
 
 void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
index 454ee24..8a56475 100644 (file)
 #include "SkiaCanvasProxy.h"
 #include "Snapshot.h"
 
-#include "SkDrawFilter.h"
-#include "SkPaint.h"
-#include "SkTLazy.h"
+#include <SkDrawFilter.h>
+#include <SkPaint.h>
+#include <SkTLazy.h>
+
 #include <vector>
 
 namespace android {
index 39cb8e9..de02bb8 100644 (file)
@@ -20,6 +20,7 @@
 #include "Debug.h"
 #if HWUI_NEW_OPS
 #include "RecordedOp.h"
+#include "BakedOpRenderer.h"
 #endif
 #include "DisplayListOp.h"
 #include "LayerRenderer.h"
@@ -42,11 +43,15 @@ namespace android {
 namespace uirenderer {
 
 void RenderNode::debugDumpLayers(const char* prefix) {
+#if HWUI_NEW_OPS
+    LOG_ALWAYS_FATAL("TODO: dump layer");
+#else
     if (mLayer) {
         ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
                 prefix, this, getName(), mLayer, mLayer->getFbo(),
                 mLayer->wasBuildLayered ? "true" : "false");
     }
+#endif
     if (mDisplayList) {
         for (auto&& child : mDisplayList->getChildren()) {
             child->renderNode->debugDumpLayers(prefix);
@@ -60,18 +65,21 @@ RenderNode::RenderNode()
         , mDisplayList(nullptr)
         , mStagingDisplayList(nullptr)
         , mAnimatorManager(*this)
-        , mLayer(nullptr)
         , mParentCount(0) {
 }
 
 RenderNode::~RenderNode() {
     deleteDisplayList();
     delete mStagingDisplayList;
+#if HWUI_NEW_OPS
+    LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
+#else
     if (mLayer) {
         ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
         mLayer->postDecStrong();
         mLayer = nullptr;
     }
+#endif
 }
 
 void RenderNode::setStagingDisplayList(DisplayList* displayList) {
@@ -240,13 +248,29 @@ void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
     }
 }
 
+layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
+#if HWUI_NEW_OPS
+    return BakedOpRenderer::createOffscreenBuffer(width, height);
+#else
+    return LayerRenderer::createRenderLayer(renderState, width, height);
+#endif
+}
+
+void destroyLayer(layer_t* layer) {
+#if HWUI_NEW_OPS
+    BakedOpRenderer::destroyOffscreenBuffer(layer);
+#else
+    LayerRenderer::destroyLayer(layer);
+#endif
+}
+
 void RenderNode::pushLayerUpdate(TreeInfo& info) {
     LayerType layerType = properties().effectiveLayerType();
     // If we are not a layer OR we cannot be rendered (eg, view was detached)
     // we need to destroy any Layers we may have had previously
     if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) {
         if (CC_UNLIKELY(mLayer)) {
-            LayerRenderer::destroyLayer(mLayer);
+            destroyLayer(mLayer);
             mLayer = nullptr;
         }
         return;
@@ -254,14 +278,18 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
 
     bool transformUpdateNeeded = false;
     if (!mLayer) {
-        mLayer = LayerRenderer::createRenderLayer(
-                info.canvasContext.getRenderState(), getWidth(), getHeight());
-        applyLayerPropertiesToLayer(info);
-        damageSelf(info);
-        transformUpdateNeeded = true;
+            mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+            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
     } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
         if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
-            LayerRenderer::destroyLayer(mLayer);
+#endif
+            destroyLayer(mLayer);
             mLayer = nullptr;
         }
         damageSelf(info);
@@ -276,7 +304,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
         if (info.errorHandler) {
             std::ostringstream err;
             err << "Unable to create layer for " << getName();
-            const int maxTextureSize = Caches::getInstance().maxTextureSize;
+            const uint32_t  maxTextureSize = Caches::getInstance().maxTextureSize;
             if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
                 err << ", size " << getWidth() << "x" << getHeight()
                         << " exceeds max size " << maxTextureSize;
@@ -292,9 +320,16 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
         // update the transform in window of the layer to reset its origin wrt light source position
         Matrix4 windowTransform;
         info.damageAccumulator->computeCurrentTransform(&windowTransform);
+#if HWUI_NEW_OPS
+        // TODO: update layer transform (perhaps as part of enqueueLayerWithDamage)
+#else
         mLayer->setWindowTransform(windowTransform);
+#endif
     }
 
+#if HWUI_NEW_OPS
+    info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
+#else
     if (dirty.intersect(0, 0, getWidth(), getHeight())) {
         dirty.roundOut(&dirty);
         mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
@@ -304,6 +339,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
     if (info.renderer && mLayer->deferredUpdateScheduled) {
         info.renderer->pushLayerUpdate(mLayer);
     }
+#endif
 
     // There might be prefetched layers that need to be accounted for.
     // That might be us, so tell CanvasContext that this layer is in the
@@ -365,7 +401,9 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
         damageSelf(info);
         info.damageAccumulator->popTransform();
         syncProperties();
+#if !HWUI_NEW_OPS
         applyLayerPropertiesToLayer(info);
+#endif
         // We could try to be clever and only re-damage if the matrix changed.
         // However, we don't need to worry about that. The cost of over-damaging
         // here is only going to be a single additional map rect of this node
@@ -376,6 +414,7 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
     }
 }
 
+#if !HWUI_NEW_OPS
 void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
     if (CC_LIKELY(!mLayer)) return;
 
@@ -384,6 +423,7 @@ void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
     mLayer->setColorFilter(props.colorFilter());
     mLayer->setBlend(props.needsBlending());
 }
+#endif
 
 void RenderNode::syncDisplayList() {
     // Make sure we inc first so that we don't fluctuate between 0 and 1,
@@ -451,7 +491,7 @@ void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayL
 
 void RenderNode::destroyHardwareResources() {
     if (mLayer) {
-        LayerRenderer::destroyLayer(mLayer);
+        destroyLayer(mLayer);
         mLayer = nullptr;
     }
     if (mDisplayList) {
@@ -978,7 +1018,11 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
         return;
     }
 
+#if HWUI_NEW_OPS
+    const bool drawLayer = false;
+#else
     const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
+#endif
     // If we are updating the contents of mLayer, we don't want to apply any of
     // the RenderNode's properties to this issueOperations pass. Those will all
     // be applied when the layer is drawn, aka when this is true.
index 57e41c6..3500cb2 100644 (file)
@@ -44,13 +44,22 @@ namespace android {
 namespace uirenderer {
 
 class CanvasState;
-class DisplayListOp;
 class DisplayListCanvas;
+class DisplayListOp;
 class OpenGLRenderer;
+class OpReorderer;
 class Rect;
-class Layer;
 class SkiaShader;
 
+
+#if HWUI_NEW_OPS
+class OffscreenBuffer;
+typedef OffscreenBuffer layer_t;
+#else
+class Layer;
+typedef Layer layer_t;
+#endif
+
 class ClipRectOp;
 class SaveLayerOp;
 class SaveOp;
@@ -162,11 +171,11 @@ public:
         return mStagingProperties;
     }
 
-    int getWidth() {
+    uint32_t getWidth() {
         return properties().getWidth();
     }
 
-    int getHeight() {
+    uint32_t getHeight() {
         return properties().getHeight();
     }
 
@@ -193,9 +202,13 @@ public:
     }
 
     // Only call if RenderNode has DisplayList...
-    const DisplayList& getDisplayList() const {
-        return *mDisplayList;
+    const DisplayList* getDisplayList() const {
+        return mDisplayList;
     }
+#if HWUI_NEW_OPS
+    OffscreenBuffer* getLayer() const { return mLayer; }
+    OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
+#endif
 
 private:
     typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
@@ -262,7 +275,9 @@ private:
     void pushStagingPropertiesChanges(TreeInfo& info);
     void pushStagingDisplayListChanges(TreeInfo& info);
     void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree);
+#if !HWUI_NEW_OPS
     void applyLayerPropertiesToLayer(TreeInfo& info);
+#endif
     void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
     void pushLayerUpdate(TreeInfo& info);
     void deleteDisplayList();
@@ -287,7 +302,7 @@ private:
 
     // Owned by RT. Lifecycle is managed by prepareTree(), with the exception
     // being in ~RenderNode() which may happen on any thread.
-    Layer* mLayer;
+    layer_t* mLayer = nullptr;
 
     /**
      * Draw time state - these properties are only set and used during rendering
index 1c31487..be25516 100644 (file)
 #ifndef TREEINFO_H
 #define TREEINFO_H
 
-#include <string>
+#include "utils/Macros.h"
 
 #include <utils/Timers.h>
 
-#include "utils/Macros.h"
+#include <string>
 
 namespace android {
 namespace uirenderer {
@@ -30,6 +30,7 @@ class CanvasContext;
 }
 
 class DamageAccumulator;
+class LayerUpdateQueue;
 class OpenGLRenderer;
 class RenderState;
 
@@ -75,9 +76,14 @@ public:
 
     // Must not be null during actual usage
     DamageAccumulator* damageAccumulator = nullptr;
+
+#if HWUI_NEW_OPS
+    LayerUpdateQueue* layerUpdateQueue = nullptr;
+#else
     // The renderer that will be drawing the next frame. Use this to push any
     // layer updates or similar. May be NULL.
     OpenGLRenderer* renderer = nullptr;
+#endif
     ErrorHandler* errorHandler = nullptr;
 
     struct Out {
index 43f170f..7b8d0e5 100644 (file)
@@ -56,7 +56,9 @@ void BM_OpReorderer_defer::Run(int iters) {
 
 BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
 void BM_OpReorderer_deferAndRender::Run(int iters) {
-    TestUtils::runOnRenderThread([this, iters](RenderState& renderState, Caches& caches) {
+    TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) {
+        RenderState& renderState = thread.renderState();
+        Caches& caches = Caches::getInstance();
         StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
             OpReorderer reorderer(200, 200, *sReorderingDisplayList);
index fac26dc..e9219a6 100644 (file)
@@ -20,6 +20,7 @@
 #include "Caches.h"
 #include "DeferredLayerUpdater.h"
 #include "EglManager.h"
+#include "LayerUpdateQueue.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "Properties.h"
@@ -198,7 +199,11 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
     mCurrentFrameInfo->markSyncStart();
 
     info.damageAccumulator = &mDamageAccumulator;
+#if HWUI_NEW_OPS
+    info.layerUpdateQueue = &mLayerUpdateQueue;
+#else
     info.renderer = mCanvas;
+#endif
 
     mAnimationContext->startFrame(info.mode);
     for (const sp<RenderNode>& node : mRenderNodes) {
@@ -333,7 +338,8 @@ void CanvasContext::draw() {
     mEglManager.damageFrame(frame, dirty);
 
 #if HWUI_NEW_OPS
-    OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
+    OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes);
+    mLayerUpdateQueue.clear();
     BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
     // TODO: profiler().draw(mCanvas);
     reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -552,7 +558,11 @@ void CanvasContext::buildLayer(RenderNode* node) {
 
     TreeInfo info(TreeInfo::MODE_FULL, *this);
     info.damageAccumulator = &mDamageAccumulator;
+#if HWUI_NEW_OPS
+    info.layerUpdateQueue = &mLayerUpdateQueue;
+#else
     info.renderer = mCanvas;
+#endif
     info.runAnimations = false;
     node->prepareTree(info);
     SkRect ignore;
index 30e6562..d656014 100644 (file)
 #define CANVASCONTEXT_H_
 
 #include "DamageAccumulator.h"
-#include "IContextFactory.h"
 #include "FrameInfo.h"
 #include "FrameInfoVisualizer.h"
+#include "IContextFactory.h"
+#include "LayerUpdateQueue.h"
 #include "RenderNode.h"
 #include "utils/RingBuffer.h"
 #include "renderthread/RenderTask.h"
@@ -83,7 +84,7 @@ public:
     void draw();
     void destroy();
 
-    // IFrameCallback, Chroreographer-driven frame callback entry point
+    // IFrameCallback, Choreographer-driven frame callback entry point
     virtual void doFrame() override;
     void prepareAndDraw(RenderNode* node);
 
@@ -118,7 +119,7 @@ public:
 
     void addRenderNode(RenderNode* node, bool placeFront) {
         int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
-        mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
+        mRenderNodes.emplace(mRenderNodes.begin() + pos, node);
     }
 
     void removeRenderNode(RenderNode* node) {
@@ -166,6 +167,7 @@ private:
     OpenGLRenderer* mCanvas = nullptr;
     bool mHaveNewSurface = false;
     DamageAccumulator mDamageAccumulator;
+    LayerUpdateQueue mLayerUpdateQueue;
     std::unique_ptr<AnimationContext> mAnimationContext;
 
     std::vector< sp<RenderNode> > mRenderNodes;
index 2eefd37..29d9803 100644 (file)
@@ -24,6 +24,7 @@
 #include <RenderNode.h>
 #include <renderthread/RenderProxy.h>
 #include <renderthread/RenderTask.h>
+#include <unit_tests/TestUtils.h>
 
 #include "Benchmark.h"
 #include "TestContext.h"
@@ -401,3 +402,27 @@ static Benchmark _SaveLayer(BenchmarkInfo{
     "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
     TreeContentAnimation::run<SaveLayerAnimation>
 });
+
+
+class HwLayerAnimation : public TreeContentAnimation {
+public:
+    sp<RenderNode> card = TestUtils::createNode<TestCanvas>(0, 0, 200, 200, [] (TestCanvas& canvas) {
+        canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
+    }, true);
+    void createContent(int width, int height, TestCanvas* canvas) override {
+        canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+        canvas->drawRenderNode(card.get());
+    }
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        card->mutateStagingProperties().setTranslationX(curFrame);
+        card->mutateStagingProperties().setTranslationY(curFrame);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    }
+};
+static Benchmark _HwLayer(BenchmarkInfo{
+    "hwlayer",
+    "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. "
+    "Tests the hardware layer codepath.",
+    TreeContentAnimation::run<HwLayerAnimation>
+});
diff --git a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
new file mode 100644 (file)
index 0000000..9d625bc
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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 <LayerUpdateQueue.h>
+#include <RenderNode.h>
+
+#include <unit_tests/TestUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+TEST(LayerUpdateQueue, construct) {
+    LayerUpdateQueue queue;
+    EXPECT_TRUE(queue.entries().empty());
+}
+
+// sync node properties, so properties() reflects correct width and height
+static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) {
+    sp<RenderNode> node = TestUtils::createNode(0, 0, width, height);
+    TestUtils::syncNodePropertiesAndDisplayList(node);
+    return node;
+}
+
+TEST(LayerUpdateQueue, enqueueSimple) {
+    sp<RenderNode> a = createSyncedNode(100, 100);
+    sp<RenderNode> b = createSyncedNode(200, 200);
+
+    LayerUpdateQueue queue;
+    queue.enqueueLayerWithDamage(a.get(), Rect(25, 25, 75, 75));
+    queue.enqueueLayerWithDamage(b.get(), Rect(100, 100, 300, 300));
+
+    EXPECT_EQ(2u, queue.entries().size());
+
+    EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+    EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
+    EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
+    EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
+}
+
+TEST(LayerUpdateQueue, enqueueUnion) {
+    sp<RenderNode> a = createSyncedNode(100, 100);
+
+    LayerUpdateQueue queue;
+    queue.enqueueLayerWithDamage(a.get(), Rect(10, 10, 20, 20));
+    queue.enqueueLayerWithDamage(a.get(), Rect(30, 30, 40, 40));
+
+    EXPECT_EQ(1u, queue.entries().size());
+
+    EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+    EXPECT_EQ(Rect(10, 10, 40, 40), queue.entries()[0].damage);
+}
+
+TEST(LayerUpdateQueue, clear) {
+    sp<RenderNode> a = createSyncedNode(100, 100);
+
+    LayerUpdateQueue queue;
+    queue.enqueueLayerWithDamage(a.get(), Rect(100, 100));
+
+    EXPECT_FALSE(queue.entries().empty());
+
+    queue.clear();
+
+    EXPECT_TRUE(queue.entries().empty());
+}
+
+};
+};
index ffb575f..cef46f7 100644 (file)
@@ -20,6 +20,7 @@
 #include <OpReorderer.h>
 #include <RecordedOp.h>
 #include <RecordingCanvas.h>
+#include <renderthread/CanvasContext.h> // todo: remove
 #include <unit_tests/TestUtils.h>
 
 #include <unordered_map>
@@ -27,6 +28,7 @@
 namespace android {
 namespace uirenderer {
 
+LayerUpdateQueue sEmptyLayerUpdateQueue;
 
 /**
  * Virtual class implemented by each test to redirect static operation / state transitions to
@@ -42,7 +44,8 @@ namespace uirenderer {
 class TestRendererBase {
 public:
     virtual ~TestRendererBase() {}
-    virtual OffscreenBuffer* startLayer(uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
+    virtual OffscreenBuffer* createLayer(uint32_t, uint32_t) { ADD_FAILURE(); return nullptr; }
+    virtual void startLayer(OffscreenBuffer*) { ADD_FAILURE(); }
     virtual void endLayer() { ADD_FAILURE(); }
     virtual void startFrame(uint32_t width, uint32_t height) {}
     virtual void endFrame() {}
@@ -192,7 +195,8 @@ TEST(OpReorderer, renderNode) {
     std::vector< sp<RenderNode> > nodes;
     nodes.push_back(parent.get());
 
-    OpReorderer reorderer(SkRect::MakeWH(200, 200), 200, 200, nodes);
+    OpReorderer reorderer(sEmptyLayerUpdateQueue,
+            SkRect::MakeWH(200, 200), 200, 200, nodes);
 
     RenderNodeTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -216,7 +220,8 @@ TEST(OpReorderer, clipped) {
     std::vector< sp<RenderNode> > nodes;
     nodes.push_back(node.get());
 
-    OpReorderer reorderer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
+    OpReorderer reorderer(sEmptyLayerUpdateQueue,
+            SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
             200, 200, nodes);
 
     ClippedTestRenderer renderer;
@@ -226,7 +231,7 @@ TEST(OpReorderer, clipped) {
 
 class SaveLayerSimpleTestRenderer : public TestRendererBase {
 public:
-    OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+    OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
         EXPECT_EQ(0, mIndex++);
         EXPECT_EQ(180u, width);
         EXPECT_EQ(180u, height);
@@ -268,13 +273,13 @@ TEST(OpReorderer, saveLayerSimple) {
 
 
 /* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
- * - startLayer2, rect2 endLayer2
- * - startLayer1, rect1, drawLayer2, endLayer1
+ * - createLayer2, rect2 endLayer2
+ * - createLayer1, rect1, drawLayer2, endLayer1
  * - startFrame, layerOp1, endFrame
  */
 class SaveLayerNestedTestRenderer : public TestRendererBase {
 public:
-    OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+    OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
         const int index = mIndex++;
         if (index == 0) {
             EXPECT_EQ(400u, width);
@@ -356,5 +361,162 @@ TEST(OpReorderer, saveLayerContentRejection) {
     reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
+class HwLayerSimpleTestRenderer : public TestRendererBase {
+public:
+    void startLayer(OffscreenBuffer* offscreenBuffer) override {
+        EXPECT_EQ(0, mIndex++);
+        EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
+    }
+    void onRectOp(const RectOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(1, mIndex++);
+
+        // verify transform is reset
+        EXPECT_TRUE(state.computedState.transform.isIdentity());
+
+        // verify damage rect is used as clip
+        EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75));
+    }
+    void endLayer() override {
+        EXPECT_EQ(2, mIndex++);
+    }
+    void startFrame(uint32_t width, uint32_t height) override {
+        EXPECT_EQ(3, mIndex++);
+    }
+    void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(4, mIndex++);
+    }
+    void endFrame() override {
+        EXPECT_EQ(5, mIndex++);
+    }
+};
+TEST(OpReorderer, hwLayerSimple) {
+    sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
+        SkPaint paint;
+        paint.setColor(SK_ColorWHITE);
+        canvas.drawRect(0, 0, 100, 100, paint);
+    });
+    node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+    node->setPropertyFieldsDirty(RenderNode::GENERIC);
+    OffscreenBuffer** bufferHandle = node->getLayerHandle();
+    *bufferHandle = (OffscreenBuffer*) 0x0124;
+
+    TestUtils::syncNodePropertiesAndDisplayList(node);
+
+    std::vector< sp<RenderNode> > nodes;
+    nodes.push_back(node.get());
+
+    // only enqueue partial damage
+    LayerUpdateQueue layerUpdateQueue;
+    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
+
+    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+    HwLayerSimpleTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(6, renderer.getIndex());
+
+    // clean up layer pointer, so we can safely destruct RenderNode
+    *bufferHandle = nullptr;
+}
+
+
+/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
+ * - startLayer(child), rect(grey), endLayer
+ * - createLayer, drawLayer(child), endLayer
+ * - startLayer(parent), rect(white), drawLayer(saveLayer), endLayer
+ * - startFrame, drawLayer(parent), endLayerb
+ */
+class HwLayerComplexTestRenderer : public TestRendererBase {
+public:
+    OffscreenBuffer* createLayer(uint32_t width, uint32_t height) {
+        EXPECT_EQ(3, mIndex++); // savelayer first
+        return (OffscreenBuffer*)0xabcd;
+    }
+    void startLayer(OffscreenBuffer* offscreenBuffer) override {
+        int index = mIndex++;
+        if (index == 0) {
+            // starting inner layer
+            EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
+        } else if (index == 6) {
+            // starting outer layer
+            EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
+        } else { ADD_FAILURE(); }
+    }
+    void onRectOp(const RectOp& op, const BakedOpState& state) override {
+        int index = mIndex++;
+        if (index == 1) {
+            // inner layer's rect (white)
+            EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+        } else if (index == 7) {
+            // outer layer's rect (grey)
+            EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+        } else { ADD_FAILURE(); }
+    }
+    void endLayer() override {
+        int index = mIndex++;
+        EXPECT_TRUE(index == 2 || index == 5 || index == 9);
+    }
+    void startFrame(uint32_t width, uint32_t height) override {
+        EXPECT_EQ(10, mIndex++);
+    }
+    void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+        int index = mIndex++;
+        if (index == 4) {
+            EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
+        } else if (index == 8) {
+            EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
+        } else if (index == 11) {
+            EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
+        } else { ADD_FAILURE(); }
+    }
+    void endFrame() override {
+        EXPECT_EQ(12, mIndex++);
+    }
+};
+TEST(OpReorderer, hwLayerComplex) {
+    sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, [](RecordingCanvas& canvas) {
+        SkPaint paint;
+        paint.setColor(SK_ColorWHITE);
+        canvas.drawRect(0, 0, 100, 100, paint);
+    });
+    child->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+    child->setPropertyFieldsDirty(RenderNode::GENERIC);
+    *(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
+
+    RenderNode* childPtr = child.get();
+    sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
+        SkPaint paint;
+        paint.setColor(SK_ColorDKGRAY);
+        canvas.drawRect(0, 0, 200, 200, paint);
+
+        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.drawRenderNode(childPtr);
+        canvas.restore();
+    });
+    parent->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+    parent->setPropertyFieldsDirty(RenderNode::GENERIC);
+    *(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
+
+    TestUtils::syncNodePropertiesAndDisplayList(child);
+    TestUtils::syncNodePropertiesAndDisplayList(parent);
+
+    std::vector< sp<RenderNode> > nodes;
+    nodes.push_back(parent.get());
+
+    LayerUpdateQueue layerUpdateQueue;
+    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
+    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
+
+    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+    HwLayerComplexTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(13, renderer.getIndex());
+
+    // clean up layer pointers, so we can safely destruct RenderNodes
+    *(child->getLayerHandle()) = nullptr;
+    *(parent->getLayerHandle()) = nullptr;
+}
+
 } // namespace uirenderer
 } // namespace android
index 5b09fda..770f413 100644 (file)
@@ -89,15 +89,24 @@ public:
         return std::unique_ptr<DisplayList>(canvas.finishRecording());
     }
 
-    template<class CanvasType>
-    static sp<RenderNode> createNode(int left, int top, int right, int bottom,
-            std::function<void(CanvasType& canvas)> canvasCallback) {
+    static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
         sp<RenderNode> node = new RenderNode();
         node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        if (onLayer) {
+            node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+            node->setPropertyFieldsDirty(RenderNode::GENERIC);
+        }
+        return node;
+    }
+
+    template<class CanvasType>
+    static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+            std::function<void(CanvasType& canvas)> canvasCallback, bool onLayer = false) {
+        sp<RenderNode> node = createNode(left, top, right, bottom, onLayer);
 
-        CanvasType canvas(
-                node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+        auto&& props = node->stagingProperties(); // staging, since not sync'd yet
+        CanvasType canvas(props.getWidth(), props.getHeight());
         canvasCallback(canvas);
         node->setStagingDisplayList(canvas.finishRecording());
         return node;
@@ -108,7 +117,7 @@ public:
         node->syncDisplayList();
     }
 
-    typedef std::function<void(RenderState& state, Caches& caches)> RtCallback;
+    typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
 
     class TestTask : public renderthread::RenderTask {
     public:
@@ -120,7 +129,7 @@ public:
             RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
 
             renderState.onGLContextCreated();
-            rtCallback(renderState, Caches::getInstance());
+            rtCallback(renderthread::RenderThread::getInstance());
             renderState.onGLContextDestroyed();
         };
         RtCallback rtCallback;