2015-10-05 13:00:52 -07:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "OpReorderer.h"
|
|
|
|
|
|
|
|
#include "utils/PaintUtils.h"
|
|
|
|
#include "RenderNode.h"
|
|
|
|
|
|
|
|
#include "SkCanvas.h"
|
|
|
|
#include "utils/Trace.h"
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
namespace uirenderer {
|
|
|
|
|
|
|
|
class BatchBase {
|
|
|
|
|
|
|
|
public:
|
|
|
|
BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
|
|
|
|
: mBatchId(batchId)
|
|
|
|
, mMerging(merging) {
|
|
|
|
mBounds = op->computedState.clippedBounds;
|
|
|
|
mOps.push_back(op);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool intersects(const Rect& rect) const {
|
|
|
|
if (!rect.intersects(mBounds)) return false;
|
|
|
|
|
|
|
|
for (const BakedOpState* op : mOps) {
|
|
|
|
if (rect.intersects(op->computedState.clippedBounds)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
batchid_t getBatchId() const { return mBatchId; }
|
|
|
|
bool isMerging() const { return mMerging; }
|
|
|
|
|
|
|
|
const std::vector<BakedOpState*>& getOps() const { return mOps; }
|
|
|
|
|
|
|
|
void dump() const {
|
2015-10-20 09:39:42 -07:00
|
|
|
ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
|
|
|
|
this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
protected:
|
|
|
|
batchid_t mBatchId;
|
|
|
|
Rect mBounds;
|
|
|
|
std::vector<BakedOpState*> mOps;
|
|
|
|
bool mMerging;
|
|
|
|
};
|
|
|
|
|
|
|
|
class OpBatch : public BatchBase {
|
|
|
|
public:
|
|
|
|
static void* operator new(size_t size, LinearAllocator& allocator) {
|
|
|
|
return allocator.alloc(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
OpBatch(batchid_t batchId, BakedOpState* op)
|
|
|
|
: BatchBase(batchId, op, false) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void batchOp(BakedOpState* op) {
|
|
|
|
mBounds.unionWith(op->computedState.clippedBounds);
|
|
|
|
mOps.push_back(op);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class MergingOpBatch : public BatchBase {
|
|
|
|
public:
|
|
|
|
static void* operator new(size_t size, LinearAllocator& allocator) {
|
|
|
|
return allocator.alloc(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
MergingOpBatch(batchid_t batchId, BakedOpState* op)
|
|
|
|
: BatchBase(batchId, op, true) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
|
|
|
|
* and clip side flags. Positive bounds delta means new bounds fit in old.
|
|
|
|
*/
|
|
|
|
static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
|
|
|
|
float boundsDelta) {
|
|
|
|
bool currentClipExists = currentFlags & side;
|
|
|
|
bool newClipExists = newFlags & side;
|
|
|
|
|
|
|
|
// if current is clipped, we must be able to fit new bounds in current
|
|
|
|
if (boundsDelta > 0 && currentClipExists) return false;
|
|
|
|
|
|
|
|
// if new is clipped, we must be able to fit current bounds in new
|
|
|
|
if (boundsDelta < 0 && newClipExists) return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool paintIsDefault(const SkPaint& paint) {
|
|
|
|
return paint.getAlpha() == 255
|
|
|
|
&& paint.getColorFilter() == nullptr
|
|
|
|
&& paint.getShader() == nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
|
|
|
|
return a.getAlpha() == b.getAlpha()
|
|
|
|
&& a.getColorFilter() == b.getColorFilter()
|
|
|
|
&& a.getShader() == b.getShader();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 can merge with another set of ops
|
|
|
|
*
|
|
|
|
* 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(BakedOpState* op) const {
|
|
|
|
bool isTextBatch = getBatchId() == OpBatchType::Text
|
|
|
|
|| getBatchId() == OpBatchType::ColorText;
|
|
|
|
|
|
|
|
// Overlapping other operations is only allowed for text without shadow. For other ops,
|
|
|
|
// multiDraw isn't guaranteed to overdraw correctly
|
|
|
|
if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
|
|
|
|
if (intersects(op->computedState.clippedBounds)) return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const BakedOpState* lhs = op;
|
|
|
|
const BakedOpState* rhs = mOps[0];
|
|
|
|
|
|
|
|
if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
|
|
|
|
|
|
|
|
// Identical round rect clip state means both ops will clip in the same way, or not at all.
|
|
|
|
// As the state objects are const, we can compare their pointers to determine mergeability
|
|
|
|
if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
|
|
|
|
if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
|
|
|
|
|
|
|
|
/* Clipping compatibility check
|
|
|
|
*
|
|
|
|
* Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
|
|
|
|
* clip for that side.
|
|
|
|
*/
|
|
|
|
const int currentFlags = mClipSideFlags;
|
|
|
|
const int newFlags = op->computedState.clipSideFlags;
|
|
|
|
if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
|
|
|
|
const Rect& opBounds = op->computedState.clippedBounds;
|
|
|
|
float boundsDelta = mBounds.left - opBounds.left;
|
|
|
|
if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
|
|
|
|
boundsDelta = mBounds.top - opBounds.top;
|
|
|
|
if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
|
|
|
|
|
|
|
|
// right and bottom delta calculation reversed to account for direction
|
|
|
|
boundsDelta = opBounds.right - mBounds.right;
|
|
|
|
if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
|
|
|
|
boundsDelta = opBounds.bottom - mBounds.bottom;
|
|
|
|
if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const SkPaint* newPaint = op->op->paint;
|
|
|
|
const SkPaint* oldPaint = mOps[0]->op->paint;
|
|
|
|
|
|
|
|
if (newPaint == oldPaint) {
|
|
|
|
// if paints are equal, then modifiers + paint attribs don't need to be compared
|
|
|
|
return true;
|
|
|
|
} else if (newPaint && !oldPaint) {
|
|
|
|
return paintIsDefault(*newPaint);
|
|
|
|
} else if (!newPaint && oldPaint) {
|
|
|
|
return paintIsDefault(*oldPaint);
|
|
|
|
}
|
|
|
|
return paintsAreEquivalent(*newPaint, *oldPaint);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mergeOp(BakedOpState* op) {
|
|
|
|
mBounds.unionWith(op->computedState.clippedBounds);
|
|
|
|
mOps.push_back(op);
|
|
|
|
|
|
|
|
const int newClipSideFlags = op->computedState.clipSideFlags;
|
|
|
|
mClipSideFlags |= newClipSideFlags;
|
|
|
|
|
|
|
|
const Rect& opClip = op->computedState.clipRect;
|
|
|
|
if (newClipSideFlags & OpClipSideFlags::Left) mClipRect.left = opClip.left;
|
|
|
|
if (newClipSideFlags & OpClipSideFlags::Top) mClipRect.top = opClip.top;
|
|
|
|
if (newClipSideFlags & OpClipSideFlags::Right) mClipRect.right = opClip.right;
|
|
|
|
if (newClipSideFlags & OpClipSideFlags::Bottom) mClipRect.bottom = opClip.bottom;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int mClipSideFlags = 0;
|
|
|
|
Rect mClipRect;
|
|
|
|
};
|
|
|
|
|
2015-10-20 09:39:42 -07:00
|
|
|
// iterate back toward target to see if anything drawn since should overlap the new op
|
2015-10-23 14:33:42 -07:00
|
|
|
// if no target, merging ops still iterate to find similar batch to insert after
|
2015-10-20 09:39:42 -07:00
|
|
|
void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
|
|
|
|
BatchBase** targetBatch, size_t* insertBatchIndex) const {
|
|
|
|
for (int i = mBatches.size() - 1; i >= 0; i--) {
|
|
|
|
BatchBase* overBatch = 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(clippedBounds)) {
|
|
|
|
// 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 = nullptr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
|
|
|
|
BakedOpState* op, batchid_t batchId) {
|
|
|
|
OpBatch* targetBatch = mBatchLookup[batchId];
|
|
|
|
|
|
|
|
size_t insertBatchIndex = mBatches.size();
|
|
|
|
if (targetBatch) {
|
|
|
|
locateInsertIndex(batchId, op->computedState.clippedBounds,
|
|
|
|
(BatchBase**)(&targetBatch), &insertBatchIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetBatch) {
|
|
|
|
targetBatch->batchOp(op);
|
|
|
|
} else {
|
|
|
|
// new non-merging batch
|
|
|
|
targetBatch = new (allocator) OpBatch(batchId, op);
|
|
|
|
mBatchLookup[batchId] = targetBatch;
|
|
|
|
mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// insertion point of a new batch, will hopefully be immediately after similar batch
|
|
|
|
// (generally, should be similar shader)
|
|
|
|
void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
|
|
|
|
BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
|
|
|
|
MergingOpBatch* targetBatch = nullptr;
|
|
|
|
|
|
|
|
// Try to merge with any existing batch with same mergeId
|
|
|
|
auto getResult = mMergingBatchLookup[batchId].find(mergeId);
|
|
|
|
if (getResult != mMergingBatchLookup[batchId].end()) {
|
|
|
|
targetBatch = getResult->second;
|
|
|
|
if (!targetBatch->canMergeWith(op)) {
|
|
|
|
targetBatch = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t insertBatchIndex = mBatches.size();
|
|
|
|
locateInsertIndex(batchId, op->computedState.clippedBounds,
|
|
|
|
(BatchBase**)(&targetBatch), &insertBatchIndex);
|
|
|
|
|
|
|
|
if (targetBatch) {
|
|
|
|
targetBatch->mergeOp(op);
|
|
|
|
} else {
|
|
|
|
// new merging batch
|
|
|
|
targetBatch = new (allocator) MergingOpBatch(batchId, op);
|
|
|
|
mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
|
|
|
|
|
|
|
|
mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-26 15:49:56 -07:00
|
|
|
void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers) const {
|
|
|
|
ATRACE_NAME("flush drawing commands");
|
2015-10-20 09:39:42 -07:00
|
|
|
for (const BatchBase* batch : mBatches) {
|
|
|
|
// TODO: different behavior based on batch->isMerging()
|
|
|
|
for (const BakedOpState* op : batch->getOps()) {
|
|
|
|
receivers[op->op->opId](arg, *op->op, *op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpReorderer::LayerReorderer::dump() const {
|
|
|
|
for (const BatchBase* batch : mBatches) {
|
|
|
|
batch->dump();
|
|
|
|
}
|
|
|
|
}
|
2015-10-05 13:00:52 -07:00
|
|
|
|
2015-10-23 14:33:42 -07:00
|
|
|
OpReorderer::OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
|
|
|
|
const std::vector< sp<RenderNode> >& nodes)
|
2015-10-20 09:39:42 -07:00
|
|
|
: mCanvasState(*this) {
|
2015-10-23 14:33:42 -07:00
|
|
|
ATRACE_NAME("prepare drawing commands");
|
2015-10-20 09:39:42 -07:00
|
|
|
|
2015-10-23 14:33:42 -07:00
|
|
|
mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
|
|
|
|
mLayerStack.push_back(0);
|
2015-10-20 09:39:42 -07:00
|
|
|
|
2015-10-05 13:00:52 -07:00
|
|
|
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
|
2015-10-14 17:42:47 -07:00
|
|
|
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
|
|
|
|
Vector3());
|
2015-10-05 13:00:52 -07:00
|
|
|
for (const sp<RenderNode>& node : nodes) {
|
|
|
|
if (node->nothingToDraw()) continue;
|
|
|
|
|
|
|
|
// TODO: dedupe this code with onRenderNode()
|
|
|
|
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
|
|
|
|
if (node->applyViewProperties(mCanvasState)) {
|
|
|
|
// not rejected do ops...
|
2015-10-16 10:24:55 -07:00
|
|
|
const DisplayList& displayList = node->getDisplayList();
|
2015-10-16 14:23:12 -07:00
|
|
|
deferImpl(displayList);
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
mCanvasState.restore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-23 14:33:42 -07:00
|
|
|
OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList)
|
|
|
|
: mCanvasState(*this) {
|
2015-10-05 13:00:52 -07:00
|
|
|
ATRACE_NAME("prepare drawing commands");
|
2015-10-23 14:33:42 -07:00
|
|
|
|
|
|
|
mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
|
|
|
|
mLayerStack.push_back(0);
|
|
|
|
|
2015-10-05 13:00:52 -07:00
|
|
|
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
|
|
|
|
0, 0, viewportWidth, viewportHeight, Vector3());
|
2015-10-16 14:23:12 -07:00
|
|
|
deferImpl(displayList);
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
|
2015-10-23 14:33:42 -07:00
|
|
|
void OpReorderer::onViewportInitialized() {}
|
|
|
|
|
|
|
|
void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
|
|
|
|
|
2015-10-05 13:00:52 -07:00
|
|
|
/**
|
|
|
|
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
|
|
|
|
*
|
|
|
|
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. E.g. a
|
|
|
|
* BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
|
|
|
|
*/
|
2015-10-20 09:39:42 -07:00
|
|
|
#define OP_RECEIVER(Type) \
|
2015-10-05 13:00:52 -07:00
|
|
|
[](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
|
2015-10-16 14:23:12 -07:00
|
|
|
void OpReorderer::deferImpl(const DisplayList& displayList) {
|
2015-10-05 13:00:52 -07:00
|
|
|
static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
|
2015-10-20 09:39:42 -07:00
|
|
|
MAP_OPS(OP_RECEIVER)
|
2015-10-05 13:00:52 -07:00
|
|
|
};
|
2015-10-16 14:23:12 -07:00
|
|
|
for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
|
2015-10-05 13:00:52 -07:00
|
|
|
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
|
2015-10-16 14:23:12 -07:00
|
|
|
const RecordedOp* op = displayList.getOps()[opIndex];
|
2015-10-05 13:00:52 -07:00
|
|
|
receivers[op->opId](*this, *op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
|
|
|
|
if (op.renderNode->nothingToDraw()) {
|
|
|
|
return;
|
|
|
|
}
|
2015-10-20 09:39:42 -07:00
|
|
|
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
|
2015-10-05 13:00:52 -07:00
|
|
|
|
|
|
|
// apply state from RecordedOp
|
|
|
|
mCanvasState.concatMatrix(op.localMatrix);
|
|
|
|
mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top,
|
|
|
|
op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op);
|
|
|
|
|
|
|
|
// apply RenderProperties state
|
|
|
|
if (op.renderNode->applyViewProperties(mCanvasState)) {
|
2015-10-20 09:39:42 -07:00
|
|
|
// if node not rejected based on properties, do ops...
|
2015-10-16 14:23:12 -07:00
|
|
|
deferImpl(op.renderNode->getDisplayList());
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
2015-10-20 09:39:42 -07:00
|
|
|
mCanvasState.restoreToCount(count);
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static batchid_t tessellatedBatchId(const SkPaint& paint) {
|
|
|
|
return paint.getPathEffect()
|
|
|
|
? OpBatchType::AlphaMaskTexture
|
|
|
|
: (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpReorderer::onBitmapOp(const BitmapOp& op) {
|
2015-10-20 09:39:42 -07:00
|
|
|
BakedOpState* bakedStateOp = tryBakeOpState(op);
|
2015-10-05 13:00:52 -07:00
|
|
|
if (!bakedStateOp) return; // quick rejected
|
|
|
|
|
|
|
|
mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
|
|
|
|
// TODO: AssetAtlas
|
2015-10-20 09:39:42 -07:00
|
|
|
currentLayer().deferMergeableOp(mAllocator, bakedStateOp, OpBatchType::Bitmap, mergeId);
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpReorderer::onRectOp(const RectOp& op) {
|
2015-10-20 09:39:42 -07:00
|
|
|
BakedOpState* bakedStateOp = tryBakeOpState(op);
|
2015-10-05 13:00:52 -07:00
|
|
|
if (!bakedStateOp) return; // quick rejected
|
2015-10-20 09:39:42 -07:00
|
|
|
currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, tessellatedBatchId(*op.paint));
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
|
2015-10-20 09:39:42 -07:00
|
|
|
BakedOpState* bakedStateOp = tryBakeOpState(op);
|
2015-10-05 13:00:52 -07:00
|
|
|
if (!bakedStateOp) return; // quick rejected
|
2015-10-20 09:39:42 -07:00
|
|
|
currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
|
2015-10-20 09:39:42 -07:00
|
|
|
// TODO: test rejection at defer time, where the bounds become empty
|
|
|
|
void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
|
2015-10-23 14:33:42 -07:00
|
|
|
const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
|
|
|
|
const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
|
|
|
|
|
2015-10-20 09:39:42 -07:00
|
|
|
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
|
|
|
|
mCanvasState.writableSnapshot()->transform->loadIdentity();
|
2015-10-23 14:33:42 -07:00
|
|
|
mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
|
2015-10-20 09:39:42 -07:00
|
|
|
mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
|
|
|
|
|
|
|
|
// create a new layer, and push its index on the stack
|
|
|
|
mLayerStack.push_back(mLayerReorderers.size());
|
2015-10-23 14:33:42 -07:00
|
|
|
mLayerReorderers.emplace_back(layerWidth, layerHeight);
|
2015-10-20 09:39:42 -07:00
|
|
|
mLayerReorderers.back().beginLayerOp = &op;
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
|
2015-10-20 09:39:42 -07:00
|
|
|
void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
|
|
|
|
mCanvasState.restore();
|
2015-10-05 13:00:52 -07:00
|
|
|
|
2015-10-20 09:39:42 -07:00
|
|
|
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
|
|
|
|
|
|
|
|
// pop finished layer off of the stack
|
|
|
|
int finishedLayerIndex = mLayerStack.back();
|
|
|
|
mLayerStack.pop_back();
|
|
|
|
|
|
|
|
// record the draw operation into the previous layer's list of draw commands
|
|
|
|
// uses state from the associated beginLayerOp, since it has all the state needed for drawing
|
|
|
|
LayerOp* drawLayerOp = new (mAllocator) LayerOp(
|
|
|
|
beginLayerOp.unmappedBounds,
|
|
|
|
beginLayerOp.localMatrix,
|
|
|
|
beginLayerOp.localClipRect,
|
2015-10-23 14:33:42 -07:00
|
|
|
beginLayerOp.paint,
|
2015-10-26 15:49:56 -07:00
|
|
|
&mLayerReorderers[finishedLayerIndex].offscreenBuffer);
|
2015-10-20 09:39:42 -07:00
|
|
|
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
|
|
|
|
|
|
|
|
if (bakedOpState) {
|
|
|
|
// Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
|
|
|
|
currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
|
|
|
|
} else {
|
|
|
|
// Layer won't be drawn - delete its drawing batches to prevent it from doing any work
|
|
|
|
mLayerReorderers[finishedLayerIndex].clear();
|
|
|
|
return;
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-20 09:39:42 -07:00
|
|
|
void OpReorderer::onLayerOp(const LayerOp& op) {
|
|
|
|
LOG_ALWAYS_FATAL("unsupported");
|
2015-10-05 13:00:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace uirenderer
|
|
|
|
} // namespace android
|