blob: 5ff92be1a90ac8eb92b6165342bd4afeee0c1621 [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
Chris Craik7273daa2013-03-28 11:25:24 -070078 renderer.restoreDisplayState(op->state);
Chris Craikff785832013-03-08 13:12:16 -080079
80#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
Chris Craikd90144d2013-03-19 15:03:48 -070081 renderer.eventMark(op->name());
Chris Craikff785832013-03-08 13:12:16 -080082#endif
Chris Craika08f95c2013-03-15 17:24:33 -070083 status |= op->applyDraw(renderer, dirty, 0);
Chris Craikff785832013-03-08 13:12:16 -080084 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);
Chris Craik7273daa2013-03-28 11:25:24 -0700109 renderer.restoreDisplayState(mOp->state);
Chris Craikff785832013-03-08 13:12:16 -0800110
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:
Chris Craik7273daa2013-03-28 11:25:24 -0700120 const StateOp* mOp;
Chris Craikff785832013-03-08 13:12:16 -0800121};
122
123class RestoreToCountBatch : public DrawOpBatch {
124public:
Chris Craik7273daa2013-03-28 11:25:24 -0700125 RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {}
Chris Craikff785832013-03-08 13:12:16 -0800126
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);
Chris Craik7273daa2013-03-28 11:25:24 -0700136
137 renderer.restoreDisplayState(mOp->state);
Chris Craikff785832013-03-08 13:12:16 -0800138 renderer.restoreToCount(mRestoreCount);
Chris Craikff785832013-03-08 13:12:16 -0800139 return DrawGlInfo::kStatusDone;
140 }
141
142private:
Chris Craik7273daa2013-03-28 11:25:24 -0700143 // we use the state storage for the RestoreToCountOp, but don't replay the op itself
144 const StateOp* mOp;
Chris Craikff785832013-03-08 13:12:16 -0800145 /*
146 * The count used here represents the flush() time saveCount. This is as opposed to the
147 * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and
148 * (saveCount + mCount) respectively). Since the count is different from the original
149 * RestoreToCountOp, we don't store a pointer to the op, as elsewhere.
150 */
151 const int mRestoreCount;
152};
153
154/////////////////////////////////////////////////////////////////////////////////
155// DeferredDisplayList
156/////////////////////////////////////////////////////////////////////////////////
157
158void DeferredDisplayList::resetBatchingState() {
Chris Craikc3566d02013-02-04 16:16:33 -0800159 for (int i = 0; i < kOpBatch_Count; i++) {
160 mBatchIndices[i] = -1;
161 }
Chris Craikff785832013-03-08 13:12:16 -0800162}
163
164void DeferredDisplayList::clear() {
165 resetBatchingState();
166 mComplexClipStackStart = -1;
167
Chris Craikc3566d02013-02-04 16:16:33 -0800168 for (unsigned int i = 0; i < mBatches.size(); i++) {
169 delete mBatches[i];
170 }
171 mBatches.clear();
Chris Craikff785832013-03-08 13:12:16 -0800172 mSaveStack.clear();
Chris Craikc3566d02013-02-04 16:16:33 -0800173}
174
Chris Craikff785832013-03-08 13:12:16 -0800175/////////////////////////////////////////////////////////////////////////////////
176// Operation adding
177/////////////////////////////////////////////////////////////////////////////////
178
179int DeferredDisplayList::getStateOpDeferFlags() const {
180 // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save
181 // the clip if we aren't recording a complex clip (and can thus trust it to be a rect)
182 return recordingComplexClip() ? 0 : kStateDeferFlag_Clip;
183}
184
185int DeferredDisplayList::getDrawOpDeferFlags() const {
186 return kStateDeferFlag_Draw | getStateOpDeferFlags();
187}
188
189/**
190 * When an clipping operation occurs that could cause a complex clip, record the operation and all
191 * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading
192 * the clip from deferred state, we play back all of the relevant state operations that generated
193 * the complex clip.
194 *
195 * Note that we don't need to record the associated restore operation, since operations at defer
196 * time record whether they should store the renderer's current clip
197 */
198void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) {
199 if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) {
200 DEFER_LOGD("%p Received complex clip operation %p", this, op);
201
202 // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded
203 storeStateOpBarrier(renderer, op);
204
205 if (!recordingComplexClip()) {
206 mComplexClipStackStart = renderer.getSaveCount() - 1;
207 DEFER_LOGD(" Starting complex clip region, start is %d", mComplexClipStackStart);
Chris Craikc3566d02013-02-04 16:16:33 -0800208 }
Chris Craikff785832013-03-08 13:12:16 -0800209 }
210}
211
212/**
213 * For now, we record save layer operations as barriers in the batch list, preventing drawing
214 * operations from reordering around the saveLayer and it's associated restore()
215 *
216 * In the future, we should send saveLayer commands (if they can be played out of order) and their
217 * contained drawing operations to a seperate list of batches, so that they may draw at the
218 * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame.
219 *
220 * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a
221 * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set.
222 */
223void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer,
224 SaveLayerOp* op, int newSaveCount) {
225 DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d",
226 this, op, op->getFlags(), newSaveCount);
227
228 storeStateOpBarrier(renderer, op);
229 mSaveStack.push(newSaveCount);
230}
231
232/**
233 * Takes save op and it's return value - the new save count - and stores it into the stream as a
234 * barrier if it's needed to properly modify a complex clip
235 */
236void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) {
237 int saveFlags = op->getFlags();
238 DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount);
239
240 if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) {
241 // store and replay the save operation, as it may be needed to correctly playback the clip
242 DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount);
243 storeStateOpBarrier(renderer, op);
244 mSaveStack.push(newSaveCount);
245 }
246}
247
248/**
249 * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw
250 * the layer in the deferred list
251 *
252 * other save() commands which occur as children of a snapshot with complex clip will be deferred,
253 * and must be restored
254 *
255 * Either will act as a barrier to draw operation reordering, as we want to play back layer
256 * save/restore and complex canvas modifications (including save/restore) in order.
257 */
Chris Craik7273daa2013-03-28 11:25:24 -0700258void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op,
259 int newSaveCount) {
Chris Craikff785832013-03-08 13:12:16 -0800260 DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount);
261
262 if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) {
263 mComplexClipStackStart = -1;
264 resetBatchingState();
265 }
266
267 if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) {
268 return;
269 }
270
271 while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop();
272
Chris Craik7273daa2013-03-28 11:25:24 -0700273 storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + 1);
Chris Craikff785832013-03-08 13:12:16 -0800274}
275
276void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
277 if (renderer.storeDisplayState(op->state, getDrawOpDeferFlags())) {
278 return; // quick rejected
279 }
280
281 op->onDrawOpDeferred(renderer);
282
283 if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
284 // TODO: elegant way to reuse batches?
Chris Craikc3566d02013-02-04 16:16:33 -0800285 DrawOpBatch* b = new DrawOpBatch();
286 b->add(op);
287 mBatches.add(b);
288 return;
289 }
290
291 // disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge
292 // the new op into it
293 DrawOpBatch* targetBatch = NULL;
294 int batchId = op->getBatchId();
295
296 if (!mBatches.isEmpty()) {
297 if (op->state.mBounds.isEmpty()) {
298 // don't know the bounds for op, so add to last batch and start from scratch on next op
299 mBatches.top()->add(op);
300 for (int i = 0; i < kOpBatch_Count; i++) {
301 mBatchIndices[i] = -1;
302 }
303#if DEBUG_DEFER
304 DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
305 op->output(2);
306#endif
307 return;
308 }
309
310 if (batchId >= 0 && mBatchIndices[batchId] != -1) {
311 int targetIndex = mBatchIndices[batchId];
312 targetBatch = mBatches[targetIndex];
313 // iterate back toward target to see if anything drawn since should overlap the new op
314 for (int i = mBatches.size() - 1; i > targetIndex; i--) {
315 DrawOpBatch* overBatch = mBatches[i];
316 if (overBatch->intersects(op->state.mBounds)) {
317 targetBatch = NULL;
318#if DEBUG_DEFER
319 DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
320 targetIndex, i);
321 op->output(2);
322#endif
323 break;
324 }
325 }
326 }
327 }
328 if (!targetBatch) {
329 targetBatch = new DrawOpBatch();
330 mBatches.add(targetBatch);
331 if (batchId >= 0) {
332 mBatchIndices[batchId] = mBatches.size() - 1;
333 }
334 }
335 targetBatch->add(op);
336}
337
Chris Craikff785832013-03-08 13:12:16 -0800338void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
339 DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size());
340
341 renderer.storeDisplayState(op->state, getStateOpDeferFlags());
342 mBatches.add(new StateOpBatch(op));
343 resetBatchingState();
344}
345
Chris Craik7273daa2013-03-28 11:25:24 -0700346void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op,
347 int newSaveCount) {
Chris Craikff785832013-03-08 13:12:16 -0800348 DEFER_LOGD("%p adding restore to count %d barrier, pos %d",
349 this, newSaveCount, mBatches.size());
350
Chris Craik7273daa2013-03-28 11:25:24 -0700351 // store displayState for the restore operation, as it may be associated with a saveLayer that
352 // doesn't have kClip_SaveFlag set
353 renderer.storeDisplayState(op->state, getStateOpDeferFlags());
354 mBatches.add(new RestoreToCountBatch(op, newSaveCount));
Chris Craikff785832013-03-08 13:12:16 -0800355 resetBatchingState();
356}
357
358/////////////////////////////////////////////////////////////////////////////////
359// Replay / flush
360/////////////////////////////////////////////////////////////////////////////////
361
362static status_t replayBatchList(Vector<DrawOpBatch*>& batchList,
363 OpenGLRenderer& renderer, Rect& dirty) {
364 status_t status = DrawGlInfo::kStatusDone;
365
366 int opCount = 0;
367 for (unsigned int i = 0; i < batchList.size(); i++) {
368 status |= batchList[i]->replay(renderer, dirty);
369 opCount += batchList[i]->count();
370 }
371 DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount);
372 return status;
373}
374
375status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
376 ATRACE_NAME("flush drawing commands");
Chris Craikc3566d02013-02-04 16:16:33 -0800377 status_t status = DrawGlInfo::kStatusDone;
378
379 if (isEmpty()) return status; // nothing to flush
Chris Craika4e16c52013-03-22 10:00:48 -0700380 renderer.restoreToCount(1);
Chris Craikc3566d02013-02-04 16:16:33 -0800381
382 DEFER_LOGD("--flushing");
Romain Guy0f667532013-03-01 14:31:04 -0800383 renderer.eventMark("Flush");
384
Chris Craika4e16c52013-03-22 10:00:48 -0700385 // save and restore (with draw modifiers) so that reordering doesn't affect final state
Chris Craikd90144d2013-03-19 15:03:48 -0700386 DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
Chris Craika4e16c52013-03-22 10:00:48 -0700387 renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
388
Chris Craikff785832013-03-08 13:12:16 -0800389 status |= replayBatchList(mBatches, renderer, dirty);
Chris Craika4e16c52013-03-22 10:00:48 -0700390
391 renderer.restoreToCount(1);
Chris Craikd90144d2013-03-19 15:03:48 -0700392 renderer.setDrawModifiers(restoreDrawModifiers);
Chris Craikc3566d02013-02-04 16:16:33 -0800393
Chris Craikff785832013-03-08 13:12:16 -0800394 DEFER_LOGD("--flush complete, returning %x", status);
Chris Craikc3566d02013-02-04 16:16:33 -0800395
Chris Craikc3566d02013-02-04 16:16:33 -0800396 clear();
397 return status;
398}
399
400}; // namespace uirenderer
401}; // namespace android