bug:8037003 The reordering enables similar operations to draw together, minimizing the OpenGL state change operations that go inbetween draws. Eventually, multiple complete canvas draw operations will be merged (into a single glDrawArrays call, for example) Reorders DisplayList draw operations when: -They can move backwards in the command stream to be after similar operations without violating draw ordering -The OpenGLRenderer is in a simple, replayable state (no complex clip, or filter/shadow etc) Also adds two system properties to control the deferral/reordering: "debug.hwui.disable_draw_defer" "debug.hwui.disable_draw_reorder" which can be set to "true" to control the display list manipulation Change-Id: I5e89f3cb0ea2d2afd3e15c64d7f32b8406777a32
177 lines
5.2 KiB
C++
177 lines
5.2 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#define LOG_TAG "OpenGLRenderer"
|
|
#define ATRACE_TAG ATRACE_TAG_VIEW
|
|
|
|
#include <utils/Trace.h>
|
|
|
|
#include "Debug.h"
|
|
#include "DisplayListOp.h"
|
|
#include "OpenGLRenderer.h"
|
|
|
|
#if DEBUG_DEFER
|
|
#define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
|
|
#else
|
|
#define DEFER_LOGD(...)
|
|
#endif
|
|
|
|
namespace android {
|
|
namespace uirenderer {
|
|
|
|
class DrawOpBatch {
|
|
public:
|
|
DrawOpBatch() {
|
|
mOps.clear();
|
|
}
|
|
|
|
~DrawOpBatch() {
|
|
mOps.clear();
|
|
}
|
|
|
|
void add(DrawOp* op) {
|
|
// NOTE: ignore empty bounds special case, since we don't merge across those ops
|
|
mBounds.unionWith(op->state.mBounds);
|
|
mOps.add(op);
|
|
}
|
|
|
|
bool intersects(Rect& rect) {
|
|
if (!rect.intersects(mBounds)) return false;
|
|
for (unsigned int i = 0; i < mOps.size(); i++) {
|
|
if (rect.intersects(mOps[i]->state.mBounds)) {
|
|
#if DEBUG_DEFER
|
|
DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i],
|
|
mOps[i]->state.mBounds.left, mOps[i]->state.mBounds.top,
|
|
mOps[i]->state.mBounds.right, mOps[i]->state.mBounds.bottom);
|
|
mOps[i]->output(2);
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Vector<DrawOp*> mOps;
|
|
private:
|
|
Rect mBounds;
|
|
};
|
|
|
|
void DeferredDisplayList::clear() {
|
|
for (int i = 0; i < kOpBatch_Count; i++) {
|
|
mBatchIndices[i] = -1;
|
|
}
|
|
for (unsigned int i = 0; i < mBatches.size(); i++) {
|
|
delete mBatches[i];
|
|
}
|
|
mBatches.clear();
|
|
}
|
|
|
|
void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) {
|
|
if (CC_UNLIKELY(disallowReorder)) {
|
|
if (!mBatches.isEmpty()) {
|
|
mBatches[0]->add(op);
|
|
return;
|
|
}
|
|
DrawOpBatch* b = new DrawOpBatch();
|
|
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();
|
|
|
|
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;
|
|
}
|
|
#if DEBUG_DEFER
|
|
DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
|
|
op->output(2);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (batchId >= 0 && mBatchIndices[batchId] != -1) {
|
|
int targetIndex = mBatchIndices[batchId];
|
|
targetBatch = mBatches[targetIndex];
|
|
// 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 (overBatch->intersects(op->state.mBounds)) {
|
|
targetBatch = NULL;
|
|
#if DEBUG_DEFER
|
|
DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
|
|
targetIndex, i);
|
|
op->output(2);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!targetBatch) {
|
|
targetBatch = new DrawOpBatch();
|
|
mBatches.add(targetBatch);
|
|
if (batchId >= 0) {
|
|
mBatchIndices[batchId] = mBatches.size() - 1;
|
|
}
|
|
}
|
|
targetBatch->add(op);
|
|
}
|
|
|
|
status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
|
|
uint32_t level) {
|
|
ATRACE_CALL();
|
|
status_t status = DrawGlInfo::kStatusDone;
|
|
|
|
if (isEmpty()) return status; // nothing to flush
|
|
|
|
DEFER_LOGD("--flushing");
|
|
DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
|
|
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
|
int opCount = 0;
|
|
for (unsigned int i = 0; i < mBatches.size(); i++) {
|
|
DrawOpBatch* batch = mBatches[i];
|
|
for (unsigned int j = 0; j < batch->mOps.size(); j++) {
|
|
DrawOp* op = batch->mOps[j];
|
|
|
|
renderer.restoreDisplayState(op->state);
|
|
|
|
#if DEBUG_DEFER
|
|
op->output(2);
|
|
#endif
|
|
status |= op->applyDraw(renderer, dirty, level,
|
|
op->state.mMultipliedAlpha >= 0, op->state.mMultipliedAlpha);
|
|
opCount++;
|
|
}
|
|
}
|
|
|
|
DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount);
|
|
renderer.restoreToCount(restoreTo);
|
|
renderer.setDrawModifiers(restoreDrawModifiers);
|
|
clear();
|
|
return status;
|
|
}
|
|
|
|
}; // namespace uirenderer
|
|
}; // namespace android
|