am 30c990c3
: Merge "Draw Operation merging" into jb-mr2-dev
* commit '30c990c361291ad578ef4ffe4a4dd0fd6080797b': Draw Operation merging
This commit is contained in:
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
72
libs/hwui/utils/TinyHashMap.h
Normal file
72
libs/hwui/utils/TinyHashMap.h
Normal 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
|
Reference in New Issue
Block a user