Home | History | Annotate | Download | only in hwui
      1 /*
      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 #include "DamageAccumulator.h"
     18 
     19 #include <log/log.h>
     20 
     21 #include "RenderNode.h"
     22 #include "utils/MathUtils.h"
     23 
     24 namespace android {
     25 namespace uirenderer {
     26 
     27 enum TransformType {
     28     TransformInvalid = 0,
     29     TransformRenderNode,
     30     TransformMatrix4,
     31     TransformNone,
     32 };
     33 
     34 struct DirtyStack {
     35     TransformType type;
     36     union {
     37         const RenderNode* renderNode;
     38         const Matrix4* matrix4;
     39     };
     40     // When this frame is pop'd, this rect is mapped through the above transform
     41     // and applied to the previous (aka parent) frame
     42     SkRect pendingDirty;
     43     DirtyStack* prev;
     44     DirtyStack* next;
     45 };
     46 
     47 DamageAccumulator::DamageAccumulator() {
     48     mHead = mAllocator.create_trivial<DirtyStack>();
     49     memset(mHead, 0, sizeof(DirtyStack));
     50     // Create a root that we will not pop off
     51     mHead->prev = mHead;
     52     mHead->type = TransformNone;
     53 }
     54 
     55 static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
     56     if (currentFrame->prev != currentFrame) {
     57         computeTransformImpl(currentFrame->prev, outMatrix);
     58     }
     59     switch (currentFrame->type) {
     60         case TransformRenderNode:
     61             currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
     62             break;
     63         case TransformMatrix4:
     64             outMatrix->multiply(*currentFrame->matrix4);
     65             break;
     66         case TransformNone:
     67             // nothing to be done
     68             break;
     69         default:
     70             LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
     71                              currentFrame->type);
     72     }
     73 }
     74 
     75 void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
     76     outMatrix->loadIdentity();
     77     computeTransformImpl(mHead, outMatrix);
     78 }
     79 
     80 void DamageAccumulator::pushCommon() {
     81     if (!mHead->next) {
     82         DirtyStack* nextFrame = mAllocator.create_trivial<DirtyStack>();
     83         nextFrame->next = nullptr;
     84         nextFrame->prev = mHead;
     85         mHead->next = nextFrame;
     86     }
     87     mHead = mHead->next;
     88     mHead->pendingDirty.setEmpty();
     89 }
     90 
     91 void DamageAccumulator::pushTransform(const RenderNode* transform) {
     92     pushCommon();
     93     mHead->type = TransformRenderNode;
     94     mHead->renderNode = transform;
     95 }
     96 
     97 void DamageAccumulator::pushTransform(const Matrix4* transform) {
     98     pushCommon();
     99     mHead->type = TransformMatrix4;
    100     mHead->matrix4 = transform;
    101 }
    102 
    103 void DamageAccumulator::popTransform() {
    104     LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
    105     DirtyStack* dirtyFrame = mHead;
    106     mHead = mHead->prev;
    107     switch (dirtyFrame->type) {
    108         case TransformRenderNode:
    109             applyRenderNodeTransform(dirtyFrame);
    110             break;
    111         case TransformMatrix4:
    112             applyMatrix4Transform(dirtyFrame);
    113             break;
    114         case TransformNone:
    115             mHead->pendingDirty.join(dirtyFrame->pendingDirty);
    116             break;
    117         default:
    118             LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
    119     }
    120 }
    121 
    122 static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
    123     if (in.isEmpty()) return;
    124     Rect temp(in);
    125     if (CC_LIKELY(!matrix->isPerspective())) {
    126         matrix->mapRect(temp);
    127     } else {
    128         // Don't attempt to calculate damage for a perspective transform
    129         // as the numbers this works with can break the perspective
    130         // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
    131         temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    132     }
    133     out->join(RECT_ARGS(temp));
    134 }
    135 
    136 void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
    137     mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
    138 }
    139 
    140 static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
    141     if (in.isEmpty()) return;
    142     const SkMatrix* transform = props.getTransformMatrix();
    143     SkRect temp(in);
    144     if (transform && !transform->isIdentity()) {
    145         if (CC_LIKELY(!transform->hasPerspective())) {
    146             transform->mapRect(&temp);
    147         } else {
    148             // Don't attempt to calculate damage for a perspective transform
    149             // as the numbers this works with can break the perspective
    150             // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
    151             temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    152         }
    153     }
    154     temp.offset(props.getLeft(), props.getTop());
    155     out->join(temp);
    156 }
    157 
    158 static DirtyStack* findParentRenderNode(DirtyStack* frame) {
    159     while (frame->prev != frame) {
    160         frame = frame->prev;
    161         if (frame->type == TransformRenderNode) {
    162             return frame;
    163         }
    164     }
    165     return nullptr;
    166 }
    167 
    168 static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
    169     if (frame) {
    170         while (frame->prev != frame) {
    171             frame = frame->prev;
    172             if (frame->type == TransformRenderNode && frame->renderNode->hasProjectionReceiver()) {
    173                 return frame;
    174             }
    175         }
    176     }
    177     return nullptr;
    178 }
    179 
    180 static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
    181     SkRect* rect = &frame->pendingDirty;
    182     while (frame != end) {
    183         if (frame->type == TransformRenderNode) {
    184             mapRect(frame->renderNode->properties(), *rect, rect);
    185         } else {
    186             mapRect(frame->matrix4, *rect, rect);
    187         }
    188         frame = frame->prev;
    189     }
    190 }
    191 
    192 void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
    193     if (frame->pendingDirty.isEmpty()) {
    194         return;
    195     }
    196 
    197     const RenderProperties& props = frame->renderNode->properties();
    198     if (props.getAlpha() <= 0) {
    199         return;
    200     }
    201 
    202     // Perform clipping
    203     if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
    204         if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
    205             frame->pendingDirty.setEmpty();
    206         }
    207     }
    208 
    209     // apply all transforms
    210     mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
    211 
    212     // project backwards if necessary
    213     if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
    214         // First, find our parent RenderNode:
    215         DirtyStack* parentNode = findParentRenderNode(frame);
    216         // Find our parent's projection receiver, which is what we project onto
    217         DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
    218         if (projectionReceiver) {
    219             applyTransforms(frame, projectionReceiver);
    220             projectionReceiver->pendingDirty.join(frame->pendingDirty);
    221         }
    222 
    223         frame->pendingDirty.setEmpty();
    224     }
    225 }
    226 
    227 void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
    228     mHead->pendingDirty.join(left, top, right, bottom);
    229 }
    230 
    231 void DamageAccumulator::peekAtDirty(SkRect* dest) const {
    232     *dest = mHead->pendingDirty;
    233 }
    234 
    235 void DamageAccumulator::finish(SkRect* totalDirty) {
    236     LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p",
    237                         mHead->prev, mHead);
    238     // Root node never has a transform, so this is the fully mapped dirty rect
    239     *totalDirty = mHead->pendingDirty;
    240     totalDirty->roundOut(totalDirty);
    241     mHead->pendingDirty.setEmpty();
    242 }
    243 
    244 } /* namespace uirenderer */
    245 } /* namespace android */
    246