diff options
| -rw-r--r-- | libs/hwui/DeferredDisplayList.cpp | 151 | ||||
| -rw-r--r-- | libs/hwui/DeferredDisplayList.h | 33 | ||||
| -rw-r--r-- | libs/hwui/DisplayListOp.h | 144 | ||||
| -rw-r--r-- | libs/hwui/Layer.cpp | 14 | ||||
| -rw-r--r-- | libs/hwui/Matrix.h | 6 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 58 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 12 | ||||
| -rw-r--r-- | libs/hwui/Rect.h | 4 |
8 files changed, 282 insertions, 140 deletions
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 512d3b1075c4..6bb54a7840e6 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -20,6 +20,8 @@ #include <SkCanvas.h> #include <utils/Trace.h> +#include <ui/Rect.h> +#include <ui/Region.h> #include "Caches.h" #include "Debug.h" @@ -51,19 +53,23 @@ class Batch { public: virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0; virtual ~Batch() {} + virtual bool purelyDrawBatch() { return false; } + virtual bool coversBounds(const Rect& bounds) { return false; } }; class DrawBatch : public Batch { public: - DrawBatch(int batchId, mergeid_t mergeId) : mBatchId(batchId), mMergeId(mergeId) { + DrawBatch(const DeferInfo& deferInfo) : mAllOpsOpaque(true), + mBatchId(deferInfo.batchId), mMergeId(deferInfo.mergeId) { mOps.clear(); } virtual ~DrawBatch() { mOps.clear(); } - void add(DrawOp* op) { + virtual void add(DrawOp* op, bool opaqueOverBounds) { // NOTE: ignore empty bounds special case, since we don't merge across those ops mBounds.unionWith(op->state.mBounds); + mAllOpsOpaque &= opaqueOverBounds; mOps.add(op); } @@ -114,14 +120,28 @@ public: return status; } + virtual bool purelyDrawBatch() { return true; } + + virtual bool coversBounds(const Rect& bounds) { + if (CC_LIKELY(!mAllOpsOpaque || !mBounds.contains(bounds) || count() == 1)) return false; + + Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom)); + for (unsigned int i = 0; i < mOps.size(); i++) { + Rect &r = mOps[i]->state.mBounds; + uncovered.subtractSelf(android::Rect(r.left, r.top, r.right, r.bottom)); + } + return uncovered.isEmpty(); + } + inline int getBatchId() const { return mBatchId; } inline mergeid_t getMergeId() const { return mMergeId; } inline int count() const { return mOps.size(); } protected: Vector<DrawOp*> mOps; - Rect mBounds; + Rect mBounds; // union of bounds of contained ops private: + bool mAllOpsOpaque; int mBatchId; mergeid_t mMergeId; }; @@ -132,7 +152,8 @@ private: class MergingDrawBatch : public DrawBatch { public: - MergingDrawBatch(int batchId, mergeid_t mergeId) : DrawBatch(batchId, mergeId) {} + MergingDrawBatch(DeferInfo& deferInfo, Rect viewport) : + DrawBatch(deferInfo), mClipRect(viewport), mClipSideFlags(kClipSide_Unclipped) {} /* * Checks if a (mergeable) op can be merged into this batch @@ -145,11 +166,6 @@ public: * dropped, so we make simplifying qualifications on the ops that can merge, per op type. */ bool canMergeWith(DrawOp* op) { - if (getBatchId() == DeferredDisplayList::kOpBatch_Bitmap) { - // Bitmap batches can handle translate and scaling - if (!op->state.mMatrix.isSimple()) return false; - } else if (!op->state.mMatrix.isPureTranslate()) return false; - bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text || getBatchId() == DeferredDisplayList::kOpBatch_ColorText; @@ -158,12 +174,29 @@ public: if (!isTextBatch || op->state.mDrawModifiers.mHasShadow) { if (intersects(op->state.mBounds)) return false; } - const DeferredDisplayState& lhs = op->state; const DeferredDisplayState& rhs = mOps[0]->state; if (NEQ_FALPHA(lhs.mAlpha, rhs.mAlpha)) return false; + // If colliding flags, ensure bounds are equal + // NOTE: only needed if op to be added is clipped *and* within old valid clip (per dimension) + int collidingClipSideFlags = mClipSideFlags & op->state.mClipSideFlags; + if (CC_UNLIKELY(collidingClipSideFlags)) { + // if multiple ops are clipped on the same side, they must be clipped at the same + // coordinate to be merged + if ((collidingClipSideFlags & kClipSide_Left) && + mClipRect.left != op->state.mClip.left) return false; + if ((collidingClipSideFlags & kClipSide_Top) && + mClipRect.top != op->state.mClip.top) return false; + if ((collidingClipSideFlags & kClipSide_Right) && + mClipRect.right != op->state.mClip.right) return false; + if ((collidingClipSideFlags & kClipSide_Bottom) && + mClipRect.bottom != op->state.mClip.bottom) return false; + } + // if op is outside of batch clip rect, it can't share its clip + if (!mClipRect.contains(op->state.mBounds)) return false; + // if paints are equal, then modifiers + paint attribs don't need to be compared if (op->mPaint == mOps[0]->mPaint) return true; @@ -181,7 +214,6 @@ public: * * These ignore cases prevent us from simply memcmp'ing the drawModifiers */ - const DrawModifiers& lhsMod = lhs.mDrawModifiers; const DrawModifiers& rhsMod = rhs.mDrawModifiers; if (lhsMod.mShader != rhsMod.mShader) return false; @@ -195,13 +227,28 @@ public: return true; } + virtual void add(DrawOp* op, bool opaqueOverBounds) { + DrawBatch::add(op, opaqueOverBounds); + + const int newClipSideFlags = op->state.mClipSideFlags; + mClipSideFlags |= newClipSideFlags; + if (newClipSideFlags & kClipSide_Left) mClipRect.left = op->state.mClip.left; + if (newClipSideFlags & kClipSide_Top) mClipRect.top = op->state.mClip.top; + if (newClipSideFlags & kClipSide_Right) mClipRect.right = op->state.mClip.right; + if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = op->state.mClip.bottom; + } + virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { - DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops (batch id %x, merge id %p)", - index, this, mOps.size(), getBatchId(), getMergeId()); + DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops," + " clip flags %x (batch id %x, merge id %p)", + index, this, mOps.size(), mClipSideFlags, getBatchId(), getMergeId()); if (mOps.size() == 1) { - return DrawBatch::replay(renderer, dirty, 0); + return DrawBatch::replay(renderer, dirty, -1); } + // clipping in the merged case is done ahead of time since all ops share the clip (if any) + renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : NULL); + DrawOp* op = mOps[0]; DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance(); buffer.writeCommand(0, "multiDraw"); @@ -214,6 +261,15 @@ public: #endif return status; } + +private: + /* + * Contains the effective clip rect shared by all merged ops. Initialized to the layer viewport, + * it will shrink if an op must be clipped on a certain side. The clipped sides are reflected in + * mClipSideFlags. + */ + Rect mClipRect; + int mClipSideFlags; }; class StateOpBatch : public Batch { @@ -297,6 +353,7 @@ void DeferredDisplayList::clear() { mBatches.clear(); mSaveStack.clear(); mEarliestBatchIndex = 0; + mEarliestUnclearedIndex = 0; } ///////////////////////////////////////////////////////////////////////////////// @@ -405,18 +462,22 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { return; // quick rejected } - int batchId = kOpBatch_None; - mergeid_t mergeId = (mergeid_t) -1; - bool mergeable = op->onDefer(renderer, &batchId, &mergeId); + DeferInfo deferInfo; + op->onDefer(renderer, deferInfo); // complex clip has a complex set of expectations on the renderer state - for now, avoid taking // the merge path in those cases - mergeable &= !recordingComplexClip(); + deferInfo.mergeable &= !recordingComplexClip(); + + if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() && + deferInfo.opaqueOverBounds && op->state.mBounds.contains(mBounds)) { + discardDrawingBatches(mBatches.size() - 1); + } if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) { // TODO: elegant way to reuse batches? - DrawBatch* b = new DrawBatch(batchId, mergeId); - b->add(op); + DrawBatch* b = new DrawBatch(deferInfo); + b->add(op, deferInfo.opaqueOverBounds); mBatches.add(b); return; } @@ -430,8 +491,8 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (!mBatches.isEmpty()) { if (op->state.mBounds.isEmpty()) { // don't know the bounds for op, so add to last batch and start from scratch on next op - DrawBatch* b = new DrawBatch(batchId, mergeId); - b->add(op); + DrawBatch* b = new DrawBatch(deferInfo); + b->add(op, deferInfo.opaqueOverBounds); mBatches.add(b); resetBatchingState(); #if DEBUG_DEFER @@ -441,19 +502,19 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { return; } - if (mergeable) { + if (deferInfo.mergeable) { // Try to merge with any existing batch with same mergeId. - if (mMergingBatches[batchId].get(mergeId, targetBatch)) { + if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) { if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op)) { targetBatch = NULL; } } } else { // join with similar, non-merging batch - targetBatch = (DrawBatch*)mBatchLookup[batchId]; + targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId]; } - if (targetBatch || mergeable) { + if (targetBatch || deferInfo.mergeable) { // iterate back toward target to see if anything drawn since should overlap the new op // if no target, merging ops still interate to find similar batch to insert after for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) { @@ -462,7 +523,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (overBatch == targetBatch) break; // TODO: also consider shader shared between batch types - if (batchId == overBatch->getBatchId()) { + if (deferInfo.batchId == overBatch->getBatchId()) { insertBatchIndex = i + 1; if (!targetBatch) break; // found insert position, quit } @@ -484,20 +545,20 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { } if (!targetBatch) { - if (mergeable) { - targetBatch = new MergingDrawBatch(batchId, mergeId); - mMergingBatches[batchId].put(mergeId, targetBatch); + if (deferInfo.mergeable) { + targetBatch = new MergingDrawBatch(deferInfo, mBounds); + mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch); } else { - targetBatch = new DrawBatch(batchId, mergeId); - mBatchLookup[batchId] = targetBatch; + targetBatch = new DrawBatch(deferInfo); + mBatchLookup[deferInfo.batchId] = targetBatch; DEFER_LOGD("creating Batch %p, bid %x, at %d", - targetBatch, batchId, insertBatchIndex); + targetBatch, deferInfo.batchId, insertBatchIndex); } mBatches.insertAt(targetBatch, insertBatchIndex); } - targetBatch->add(op); + targetBatch->add(op, deferInfo.opaqueOverBounds); } void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) { @@ -529,7 +590,9 @@ static status_t replayBatchList(const Vector<Batch*>& batchList, status_t status = DrawGlInfo::kStatusDone; for (unsigned int i = 0; i < batchList.size(); i++) { - status |= batchList[i]->replay(renderer, dirty, i); + if (batchList[i]) { + status |= batchList[i]->replay(renderer, dirty, i); + } } DEFER_LOGD("--flushed, drew %d batches", batchList.size()); return status; @@ -551,6 +614,13 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers(); renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + if (CC_LIKELY(mAvoidOverdraw)) { + for (unsigned int i = 1; i < mBatches.size(); i++) { + if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) { + discardDrawingBatches(i - 1); + } + } + } // NOTE: depth of the save stack at this point, before playback, should be reflected in // FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly status |= replayBatchList(mBatches, renderer, dirty); @@ -563,5 +633,16 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { return status; } +void DeferredDisplayList::discardDrawingBatches(unsigned int maxIndex) { + for (unsigned int i = mEarliestUnclearedIndex; i <= maxIndex; i++) { + if (mBatches[i] && mBatches[i]->purelyDrawBatch()) { + DrawBatch* b = (DrawBatch*) mBatches[i]; + delete mBatches[i]; + mBatches.replaceAt(NULL, i); + } + } + mEarliestUnclearedIndex = maxIndex + 1; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 9782c1c51794..7aa16726bd42 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -44,8 +44,12 @@ typedef void* mergeid_t; class DeferredDisplayList { public: - DeferredDisplayList() { clear(); } + DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : + mBounds(bounds), mAvoidOverdraw(avoidOverdraw) { + clear(); + } ~DeferredDisplayList() { clear(); } + void reset(const Rect& bounds) { mBounds.set(bounds); } enum OpBatchId { kOpBatch_None = 0, // Don't batch @@ -96,6 +100,12 @@ private: int getStateOpDeferFlags() const; int getDrawOpDeferFlags() const; + void discardDrawingBatches(unsigned int maxIndex); + + // layer space bounds of rendering + Rect mBounds; + const bool mAvoidOverdraw; + /** * At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so * that when an associated restoreToCount is deferred, it can be recorded as a @@ -112,6 +122,9 @@ private: // Points to the index after the most recent barrier int mEarliestBatchIndex; + // Points to the first index that may contain a pure drawing batch + int mEarliestUnclearedIndex; + /** * 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 @@ -120,6 +133,24 @@ private: TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count]; }; +/** + * Struct containing information that instructs the defer + */ +struct DeferInfo { +public: + DeferInfo() : + batchId(DeferredDisplayList::kOpBatch_None), + mergeId((mergeid_t) -1), + mergeable(false), + opaqueOverBounds(false) { + }; + + int batchId; + mergeid_t mergeId; + bool mergeable; + bool opaqueOverBounds; // opaque over bounds in DeferredDisplayState - can skip ops below +}; + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 028decdbfbd6..470cddacac36 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -37,17 +37,8 @@ ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \ } while(false) -#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]" -#define MATRIX_ARGS(m) \ - m->get(0), m->get(1), m->get(2), \ - m->get(3), m->get(4), m->get(5), \ - m->get(6), m->get(7), m->get(8) -#define RECT_STRING "%.2f %.2f %.2f %.2f" -#define RECT_ARGS(r) \ - r.left, r.top, r.right, r.bottom - // Use OP_LOG for logging with arglist, OP_LOGS if just printing char* -#define OP_LOGS(s) OP_LOG("%s", s) +#define OP_LOGS(s) OP_LOG("%s", (s)) #define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ ) namespace android { @@ -168,13 +159,13 @@ public: const Vector<DrawOp*>& ops, const Rect& bounds) { status_t status = DrawGlInfo::kStatusDone; for (unsigned int i = 0; i < ops.size(); i++) { - renderer.restoreDisplayState(ops[i]->state); + renderer.restoreDisplayState(ops[i]->state, true); status |= ops[i]->applyDraw(renderer, dirty); } return status; } - /* + /** * When this method is invoked the state field is initialized to have the * final rendering state. We can thus use it to process data as it will be * used at draw time. @@ -182,12 +173,9 @@ public: * Additionally, this method allows subclasses to provide defer-time preferences for batching * and merging. * - * Return true if the op can merge with others of its kind (such subclasses should implement - * multiDraw) + * if a subclass can set deferInfo.mergeable to true, it should implement multiDraw() */ - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - return false; - } + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {} // returns true if bounds exist virtual bool getLocalBounds(Rect& localBounds) { return false; } @@ -211,6 +199,23 @@ protected: return renderer.filterPaint(mPaint); } + // Helper method for determining op opaqueness. Assumes op fills its bounds in local + // coordinates, and that paint's alpha is used + inline bool isOpaqueOverBounds() { + // ensure that local bounds cover mapped bounds + if (!state.mMatrix.isSimple()) return false; + + // check state/paint for transparency + if (state.mDrawModifiers.mShader || + state.mAlpha != 1.0f || + (mPaint && mPaint->getAlpha() != 0xFF)) return false; + + SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint); + return (mode == SkXfermode::kSrcOver_Mode || + mode == SkXfermode::kSrc_Mode); + + } + SkPaint* mPaint; // should be accessed via getPaint() when applying bool mQuickRejected; }; @@ -245,16 +250,6 @@ public: return true; } - bool mergeAllowed() { - if (!state.mMatrix.isPureTranslate()) return false; - - // checks that we're unclipped, and srcover - const Rect& opBounds = state.mBounds; - return fabs(opBounds.getWidth() - mLocalBounds.getWidth()) < 0.1 && - fabs(opBounds.getHeight() - mLocalBounds.getHeight()) < 0.1 && - (OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode); - } - protected: Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint }; @@ -756,7 +751,6 @@ public: virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, const Vector<DrawOp*>& ops, const Rect& bounds) { renderer.restoreDisplayState(state, true); // restore all but the clip - renderer.setFullScreenClip(); // ensure merged ops aren't clipped TextureVertex vertices[6 * ops.size()]; TextureVertex* vertex = &vertices[0]; @@ -793,18 +787,16 @@ public: virtual const char* name() { return "DrawBitmap"; } - bool bitmapMergeAllowed() { - return state.mMatrix.isSimple() && !state.mClipped && - OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; - } + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; + deferInfo.mergeId = mAtlasEntry ? (mergeid_t) &mAtlasEntry->atlas : (mergeid_t) mBitmap; - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = DeferredDisplayList::kOpBatch_Bitmap; - *mergeId = mAtlasEntry ? (mergeid_t) &mAtlasEntry->atlas : (mergeid_t) mBitmap; - - // don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in - // MergingDrawBatch::canMergeWith - return bitmapMergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config); + // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in + // MergingDrawBatch::canMergeWith() + // TODO: support clipped bitmaps by handling them in SET_TEXTURE + deferInfo.mergeable = state.mMatrix.isSimple() && !state.mClipSideFlags && + OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode && + (mBitmap->getConfig() != SkBitmap::kA8_Config); } const SkBitmap* bitmap() { return mBitmap; } @@ -833,9 +825,8 @@ public: virtual const char* name() { return "DrawBitmapMatrix"; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = DeferredDisplayList::kOpBatch_Bitmap; - return false; + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } private: @@ -863,9 +854,8 @@ public: virtual const char* name() { return "DrawBitmapRect"; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = DeferredDisplayList::kOpBatch_Bitmap; - return false; + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } private: @@ -889,9 +879,8 @@ public: virtual const char* name() { return "DrawBitmapData"; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = DeferredDisplayList::kOpBatch_Bitmap; - return false; + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } }; @@ -914,9 +903,8 @@ public: virtual const char* name() { return "DrawBitmapMesh"; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = DeferredDisplayList::kOpBatch_Bitmap; - return false; + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } private: @@ -957,10 +945,12 @@ public: virtual const char* name() { return "DrawPatch"; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = DeferredDisplayList::kOpBatch_Patch; - *mergeId = (mergeid_t) mBitmap; - return true; + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch; + deferInfo.mergeId = (mergeid_t) mBitmap; + deferInfo.mergeable = true; + deferInfo.opaqueOverBounds = isOpaqueOverBounds() && + mBitmap->isOpaque(); } private: @@ -1008,15 +998,14 @@ public: return true; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { if (mPaint->getPathEffect()) { - *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; + deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; } else { - *batchId = mPaint->isAntiAlias() ? + deferInfo.batchId = mPaint->isAntiAlias() ? DeferredDisplayList::kOpBatch_AlphaVertices : DeferredDisplayList::kOpBatch_Vertices; } - return false; } }; @@ -1034,6 +1023,12 @@ public: OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds)); } + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + DrawStrokableOp::onDefer(renderer, deferInfo); + deferInfo.opaqueOverBounds = isOpaqueOverBounds() && + mPaint->getStyle() == SkPaint::kFill_Style; + } + virtual const char* name() { return "DrawRect"; } }; @@ -1053,9 +1048,8 @@ public: virtual const char* name() { return "DrawRects"; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = DeferredDisplayList::kOpBatch_Vertices; - return false; + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices; } private: @@ -1166,12 +1160,11 @@ public: return renderer.drawPath(mPath, getPaint(renderer)); } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { SkPaint* paint = getPaint(renderer); renderer.getCaches().pathCache.precache(mPath, paint); - *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; - return false; + deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; } virtual void output(int level, uint32_t logFlags) { @@ -1202,11 +1195,10 @@ public: virtual const char* name() { return "DrawLines"; } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { - *batchId = mPaint->isAntiAlias() ? + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { + deferInfo.batchId = mPaint->isAntiAlias() ? DeferredDisplayList::kOpBatch_AlphaVertices : DeferredDisplayList::kOpBatch_Vertices; - return false; } protected: @@ -1239,16 +1231,14 @@ public: OP_LOG("Draw some text, %d bytes", mBytesCount); } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); fontRenderer.precache(paint, mText, mCount, mat4::identity()); - *batchId = mPaint->getColor() == 0xff000000 ? + deferInfo.batchId = mPaint->getColor() == 0xff000000 ? DeferredDisplayList::kOpBatch_Text : DeferredDisplayList::kOpBatch_ColorText; - - return false; } protected: @@ -1307,7 +1297,7 @@ public: memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float)); } - virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); const mat4& transform = renderer.findBestFontTransform(state.mMatrix); @@ -1315,16 +1305,17 @@ public: fontRenderer.precache(paint, mText, mCount, transform); mPrecacheTransform = transform; } - *batchId = mPaint->getColor() == 0xff000000 ? + deferInfo.batchId = mPaint->getColor() == 0xff000000 ? DeferredDisplayList::kOpBatch_Text : DeferredDisplayList::kOpBatch_ColorText; - *mergeId = (mergeid_t)mPaint->getColor(); + deferInfo.mergeId = (mergeid_t)mPaint->getColor(); // don't merge decorated text - the decorations won't draw in order bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)); - return mergeAllowed() && noDecorations; + deferInfo.mergeable = state.mMatrix.isPureTranslate() && noDecorations && + OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; } virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1335,7 +1326,6 @@ public: virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, const Vector<DrawOp*>& ops, const Rect& bounds) { status_t status = DrawGlInfo::kStatusDone; - renderer.setFullScreenClip(); // ensure merged ops aren't clipped for (unsigned int i = 0; i < ops.size(); i++) { DrawOpMode drawOpMode = (i == ops.size() - 1) ? kDrawOpMode_Flush : kDrawOpMode_Defer; renderer.restoreDisplayState(ops[i]->state, true); // restore all but the clip diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 134f452aa4d1..7c22bbb6f973 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -188,12 +188,6 @@ void Layer::allocateTexture() { } void Layer::defer() { - if (!deferredList) { - deferredList = new DeferredDisplayList; - } - DeferStateStruct deferredState(*deferredList, *renderer, - DisplayList::kReplayFlag_ClipChildren); - const float width = layer.getWidth(); const float height = layer.getHeight(); @@ -202,6 +196,14 @@ void Layer::defer() { dirtyRect.set(0, 0, width, height); } + if (deferredList) { + deferredList->reset(dirtyRect); + } else { + deferredList = new DeferredDisplayList(dirtyRect); + } + DeferStateStruct deferredState(*deferredList, *renderer, + DisplayList::kReplayFlag_ClipChildren); + renderer->initViewport(width, height); renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index df744bef6aca..af520bdc33e7 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -26,6 +26,12 @@ namespace android { namespace uirenderer { +#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]" +#define MATRIX_ARGS(m) \ + (m)->get(0), (m)->get(1), (m)->get(2), \ + (m)->get(3), (m)->get(4), (m)->get(5), \ + (m)->get(6), (m)->get(7), (m)->get(8) + /////////////////////////////////////////////////////////////////////////////// // Classes /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index d95a62c19d39..5ac06cf04113 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1360,15 +1360,26 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef // state has bounds initialized in local coordinates if (!state.mBounds.isEmpty()) { currentMatrix.mapRect(state.mBounds); - state.mClipped = !currentClip.contains(state.mBounds); - if (!state.mBounds.intersect(currentClip)) { + Rect clippedBounds(state.mBounds); + if(!clippedBounds.intersect(currentClip)) { // quick rejected return true; } + + state.mClipSideFlags = kClipSide_Unclipped; + if (!currentClip.contains(state.mBounds)) { + int& flags = state.mClipSideFlags; + // op partially clipped, so record which sides are clipped for clip-aware merging + if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left; + if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top; + if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right; + if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom; + } + state.mBounds.set(clippedBounds); } else { // If we don't have bounds, let's assume we're clipped // to prevent merging - state.mClipped = true; + state.mClipSideFlags = kClipSide_Full; state.mBounds.set(currentClip); } } @@ -1392,14 +1403,27 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool mSnapshot->alpha = state.mAlpha; if (state.mClipValid && !skipClipRestore) { - mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom); + mSnapshot->setClip(state.mClip.left, state.mClip.top, + state.mClip.right, state.mClip.bottom); dirtyClip(); } } -void OpenGLRenderer::setFullScreenClip() { - mSnapshot->setClip(0, 0, mWidth, mHeight); +/** + * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done + * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at + * least one op is clipped), or disabled entirely (because no merged op is clipped) + * + * This method should be called when restoreDisplayState() won't be restoring the clip + */ +void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) { + if (clipRect != NULL) { + mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); + } else { + mSnapshot->setClip(0, 0, mWidth, mHeight); + } dirtyClip(); + mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled); } /////////////////////////////////////////////////////////////////////////////// @@ -1965,7 +1989,8 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, return status | replayStruct.mDrawGlStatus; } - DeferredDisplayList deferredList; + bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs! + DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); displayList->defer(deferStruct, 0); @@ -2016,10 +2041,6 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices, bool transformed, const Rect& bounds, SkPaint* paint) { - - // merged draw operations don't need scissor, but clip should still be valid - mCaches.setScissorEnabled(mScissorOptimizationDisabled); - mCaches.activeTexture(0); Texture* texture = getTexture(bitmap); if (!texture) return DrawGlInfo::kStatusDone; @@ -2860,16 +2881,13 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { - if (drawOpMode == kDrawOpMode_Immediate && - (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint))) { - return DrawGlInfo::kStatusDone; - } - if (drawOpMode == kDrawOpMode_Immediate) { - if (quickReject(bounds)) return DrawGlInfo::kStatusDone; - } else { - // merged draw operations don't need scissor, but clip should still be valid - mCaches.setScissorEnabled(mScissorOptimizationDisabled); + // The checks for corner-case ignorable text and quick rejection is only done for immediate + // drawing as ops from DeferredDisplayList are already filtered for these + if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) || + quickReject(bounds)) { + return DrawGlInfo::kStatusDone; + } } const float oldX = x; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index ce4ce42bb8f8..78469c45860f 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -80,6 +80,15 @@ enum DrawOpMode { kDrawOpMode_Flush }; +enum ClipSideFlags { + kClipSide_Unclipped = 0x0, + kClipSide_Left = 0x1, + kClipSide_Top = 0x2, + kClipSide_Right = 0x4, + kClipSide_Bottom = 0x8, + kClipSide_Full = 0xF +}; + struct DeferredDisplayState { // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped Rect mBounds; @@ -87,6 +96,7 @@ struct DeferredDisplayState { // the below are set and used by the OpenGLRenderer at record and deferred playback bool mClipValid; Rect mClip; + int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared bool mClipped; mat4 mMatrix; DrawModifiers mDrawModifiers; @@ -307,7 +317,7 @@ public: bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags); void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false); - void setFullScreenClip(); + void setupMergedMultiDraw(const Rect* clipRect); const DrawModifiers& getDrawModifiers() { return mDrawModifiers; } void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; } diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 689fe6c041b6..87c6c1051fc8 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -24,6 +24,10 @@ namespace android { namespace uirenderer { +#define RECT_STRING "%4.2f %4.2f %4.2f %4.2f" +#define RECT_ARGS(r) \ + (r).left, (r).top, (r).right, (r).bottom + /////////////////////////////////////////////////////////////////////////////// // Structs /////////////////////////////////////////////////////////////////////////////// |