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