summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Craik <ccraik@google.com>2013-05-31 11:38:03 -0700
committerChris Craik <ccraik@google.com>2013-06-12 14:46:52 -0700
commit28ce94a4ffc7576f40776d212f1ada79fafaa061 (patch)
tree644d590fb3ccb5db34ecf68bb3360923f3d65c29
parent97f41383eb2bb098767ca153e470009fea810540 (diff)
downloadframeworks_base-28ce94a4ffc7576f40776d212f1ada79fafaa061.zip
frameworks_base-28ce94a4ffc7576f40776d212f1ada79fafaa061.tar.gz
frameworks_base-28ce94a4ffc7576f40776d212f1ada79fafaa061.tar.bz2
Overdraw avoidance and merging of clipped ops
bug:8951267 If an opaque op, or group of opaque ops covers the invalidate region, skip draw operations that precede it. Clipped operations may now be merged, but only if they share a clipRect - this is a very case for e.g. ListView, where all background elements may now be a part of the same MergingDrawBatch. It is this more aggressive merging that groups together clipped background elements in the ListView case, enabling the overdraw avoidance skipping the window background. Change-Id: Ib0961977e272c5ac37f59e4c67d828467422d259
-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 512d3b1..6bb54a7 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 9782c1c..7aa1672 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 028decd..470cdda 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 134f452..7c22bbb 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 df744be..af520bd 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 d95a62c..5ac06cf 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 ce4ce42..78469c4 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 689fe6c0..87c6c10 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
///////////////////////////////////////////////////////////////////////////////