am 30c990c3: Merge "Draw Operation merging" into jb-mr2-dev

* commit '30c990c361291ad578ef4ffe4a4dd0fd6080797b':
  Draw Operation merging
This commit is contained in:
Chris Craik
2013-04-15 14:31:02 -07:00
committed by Android Git Automerger
12 changed files with 626 additions and 175 deletions

View File

@ -84,6 +84,9 @@
// Turn on to insert an event marker for each display list op
#define DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 0
// Turn on to highlight drawing batches and merged batches with different colors
#define DEBUG_MERGE_BEHAVIOR 0
#if DEBUG_INIT
#define INIT_LOGD(...) ALOGD(__VA_ARGS__)
#else

View File

@ -23,6 +23,7 @@
#include "Caches.h"
#include "Debug.h"
#include "DeferredDisplayList.h"
#include "DisplayListOp.h"
#include "OpenGLRenderer.h"
@ -38,15 +39,27 @@ namespace uirenderer {
// Depth of the save stack at the beginning of batch playback at flush time
#define FLUSH_SAVE_STACK_DEPTH 2
#define DEBUG_COLOR_BARRIER 0x1f000000
#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff
#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
/////////////////////////////////////////////////////////////////////////////////
// Operation Batches
/////////////////////////////////////////////////////////////////////////////////
class DrawOpBatch {
class Batch {
public:
DrawOpBatch() { mOps.clear(); }
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
virtual ~Batch() {}
};
virtual ~DrawOpBatch() { mOps.clear(); }
class DrawBatch : public Batch {
public:
DrawBatch(int batchId, mergeid_t mergeId) : mBatchId(batchId), mMergeId(mergeId) {
mOps.clear();
}
virtual ~DrawBatch() { mOps.clear(); }
void add(DrawOp* op) {
// NOTE: ignore empty bounds special case, since we don't merge across those ops
@ -54,7 +67,7 @@ public:
mOps.add(op);
}
virtual bool intersects(Rect& rect) {
bool intersects(Rect& rect) {
if (!rect.intersects(mBounds)) return false;
for (unsigned int i = 0; i < mOps.size(); i++) {
@ -71,8 +84,9 @@ public:
return false;
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
DEFER_LOGD("replaying draw batch %p", this);
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
index, this, mOps.size(), mOps[0]->getBatchId(), mOps[0]->getMergeId());
status_t status = DrawGlInfo::kStatusDone;
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
@ -84,31 +98,127 @@ public:
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
renderer.eventMark(op->name());
#endif
status |= op->applyDraw(renderer, dirty, 0);
status |= op->applyDraw(renderer, dirty);
logBuffer.writeCommand(0, op->name());
#if DEBUG_MERGE_BEHAVIOR
Rect& bounds = mOps[i]->state.mBounds;
int batchColor = 0x1f000000;
if (getBatchId() & 0x1) batchColor |= 0x0000ff;
if (getBatchId() & 0x2) batchColor |= 0x00ff00;
if (getBatchId() & 0x4) batchColor |= 0xff0000;
renderer.drawScreenSpaceColorRect(bounds.left, bounds.top, bounds.right, bounds.bottom,
batchColor);
#endif
}
return status;
}
inline int getBatchId() const { return mBatchId; }
inline mergeid_t getMergeId() const { return mMergeId; }
inline int count() const { return mOps.size(); }
private:
protected:
Vector<DrawOp*> mOps;
Rect mBounds;
private:
int mBatchId;
mergeid_t mMergeId;
};
class StateOpBatch : public DrawOpBatch {
// compare alphas approximately, with a small margin
#define NEQ_FALPHA(lhs, rhs) \
fabs((float)lhs - (float)rhs) > 0.001f
class MergingDrawBatch : public DrawBatch {
public:
MergingDrawBatch(int batchId, mergeid_t mergeId) : DrawBatch(batchId, mergeId) {}
/*
* Checks if a (mergeable) op can be merged into this batch
*
* If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
* important to consider all paint attributes used in the draw calls in deciding both a) if an
* op tries to merge at all, and b) if the op
*
* False positives can lead to information from the paints of subsequent merged operations being
* dropped, so we make simplifying qualifications on the ops that can merge, per op type.
*/
bool canMergeWith(DrawOp* op) {
if (!op->state.mMatrix.isPureTranslate()) return false;
bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
// Overlapping other operations is only allowed for text without shadow. For other ops,
// multiDraw isn't guaranteed to overdraw correctly
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 paints are equal, then modifiers + paint attribs don't need to be compared
if (op->mPaint == mOps[0]->mPaint) return true;
if (op->getPaintAlpha() != mOps[0]->getPaintAlpha()) return false;
/* Draw Modifiers compatibility check
*
* Shadows are ignored, as only text uses them, and in that case they are drawn
* per-DrawTextOp, before the unified text draw. Because of this, it's always safe to merge
* text UNLESS a later draw's shadow should overlays a previous draw's text. This is covered
* above with the intersection check.
*
* OverrideLayerAlpha is also ignored, as it's only used for drawing layers, which are never
* merged.
*
* 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;
if (lhsMod.mColorFilter != rhsMod.mColorFilter) return false;
// Draw filter testing expects bit fields to be clear if filter not set.
if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
if (lhsMod.mPaintFilterClearBits != rhsMod.mPaintFilterClearBits) return false;
if (lhsMod.mPaintFilterSetBits != rhsMod.mPaintFilterSetBits) return false;
return true;
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
index, this, mOps.size(), getBatchId(), getMergeId());
if (mOps.size() == 1) {
return DrawBatch::replay(renderer, dirty, false);
}
DrawOp* op = mOps[0];
status_t status = op->multiDraw(renderer, dirty, mOps, mBounds);
DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance();
buffer.writeCommand(0, "multiDraw");
buffer.writeCommand(1, op->name());
#if DEBUG_MERGE_BEHAVIOR
renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom,
DEBUG_COLOR_MERGEDBATCH);
#endif
return status;
}
};
class StateOpBatch : public Batch {
public:
// creates a single operation batch
StateOpBatch(StateOp* op) : mOp(op) {}
bool intersects(Rect& rect) {
// if something checks for intersection, it's trying to go backwards across a state op,
// something not currently supported - state ops are always barriers
CRASH();
return false;
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("replaying state op batch %p", this);
renderer.restoreDisplayState(mOp->state);
@ -124,18 +234,11 @@ private:
const StateOp* mOp;
};
class RestoreToCountBatch : public DrawOpBatch {
class RestoreToCountBatch : public Batch {
public:
RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {}
bool intersects(Rect& rect) {
// if something checks for intersection, it's trying to go backwards across a state op,
// something not currently supported - state ops are always barriers
CRASH();
return false;
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
renderer.restoreDisplayState(mOp->state);
@ -155,14 +258,30 @@ private:
const int mRestoreCount;
};
#if DEBUG_MERGE_BEHAVIOR
class BarrierDebugBatch : public Batch {
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER);
return DrawGlInfo::kStatusDrew;
}
};
#endif
/////////////////////////////////////////////////////////////////////////////////
// DeferredDisplayList
/////////////////////////////////////////////////////////////////////////////////
void DeferredDisplayList::resetBatchingState() {
for (int i = 0; i < kOpBatch_Count; i++) {
mBatchIndices[i] = -1;
mBatchLookup[i] = NULL;
mMergingBatches[i].clear();
}
#if DEBUG_MERGE_BEHAVIOR
if (mBatches.size() != 0) {
mBatches.add(new BarrierDebugBatch());
}
#endif
mEarliestBatchIndex = mBatches.size();
}
void DeferredDisplayList::clear() {
@ -174,6 +293,7 @@ void DeferredDisplayList::clear() {
}
mBatches.clear();
mSaveStack.clear();
mEarliestBatchIndex = 0;
}
/////////////////////////////////////////////////////////////////////////////////
@ -282,28 +402,35 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
return; // quick rejected
}
op->onDrawOpDeferred(renderer);
int batchId = kOpBatch_None;
mergeid_t mergeId = (mergeid_t) -1;
bool mergeable = op->onDefer(renderer, &batchId, &mergeId);
// complex clip has a complex set of expectations on the renderer state - for now, avoid taking
// the merge path in those cases
mergeable &= !recordingComplexClip();
if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
// TODO: elegant way to reuse batches?
DrawOpBatch* b = new DrawOpBatch();
DrawBatch* b = new DrawBatch(batchId, mergeId);
b->add(op);
mBatches.add(b);
return;
}
// disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge
// the new op into it
DrawOpBatch* targetBatch = NULL;
int batchId = op->getBatchId();
// find the latest batch of the new op's type, and try to merge the new op into it
DrawBatch* targetBatch = NULL;
// insertion point of a new batch, will hopefully be immediately after similar batch
// (eventually, should be similar shader)
int insertBatchIndex = mBatches.size();
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
mBatches.top()->add(op);
for (int i = 0; i < kOpBatch_Count; i++) {
mBatchIndices[i] = -1;
}
DrawBatch* b = new DrawBatch(batchId, mergeId);
b->add(op);
mBatches.add(b);
resetBatchingState();
#if DEBUG_DEFER
DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
op->output(2);
@ -311,13 +438,36 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
return;
}
if (batchId >= 0 && mBatchIndices[batchId] != -1) {
int targetIndex = mBatchIndices[batchId];
targetBatch = mBatches[targetIndex];
if (mergeable) {
// Try to merge with any existing batch with same mergeId.
if (mMergingBatches[batchId].get(mergeId, targetBatch)) {
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op)) {
targetBatch = NULL;
}
}
} else {
// join with similar, non-merging batch
targetBatch = (DrawBatch*)mBatchLookup[batchId];
}
if (targetBatch || mergeable) {
// iterate back toward target to see if anything drawn since should overlap the new op
for (int i = mBatches.size() - 1; i > targetIndex; i--) {
DrawOpBatch* overBatch = mBatches[i];
// if no target, merging ops still interate to find similar batch to insert after
for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
DrawBatch* overBatch = (DrawBatch*)mBatches[i];
if (overBatch == targetBatch) break;
// TODO: also consider shader shared between batch types
if (batchId == overBatch->getBatchId()) {
insertBatchIndex = i + 1;
if (!targetBatch) break; // found insert position, quit
}
if (overBatch->intersects(op->state.mBounds)) {
// NOTE: it may be possible to optimize for special cases where two operations
// of the same batch/paint could swap order, such as with a non-mergeable
// (clipped) and a mergeable text operation
targetBatch = NULL;
#if DEBUG_DEFER
DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
@ -329,13 +479,21 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
}
}
}
if (!targetBatch) {
targetBatch = new DrawOpBatch();
mBatches.add(targetBatch);
if (batchId >= 0) {
mBatchIndices[batchId] = mBatches.size() - 1;
if (mergeable) {
targetBatch = new MergingDrawBatch(batchId, mergeId);
mMergingBatches[batchId].put(mergeId, targetBatch);
} else {
targetBatch = new DrawBatch(batchId, mergeId);
mBatchLookup[batchId] = targetBatch;
DEFER_LOGD("creating Batch %p, bid %x, at %d",
targetBatch, batchId, insertBatchIndex);
}
mBatches.insertAt(targetBatch, insertBatchIndex);
}
targetBatch->add(op);
}
@ -363,16 +521,14 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S
// Replay / flush
/////////////////////////////////////////////////////////////////////////////////
static status_t replayBatchList(Vector<DrawOpBatch*>& batchList,
static status_t replayBatchList(const Vector<Batch*>& batchList,
OpenGLRenderer& renderer, Rect& dirty) {
status_t status = DrawGlInfo::kStatusDone;
int opCount = 0;
for (unsigned int i = 0; i < batchList.size(); i++) {
status |= batchList[i]->replay(renderer, dirty);
opCount += batchList[i]->count();
status |= batchList[i]->replay(renderer, dirty, i);
}
DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount);
DEFER_LOGD("--flushed, drew %d batches", batchList.size());
return status;
}
@ -400,7 +556,6 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
renderer.setDrawModifiers(restoreDrawModifiers);
DEFER_LOGD("--flush complete, returning %x", status);
clear();
return status;
}

View File

@ -22,6 +22,9 @@
#include "Matrix.h"
#include "Rect.h"
#include "utils/TinyHashMap.h"
class SkBitmap;
namespace android {
namespace uirenderer {
@ -31,16 +34,21 @@ class DrawOp;
class SaveOp;
class SaveLayerOp;
class StateOp;
class DrawOpBatch;
class OpenGLRenderer;
class Batch;
class DrawBatch;
class MergingDrawBatch;
typedef void* mergeid_t;
class DeferredDisplayList {
public:
DeferredDisplayList() { clear(); }
~DeferredDisplayList() { clear(); }
enum OpBatchId {
kOpBatch_None = -1, // Don't batch
kOpBatch_None = 0, // Don't batch
kOpBatch_Bitmap,
kOpBatch_Patch,
kOpBatch_AlphaVertices,
@ -96,8 +104,20 @@ private:
Vector<int> mSaveStack;
int mComplexClipStackStart;
Vector<DrawOpBatch*> mBatches;
int mBatchIndices[kOpBatch_Count];
Vector<Batch*> mBatches;
// Maps batch ids to the most recent *non-merging* batch of that id
Batch* mBatchLookup[kOpBatch_Count];
// Points to the index after the most recent barrier
int mEarliestBatchIndex;
/**
* Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
* MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
* collide, which avoids the need to resolve mergeid collisions.
*/
TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
};
}; // namespace uirenderer

View File

@ -485,7 +485,7 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level)
#if DEBUG_DISPLAY_LIST
Rect* clipRect = renderer.getClipRect();
DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
clipRect->right, clipRect->bottom);
#endif

View File

@ -121,6 +121,7 @@ public:
};
class DrawOp : public DisplayListOp {
friend class MergingDrawBatch;
public:
DrawOp(SkPaint* paint)
: mPaint(paint), mQuickRejected(false) {}
@ -145,12 +146,41 @@ public:
return;
}
replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty, level);
replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty);
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) = 0;
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0;
virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
/**
* Draw multiple instances of an operation, must be overidden for operations that merge
*
* Currently guarantees certain similarities between ops (see MergingDrawBatch::canMergeWith),
* and pure translation transformations. Other guarantees of similarity should be enforced by
* reducing which operations are tagged as mergeable.
*/
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
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);
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.
*
* 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)
*/
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
return false;
}
// returns true if bounds exist
@ -160,12 +190,11 @@ public:
void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
bool getQuickRejected() { return mQuickRejected; }
/** Batching disabled by default, turned on for individual ops */
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_None;
inline int getPaintAlpha() {
return OpenGLRenderer::getAlphaDirect(mPaint);
}
float strokeWidthOutset() {
inline float strokeWidthOutset() {
float width = mPaint->getStrokeWidth();
if (width == 0) return 0.5f; // account for hairline
return width * 0.5f;
@ -207,6 +236,14 @@ public:
return true;
}
bool mergeAllowed() {
// 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
};
@ -686,20 +723,58 @@ public:
paint),
mBitmap(bitmap) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
getPaint(renderer));
}
#define SET_TEXTURE(ptr, posRect, offsetRect, texCoordsRect, xDim, yDim) \
TextureVertex::set(ptr++, posRect.xDim - offsetRect.left, posRect.yDim - offsetRect.top, \
texCoordsRect.xDim, texCoordsRect.yDim)
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];
// TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, and allowing
// them to be merged in getBatchId()
const Rect texCoords(0, 0, 1, 1);
const float width = mBitmap->width();
const float height = mBitmap->height();
for (unsigned int i = 0; i < ops.size(); i++) {
const Rect& opBounds = ops[i]->state.mBounds;
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
}
return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
}
virtual void output(int level, uint32_t logFlags) {
OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
}
virtual const char* name() { return "DrawBitmap"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_Bitmap;
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Bitmap;
*mergeId = (mergeid_t)mBitmap;
// don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
// MergingDrawBatch::canMergeWith
return mergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
}
const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
};
@ -713,7 +788,7 @@ public:
transform.mapRect(mLocalBounds);
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
}
@ -721,9 +796,11 @@ public:
OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
}
virtual const char* name() { return "DrawBitmap"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_Bitmap;
virtual const char* name() { return "DrawBitmapMatrix"; }
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Bitmap;
return false;
}
private:
@ -738,7 +815,7 @@ public:
: DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom,
mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
getPaint(renderer));
@ -750,8 +827,10 @@ public:
}
virtual const char* name() { return "DrawBitmapRect"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_Bitmap;
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Bitmap;
return false;
}
private:
@ -764,7 +843,7 @@ public:
DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
: DrawBitmapOp(bitmap, left, top, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmapData(mBitmap, mLocalBounds.left,
mLocalBounds.top, getPaint(renderer));
}
@ -774,8 +853,10 @@ public:
}
virtual const char* name() { return "DrawBitmapData"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_Bitmap;
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Bitmap;
return false;
}
};
@ -787,7 +868,7 @@ public:
mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
mVertices(vertices), mColors(colors) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
mVertices, mColors, getPaint(renderer));
}
@ -797,8 +878,10 @@ public:
}
virtual const char* name() { return "DrawBitmapMesh"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_Bitmap;
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Bitmap;
return false;
}
private:
@ -820,7 +903,7 @@ public:
mColors(colors), mxDivsCount(width), myDivsCount(height),
mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
// NOTE: not calling the virtual method, which takes a paint
return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
mxDivsCount, myDivsCount, mNumColors,
@ -833,8 +916,11 @@ public:
}
virtual const char* name() { return "DrawPatch"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_Patch;
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Patch;
*mergeId = (mergeid_t)mBitmap;
return true;
}
private:
@ -854,7 +940,7 @@ public:
DrawColorOp(int color, SkXfermode::Mode mode)
: DrawOp(0), mColor(color), mMode(mode) {};
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawColor(mColor, mMode);
}
@ -882,13 +968,15 @@ public:
return true;
}
virtual DeferredDisplayList::OpBatchId getBatchId() {
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
if (mPaint->getPathEffect()) {
return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
*batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
} else {
*batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
}
return mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
return false;
}
};
@ -897,7 +985,7 @@ public:
DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
@ -915,7 +1003,7 @@ public:
: DrawBoundedOp(rects, count, paint),
mRects(rects), mCount(count) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRects(mRects, mCount, getPaint(renderer));
}
@ -925,8 +1013,9 @@ public:
virtual const char* name() { return "DrawRects"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_Vertices;
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Vertices;
return false;
}
private:
@ -940,7 +1029,7 @@ public:
float rx, float ry, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
}
@ -962,7 +1051,7 @@ public:
: DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
mX(x), mY(y), mRadius(radius) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
}
@ -983,7 +1072,7 @@ public:
DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
@ -1002,7 +1091,7 @@ public:
: DrawStrokableOp(left, top, right, bottom, paint),
mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom,
mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
@ -1033,13 +1122,16 @@ public:
mLocalBounds.set(left, top, left + width, top + height);
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPath(mPath, getPaint(renderer));
}
virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
renderer.getCaches().pathCache.precache(mPath, paint);
*batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
return false;
}
virtual void output(int level, uint32_t logFlags) {
@ -1048,9 +1140,6 @@ public:
virtual const char* name() { return "DrawPath"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
}
private:
SkPath* mPath;
};
@ -1063,7 +1152,7 @@ public:
mLocalBounds.outset(strokeWidthOutset());
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawLines(mPoints, mCount, getPaint(renderer));
}
@ -1073,10 +1162,11 @@ public:
virtual const char* name() { return "DrawLines"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return mPaint->isAntiAlias() ?
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
return false;
}
protected:
@ -1089,7 +1179,7 @@ public:
DrawPointsOp(float* points, int count, SkPaint* paint)
: DrawLinesOp(points, count, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
}
@ -1109,17 +1199,18 @@ public:
OP_LOG("Draw some text, %d bytes", mBytesCount);
}
virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, mText, mCount, mat4::identity());
}
virtual DeferredDisplayList::OpBatchId getBatchId() {
return mPaint->getColor() == 0xff000000 ?
*batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
return false;
}
protected:
const char* mText;
int mBytesCount;
@ -1135,7 +1226,7 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
mHOffset, mVOffset, getPaint(renderer));
}
@ -1156,7 +1247,7 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer));
}
@ -1189,12 +1280,7 @@ public:
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
/*
* 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.
*/
virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
const mat4& transform = renderer.findBestFontTransform(state.mMatrix);
@ -1202,25 +1288,44 @@ public:
fontRenderer.precache(paint, mText, mCount, transform);
mPrecacheTransform = transform;
}
*batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
*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;
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
mPositions, getPaint(renderer), mLength);
}
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
DrawTextOp& op = *((DrawTextOp*)ops[i]);
status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
op.mPositions, op.getPaint(renderer), op.mLength, drawOpMode);
}
return status;
}
virtual void output(int level, uint32_t logFlags) {
OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
}
virtual const char* name() { return "DrawText"; }
virtual DeferredDisplayList::OpBatchId getBatchId() {
return mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
}
private:
const char* mText;
int mBytesCount;
@ -1241,7 +1346,7 @@ public:
DrawFunctorOp(Functor* functor)
: DrawOp(0), mFunctor(functor) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
renderer.startMark("GL functor");
status_t ret = renderer.callDrawGLFunction(mFunctor, dirty);
renderer.endMark();
@ -1269,14 +1374,14 @@ public:
mDisplayList->defer(deferStruct, level + 1);
}
}
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
if (mDisplayList && mDisplayList->isRenderable()) {
mDisplayList->replay(replayStruct, level + 1);
}
}
// NOT USED since replay() is overridden
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return DrawGlInfo::kStatusDone;
}
@ -1299,7 +1404,7 @@ public:
DrawLayerOp(Layer* layer, float x, float y)
: DrawOp(0), mLayer(layer), mX(x), mY(y) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawLayer(mLayer, mX, mY);
}

