DisplayList::DisplayList()
: projectionReceiveIndex(-1)
+ , stdAllocator(allocator)
+ , chunks(stdAllocator)
+ , ops(stdAllocator)
+ , children(stdAllocator)
+ , bitmapResources(stdAllocator)
+ , pathResources(stdAllocator)
+ , patchResources(stdAllocator)
+ , paints(stdAllocator)
+ , regions(stdAllocator)
+ , referenceHolders(stdAllocator)
+ , functors(stdAllocator)
, hasDrawOps(false) {
}
}
size_t DisplayList::addChild(NodeOpType* op) {
- mReferenceHolders.push_back(op->renderNode);
- size_t index = mChildren.size();
- mChildren.push_back(op);
+ referenceHolders.push_back(op->renderNode);
+ size_t index = children.size();
+ children.push_back(op);
return index;
}
DisplayList();
~DisplayList();
- // pointers to all ops within display list, pointing into allocator data
- std::vector<DisplayListOp*> displayListOps;
-
// index of DisplayListOp restore, after which projected descendents should be drawn
int projectionReceiveIndex;
- std::vector<const SkBitmap*> bitmapResources;
- std::vector<const SkPath*> pathResources;
- std::vector<const Res_png_9patch*> patchResources;
+ const LsaVector<Chunk>& getChunks() const { return chunks; }
+ const LsaVector<BaseOpType*>& getOps() const { return ops; }
- std::vector<std::unique_ptr<const SkPaint>> paints;
- std::vector<std::unique_ptr<const SkRegion>> regions;
- Vector<Functor*> functors;
+ const LsaVector<NodeOpType*>& getChildren() const { return children; }
- const std::vector<Chunk>& getChunks() const {
- return chunks;
- }
- const std::vector<BaseOpType*>& getOps() const {
- return ops;
- }
+ const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
+ const LsaVector<Functor*>& getFunctors() const { return functors; }
size_t addChild(NodeOpType* childOp);
- const std::vector<NodeOpType*>& children() { return mChildren; }
+
void ref(VirtualLightRefBase* prop) {
- mReferenceHolders.push_back(prop);
+ referenceHolders.push_back(prop);
}
size_t getUsedSize() {
}
private:
- std::vector<BaseOpType*> ops;
+ // allocator into which all ops and LsaVector arrays allocated
+ LinearAllocator allocator;
+ LinearStdAllocator<void*> stdAllocator;
- std::vector< sp<VirtualLightRefBase> > mReferenceHolders;
+ LsaVector<Chunk> chunks;
+ LsaVector<BaseOpType*> ops;
- // list of children display lists for quick, non-drawing traversal
- std::vector<NodeOpType*> mChildren;
+ // list of Ops referring to RenderNode children for quick, non-drawing traversal
+ LsaVector<NodeOpType*> children;
- std::vector<Chunk> chunks;
+ // Resources - Skia objects + 9 patches referred to by this DisplayList
+ LsaVector<const SkBitmap*> bitmapResources;
+ LsaVector<const SkPath*> pathResources;
+ LsaVector<const Res_png_9patch*> patchResources;
+ LsaVector<std::unique_ptr<const SkPaint>> paints;
+ LsaVector<std::unique_ptr<const SkRegion>> regions;
+ LsaVector< sp<VirtualLightRefBase> > referenceHolders;
+
+ // List of functors
+ LsaVector<Functor*> functors;
- // allocator into which all ops were allocated
- LinearAllocator allocator;
bool hasDrawOps;
void cleanupResources();
void DisplayListCanvas::callDrawGLFunction(Functor *functor) {
addDrawOp(new (alloc()) DrawFunctorOp(functor));
- mDisplayList->functors.add(functor);
+ mDisplayList->functors.push_back(functor);
}
SkCanvas* DisplayListCanvas::asSkCanvas() {
newChunk.endOpIndex = insertIndex + 1;
newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
- int nextChildIndex = mDisplayList->children().size();
+ int nextChildIndex = mDisplayList->children.size();
newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
mDeferredBarrierType = kBarrier_None;
} else {
if (node->applyViewProperties(mCanvasState)) {
// not rejected do ops...
const DisplayList& displayList = node->getDisplayList();
- deferImpl(displayList.getChunks(), displayList.getOps());
+ deferImpl(displayList);
}
mCanvasState.restore();
}
}
-void OpReorderer::defer(int viewportWidth, int viewportHeight,
- const std::vector<DisplayList::Chunk>& chunks, const std::vector<RecordedOp*>& ops) {
+void OpReorderer::defer(int viewportWidth, int viewportHeight, const DisplayList& displayList) {
ATRACE_NAME("prepare drawing commands");
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
0, 0, viewportWidth, viewportHeight, Vector3());
- deferImpl(chunks, ops);
+ deferImpl(displayList);
}
/**
*/
#define OP_RECIEVER(Type) \
[](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
-void OpReorderer::deferImpl(const std::vector<DisplayList::Chunk>& chunks,
- const std::vector<RecordedOp*>& ops) {
+void OpReorderer::deferImpl(const DisplayList& displayList) {
static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
MAP_OPS(OP_RECIEVER)
};
- for (const DisplayList::Chunk& chunk : chunks) {
+ for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
- const RecordedOp* op = ops[opIndex];
+ const RecordedOp* op = displayList.getOps()[opIndex];
receivers[op->opId](*this, *op);
}
}
// apply RenderProperties state
if (op.renderNode->applyViewProperties(mCanvasState)) {
// not rejected do ops...
- const DisplayList& displayList = op.renderNode->getDisplayList();
- deferImpl(displayList.getChunks(), displayList.getOps());
+ deferImpl(op.renderNode->getDisplayList());
}
mCanvasState.restore();
}
void defer(const SkRect& clip, int viewportWidth, int viewportHeight,
const std::vector< sp<RenderNode> >& nodes);
- void defer(int viewportWidth, int viewportHeight,
- const std::vector<DisplayList::Chunk>& chunks, const std::vector<RecordedOp*>& ops);
+ void defer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
/**
private:
BakedOpState* bakeOpState(const RecordedOp& recordedOp);
- void deferImpl(const std::vector<DisplayList::Chunk>& chunks,
- const std::vector<RecordedOp*>& ops);
+ void deferImpl(const DisplayList& displayList);
void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
newChunk.endOpIndex = insertIndex + 1;
newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
- int nextChildIndex = mDisplayList->children().size();
+ int nextChildIndex = mDisplayList->children.size();
newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
mDeferredBarrierType = kBarrier_None;
} else {
mLayer->wasBuildLayered ? "true" : "false");
}
if (mDisplayList) {
- for (auto&& child : mDisplayList->children()) {
+ for (auto&& child : mDisplayList->getChildren()) {
child->renderNode->debugDumpLayers(prefix);
}
}
pnode->clear_children();
if (mDisplayList) {
- for (auto&& child : mDisplayList->children()) {
+ for (auto&& child : mDisplayList->getChildren()) {
child->renderNode->copyTo(pnode->add_children());
}
}
bool willHaveFunctor = false;
if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
- willHaveFunctor = !mStagingDisplayList->functors.isEmpty();
+ willHaveFunctor = !mStagingDisplayList->getFunctors().empty();
} else if (mDisplayList) {
- willHaveFunctor = !mDisplayList->functors.isEmpty();
+ willHaveFunctor = !mDisplayList->getFunctors().empty();
}
bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
willHaveFunctor, functorsNeedLayer);
// Make sure we inc first so that we don't fluctuate between 0 and 1,
// which would thrash the layer cache
if (mStagingDisplayList) {
- for (auto&& child : mStagingDisplayList->children()) {
+ for (auto&& child : mStagingDisplayList->getChildren()) {
child->renderNode->incParentRefCount();
}
}
mDisplayList = mStagingDisplayList;
mStagingDisplayList = nullptr;
if (mDisplayList) {
- for (size_t i = 0; i < mDisplayList->functors.size(); i++) {
- (*mDisplayList->functors[i])(DrawGlInfo::kModeSync, nullptr);
+ for (size_t i = 0; i < mDisplayList->getFunctors().size(); i++) {
+ (*mDisplayList->getFunctors()[i])(DrawGlInfo::kModeSync, nullptr);
}
}
}
void RenderNode::deleteDisplayList() {
if (mDisplayList) {
- for (auto&& child : mDisplayList->children()) {
+ for (auto&& child : mDisplayList->getChildren()) {
child->renderNode->decParentRefCount();
}
}
void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
- info.out.hasFunctors |= subtree->functors.size();
- for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
- info.prepareTextures = cache.prefetchAndMarkInUse(
- info.canvasContext, subtree->bitmapResources[i]);
+ info.out.hasFunctors |= subtree->getFunctors().size();
+ for (auto&& bitmapResource : subtree->getBitmapResources()) {
+ info.prepareTextures = cache.prefetchAndMarkInUse(info.canvasContext, bitmapResource);
}
- for (auto&& op : subtree->children()) {
+ for (auto&& op : subtree->getChildren()) {
RenderNode* childNode = op->renderNode;
#if HWUI_NEW_OPS
info.damageAccumulator->pushTransform(&op->localMatrix);
mLayer = nullptr;
}
if (mDisplayList) {
- for (auto&& child : mDisplayList->children()) {
+ for (auto&& child : mDisplayList->getChildren()) {
child->renderNode->destroyHardwareResources();
}
if (mNeedsDisplayListSync) {
// TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
// transform properties are applied correctly to top level children
if (mDisplayList == nullptr) return;
- for (unsigned int i = 0; i < mDisplayList->children().size(); i++) {
- DrawRenderNodeOp* childOp = mDisplayList->children()[i];
+ for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
}
#endif
opState->mSkipInOrderDraw = false;
}
- if (mDisplayList->children().size() > 0) {
+ if (mDisplayList->getChildren().size() > 0) {
const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
bool haveAppliedPropertiesToProjection = false;
- for (unsigned int i = 0; i < mDisplayList->children().size(); i++) {
- DrawRenderNodeOp* childOp = mDisplayList->children()[i];
+ for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
RenderNode* child = childOp->renderNode;
std::vector<DrawRenderNodeOp*>* projectionChildren = nullptr;
if (chunk.beginChildIndex == chunk.endChildIndex) return;
for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
- DrawRenderNodeOp* childOp = mDisplayList->children()[i];
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
RenderNode* child = childOp->renderNode;
float childZ = child->properties().getZ();
#include "DisplayListCanvas.h"
#endif
#include "microbench/MicroBench.h"
+#include "unit_tests/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
StopBenchmarkTiming();
}
+/**
+ * Simulate a simple view drawing a background, overlapped by an image.
+ *
+ * Note that the recording commands are intentionally not perfectly efficient, as the
+ * View system frequently produces unneeded save/restores.
+ */
+BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_simpleBitmapView);
+void BM_DisplayListCanvas_record_simpleBitmapView::Run(int iters) {
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
+
+ SkPaint rectPaint;
+ SkBitmap iconBitmap = TestUtils::createSkBitmap(80, 80);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ canvas.reset(100, 100);
+ {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.drawRect(0, 0, 100, 100, rectPaint);
+ canvas.restore();
+ }
+ {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(10, 10);
+ canvas.drawBitmap(iconBitmap, 0, 0, nullptr);
+ canvas.restore();
+ }
+ MicroBench::DoNotOptimize(&canvas);
+ delete canvas.finishRecording();
+ }
+ StopBenchmarkTiming();
+}
+
class NullClient: public CanvasStateClient {
void onViewportInitialized() override {}
void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
static void endFrame(Info& info) {}
};
TEST(OpReorderer, simple) {
- auto dld = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
OpReorderer reorderer;
- reorderer.defer(200, 200, dld->getChunks(), dld->getOps());
+ reorderer.defer(200, 200, *dl);
Info info;
reorderer.replayBakedOps<SimpleReceiver>(&info);
static void endFrame(Info& info) {}
};
TEST(OpReorderer, simpleBatching) {
- auto dld = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
});
OpReorderer reorderer;
- reorderer.defer(200, 200, dld->getChunks(), dld->getOps());
+ reorderer.defer(200, 200, *dl);
Info info;
reorderer.replayBakedOps<SimpleBatchingReceiver>(&info);
namespace android {
namespace uirenderer {
-static void playbackOps(const std::vector<DisplayList::Chunk>& chunks,
- const std::vector<RecordedOp*>& ops, std::function<void(const RecordedOp&)> opReciever) {
- for (const DisplayList::Chunk& chunk : chunks) {
+static void playbackOps(const DisplayList& displayList,
+ std::function<void(const RecordedOp&)> opReciever) {
+ for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
- opReciever(*ops[opIndex]);
+ RecordedOp* op = displayList.getOps()[opIndex];
+ opReciever(*op);
}
}
}
TEST(RecordingCanvas, emptyPlayback) {
- auto dld = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
canvas.restore();
});
- playbackOps(dld->getChunks(), dld->getOps(), [](const RecordedOp& op) { FAIL(); });
+ playbackOps(*dl, [](const RecordedOp& op) { FAIL(); });
}
TEST(RecordingCanvas, testSimpleRectRecord) {
- auto dld = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.drawRect(10, 20, 90, 180, SkPaint());
});
int count = 0;
- playbackOps(dld->getChunks(), dld->getOps(), [&count](const RecordedOp& op) {
+ playbackOps(*dl, [&count](const RecordedOp& op) {
count++;
ASSERT_EQ(RecordedOpId::RectOp, op.opId);
ASSERT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
}
TEST(RecordingCanvas, backgroundAndImage) {
- auto dld = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap;
bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
SkPaint paint;
});
int count = 0;
- playbackOps(dld->getChunks(), dld->getOps(), [&count](const RecordedOp& op) {
+ playbackOps(*dl, [&count](const RecordedOp& op) {
if (count == 0) {
ASSERT_EQ(RecordedOpId::RectOp, op.opId);
ASSERT_NE(nullptr, op.paint);
#include <stddef.h>
#include <type_traits>
+#include <vector>
+
namespace android {
namespace uirenderer {
template <class T1, class T2>
bool operator!= (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { return false; }
+template <class T>
+class LsaVector : public std::vector<T, LinearStdAllocator<T>> {
+public:
+ LsaVector(const LinearStdAllocator<T>& allocator)
+ : std::vector<T, LinearStdAllocator<T>>(allocator) {}
+};
+
}; // namespace uirenderer
}; // namespace android