OSDN Git Service

DO NOT MERGE. KEY_INTENT shouldn't grant permissions. am: 1f2a5d3622 -s ours am...
[android-x86/frameworks-base.git] / libs / hwui / DeferredDisplayList.cpp
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <utils/Trace.h>
18 #include <ui/Rect.h>
19 #include <ui/Region.h>
20
21 #include "Caches.h"
22 #include "Debug.h"
23 #include "DeferredDisplayList.h"
24 #include "DisplayListOp.h"
25 #include "OpenGLRenderer.h"
26 #include "Properties.h"
27 #include "utils/MathUtils.h"
28
29 #if DEBUG_DEFER
30     #define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
31 #else
32     #define DEFER_LOGD(...)
33 #endif
34
35 namespace android {
36 namespace uirenderer {
37
38 // Depth of the save stack at the beginning of batch playback at flush time
39 #define FLUSH_SAVE_STACK_DEPTH 2
40
41 #define DEBUG_COLOR_BARRIER          0x1f000000
42 #define DEBUG_COLOR_MERGEDBATCH      0x5f7f7fff
43 #define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
44
45 static bool avoidOverdraw() {
46     // Don't avoid overdraw when visualizing it, since that makes it harder to
47     // debug where it's coming from, and when the problem occurs.
48     return !Properties::debugOverdraw;
49 };
50
51 /////////////////////////////////////////////////////////////////////////////////
52 // Operation Batches
53 /////////////////////////////////////////////////////////////////////////////////
54
55 class Batch {
56 public:
57     virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
58     virtual ~Batch() {}
59     virtual bool purelyDrawBatch() { return false; }
60     virtual bool coversBounds(const Rect& bounds) { return false; }
61 };
62
63 class DrawBatch : public Batch {
64 public:
65     DrawBatch(const DeferInfo& deferInfo) : mAllOpsOpaque(true),
66             mBatchId(deferInfo.batchId), mMergeId(deferInfo.mergeId) {
67         mOps.clear();
68     }
69
70     virtual ~DrawBatch() { mOps.clear(); }
71
72     virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) {
73         // NOTE: ignore empty bounds special case, since we don't merge across those ops
74         mBounds.unionWith(state->mBounds);
75         mAllOpsOpaque &= opaqueOverBounds;
76         mOps.push_back(OpStatePair(op, state));
77     }
78
79     bool intersects(const Rect& rect) {
80         if (!rect.intersects(mBounds)) return false;
81
82         for (unsigned int i = 0; i < mOps.size(); i++) {
83             if (rect.intersects(mOps[i].state->mBounds)) {
84 #if DEBUG_DEFER
85                 DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i].op,
86                         mOps[i].state->mBounds.left, mOps[i].state->mBounds.top,
87                         mOps[i].state->mBounds.right, mOps[i].state->mBounds.bottom);
88                 mOps[i].op->output(2);
89 #endif
90                 return true;
91             }
92         }
93         return false;
94     }
95
96     virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
97         DEFER_LOGD("%d  replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)",
98                 index, this, mOps.size(), getBatchId(), getMergeId());
99
100         for (unsigned int i = 0; i < mOps.size(); i++) {
101             DrawOp* op = mOps[i].op;
102             const DeferredDisplayState* state = mOps[i].state;
103             renderer.restoreDisplayState(*state);
104
105 #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
106             renderer.eventMark(op->name());
107 #endif
108             op->applyDraw(renderer, dirty);
109
110 #if DEBUG_MERGE_BEHAVIOR
111             const Rect& bounds = state->mBounds;
112             int batchColor = 0x1f000000;
113             if (getBatchId() & 0x1) batchColor |= 0x0000ff;
114             if (getBatchId() & 0x2) batchColor |= 0x00ff00;
115             if (getBatchId() & 0x4) batchColor |= 0xff0000;
116             renderer.drawScreenSpaceColorRect(bounds.left, bounds.top, bounds.right, bounds.bottom,
117                     batchColor);
118 #endif
119         }
120     }
121
122     virtual bool purelyDrawBatch() override { return true; }
123
124     virtual bool coversBounds(const Rect& bounds) override {
125         if (CC_LIKELY(!mAllOpsOpaque || !mBounds.contains(bounds) || count() == 1)) return false;
126
127         Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom));
128         for (unsigned int i = 0; i < mOps.size(); i++) {
129             const Rect &r = mOps[i].state->mBounds;
130             uncovered.subtractSelf(android::Rect(r.left, r.top, r.right, r.bottom));
131         }
132         return uncovered.isEmpty();
133     }
134
135     inline int getBatchId() const { return mBatchId; }
136     inline mergeid_t getMergeId() const { return mMergeId; }
137     inline int count() const { return mOps.size(); }
138
139 protected:
140     std::vector<OpStatePair> mOps;
141     Rect mBounds; // union of bounds of contained ops
142 private:
143     bool mAllOpsOpaque;
144     int mBatchId;
145     mergeid_t mMergeId;
146 };
147
148 class MergingDrawBatch : public DrawBatch {
149 public:
150     MergingDrawBatch(DeferInfo& deferInfo, int width, int height) :
151             DrawBatch(deferInfo), mClipRect(width, height),
152             mClipSideFlags(kClipSide_None) {}
153
154     /*
155      * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
156      * and clip side flags. Positive bounds delta means new bounds fit in old.
157      */
158     static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
159             float boundsDelta) {
160         bool currentClipExists = currentFlags & side;
161         bool newClipExists = newFlags & side;
162
163         // if current is clipped, we must be able to fit new bounds in current
164         if (boundsDelta > 0 && currentClipExists) return false;
165
166         // if new is clipped, we must be able to fit current bounds in new
167         if (boundsDelta < 0 && newClipExists) return false;
168
169         return true;
170     }
171
172     /*
173      * Checks if a (mergeable) op can be merged into this batch
174      *
175      * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
176      * important to consider all paint attributes used in the draw calls in deciding both a) if an
177      * op tries to merge at all, and b) if the op can merge with another set of ops
178      *
179      * False positives can lead to information from the paints of subsequent merged operations being
180      * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
181      */
182     bool canMergeWith(const DrawOp* op, const DeferredDisplayState* state) {
183         bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
184                 getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
185
186         // Overlapping other operations is only allowed for text without shadow. For other ops,
187         // multiDraw isn't guaranteed to overdraw correctly
188         if (!isTextBatch || op->hasTextShadow()) {
189             if (intersects(state->mBounds)) return false;
190         }
191         const DeferredDisplayState* lhs = state;
192         const DeferredDisplayState* rhs = mOps[0].state;
193
194         if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false;
195
196         // Identical round rect clip state means both ops will clip in the same way, or not at all.
197         // As the state objects are const, we can compare their pointers to determine mergeability
198         if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false;
199         if (lhs->mProjectionPathMask != rhs->mProjectionPathMask) return false;
200
201         /* Clipping compatibility check
202          *
203          * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
204          * clip for that side.
205          */
206         const int currentFlags = mClipSideFlags;
207         const int newFlags = state->mClipSideFlags;
208         if (currentFlags != kClipSide_None || newFlags != kClipSide_None) {
209             const Rect& opBounds = state->mBounds;
210             float boundsDelta = mBounds.left - opBounds.left;
211             if (!checkSide(currentFlags, newFlags, kClipSide_Left, boundsDelta)) return false;
212             boundsDelta = mBounds.top - opBounds.top;
213             if (!checkSide(currentFlags, newFlags, kClipSide_Top, boundsDelta)) return false;
214
215             // right and bottom delta calculation reversed to account for direction
216             boundsDelta = opBounds.right - mBounds.right;
217             if (!checkSide(currentFlags, newFlags, kClipSide_Right, boundsDelta)) return false;
218             boundsDelta = opBounds.bottom - mBounds.bottom;
219             if (!checkSide(currentFlags, newFlags, kClipSide_Bottom, boundsDelta)) return false;
220         }
221
222         // if paints are equal, then modifiers + paint attribs don't need to be compared
223         if (op->mPaint == mOps[0].op->mPaint) return true;
224
225         if (PaintUtils::getAlphaDirect(op->mPaint)
226                 != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) {
227             return false;
228         }
229
230         if (op->mPaint && mOps[0].op->mPaint &&
231             op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
232             return false;
233         }
234
235         if (op->mPaint && mOps[0].op->mPaint &&
236             op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) {
237             return false;
238         }
239
240         return true;
241     }
242
243     virtual void add(DrawOp* op, const DeferredDisplayState* state,
244             bool opaqueOverBounds) override {
245         DrawBatch::add(op, state, opaqueOverBounds);
246
247         const int newClipSideFlags = state->mClipSideFlags;
248         mClipSideFlags |= newClipSideFlags;
249         if (newClipSideFlags & kClipSide_Left) mClipRect.left = state->mClip.left;
250         if (newClipSideFlags & kClipSide_Top) mClipRect.top = state->mClip.top;
251         if (newClipSideFlags & kClipSide_Right) mClipRect.right = state->mClip.right;
252         if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = state->mClip.bottom;
253     }
254
255     virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
256         DEFER_LOGD("%d  replaying MergingDrawBatch %p, with %d ops,"
257                 " clip flags %x (batch id %x, merge id %p)",
258                 index, this, mOps.size(), mClipSideFlags, getBatchId(), getMergeId());
259         if (mOps.size() == 1) {
260             DrawBatch::replay(renderer, dirty, -1);
261             return;
262         }
263
264         // clipping in the merged case is done ahead of time since all ops share the clip (if any)
265         renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : nullptr);
266
267         DrawOp* op = mOps[0].op;
268 #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
269         renderer.eventMark("multiDraw");
270         renderer.eventMark(op->name());
271 #endif
272         op->multiDraw(renderer, dirty, mOps, mBounds);
273
274 #if DEBUG_MERGE_BEHAVIOR
275         renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom,
276                 DEBUG_COLOR_MERGEDBATCH);
277 #endif
278     }
279
280 private:
281     /*
282      * Contains the effective clip rect shared by all merged ops. Initialized to the layer viewport,
283      * it will shrink if an op must be clipped on a certain side. The clipped sides are reflected in
284      * mClipSideFlags.
285      */
286     Rect mClipRect;
287     int mClipSideFlags;
288 };
289
290 class StateOpBatch : public Batch {
291 public:
292     // creates a single operation batch
293     StateOpBatch(const StateOp* op, const DeferredDisplayState* state) : mOp(op), mState(state) {}
294
295     virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
296         DEFER_LOGD("replaying state op batch %p", this);
297         renderer.restoreDisplayState(*mState);
298
299         // use invalid save count because it won't be used at flush time - RestoreToCountOp is the
300         // only one to use it, and we don't use that class at flush time, instead calling
301         // renderer.restoreToCount directly
302         int saveCount = -1;
303         mOp->applyState(renderer, saveCount);
304     }
305
306 private:
307     const StateOp* mOp;
308     const DeferredDisplayState* mState;
309 };
310
311 class RestoreToCountBatch : public Batch {
312 public:
313     RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) :
314             mState(state), mRestoreCount(restoreCount) {}
315
316     virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
317         DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
318
319         renderer.restoreDisplayState(*mState);
320         renderer.restoreToCount(mRestoreCount);
321     }
322
323 private:
324     // we use the state storage for the RestoreToCountOp, but don't replay the op itself
325     const DeferredDisplayState* mState;
326
327     /*
328      * The count used here represents the flush() time saveCount. This is as opposed to the
329      * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and
330      * (saveCount + mCount) respectively). Since the count is different from the original
331      * RestoreToCountOp, we don't store a pointer to the op, as elsewhere.
332      */
333     const int mRestoreCount;
334 };
335
336 #if DEBUG_MERGE_BEHAVIOR
337 class BarrierDebugBatch : public Batch {
338     virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
339         renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER);
340     }
341 };
342 #endif
343
344 /////////////////////////////////////////////////////////////////////////////////
345 // DeferredDisplayList
346 /////////////////////////////////////////////////////////////////////////////////
347
348 void DeferredDisplayList::resetBatchingState() {
349     for (int i = 0; i < kOpBatch_Count; i++) {
350         mBatchLookup[i] = nullptr;
351         mMergingBatches[i].clear();
352     }
353 #if DEBUG_MERGE_BEHAVIOR
354     if (mBatches.size() != 0) {
355         mBatches.add(new BarrierDebugBatch());
356     }
357 #endif
358     mEarliestBatchIndex = mBatches.size();
359 }
360
361 void DeferredDisplayList::clear() {
362     resetBatchingState();
363     mComplexClipStackStart = -1;
364
365     for (unsigned int i = 0; i < mBatches.size(); i++) {
366         delete mBatches[i];
367     }
368     mBatches.clear();
369     mSaveStack.clear();
370     mEarliestBatchIndex = 0;
371     mEarliestUnclearedIndex = 0;
372 }
373
374 /////////////////////////////////////////////////////////////////////////////////
375 // Operation adding
376 /////////////////////////////////////////////////////////////////////////////////
377
378 int DeferredDisplayList::getStateOpDeferFlags() const {
379     // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save
380     // the clip if we aren't recording a complex clip (and can thus trust it to be a rect)
381     return recordingComplexClip() ? 0 : kStateDeferFlag_Clip;
382 }
383
384 int DeferredDisplayList::getDrawOpDeferFlags() const {
385     return kStateDeferFlag_Draw | getStateOpDeferFlags();
386 }
387
388 /**
389  * When an clipping operation occurs that could cause a complex clip, record the operation and all
390  * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading
391  * the clip from deferred state, we play back all of the relevant state operations that generated
392  * the complex clip.
393  *
394  * Note that we don't need to record the associated restore operation, since operations at defer
395  * time record whether they should store the renderer's current clip
396  */
397 void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) {
398     if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) {
399         DEFER_LOGD("%p Received complex clip operation %p", this, op);
400
401         // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded
402         storeStateOpBarrier(renderer, op);
403
404         if (!recordingComplexClip()) {
405             mComplexClipStackStart = renderer.getSaveCount() - 1;
406             DEFER_LOGD("    Starting complex clip region, start is %d", mComplexClipStackStart);
407         }
408     }
409 }
410
411 /**
412  * For now, we record save layer operations as barriers in the batch list, preventing drawing
413  * operations from reordering around the saveLayer and it's associated restore()
414  *
415  * In the future, we should send saveLayer commands (if they can be played out of order) and their
416  * contained drawing operations to a seperate list of batches, so that they may draw at the
417  * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame.
418  *
419  * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a
420  * complex clip, and if the flags (SaveFlags::Clip & SaveFlags::ClipToLayer) are set.
421  */
422 void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer,
423         SaveLayerOp* op, int newSaveCount) {
424     DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d",
425             this, op, op->getFlags(), newSaveCount);
426
427     storeStateOpBarrier(renderer, op);
428     mSaveStack.push_back(newSaveCount);
429 }
430
431 /**
432  * Takes save op and it's return value - the new save count - and stores it into the stream as a
433  * barrier if it's needed to properly modify a complex clip
434  */
435 void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) {
436     int saveFlags = op->getFlags();
437     DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount);
438
439     if (recordingComplexClip() && (saveFlags & SaveFlags::Clip)) {
440         // store and replay the save operation, as it may be needed to correctly playback the clip
441         DEFER_LOGD("    adding save barrier with new save count %d", newSaveCount);
442         storeStateOpBarrier(renderer, op);
443         mSaveStack.push_back(newSaveCount);
444     }
445 }
446
447 /**
448  * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw
449  * the layer in the deferred list
450  *
451  * other save() commands which occur as children of a snapshot with complex clip will be deferred,
452  * and must be restored
453  *
454  * Either will act as a barrier to draw operation reordering, as we want to play back layer
455  * save/restore and complex canvas modifications (including save/restore) in order.
456  */
457 void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op,
458         int newSaveCount) {
459     DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount);
460
461     if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) {
462         mComplexClipStackStart = -1;
463         resetBatchingState();
464     }
465
466     if (mSaveStack.empty() || newSaveCount > mSaveStack.back()) {
467         return;
468     }
469
470     while (!mSaveStack.empty() && mSaveStack.back() >= newSaveCount) mSaveStack.pop_back();
471
472     storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH);
473 }
474
475 void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
476     /* 1: op calculates local bounds */
477     DeferredDisplayState* const state = createState();
478     if (op->getLocalBounds(state->mBounds)) {
479         if (state->mBounds.isEmpty()) {
480             // valid empty bounds, don't bother deferring
481             tryRecycleState(state);
482             return;
483         }
484     } else {
485         state->mBounds.setEmpty();
486     }
487
488     /* 2: renderer calculates global bounds + stores state */
489     if (renderer.storeDisplayState(*state, getDrawOpDeferFlags())) {
490         tryRecycleState(state);
491         return; // quick rejected
492     }
493
494     /* 3: ask op for defer info, given renderer state */
495     DeferInfo deferInfo;
496     op->onDefer(renderer, deferInfo, *state);
497
498     // complex clip has a complex set of expectations on the renderer state - for now, avoid taking
499     // the merge path in those cases
500     deferInfo.mergeable &= !recordingComplexClip();
501     deferInfo.opaqueOverBounds &= !recordingComplexClip()
502             && mSaveStack.empty()
503             && !state->mRoundRectClipState;
504
505     if (CC_LIKELY(avoidOverdraw()) && mBatches.size() &&
506             state->mClipSideFlags != kClipSide_ConservativeFull &&
507             deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
508         // avoid overdraw by resetting drawing state + discarding drawing ops
509         discardDrawingBatches(mBatches.size() - 1);
510         resetBatchingState();
511     }
512
513     if (CC_UNLIKELY(Properties::drawReorderDisabled)) {
514         // TODO: elegant way to reuse batches?
515         DrawBatch* b = new DrawBatch(deferInfo);
516         b->add(op, state, deferInfo.opaqueOverBounds);
517         mBatches.push_back(b);
518         return;
519     }
520
521     // find the latest batch of the new op's type, and try to merge the new op into it
522     DrawBatch* targetBatch = nullptr;
523
524     // insertion point of a new batch, will hopefully be immediately after similar batch
525     // (eventually, should be similar shader)
526     int insertBatchIndex = mBatches.size();
527     if (!mBatches.empty()) {
528         if (state->mBounds.isEmpty()) {
529             // don't know the bounds for op, so create new batch and start from scratch on next op
530             DrawBatch* b = new DrawBatch(deferInfo);
531             b->add(op, state, deferInfo.opaqueOverBounds);
532             mBatches.push_back(b);
533             resetBatchingState();
534 #if DEBUG_DEFER
535             DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
536             op->output(2);
537 #endif
538             return;
539         }
540
541         if (deferInfo.mergeable) {
542             // Try to merge with any existing batch with same mergeId.
543             std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch
544                     = mMergingBatches[deferInfo.batchId];
545             auto getResult = mergingBatch.find(deferInfo.mergeId);
546             if (getResult != mergingBatch.end()) {
547                 targetBatch = getResult->second;
548                 if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
549                     targetBatch = nullptr;
550                 }
551             }
552         } else {
553             // join with similar, non-merging batch
554             targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
555         }
556
557         if (targetBatch || deferInfo.mergeable) {
558             // iterate back toward target to see if anything drawn since should overlap the new op
559             // if no target, merging ops still interate to find similar batch to insert after
560             for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
561                 DrawBatch* overBatch = (DrawBatch*)mBatches[i];
562
563                 if (overBatch == targetBatch) break;
564
565                 // TODO: also consider shader shared between batch types
566                 if (deferInfo.batchId == overBatch->getBatchId()) {
567                     insertBatchIndex = i + 1;
568                     if (!targetBatch) break; // found insert position, quit
569                 }
570
571                 if (overBatch->intersects(state->mBounds)) {
572                     // NOTE: it may be possible to optimize for special cases where two operations
573                     // of the same batch/paint could swap order, such as with a non-mergeable
574                     // (clipped) and a mergeable text operation
575                     targetBatch = nullptr;
576 #if DEBUG_DEFER
577                     DEFER_LOGD("op couldn't join batch %p, was intersected by batch %d",
578                             targetBatch, i);
579                     op->output(2);
580 #endif
581                     break;
582                 }
583             }
584         }
585     }
586
587     if (!targetBatch) {
588         if (deferInfo.mergeable) {
589             targetBatch = new MergingDrawBatch(deferInfo,
590                     renderer.getViewportWidth(), renderer.getViewportHeight());
591             mMergingBatches[deferInfo.batchId].insert(
592                     std::make_pair(deferInfo.mergeId, targetBatch));
593         } else {
594             targetBatch = new DrawBatch(deferInfo);
595             mBatchLookup[deferInfo.batchId] = targetBatch;
596         }
597
598         DEFER_LOGD("creating %singBatch %p, bid %x, at %d",
599                 deferInfo.mergeable ? "Merg" : "Draw",
600                 targetBatch, deferInfo.batchId, insertBatchIndex);
601         mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
602     }
603
604     targetBatch->add(op, state, deferInfo.opaqueOverBounds);
605 }
606
607 void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
608     DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size());
609
610     DeferredDisplayState* state = createState();
611     renderer.storeDisplayState(*state, getStateOpDeferFlags());
612     mBatches.push_back(new StateOpBatch(op, state));
613     resetBatchingState();
614 }
615
616 void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op,
617         int newSaveCount) {
618     DEFER_LOGD("%p adding restore to count %d barrier, pos %d",
619             this, newSaveCount, mBatches.size());
620
621     // store displayState for the restore operation, as it may be associated with a saveLayer that
622     // doesn't have SaveFlags::Clip set
623     DeferredDisplayState* state = createState();
624     renderer.storeDisplayState(*state, getStateOpDeferFlags());
625     mBatches.push_back(new RestoreToCountBatch(op, state, newSaveCount));
626     resetBatchingState();
627 }
628
629 /////////////////////////////////////////////////////////////////////////////////
630 // Replay / flush
631 /////////////////////////////////////////////////////////////////////////////////
632
633 static void replayBatchList(const std::vector<Batch*>& batchList,
634         OpenGLRenderer& renderer, Rect& dirty) {
635
636     for (unsigned int i = 0; i < batchList.size(); i++) {
637         if (batchList[i]) {
638             batchList[i]->replay(renderer, dirty, i);
639         }
640     }
641     DEFER_LOGD("--flushed, drew %d batches", batchList.size());
642 }
643
644 void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
645     ATRACE_NAME("flush drawing commands");
646     Caches::getInstance().fontRenderer.endPrecaching();
647
648     if (isEmpty()) return; // nothing to flush
649     renderer.restoreToCount(1);
650
651     DEFER_LOGD("--flushing");
652     renderer.eventMark("Flush");
653
654     // save and restore so that reordering doesn't affect final state
655     renderer.save(SaveFlags::MatrixClip);
656
657     if (CC_LIKELY(avoidOverdraw())) {
658         for (unsigned int i = 1; i < mBatches.size(); i++) {
659             if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
660                 discardDrawingBatches(i - 1);
661             }
662         }
663     }
664     // NOTE: depth of the save stack at this point, before playback, should be reflected in
665     // FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly
666     replayBatchList(mBatches, renderer, dirty);
667
668     renderer.restoreToCount(1);
669
670     DEFER_LOGD("--flush complete, returning %x", status);
671     clear();
672 }
673
674 void DeferredDisplayList::discardDrawingBatches(const unsigned int maxIndex) {
675     for (unsigned int i = mEarliestUnclearedIndex; i <= maxIndex; i++) {
676         // leave deferred state ops alone for simplicity (empty save restore pairs may now exist)
677         if (mBatches[i] && mBatches[i]->purelyDrawBatch()) {
678             delete mBatches[i];
679             mBatches[i] = nullptr;
680         }
681     }
682     mEarliestUnclearedIndex = maxIndex + 1;
683 }
684
685 }; // namespace uirenderer
686 }; // namespace android