Loading libs/hwui/Caches.cpp +2 −7 Original line number Diff line number Diff line Loading @@ -103,14 +103,9 @@ void Caches::initFont() { void Caches::initExtensions() { if (mExtensions.hasDebugMarker()) { eventMark = glInsertEventMarkerEXT; if ((drawDeferDisabled || drawReorderDisabled)) { startMark = glPushGroupMarkerEXT; endMark = glPopGroupMarkerEXT; } else { startMark = startMarkNull; endMark = endMarkNull; } } else { eventMark = eventMarkNull; startMark = startMarkNull; Loading libs/hwui/DeferredDisplayList.cpp +243 −37 Original line number Diff line number Diff line Loading @@ -32,15 +32,15 @@ namespace android { namespace uirenderer { ///////////////////////////////////////////////////////////////////////////////// // Operation Batches ///////////////////////////////////////////////////////////////////////////////// class DrawOpBatch { public: DrawOpBatch() { mOps.clear(); } DrawOpBatch() { mOps.clear(); } ~DrawOpBatch() { mOps.clear(); } virtual ~DrawOpBatch() { mOps.clear(); } void add(DrawOp* op) { // NOTE: ignore empty bounds special case, since we don't merge across those ops Loading @@ -48,8 +48,9 @@ public: mOps.add(op); } bool intersects(Rect& rect) { virtual 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 Loading @@ -64,27 +65,217 @@ public: return false; } Vector<DrawOp*> mOps; virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) { DEFER_LOGD("replaying draw batch %p", this); status_t status = DrawGlInfo::kStatusDone; DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); for (unsigned int i = 0; i < mOps.size(); i++) { DrawOp* op = mOps[i]; renderer.restoreDisplayState(op->state, kStateDeferFlag_Draw); #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark(strlen(op->name()), op->name()); #endif status |= op->applyDraw(renderer, dirty, 0, op->state.mMultipliedAlpha); logBuffer.writeCommand(0, op->name()); } return status; } inline int count() const { return mOps.size(); } private: Vector<DrawOp*> mOps; Rect mBounds; }; void DeferredDisplayList::clear() { class StateOpBatch : public DrawOpBatch { 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) { DEFER_LOGD("replaying state op batch %p", this); renderer.restoreDisplayState(mOp->state, 0); // use invalid save count because it won't be used at flush time - RestoreToCountOp is the // only one to use it, and we don't use that class at flush time, instead calling // renderer.restoreToCount directly int saveCount = -1; mOp->applyState(renderer, saveCount); return DrawGlInfo::kStatusDone; } private: StateOp* mOp; }; class RestoreToCountBatch : public DrawOpBatch { public: RestoreToCountBatch(int restoreCount) : 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) { DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount); renderer.restoreToCount(mRestoreCount); return DrawGlInfo::kStatusDone; } private: /* * The count used here represents the flush() time saveCount. This is as opposed to the * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and * (saveCount + mCount) respectively). Since the count is different from the original * RestoreToCountOp, we don't store a pointer to the op, as elsewhere. */ const int mRestoreCount; }; ///////////////////////////////////////////////////////////////////////////////// // DeferredDisplayList ///////////////////////////////////////////////////////////////////////////////// void DeferredDisplayList::resetBatchingState() { for (int i = 0; i < kOpBatch_Count; i++) { mBatchIndices[i] = -1; } } void DeferredDisplayList::clear() { resetBatchingState(); mComplexClipStackStart = -1; for (unsigned int i = 0; i < mBatches.size(); i++) { delete mBatches[i]; } mBatches.clear(); mSaveStack.clear(); } void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) { if (CC_UNLIKELY(disallowReorder)) { if (!mBatches.isEmpty()) { mBatches[0]->add(op); ///////////////////////////////////////////////////////////////////////////////// // Operation adding ///////////////////////////////////////////////////////////////////////////////// int DeferredDisplayList::getStateOpDeferFlags() const { // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save // the clip if we aren't recording a complex clip (and can thus trust it to be a rect) return recordingComplexClip() ? 0 : kStateDeferFlag_Clip; } int DeferredDisplayList::getDrawOpDeferFlags() const { return kStateDeferFlag_Draw | getStateOpDeferFlags(); } /** * When an clipping operation occurs that could cause a complex clip, record the operation and all * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading * the clip from deferred state, we play back all of the relevant state operations that generated * the complex clip. * * Note that we don't need to record the associated restore operation, since operations at defer * time record whether they should store the renderer's current clip */ void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) { if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) { DEFER_LOGD("%p Received complex clip operation %p", this, op); // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded storeStateOpBarrier(renderer, op); if (!recordingComplexClip()) { mComplexClipStackStart = renderer.getSaveCount() - 1; DEFER_LOGD(" Starting complex clip region, start is %d", mComplexClipStackStart); } } } /** * For now, we record save layer operations as barriers in the batch list, preventing drawing * operations from reordering around the saveLayer and it's associated restore() * * In the future, we should send saveLayer commands (if they can be played out of order) and their * contained drawing operations to a seperate list of batches, so that they may draw at the * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame. * * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set. */ void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount) { DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d", this, op, op->getFlags(), newSaveCount); storeStateOpBarrier(renderer, op); mSaveStack.push(newSaveCount); } /** * Takes save op and it's return value - the new save count - and stores it into the stream as a * barrier if it's needed to properly modify a complex clip */ void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) { int saveFlags = op->getFlags(); DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount); if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) { // store and replay the save operation, as it may be needed to correctly playback the clip DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount); storeStateOpBarrier(renderer, op); mSaveStack.push(newSaveCount); } } /** * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw * the layer in the deferred list * * other save() commands which occur as children of a snapshot with complex clip will be deferred, * and must be restored * * Either will act as a barrier to draw operation reordering, as we want to play back layer * save/restore and complex canvas modifications (including save/restore) in order. */ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount) { DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount); if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) { mComplexClipStackStart = -1; resetBatchingState(); } if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) { return; } while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop(); storeRestoreToCountBarrier(mSaveStack.size() + 1); } void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (renderer.storeDisplayState(op->state, getDrawOpDeferFlags())) { return; // quick rejected } op->onDrawOpDeferred(renderer); if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) { // TODO: elegant way to reuse batches? DrawOpBatch* b = new DrawOpBatch(); b->add(op); mBatches.add(b); Loading Loading @@ -138,9 +329,41 @@ void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) { targetBatch->add(op); } status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) { ATRACE_CALL(); void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) { DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size()); renderer.storeDisplayState(op->state, getStateOpDeferFlags()); mBatches.add(new StateOpBatch(op)); resetBatchingState(); } void DeferredDisplayList::storeRestoreToCountBarrier(int newSaveCount) { DEFER_LOGD("%p adding restore to count %d barrier, pos %d", this, newSaveCount, mBatches.size()); mBatches.add(new RestoreToCountBatch(newSaveCount)); resetBatchingState(); } ///////////////////////////////////////////////////////////////////////////////// // Replay / flush ///////////////////////////////////////////////////////////////////////////////// static status_t replayBatchList(Vector<DrawOpBatch*>& 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(); } DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount); return status; } status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { ATRACE_NAME("flush drawing commands"); status_t status = DrawGlInfo::kStatusDone; if (isEmpty()) return status; // nothing to flush Loading @@ -148,29 +371,12 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32 DEFER_LOGD("--flushing"); renderer.eventMark("Flush"); 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.restoreToCount(1); 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++; } } status |= replayBatchList(mBatches, renderer, dirty); DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount); DEFER_LOGD("--flush complete, returning %x", status); renderer.restoreToCount(restoreTo); renderer.setDrawModifiers(restoreDrawModifiers); clear(); return status; } Loading libs/hwui/DeferredDisplayList.h +31 −4 Original line number Diff line number Diff line Loading @@ -26,10 +26,13 @@ namespace android { namespace uirenderer { class ClipOp; class DrawOp; class SaveOp; class SaveLayerOp; class StateOp; class DrawOpBatch; class OpenGLRenderer; class SkiaShader; class DeferredDisplayList { public: Loading @@ -55,18 +58,42 @@ public: * Plays back all of the draw ops recorded into batches to the renderer. * Adjusts the state of the renderer as necessary, and restores it when complete */ status_t flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level); status_t flush(OpenGLRenderer& renderer, Rect& dirty); void addClip(OpenGLRenderer& renderer, ClipOp* op); void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount); void addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount); void addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount); /** * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if * disallowReorder is false, respecting draw order when overlaps occur */ void add(DrawOp* op, bool disallowReorder); void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); private: /* * Resets the batching back-pointers, creating a barrier in the operation stream so that no ops * added in the future will be inserted into a batch that already exist. */ void resetBatchingState(); void clear(); void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op); void storeRestoreToCountBarrier(int newSaveCount); bool recordingComplexClip() const { return mComplexClipStackStart >= 0; } int getStateOpDeferFlags() const; int getDrawOpDeferFlags() const; /* * * at defer time, stores the savecount of save/saveLayer ops that were */ Vector<int> mSaveStack; int mComplexClipStackStart; Vector<DrawOpBatch*> mBatches; int mBatchIndices[kOpBatch_Count]; Loading libs/hwui/DisplayList.cpp +103 −48 Original line number Diff line number Diff line Loading @@ -61,6 +61,12 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) { void DisplayList::clearResources() { mDisplayListData = NULL; mClipRectOp = NULL; mSaveLayerOp = NULL; mSaveOp = NULL; mRestoreToCountOp = NULL; delete mTransformMatrix; delete mTransformCamera; delete mTransformMatrix3D; Loading Loading @@ -156,6 +162,13 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde return; } // allocate reusable ops for state-deferral LinearAllocator& alloc = mDisplayListData->allocator; mClipRectOp = new (alloc) ClipRectOp(); mSaveLayerOp = new (alloc) SaveLayerOp(); mSaveOp = new (alloc) SaveOp(); mRestoreToCountOp = new (alloc) RestoreToCountOp(); mFunctorCount = recorder.getFunctorCount(); Caches& caches = Caches::getInstance(); Loading Loading @@ -318,7 +331,7 @@ void DisplayList::updateMatrix() { } } void DisplayList::outputViewProperties(uint32_t level) { void DisplayList::outputViewProperties(const int level) { updateMatrix(); if (mLeft != 0 || mTop != 0) { ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop); Loading Loading @@ -358,10 +371,17 @@ void DisplayList::outputViewProperties(uint32_t level) { } } status_t DisplayList::setViewProperties(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level, DeferredDisplayList* deferredList) { status_t status = DrawGlInfo::kStatusDone; #if DEBUG_DISPLAYLIST /* * For property operations, we pass a savecount of 0, since the operations aren't part of the * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in * base saveCount (i.e., how RestoreToCount uses saveCount + mCount) */ #define PROPERTY_SAVECOUNT 0 template <class T> void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, const int level) { #if DEBUG_DISPLAY_LIST outputViewProperties(level); #endif updateMatrix(); Loading @@ -381,86 +401,121 @@ status_t DisplayList::setViewProperties(OpenGLRenderer& renderer, Rect& dirty, } } if (mAlpha < 1 && !mCaching) { if (deferredList) { // flush since we'll either enter a Layer, or set alpha, both not supported in deferral status |= deferredList->flush(renderer, dirty, flags, level); } if (!mHasOverlappingRendering) { renderer.setAlpha(mAlpha); } else { // TODO: should be able to store the size of a DL at record time and not // have to pass it into this call. In fact, this information might be in the // location/size info that we store with the new native transform data. int flags = SkCanvas::kHasAlphaLayer_SaveFlag; int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag; if (mClipChildren) { flags |= SkCanvas::kClipToLayer_SaveFlag; saveFlags |= SkCanvas::kClipToLayer_SaveFlag; } renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop, mMultipliedAlpha, flags); handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, mMultipliedAlpha, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT); } } if (mClipChildren && !mCaching) { if (deferredList && CC_UNLIKELY(!renderer.hasRectToRectTransform())) { // flush, since clip will likely be a region status |= deferredList->flush(renderer, dirty, flags, level); handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op), PROPERTY_SAVECOUNT); } } class DeferOperationHandler { public: DeferOperationHandler(DeferStateStruct& deferStruct, int multipliedAlpha, int level) : mDeferStruct(deferStruct), mMultipliedAlpha(multipliedAlpha), mLevel(level) {} inline void operator()(DisplayListOp* operation, int saveCount) { operation->defer(mDeferStruct, saveCount, mLevel, mMultipliedAlpha); } renderer.clipRect(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op); private: DeferStateStruct& mDeferStruct; const int mMultipliedAlpha; const int mLevel; }; void DisplayList::defer(DeferStateStruct& deferStruct, const int level) { DeferOperationHandler handler(deferStruct, mCaching ? mMultipliedAlpha : -1, level); iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level); } class ReplayOperationHandler { public: ReplayOperationHandler(ReplayStateStruct& replayStruct, int multipliedAlpha, int level) : mReplayStruct(replayStruct), mMultipliedAlpha(multipliedAlpha), mLevel(level) {} inline void operator()(DisplayListOp* operation, int saveCount) { #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS replayStruct.mRenderer.eventMark(operation->name()); #endif operation->replay(mReplayStruct, saveCount, mLevel, mMultipliedAlpha); } return status; private: ReplayStateStruct& mReplayStruct; const int mMultipliedAlpha; const int mLevel; }; void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) { ReplayOperationHandler handler(replayStruct, mCaching ? mMultipliedAlpha : -1, level); replayStruct.mRenderer.startMark(mName.string()); iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level); replayStruct.mRenderer.endMark(); DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(), replayStruct.mDrawGlStatus); } status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level, DeferredDisplayList* deferredList) { status_t drawGlStatus = DrawGlInfo::kStatusDone; /** * This function serves both defer and replay modes, and will organize the displayList's component * operations for a single frame: * * Every 'simple' operation that affects just the matrix and alpha (or other factors of * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom * defer logic) and operations in displayListOps are issued through the 'handler' which handles the * defer vs replay logic, per operation */ template <class T> void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) { if (mSize == 0 || mAlpha <= 0) { DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string()); return; } #if DEBUG_DISPLAY_LIST Rect* clipRect = renderer.getClipRect(); DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f", (level+1)*2, "", this, mName.string(), clipRect->left, clipRect->top, level * 2, "", this, mName.string(), clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); #endif renderer.startMark(mName.string()); int restoreTo = renderer.getSaveCount(); handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), PROPERTY_SAVECOUNT); int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "", DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); drawGlStatus |= setViewProperties(renderer, dirty, flags, level, deferredList); setViewProperties<T>(renderer, handler, level + 1); if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) { DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo); renderer.restoreToCount(restoreTo); renderer.endMark(); return drawGlStatus; handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT); return; } DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); int saveCount = renderer.getSaveCount() - 1; for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { DisplayListOp *op = mDisplayListData->displayListOps[i]; #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark(strlen(op->name()), op->name()); #endif drawGlStatus |= op->replay(renderer, dirty, flags, saveCount, level, mCaching, mMultipliedAlpha, deferredList); handler(op, saveCount); logBuffer.writeCommand(level, op->name()); } DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo); DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT); renderer.restoreToCount(restoreTo); renderer.endMark(); DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", (level + 1) * 2, "", this, mName.string(), drawGlStatus); if (!level && CC_LIKELY(deferredList)) { drawGlStatus |= deferredList->flush(renderer, dirty, flags, level); } return drawGlStatus; } }; // namespace uirenderer Loading Loading
libs/hwui/Caches.cpp +2 −7 Original line number Diff line number Diff line Loading @@ -103,14 +103,9 @@ void Caches::initFont() { void Caches::initExtensions() { if (mExtensions.hasDebugMarker()) { eventMark = glInsertEventMarkerEXT; if ((drawDeferDisabled || drawReorderDisabled)) { startMark = glPushGroupMarkerEXT; endMark = glPopGroupMarkerEXT; } else { startMark = startMarkNull; endMark = endMarkNull; } } else { eventMark = eventMarkNull; startMark = startMarkNull; Loading
libs/hwui/DeferredDisplayList.cpp +243 −37 Original line number Diff line number Diff line Loading @@ -32,15 +32,15 @@ namespace android { namespace uirenderer { ///////////////////////////////////////////////////////////////////////////////// // Operation Batches ///////////////////////////////////////////////////////////////////////////////// class DrawOpBatch { public: DrawOpBatch() { mOps.clear(); } DrawOpBatch() { mOps.clear(); } ~DrawOpBatch() { mOps.clear(); } virtual ~DrawOpBatch() { mOps.clear(); } void add(DrawOp* op) { // NOTE: ignore empty bounds special case, since we don't merge across those ops Loading @@ -48,8 +48,9 @@ public: mOps.add(op); } bool intersects(Rect& rect) { virtual 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 Loading @@ -64,27 +65,217 @@ public: return false; } Vector<DrawOp*> mOps; virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) { DEFER_LOGD("replaying draw batch %p", this); status_t status = DrawGlInfo::kStatusDone; DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); for (unsigned int i = 0; i < mOps.size(); i++) { DrawOp* op = mOps[i]; renderer.restoreDisplayState(op->state, kStateDeferFlag_Draw); #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark(strlen(op->name()), op->name()); #endif status |= op->applyDraw(renderer, dirty, 0, op->state.mMultipliedAlpha); logBuffer.writeCommand(0, op->name()); } return status; } inline int count() const { return mOps.size(); } private: Vector<DrawOp*> mOps; Rect mBounds; }; void DeferredDisplayList::clear() { class StateOpBatch : public DrawOpBatch { 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) { DEFER_LOGD("replaying state op batch %p", this); renderer.restoreDisplayState(mOp->state, 0); // use invalid save count because it won't be used at flush time - RestoreToCountOp is the // only one to use it, and we don't use that class at flush time, instead calling // renderer.restoreToCount directly int saveCount = -1; mOp->applyState(renderer, saveCount); return DrawGlInfo::kStatusDone; } private: StateOp* mOp; }; class RestoreToCountBatch : public DrawOpBatch { public: RestoreToCountBatch(int restoreCount) : 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) { DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount); renderer.restoreToCount(mRestoreCount); return DrawGlInfo::kStatusDone; } private: /* * The count used here represents the flush() time saveCount. This is as opposed to the * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and * (saveCount + mCount) respectively). Since the count is different from the original * RestoreToCountOp, we don't store a pointer to the op, as elsewhere. */ const int mRestoreCount; }; ///////////////////////////////////////////////////////////////////////////////// // DeferredDisplayList ///////////////////////////////////////////////////////////////////////////////// void DeferredDisplayList::resetBatchingState() { for (int i = 0; i < kOpBatch_Count; i++) { mBatchIndices[i] = -1; } } void DeferredDisplayList::clear() { resetBatchingState(); mComplexClipStackStart = -1; for (unsigned int i = 0; i < mBatches.size(); i++) { delete mBatches[i]; } mBatches.clear(); mSaveStack.clear(); } void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) { if (CC_UNLIKELY(disallowReorder)) { if (!mBatches.isEmpty()) { mBatches[0]->add(op); ///////////////////////////////////////////////////////////////////////////////// // Operation adding ///////////////////////////////////////////////////////////////////////////////// int DeferredDisplayList::getStateOpDeferFlags() const { // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save // the clip if we aren't recording a complex clip (and can thus trust it to be a rect) return recordingComplexClip() ? 0 : kStateDeferFlag_Clip; } int DeferredDisplayList::getDrawOpDeferFlags() const { return kStateDeferFlag_Draw | getStateOpDeferFlags(); } /** * When an clipping operation occurs that could cause a complex clip, record the operation and all * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading * the clip from deferred state, we play back all of the relevant state operations that generated * the complex clip. * * Note that we don't need to record the associated restore operation, since operations at defer * time record whether they should store the renderer's current clip */ void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) { if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) { DEFER_LOGD("%p Received complex clip operation %p", this, op); // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded storeStateOpBarrier(renderer, op); if (!recordingComplexClip()) { mComplexClipStackStart = renderer.getSaveCount() - 1; DEFER_LOGD(" Starting complex clip region, start is %d", mComplexClipStackStart); } } } /** * For now, we record save layer operations as barriers in the batch list, preventing drawing * operations from reordering around the saveLayer and it's associated restore() * * In the future, we should send saveLayer commands (if they can be played out of order) and their * contained drawing operations to a seperate list of batches, so that they may draw at the * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame. * * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set. */ void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount) { DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d", this, op, op->getFlags(), newSaveCount); storeStateOpBarrier(renderer, op); mSaveStack.push(newSaveCount); } /** * Takes save op and it's return value - the new save count - and stores it into the stream as a * barrier if it's needed to properly modify a complex clip */ void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) { int saveFlags = op->getFlags(); DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount); if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) { // store and replay the save operation, as it may be needed to correctly playback the clip DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount); storeStateOpBarrier(renderer, op); mSaveStack.push(newSaveCount); } } /** * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw * the layer in the deferred list * * other save() commands which occur as children of a snapshot with complex clip will be deferred, * and must be restored * * Either will act as a barrier to draw operation reordering, as we want to play back layer * save/restore and complex canvas modifications (including save/restore) in order. */ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount) { DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount); if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) { mComplexClipStackStart = -1; resetBatchingState(); } if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) { return; } while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop(); storeRestoreToCountBarrier(mSaveStack.size() + 1); } void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (renderer.storeDisplayState(op->state, getDrawOpDeferFlags())) { return; // quick rejected } op->onDrawOpDeferred(renderer); if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) { // TODO: elegant way to reuse batches? DrawOpBatch* b = new DrawOpBatch(); b->add(op); mBatches.add(b); Loading Loading @@ -138,9 +329,41 @@ void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) { targetBatch->add(op); } status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) { ATRACE_CALL(); void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) { DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size()); renderer.storeDisplayState(op->state, getStateOpDeferFlags()); mBatches.add(new StateOpBatch(op)); resetBatchingState(); } void DeferredDisplayList::storeRestoreToCountBarrier(int newSaveCount) { DEFER_LOGD("%p adding restore to count %d barrier, pos %d", this, newSaveCount, mBatches.size()); mBatches.add(new RestoreToCountBatch(newSaveCount)); resetBatchingState(); } ///////////////////////////////////////////////////////////////////////////////// // Replay / flush ///////////////////////////////////////////////////////////////////////////////// static status_t replayBatchList(Vector<DrawOpBatch*>& 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(); } DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount); return status; } status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { ATRACE_NAME("flush drawing commands"); status_t status = DrawGlInfo::kStatusDone; if (isEmpty()) return status; // nothing to flush Loading @@ -148,29 +371,12 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32 DEFER_LOGD("--flushing"); renderer.eventMark("Flush"); 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.restoreToCount(1); 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++; } } status |= replayBatchList(mBatches, renderer, dirty); DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount); DEFER_LOGD("--flush complete, returning %x", status); renderer.restoreToCount(restoreTo); renderer.setDrawModifiers(restoreDrawModifiers); clear(); return status; } Loading
libs/hwui/DeferredDisplayList.h +31 −4 Original line number Diff line number Diff line Loading @@ -26,10 +26,13 @@ namespace android { namespace uirenderer { class ClipOp; class DrawOp; class SaveOp; class SaveLayerOp; class StateOp; class DrawOpBatch; class OpenGLRenderer; class SkiaShader; class DeferredDisplayList { public: Loading @@ -55,18 +58,42 @@ public: * Plays back all of the draw ops recorded into batches to the renderer. * Adjusts the state of the renderer as necessary, and restores it when complete */ status_t flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level); status_t flush(OpenGLRenderer& renderer, Rect& dirty); void addClip(OpenGLRenderer& renderer, ClipOp* op); void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount); void addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount); void addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount); /** * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if * disallowReorder is false, respecting draw order when overlaps occur */ void add(DrawOp* op, bool disallowReorder); void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); private: /* * Resets the batching back-pointers, creating a barrier in the operation stream so that no ops * added in the future will be inserted into a batch that already exist. */ void resetBatchingState(); void clear(); void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op); void storeRestoreToCountBarrier(int newSaveCount); bool recordingComplexClip() const { return mComplexClipStackStart >= 0; } int getStateOpDeferFlags() const; int getDrawOpDeferFlags() const; /* * * at defer time, stores the savecount of save/saveLayer ops that were */ Vector<int> mSaveStack; int mComplexClipStackStart; Vector<DrawOpBatch*> mBatches; int mBatchIndices[kOpBatch_Count]; Loading
libs/hwui/DisplayList.cpp +103 −48 Original line number Diff line number Diff line Loading @@ -61,6 +61,12 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) { void DisplayList::clearResources() { mDisplayListData = NULL; mClipRectOp = NULL; mSaveLayerOp = NULL; mSaveOp = NULL; mRestoreToCountOp = NULL; delete mTransformMatrix; delete mTransformCamera; delete mTransformMatrix3D; Loading Loading @@ -156,6 +162,13 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde return; } // allocate reusable ops for state-deferral LinearAllocator& alloc = mDisplayListData->allocator; mClipRectOp = new (alloc) ClipRectOp(); mSaveLayerOp = new (alloc) SaveLayerOp(); mSaveOp = new (alloc) SaveOp(); mRestoreToCountOp = new (alloc) RestoreToCountOp(); mFunctorCount = recorder.getFunctorCount(); Caches& caches = Caches::getInstance(); Loading Loading @@ -318,7 +331,7 @@ void DisplayList::updateMatrix() { } } void DisplayList::outputViewProperties(uint32_t level) { void DisplayList::outputViewProperties(const int level) { updateMatrix(); if (mLeft != 0 || mTop != 0) { ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop); Loading Loading @@ -358,10 +371,17 @@ void DisplayList::outputViewProperties(uint32_t level) { } } status_t DisplayList::setViewProperties(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level, DeferredDisplayList* deferredList) { status_t status = DrawGlInfo::kStatusDone; #if DEBUG_DISPLAYLIST /* * For property operations, we pass a savecount of 0, since the operations aren't part of the * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in * base saveCount (i.e., how RestoreToCount uses saveCount + mCount) */ #define PROPERTY_SAVECOUNT 0 template <class T> void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, const int level) { #if DEBUG_DISPLAY_LIST outputViewProperties(level); #endif updateMatrix(); Loading @@ -381,86 +401,121 @@ status_t DisplayList::setViewProperties(OpenGLRenderer& renderer, Rect& dirty, } } if (mAlpha < 1 && !mCaching) { if (deferredList) { // flush since we'll either enter a Layer, or set alpha, both not supported in deferral status |= deferredList->flush(renderer, dirty, flags, level); } if (!mHasOverlappingRendering) { renderer.setAlpha(mAlpha); } else { // TODO: should be able to store the size of a DL at record time and not // have to pass it into this call. In fact, this information might be in the // location/size info that we store with the new native transform data. int flags = SkCanvas::kHasAlphaLayer_SaveFlag; int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag; if (mClipChildren) { flags |= SkCanvas::kClipToLayer_SaveFlag; saveFlags |= SkCanvas::kClipToLayer_SaveFlag; } renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop, mMultipliedAlpha, flags); handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, mMultipliedAlpha, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT); } } if (mClipChildren && !mCaching) { if (deferredList && CC_UNLIKELY(!renderer.hasRectToRectTransform())) { // flush, since clip will likely be a region status |= deferredList->flush(renderer, dirty, flags, level); handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op), PROPERTY_SAVECOUNT); } } class DeferOperationHandler { public: DeferOperationHandler(DeferStateStruct& deferStruct, int multipliedAlpha, int level) : mDeferStruct(deferStruct), mMultipliedAlpha(multipliedAlpha), mLevel(level) {} inline void operator()(DisplayListOp* operation, int saveCount) { operation->defer(mDeferStruct, saveCount, mLevel, mMultipliedAlpha); } renderer.clipRect(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op); private: DeferStateStruct& mDeferStruct; const int mMultipliedAlpha; const int mLevel; }; void DisplayList::defer(DeferStateStruct& deferStruct, const int level) { DeferOperationHandler handler(deferStruct, mCaching ? mMultipliedAlpha : -1, level); iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level); } class ReplayOperationHandler { public: ReplayOperationHandler(ReplayStateStruct& replayStruct, int multipliedAlpha, int level) : mReplayStruct(replayStruct), mMultipliedAlpha(multipliedAlpha), mLevel(level) {} inline void operator()(DisplayListOp* operation, int saveCount) { #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS replayStruct.mRenderer.eventMark(operation->name()); #endif operation->replay(mReplayStruct, saveCount, mLevel, mMultipliedAlpha); } return status; private: ReplayStateStruct& mReplayStruct; const int mMultipliedAlpha; const int mLevel; }; void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) { ReplayOperationHandler handler(replayStruct, mCaching ? mMultipliedAlpha : -1, level); replayStruct.mRenderer.startMark(mName.string()); iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level); replayStruct.mRenderer.endMark(); DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(), replayStruct.mDrawGlStatus); } status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level, DeferredDisplayList* deferredList) { status_t drawGlStatus = DrawGlInfo::kStatusDone; /** * This function serves both defer and replay modes, and will organize the displayList's component * operations for a single frame: * * Every 'simple' operation that affects just the matrix and alpha (or other factors of * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom * defer logic) and operations in displayListOps are issued through the 'handler' which handles the * defer vs replay logic, per operation */ template <class T> void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) { if (mSize == 0 || mAlpha <= 0) { DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string()); return; } #if DEBUG_DISPLAY_LIST Rect* clipRect = renderer.getClipRect(); DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f", (level+1)*2, "", this, mName.string(), clipRect->left, clipRect->top, level * 2, "", this, mName.string(), clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); #endif renderer.startMark(mName.string()); int restoreTo = renderer.getSaveCount(); handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), PROPERTY_SAVECOUNT); int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "", DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); drawGlStatus |= setViewProperties(renderer, dirty, flags, level, deferredList); setViewProperties<T>(renderer, handler, level + 1); if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) { DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo); renderer.restoreToCount(restoreTo); renderer.endMark(); return drawGlStatus; handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT); return; } DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); int saveCount = renderer.getSaveCount() - 1; for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { DisplayListOp *op = mDisplayListData->displayListOps[i]; #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark(strlen(op->name()), op->name()); #endif drawGlStatus |= op->replay(renderer, dirty, flags, saveCount, level, mCaching, mMultipliedAlpha, deferredList); handler(op, saveCount); logBuffer.writeCommand(level, op->name()); } DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo); DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT); renderer.restoreToCount(restoreTo); renderer.endMark(); DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", (level + 1) * 2, "", this, mName.string(), drawGlStatus); if (!level && CC_LIKELY(deferredList)) { drawGlStatus |= deferredList->flush(renderer, dirty, flags, level); } return drawGlStatus; } }; // namespace uirenderer Loading