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", currentFrame->type);
     71     }
     72 }
     73 
     74 void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
     75     outMatrix->loadIdentity();
     76     computeTransformImpl(mHead, outMatrix);
     77 }
     78 
     79 void DamageAccumulator::pushCommon() {
     80     if (!mHead->next) {
     81         DirtyStack* nextFrame = mAllocator.create_trivial<DirtyStack>();
     82         nextFrame->next = nullptr;
     83         nextFrame->prev = mHead;
     84         mHead->next = nextFrame;
     85     }
     86     mHead = mHead->next;
     87     mHead->pendingDirty.setEmpty();
     88 }
     89 
     90 void DamageAccumulator::pushTransform(const RenderNode* transform) {
     91     pushCommon();
     92     mHead->type = TransformRenderNode;
     93     mHead->renderNode = transform;
     94 }
     95 
     96 void DamageAccumulator::pushTransform(const Matrix4* transform) {
     97     pushCommon();
     98     mHead->type = TransformMatrix4;
     99     mHead->matrix4 = transform;
    100 }
    101 
    102 void DamageAccumulator::popTransform() {
    103     LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
    104     DirtyStack* dirtyFrame = mHead;
    105     mHead = mHead->prev;
    106     switch (dirtyFrame->type) {
    107     case TransformRenderNode:
    108         applyRenderNodeTransform(dirtyFrame);
    109         break;
    110     case TransformMatrix4:
    111         applyMatrix4Transform(dirtyFrame);
    112         break;
    113     case TransformNone:
    114         mHead->pendingDirty.join(dirtyFrame->pendingDirty);
    115         break;
    116     default:
    117         LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
    118     }
    119 }
    120 
    121 static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
    122     if (in.isEmpty()) return;
    123     Rect temp(in);
    124     if (CC_LIKELY(!matrix->isPerspective())) {
    125         matrix->mapRect(temp);
    126     } else {
    127         // Don't attempt to calculate damage for a perspective transform
    128         // as the numbers this works with can break the perspective
    129         // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
    130         temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    131     }
    132     out->join(RECT_ARGS(temp));
    133 }
    134 
    135 void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
    136     mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
    137 }
    138 
    139 static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
    140     if (in.isEmpty()) return;
    141     const SkMatrix* transform = props.getTransformMatrix();
    142     SkRect temp(in);
    143     if (transform && !transform->isIdentity()) {
    144         if (CC_LIKELY(!transform->hasPerspective())) {
    145             transform->mapRect(&temp);
    146         } else {
    147             // Don't attempt to calculate damage for a perspective transform
    148             // as the numbers this works with can break the perspective
    149             // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
    150             temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    151         }
    152     }
    153     temp.offset(props.getLeft(), props.getTop());
    154     out->join(temp);
    155 }
    156 
    157 static DirtyStack* findParentRenderNode(DirtyStack* frame) {
    158     while (frame->prev != frame) {
    159         frame = frame->prev;
    160         if (frame->type == TransformRenderNode) {
    161             return frame;
    162         }
    163     }
    164     return nullptr;
    165 }
    166 
    167 static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
    168     if (frame) {
    169         while (frame->prev != frame) {
    170             frame = frame->prev;
    171             if (frame->type == TransformRenderNode
    172                     && 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", mHead->prev, mHead);
    237     // Root node never has a transform, so this is the fully mapped dirty rect
    238     *totalDirty = mHead->pendingDirty;
    239     totalDirty->roundOut(totalDirty);
    240     mHead->pendingDirty.setEmpty();
    241 }
    242 
    243 } /* namespace uirenderer */
    244 } /* namespace android */
    245