View File

@ -276,6 +276,15 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float
bitmap = refBitmap(bitmap);
paint = refPaint(paint);
if (srcLeft == 0 && srcTop == 0 &&
srcRight == bitmap->width() && srcBottom == bitmap->height() &&
(srcBottom - srcTop == dstBottom - dstTop) &&
(srcRight - srcLeft == dstRight - dstLeft)) {
// transform simple rect to rect drawing case into position bitmap ops, since they merge
addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint));
return DrawGlInfo::kStatusDone;
}
addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap,
srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, paint));
@ -413,7 +422,9 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
}
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint, float length) {
float x, float y, const float* positions, SkPaint* paint,
float length, DrawOpMode drawOpMode) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
if (length < 0.0f) length = paint->measureText(text, bytesCount);

View File

@ -121,8 +121,9 @@ public:
float hOffset, float vOffset, SkPaint* paint);
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint, float length);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, SkPaint* paint, float length, DrawOpMode drawOpMode);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();

View File

@ -57,7 +57,6 @@ FontRenderer::FontRenderer() :
mGammaTable = NULL;
mInitialized = false;
mMaxNumberOfQuads = 1024;
mCurrentCacheTexture = NULL;
@ -293,7 +292,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
}
CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
if (allocate) {
Caches::getInstance().activeTexture(0);
@ -320,12 +319,12 @@ void FontRenderer::initTextTexture() {
// Avoid having to reallocate memory and render quad by quad
void FontRenderer::initVertexArrayBuffers() {
uint32_t numIndices = mMaxNumberOfQuads * 6;
uint32_t numIndices = gMaxNumberOfQuads * 6;
uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
// Four verts, two triangles , six indices per quad
for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
int i6 = i * 6;
int i4 = i * 4;
@ -594,7 +593,7 @@ void FontRenderer::endPrecaching() {
bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
const float* positions, Rect* bounds, Functor* functor) {
const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
@ -602,7 +601,10 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t
initRender(clip, bounds, functor);
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
finishRender();
if (forceFinish) {
finishRender();
}
return mDrawn;
}

View File

@ -66,7 +66,8 @@ public:
// bounds is an out parameter
bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds,
Functor* functor);
Functor* functor, bool forceFinish = true);
// bounds is an out parameter
bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
@ -101,6 +102,8 @@ public:
private:
friend class Font;
static const uint32_t gMaxNumberOfQuads = 2048;
const uint8_t* mGammaTable;
void allocateTextureMemory(CacheTexture* cacheTexture);
@ -154,7 +157,6 @@ private:
bool mUploadTexture;
uint32_t mMaxNumberOfQuads;
uint32_t mIndexBufferID;
Functor* mFunctor;

View File

@ -112,11 +112,9 @@ static const Blender gBlendsSwap[] = {
OpenGLRenderer::OpenGLRenderer():
mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
mDrawModifiers.mShader = NULL;
mDrawModifiers.mColorFilter = NULL;
// *set* draw modifiers to be 0
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
mDrawModifiers.mHasShadow = false;
mDrawModifiers.mHasDrawFilter = false;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
@ -1330,10 +1328,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
}
}
if (stateDeferFlags & kStateDeferFlag_Clip) {
state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
if (state.mClipValid) {
state.mClip.set(currentClip);
} else {
state.mClip.setEmpty();
}
// Transform, drawModifiers, and alpha always deferred, since they are used by state operations
@ -1344,17 +1341,22 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
return false;
}
void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) {
void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
currentTransform().load(state.mMatrix);
mDrawModifiers = state.mDrawModifiers;
mSnapshot->alpha = state.mAlpha;
if (!state.mClip.isEmpty()) {
if (state.mClipValid && !skipClipRestore) {
mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
dirtyClip();
}
}
void OpenGLRenderer::setFullScreenClip() {
mSnapshot->setClip(0, 0, mWidth, mHeight);
dirtyClip();
}
///////////////////////////////////////////////////////////////////////////////
// Transforms
///////////////////////////////////////////////////////////////////////////////
@ -1963,6 +1965,42 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk
(GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
}
status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
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 = mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_NEAREST, true); // merged ops are always pure-translation for now
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
int color = paint != NULL ? paint->getColor() : 0;
drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
texture->id, paint != NULL, color, alpha, mode,
&vertices[0].position[0], &vertices[0].texture[0],
GL_TRIANGLES, bitmapCount * 6, true, true);
} else {
drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
texture->id, alpha / 255.0f, mode, texture->blend,
&vertices[0].position[0], &vertices[0].texture[0],
GL_TRIANGLES, bitmapCount * 6, false, true, 0, true);
}
return DrawGlInfo::kStatusDrew;
}
status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
const float right = left + bitmap->width();
const float bottom = top + bitmap->height();
@ -2796,8 +2834,11 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
}
status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint, float length) {
if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
float x, float y, const float* positions, SkPaint* paint, float length,
DrawOpMode drawOpMode) {
if (drawOpMode == kDrawOpMode_Immediate &&
(text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint))) {
return DrawGlInfo::kStatusDone;
}
@ -2815,8 +2856,13 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
SkPaint::FontMetrics metrics;
paint->getFontMetrics(&metrics, 0.0f);
if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
return DrawGlInfo::kStatusDone;
if (drawOpMode == kDrawOpMode_Immediate) {
if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
return DrawGlInfo::kStatusDone;
}
} else {
// merged draw operations don't need scissor, but clip should still be valid
mCaches.setScissorEnabled(mScissorOptimizationDisabled);
}
const float oldX = x;
@ -2868,17 +2914,20 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
bool status;
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
// don't call issuedrawcommand, do it at end of batch
bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
positions, hasActiveLayer ? &bounds : NULL, &functor);
positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
positions, hasActiveLayer ? &bounds : NULL, &functor);
positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
}
if (status && hasActiveLayer) {
if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
if (!pureTranslate) {
transform.mapRect(bounds);
}
@ -3093,7 +3142,11 @@ void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::resetPaintFilter() {
// when clearing the PaintFilter, the masks should also be cleared for simple DrawModifier
// comparison, see MergingDrawBatch::canMergeWith
mDrawModifiers.mHasDrawFilter = false;
mDrawModifiers.mPaintFilterClearBits = 0;
mDrawModifiers.mPaintFilterSetBits = 0;
}
void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
@ -3365,7 +3418,7 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
bool ignoreTransform, bool dirty) {
bool ignoreTransform, bool ignoreScale, bool dirty) {
setupDraw();
setupDrawWithTexture(true);
@ -3377,7 +3430,11 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f
setupDrawBlending(true, mode);
setupDrawProgram();
if (!dirty) setupDrawDirtyRegionsDisabled();
setupDrawModelView(left, top, right, bottom, ignoreTransform);
if (!ignoreScale) {
setupDrawModelView(left, top, right, bottom, ignoreTransform);
} else {
setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
}
setupDrawTexture(texture);
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();

View File

@ -71,10 +71,17 @@ enum StateDeferFlags {
kStateDeferFlag_Clip = 0x2
};
enum DrawOpMode {
kDrawOpMode_Immediate,
kDrawOpMode_Defer,
kDrawOpMode_Flush
};
struct DeferredDisplayState {
Rect mBounds; // local bounds, mapped with matrix to be in screen space coordinates, clipped.
Rect mBounds; // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped.
// the below are set and used by the OpenGLRenderer at record and deferred playback
bool mClipValid;
Rect mClip;
mat4 mMatrix;
DrawModifiers mDrawModifiers;
@ -232,6 +239,8 @@ public:
virtual void outputDisplayList(DisplayList* displayList);
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
status_t drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
const Rect& bounds, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@ -261,7 +270,8 @@ public:
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, SkPaint* paint, float length = -1.0f);
const float* positions, SkPaint* paint, float length = -1.0f,
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();
@ -282,7 +292,8 @@ public:
SkPaint* filterPaint(SkPaint* paint);
bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
void restoreDisplayState(const DeferredDisplayState& state);
void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false);
void setFullScreenClip();
const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
@ -336,20 +347,18 @@ public:
* @param mode Where to store the resulting xfermode
*/
static inline void getAlphaAndModeDirect(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
if (paint) {
*mode = getXfermode(paint->getXfermode());
*mode = getXfermodeDirect(paint);
*alpha = getAlphaDirect(paint);
}
// Skia draws using the color's alpha channel if < 255
// Otherwise, it uses the paint's alpha
int color = paint->getColor();
*alpha = (color >> 24) & 0xFF;
if (*alpha == 255) {
*alpha = paint->getAlpha();
}
} else {
*mode = SkXfermode::kSrcOver_Mode;
*alpha = 255;
}
static inline SkXfermode::Mode getXfermodeDirect(SkPaint* paint) {
if (!paint) return SkXfermode::kSrcOver_Mode;
return getXfermode(paint->getXfermode());
}
static inline int getAlphaDirect(SkPaint* paint) {
if (!paint) return 255;
return paint->getAlpha();
}
/**
@ -358,6 +367,20 @@ public:
*/
mat4 findBestFontTransform(const mat4& transform) const;
#if DEBUG_MERGE_BEHAVIOR
void drawScreenSpaceColorRect(float left, float top, float right, float bottom, int color) {
mCaches.setScissorEnabled(false);
// should only be called outside of other draw ops, so stencil can only be in test state
bool stencilWasEnabled = mCaches.stencil.isTestEnabled();
mCaches.stencil.disable();
drawColorRect(left, top, right, bottom, color, SkXfermode::kSrcOver_Mode, true);
if (stencilWasEnabled) mCaches.stencil.enableTest();
}
#endif
protected:
/**
* Computes the projection matrix, initialize the first snapshot
@ -778,7 +801,7 @@ private:
void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
bool ignoreTransform, bool dirty = true);
bool ignoreTransform, bool ignoreScale = false, bool dirty = true);
/**
* Draws text underline and strike-through if needed.

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_HWUI_TINYHASHMAP_H
#define ANDROID_HWUI_TINYHASHMAP_H
#include <utils/BasicHashtable.h>
namespace android {
namespace uirenderer {
/**
* A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
*
* Currently, expects simple keys that are handled by hash_t()
*/
template <typename TKey, typename TValue>
class TinyHashMap {
public:
typedef key_value_pair_t<TKey, TValue> TEntry;
/**
* Puts an entry in the hash, removing any existing entry with the same key
*/
void put(TKey key, TValue value) {
hash_t hash = hash_t(key);
ssize_t index = mTable.find(-1, hash, key);
if (index != -1) {
mTable.removeAt(index);
}
TEntry initEntry(key, value);
mTable.add(hash, initEntry);
}
/**
* Return true if key is in the map, in which case stores the value in the output ref
*/
bool get(TKey key, TValue& outValue) {
hash_t hash = hash_t(key);
ssize_t index = mTable.find(-1, hash, key);
if (index == -1) {
return false;
}
outValue = mTable.entryAt(index).value;
return true;
}
void clear() { mTable.clear(); }
private:
BasicHashtable<TKey, TEntry> mTable;
};
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_HWUI_TINYHASHMAP_H