DeferredDisplayList.cpp revision 1ed30c907ffbb71d1bc448ee57c66b0d16f600ef
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#define LOG_TAG "OpenGLRenderer" 18#define ATRACE_TAG ATRACE_TAG_VIEW 19 20#include <SkCanvas.h> 21 22#include <utils/Trace.h> 23 24#include "Debug.h" 25#include "DisplayListOp.h" 26#include "OpenGLRenderer.h" 27 28#if DEBUG_DEFER 29 #define DEFER_LOGD(...) ALOGD(__VA_ARGS__) 30#else 31 #define DEFER_LOGD(...) 32#endif 33 34namespace android { 35namespace uirenderer { 36 37// Depth of the save stack at the beginning of batch playback at flush time 38#define FLUSH_SAVE_STACK_DEPTH 2 39 40///////////////////////////////////////////////////////////////////////////////// 41// Operation Batches 42///////////////////////////////////////////////////////////////////////////////// 43 44class DrawOpBatch { 45public: 46 DrawOpBatch() { mOps.clear(); } 47 48 virtual ~DrawOpBatch() { mOps.clear(); } 49 50 void add(DrawOp* op) { 51 // NOTE: ignore empty bounds special case, since we don't merge across those ops 52 mBounds.unionWith(op->state.mBounds); 53 mOps.add(op); 54 } 55 56 virtual bool intersects(Rect& rect) { 57 if (!rect.intersects(mBounds)) return false; 58 59 for (unsigned int i = 0; i < mOps.size(); i++) { 60 if (rect.intersects(mOps[i]->state.mBounds)) { 61#if DEBUG_DEFER 62 DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i], 63 mOps[i]->state.mBounds.left, mOps[i]->state.mBounds.top, 64 mOps[i]->state.mBounds.right, mOps[i]->state.mBounds.bottom); 65 mOps[i]->output(2); 66#endif 67 return true; 68 } 69 } 70 return false; 71 } 72 73 virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) { 74 DEFER_LOGD("replaying draw batch %p", this); 75 76 status_t status = DrawGlInfo::kStatusDone; 77 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); 78 for (unsigned int i = 0; i < mOps.size(); i++) { 79 DrawOp* op = mOps[i]; 80 81 renderer.restoreDisplayState(op->state); 82 83#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 84 renderer.eventMark(op->name()); 85#endif 86 status |= op->applyDraw(renderer, dirty, 0); 87 logBuffer.writeCommand(0, op->name()); 88 } 89 return status; 90 } 91 92 inline int count() const { return mOps.size(); } 93private: 94 Vector<DrawOp*> mOps; 95 Rect mBounds; 96}; 97 98class StateOpBatch : public DrawOpBatch { 99public: 100 // creates a single operation batch 101 StateOpBatch(StateOp* op) : mOp(op) {} 102 103 bool intersects(Rect& rect) { 104 // if something checks for intersection, it's trying to go backwards across a state op, 105 // something not currently supported - state ops are always barriers 106 CRASH(); 107 return false; 108 } 109 110 virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) { 111 DEFER_LOGD("replaying state op batch %p", this); 112 renderer.restoreDisplayState(mOp->state); 113 114 // use invalid save count because it won't be used at flush time - RestoreToCountOp is the 115 // only one to use it, and we don't use that class at flush time, instead calling 116 // renderer.restoreToCount directly 117 int saveCount = -1; 118 mOp->applyState(renderer, saveCount); 119 return DrawGlInfo::kStatusDone; 120 } 121 122private: 123 const StateOp* mOp; 124}; 125 126class RestoreToCountBatch : public DrawOpBatch { 127public: 128 RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {} 129 130 bool intersects(Rect& rect) { 131 // if something checks for intersection, it's trying to go backwards across a state op, 132 // something not currently supported - state ops are always barriers 133 CRASH(); 134 return false; 135 } 136 137 virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) { 138 DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount); 139 140 renderer.restoreDisplayState(mOp->state); 141 renderer.restoreToCount(mRestoreCount); 142 return DrawGlInfo::kStatusDone; 143 } 144 145private: 146 // we use the state storage for the RestoreToCountOp, but don't replay the op itself 147 const StateOp* mOp; 148 /* 149 * The count used here represents the flush() time saveCount. This is as opposed to the 150 * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and 151 * (saveCount + mCount) respectively). Since the count is different from the original 152 * RestoreToCountOp, we don't store a pointer to the op, as elsewhere. 153 */ 154 const int mRestoreCount; 155}; 156 157///////////////////////////////////////////////////////////////////////////////// 158// DeferredDisplayList 159///////////////////////////////////////////////////////////////////////////////// 160 161void DeferredDisplayList::resetBatchingState() { 162 for (int i = 0; i < kOpBatch_Count; i++) { 163 mBatchIndices[i] = -1; 164 } 165} 166 167void DeferredDisplayList::clear() { 168 resetBatchingState(); 169 mComplexClipStackStart = -1; 170 171 for (unsigned int i = 0; i < mBatches.size(); i++) { 172 delete mBatches[i]; 173 } 174 mBatches.clear(); 175 mSaveStack.clear(); 176} 177 178///////////////////////////////////////////////////////////////////////////////// 179// Operation adding 180///////////////////////////////////////////////////////////////////////////////// 181 182int DeferredDisplayList::getStateOpDeferFlags() const { 183 // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save 184 // the clip if we aren't recording a complex clip (and can thus trust it to be a rect) 185 return recordingComplexClip() ? 0 : kStateDeferFlag_Clip; 186} 187 188int DeferredDisplayList::getDrawOpDeferFlags() const { 189 return kStateDeferFlag_Draw | getStateOpDeferFlags(); 190} 191 192/** 193 * When an clipping operation occurs that could cause a complex clip, record the operation and all 194 * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading 195 * the clip from deferred state, we play back all of the relevant state operations that generated 196 * the complex clip. 197 * 198 * Note that we don't need to record the associated restore operation, since operations at defer 199 * time record whether they should store the renderer's current clip 200 */ 201void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) { 202 if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) { 203 DEFER_LOGD("%p Received complex clip operation %p", this, op); 204 205 // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded 206 storeStateOpBarrier(renderer, op); 207 208 if (!recordingComplexClip()) { 209 mComplexClipStackStart = renderer.getSaveCount() - 1; 210 DEFER_LOGD(" Starting complex clip region, start is %d", mComplexClipStackStart); 211 } 212 } 213} 214 215/** 216 * For now, we record save layer operations as barriers in the batch list, preventing drawing 217 * operations from reordering around the saveLayer and it's associated restore() 218 * 219 * In the future, we should send saveLayer commands (if they can be played out of order) and their 220 * contained drawing operations to a seperate list of batches, so that they may draw at the 221 * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame. 222 * 223 * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a 224 * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set. 225 */ 226void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer, 227 SaveLayerOp* op, int newSaveCount) { 228 DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d", 229 this, op, op->getFlags(), newSaveCount); 230 231 storeStateOpBarrier(renderer, op); 232 mSaveStack.push(newSaveCount); 233} 234 235/** 236 * Takes save op and it's return value - the new save count - and stores it into the stream as a 237 * barrier if it's needed to properly modify a complex clip 238 */ 239void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) { 240 int saveFlags = op->getFlags(); 241 DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount); 242 243 if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) { 244 // store and replay the save operation, as it may be needed to correctly playback the clip 245 DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount); 246 storeStateOpBarrier(renderer, op); 247 mSaveStack.push(newSaveCount); 248 } 249} 250 251/** 252 * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw 253 * the layer in the deferred list 254 * 255 * other save() commands which occur as children of a snapshot with complex clip will be deferred, 256 * and must be restored 257 * 258 * Either will act as a barrier to draw operation reordering, as we want to play back layer 259 * save/restore and complex canvas modifications (including save/restore) in order. 260 */ 261void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op, 262 int newSaveCount) { 263 DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount); 264 265 if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) { 266 mComplexClipStackStart = -1; 267 resetBatchingState(); 268 } 269 270 if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) { 271 return; 272 } 273 274 while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop(); 275 276 storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH); 277} 278 279void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { 280 if (renderer.storeDisplayState(op->state, getDrawOpDeferFlags())) { 281 return; // quick rejected 282 } 283 284 op->onDrawOpDeferred(renderer); 285 286 if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) { 287 // TODO: elegant way to reuse batches? 288 DrawOpBatch* b = new DrawOpBatch(); 289 b->add(op); 290 mBatches.add(b); 291 return; 292 } 293 294 // disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge 295 // the new op into it 296 DrawOpBatch* targetBatch = NULL; 297 int batchId = op->getBatchId(); 298 299 if (!mBatches.isEmpty()) { 300 if (op->state.mBounds.isEmpty()) { 301 // don't know the bounds for op, so add to last batch and start from scratch on next op 302 mBatches.top()->add(op); 303 for (int i = 0; i < kOpBatch_Count; i++) { 304 mBatchIndices[i] = -1; 305 } 306#if DEBUG_DEFER 307 DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches"); 308 op->output(2); 309#endif 310 return; 311 } 312 313 if (batchId >= 0 && mBatchIndices[batchId] != -1) { 314 int targetIndex = mBatchIndices[batchId]; 315 targetBatch = mBatches[targetIndex]; 316 // iterate back toward target to see if anything drawn since should overlap the new op 317 for (int i = mBatches.size() - 1; i > targetIndex; i--) { 318 DrawOpBatch* overBatch = mBatches[i]; 319 if (overBatch->intersects(op->state.mBounds)) { 320 targetBatch = NULL; 321#if DEBUG_DEFER 322 DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d", 323 targetIndex, i); 324 op->output(2); 325#endif 326 break; 327 } 328 } 329 } 330 } 331 if (!targetBatch) { 332 targetBatch = new DrawOpBatch(); 333 mBatches.add(targetBatch); 334 if (batchId >= 0) { 335 mBatchIndices[batchId] = mBatches.size() - 1; 336 } 337 } 338 targetBatch->add(op); 339} 340 341void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) { 342 DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size()); 343 344 renderer.storeDisplayState(op->state, getStateOpDeferFlags()); 345 mBatches.add(new StateOpBatch(op)); 346 resetBatchingState(); 347} 348 349void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, 350 int newSaveCount) { 351 DEFER_LOGD("%p adding restore to count %d barrier, pos %d", 352 this, newSaveCount, mBatches.size()); 353 354 // store displayState for the restore operation, as it may be associated with a saveLayer that 355 // doesn't have kClip_SaveFlag set 356 renderer.storeDisplayState(op->state, getStateOpDeferFlags()); 357 mBatches.add(new RestoreToCountBatch(op, newSaveCount)); 358 resetBatchingState(); 359} 360 361///////////////////////////////////////////////////////////////////////////////// 362// Replay / flush 363///////////////////////////////////////////////////////////////////////////////// 364 365static status_t replayBatchList(Vector<DrawOpBatch*>& batchList, 366 OpenGLRenderer& renderer, Rect& dirty) { 367 status_t status = DrawGlInfo::kStatusDone; 368 369 int opCount = 0; 370 for (unsigned int i = 0; i < batchList.size(); i++) { 371 status |= batchList[i]->replay(renderer, dirty); 372 opCount += batchList[i]->count(); 373 } 374 DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount); 375 return status; 376} 377 378status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { 379 ATRACE_NAME("flush drawing commands"); 380 status_t status = DrawGlInfo::kStatusDone; 381 382 if (isEmpty()) return status; // nothing to flush 383 renderer.restoreToCount(1); 384 385 DEFER_LOGD("--flushing"); 386 renderer.eventMark("Flush"); 387 388 // save and restore (with draw modifiers) so that reordering doesn't affect final state 389 DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers(); 390 renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 391 392 // NOTE: depth of the save stack at this point, before playback, should be reflected in 393 // FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly 394 status |= replayBatchList(mBatches, renderer, dirty); 395 396 renderer.restoreToCount(1); 397 renderer.setDrawModifiers(restoreDrawModifiers); 398 399 DEFER_LOGD("--flush complete, returning %x", status); 400 401 clear(); 402 return status; 403} 404 405}; // namespace uirenderer 406}; // namespace android 407