blob: 663b67ef053ccf613c6c2982950221b81b3539ee [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) {
Chris Craik49e6c7392014-03-31 12:34:11 -070064 if (Caches::hasInstance()) {
65 DISPLAY_LIST_LOGD("Deferring display list destruction");
66 Caches::getInstance().deleteDisplayListDeferred(displayList);
67 } else {
68 delete displayList;
69 }
John Reck113e0822014-03-18 09:22:59 -070070 }
71}
72
73void RenderNode::setData(DisplayListData* data) {
74 delete mDisplayListData;
75 mDisplayListData = data;
76 if (mDisplayListData) {
77 Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
78 }
79}
80
81/**
82 * This function is a simplified version of replay(), where we simply retrieve and log the
83 * display list. This function should remain in sync with the replay() function.
84 */
85void RenderNode::output(uint32_t level) {
86 ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
87 mName.string(), isRenderable());
88 ALOGD("%*s%s %d", level * 2, "", "Save",
89 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
90
John Reckd0a0b2a2014-03-20 16:28:56 -070091 properties().debugOutputProperties(level);
John Reck113e0822014-03-18 09:22:59 -070092 int flags = DisplayListOp::kOpLogFlag_Recurse;
93 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
94 mDisplayListData->displayListOps[i]->output(level, flags);
95 }
96
97 ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
98}
99
John Reckd0a0b2a2014-03-20 16:28:56 -0700100void RenderNode::updateProperties() {
101 if (mNeedsPropertiesSync) {
102 mNeedsPropertiesSync = false;
103 mProperties = mStagingProperties;
John Reck113e0822014-03-18 09:22:59 -0700104 }
105
John Reck5bf11bb2014-03-25 10:22:09 -0700106 if (mDisplayListData) {
107 for (size_t i = 0; i < mDisplayListData->children.size(); i++) {
108 RenderNode* childNode = mDisplayListData->children[i]->mDisplayList;
109 childNode->updateProperties();
110 }
John Reck113e0822014-03-18 09:22:59 -0700111 }
112}
113
John Reck668f0e32014-03-26 15:10:40 -0700114bool RenderNode::hasFunctors() {
115 if (!mDisplayListData) return false;
116
117 if (mDisplayListData->functorCount) {
118 return true;
119 }
120
121 for (size_t i = 0; i < mDisplayListData->children.size(); i++) {
122 RenderNode* childNode = mDisplayListData->children[i]->mDisplayList;
123 if (childNode->hasFunctors()) {
124 return true;
125 }
126 }
127
128 return false;
129}
130
John Reck113e0822014-03-18 09:22:59 -0700131/*
132 * For property operations, we pass a savecount of 0, since the operations aren't part of the
133 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
John Reckd0a0b2a2014-03-20 16:28:56 -0700134 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount())
John Reck113e0822014-03-18 09:22:59 -0700135 */
136#define PROPERTY_SAVECOUNT 0
137
138template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700139void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
John Reck113e0822014-03-18 09:22:59 -0700140#if DEBUG_DISPLAY_LIST
Chris Craikb265e2c2014-03-27 15:50:09 -0700141 properties().debugOutputProperties(handler.level() + 1);
John Reck113e0822014-03-18 09:22:59 -0700142#endif
John Reckd0a0b2a2014-03-20 16:28:56 -0700143 if (properties().getLeft() != 0 || properties().getTop() != 0) {
144 renderer.translate(properties().getLeft(), properties().getTop());
John Reck113e0822014-03-18 09:22:59 -0700145 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700146 if (properties().getStaticMatrix()) {
147 renderer.concatMatrix(properties().getStaticMatrix());
148 } else if (properties().getAnimationMatrix()) {
149 renderer.concatMatrix(properties().getAnimationMatrix());
John Reck113e0822014-03-18 09:22:59 -0700150 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700151 if (properties().getMatrixFlags() != 0) {
152 if (properties().getMatrixFlags() == TRANSLATION) {
153 renderer.translate(properties().getTranslationX(), properties().getTranslationY());
John Reck113e0822014-03-18 09:22:59 -0700154 } else {
John Reckd0a0b2a2014-03-20 16:28:56 -0700155 renderer.concatMatrix(*properties().getTransformMatrix());
John Reck113e0822014-03-18 09:22:59 -0700156 }
157 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700158 bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds();
159 if (properties().getAlpha() < 1) {
160 if (properties().getCaching()) {
161 renderer.setOverrideLayerAlpha(properties().getAlpha());
162 } else if (!properties().getHasOverlappingRendering()) {
163 renderer.scaleAlpha(properties().getAlpha());
John Reck113e0822014-03-18 09:22:59 -0700164 } else {
165 // TODO: should be able to store the size of a DL at record time and not
166 // have to pass it into this call. In fact, this information might be in the
167 // location/size info that we store with the new native transform data.
168 int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
169 if (clipToBoundsNeeded) {
170 saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
171 clipToBoundsNeeded = false; // clipping done by saveLayer
172 }
173
174 SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
Chris Craik8c271ca2014-03-25 10:33:01 -0700175 0, 0, properties().getWidth(), properties().getHeight(),
176 properties().getAlpha() * 255, saveFlags);
John Reckd0a0b2a2014-03-20 16:28:56 -0700177 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700178 }
179 }
180 if (clipToBoundsNeeded) {
Chris Craik8c271ca2014-03-25 10:33:01 -0700181 ClipRectOp* op = new (handler.allocator()) ClipRectOp(
182 0, 0, properties().getWidth(), properties().getHeight(), SkRegion::kIntersect_Op);
John Reckd0a0b2a2014-03-20 16:28:56 -0700183 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700184 }
Chris Craik8c271ca2014-03-25 10:33:01 -0700185
186 if (CC_UNLIKELY(properties().hasClippingPath())) {
187 // TODO: optimize for round rect/circle clipping
188 const SkPath* path = properties().getClippingPath();
189 ClipPathOp* op = new (handler.allocator()) ClipPathOp(path, SkRegion::kIntersect_Op);
John Reckd0a0b2a2014-03-20 16:28:56 -0700190 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700191 }
192}
193
194/**
195 * Apply property-based transformations to input matrix
196 *
197 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
198 * matrix computation instead of the Skia 3x3 matrix + camera hackery.
199 */
200void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700201 if (properties().getLeft() != 0 || properties().getTop() != 0) {
202 matrix.translate(properties().getLeft(), properties().getTop());
John Reck113e0822014-03-18 09:22:59 -0700203 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700204 if (properties().getStaticMatrix()) {
205 mat4 stat(*properties().getStaticMatrix());
John Reck113e0822014-03-18 09:22:59 -0700206 matrix.multiply(stat);
John Reckd0a0b2a2014-03-20 16:28:56 -0700207 } else if (properties().getAnimationMatrix()) {
208 mat4 anim(*properties().getAnimationMatrix());
John Reck113e0822014-03-18 09:22:59 -0700209 matrix.multiply(anim);
210 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700211 if (properties().getMatrixFlags() != 0) {
212 if (properties().getMatrixFlags() == TRANSLATION) {
213 matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
214 true3dTransform ? properties().getTranslationZ() : 0.0f);
John Reck113e0822014-03-18 09:22:59 -0700215 } else {
216 if (!true3dTransform) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700217 matrix.multiply(*properties().getTransformMatrix());
John Reck113e0822014-03-18 09:22:59 -0700218 } else {
219 mat4 true3dMat;
220 true3dMat.loadTranslate(
John Reckd0a0b2a2014-03-20 16:28:56 -0700221 properties().getPivotX() + properties().getTranslationX(),
222 properties().getPivotY() + properties().getTranslationY(),
223 properties().getTranslationZ());
224 true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
225 true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
226 true3dMat.rotate(properties().getRotation(), 0, 0, 1);
227 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
228 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
John Reck113e0822014-03-18 09:22:59 -0700229
230 matrix.multiply(true3dMat);
231 }
232 }
233 }
234}
235
236/**
237 * Organizes the DisplayList hierarchy to prepare for background projection reordering.
238 *
239 * This should be called before a call to defer() or drawDisplayList()
240 *
241 * Each DisplayList that serves as a 3d root builds its list of composited children,
242 * which are flagged to not draw in the standard draw loop.
243 */
244void RenderNode::computeOrdering() {
245 ATRACE_CALL();
246 mProjectedNodes.clear();
247
248 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
249 // transform properties are applied correctly to top level children
250 if (mDisplayListData == NULL) return;
251 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
252 DrawDisplayListOp* childOp = mDisplayListData->children[i];
253 childOp->mDisplayList->computeOrderingImpl(childOp,
254 &mProjectedNodes, &mat4::identity());
255 }
256}
257
258void RenderNode::computeOrderingImpl(
259 DrawDisplayListOp* opState,
260 Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
261 const mat4* transformFromProjectionSurface) {
262 mProjectedNodes.clear();
263 if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
264
265 // TODO: should avoid this calculation in most cases
266 // TODO: just calculate single matrix, down to all leaf composited elements
267 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
268 localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
269
John Reckd0a0b2a2014-03-20 16:28:56 -0700270 if (properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700271 // composited projectee, flag for out of order draw, save matrix, and store in proj surface
272 opState->mSkipInOrderDraw = true;
273 opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
274 compositedChildrenOfProjectionSurface->add(opState);
275 } else {
276 // standard in order draw
277 opState->mSkipInOrderDraw = false;
278 }
279
280 if (mDisplayListData->children.size() > 0) {
281 const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
282 bool haveAppliedPropertiesToProjection = false;
283 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
284 DrawDisplayListOp* childOp = mDisplayListData->children[i];
285 RenderNode* child = childOp->mDisplayList;
286
287 Vector<DrawDisplayListOp*>* projectionChildren = NULL;
288 const mat4* projectionTransform = NULL;
John Reckd0a0b2a2014-03-20 16:28:56 -0700289 if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700290 // if receiving projections, collect projecting descendent
291
292 // Note that if a direct descendent is projecting backwards, we pass it's
293 // grandparent projection collection, since it shouldn't project onto it's
294 // parent, where it will already be drawing.
295 projectionChildren = &mProjectedNodes;
296 projectionTransform = &mat4::identity();
297 } else {
298 if (!haveAppliedPropertiesToProjection) {
299 applyViewPropertyTransforms(localTransformFromProjectionSurface);
300 haveAppliedPropertiesToProjection = true;
301 }
302 projectionChildren = compositedChildrenOfProjectionSurface;
303 projectionTransform = &localTransformFromProjectionSurface;
304 }
305 child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
306 }
307 }
John Reck113e0822014-03-18 09:22:59 -0700308}
309
310class DeferOperationHandler {
311public:
312 DeferOperationHandler(DeferStateStruct& deferStruct, int level)
313 : mDeferStruct(deferStruct), mLevel(level) {}
314 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
315 operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
316 }
317 inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
Chris Craikb265e2c2014-03-27 15:50:09 -0700318 inline void startMark(const char* name) {} // do nothing
319 inline void endMark() {}
320 inline int level() { return mLevel; }
321 inline int replayFlags() { return mDeferStruct.mReplayFlags; }
John Reck113e0822014-03-18 09:22:59 -0700322
323private:
324 DeferStateStruct& mDeferStruct;
325 const int mLevel;
326};
327
Chris Craikb265e2c2014-03-27 15:50:09 -0700328void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) {
329 DeferOperationHandler handler(deferStruct, 0);
330 if (properties().getTranslationZ() > 0.0f) issueDrawShadowOperation(Matrix4::identity(), handler);
331 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
332}
333
334void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) {
John Reck113e0822014-03-18 09:22:59 -0700335 DeferOperationHandler handler(deferStruct, level);
Chris Craikb265e2c2014-03-27 15:50:09 -0700336 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700337}
338
339class ReplayOperationHandler {
340public:
341 ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
342 : mReplayStruct(replayStruct), mLevel(level) {}
343 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
344#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
John Reckd0a0b2a2014-03-20 16:28:56 -0700345 properties().getReplayStruct().mRenderer.eventMark(operation->name());
John Reck113e0822014-03-18 09:22:59 -0700346#endif
347 operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
348 }
349 inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
Chris Craikb265e2c2014-03-27 15:50:09 -0700350 inline void startMark(const char* name) {
351 mReplayStruct.mRenderer.startMark(name);
352 }
353 inline void endMark() {
354 mReplayStruct.mRenderer.endMark();
355 DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
356 mReplayStruct.mDrawGlStatus);
357 }
358 inline int level() { return mLevel; }
359 inline int replayFlags() { return mReplayStruct.mReplayFlags; }
John Reck113e0822014-03-18 09:22:59 -0700360
361private:
362 ReplayStateStruct& mReplayStruct;
363 const int mLevel;
364};
365
Chris Craikb265e2c2014-03-27 15:50:09 -0700366void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) {
367 ReplayOperationHandler handler(replayStruct, 0);
368 if (properties().getTranslationZ() > 0.0f) issueDrawShadowOperation(Matrix4::identity(), handler);
369 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
370}
371
372void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) {
John Reck113e0822014-03-18 09:22:59 -0700373 ReplayOperationHandler handler(replayStruct, level);
Chris Craikb265e2c2014-03-27 15:50:09 -0700374 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700375}
376
377void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
378 if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
379
380 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
381 DrawDisplayListOp* childOp = mDisplayListData->children[i];
382 RenderNode* child = childOp->mDisplayList;
John Reckd0a0b2a2014-03-20 16:28:56 -0700383 float childZ = child->properties().getTranslationZ();
John Reck113e0822014-03-18 09:22:59 -0700384
385 if (childZ != 0.0f) {
386 zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
387 childOp->mSkipInOrderDraw = true;
John Reckd0a0b2a2014-03-20 16:28:56 -0700388 } else if (!child->properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700389 // regular, in order drawing DisplayList
390 childOp->mSkipInOrderDraw = false;
391 }
392 }
393
394 // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
395 std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
396}
397
Chris Craikb265e2c2014-03-27 15:50:09 -0700398template <class T>
399void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
400 if (properties().getAlpha() <= 0.0f) return;
401
402 mat4 shadowMatrixXY(transformFromParent);
403 applyViewPropertyTransforms(shadowMatrixXY);
404
405 // Z matrix needs actual 3d transformation, so mapped z values will be correct
406 mat4 shadowMatrixZ(transformFromParent);
407 applyViewPropertyTransforms(shadowMatrixZ, true);
408
409 const SkPath* outlinePath = properties().getOutline().getPath();
410 const RevealClip& revealClip = properties().getRevealClip();
411 const SkPath* revealClipPath = revealClip.hasConvexClip()
412 ? revealClip.getPath() : NULL; // only pass the reveal clip's path if it's convex
413
414 /**
415 * The drawing area of the caster is always the same as the its perimeter (which
416 * the shadow system uses) *except* in the inverse clip case. Inform the shadow
417 * system that the caster's drawing area (as opposed to its perimeter) has been
418 * clipped, so that it knows the caster can't be opaque.
419 */
420 bool casterUnclipped = !revealClip.willClip() || revealClip.hasConvexClip();
421
422 DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp(
423 shadowMatrixXY, shadowMatrixZ,
424 properties().getAlpha(), casterUnclipped,
425 properties().getWidth(), properties().getHeight(),
426 outlinePath, revealClipPath);
427 handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
428}
429
John Reck113e0822014-03-18 09:22:59 -0700430#define SHADOW_DELTA 0.1f
431
432template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700433void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
John Reck113e0822014-03-18 09:22:59 -0700434 ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
435 const int size = zTranslatedNodes.size();
436 if (size == 0
437 || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
438 || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
439 // no 3d children to draw
440 return;
441 }
442
John Reck113e0822014-03-18 09:22:59 -0700443 /**
444 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
445 * with very similar Z heights to draw together.
446 *
447 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
448 * underneath both, and neither's shadow is drawn on top of the other.
449 */
450 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
451 size_t drawIndex, shadowIndex, endIndex;
452 if (mode == kNegativeZChildren) {
453 drawIndex = 0;
454 endIndex = nonNegativeIndex;
455 shadowIndex = endIndex; // draw no shadows
456 } else {
457 drawIndex = nonNegativeIndex;
458 endIndex = size;
459 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
460 }
461 float lastCasterZ = 0.0f;
462 while (shadowIndex < endIndex || drawIndex < endIndex) {
463 if (shadowIndex < endIndex) {
464 DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
465 RenderNode* caster = casterOp->mDisplayList;
466 const float casterZ = zTranslatedNodes[shadowIndex].key;
467 // attempt to render the shadow if the caster about to be drawn is its caster,
468 // OR if its caster's Z value is similar to the previous potential caster
469 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
Chris Craikb265e2c2014-03-27 15:50:09 -0700470 caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler);
John Reck113e0822014-03-18 09:22:59 -0700471
472 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
473 shadowIndex++;
474 continue;
475 }
476 }
477
478 // only the actual child DL draw needs to be in save/restore,
479 // since it modifies the renderer's matrix
480 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
481
482 DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
483 RenderNode* child = childOp->mDisplayList;
484
485 renderer.concatMatrix(childOp->mTransformFromParent);
486 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
John Reckd0a0b2a2014-03-20 16:28:56 -0700487 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700488 childOp->mSkipInOrderDraw = true;
489
490 renderer.restoreToCount(restoreTo);
491 drawIndex++;
492 }
John Reck113e0822014-03-18 09:22:59 -0700493}
494
495template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700496void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
John Reck113e0822014-03-18 09:22:59 -0700497 for (size_t i = 0; i < mProjectedNodes.size(); i++) {
498 DrawDisplayListOp* childOp = mProjectedNodes[i];
499
500 // matrix save, concat, and restore can be done safely without allocating operations
501 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
502 renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
503 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
John Reckd0a0b2a2014-03-20 16:28:56 -0700504 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700505 childOp->mSkipInOrderDraw = true;
506 renderer.restoreToCount(restoreTo);
507 }
John Reck113e0822014-03-18 09:22:59 -0700508}
509
510/**
511 * This function serves both defer and replay modes, and will organize the displayList's component
512 * operations for a single frame:
513 *
514 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
515 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
516 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
517 * defer vs replay logic, per operation
518 */
519template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700520void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
521 const int level = handler.level();
John Reck113e0822014-03-18 09:22:59 -0700522 if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
523 ALOGW("Error: %s is drawing after destruction", mName.string());
524 CRASH();
525 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700526 if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
John Reck113e0822014-03-18 09:22:59 -0700527 DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
528 return;
529 }
530
Chris Craikb265e2c2014-03-27 15:50:09 -0700531 handler.startMark(mName.string());
532
John Reck113e0822014-03-18 09:22:59 -0700533#if DEBUG_DISPLAY_LIST
534 Rect* clipRect = renderer.getClipRect();
535 DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
536 level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
537 clipRect->right, clipRect->bottom);
538#endif
539
540 LinearAllocator& alloc = handler.allocator();
541 int restoreTo = renderer.getSaveCount();
542 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
John Reckd0a0b2a2014-03-20 16:28:56 -0700543 PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700544
545 DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
546 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
547
Chris Craikb265e2c2014-03-27 15:50:09 -0700548 setViewProperties<T>(renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700549
Chris Craik8c271ca2014-03-25 10:33:01 -0700550 bool quickRejected = properties().getClipToBounds()
551 && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
John Reck113e0822014-03-18 09:22:59 -0700552 if (!quickRejected) {
553 Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
554 buildZSortedChildList(zTranslatedNodes);
555
556 // for 3d root, draw children with negative z values
Chris Craikb265e2c2014-03-27 15:50:09 -0700557 issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700558
559 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
560 const int saveCountOffset = renderer.getSaveCount() - 1;
561 const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
562 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
563 DisplayListOp *op = mDisplayListData->displayListOps[i];
564
565#if DEBUG_DISPLAY_LIST
566 op->output(level + 1);
567#endif
John Reck113e0822014-03-18 09:22:59 -0700568 logBuffer.writeCommand(level, op->name());
John Reckd0a0b2a2014-03-20 16:28:56 -0700569 handler(op, saveCountOffset, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700570
571 if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
Chris Craikb265e2c2014-03-27 15:50:09 -0700572 issueOperationsOfProjectedChildren(renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700573 }
574 }
575
576 // for 3d root, draw children with positive z values
Chris Craikb265e2c2014-03-27 15:50:09 -0700577 issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700578 }
579
580 DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
581 handler(new (alloc) RestoreToCountOp(restoreTo),
John Reckd0a0b2a2014-03-20 16:28:56 -0700582 PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700583 renderer.setOverrideLayerAlpha(1.0f);
Chris Craikb265e2c2014-03-27 15:50:09 -0700584
585 handler.endMark();
John Reck113e0822014-03-18 09:22:59 -0700586}
587
588} /* namespace uirenderer */
589} /* namespace android */