summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/hwui/DeferredDisplayList.cpp151
-rw-r--r--libs/hwui/DeferredDisplayList.h33
-rw-r--r--libs/hwui/DisplayListOp.h144
-rw-r--r--libs/hwui/Layer.cpp14
-rw-r--r--libs/hwui/Matrix.h6
-rw-r--r--libs/hwui/OpenGLRenderer.cpp58
-rw-r--r--libs/hwui/OpenGLRenderer.h12
-rw-r--r--libs/hwui/Rect.h4
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
///////////////////////////////////////////////////////////////////////////////