blob: 34d98a105f33b011541e432254fd0cbefd34ee14 [file] [log] [blame]
John Reck113e0822014-03-18 09:22:59 -07001/*
2 * Copyright (C) 2014 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 ATRACE_TAG ATRACE_TAG_VIEW
18
19#include "RenderNode.h"
20
21#include <SkCanvas.h>
22#include <algorithm>
23
24#include <utils/Trace.h>
25
26#include "Debug.h"
27#include "DisplayListOp.h"
28#include "DisplayListLogBuffer.h"
29
30namespace android {
31namespace uirenderer {
32
33void RenderNode::outputLogBuffer(int fd) {
34 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
35 if (logBuffer.isEmpty()) {
36 return;
37 }
38
39 FILE *file = fdopen(fd, "a");
40
41 fprintf(file, "\nRecent DisplayList operations\n");
42 logBuffer.outputCommands(file);
43
44 String8 cachesLog;
45 Caches::getInstance().dumpMemoryUsage(cachesLog);
46 fprintf(file, "\nCaches:\n%s", cachesLog.string());
47 fprintf(file, "\n");
48
49 fflush(file);
50}
51
John Reckd0a0b2a2014-03-20 16:28:56 -070052RenderNode::RenderNode() : mDestroyed(false), mNeedsPropertiesSync(false), mDisplayListData(0) {
John Reck113e0822014-03-18 09:22:59 -070053}
54
55RenderNode::~RenderNode() {
56 LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
57
58 mDestroyed = true;
59 delete mDisplayListData;
60}
61
62void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
63 if (displayList) {
64 DISPLAY_LIST_LOGD("Deferring display list destruction");
65 Caches::getInstance().deleteDisplayListDeferred(displayList);
66 }
67}
68
69void RenderNode::setData(DisplayListData* data) {
70 delete mDisplayListData;
71 mDisplayListData = data;
72 if (mDisplayListData) {
73 Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
74 }
75}
76
77/**
78 * This function is a simplified version of replay(), where we simply retrieve and log the
79 * display list. This function should remain in sync with the replay() function.
80 */
81void RenderNode::output(uint32_t level) {
82 ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
83 mName.string(), isRenderable());
84 ALOGD("%*s%s %d", level * 2, "", "Save",
85 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
86
John Reckd0a0b2a2014-03-20 16:28:56 -070087 properties().debugOutputProperties(level);
John Reck113e0822014-03-18 09:22:59 -070088 int flags = DisplayListOp::kOpLogFlag_Recurse;
89 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
90 mDisplayListData->displayListOps[i]->output(level, flags);
91 }
92
93 ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
94}
95
John Reckd0a0b2a2014-03-20 16:28:56 -070096void RenderNode::updateProperties() {
97 if (mNeedsPropertiesSync) {
98 mNeedsPropertiesSync = false;
99 mProperties = mStagingProperties;
John Reck113e0822014-03-18 09:22:59 -0700100 }
101
John Reck5bf11bb2014-03-25 10:22:09 -0700102 if (mDisplayListData) {
103 for (size_t i = 0; i < mDisplayListData->children.size(); i++) {
104 RenderNode* childNode = mDisplayListData->children[i]->mDisplayList;
105 childNode->updateProperties();
106 }
John Reck113e0822014-03-18 09:22:59 -0700107 }
108}
109
John Reck668f0e32014-03-26 15:10:40 -0700110bool RenderNode::hasFunctors() {
111 if (!mDisplayListData) return false;
112
113 if (mDisplayListData->functorCount) {
114 return true;
115 }
116
117 for (size_t i = 0; i < mDisplayListData->children.size(); i++) {
118 RenderNode* childNode = mDisplayListData->children[i]->mDisplayList;
119 if (childNode->hasFunctors()) {
120 return true;
121 }
122 }
123
124 return false;
125}
126
John Reck113e0822014-03-18 09:22:59 -0700127/*
128 * For property operations, we pass a savecount of 0, since the operations aren't part of the
129 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
John Reckd0a0b2a2014-03-20 16:28:56 -0700130 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount())
John Reck113e0822014-03-18 09:22:59 -0700131 */
132#define PROPERTY_SAVECOUNT 0
133
134template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700135void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
John Reck113e0822014-03-18 09:22:59 -0700136#if DEBUG_DISPLAY_LIST
Chris Craikb265e2c2014-03-27 15:50:09 -0700137 properties().debugOutputProperties(handler.level() + 1);
John Reck113e0822014-03-18 09:22:59 -0700138#endif
John Reckd0a0b2a2014-03-20 16:28:56 -0700139 if (properties().getLeft() != 0 || properties().getTop() != 0) {
140 renderer.translate(properties().getLeft(), properties().getTop());
John Reck113e0822014-03-18 09:22:59 -0700141 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700142 if (properties().getStaticMatrix()) {
143 renderer.concatMatrix(properties().getStaticMatrix());
144 } else if (properties().getAnimationMatrix()) {
145 renderer.concatMatrix(properties().getAnimationMatrix());
John Reck113e0822014-03-18 09:22:59 -0700146 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700147 if (properties().getMatrixFlags() != 0) {
148 if (properties().getMatrixFlags() == TRANSLATION) {
149 renderer.translate(properties().getTranslationX(), properties().getTranslationY());
John Reck113e0822014-03-18 09:22:59 -0700150 } else {
John Reckd0a0b2a2014-03-20 16:28:56 -0700151 renderer.concatMatrix(*properties().getTransformMatrix());
John Reck113e0822014-03-18 09:22:59 -0700152 }
153 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700154 bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds();
155 if (properties().getAlpha() < 1) {
156 if (properties().getCaching()) {
157 renderer.setOverrideLayerAlpha(properties().getAlpha());
158 } else if (!properties().getHasOverlappingRendering()) {
159 renderer.scaleAlpha(properties().getAlpha());
John Reck113e0822014-03-18 09:22:59 -0700160 } else {
161 // TODO: should be able to store the size of a DL at record time and not
162 // have to pass it into this call. In fact, this information might be in the
163 // location/size info that we store with the new native transform data.
164 int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
165 if (clipToBoundsNeeded) {
166 saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
167 clipToBoundsNeeded = false; // clipping done by saveLayer
168 }
169
170 SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
Chris Craik8c271ca2014-03-25 10:33:01 -0700171 0, 0, properties().getWidth(), properties().getHeight(),
172 properties().getAlpha() * 255, saveFlags);
John Reckd0a0b2a2014-03-20 16:28:56 -0700173 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700174 }
175 }
176 if (clipToBoundsNeeded) {
Chris Craik8c271ca2014-03-25 10:33:01 -0700177 ClipRectOp* op = new (handler.allocator()) ClipRectOp(
178 0, 0, properties().getWidth(), properties().getHeight(), SkRegion::kIntersect_Op);
John Reckd0a0b2a2014-03-20 16:28:56 -0700179 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700180 }
Chris Craik8c271ca2014-03-25 10:33:01 -0700181
182 if (CC_UNLIKELY(properties().hasClippingPath())) {
183 // TODO: optimize for round rect/circle clipping
184 const SkPath* path = properties().getClippingPath();
185 ClipPathOp* op = new (handler.allocator()) ClipPathOp(path, SkRegion::kIntersect_Op);
John Reckd0a0b2a2014-03-20 16:28:56 -0700186 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700187 }
188}
189
190/**
191 * Apply property-based transformations to input matrix
192 *
193 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
194 * matrix computation instead of the Skia 3x3 matrix + camera hackery.
195 */
196void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700197 if (properties().getLeft() != 0 || properties().getTop() != 0) {
198 matrix.translate(properties().getLeft(), properties().getTop());
John Reck113e0822014-03-18 09:22:59 -0700199 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700200 if (properties().getStaticMatrix()) {
201 mat4 stat(*properties().getStaticMatrix());
John Reck113e0822014-03-18 09:22:59 -0700202 matrix.multiply(stat);
John Reckd0a0b2a2014-03-20 16:28:56 -0700203 } else if (properties().getAnimationMatrix()) {
204 mat4 anim(*properties().getAnimationMatrix());
John Reck113e0822014-03-18 09:22:59 -0700205 matrix.multiply(anim);
206 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700207 if (properties().getMatrixFlags() != 0) {
208 if (properties().getMatrixFlags() == TRANSLATION) {
209 matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
210 true3dTransform ? properties().getTranslationZ() : 0.0f);
John Reck113e0822014-03-18 09:22:59 -0700211 } else {
212 if (!true3dTransform) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700213 matrix.multiply(*properties().getTransformMatrix());
John Reck113e0822014-03-18 09:22:59 -0700214 } else {
215 mat4 true3dMat;
216 true3dMat.loadTranslate(
John Reckd0a0b2a2014-03-20 16:28:56 -0700217 properties().getPivotX() + properties().getTranslationX(),
218 properties().getPivotY() + properties().getTranslationY(),
219 properties().getTranslationZ());
220 true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
221 true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
222 true3dMat.rotate(properties().getRotation(), 0, 0, 1);
223 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
224 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
John Reck113e0822014-03-18 09:22:59 -0700225
226 matrix.multiply(true3dMat);
227 }
228 }
229 }
230}
231
232/**
233 * Organizes the DisplayList hierarchy to prepare for background projection reordering.
234 *
235 * This should be called before a call to defer() or drawDisplayList()
236 *
237 * Each DisplayList that serves as a 3d root builds its list of composited children,
238 * which are flagged to not draw in the standard draw loop.
239 */
240void RenderNode::computeOrdering() {
241 ATRACE_CALL();
242 mProjectedNodes.clear();
243
244 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
245 // transform properties are applied correctly to top level children
246 if (mDisplayListData == NULL) return;
247 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
248 DrawDisplayListOp* childOp = mDisplayListData->children[i];
249 childOp->mDisplayList->computeOrderingImpl(childOp,
250 &mProjectedNodes, &mat4::identity());
251 }
252}
253
254void RenderNode::computeOrderingImpl(
255 DrawDisplayListOp* opState,
256 Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
257 const mat4* transformFromProjectionSurface) {
258 mProjectedNodes.clear();
259 if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
260
261 // TODO: should avoid this calculation in most cases
262 // TODO: just calculate single matrix, down to all leaf composited elements
263 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
264 localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
265
John Reckd0a0b2a2014-03-20 16:28:56 -0700266 if (properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700267 // composited projectee, flag for out of order draw, save matrix, and store in proj surface
268 opState->mSkipInOrderDraw = true;
269 opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
270 compositedChildrenOfProjectionSurface->add(opState);
271 } else {
272 // standard in order draw
273 opState->mSkipInOrderDraw = false;
274 }
275
276 if (mDisplayListData->children.size() > 0) {
277 const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
278 bool haveAppliedPropertiesToProjection = false;
279 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
280 DrawDisplayListOp* childOp = mDisplayListData->children[i];
281 RenderNode* child = childOp->mDisplayList;
282
283 Vector<DrawDisplayListOp*>* projectionChildren = NULL;
284 const mat4* projectionTransform = NULL;
John Reckd0a0b2a2014-03-20 16:28:56 -0700285 if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700286 // if receiving projections, collect projecting descendent
287
288 // Note that if a direct descendent is projecting backwards, we pass it's
289 // grandparent projection collection, since it shouldn't project onto it's
290 // parent, where it will already be drawing.
291 projectionChildren = &mProjectedNodes;
292 projectionTransform = &mat4::identity();
293 } else {
294 if (!haveAppliedPropertiesToProjection) {
295 applyViewPropertyTransforms(localTransformFromProjectionSurface);
296 haveAppliedPropertiesToProjection = true;
297 }
298 projectionChildren = compositedChildrenOfProjectionSurface;
299 projectionTransform = &localTransformFromProjectionSurface;
300 }
301 child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
302 }
303 }
John Reck113e0822014-03-18 09:22:59 -0700304}
305
306class DeferOperationHandler {
307public:
308 DeferOperationHandler(DeferStateStruct& deferStruct, int level)
309 : mDeferStruct(deferStruct), mLevel(level) {}
310 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
311 operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
312 }
313 inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
Chris Craikb265e2c2014-03-27 15:50:09 -0700314 inline void startMark(const char* name) {} // do nothing
315 inline void endMark() {}
316 inline int level() { return mLevel; }
317 inline int replayFlags() { return mDeferStruct.mReplayFlags; }
John Reck113e0822014-03-18 09:22:59 -0700318
319private:
320 DeferStateStruct& mDeferStruct;
321 const int mLevel;
322};
323
Chris Craikb265e2c2014-03-27 15:50:09 -0700324void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) {
325 DeferOperationHandler handler(deferStruct, 0);
326 if (properties().getTranslationZ() > 0.0f) issueDrawShadowOperation(Matrix4::identity(), handler);
327 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
328}
329
330void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) {
John Reck113e0822014-03-18 09:22:59 -0700331 DeferOperationHandler handler(deferStruct, level);
Chris Craikb265e2c2014-03-27 15:50:09 -0700332 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700333}
334
335class ReplayOperationHandler {
336public:
337 ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
338 : mReplayStruct(replayStruct), mLevel(level) {}
339 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
340#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
John Reckd0a0b2a2014-03-20 16:28:56 -0700341 properties().getReplayStruct().mRenderer.eventMark(operation->name());
John Reck113e0822014-03-18 09:22:59 -0700342#endif
343 operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
344 }
345 inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
Chris Craikb265e2c2014-03-27 15:50:09 -0700346 inline void startMark(const char* name) {
347 mReplayStruct.mRenderer.startMark(name);
348 }
349 inline void endMark() {
350 mReplayStruct.mRenderer.endMark();
351 DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
352 mReplayStruct.mDrawGlStatus);
353 }
354 inline int level() { return mLevel; }
355 inline int replayFlags() { return mReplayStruct.mReplayFlags; }
John Reck113e0822014-03-18 09:22:59 -0700356
357private:
358 ReplayStateStruct& mReplayStruct;
359 const int mLevel;
360};
361
Chris Craikb265e2c2014-03-27 15:50:09 -0700362void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) {
363 ReplayOperationHandler handler(replayStruct, 0);
364 if (properties().getTranslationZ() > 0.0f) issueDrawShadowOperation(Matrix4::identity(), handler);
365 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
366}
367
368void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) {
John Reck113e0822014-03-18 09:22:59 -0700369 ReplayOperationHandler handler(replayStruct, level);
Chris Craikb265e2c2014-03-27 15:50:09 -0700370 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700371}
372
373void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
374 if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
375
376 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
377 DrawDisplayListOp* childOp = mDisplayListData->children[i];
378 RenderNode* child = childOp->mDisplayList;
John Reckd0a0b2a2014-03-20 16:28:56 -0700379 float childZ = child->properties().getTranslationZ();
John Reck113e0822014-03-18 09:22:59 -0700380
381 if (childZ != 0.0f) {
382 zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
383 childOp->mSkipInOrderDraw = true;
John Reckd0a0b2a2014-03-20 16:28:56 -0700384 } else if (!child->properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700385 // regular, in order drawing DisplayList
386 childOp->mSkipInOrderDraw = false;
387 }
388 }
389
390 // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
391 std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
392}
393
Chris Craikb265e2c2014-03-27 15:50:09 -0700394template <class T>
395void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
396 if (properties().getAlpha() <= 0.0f) return;
397
398 mat4 shadowMatrixXY(transformFromParent);
399 applyViewPropertyTransforms(shadowMatrixXY);
400
401 // Z matrix needs actual 3d transformation, so mapped z values will be correct
402 mat4 shadowMatrixZ(transformFromParent);
403 applyViewPropertyTransforms(shadowMatrixZ, true);
404
405 const SkPath* outlinePath = properties().getOutline().getPath();
406 const RevealClip& revealClip = properties().getRevealClip();
407 const SkPath* revealClipPath = revealClip.hasConvexClip()
408 ? revealClip.getPath() : NULL; // only pass the reveal clip's path if it's convex
409
410 /**
411 * The drawing area of the caster is always the same as the its perimeter (which
412 * the shadow system uses) *except* in the inverse clip case. Inform the shadow
413 * system that the caster's drawing area (as opposed to its perimeter) has been
414 * clipped, so that it knows the caster can't be opaque.
415 */
416 bool casterUnclipped = !revealClip.willClip() || revealClip.hasConvexClip();
417
418 DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp(
419 shadowMatrixXY, shadowMatrixZ,
420 properties().getAlpha(), casterUnclipped,
421 properties().getWidth(), properties().getHeight(),
422 outlinePath, revealClipPath);
423 handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
424}
425
John Reck113e0822014-03-18 09:22:59 -0700426#define SHADOW_DELTA 0.1f
427
428template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700429void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
John Reck113e0822014-03-18 09:22:59 -0700430 ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
431 const int size = zTranslatedNodes.size();
432 if (size == 0
433 || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
434 || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
435 // no 3d children to draw
436 return;
437 }
438
John Reck113e0822014-03-18 09:22:59 -0700439 /**
440 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
441 * with very similar Z heights to draw together.
442 *
443 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
444 * underneath both, and neither's shadow is drawn on top of the other.
445 */
446 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
447 size_t drawIndex, shadowIndex, endIndex;
448 if (mode == kNegativeZChildren) {
449 drawIndex = 0;
450 endIndex = nonNegativeIndex;
451 shadowIndex = endIndex; // draw no shadows
452 } else {
453 drawIndex = nonNegativeIndex;
454 endIndex = size;
455 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
456 }
457 float lastCasterZ = 0.0f;
458 while (shadowIndex < endIndex || drawIndex < endIndex) {
459 if (shadowIndex < endIndex) {
460 DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
461 RenderNode* caster = casterOp->mDisplayList;
462 const float casterZ = zTranslatedNodes[shadowIndex].key;
463 // attempt to render the shadow if the caster about to be drawn is its caster,
464 // OR if its caster's Z value is similar to the previous potential caster
465 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
Chris Craikb265e2c2014-03-27 15:50:09 -0700466 caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler);
John Reck113e0822014-03-18 09:22:59 -0700467
468 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
469 shadowIndex++;
470 continue;
471 }
472 }
473
474 // only the actual child DL draw needs to be in save/restore,
475 // since it modifies the renderer's matrix
476 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
477
478 DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
479 RenderNode* child = childOp->mDisplayList;
480
481 renderer.concatMatrix(childOp->mTransformFromParent);
482 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
John Reckd0a0b2a2014-03-20 16:28:56 -0700483 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700484 childOp->mSkipInOrderDraw = true;
485
486 renderer.restoreToCount(restoreTo);
487 drawIndex++;
488 }
John Reck113e0822014-03-18 09:22:59 -0700489}
490
491template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700492void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
John Reck113e0822014-03-18 09:22:59 -0700493 for (size_t i = 0; i < mProjectedNodes.size(); i++) {
494 DrawDisplayListOp* childOp = mProjectedNodes[i];
495
496 // matrix save, concat, and restore can be done safely without allocating operations
497 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
498 renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
499 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
John Reckd0a0b2a2014-03-20 16:28:56 -0700500 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700501 childOp->mSkipInOrderDraw = true;
502 renderer.restoreToCount(restoreTo);
503 }
John Reck113e0822014-03-18 09:22:59 -0700504}
505
506/**
507 * This function serves both defer and replay modes, and will organize the displayList's component
508 * operations for a single frame:
509 *
510 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
511 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
512 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
513 * defer vs replay logic, per operation
514 */
515template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700516void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
517 const int level = handler.level();
John Reck113e0822014-03-18 09:22:59 -0700518 if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
519 ALOGW("Error: %s is drawing after destruction", mName.string());
520 CRASH();
521 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700522 if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
John Reck113e0822014-03-18 09:22:59 -0700523 DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
524 return;
525 }
526
Chris Craikb265e2c2014-03-27 15:50:09 -0700527 handler.startMark(mName.string());
528
John Reck113e0822014-03-18 09:22:59 -0700529#if DEBUG_DISPLAY_LIST
530 Rect* clipRect = renderer.getClipRect();
531 DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
532 level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
533 clipRect->right, clipRect->bottom);
534#endif
535
536 LinearAllocator& alloc = handler.allocator();
537 int restoreTo = renderer.getSaveCount();
538 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
John Reckd0a0b2a2014-03-20 16:28:56 -0700539 PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700540
541 DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
542 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
543
Chris Craikb265e2c2014-03-27 15:50:09 -0700544 setViewProperties<T>(renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700545
Chris Craik8c271ca2014-03-25 10:33:01 -0700546 bool quickRejected = properties().getClipToBounds()
547 && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
John Reck113e0822014-03-18 09:22:59 -0700548 if (!quickRejected) {
549 Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
550 buildZSortedChildList(zTranslatedNodes);
551
552 // for 3d root, draw children with negative z values
Chris Craikb265e2c2014-03-27 15:50:09 -0700553 issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700554
555 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
556 const int saveCountOffset = renderer.getSaveCount() - 1;
557 const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
558 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
559 DisplayListOp *op = mDisplayListData->displayListOps[i];
560
561#if DEBUG_DISPLAY_LIST
562 op->output(level + 1);
563#endif
John Reck113e0822014-03-18 09:22:59 -0700564 logBuffer.writeCommand(level, op->name());
John Reckd0a0b2a2014-03-20 16:28:56 -0700565 handler(op, saveCountOffset, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700566
567 if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
Chris Craikb265e2c2014-03-27 15:50:09 -0700568 issueOperationsOfProjectedChildren(renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700569 }
570 }
571
572 // for 3d root, draw children with positive z values
Chris Craikb265e2c2014-03-27 15:50:09 -0700573 issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700574 }
575
576 DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
577 handler(new (alloc) RestoreToCountOp(restoreTo),
John Reckd0a0b2a2014-03-20 16:28:56 -0700578 PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700579 renderer.setOverrideLayerAlpha(1.0f);
Chris Craikb265e2c2014-03-27 15:50:09 -0700580
581 handler.endMark();
John Reck113e0822014-03-18 09:22:59 -0700582}
583
584} /* namespace uirenderer */
585} /* namespace android */