blob: 83ad76f02b4dec60a07d3856e5abf29b7597350f [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
John Recka447d292014-06-11 18:39:44 -070018#define LOG_TAG "RenderNode"
John Reck113e0822014-03-18 09:22:59 -070019
20#include "RenderNode.h"
21
John Recke45b1fd2014-04-15 09:50:16 -070022#include <algorithm>
23
John Reck113e0822014-03-18 09:22:59 -070024#include <SkCanvas.h>
25#include <algorithm>
26
27#include <utils/Trace.h>
28
John Recke4267ea2014-06-03 15:53:15 -070029#include "DamageAccumulator.h"
John Reck113e0822014-03-18 09:22:59 -070030#include "Debug.h"
31#include "DisplayListOp.h"
32#include "DisplayListLogBuffer.h"
Chris Craike0bb87d2014-04-22 17:55:41 -070033#include "utils/MathUtils.h"
John Reck113e0822014-03-18 09:22:59 -070034
35namespace android {
36namespace uirenderer {
37
38void RenderNode::outputLogBuffer(int fd) {
39 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
40 if (logBuffer.isEmpty()) {
41 return;
42 }
43
44 FILE *file = fdopen(fd, "a");
45
46 fprintf(file, "\nRecent DisplayList operations\n");
47 logBuffer.outputCommands(file);
48
49 String8 cachesLog;
50 Caches::getInstance().dumpMemoryUsage(cachesLog);
51 fprintf(file, "\nCaches:\n%s", cachesLog.string());
52 fprintf(file, "\n");
53
54 fflush(file);
55}
56
John Reck8de65a82014-04-09 15:23:38 -070057RenderNode::RenderNode()
John Reckff941dc2014-05-14 16:34:14 -070058 : mDirtyPropertyFields(0)
John Reck8de65a82014-04-09 15:23:38 -070059 , mNeedsDisplayListDataSync(false)
60 , mDisplayListData(0)
John Recke45b1fd2014-04-15 09:50:16 -070061 , mStagingDisplayListData(0)
62 , mNeedsAnimatorsSync(false) {
John Reck113e0822014-03-18 09:22:59 -070063}
64
65RenderNode::~RenderNode() {
John Reck113e0822014-03-18 09:22:59 -070066 delete mDisplayListData;
John Reck8de65a82014-04-09 15:23:38 -070067 delete mStagingDisplayListData;
John Reck113e0822014-03-18 09:22:59 -070068}
69
John Reck8de65a82014-04-09 15:23:38 -070070void RenderNode::setStagingDisplayList(DisplayListData* data) {
71 mNeedsDisplayListDataSync = true;
72 delete mStagingDisplayListData;
73 mStagingDisplayListData = data;
74 if (mStagingDisplayListData) {
75 Caches::getInstance().registerFunctors(mStagingDisplayListData->functorCount);
John Reck113e0822014-03-18 09:22:59 -070076 }
77}
78
79/**
80 * This function is a simplified version of replay(), where we simply retrieve and log the
81 * display list. This function should remain in sync with the replay() function.
82 */
83void RenderNode::output(uint32_t level) {
84 ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
Chris Craik3f0854292014-04-15 16:18:08 -070085 getName(), isRenderable());
John Reck113e0822014-03-18 09:22:59 -070086 ALOGD("%*s%s %d", level * 2, "", "Save",
87 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
88
John Reckd0a0b2a2014-03-20 16:28:56 -070089 properties().debugOutputProperties(level);
John Reck113e0822014-03-18 09:22:59 -070090 int flags = DisplayListOp::kOpLogFlag_Recurse;
91 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
92 mDisplayListData->displayListOps[i]->output(level, flags);
93 }
94
Chris Craik3f0854292014-04-15 16:18:08 -070095 ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
John Reck113e0822014-03-18 09:22:59 -070096}
97
John Reckfe5e7b72014-05-23 17:42:28 -070098int RenderNode::getDebugSize() {
99 int size = sizeof(RenderNode);
100 if (mStagingDisplayListData) {
101 size += mStagingDisplayListData->allocator.usedSize();
102 }
103 if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
104 size += mDisplayListData->allocator.usedSize();
105 }
106 return size;
107}
108
John Reckf4198b72014-04-09 17:00:04 -0700109void RenderNode::prepareTree(TreeInfo& info) {
110 ATRACE_CALL();
111
112 prepareTreeImpl(info);
113}
114
John Recke4267ea2014-06-03 15:53:15 -0700115void RenderNode::damageSelf(TreeInfo& info) {
John Recka447d292014-06-11 18:39:44 -0700116 if (isRenderable() && properties().getAlpha() > 0) {
117 if (properties().getClipToBounds()) {
118 info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
119 } else {
120 // Hope this is big enough?
121 // TODO: Get this from the display list ops or something
122 info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX);
123 }
John Recke4267ea2014-06-03 15:53:15 -0700124 }
125}
126
127void RenderNode::prepareTreeImpl(TreeInfo& info) {
John Recka447d292014-06-11 18:39:44 -0700128 info.damageAccumulator->pushTransform(this);
John Recke4267ea2014-06-03 15:53:15 -0700129 if (info.mode == TreeInfo::MODE_FULL) {
130 pushStagingChanges(info);
131 evaluateAnimations(info);
132 } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) {
133 pushStagingChanges(info);
134 } else if (info.mode == TreeInfo::MODE_RT_ONLY) {
John Recke45b1fd2014-04-15 09:50:16 -0700135 evaluateAnimations(info);
136 }
John Reckf4198b72014-04-09 17:00:04 -0700137 prepareSubTree(info, mDisplayListData);
John Recka447d292014-06-11 18:39:44 -0700138 info.damageAccumulator->popTransform();
John Reckf4198b72014-04-09 17:00:04 -0700139}
140
John Reckff941dc2014-05-14 16:34:14 -0700141class PushAnimatorsFunctor {
142public:
143 PushAnimatorsFunctor(RenderNode* target, TreeInfo& info)
144 : mTarget(target), mInfo(info) {}
145
146 bool operator() (const sp<BaseRenderNodeAnimator>& animator) {
147 animator->setupStartValueIfNecessary(mTarget, mInfo);
148 return animator->isFinished();
149 }
150private:
151 RenderNode* mTarget;
152 TreeInfo& mInfo;
153};
John Recke45b1fd2014-04-15 09:50:16 -0700154
John Reckf4198b72014-04-09 17:00:04 -0700155void RenderNode::pushStagingChanges(TreeInfo& info) {
John Reckff941dc2014-05-14 16:34:14 -0700156 // Push the animators first so that setupStartValueIfNecessary() is called
157 // before properties() is trampled by stagingProperties(), as they are
158 // required by some animators.
John Recke45b1fd2014-04-15 09:50:16 -0700159 if (mNeedsAnimatorsSync) {
John Reck52622662014-04-30 14:19:56 -0700160 mAnimators.resize(mStagingAnimators.size());
John Reck52244ff2014-05-01 21:27:37 -0700161 std::vector< sp<BaseRenderNodeAnimator> >::iterator it;
John Reckff941dc2014-05-14 16:34:14 -0700162 PushAnimatorsFunctor functor(this, info);
John Recke45b1fd2014-04-15 09:50:16 -0700163 // hint: this means copy_if_not()
164 it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(),
John Reckff941dc2014-05-14 16:34:14 -0700165 mAnimators.begin(), functor);
John Recke45b1fd2014-04-15 09:50:16 -0700166 mAnimators.resize(std::distance(mAnimators.begin(), it));
167 }
John Reckff941dc2014-05-14 16:34:14 -0700168 if (mDirtyPropertyFields) {
169 mDirtyPropertyFields = 0;
John Recke4267ea2014-06-03 15:53:15 -0700170 damageSelf(info);
John Recka447d292014-06-11 18:39:44 -0700171 info.damageAccumulator->popTransform();
John Reckff941dc2014-05-14 16:34:14 -0700172 mProperties = mStagingProperties;
John Recke4267ea2014-06-03 15:53:15 -0700173 // We could try to be clever and only re-damage if the matrix changed.
174 // However, we don't need to worry about that. The cost of over-damaging
175 // here is only going to be a single additional map rect of this node
176 // plus a rect join(). The parent's transform (and up) will only be
177 // performed once.
John Recka447d292014-06-11 18:39:44 -0700178 info.damageAccumulator->pushTransform(this);
John Recke4267ea2014-06-03 15:53:15 -0700179 damageSelf(info);
John Reckff941dc2014-05-14 16:34:14 -0700180 }
John Reck8de65a82014-04-09 15:23:38 -0700181 if (mNeedsDisplayListDataSync) {
182 mNeedsDisplayListDataSync = false;
183 // Do a push pass on the old tree to handle freeing DisplayListData
184 // that are no longer used
John Recke4267ea2014-06-03 15:53:15 -0700185 TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING);
186 oldTreeInfo.damageAccumulator = info.damageAccumulator;
John Reckf4198b72014-04-09 17:00:04 -0700187 prepareSubTree(oldTreeInfo, mDisplayListData);
John Reck8de65a82014-04-09 15:23:38 -0700188 delete mDisplayListData;
189 mDisplayListData = mStagingDisplayListData;
190 mStagingDisplayListData = 0;
John Recke4267ea2014-06-03 15:53:15 -0700191 damageSelf(info);
John Reck8de65a82014-04-09 15:23:38 -0700192 }
John Reck8de65a82014-04-09 15:23:38 -0700193}
194
John Recke45b1fd2014-04-15 09:50:16 -0700195class AnimateFunctor {
196public:
John Reck52244ff2014-05-01 21:27:37 -0700197 AnimateFunctor(RenderNode* target, TreeInfo& info)
John Recke45b1fd2014-04-15 09:50:16 -0700198 : mTarget(target), mInfo(info) {}
199
John Reckff941dc2014-05-14 16:34:14 -0700200 bool operator() (const sp<BaseRenderNodeAnimator>& animator) {
John Reck52244ff2014-05-01 21:27:37 -0700201 return animator->animate(mTarget, mInfo);
John Recke45b1fd2014-04-15 09:50:16 -0700202 }
203private:
John Reck52244ff2014-05-01 21:27:37 -0700204 RenderNode* mTarget;
John Recke45b1fd2014-04-15 09:50:16 -0700205 TreeInfo& mInfo;
206};
207
208void RenderNode::evaluateAnimations(TreeInfo& info) {
209 if (!mAnimators.size()) return;
210
John Recke4267ea2014-06-03 15:53:15 -0700211 // TODO: Can we target this better? For now treat it like any other staging
212 // property push and just damage self before and after animators are run
213
214 damageSelf(info);
John Recka447d292014-06-11 18:39:44 -0700215 info.damageAccumulator->popTransform();
John Recke4267ea2014-06-03 15:53:15 -0700216
John Reck52244ff2014-05-01 21:27:37 -0700217 AnimateFunctor functor(this, info);
218 std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd;
John Recke45b1fd2014-04-15 09:50:16 -0700219 newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
220 mAnimators.erase(newEnd, mAnimators.end());
221 mProperties.updateMatrix();
John Reckf9be7792014-05-02 18:21:16 -0700222 info.out.hasAnimations |= mAnimators.size();
John Recke4267ea2014-06-03 15:53:15 -0700223
John Recka447d292014-06-11 18:39:44 -0700224 info.damageAccumulator->pushTransform(this);
John Recke4267ea2014-06-03 15:53:15 -0700225 damageSelf(info);
John Recke45b1fd2014-04-15 09:50:16 -0700226}
227
John Reckf4198b72014-04-09 17:00:04 -0700228void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
John Reck8de65a82014-04-09 15:23:38 -0700229 if (subtree) {
John Reck860d1552014-04-11 19:15:05 -0700230 TextureCache& cache = Caches::getInstance().textureCache;
John Reckf9be7792014-05-02 18:21:16 -0700231 info.out.hasFunctors |= subtree->functorCount;
John Reck860d1552014-04-11 19:15:05 -0700232 // TODO: Fix ownedBitmapResources to not require disabling prepareTextures
233 // and thus falling out of async drawing path.
234 if (subtree->ownedBitmapResources.size()) {
235 info.prepareTextures = false;
236 }
237 for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
238 info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
John Reckf4198b72014-04-09 17:00:04 -0700239 }
John Reck8de65a82014-04-09 15:23:38 -0700240 for (size_t i = 0; i < subtree->children().size(); i++) {
John Recka447d292014-06-11 18:39:44 -0700241 DrawDisplayListOp* op = subtree->children()[i];
242 RenderNode* childNode = op->mDisplayList;
243 info.damageAccumulator->pushTransform(&op->mTransformFromParent);
John Reckf4198b72014-04-09 17:00:04 -0700244 childNode->prepareTreeImpl(info);
John Recka447d292014-06-11 18:39:44 -0700245 info.damageAccumulator->popTransform();
John Reck5bf11bb2014-03-25 10:22:09 -0700246 }
John Reck113e0822014-03-18 09:22:59 -0700247 }
248}
249
250/*
251 * For property operations, we pass a savecount of 0, since the operations aren't part of the
252 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
John Reckd0a0b2a2014-03-20 16:28:56 -0700253 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount())
John Reck113e0822014-03-18 09:22:59 -0700254 */
255#define PROPERTY_SAVECOUNT 0
256
257template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700258void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
John Reck113e0822014-03-18 09:22:59 -0700259#if DEBUG_DISPLAY_LIST
Chris Craikb265e2c2014-03-27 15:50:09 -0700260 properties().debugOutputProperties(handler.level() + 1);
John Reck113e0822014-03-18 09:22:59 -0700261#endif
John Reckd0a0b2a2014-03-20 16:28:56 -0700262 if (properties().getLeft() != 0 || properties().getTop() != 0) {
263 renderer.translate(properties().getLeft(), properties().getTop());
John Reck113e0822014-03-18 09:22:59 -0700264 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700265 if (properties().getStaticMatrix()) {
Derek Sollenberger13908822013-12-10 12:28:58 -0500266 renderer.concatMatrix(*properties().getStaticMatrix());
John Reckd0a0b2a2014-03-20 16:28:56 -0700267 } else if (properties().getAnimationMatrix()) {
Derek Sollenberger13908822013-12-10 12:28:58 -0500268 renderer.concatMatrix(*properties().getAnimationMatrix());
John Reck113e0822014-03-18 09:22:59 -0700269 }
John Reckf7483e32014-04-11 08:54:47 -0700270 if (properties().hasTransformMatrix()) {
271 if (properties().isTransformTranslateOnly()) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700272 renderer.translate(properties().getTranslationX(), properties().getTranslationY());
John Reck113e0822014-03-18 09:22:59 -0700273 } else {
John Reckd0a0b2a2014-03-20 16:28:56 -0700274 renderer.concatMatrix(*properties().getTransformMatrix());
John Reck113e0822014-03-18 09:22:59 -0700275 }
276 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700277 bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds();
278 if (properties().getAlpha() < 1) {
279 if (properties().getCaching()) {
280 renderer.setOverrideLayerAlpha(properties().getAlpha());
281 } else if (!properties().getHasOverlappingRendering()) {
282 renderer.scaleAlpha(properties().getAlpha());
John Reck113e0822014-03-18 09:22:59 -0700283 } else {
284 // TODO: should be able to store the size of a DL at record time and not
285 // have to pass it into this call. In fact, this information might be in the
286 // location/size info that we store with the new native transform data.
287 int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
288 if (clipToBoundsNeeded) {
289 saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
290 clipToBoundsNeeded = false; // clipping done by saveLayer
291 }
292
293 SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
Chris Craik8c271ca2014-03-25 10:33:01 -0700294 0, 0, properties().getWidth(), properties().getHeight(),
295 properties().getAlpha() * 255, saveFlags);
John Reckd0a0b2a2014-03-20 16:28:56 -0700296 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700297 }
298 }
299 if (clipToBoundsNeeded) {
Chris Craik8c271ca2014-03-25 10:33:01 -0700300 ClipRectOp* op = new (handler.allocator()) ClipRectOp(
301 0, 0, properties().getWidth(), properties().getHeight(), SkRegion::kIntersect_Op);
John Reckd0a0b2a2014-03-20 16:28:56 -0700302 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700303 }
Chris Craik8c271ca2014-03-25 10:33:01 -0700304
305 if (CC_UNLIKELY(properties().hasClippingPath())) {
Chris Craik2bcad172014-05-14 18:11:23 -0700306 ClipPathOp* op = new (handler.allocator()) ClipPathOp(
307 properties().getClippingPath(), properties().getClippingPathOp());
John Reckd0a0b2a2014-03-20 16:28:56 -0700308 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700309 }
310}
311
312/**
313 * Apply property-based transformations to input matrix
314 *
315 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
316 * matrix computation instead of the Skia 3x3 matrix + camera hackery.
317 */
318void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700319 if (properties().getLeft() != 0 || properties().getTop() != 0) {
320 matrix.translate(properties().getLeft(), properties().getTop());
John Reck113e0822014-03-18 09:22:59 -0700321 }
John Reckd0a0b2a2014-03-20 16:28:56 -0700322 if (properties().getStaticMatrix()) {
323 mat4 stat(*properties().getStaticMatrix());
John Reck113e0822014-03-18 09:22:59 -0700324 matrix.multiply(stat);
John Reckd0a0b2a2014-03-20 16:28:56 -0700325 } else if (properties().getAnimationMatrix()) {
326 mat4 anim(*properties().getAnimationMatrix());
John Reck113e0822014-03-18 09:22:59 -0700327 matrix.multiply(anim);
328 }
Chris Craike0bb87d2014-04-22 17:55:41 -0700329
Chris Craikcc39e162014-04-25 18:34:11 -0700330 bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
Chris Craike0bb87d2014-04-22 17:55:41 -0700331 if (properties().hasTransformMatrix() || applyTranslationZ) {
John Reckf7483e32014-04-11 08:54:47 -0700332 if (properties().isTransformTranslateOnly()) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700333 matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
Chris Craikcc39e162014-04-25 18:34:11 -0700334 true3dTransform ? properties().getZ() : 0.0f);
John Reck113e0822014-03-18 09:22:59 -0700335 } else {
336 if (!true3dTransform) {
John Reckd0a0b2a2014-03-20 16:28:56 -0700337 matrix.multiply(*properties().getTransformMatrix());
John Reck113e0822014-03-18 09:22:59 -0700338 } else {
339 mat4 true3dMat;
340 true3dMat.loadTranslate(
John Reckd0a0b2a2014-03-20 16:28:56 -0700341 properties().getPivotX() + properties().getTranslationX(),
342 properties().getPivotY() + properties().getTranslationY(),
Chris Craikcc39e162014-04-25 18:34:11 -0700343 properties().getZ());
John Reckd0a0b2a2014-03-20 16:28:56 -0700344 true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
345 true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
346 true3dMat.rotate(properties().getRotation(), 0, 0, 1);
347 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
348 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
John Reck113e0822014-03-18 09:22:59 -0700349
350 matrix.multiply(true3dMat);
351 }
352 }
353 }
354}
355
356/**
357 * Organizes the DisplayList hierarchy to prepare for background projection reordering.
358 *
359 * This should be called before a call to defer() or drawDisplayList()
360 *
361 * Each DisplayList that serves as a 3d root builds its list of composited children,
362 * which are flagged to not draw in the standard draw loop.
363 */
364void RenderNode::computeOrdering() {
365 ATRACE_CALL();
366 mProjectedNodes.clear();
367
368 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
369 // transform properties are applied correctly to top level children
370 if (mDisplayListData == NULL) return;
John Reck087bc0c2014-04-04 16:20:08 -0700371 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
372 DrawDisplayListOp* childOp = mDisplayListData->children()[i];
John Reck113e0822014-03-18 09:22:59 -0700373 childOp->mDisplayList->computeOrderingImpl(childOp,
Chris Craik3f0854292014-04-15 16:18:08 -0700374 properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
John Reck113e0822014-03-18 09:22:59 -0700375 }
376}
377
378void RenderNode::computeOrderingImpl(
379 DrawDisplayListOp* opState,
Chris Craik3f0854292014-04-15 16:18:08 -0700380 const SkPath* outlineOfProjectionSurface,
John Reck113e0822014-03-18 09:22:59 -0700381 Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
382 const mat4* transformFromProjectionSurface) {
383 mProjectedNodes.clear();
384 if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
385
386 // TODO: should avoid this calculation in most cases
387 // TODO: just calculate single matrix, down to all leaf composited elements
388 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
389 localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
390
John Reckd0a0b2a2014-03-20 16:28:56 -0700391 if (properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700392 // composited projectee, flag for out of order draw, save matrix, and store in proj surface
393 opState->mSkipInOrderDraw = true;
394 opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
395 compositedChildrenOfProjectionSurface->add(opState);
396 } else {
397 // standard in order draw
398 opState->mSkipInOrderDraw = false;
399 }
400
John Reck087bc0c2014-04-04 16:20:08 -0700401 if (mDisplayListData->children().size() > 0) {
John Reck113e0822014-03-18 09:22:59 -0700402 const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
403 bool haveAppliedPropertiesToProjection = false;
John Reck087bc0c2014-04-04 16:20:08 -0700404 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
405 DrawDisplayListOp* childOp = mDisplayListData->children()[i];
John Reck113e0822014-03-18 09:22:59 -0700406 RenderNode* child = childOp->mDisplayList;
407
Chris Craik3f0854292014-04-15 16:18:08 -0700408 const SkPath* projectionOutline = NULL;
John Reck113e0822014-03-18 09:22:59 -0700409 Vector<DrawDisplayListOp*>* projectionChildren = NULL;
410 const mat4* projectionTransform = NULL;
John Reckd0a0b2a2014-03-20 16:28:56 -0700411 if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700412 // if receiving projections, collect projecting descendent
413
414 // Note that if a direct descendent is projecting backwards, we pass it's
415 // grandparent projection collection, since it shouldn't project onto it's
416 // parent, where it will already be drawing.
Chris Craik3f0854292014-04-15 16:18:08 -0700417 projectionOutline = properties().getOutline().getPath();
John Reck113e0822014-03-18 09:22:59 -0700418 projectionChildren = &mProjectedNodes;
419 projectionTransform = &mat4::identity();
420 } else {
421 if (!haveAppliedPropertiesToProjection) {
422 applyViewPropertyTransforms(localTransformFromProjectionSurface);
423 haveAppliedPropertiesToProjection = true;
424 }
Chris Craik3f0854292014-04-15 16:18:08 -0700425 projectionOutline = outlineOfProjectionSurface;
John Reck113e0822014-03-18 09:22:59 -0700426 projectionChildren = compositedChildrenOfProjectionSurface;
427 projectionTransform = &localTransformFromProjectionSurface;
428 }
Chris Craik3f0854292014-04-15 16:18:08 -0700429 child->computeOrderingImpl(childOp,
430 projectionOutline, projectionChildren, projectionTransform);
John Reck113e0822014-03-18 09:22:59 -0700431 }
432 }
John Reck113e0822014-03-18 09:22:59 -0700433}
434
435class DeferOperationHandler {
436public:
437 DeferOperationHandler(DeferStateStruct& deferStruct, int level)
438 : mDeferStruct(deferStruct), mLevel(level) {}
439 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
440 operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
441 }
442 inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
Chris Craikb265e2c2014-03-27 15:50:09 -0700443 inline void startMark(const char* name) {} // do nothing
444 inline void endMark() {}
445 inline int level() { return mLevel; }
446 inline int replayFlags() { return mDeferStruct.mReplayFlags; }
John Reck113e0822014-03-18 09:22:59 -0700447
448private:
449 DeferStateStruct& mDeferStruct;
450 const int mLevel;
451};
452
Chris Craikb265e2c2014-03-27 15:50:09 -0700453void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) {
454 DeferOperationHandler handler(deferStruct, 0);
Chris Craikcc39e162014-04-25 18:34:11 -0700455 if (MathUtils::isPositive(properties().getZ())) {
456 issueDrawShadowOperation(Matrix4::identity(), handler);
457 }
Chris Craikb265e2c2014-03-27 15:50:09 -0700458 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
459}
460
461void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) {
John Reck113e0822014-03-18 09:22:59 -0700462 DeferOperationHandler handler(deferStruct, level);
Chris Craikb265e2c2014-03-27 15:50:09 -0700463 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700464}
465
466class ReplayOperationHandler {
467public:
468 ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
469 : mReplayStruct(replayStruct), mLevel(level) {}
470 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
471#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
Chris Craik3f0854292014-04-15 16:18:08 -0700472 mReplayStruct.mRenderer.eventMark(operation->name());
John Reck113e0822014-03-18 09:22:59 -0700473#endif
474 operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
475 }
476 inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
Chris Craikb265e2c2014-03-27 15:50:09 -0700477 inline void startMark(const char* name) {
478 mReplayStruct.mRenderer.startMark(name);
479 }
480 inline void endMark() {
481 mReplayStruct.mRenderer.endMark();
Chris Craikb265e2c2014-03-27 15:50:09 -0700482 }
483 inline int level() { return mLevel; }
484 inline int replayFlags() { return mReplayStruct.mReplayFlags; }
John Reck113e0822014-03-18 09:22:59 -0700485
486private:
487 ReplayStateStruct& mReplayStruct;
488 const int mLevel;
489};
490
Chris Craikb265e2c2014-03-27 15:50:09 -0700491void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) {
492 ReplayOperationHandler handler(replayStruct, 0);
Chris Craikcc39e162014-04-25 18:34:11 -0700493 if (MathUtils::isPositive(properties().getZ())) {
494 issueDrawShadowOperation(Matrix4::identity(), handler);
495 }
Chris Craikb265e2c2014-03-27 15:50:09 -0700496 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
497}
498
499void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) {
John Reck113e0822014-03-18 09:22:59 -0700500 ReplayOperationHandler handler(replayStruct, level);
Chris Craikb265e2c2014-03-27 15:50:09 -0700501 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700502}
503
504void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
John Reck087bc0c2014-04-04 16:20:08 -0700505 if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return;
John Reck113e0822014-03-18 09:22:59 -0700506
John Reck087bc0c2014-04-04 16:20:08 -0700507 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
508 DrawDisplayListOp* childOp = mDisplayListData->children()[i];
John Reck113e0822014-03-18 09:22:59 -0700509 RenderNode* child = childOp->mDisplayList;
Chris Craikcc39e162014-04-25 18:34:11 -0700510 float childZ = child->properties().getZ();
John Reck113e0822014-03-18 09:22:59 -0700511
Chris Craike0bb87d2014-04-22 17:55:41 -0700512 if (!MathUtils::isZero(childZ)) {
John Reck113e0822014-03-18 09:22:59 -0700513 zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
514 childOp->mSkipInOrderDraw = true;
John Reckd0a0b2a2014-03-20 16:28:56 -0700515 } else if (!child->properties().getProjectBackwards()) {
John Reck113e0822014-03-18 09:22:59 -0700516 // regular, in order drawing DisplayList
517 childOp->mSkipInOrderDraw = false;
518 }
519 }
520
521 // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
522 std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
523}
524
Chris Craikb265e2c2014-03-27 15:50:09 -0700525template <class T>
526void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
Chris Craik61317322014-05-21 13:03:52 -0700527 if (properties().getAlpha() <= 0.0f || properties().getOutline().isEmpty()) return;
Chris Craikb265e2c2014-03-27 15:50:09 -0700528
529 mat4 shadowMatrixXY(transformFromParent);
530 applyViewPropertyTransforms(shadowMatrixXY);
531
532 // Z matrix needs actual 3d transformation, so mapped z values will be correct
533 mat4 shadowMatrixZ(transformFromParent);
534 applyViewPropertyTransforms(shadowMatrixZ, true);
535
536 const SkPath* outlinePath = properties().getOutline().getPath();
537 const RevealClip& revealClip = properties().getRevealClip();
538 const SkPath* revealClipPath = revealClip.hasConvexClip()
539 ? revealClip.getPath() : NULL; // only pass the reveal clip's path if it's convex
540
Chris Craik61317322014-05-21 13:03:52 -0700541 if (revealClipPath && revealClipPath->isEmpty()) return;
542
Chris Craikb265e2c2014-03-27 15:50:09 -0700543 /**
544 * The drawing area of the caster is always the same as the its perimeter (which
545 * the shadow system uses) *except* in the inverse clip case. Inform the shadow
546 * system that the caster's drawing area (as opposed to its perimeter) has been
547 * clipped, so that it knows the caster can't be opaque.
548 */
549 bool casterUnclipped = !revealClip.willClip() || revealClip.hasConvexClip();
550
551 DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp(
552 shadowMatrixXY, shadowMatrixZ,
553 properties().getAlpha(), casterUnclipped,
Chris Craikb265e2c2014-03-27 15:50:09 -0700554 outlinePath, revealClipPath);
555 handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
556}
557
John Reck113e0822014-03-18 09:22:59 -0700558#define SHADOW_DELTA 0.1f
559
560template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700561void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
John Reck113e0822014-03-18 09:22:59 -0700562 ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
563 const int size = zTranslatedNodes.size();
564 if (size == 0
565 || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
566 || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
567 // no 3d children to draw
568 return;
569 }
570
John Reck113e0822014-03-18 09:22:59 -0700571 /**
572 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
573 * with very similar Z heights to draw together.
574 *
575 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
576 * underneath both, and neither's shadow is drawn on top of the other.
577 */
578 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
579 size_t drawIndex, shadowIndex, endIndex;
580 if (mode == kNegativeZChildren) {
581 drawIndex = 0;
582 endIndex = nonNegativeIndex;
583 shadowIndex = endIndex; // draw no shadows
584 } else {
585 drawIndex = nonNegativeIndex;
586 endIndex = size;
587 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
588 }
Chris Craik3f0854292014-04-15 16:18:08 -0700589
590 DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
591 endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
592
John Reck113e0822014-03-18 09:22:59 -0700593 float lastCasterZ = 0.0f;
594 while (shadowIndex < endIndex || drawIndex < endIndex) {
595 if (shadowIndex < endIndex) {
596 DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
597 RenderNode* caster = casterOp->mDisplayList;
598 const float casterZ = zTranslatedNodes[shadowIndex].key;
599 // attempt to render the shadow if the caster about to be drawn is its caster,
600 // OR if its caster's Z value is similar to the previous potential caster
601 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
Chris Craikb265e2c2014-03-27 15:50:09 -0700602 caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler);
John Reck113e0822014-03-18 09:22:59 -0700603
604 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
605 shadowIndex++;
606 continue;
607 }
608 }
609
610 // only the actual child DL draw needs to be in save/restore,
611 // since it modifies the renderer's matrix
612 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
613
614 DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
615 RenderNode* child = childOp->mDisplayList;
616
617 renderer.concatMatrix(childOp->mTransformFromParent);
618 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
John Reckd0a0b2a2014-03-20 16:28:56 -0700619 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700620 childOp->mSkipInOrderDraw = true;
621
622 renderer.restoreToCount(restoreTo);
623 drawIndex++;
624 }
John Reck113e0822014-03-18 09:22:59 -0700625}
626
627template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700628void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
Chris Craik3f0854292014-04-15 16:18:08 -0700629 DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
630 const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
631 bool maskProjecteesWithPath = projectionReceiverOutline != NULL
632 && !projectionReceiverOutline->isRect(NULL);
633 int restoreTo = renderer.getSaveCount();
634
635 // If the projection reciever has an outline, we mask each of the projected rendernodes to it
636 // Either with clipRect, or special saveLayer masking
637 LinearAllocator& alloc = handler.allocator();
638 if (projectionReceiverOutline != NULL) {
639 const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
640 if (projectionReceiverOutline->isRect(NULL)) {
641 // mask to the rect outline simply with clipRect
642 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
643 PROPERTY_SAVECOUNT, properties().getClipToBounds());
644 ClipRectOp* clipOp = new (alloc) ClipRectOp(
645 outlineBounds.left(), outlineBounds.top(),
646 outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
647 handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
648 } else {
649 // wrap the projected RenderNodes with a SaveLayer that will mask to the outline
650 SaveLayerOp* op = new (alloc) SaveLayerOp(
651 outlineBounds.left(), outlineBounds.top(),
652 outlineBounds.right(), outlineBounds.bottom(),
653 255, SkCanvas::kARGB_ClipLayer_SaveFlag);
654 op->setMask(projectionReceiverOutline);
655 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
656
657 /* TODO: add optimizations here to take advantage of placement/size of projected
658 * children (which may shrink saveLayer area significantly). This is dependent on
659 * passing actual drawing/dirtying bounds of projected content down to native.
660 */
661 }
662 }
663
664 // draw projected nodes
John Reck113e0822014-03-18 09:22:59 -0700665 for (size_t i = 0; i < mProjectedNodes.size(); i++) {
666 DrawDisplayListOp* childOp = mProjectedNodes[i];
667
668 // matrix save, concat, and restore can be done safely without allocating operations
669 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
670 renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
671 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
John Reckd0a0b2a2014-03-20 16:28:56 -0700672 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700673 childOp->mSkipInOrderDraw = true;
674 renderer.restoreToCount(restoreTo);
675 }
Chris Craik3f0854292014-04-15 16:18:08 -0700676
677 if (projectionReceiverOutline != NULL) {
678 handler(new (alloc) RestoreToCountOp(restoreTo),
679 PROPERTY_SAVECOUNT, properties().getClipToBounds());
680 }
John Reck113e0822014-03-18 09:22:59 -0700681}
682
683/**
684 * This function serves both defer and replay modes, and will organize the displayList's component
685 * operations for a single frame:
686 *
687 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
688 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
689 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
690 * defer vs replay logic, per operation
691 */
692template <class T>
Chris Craikb265e2c2014-03-27 15:50:09 -0700693void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
694 const int level = handler.level();
John Reckd0a0b2a2014-03-20 16:28:56 -0700695 if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
Chris Craik3f0854292014-04-15 16:18:08 -0700696 DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
John Reck113e0822014-03-18 09:22:59 -0700697 return;
698 }
699
Chris Craik3f0854292014-04-15 16:18:08 -0700700 handler.startMark(getName());
Chris Craikb265e2c2014-03-27 15:50:09 -0700701
John Reck113e0822014-03-18 09:22:59 -0700702#if DEBUG_DISPLAY_LIST
Chris Craik3f0854292014-04-15 16:18:08 -0700703 const Rect& clipRect = renderer.getLocalClipBounds();
704 DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
705 level * 2, "", this, getName(),
706 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
John Reck113e0822014-03-18 09:22:59 -0700707#endif
708
709 LinearAllocator& alloc = handler.allocator();
710 int restoreTo = renderer.getSaveCount();
711 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
John Reckd0a0b2a2014-03-20 16:28:56 -0700712 PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700713
714 DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
715 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
716
Chris Craikb265e2c2014-03-27 15:50:09 -0700717 setViewProperties<T>(renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700718
Chris Craik8c271ca2014-03-25 10:33:01 -0700719 bool quickRejected = properties().getClipToBounds()
720 && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
John Reck113e0822014-03-18 09:22:59 -0700721 if (!quickRejected) {
Chris Craikdeeda3d2014-05-05 19:09:33 -0700722 if (mProperties.getOutline().willClip()) {
723 renderer.setClippingOutline(alloc, &(mProperties.getOutline()));
724 }
725
John Reck113e0822014-03-18 09:22:59 -0700726 Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
727 buildZSortedChildList(zTranslatedNodes);
728
729 // for 3d root, draw children with negative z values
Chris Craikb265e2c2014-03-27 15:50:09 -0700730 issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700731
732 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
733 const int saveCountOffset = renderer.getSaveCount() - 1;
734 const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
735 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
736 DisplayListOp *op = mDisplayListData->displayListOps[i];
737
738#if DEBUG_DISPLAY_LIST
739 op->output(level + 1);
740#endif
John Reck113e0822014-03-18 09:22:59 -0700741 logBuffer.writeCommand(level, op->name());
John Reckd0a0b2a2014-03-20 16:28:56 -0700742 handler(op, saveCountOffset, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700743
744 if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
Chris Craikb265e2c2014-03-27 15:50:09 -0700745 issueOperationsOfProjectedChildren(renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700746 }
747 }
748
749 // for 3d root, draw children with positive z values
Chris Craikb265e2c2014-03-27 15:50:09 -0700750 issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
John Reck113e0822014-03-18 09:22:59 -0700751 }
752
753 DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
754 handler(new (alloc) RestoreToCountOp(restoreTo),
John Reckd0a0b2a2014-03-20 16:28:56 -0700755 PROPERTY_SAVECOUNT, properties().getClipToBounds());
John Reck113e0822014-03-18 09:22:59 -0700756 renderer.setOverrideLayerAlpha(1.0f);
Chris Craikb265e2c2014-03-27 15:50:09 -0700757
Chris Craik3f0854292014-04-15 16:18:08 -0700758 DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName());
Chris Craikb265e2c2014-03-27 15:50:09 -0700759 handler.endMark();
John Reck113e0822014-03-18 09:22:59 -0700760}
761
762} /* namespace uirenderer */
763} /* namespace android */