diff options
| -rw-r--r-- | libs/hwui/Android.mk | 9 | ||||
| -rw-r--r-- | libs/hwui/FrameReorderer.cpp (renamed from libs/hwui/OpReorderer.cpp) | 418 | ||||
| -rw-r--r-- | libs/hwui/FrameReorderer.h (renamed from libs/hwui/OpReorderer.h) | 130 | ||||
| -rw-r--r-- | libs/hwui/LayerReorderer.cpp | 367 | ||||
| -rw-r--r-- | libs/hwui/LayerReorderer.h | 135 | ||||
| -rw-r--r-- | libs/hwui/RenderNode.h | 2 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 4 | ||||
| -rw-r--r-- | libs/hwui/tests/microbench/FrameReordererBench.cpp (renamed from libs/hwui/tests/microbench/OpReordererBench.cpp) | 28 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/FrameReordererTests.cpp (renamed from libs/hwui/tests/unit/OpReordererTests.cpp) | 120 | 
9 files changed, 649 insertions, 564 deletions
| diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 11056d4fac97..0932e8998ce2 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -109,7 +109,8 @@ ifeq (true, $(HWUI_NEW_OPS))          BakedOpDispatcher.cpp \          BakedOpRenderer.cpp \          BakedOpState.cpp \ -        OpReorderer.cpp \ +        FrameReorderer.cpp \ +        LayerReorderer.cpp \          RecordingCanvas.cpp      hwui_cflags += -DHWUI_NEW_OPS @@ -236,8 +237,8 @@ LOCAL_SRC_FILES += \  ifeq (true, $(HWUI_NEW_OPS))      LOCAL_SRC_FILES += \          tests/unit/BakedOpStateTests.cpp \ -        tests/unit/RecordingCanvasTests.cpp \ -        tests/unit/OpReordererTests.cpp +        tests/unit/FrameReordererTests.cpp \ +        tests/unit/RecordingCanvasTests.cpp  endif  include $(BUILD_NATIVE_TEST) @@ -298,7 +299,7 @@ LOCAL_SRC_FILES += \  ifeq (true, $(HWUI_NEW_OPS))      LOCAL_SRC_FILES += \ -        tests/microbench/OpReordererBench.cpp +        tests/microbench/FrameReordererBench.cpp  endif  include $(BUILD_EXECUTABLE) diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/FrameReorderer.cpp index cff4f3c47d60..4bfc0b413c56 100644 --- a/libs/hwui/OpReorderer.cpp +++ b/libs/hwui/FrameReorderer.cpp @@ -1,5 +1,5 @@  /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@   * limitations under the License.   */ -#include "OpReorderer.h" +#include "FrameReorderer.h"  #include "LayerUpdateQueue.h"  #include "RenderNode.h" @@ -30,343 +30,7 @@  namespace android {  namespace uirenderer { -class BatchBase { -public: -    BatchBase(batchid_t batchId, BakedOpState* op, bool merging) -            : mBatchId(batchId) -            , mMerging(merging) { -        mBounds = op->computedState.clippedBounds; -        mOps.push_back(op); -    } - -    bool intersects(const Rect& rect) const { -        if (!rect.intersects(mBounds)) return false; - -        for (const BakedOpState* op : mOps) { -            if (rect.intersects(op->computedState.clippedBounds)) { -                return true; -            } -        } -        return false; -    } - -    batchid_t getBatchId() const { return mBatchId; } -    bool isMerging() const { return mMerging; } - -    const std::vector<BakedOpState*>& getOps() const { return mOps; } - -    void dump() const { -        ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING, -                this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds)); -    } -protected: -    batchid_t mBatchId; -    Rect mBounds; -    std::vector<BakedOpState*> mOps; -    bool mMerging; -}; - -class OpBatch : public BatchBase { -public: -    static void* operator new(size_t size, LinearAllocator& allocator) { -        return allocator.alloc(size); -    } - -    OpBatch(batchid_t batchId, BakedOpState* op) -            : BatchBase(batchId, op, false) { -    } - -    void batchOp(BakedOpState* op) { -        mBounds.unionWith(op->computedState.clippedBounds); -        mOps.push_back(op); -    } -}; - -class MergingOpBatch : public BatchBase { -public: -    static void* operator new(size_t size, LinearAllocator& allocator) { -        return allocator.alloc(size); -    } - -    MergingOpBatch(batchid_t batchId, BakedOpState* op) -            : BatchBase(batchId, op, true) -            , mClipSideFlags(op->computedState.clipSideFlags) { -    } - -    /* -     * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds -     * and clip side flags. Positive bounds delta means new bounds fit in old. -     */ -    static inline bool checkSide(const int currentFlags, const int newFlags, const int side, -            float boundsDelta) { -        bool currentClipExists = currentFlags & side; -        bool newClipExists = newFlags & side; - -        // if current is clipped, we must be able to fit new bounds in current -        if (boundsDelta > 0 && currentClipExists) return false; - -        // if new is clipped, we must be able to fit current bounds in new -        if (boundsDelta < 0 && newClipExists) return false; - -        return true; -    } - -    static bool paintIsDefault(const SkPaint& paint) { -        return paint.getAlpha() == 255 -                && paint.getColorFilter() == nullptr -                && paint.getShader() == nullptr; -    } - -    static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) { -        return a.getAlpha() == b.getAlpha() -                && a.getColorFilter() == b.getColorFilter() -                && a.getShader() == b.getShader(); -    } - -    /* -     * Checks if a (mergeable) op can be merged into this batch -     * -     * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is -     * important to consider all paint attributes used in the draw calls in deciding both a) if an -     * op tries to merge at all, and b) if the op can merge with another set of ops -     * -     * False positives can lead to information from the paints of subsequent merged operations being -     * dropped, so we make simplifying qualifications on the ops that can merge, per op type. -     */ -    bool canMergeWith(BakedOpState* op) const { -        bool isTextBatch = getBatchId() == OpBatchType::Text -                || getBatchId() == OpBatchType::ColorText; - -        // Overlapping other operations is only allowed for text without shadow. For other ops, -        // multiDraw isn't guaranteed to overdraw correctly -        if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) { -            if (intersects(op->computedState.clippedBounds)) return false; -        } - -        const BakedOpState* lhs = op; -        const BakedOpState* rhs = mOps[0]; - -        if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false; - -        // Identical round rect clip state means both ops will clip in the same way, or not at all. -        // As the state objects are const, we can compare their pointers to determine mergeability -        if (lhs->roundRectClipState != rhs->roundRectClipState) return false; -        if (lhs->projectionPathMask != rhs->projectionPathMask) return false; - -        /* Clipping compatibility check -         * -         * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its -         * clip for that side. -         */ -        const int currentFlags = mClipSideFlags; -        const int newFlags = op->computedState.clipSideFlags; -        if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) { -            const Rect& opBounds = op->computedState.clippedBounds; -            float boundsDelta = mBounds.left - opBounds.left; -            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false; -            boundsDelta = mBounds.top - opBounds.top; -            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false; - -            // right and bottom delta calculation reversed to account for direction -            boundsDelta = opBounds.right - mBounds.right; -            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false; -            boundsDelta = opBounds.bottom - mBounds.bottom; -            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false; -        } - -        const SkPaint* newPaint = op->op->paint; -        const SkPaint* oldPaint = mOps[0]->op->paint; - -        if (newPaint == oldPaint) { -            // if paints are equal, then modifiers + paint attribs don't need to be compared -            return true; -        } else if (newPaint && !oldPaint) { -            return paintIsDefault(*newPaint); -        } else if (!newPaint && oldPaint) { -            return paintIsDefault(*oldPaint); -        } -        return paintsAreEquivalent(*newPaint, *oldPaint); -    } - -    void mergeOp(BakedOpState* op) { -        mBounds.unionWith(op->computedState.clippedBounds); -        mOps.push_back(op); - -        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat -        // check, and doesn't extend past a side of the clip that's in use by the merged batch. -        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect. -        mClipSideFlags |= op->computedState.clipSideFlags; -    } - -    int getClipSideFlags() const { return mClipSideFlags; } -    const Rect& getClipRect() const { return mBounds; } - -private: -    int mClipSideFlags; -}; - -OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height, -        const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode) -        : width(width) -        , height(height) -        , repaintRect(repaintRect) -        , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr) -        , beginLayerOp(beginLayerOp) -        , renderNode(renderNode) -        , viewportClip(Rect(width, height)) {} - -// 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, -        BatchBase** targetBatch, size_t* insertBatchIndex) const { -    for (int i = mBatches.size() - 1; i >= 0; i--) { -        BatchBase* overBatch = mBatches[i]; - -        if (overBatch == *targetBatch) break; - -        // TODO: also consider shader shared between batch types -        if (batchId == overBatch->getBatchId()) { -            *insertBatchIndex = i + 1; -            if (!*targetBatch) break; // found insert position, quit -        } - -        if (overBatch->intersects(clippedBounds)) { -            // NOTE: it may be possible to optimize for special cases where two operations -            // of the same batch/paint could swap order, such as with a non-mergeable -            // (clipped) and a mergeable text operation -            *targetBatch = nullptr; -            break; -        } -    } -} - -void OpReorderer::LayerReorderer::deferLayerClear(const Rect& rect) { -    mClearRects.push_back(rect); -} - -void OpReorderer::LayerReorderer::flushLayerClears(LinearAllocator& allocator) { -    if (CC_UNLIKELY(!mClearRects.empty())) { -        const int vertCount = mClearRects.size() * 4; -        // put the verts in the frame allocator, since -        //     1) SimpleRectsOps needs verts, not rects -        //     2) even if mClearRects stored verts, std::vectors will move their contents -        Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex)); - -        Vertex* currentVert = verts; -        Rect bounds = mClearRects[0]; -        for (auto&& rect : mClearRects) { -            bounds.unionWith(rect); -            Vertex::set(currentVert++, rect.left, rect.top); -            Vertex::set(currentVert++, rect.right, rect.top); -            Vertex::set(currentVert++, rect.left, rect.bottom); -            Vertex::set(currentVert++, rect.right, rect.bottom); -        } -        mClearRects.clear(); // discard rects before drawing so this method isn't reentrant - -        // One or more unclipped saveLayers have been enqueued, with deferred clears. -        // Flush all of these clears with a single draw -        SkPaint* paint = allocator.create<SkPaint>(); -        paint->setXfermodeMode(SkXfermode::kClear_Mode); -        SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds, -                Matrix4::identity(), nullptr, paint, -                verts, vertCount); -        BakedOpState* bakedState = BakedOpState::directConstruct(allocator, -                &viewportClip, bounds, *op); - - -        deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices); -    } -} - -void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator, -        BakedOpState* op, batchid_t batchId) { -    if (batchId != OpBatchType::CopyToLayer) { -        // if first op after one or more unclipped saveLayers, flush the layer clears -        flushLayerClears(allocator); -    } - -    OpBatch* targetBatch = mBatchLookup[batchId]; - -    size_t insertBatchIndex = mBatches.size(); -    if (targetBatch) { -        locateInsertIndex(batchId, op->computedState.clippedBounds, -                (BatchBase**)(&targetBatch), &insertBatchIndex); -    } - -    if (targetBatch) { -        targetBatch->batchOp(op); -    } else  { -        // new non-merging batch -        targetBatch = new (allocator) OpBatch(batchId, op); -        mBatchLookup[batchId] = targetBatch; -        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); -    } -} - -void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator, -        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) { -    if (batchId != OpBatchType::CopyToLayer) { -        // if first op after one or more unclipped saveLayers, flush the layer clears -        flushLayerClears(allocator); -    } -    MergingOpBatch* targetBatch = nullptr; - -    // Try to merge with any existing batch with same mergeId -    auto getResult = mMergingBatchLookup[batchId].find(mergeId); -    if (getResult != mMergingBatchLookup[batchId].end()) { -        targetBatch = getResult->second; -        if (!targetBatch->canMergeWith(op)) { -            targetBatch = nullptr; -        } -    } - -    size_t insertBatchIndex = mBatches.size(); -    locateInsertIndex(batchId, op->computedState.clippedBounds, -            (BatchBase**)(&targetBatch), &insertBatchIndex); - -    if (targetBatch) { -        targetBatch->mergeOp(op); -    } else  { -        // new merging batch -        targetBatch = new (allocator) MergingOpBatch(batchId, op); -        mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch)); - -        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); -    } -} - -void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, -        BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const { -    ATRACE_NAME("flush drawing commands"); -    for (const BatchBase* batch : mBatches) { -        size_t size = batch->getOps().size(); -        if (size > 1 && batch->isMerging()) { -            int opId = batch->getOps()[0]->op->opId; -            const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch); -            MergedBakedOpList data = { -                    batch->getOps().data(), -                    size, -                    mergingBatch->getClipSideFlags(), -                    mergingBatch->getClipRect() -            }; -            mergedReceivers[opId](arg, data); -        } else { -            for (const BakedOpState* op : batch->getOps()) { -                unmergedReceivers[op->op->opId](arg, *op); -            } -        } -    } -} - -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 LayerUpdateQueue& layers, const SkRect& clip, +FrameReorderer::FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,          uint32_t viewportWidth, uint32_t viewportHeight,          const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)          : mCanvasState(*this) { @@ -413,11 +77,11 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,      }  } -void OpReorderer::onViewportInitialized() {} +void FrameReorderer::onViewportInitialized() {} -void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} +void FrameReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} -void OpReorderer::deferNodePropsAndOps(RenderNode& node) { +void FrameReorderer::deferNodePropsAndOps(RenderNode& node) {      const RenderProperties& properties = node.properties();      const Outline& outline = properties.getOutline();      if (properties.getAlpha() <= 0 @@ -549,7 +213,7 @@ static size_t findNonNegativeIndex(const V& zTranslatedNodes) {  }  template <typename V> -void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) { +void FrameReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {      const int size = zTranslatedNodes.size();      if (size == 0              || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f) @@ -599,7 +263,7 @@ void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedN      }  } -void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) { +void FrameReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {      auto& node = *casterNodeOp.renderNode;      auto& properties = node.properties(); @@ -655,7 +319,7 @@ void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {      }  } -void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) { +void FrameReorderer::deferProjectedChildren(const RenderNode& renderNode) {      const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();      int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); @@ -688,15 +352,15 @@ void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) {  }  /** - * Used to define a list of lambdas referencing private OpReorderer::onXX::defer() methods. + * Used to define a list of lambdas referencing private FrameReorderer::onXX::defer() methods.   *   * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. - * E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&) + * E.g. a BitmapOp op then would be dispatched to FrameReorderer::onBitmapOp(const BitmapOp&)   */  #define OP_RECEIVER(Type) \ -        [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); }, -void OpReorderer::deferNodeOps(const RenderNode& renderNode) { -    typedef void (*OpDispatcher) (OpReorderer& reorderer, const RecordedOp& op); +        [](FrameReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); }, +void FrameReorderer::deferNodeOps(const RenderNode& renderNode) { +    typedef void (*OpDispatcher) (FrameReorderer& reorderer, const RecordedOp& op);      static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);      // can't be null, since DL=null node rejection happens before deferNodePropsAndOps @@ -720,7 +384,7 @@ void OpReorderer::deferNodeOps(const RenderNode& renderNode) {      }  } -void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) { +void FrameReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {      if (op.renderNode->nothingToDraw()) return;      int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); @@ -735,7 +399,7 @@ void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {      mCanvasState.restoreToCount(count);  } -void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) { +void FrameReorderer::deferRenderNodeOp(const RenderNodeOp& op) {      if (!op.skipInOrderDraw) {          deferRenderNodeOpImpl(op);      } @@ -745,7 +409,7 @@ void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {   * Defers an unmergeable, strokeable op, accounting correctly   * for paint's style on the bounds being computed.   */ -void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, +void FrameReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,          BakedOpState::StrokeBehavior strokeBehavior) {      // Note: here we account for stroke when baking the op      BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( @@ -767,7 +431,7 @@ static batchid_t tessBatchId(const RecordedOp& op) {              : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);  } -void OpReorderer::deferArcOp(const ArcOp& op) { +void FrameReorderer::deferArcOp(const ArcOp& op) {      deferStrokeableOp(op, tessBatchId(op));  } @@ -776,7 +440,7 @@ static bool hasMergeableClip(const BakedOpState& state) {              || state.computedState.clipState->mode == ClipMode::Rectangle;  } -void OpReorderer::deferBitmapOp(const BitmapOp& op) { +void FrameReorderer::deferBitmapOp(const BitmapOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected @@ -796,19 +460,19 @@ void OpReorderer::deferBitmapOp(const BitmapOp& op) {      }  } -void OpReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) { +void FrameReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected      currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);  } -void OpReorderer::deferBitmapRectOp(const BitmapRectOp& op) { +void FrameReorderer::deferBitmapRectOp(const BitmapRectOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected      currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);  } -void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) { +void FrameReorderer::deferCirclePropsOp(const CirclePropsOp& op) {      // allocate a temporary oval op (with mAllocator, so it persists until render), so the      // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.      float x = *(op.x); @@ -823,22 +487,22 @@ void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {      deferOvalOp(*resolvedOp);  } -void OpReorderer::deferFunctorOp(const FunctorOp& op) { +void FrameReorderer::deferFunctorOp(const FunctorOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected      currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);  } -void OpReorderer::deferLinesOp(const LinesOp& op) { +void FrameReorderer::deferLinesOp(const LinesOp& op) {      batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;      deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);  } -void OpReorderer::deferOvalOp(const OvalOp& op) { +void FrameReorderer::deferOvalOp(const OvalOp& op) {      deferStrokeableOp(op, tessBatchId(op));  } -void OpReorderer::deferPatchOp(const PatchOp& op) { +void FrameReorderer::deferPatchOp(const PatchOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected @@ -856,24 +520,24 @@ void OpReorderer::deferPatchOp(const PatchOp& op) {      }  } -void OpReorderer::deferPathOp(const PathOp& op) { +void FrameReorderer::deferPathOp(const PathOp& op) {      deferStrokeableOp(op, OpBatchType::Bitmap);  } -void OpReorderer::deferPointsOp(const PointsOp& op) { +void FrameReorderer::deferPointsOp(const PointsOp& op) {      batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;      deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);  } -void OpReorderer::deferRectOp(const RectOp& op) { +void FrameReorderer::deferRectOp(const RectOp& op) {      deferStrokeableOp(op, tessBatchId(op));  } -void OpReorderer::deferRoundRectOp(const RoundRectOp& op) { +void FrameReorderer::deferRoundRectOp(const RoundRectOp& op) {      deferStrokeableOp(op, tessBatchId(op));  } -void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) { +void FrameReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {      // allocate a temporary round rect op (with mAllocator, so it persists until render), so the      // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.      const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp( @@ -884,7 +548,7 @@ void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {      deferRoundRectOp(*resolvedOp);  } -void OpReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) { +void FrameReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected      currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices); @@ -895,7 +559,7 @@ static batchid_t textBatchId(const SkPaint& paint) {      return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;  } -void OpReorderer::deferTextOp(const TextOp& op) { +void FrameReorderer::deferTextOp(const TextOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected @@ -910,19 +574,19 @@ void OpReorderer::deferTextOp(const TextOp& op) {      }  } -void OpReorderer::deferTextOnPathOp(const TextOnPathOp& op) { +void FrameReorderer::deferTextOnPathOp(const TextOnPathOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected      currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));  } -void OpReorderer::deferTextureLayerOp(const TextureLayerOp& op) { +void FrameReorderer::deferTextureLayerOp(const TextureLayerOp& op) {      BakedOpState* bakedState = tryBakeOpState(op);      if (!bakedState) return; // quick rejected      currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);  } -void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, +void FrameReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,          float contentTranslateX, float contentTranslateY,          const Rect& repaintRect,          const Vector3& lightCenter, @@ -941,7 +605,7 @@ void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,      mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);  } -void OpReorderer::restoreForLayer() { +void FrameReorderer::restoreForLayer() {      // restore canvas, and pop finished layer off of the stack      mCanvasState.restore();      mLayerStack.pop_back(); @@ -949,7 +613,7 @@ void OpReorderer::restoreForLayer() {  // TODO: defer time rejection (when bounds become empty) + tests  // Option - just skip layers with no bounds at playback + defer? -void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) { +void FrameReorderer::deferBeginLayerOp(const BeginLayerOp& op) {      uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();      uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight(); @@ -994,7 +658,7 @@ void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {              &op, nullptr);  } -void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) { +void FrameReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {      const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;      int finishedLayerIndex = mLayerStack.back(); @@ -1022,7 +686,7 @@ void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {      }  } -void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { +void FrameReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {      Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));      boundsTransform.multiply(op.localMatrix); @@ -1057,7 +721,7 @@ void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {      currentLayer().activeUnclippedSaveLayers.push_back(bakedState);  } -void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) { +void FrameReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {      LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");      BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back(); diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/FrameReorderer.h index 8d9d90be057c..562e6a1a3d37 100644 --- a/libs/hwui/OpReorderer.h +++ b/libs/hwui/FrameReorderer.h @@ -1,5 +1,5 @@  /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -14,12 +14,12 @@   * limitations under the License.   */ -#ifndef ANDROID_HWUI_OP_REORDERER_H -#define ANDROID_HWUI_OP_REORDERER_H +#pragma once  #include "BakedOpState.h"  #include "CanvasState.h"  #include "DisplayList.h" +#include "LayerReorderer.h"  #include "RecordedOp.h"  #include <vector> @@ -31,114 +31,34 @@ namespace android {  namespace uirenderer {  class BakedOpState; -class BatchBase;  class LayerUpdateQueue; -class MergingOpBatch;  class OffscreenBuffer; -class OpBatch;  class Rect; -typedef int batchid_t; -typedef const void* mergeid_t; - -namespace OpBatchType { -    enum { -        Bitmap, -        MergedPatch, -        AlphaVertices, -        Vertices, -        AlphaMaskTexture, -        Text, -        ColorText, -        Shadow, -        TextureLayer, -        Functor, -        CopyToLayer, -        CopyFromLayer, - -        Count // must be last -    }; -} - -class OpReorderer : public CanvasStateClient { -    typedef void (*BakedOpReceiver)(void*, const BakedOpState&); -    typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList); - -    /** -     * Stores the deferred render operations and state used to compute ordering -     * for a single FBO/layer. -     */ -    class LayerReorderer { -    public: -        // Create LayerReorderer for Fbo0 -        LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect) -                : LayerReorderer(width, height, repaintRect, 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 Rect& repaintRect, 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 -        void locateInsertIndex(int batchId, const Rect& clippedBounds, -                BatchBase** targetBatch, size_t* insertBatchIndex) const; - -        void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId); - -        // insertion point of a new batch, will hopefully be immediately after similar batch -        // (generally, should be similar shader) -        void deferMergeableOp(LinearAllocator& allocator, -                BakedOpState* op, batchid_t batchId, mergeid_t mergeId); - -        void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const; - -        void deferLayerClear(const Rect& dstRect); - -        bool empty() const { -            return mBatches.empty(); -        } - -        void clear() { -            mBatches.clear(); -        } - -        void dump() const; - -        const uint32_t width; -        const uint32_t height; -        const Rect repaintRect; -        OffscreenBuffer* offscreenBuffer; -        const BeginLayerOp* beginLayerOp; -        const RenderNode* renderNode; -        const ClipRect viewportClip; - -        // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps -        std::vector<BakedOpState*> activeUnclippedSaveLayers; -    private: -        void flushLayerClears(LinearAllocator& allocator); - -        std::vector<BatchBase*> mBatches; - -        /** -         * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen -         * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not -         * collide, which avoids the need to resolve mergeid collisions. -         */ -        std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count]; - -        // Maps batch ids to the most recent *non-merging* batch of that id -        OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr }; - -        std::vector<Rect> mClearRects; -    }; - +/** + * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing + * them to be rendered. + * + * Resolves final drawing state for each operation (including clip, alpha and matrix), and then + * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either + * from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the + * draw stream) will create different reorder contexts, each in its own LayerReorderer. + * + * Then the prepared or 'baked' drawing commands can be issued by calling the templated + * replayBakedOps() function, which will dispatch them (including any created merged op collections) + * to a Dispatcher and Renderer. See BakedOpDispatcher for how these baked drawing operations are + * resolved into Glops and rendered via BakedOpRenderer. + * + * This class is also the authoritative source for traversing RenderNodes, both for standard op + * traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection. + */ +class FrameReorderer : public CanvasStateClient {  public: -    OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip, +    FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,              uint32_t viewportWidth, uint32_t viewportHeight,              const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter); -    virtual ~OpReorderer() {} +    virtual ~FrameReorderer() {}      /**       * replayBakedOps() is templated based on what class will receive ops being replayed. @@ -253,7 +173,7 @@ private:              BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);      /** -     * Declares all OpReorderer::deferXXXXOp() methods for every RecordedOp type. +     * Declares all FrameReorderer::deferXXXXOp() methods for every RecordedOp type.       *       * These private methods are called from within deferImpl to defer each individual op       * type differently. @@ -287,5 +207,3 @@ private:  }; // namespace uirenderer  }; // namespace android - -#endif // ANDROID_HWUI_OP_REORDERER_H diff --git a/libs/hwui/LayerReorderer.cpp b/libs/hwui/LayerReorderer.cpp new file mode 100644 index 000000000000..9a17e9349e56 --- /dev/null +++ b/libs/hwui/LayerReorderer.cpp @@ -0,0 +1,367 @@ +/* + * 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 "LayerReorderer.h" + +#include "BakedOpState.h" +#include "RenderNode.h" +#include "utils/PaintUtils.h" +#include "utils/TraceUtils.h" + +#include <utils/TypeHelpers.h> + +namespace android { +namespace uirenderer { + +class BatchBase { +public: +    BatchBase(batchid_t batchId, BakedOpState* op, bool merging) +            : mBatchId(batchId) +            , mMerging(merging) { +        mBounds = op->computedState.clippedBounds; +        mOps.push_back(op); +    } + +    bool intersects(const Rect& rect) const { +        if (!rect.intersects(mBounds)) return false; + +        for (const BakedOpState* op : mOps) { +            if (rect.intersects(op->computedState.clippedBounds)) { +                return true; +            } +        } +        return false; +    } + +    batchid_t getBatchId() const { return mBatchId; } +    bool isMerging() const { return mMerging; } + +    const std::vector<BakedOpState*>& getOps() const { return mOps; } + +    void dump() const { +        ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING, +                this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds)); +    } +protected: +    batchid_t mBatchId; +    Rect mBounds; +    std::vector<BakedOpState*> mOps; +    bool mMerging; +}; + +class OpBatch : public BatchBase { +public: +    static void* operator new(size_t size, LinearAllocator& allocator) { +        return allocator.alloc(size); +    } + +    OpBatch(batchid_t batchId, BakedOpState* op) +            : BatchBase(batchId, op, false) { +    } + +    void batchOp(BakedOpState* op) { +        mBounds.unionWith(op->computedState.clippedBounds); +        mOps.push_back(op); +    } +}; + +class MergingOpBatch : public BatchBase { +public: +    static void* operator new(size_t size, LinearAllocator& allocator) { +        return allocator.alloc(size); +    } + +    MergingOpBatch(batchid_t batchId, BakedOpState* op) +            : BatchBase(batchId, op, true) +            , mClipSideFlags(op->computedState.clipSideFlags) { +    } + +    /* +     * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds +     * and clip side flags. Positive bounds delta means new bounds fit in old. +     */ +    static inline bool checkSide(const int currentFlags, const int newFlags, const int side, +            float boundsDelta) { +        bool currentClipExists = currentFlags & side; +        bool newClipExists = newFlags & side; + +        // if current is clipped, we must be able to fit new bounds in current +        if (boundsDelta > 0 && currentClipExists) return false; + +        // if new is clipped, we must be able to fit current bounds in new +        if (boundsDelta < 0 && newClipExists) return false; + +        return true; +    } + +    static bool paintIsDefault(const SkPaint& paint) { +        return paint.getAlpha() == 255 +                && paint.getColorFilter() == nullptr +                && paint.getShader() == nullptr; +    } + +    static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) { +        // Note: don't check color, since all currently mergeable ops can merge across colors +        return a.getAlpha() == b.getAlpha() +                && a.getColorFilter() == b.getColorFilter() +                && a.getShader() == b.getShader(); +    } + +    /* +     * Checks if a (mergeable) op can be merged into this batch +     * +     * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is +     * important to consider all paint attributes used in the draw calls in deciding both a) if an +     * op tries to merge at all, and b) if the op can merge with another set of ops +     * +     * False positives can lead to information from the paints of subsequent merged operations being +     * dropped, so we make simplifying qualifications on the ops that can merge, per op type. +     */ +    bool canMergeWith(BakedOpState* op) const { +        bool isTextBatch = getBatchId() == OpBatchType::Text +                || getBatchId() == OpBatchType::ColorText; + +        // Overlapping other operations is only allowed for text without shadow. For other ops, +        // multiDraw isn't guaranteed to overdraw correctly +        if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) { +            if (intersects(op->computedState.clippedBounds)) return false; +        } + +        const BakedOpState* lhs = op; +        const BakedOpState* rhs = mOps[0]; + +        if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false; + +        // Identical round rect clip state means both ops will clip in the same way, or not at all. +        // As the state objects are const, we can compare their pointers to determine mergeability +        if (lhs->roundRectClipState != rhs->roundRectClipState) return false; +        if (lhs->projectionPathMask != rhs->projectionPathMask) return false; + +        /* Clipping compatibility check +         * +         * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its +         * clip for that side. +         */ +        const int currentFlags = mClipSideFlags; +        const int newFlags = op->computedState.clipSideFlags; +        if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) { +            const Rect& opBounds = op->computedState.clippedBounds; +            float boundsDelta = mBounds.left - opBounds.left; +            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false; +            boundsDelta = mBounds.top - opBounds.top; +            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false; + +            // right and bottom delta calculation reversed to account for direction +            boundsDelta = opBounds.right - mBounds.right; +            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false; +            boundsDelta = opBounds.bottom - mBounds.bottom; +            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false; +        } + +        const SkPaint* newPaint = op->op->paint; +        const SkPaint* oldPaint = mOps[0]->op->paint; + +        if (newPaint == oldPaint) { +            // if paints are equal, then modifiers + paint attribs don't need to be compared +            return true; +        } else if (newPaint && !oldPaint) { +            return paintIsDefault(*newPaint); +        } else if (!newPaint && oldPaint) { +            return paintIsDefault(*oldPaint); +        } +        return paintsAreEquivalent(*newPaint, *oldPaint); +    } + +    void mergeOp(BakedOpState* op) { +        mBounds.unionWith(op->computedState.clippedBounds); +        mOps.push_back(op); + +        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat +        // check, and doesn't extend past a side of the clip that's in use by the merged batch. +        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect. +        mClipSideFlags |= op->computedState.clipSideFlags; +    } + +    int getClipSideFlags() const { return mClipSideFlags; } +    const Rect& getClipRect() const { return mBounds; } + +private: +    int mClipSideFlags; +}; + +LayerReorderer::LayerReorderer(uint32_t width, uint32_t height, +        const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode) +        : width(width) +        , height(height) +        , repaintRect(repaintRect) +        , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr) +        , beginLayerOp(beginLayerOp) +        , renderNode(renderNode) +        , viewportClip(Rect(width, height)) {} + +// 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 LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds, +        BatchBase** targetBatch, size_t* insertBatchIndex) const { +    for (int i = mBatches.size() - 1; i >= 0; i--) { +        BatchBase* overBatch = mBatches[i]; + +        if (overBatch == *targetBatch) break; + +        // TODO: also consider shader shared between batch types +        if (batchId == overBatch->getBatchId()) { +            *insertBatchIndex = i + 1; +            if (!*targetBatch) break; // found insert position, quit +        } + +        if (overBatch->intersects(clippedBounds)) { +            // NOTE: it may be possible to optimize for special cases where two operations +            // of the same batch/paint could swap order, such as with a non-mergeable +            // (clipped) and a mergeable text operation +            *targetBatch = nullptr; +            break; +        } +    } +} + +void LayerReorderer::deferLayerClear(const Rect& rect) { +    mClearRects.push_back(rect); +} + +void LayerReorderer::flushLayerClears(LinearAllocator& allocator) { +    if (CC_UNLIKELY(!mClearRects.empty())) { +        const int vertCount = mClearRects.size() * 4; +        // put the verts in the frame allocator, since +        //     1) SimpleRectsOps needs verts, not rects +        //     2) even if mClearRects stored verts, std::vectors will move their contents +        Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex)); + +        Vertex* currentVert = verts; +        Rect bounds = mClearRects[0]; +        for (auto&& rect : mClearRects) { +            bounds.unionWith(rect); +            Vertex::set(currentVert++, rect.left, rect.top); +            Vertex::set(currentVert++, rect.right, rect.top); +            Vertex::set(currentVert++, rect.left, rect.bottom); +            Vertex::set(currentVert++, rect.right, rect.bottom); +        } +        mClearRects.clear(); // discard rects before drawing so this method isn't reentrant + +        // One or more unclipped saveLayers have been enqueued, with deferred clears. +        // Flush all of these clears with a single draw +        SkPaint* paint = allocator.create<SkPaint>(); +        paint->setXfermodeMode(SkXfermode::kClear_Mode); +        SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds, +                Matrix4::identity(), nullptr, paint, +                verts, vertCount); +        BakedOpState* bakedState = BakedOpState::directConstruct(allocator, +                &viewportClip, bounds, *op); + + +        deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices); +    } +} + +void LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator, +        BakedOpState* op, batchid_t batchId) { +    if (batchId != OpBatchType::CopyToLayer) { +        // if first op after one or more unclipped saveLayers, flush the layer clears +        flushLayerClears(allocator); +    } + +    OpBatch* targetBatch = mBatchLookup[batchId]; + +    size_t insertBatchIndex = mBatches.size(); +    if (targetBatch) { +        locateInsertIndex(batchId, op->computedState.clippedBounds, +                (BatchBase**)(&targetBatch), &insertBatchIndex); +    } + +    if (targetBatch) { +        targetBatch->batchOp(op); +    } else  { +        // new non-merging batch +        targetBatch = new (allocator) OpBatch(batchId, op); +        mBatchLookup[batchId] = targetBatch; +        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); +    } +} + +void LayerReorderer::deferMergeableOp(LinearAllocator& allocator, +        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) { +    if (batchId != OpBatchType::CopyToLayer) { +        // if first op after one or more unclipped saveLayers, flush the layer clears +        flushLayerClears(allocator); +    } +    MergingOpBatch* targetBatch = nullptr; + +    // Try to merge with any existing batch with same mergeId +    auto getResult = mMergingBatchLookup[batchId].find(mergeId); +    if (getResult != mMergingBatchLookup[batchId].end()) { +        targetBatch = getResult->second; +        if (!targetBatch->canMergeWith(op)) { +            targetBatch = nullptr; +        } +    } + +    size_t insertBatchIndex = mBatches.size(); +    locateInsertIndex(batchId, op->computedState.clippedBounds, +            (BatchBase**)(&targetBatch), &insertBatchIndex); + +    if (targetBatch) { +        targetBatch->mergeOp(op); +    } else  { +        // new merging batch +        targetBatch = new (allocator) MergingOpBatch(batchId, op); +        mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch)); + +        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); +    } +} + +void LayerReorderer::replayBakedOpsImpl(void* arg, +        BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const { +    ATRACE_NAME("flush drawing commands"); +    for (const BatchBase* batch : mBatches) { +        size_t size = batch->getOps().size(); +        if (size > 1 && batch->isMerging()) { +            int opId = batch->getOps()[0]->op->opId; +            const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch); +            MergedBakedOpList data = { +                    batch->getOps().data(), +                    size, +                    mergingBatch->getClipSideFlags(), +                    mergingBatch->getClipRect() +            }; +            mergedReceivers[opId](arg, data); +        } else { +            for (const BakedOpState* op : batch->getOps()) { +                unmergedReceivers[op->op->opId](arg, *op); +            } +        } +    } +} + +void 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(); +    } +} + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/LayerReorderer.h b/libs/hwui/LayerReorderer.h new file mode 100644 index 000000000000..83cda812035d --- /dev/null +++ b/libs/hwui/LayerReorderer.h @@ -0,0 +1,135 @@ +/* + * 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. + */ + +#pragma once + +#include "ClipArea.h" +#include "Rect.h" + +#include <vector> +#include <unordered_map> + +struct SkRect; + +namespace android { +namespace uirenderer { + +class BakedOpState; +struct BeginLayerOp; +class BatchBase; +class LinearAllocator; +struct MergedBakedOpList; +class MergingOpBatch; +class OffscreenBuffer; +class OpBatch; +class RenderNode; + +typedef int batchid_t; +typedef const void* mergeid_t; + +namespace OpBatchType { +    enum { +        Bitmap, +        MergedPatch, +        AlphaVertices, +        Vertices, +        AlphaMaskTexture, +        Text, +        ColorText, +        Shadow, +        TextureLayer, +        Functor, +        CopyToLayer, +        CopyFromLayer, + +        Count // must be last +    }; +} + +typedef void (*BakedOpReceiver)(void*, const BakedOpState&); +typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList); + +/** + * Stores the deferred render operations and state used to compute ordering + * for a single FBO/layer. + */ +class LayerReorderer { +public: +    // Create LayerReorderer for Fbo0 +    LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect) +            : LayerReorderer(width, height, repaintRect, 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 Rect& repaintRect, 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 +    void locateInsertIndex(int batchId, const Rect& clippedBounds, +            BatchBase** targetBatch, size_t* insertBatchIndex) const; + +    void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId); + +    // insertion point of a new batch, will hopefully be immediately after similar batch +    // (generally, should be similar shader) +    void deferMergeableOp(LinearAllocator& allocator, +            BakedOpState* op, batchid_t batchId, mergeid_t mergeId); + +    void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const; + +    void deferLayerClear(const Rect& dstRect); + +    bool empty() const { +        return mBatches.empty(); +    } + +    void clear() { +        mBatches.clear(); +    } + +    void dump() const; + +    const uint32_t width; +    const uint32_t height; +    const Rect repaintRect; +    OffscreenBuffer* offscreenBuffer; +    const BeginLayerOp* beginLayerOp; +    const RenderNode* renderNode; +    const ClipRect viewportClip; + +    // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps +    std::vector<BakedOpState*> activeUnclippedSaveLayers; +private: +    void flushLayerClears(LinearAllocator& allocator); + +    std::vector<BatchBase*> mBatches; + +    /** +     * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen +     * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not +     * collide, which avoids the need to resolve mergeid collisions. +     */ +    std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count]; + +    // Maps batch ids to the most recent *non-merging* batch of that id +    OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr }; + +    std::vector<Rect> mClearRects; +}; + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index b6f50b111ab5..612cdfdc7185 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -87,7 +87,7 @@ class RenderNode;   */  class RenderNode : public VirtualLightRefBase {  friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties -friend class OpReorderer; +friend class FrameReorderer;  public:      enum DirtyPropertyMask {          GENERIC         = 1 << 1, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 1af8f804e72a..fff8e0968ee6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -31,7 +31,7 @@  #include "utils/TimeUtils.h"  #if HWUI_NEW_OPS -#include "OpReorderer.h" +#include "FrameReorderer.h"  #endif  #include <cutils/properties.h> @@ -338,7 +338,7 @@ void CanvasContext::draw() {      mEglManager.damageFrame(frame, dirty);  #if HWUI_NEW_OPS -    OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(), +    FrameReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),              mRenderNodes, mLightCenter);      mLayerUpdateQueue.clear();      BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), diff --git a/libs/hwui/tests/microbench/OpReordererBench.cpp b/libs/hwui/tests/microbench/FrameReordererBench.cpp index 6bfe5a9a1028..b4c9a3626597 100644 --- a/libs/hwui/tests/microbench/OpReordererBench.cpp +++ b/libs/hwui/tests/microbench/FrameReordererBench.cpp @@ -1,5 +1,5 @@  /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -19,8 +19,8 @@  #include "BakedOpState.h"  #include "BakedOpDispatcher.h"  #include "BakedOpRenderer.h" +#include "FrameReorderer.h"  #include "LayerUpdateQueue.h" -#include "OpReorderer.h"  #include "RecordedOp.h"  #include "RecordingCanvas.h"  #include "tests/common/TestContext.h" @@ -61,20 +61,20 @@ static std::vector<sp<RenderNode>> createTestNodeList() {      return vec;  } -BENCHMARK_NO_ARG(BM_OpReorderer_defer); -void BM_OpReorderer_defer::Run(int iters) { +BENCHMARK_NO_ARG(BM_FrameBuilder_defer); +void BM_FrameBuilder_defer::Run(int iters) {      auto nodes = createTestNodeList();      StartBenchmarkTiming();      for (int i = 0; i < iters; i++) { -        OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, +        FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,                  nodes, sLightCenter);          MicroBench::DoNotOptimize(&reorderer);      }      StopBenchmarkTiming();  } -BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender); -void BM_OpReorderer_deferAndRender::Run(int iters) { +BENCHMARK_NO_ARG(BM_FrameBuilder_deferAndRender); +void BM_FrameBuilder_deferAndRender::Run(int iters) {      TestUtils::runOnRenderThread([this, iters](RenderThread& thread) {          auto nodes = createTestNodeList();          BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128 }; @@ -84,7 +84,7 @@ void BM_OpReorderer_deferAndRender::Run(int iters) {          StartBenchmarkTiming();          for (int i = 0; i < iters; i++) { -            OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, +            FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,                      nodes, sLightCenter);              BakedOpRenderer renderer(caches, renderState, true, lightInfo); @@ -117,7 +117,7 @@ static void benchDeferScene(testing::Benchmark& benchmark, int iters, const char      auto nodes = getSyncedSceneNodes(sceneName);      benchmark.StartBenchmarkTiming();      for (int i = 0; i < iters; i++) { -        OpReorderer reorderer(sEmptyLayerUpdateQueue, +        FrameReorderer reorderer(sEmptyLayerUpdateQueue,                  SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,                  nodes, sLightCenter);          MicroBench::DoNotOptimize(&reorderer); @@ -136,7 +136,7 @@ static void benchDeferAndRenderScene(testing::Benchmark& benchmark,          benchmark.StartBenchmarkTiming();          for (int i = 0; i < iters; i++) { -            OpReorderer reorderer(sEmptyLayerUpdateQueue, +            FrameReorderer reorderer(sEmptyLayerUpdateQueue,                      SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,                      nodes, sLightCenter); @@ -148,13 +148,13 @@ static void benchDeferAndRenderScene(testing::Benchmark& benchmark,      });  } -BENCHMARK_NO_ARG(BM_OpReorderer_listview_defer); -void BM_OpReorderer_listview_defer::Run(int iters) { +BENCHMARK_NO_ARG(BM_FrameBuilder_listview_defer); +void BM_FrameBuilder_listview_defer::Run(int iters) {      benchDeferScene(*this, iters, "listview");  } -BENCHMARK_NO_ARG(BM_OpReorderer_listview_deferAndRender); -void BM_OpReorderer_listview_deferAndRender::Run(int iters) { +BENCHMARK_NO_ARG(BM_FrameBuilder_listview_deferAndRender); +void BM_FrameBuilder_listview_deferAndRender::Run(int iters) {      benchDeferAndRenderScene(*this, iters, "listview");  } diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/FrameReordererTests.cpp index 701e4460c35f..9d2eb98a011d 100644 --- a/libs/hwui/tests/unit/OpReordererTests.cpp +++ b/libs/hwui/tests/unit/FrameReordererTests.cpp @@ -1,5 +1,5 @@  /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -18,8 +18,8 @@  #include <BakedOpState.h>  #include <DeferredLayerUpdater.h> +#include <FrameReorderer.h>  #include <LayerUpdateQueue.h> -#include <OpReorderer.h>  #include <RecordedOp.h>  #include <RecordingCanvas.h>  #include <tests/common/TestUtils.h> @@ -113,7 +113,7 @@ public:  class FailRenderer : public TestRendererBase {}; -TEST(OpReorderer, simple) { +TEST(FrameReorderer, simple) {      class SimpleTestRenderer : public TestRendererBase {      public:          void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { @@ -138,14 +138,14 @@ TEST(OpReorderer, simple) {          canvas.drawRect(0, 0, 100, 200, SkPaint());          canvas.drawBitmap(bitmap, 10, 10, nullptr);      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,              createSyncedNodeList(node), sLightCenter);      SimpleTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end  } -TEST(OpReorderer, simpleStroke) { +TEST(FrameReorderer, simpleStroke) {      class SimpleStrokeTestRenderer : public TestRendererBase {      public:          void onPointsOp(const PointsOp& op, const BakedOpState& state) override { @@ -164,14 +164,14 @@ TEST(OpReorderer, simpleStroke) {          strokedPaint.setStrokeWidth(10);          canvas.drawPoint(50, 50, strokedPaint);      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,              createSyncedNodeList(node), sLightCenter);      SimpleStrokeTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(1, renderer.getIndex());  } -TEST(OpReorderer, simpleRejection) { +TEST(FrameReorderer, simpleRejection) {      auto node = TestUtils::createNode(0, 0, 200, 200,              [](RenderProperties& props, RecordingCanvas& canvas) {          canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); @@ -179,14 +179,14 @@ TEST(OpReorderer, simpleRejection) {          canvas.drawRect(0, 0, 400, 400, SkPaint());          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(node), sLightCenter);      FailRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);  } -TEST(OpReorderer, simpleBatching) { +TEST(FrameReorderer, simpleBatching) {      const int LOOPS = 5;      class SimpleBatchingTestRenderer : public TestRendererBase {      public: @@ -214,7 +214,7 @@ TEST(OpReorderer, simpleBatching) {          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(node), sLightCenter);      SimpleBatchingTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -222,7 +222,7 @@ TEST(OpReorderer, simpleBatching) {              << "Expect number of ops = 2 * loop count";  } -TEST(OpReorderer, clippedMerging) { +TEST(FrameReorderer, clippedMerging) {      class ClippedMergingTestRenderer : public TestRendererBase {      public:          void onMergedBitmapOps(const MergedBakedOpList& opList) override { @@ -255,14 +255,14 @@ TEST(OpReorderer, clippedMerging) {          canvas.drawBitmap(bitmap, 40, 70, nullptr);      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,              createSyncedNodeList(node), sLightCenter);      ClippedMergingTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(4, renderer.getIndex());  } -TEST(OpReorderer, textMerging) { +TEST(FrameReorderer, textMerging) {      class TextMergingTestRenderer : public TestRendererBase {      public:          void onMergedTextOps(const MergedBakedOpList& opList) override { @@ -283,14 +283,14 @@ TEST(OpReorderer, textMerging) {          TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped          TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,              createSyncedNodeList(node), sLightCenter);      TextMergingTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";  } -TEST(OpReorderer, textStrikethrough) { +TEST(FrameReorderer, textStrikethrough) {      const int LOOPS = 5;      class TextStrikethroughTestRenderer : public TestRendererBase {      public: @@ -314,7 +314,7 @@ TEST(OpReorderer, textStrikethrough) {              TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));          }      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,              createSyncedNodeList(node), sLightCenter);      TextStrikethroughTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -322,7 +322,7 @@ TEST(OpReorderer, textStrikethrough) {              << "Expect number of ops = 2 * loop count";  } -RENDERTHREAD_TEST(OpReorderer, textureLayer) { +RENDERTHREAD_TEST(FrameReorderer, textureLayer) {      class TextureLayerTestRenderer : public TestRendererBase {      public:          void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { @@ -348,14 +348,14 @@ RENDERTHREAD_TEST(OpReorderer, textureLayer) {          canvas.drawLayer(layerUpdater.get());          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(node), sLightCenter);      TextureLayerTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(1, renderer.getIndex());  } -TEST(OpReorderer, renderNode) { +TEST(FrameReorderer, renderNode) {      class RenderNodeTestRenderer : public TestRendererBase {      public:          void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -393,13 +393,13 @@ TEST(OpReorderer, renderNode) {          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(parent), sLightCenter);      RenderNodeTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);  } -TEST(OpReorderer, clipped) { +TEST(FrameReorderer, clipped) {      class ClippedTestRenderer : public TestRendererBase {      public:          void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { @@ -416,14 +416,14 @@ TEST(OpReorderer, clipped) {          canvas.drawBitmap(bitmap, 0, 0, nullptr);      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue,              SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver              200, 200, createSyncedNodeList(node), sLightCenter);      ClippedTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);  } -TEST(OpReorderer, saveLayer_simple) { +TEST(FrameReorderer, saveLayer_simple) {      class SaveLayerSimpleTestRenderer : public TestRendererBase {      public:          OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { @@ -459,14 +459,14 @@ TEST(OpReorderer, saveLayer_simple) {          canvas.drawRect(10, 10, 190, 190, SkPaint());          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(node), sLightCenter);      SaveLayerSimpleTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(4, renderer.getIndex());  } -TEST(OpReorderer, saveLayer_nested) { +TEST(FrameReorderer, saveLayer_nested) {      /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:       * - startTemporaryLayer2, rect2 endLayer2       * - startTemporaryLayer1, rect1, drawLayer2, endLayer1 @@ -531,14 +531,14 @@ TEST(OpReorderer, saveLayer_nested) {          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,              createSyncedNodeList(node), sLightCenter);      SaveLayerNestedTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(10, renderer.getIndex());  } -TEST(OpReorderer, saveLayer_contentRejection) { +TEST(FrameReorderer, saveLayer_contentRejection) {          auto node = TestUtils::createNode(0, 0, 200, 200,                  [](RenderProperties& props, RecordingCanvas& canvas) {          canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); @@ -551,7 +551,7 @@ TEST(OpReorderer, saveLayer_contentRejection) {          canvas.restore();          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(node), sLightCenter);      FailRenderer renderer; @@ -559,7 +559,7 @@ TEST(OpReorderer, saveLayer_contentRejection) {      reorderer.replayBakedOps<TestDispatcher>(renderer);  } -TEST(OpReorderer, saveLayerUnclipped_simple) { +TEST(FrameReorderer, saveLayerUnclipped_simple) {      class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {      public:          void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { @@ -594,14 +594,14 @@ TEST(OpReorderer, saveLayerUnclipped_simple) {          canvas.drawRect(0, 0, 200, 200, SkPaint());          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(node), sLightCenter);      SaveLayerUnclippedSimpleTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(4, renderer.getIndex());  } -TEST(OpReorderer, saveLayerUnclipped_mergedClears) { +TEST(FrameReorderer, saveLayerUnclipped_mergedClears) {      class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {      public:          void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { @@ -648,7 +648,7 @@ TEST(OpReorderer, saveLayerUnclipped_mergedClears) {          canvas.drawRect(0, 0, 100, 100, SkPaint());          canvas.restoreToCount(restoreTo);      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(node), sLightCenter);      SaveLayerUnclippedMergedClearsTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -660,7 +660,7 @@ TEST(OpReorderer, saveLayerUnclipped_mergedClears) {   * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer   * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe   */ -TEST(OpReorderer, saveLayerUnclipped_complex) { +TEST(FrameReorderer, saveLayerUnclipped_complex) {      class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {      public:          OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { @@ -710,14 +710,14 @@ TEST(OpReorderer, saveLayerUnclipped_complex) {          canvas.restore();          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,              createSyncedNodeList(node), sLightCenter);      SaveLayerUnclippedComplexTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(12, renderer.getIndex());  } -RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) { +RENDERTHREAD_TEST(FrameReorderer, hwLayer_simple) {      class HwLayerSimpleTestRenderer : public TestRendererBase {      public:          void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { @@ -768,7 +768,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {      LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid      layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); -    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              syncedNodeList, sLightCenter);      HwLayerSimpleTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -778,7 +778,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {      *layerHandle = nullptr;  } -RENDERTHREAD_TEST(OpReorderer, hwLayer_complex) { +RENDERTHREAD_TEST(FrameReorderer, hwLayer_complex) {      /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:       * - startRepaintLayer(child), rect(grey), endLayer       * - startTemporaryLayer, drawLayer(child), endLayer @@ -869,7 +869,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_complex) {      layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));      layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); -    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              syncedList, sLightCenter);      HwLayerComplexTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -894,7 +894,7 @@ static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder,      node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);      canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership  } -TEST(OpReorderer, zReorder) { +TEST(FrameReorderer, zReorder) {      class ZReorderTestRenderer : public TestRendererBase {      public:          void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -918,14 +918,14 @@ TEST(OpReorderer, zReorder) {          drawOrderedRect(&canvas, 8);          drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,              createSyncedNodeList(parent), sLightCenter);      ZReorderTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(10, renderer.getIndex());  }; -TEST(OpReorderer, projectionReorder) { +TEST(FrameReorderer, projectionReorder) {      static const int scrollX = 5;      static const int scrollY = 10;      class ProjectionReorderTestRenderer : public TestRendererBase { @@ -1001,7 +1001,7 @@ TEST(OpReorderer, projectionReorder) {          canvas.restore();      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,              createSyncedNodeList(parent), sLightCenter);      ProjectionReorderTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -1020,7 +1020,7 @@ static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {      });  } -TEST(OpReorderer, shadow) { +TEST(FrameReorderer, shadow) {      class ShadowTestRenderer : public TestRendererBase {      public:          void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { @@ -1044,14 +1044,14 @@ TEST(OpReorderer, shadow) {          canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(parent), sLightCenter);      ShadowTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(2, renderer.getIndex());  } -TEST(OpReorderer, shadowSaveLayer) { +TEST(FrameReorderer, shadowSaveLayer) {      class ShadowSaveLayerTestRenderer : public TestRendererBase {      public:          OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { @@ -1085,14 +1085,14 @@ TEST(OpReorderer, shadowSaveLayer) {          canvas.restoreToCount(count);      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });      ShadowSaveLayerTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(5, renderer.getIndex());  } -RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) { +RENDERTHREAD_TEST(FrameReorderer, shadowHwLayer) {      class ShadowHwLayerTestRenderer : public TestRendererBase {      public:          void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { @@ -1135,7 +1135,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {      auto syncedList = createSyncedNodeList(parent);      LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid      layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); -    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              syncedList, (Vector3) { 100, 100, 100 });      ShadowHwLayerTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -1145,7 +1145,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {      *layerHandle = nullptr;  } -TEST(OpReorderer, shadowLayering) { +TEST(FrameReorderer, shadowLayering) {      class ShadowLayeringTestRenderer : public TestRendererBase {      public:          void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { @@ -1164,7 +1164,7 @@ TEST(OpReorderer, shadowLayering) {          canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,              createSyncedNodeList(parent), sLightCenter);      ShadowLayeringTestRenderer renderer;      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -1192,14 +1192,14 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac          canvas.drawRect(0, 0, 100, 100, paint);      }); -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,              createSyncedNodeList(node), sLightCenter);      PropertyTestRenderer renderer(opValidateCallback);      reorderer.replayBakedOps<TestDispatcher>(renderer);      EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";  } -TEST(OpReorderer, renderPropOverlappingRenderingAlpha) { +TEST(FrameReorderer, renderPropOverlappingRenderingAlpha) {      testProperty([](RenderProperties& properties) {          properties.setAlpha(0.5f);          properties.setHasOverlappingRendering(false); @@ -1208,7 +1208,7 @@ TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {      });  } -TEST(OpReorderer, renderPropClipping) { +TEST(FrameReorderer, renderPropClipping) {      testProperty([](RenderProperties& properties) {          properties.setClipToBounds(true);          properties.setClipBounds(Rect(10, 20, 300, 400)); @@ -1218,7 +1218,7 @@ TEST(OpReorderer, renderPropClipping) {      });  } -TEST(OpReorderer, renderPropRevealClip) { +TEST(FrameReorderer, renderPropRevealClip) {      testProperty([](RenderProperties& properties) {          properties.mutableRevealClip().set(true, 50, 50, 25);      }, [](const RectOp& op, const BakedOpState& state) { @@ -1229,7 +1229,7 @@ TEST(OpReorderer, renderPropRevealClip) {      });  } -TEST(OpReorderer, renderPropOutlineClip) { +TEST(FrameReorderer, renderPropOutlineClip) {      testProperty([](RenderProperties& properties) {          properties.mutableOutline().setShouldClip(true);          properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); @@ -1241,7 +1241,7 @@ TEST(OpReorderer, renderPropOutlineClip) {      });  } -TEST(OpReorderer, renderPropTransform) { +TEST(FrameReorderer, renderPropTransform) {      testProperty([](RenderProperties& properties) {          properties.setLeftTopRightBottom(10, 10, 110, 110); @@ -1334,7 +1334,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,      });      auto nodes = createSyncedNodeList(node); // sync before querying height -    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter); +    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);      SaveLayerAlphaClipTestRenderer renderer(outObservedData);      reorderer.replayBakedOps<TestDispatcher>(renderer); @@ -1342,7 +1342,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,      ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";  } -TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) { +TEST(FrameReorderer, renderPropSaveLayerAlphaClipBig) {      SaveLayerAlphaData observedData;      testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {          properties.setTranslationX(10); // offset rendering content @@ -1358,7 +1358,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {              << "expect content to be translated as part of being clipped";  } -TEST(OpReorderer, renderPropSaveLayerAlphaRotate) { +TEST(FrameReorderer, renderPropSaveLayerAlphaRotate) {      SaveLayerAlphaData observedData;      testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {          // Translate and rotate the view so that the only visible part is the top left corner of @@ -1377,7 +1377,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {      EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);  } -TEST(OpReorderer, renderPropSaveLayerAlphaScale) { +TEST(FrameReorderer, renderPropSaveLayerAlphaScale) {      SaveLayerAlphaData observedData;      testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {          properties.setPivotX(0); |