FrameBuilder.cpp revision fc29f7acd1352efa97269b5f3856eb879d5cfd53
122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao/*
25460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao * Copyright (C) 2016 The Android Open Source Project
35460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao *
45460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao * Licensed under the Apache License, Version 2.0 (the "License");
55460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao * you may not use this file except in compliance with the License.
65460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao * You may obtain a copy of the License at
75460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao *
85460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao *      http://www.apache.org/licenses/LICENSE-2.0
937b74a387bb3993387029859c2d9d051c41c724eStephen Hines *
1037b74a387bb3993387029859c2d9d051c41c724eStephen Hines * Unless required by applicable law or agreed to in writing, software
1137b74a387bb3993387029859c2d9d051c41c724eStephen Hines * distributed under the License is distributed on an "AS IS" BASIS,
1237b74a387bb3993387029859c2d9d051c41c724eStephen Hines * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1337b74a387bb3993387029859c2d9d051c41c724eStephen Hines * See the License for the specific language governing permissions and
140dea6bc96bb52346737966839ac68644f7939f58Stephen Hines * limitations under the License.
150dea6bc96bb52346737966839ac68644f7939f58Stephen Hines */
160dea6bc96bb52346737966839ac68644f7939f58Stephen Hines
175460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "FrameBuilder.h"
1822add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao
195460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "DeferredLayerUpdater.h"
2022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao#include "LayerUpdateQueue.h"
2187f34658dec9097d987d254a990ea7f311bfc95fStephen Hines#include "RenderNode.h"
225460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "VectorDrawable.h"
235460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "renderstate/OffscreenBufferPool.h"
245460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "hwui/Canvas.h"
255460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "utils/FatVector.h"
265460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "utils/PaintUtils.h"
275460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao#include "utils/TraceUtils.h"
2837b74a387bb3993387029859c2d9d051c41c724eStephen Hines
2937b74a387bb3993387029859c2d9d051c41c724eStephen Hines#include <SkPathOps.h>
302bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar#include <utils/TypeHelpers.h>
316f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines
326f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hinesnamespace android {
336f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hinesnamespace uirenderer {
346f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines
356f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen HinesFrameBuilder::FrameBuilder(const SkRect& clip,
366f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        uint32_t viewportWidth, uint32_t viewportHeight,
372bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        const LightGeometry& lightGeometry, Caches& caches)
382bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        : mStdAllocator(mAllocator)
392bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        , mLayerBuilders(mStdAllocator)
402bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        , mLayerStack(mStdAllocator)
412bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        , mCanvasState(*this)
422bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        , mCaches(caches)
436f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        , mLightRadius(lightGeometry.radius)
442bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        , mDrawFbo0(true) {
452bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar
462bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar    // Prepare to defer Fbo0
472bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar    auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
482bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar    mLayerBuilders.push_back(fbo0);
492bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar    mLayerStack.push_back(0);
500dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
516f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines            clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
526f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines            lightGeometry.center);
536f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines}
546f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines
5587f34658dec9097d987d254a990ea7f311bfc95fStephen HinesFrameBuilder::FrameBuilder(const LayerUpdateQueue& layers,
5687f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        const LightGeometry& lightGeometry, Caches& caches)
5787f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        : mStdAllocator(mAllocator)
5887f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        , mLayerBuilders(mStdAllocator)
596f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        , mLayerStack(mStdAllocator)
606f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        , mCanvasState(*this)
616f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        , mCaches(caches)
626f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        , mLightRadius(lightGeometry.radius)
630dea6bc96bb52346737966839ac68644f7939f58Stephen Hines        , mDrawFbo0(false) {
640dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    // TODO: remove, with each layer on its own save stack
650dea6bc96bb52346737966839ac68644f7939f58Stephen Hines
660dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    // Prepare to defer Fbo0 (which will be empty)
670dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1));
680dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    mLayerBuilders.push_back(fbo0);
6937b74a387bb3993387029859c2d9d051c41c724eStephen Hines    mLayerStack.push_back(0);
70affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    mCanvasState.initializeSaveStack(1, 1,
71affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            0, 0, 1, 1,
72affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            lightGeometry.center);
735460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao
7437b74a387bb3993387029859c2d9d051c41c724eStephen Hines    deferLayers(layers);
755460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao}
7637b74a387bb3993387029859c2d9d051c41c724eStephen Hines
775460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liaovoid FrameBuilder::deferLayers(const LayerUpdateQueue& layers) {
7837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
795460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao    // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
8037b74a387bb3993387029859c2d9d051c41c724eStephen Hines    for (int i = layers.entries().size() - 1; i >= 0; i--) {
815460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao        RenderNode* layerNode = layers.entries()[i].renderNode.get();
8237b74a387bb3993387029859c2d9d051c41c724eStephen Hines        // only schedule repaint if node still on layer - possible it may have been
83affc150dc44fab1911775a49636d0ce85333b634Zonr Chang        // removed during a dropped frame, but layers may still remain scheduled so
8437b74a387bb3993387029859c2d9d051c41c724eStephen Hines        // as not to lose info on what portion is damaged
85affc150dc44fab1911775a49636d0ce85333b634Zonr Chang        OffscreenBuffer* layer = layerNode->getLayer();
8637b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (CC_LIKELY(layer)) {
87affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u",
8837b74a387bb3993387029859c2d9d051c41c724eStephen Hines                    layerNode->getName(), layerNode->getWidth(), layerNode->getHeight());
89affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
9037b74a387bb3993387029859c2d9d051c41c724eStephen Hines            Rect layerDamage = layers.entries()[i].damage;
91affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            // TODO: ensure layer damage can't be larger than layer
9237b74a387bb3993387029859c2d9d051c41c724eStephen Hines            layerDamage.doIntersect(0, 0, layer->viewportWidth, layer->viewportHeight);
93affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            layerNode->computeOrdering();
9437b74a387bb3993387029859c2d9d051c41c724eStephen Hines
95affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            // map current light center into RenderNode's coordinate space
9622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
9722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            layer->inverseTransformInWindow.mapPoint3d(lightCenter);
9837b74a387bb3993387029859c2d9d051c41c724eStephen Hines
9922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
10037b74a387bb3993387029859c2d9d051c41c724eStephen Hines                    layerDamage, lightCenter, nullptr, layerNode);
101affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
10237b74a387bb3993387029859c2d9d051c41c724eStephen Hines            if (layerNode->getDisplayList()) {
103affc150dc44fab1911775a49636d0ce85333b634Zonr Chang                deferNodeOps(*layerNode);
10437b74a387bb3993387029859c2d9d051c41c724eStephen Hines            }
105affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            restoreForLayer();
10637b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
107affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    }
10837b74a387bb3993387029859c2d9d051c41c724eStephen Hines}
109affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
11037b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::deferRenderNode(RenderNode& renderNode) {
111affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    renderNode.computeOrdering();
11237b74a387bb3993387029859c2d9d051c41c724eStephen Hines
113affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    mCanvasState.save(SaveFlags::MatrixClip);
11437b74a387bb3993387029859c2d9d051c41c724eStephen Hines    deferNodePropsAndOps(renderNode);
115affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    mCanvasState.restore();
11637b74a387bb3993387029859c2d9d051c41c724eStephen Hines}
11737b74a387bb3993387029859c2d9d051c41c724eStephen Hines
11837b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) {
119affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    renderNode.computeOrdering();
12004c59f3b00def22b7c75f5a490c323cec58a7c71Stephen Hines
12104c59f3b00def22b7c75f5a490c323cec58a7c71Stephen Hines    mCanvasState.save(SaveFlags::MatrixClip);
12204c59f3b00def22b7c75f5a490c323cec58a7c71Stephen Hines    mCanvasState.translate(tx, ty);
12304c59f3b00def22b7c75f5a490c323cec58a7c71Stephen Hines    mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
12404c59f3b00def22b7c75f5a490c323cec58a7c71Stephen Hines            SkClipOp::kIntersect);
12504c59f3b00def22b7c75f5a490c323cec58a7c71Stephen Hines    deferNodePropsAndOps(renderNode);
12637b74a387bb3993387029859c2d9d051c41c724eStephen Hines    mCanvasState.restore();
127affc150dc44fab1911775a49636d0ce85333b634Zonr Chang}
12837b74a387bb3993387029859c2d9d051c41c724eStephen Hines
129affc150dc44fab1911775a49636d0ce85333b634Zonr Changstatic Rect nodeBounds(RenderNode& node) {
130affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    auto& props = node.properties();
131affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    return Rect(props.getLeft(), props.getTop(),
132affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            props.getRight(), props.getBottom());
13337b74a387bb3993387029859c2d9d051c41c724eStephen Hines}
134affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
13537b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
13687f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        const Rect& contentDrawBounds) {
13737b74a387bb3993387029859c2d9d051c41c724eStephen Hines    if (nodes.size() < 1) return;
138affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    if (nodes.size() == 1) {
13937b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (!nodes[0]->nothingToDraw()) {
140affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            deferRenderNode(*nodes[0]);
14137b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
142affc150dc44fab1911775a49636d0ce85333b634Zonr Chang        return;
14337b74a387bb3993387029859c2d9d051c41c724eStephen Hines    }
144affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    // It there are multiple render nodes, they are laid out as follows:
14537b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // #0 - backdrop (content + caption)
146affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
14737b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // #2 - additional overlay nodes
148affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
14937b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // resizing however it might become partially visible. The following render loop will crop the
15087f34658dec9097d987d254a990ea7f311bfc95fStephen Hines    // backdrop against the content and draw the remaining part of it. It will then draw the content
15137b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // cropped to the backdrop (since that indicates a shrinking of the window).
152affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    //
15337b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // Additional nodes will be drawn on top with no particular clipping semantics.
154affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
15537b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // Usually the contents bounds should be mContentDrawBounds - however - we will
156affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    // move it towards the fixed edge to give it a more stable appearance (for the moment).
15737b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // If there is no content bounds we ignore the layering as stated above and start with 2.
158affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
15937b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // Backdrop bounds in render target space
160affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    const Rect backdrop = nodeBounds(*nodes[0]);
16137b74a387bb3993387029859c2d9d051c41c724eStephen Hines
162affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    // Bounds that content will fill in render target space (note content node bounds may be bigger)
16337b74a387bb3993387029859c2d9d051c41c724eStephen Hines    Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
164affc150dc44fab1911775a49636d0ce85333b634Zonr Chang    content.translate(backdrop.left, backdrop.top);
16537b74a387bb3993387029859c2d9d051c41c724eStephen Hines    if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
166affc150dc44fab1911775a49636d0ce85333b634Zonr Chang        // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
16737b74a387bb3993387029859c2d9d051c41c724eStephen Hines
168affc150dc44fab1911775a49636d0ce85333b634Zonr Chang        // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
16937b74a387bb3993387029859c2d9d051c41c724eStephen Hines        // also fill left/top. Currently, both 2up and freeform position content at the top/left of
170affc150dc44fab1911775a49636d0ce85333b634Zonr Chang        // the backdrop, so this isn't necessary.
17137b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (content.right < backdrop.right) {
172affc150dc44fab1911775a49636d0ce85333b634Zonr Chang            // draw backdrop to right side of content
17337b74a387bb3993387029859c2d9d051c41c724eStephen Hines            deferRenderNode(0, 0, Rect(content.right, backdrop.top,
174affc150dc44fab1911775a49636d0ce85333b634Zonr Chang                    backdrop.right, backdrop.bottom), *nodes[0]);
17522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao        }
17637b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (content.bottom < backdrop.bottom) {
17722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            // draw backdrop to bottom of content
17837b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
17922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            deferRenderNode(0, 0, Rect(content.left, content.bottom,
18022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao                    content.right, backdrop.bottom), *nodes[0]);
18137b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
18222add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao    }
18337b74a387bb3993387029859c2d9d051c41c724eStephen Hines
18422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao    if (!nodes[1]->nothingToDraw()) {
18522add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao        if (!backdrop.isEmpty()) {
18637b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // content node translation to catch up with backdrop
18722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            float dx = contentDrawBounds.left - backdrop.left;
18837b74a387bb3993387029859c2d9d051c41c724eStephen Hines            float dy = contentDrawBounds.top - backdrop.top;
18922add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao
19022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            Rect contentLocalClip = backdrop;
19137b74a387bb3993387029859c2d9d051c41c724eStephen Hines            contentLocalClip.translate(dx, dy);
19237b74a387bb3993387029859c2d9d051c41c724eStephen Hines            deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
19337b74a387bb3993387029859c2d9d051c41c724eStephen Hines        } else {
19422add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            deferRenderNode(*nodes[1]);
19537b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
19622add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao    }
19722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao
19837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // remaining overlay nodes, simply defer
19937b74a387bb3993387029859c2d9d051c41c724eStephen Hines    for (size_t index = 2; index < nodes.size(); index++) {
20037b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (!nodes[index]->nothingToDraw()) {
20122add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao            deferRenderNode(*nodes[index]);
20237b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
203d0fbbb227051be16931a1aa9b4a7722ac039c698Shih-wei Liao    }
20437b74a387bb3993387029859c2d9d051c41c724eStephen Hines}
20537b74a387bb3993387029859c2d9d051c41c724eStephen Hines
20637b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::onViewportInitialized() {}
207d0fbbb227051be16931a1aa9b4a7722ac039c698Shih-wei Liao
20837b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
209d0fbbb227051be16931a1aa9b4a7722ac039c698Shih-wei Liao
21037b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
211d0fbbb227051be16931a1aa9b4a7722ac039c698Shih-wei Liao    const RenderProperties& properties = node.properties();
21237b74a387bb3993387029859c2d9d051c41c724eStephen Hines    const Outline& outline = properties.getOutline();
213d0fbbb227051be16931a1aa9b4a7722ac039c698Shih-wei Liao    if (properties.getAlpha() <= 0
21437b74a387bb3993387029859c2d9d051c41c724eStephen Hines            || (outline.getShouldClip() && outline.isEmpty())
215d0fbbb227051be16931a1aa9b4a7722ac039c698Shih-wei Liao            || properties.getScaleX() == 0
21637b74a387bb3993387029859c2d9d051c41c724eStephen Hines            || properties.getScaleY() == 0) {
21722add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao        return; // rejected
21837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    }
2196f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines
22037b74a387bb3993387029859c2d9d051c41c724eStephen Hines    if (properties.getLeft() != 0 || properties.getTop() != 0) {
2216f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        mCanvasState.translate(properties.getLeft(), properties.getTop());
22237b74a387bb3993387029859c2d9d051c41c724eStephen Hines    }
2236f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    if (properties.getStaticMatrix()) {
22437b74a387bb3993387029859c2d9d051c41c724eStephen Hines        mCanvasState.concatMatrix(*properties.getStaticMatrix());
2256f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    } else if (properties.getAnimationMatrix()) {
22637b74a387bb3993387029859c2d9d051c41c724eStephen Hines        mCanvasState.concatMatrix(*properties.getAnimationMatrix());
2276f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    }
22837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    if (properties.hasTransformMatrix()) {
2296f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        if (properties.isTransformTranslateOnly()) {
230f7ac0f19a1c8d0ad14bcf6456ce368b830fea886Stephen Hines            mCanvasState.translate(properties.getTranslationX(), properties.getTranslationY());
23137b74a387bb3993387029859c2d9d051c41c724eStephen Hines        } else {
232f7ac0f19a1c8d0ad14bcf6456ce368b830fea886Stephen Hines            mCanvasState.concatMatrix(*properties.getTransformMatrix());
23337b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
234f7ac0f19a1c8d0ad14bcf6456ce368b830fea886Stephen Hines    }
23537b74a387bb3993387029859c2d9d051c41c724eStephen Hines
23687f34658dec9097d987d254a990ea7f311bfc95fStephen Hines    const int width = properties.getWidth();
23737b74a387bb3993387029859c2d9d051c41c724eStephen Hines    const int height = properties.getHeight();
23887f34658dec9097d987d254a990ea7f311bfc95fStephen Hines
23987f34658dec9097d987d254a990ea7f311bfc95fStephen Hines    Rect saveLayerBounds; // will be set to non-empty if saveLayer needed
24037b74a387bb3993387029859c2d9d051c41c724eStephen Hines    const bool isLayer = properties.effectiveLayerType() != LayerType::None;
24187f34658dec9097d987d254a990ea7f311bfc95fStephen Hines    int clipFlags = properties.getClippingFlags();
24237b74a387bb3993387029859c2d9d051c41c724eStephen Hines    if (properties.getAlpha() < 1) {
24387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        if (isLayer) {
2440dea6bc96bb52346737966839ac68644f7939f58Stephen Hines            clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
24537b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
2460dea6bc96bb52346737966839ac68644f7939f58Stephen Hines        if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
24737b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // simply scale rendering content's alpha
2480dea6bc96bb52346737966839ac68644f7939f58Stephen Hines            mCanvasState.scaleAlpha(properties.getAlpha());
24987f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        } else {
25037b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // schedule saveLayer by initializing saveLayerBounds
25187f34658dec9097d987d254a990ea7f311bfc95fStephen Hines            saveLayerBounds.set(0, 0, width, height);
25237b74a387bb3993387029859c2d9d051c41c724eStephen Hines            if (clipFlags) {
25387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines                properties.getClippingRectForFlags(clipFlags, &saveLayerBounds);
2542bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar                clipFlags = 0; // all clipping done by savelayer
2552bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar            }
2562bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        }
2572bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar
2582bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
2592bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar            // pretend alpha always causes savelayer to warn about
2602bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar            // performance problem affecting old versions
2612bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar            ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", node.getName(), width, height);
2622bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        }
2636f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    }
2642bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar    if (clipFlags) {
2656f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        Rect clipRect;
2660dea6bc96bb52346737966839ac68644f7939f58Stephen Hines        properties.getClippingRectForFlags(clipFlags, &clipRect);
2670dea6bc96bb52346737966839ac68644f7939f58Stephen Hines        mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
26837b74a387bb3993387029859c2d9d051c41c724eStephen Hines                SkClipOp::kIntersect);
2690dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    }
2700dea6bc96bb52346737966839ac68644f7939f58Stephen Hines
2710dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    if (properties.getRevealClip().willClip()) {
27237b74a387bb3993387029859c2d9d051c41c724eStephen Hines        Rect bounds;
2730dea6bc96bb52346737966839ac68644f7939f58Stephen Hines        properties.getRevealClip().getBounds(&bounds);
2740dea6bc96bb52346737966839ac68644f7939f58Stephen Hines        mCanvasState.setClippingRoundRect(mAllocator,
2750dea6bc96bb52346737966839ac68644f7939f58Stephen Hines                bounds, properties.getRevealClip().getRadius());
2762bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar    } else if (properties.getOutline().willClip()) {
27737b74a387bb3993387029859c2d9d051c41c724eStephen Hines        mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline()));
27837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    }
2790dea6bc96bb52346737966839ac68644f7939f58Stephen Hines
2806f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty()
2816f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines            || (properties.getClipToBounds()
28237b74a387bb3993387029859c2d9d051c41c724eStephen Hines                    && mCanvasState.quickRejectConservative(0, 0, width, height));
2836f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    if (!quickRejected) {
2846f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
28537b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (node.getLayer()) {
28637b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // HW layer
28737b74a387bb3993387029859c2d9d051c41c724eStephen Hines            LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(node);
2886f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines            BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
28987f34658dec9097d987d254a990ea7f311bfc95fStephen Hines            if (bakedOpState) {
29087f34658dec9097d987d254a990ea7f311bfc95fStephen Hines                // Node's layer already deferred, schedule it to render into parent layer
29137b74a387bb3993387029859c2d9d051c41c724eStephen Hines                currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
29287f34658dec9097d987d254a990ea7f311bfc95fStephen Hines            }
29387f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        } else if (CC_UNLIKELY(!saveLayerBounds.isEmpty())) {
29437b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // draw DisplayList contents within temporary, since persisted layer could not be used.
29537b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // (temp layers are clipped to viewport, since they don't persist offscreen content)
29637b74a387bb3993387029859c2d9d051c41c724eStephen Hines            SkPaint saveLayerPaint;
29787f34658dec9097d987d254a990ea7f311bfc95fStephen Hines            saveLayerPaint.setAlpha(properties.getAlpha());
2980dea6bc96bb52346737966839ac68644f7939f58Stephen Hines            deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>(
2990dea6bc96bb52346737966839ac68644f7939f58Stephen Hines                    saveLayerBounds,
30037b74a387bb3993387029859c2d9d051c41c724eStephen Hines                    Matrix4::identity(),
3010dea6bc96bb52346737966839ac68644f7939f58Stephen Hines                    nullptr, // no record-time clip - need only respect defer-time one
30237b74a387bb3993387029859c2d9d051c41c724eStephen Hines                    &saveLayerPaint));
30337b74a387bb3993387029859c2d9d051c41c724eStephen Hines            deferNodeOps(node);
30437b74a387bb3993387029859c2d9d051c41c724eStephen Hines            deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>());
30537b74a387bb3993387029859c2d9d051c41c724eStephen Hines        } else {
3060dea6bc96bb52346737966839ac68644f7939f58Stephen Hines            deferNodeOps(node);
30737b74a387bb3993387029859c2d9d051c41c724eStephen Hines        }
30837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    }
30937b74a387bb3993387029859c2d9d051c41c724eStephen Hines}
31037b74a387bb3993387029859c2d9d051c41c724eStephen Hines
3110dea6bc96bb52346737966839ac68644f7939f58Stephen Hinestypedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair;
3126f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines
31337b74a387bb3993387029859c2d9d051c41c724eStephen Hinestemplate <typename V>
3146f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hinesstatic void buildZSortedChildList(V* zTranslatedNodes,
31537b74a387bb3993387029859c2d9d051c41c724eStephen Hines        const DisplayList& displayList, const DisplayList::Chunk& chunk) {
3166f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    if (chunk.beginChildIndex == chunk.endChildIndex) return;
31737b74a387bb3993387029859c2d9d051c41c724eStephen Hines
3186f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines    for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
3196f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        RenderNodeOp* childOp = displayList.getChildren()[i];
32037b74a387bb3993387029859c2d9d051c41c724eStephen Hines        RenderNode* child = childOp->renderNode;
3216f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        float childZ = child->properties().getZ();
3226f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines
32337b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
32437b74a387bb3993387029859c2d9d051c41c724eStephen Hines            zTranslatedNodes->push_back(ZRenderNodeOpPair(childZ, childOp));
32537b74a387bb3993387029859c2d9d051c41c724eStephen Hines            childOp->skipInOrderDraw = true;
3266f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        } else if (!child->properties().getProjectBackwards()) {
3270dea6bc96bb52346737966839ac68644f7939f58Stephen Hines            // regular, in order drawing DisplayList
32837b74a387bb3993387029859c2d9d051c41c724eStephen Hines            childOp->skipInOrderDraw = false;
3290dea6bc96bb52346737966839ac68644f7939f58Stephen Hines        }
3300dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    }
3310dea6bc96bb52346737966839ac68644f7939f58Stephen Hines
33237b74a387bb3993387029859c2d9d051c41c724eStephen Hines    // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
3330dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    std::stable_sort(zTranslatedNodes->begin(), zTranslatedNodes->end());
33437b74a387bb3993387029859c2d9d051c41c724eStephen Hines}
33537b74a387bb3993387029859c2d9d051c41c724eStephen Hines
336affc150dc44fab1911775a49636d0ce85333b634Zonr Changtemplate <typename V>
33737b74a387bb3993387029859c2d9d051c41c724eStephen Hinesstatic size_t findNonNegativeIndex(const V& zTranslatedNodes) {
3385460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao    for (size_t i = 0; i < zTranslatedNodes.size(); i++) {
339affc150dc44fab1911775a49636d0ce85333b634Zonr Chang        if (zTranslatedNodes[i].key >= 0.0f) return i;
34022add6ff3426df1a85089fe6a6e1597ee3b6f300Shih-wei Liao    }
34137b74a387bb3993387029859c2d9d051c41c724eStephen Hines    return zTranslatedNodes.size();
34237b74a387bb3993387029859c2d9d051c41c724eStephen Hines}
34337b74a387bb3993387029859c2d9d051c41c724eStephen Hines
34404c59f3b00def22b7c75f5a490c323cec58a7c71Stephen Hinestemplate <typename V>
34537b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode,
34637b74a387bb3993387029859c2d9d051c41c724eStephen Hines        const V& zTranslatedNodes) {
34737b74a387bb3993387029859c2d9d051c41c724eStephen Hines    const int size = zTranslatedNodes.size();
34837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    if (size == 0
34937b74a387bb3993387029859c2d9d051c41c724eStephen Hines            || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
35037b74a387bb3993387029859c2d9d051c41c724eStephen Hines            || (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) {
35137b74a387bb3993387029859c2d9d051c41c724eStephen Hines        // no 3d children to draw
35237b74a387bb3993387029859c2d9d051c41c724eStephen Hines        return;
35337b74a387bb3993387029859c2d9d051c41c724eStephen Hines    }
35437b74a387bb3993387029859c2d9d051c41c724eStephen Hines
35537b74a387bb3993387029859c2d9d051c41c724eStephen Hines    /**
35637b74a387bb3993387029859c2d9d051c41c724eStephen Hines     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
35737b74a387bb3993387029859c2d9d051c41c724eStephen Hines     * with very similar Z heights to draw together.
35837b74a387bb3993387029859c2d9d051c41c724eStephen Hines     *
35937b74a387bb3993387029859c2d9d051c41c724eStephen Hines     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
36037b74a387bb3993387029859c2d9d051c41c724eStephen Hines     * underneath both, and neither's shadow is drawn on top of the other.
36137b74a387bb3993387029859c2d9d051c41c724eStephen Hines     */
36237b74a387bb3993387029859c2d9d051c41c724eStephen Hines    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
36337b74a387bb3993387029859c2d9d051c41c724eStephen Hines    size_t drawIndex, shadowIndex, endIndex;
36437b74a387bb3993387029859c2d9d051c41c724eStephen Hines    if (mode == ChildrenSelectMode::Negative) {
36537b74a387bb3993387029859c2d9d051c41c724eStephen Hines        drawIndex = 0;
36637b74a387bb3993387029859c2d9d051c41c724eStephen Hines        endIndex = nonNegativeIndex;
36737b74a387bb3993387029859c2d9d051c41c724eStephen Hines        shadowIndex = endIndex; // draw no shadows
36837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    } else {
36937b74a387bb3993387029859c2d9d051c41c724eStephen Hines        drawIndex = nonNegativeIndex;
37037b74a387bb3993387029859c2d9d051c41c724eStephen Hines        endIndex = size;
37137b74a387bb3993387029859c2d9d051c41c724eStephen Hines        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
37237b74a387bb3993387029859c2d9d051c41c724eStephen Hines    }
37337b74a387bb3993387029859c2d9d051c41c724eStephen Hines
37437b74a387bb3993387029859c2d9d051c41c724eStephen Hines    float lastCasterZ = 0.0f;
37537b74a387bb3993387029859c2d9d051c41c724eStephen Hines    while (shadowIndex < endIndex || drawIndex < endIndex) {
37637b74a387bb3993387029859c2d9d051c41c724eStephen Hines        if (shadowIndex < endIndex) {
37737b74a387bb3993387029859c2d9d051c41c724eStephen Hines            const RenderNodeOp* casterNodeOp = zTranslatedNodes[shadowIndex].value;
37837b74a387bb3993387029859c2d9d051c41c724eStephen Hines            const float casterZ = zTranslatedNodes[shadowIndex].key;
37937b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // attempt to render the shadow if the caster about to be drawn is its caster,
38037b74a387bb3993387029859c2d9d051c41c724eStephen Hines            // OR if its caster's Z value is similar to the previous potential caster
38137b74a387bb3993387029859c2d9d051c41c724eStephen Hines            if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) {
38237b74a387bb3993387029859c2d9d051c41c724eStephen Hines                deferShadow(reorderClip, *casterNodeOp);
38337b74a387bb3993387029859c2d9d051c41c724eStephen Hines
3840dea6bc96bb52346737966839ac68644f7939f58Stephen Hines                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
3850dea6bc96bb52346737966839ac68644f7939f58Stephen Hines                shadowIndex++;
3866f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines                continue;
3876f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines            }
38887f34658dec9097d987d254a990ea7f311bfc95fStephen Hines        }
38937b74a387bb3993387029859c2d9d051c41c724eStephen Hines
3902bf3f881f79c4d883f379e63725e788c310739a3Pirama Arumuga Nainar        const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
3916f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        deferRenderNodeOpImpl(*childOp);
3926f75755c9204b1d8817ae5a65a2f7e5af0ec3f70Stephen Hines        drawIndex++;
3930dea6bc96bb52346737966839ac68644f7939f58Stephen Hines    }
3945460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao}
3955460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao
39637b74a387bb3993387029859c2d9d051c41c724eStephen Hinesvoid FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterNodeOp) {
3975460a1f25d9ddecb5c70667267d66d51af177a99Shih-wei Liao    auto& node = *casterNodeOp.renderNode;
39837b74a387bb3993387029859c2d9d051c41c724eStephen Hines    auto& properties = node.properties();
399
400    if (properties.getAlpha() <= 0.0f
401            || properties.getOutline().getAlpha() <= 0.0f
402            || !properties.getOutline().getPath()
403            || properties.getScaleX() == 0
404            || properties.getScaleY() == 0) {
405        // no shadow to draw
406        return;
407    }
408
409    const SkPath* casterOutlinePath = properties.getOutline().getPath();
410    const SkPath* revealClipPath = properties.getRevealClip().getPath();
411    if (revealClipPath && revealClipPath->isEmpty()) return;
412
413    float casterAlpha = properties.getAlpha() * properties.getOutline().getAlpha();
414
415    // holds temporary SkPath to store the result of intersections
416    SkPath* frameAllocatedPath = nullptr;
417    const SkPath* casterPath = casterOutlinePath;
418
419    // intersect the shadow-casting path with the reveal, if present
420    if (revealClipPath) {
421        frameAllocatedPath = createFrameAllocatedPath();
422
423        Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath);
424        casterPath = frameAllocatedPath;
425    }
426
427    // intersect the shadow-casting path with the clipBounds, if present
428    if (properties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS) {
429        if (!frameAllocatedPath) {
430            frameAllocatedPath = createFrameAllocatedPath();
431        }
432        Rect clipBounds;
433        properties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
434        SkPath clipBoundsPath;
435        clipBoundsPath.addRect(clipBounds.left, clipBounds.top,
436                clipBounds.right, clipBounds.bottom);
437
438        Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath);
439        casterPath = frameAllocatedPath;
440    }
441
442    // apply reorder clip to shadow, so it respects clip at beginning of reorderable chunk
443    int restoreTo = mCanvasState.save(SaveFlags::MatrixClip);
444    mCanvasState.writableSnapshot()->applyClip(reorderClip,
445            *mCanvasState.currentSnapshot()->transform);
446    if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) {
447        Matrix4 shadowMatrixXY(casterNodeOp.localMatrix);
448        Matrix4 shadowMatrixZ(casterNodeOp.localMatrix);
449        node.applyViewPropertyTransforms(shadowMatrixXY, false);
450        node.applyViewPropertyTransforms(shadowMatrixZ, true);
451
452        sp<TessellationCache::ShadowTask> task = mCaches.tessellationCache.getShadowTask(
453                mCanvasState.currentTransform(),
454                mCanvasState.getLocalClipBounds(),
455                casterAlpha >= 1.0f,
456                casterPath,
457                &shadowMatrixXY, &shadowMatrixZ,
458                mCanvasState.currentSnapshot()->getRelativeLightCenter(),
459                mLightRadius);
460        ShadowOp* shadowOp = mAllocator.create<ShadowOp>(task, casterAlpha);
461        BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct(
462                mAllocator, *mCanvasState.writableSnapshot(), shadowOp);
463        if (CC_LIKELY(bakedOpState)) {
464            currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow);
465        }
466    }
467    mCanvasState.restoreToCount(restoreTo);
468}
469
470void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
471    int count = mCanvasState.save(SaveFlags::MatrixClip);
472    const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
473
474    SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy
475    if (projectionReceiverOutline) {
476        // transform the mask for this projector into render target space
477        // TODO: consider combining both transforms by stashing transform instead of applying
478        SkMatrix skCurrentTransform;
479        mCanvasState.currentTransform()->copyTo(skCurrentTransform);
480        projectionReceiverOutline->transform(
481                skCurrentTransform,
482                &transformedMaskPath);
483        mCanvasState.setProjectionPathMask(&transformedMaskPath);
484    }
485
486    for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) {
487        RenderNodeOp* childOp = renderNode.mProjectedNodes[i];
488        RenderNode& childNode = *childOp->renderNode;
489
490        // Draw child if it has content, but ignore state in childOp - matrix already applied to
491        // transformFromCompositingAncestor, and record-time clip is ignored when projecting
492        if (!childNode.nothingToDraw()) {
493            int restoreTo = mCanvasState.save(SaveFlags::MatrixClip);
494
495            // Apply transform between ancestor and projected descendant
496            mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
497
498            deferNodePropsAndOps(childNode);
499
500            mCanvasState.restoreToCount(restoreTo);
501        }
502    }
503    mCanvasState.restoreToCount(count);
504}
505
506/**
507 * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() methods.
508 *
509 * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
510 * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&)
511 */
512#define OP_RECEIVER(Type) \
513        [](FrameBuilder& frameBuilder, const RecordedOp& op) { frameBuilder.defer##Type(static_cast<const Type&>(op)); },
514void FrameBuilder::deferNodeOps(const RenderNode& renderNode) {
515    typedef void (*OpDispatcher) (FrameBuilder& frameBuilder, const RecordedOp& op);
516    static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
517
518    // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
519    const DisplayList& displayList = *(renderNode.getDisplayList());
520    for (auto& chunk : displayList.getChunks()) {
521        FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
522        buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
523
524        defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Negative, zTranslatedNodes);
525        for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
526            const RecordedOp* op = displayList.getOps()[opIndex];
527            receivers[op->opId](*this, *op);
528
529            if (CC_UNLIKELY(!renderNode.mProjectedNodes.empty()
530                    && displayList.projectionReceiveIndex >= 0
531                    && static_cast<int>(opIndex) == displayList.projectionReceiveIndex)) {
532                deferProjectedChildren(renderNode);
533            }
534        }
535        defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Positive, zTranslatedNodes);
536    }
537}
538
539void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
540    if (op.renderNode->nothingToDraw()) return;
541    int count = mCanvasState.save(SaveFlags::MatrixClip);
542
543    // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix)
544    mCanvasState.writableSnapshot()->applyClip(op.localClip,
545            *mCanvasState.currentSnapshot()->transform);
546    mCanvasState.concatMatrix(op.localMatrix);
547
548    // then apply state from node properties, and defer ops
549    deferNodePropsAndOps(*op.renderNode);
550
551    mCanvasState.restoreToCount(count);
552}
553
554void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
555    if (!op.skipInOrderDraw) {
556        deferRenderNodeOpImpl(op);
557    }
558}
559
560/**
561 * Defers an unmergeable, strokeable op, accounting correctly
562 * for paint's style on the bounds being computed.
563 */
564BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
565        BakedOpState::StrokeBehavior strokeBehavior) {
566    // Note: here we account for stroke when baking the op
567    BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
568            mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior);
569    if (!bakedState) return nullptr; // quick rejected
570
571    if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) {
572        bakedState->setupOpacity(op.paint);
573    }
574
575    currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
576    return bakedState;
577}
578
579/**
580 * Returns batch id for tessellatable shapes, based on paint. Checks to see if path effect/AA will
581 * be used, since they trigger significantly different rendering paths.
582 *
583 * Note: not used for lines/points, since they don't currently support path effects.
584 */
585static batchid_t tessBatchId(const RecordedOp& op) {
586    const SkPaint& paint = *(op.paint);
587    return paint.getPathEffect()
588            ? OpBatchType::AlphaMaskTexture
589            : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
590}
591
592void FrameBuilder::deferArcOp(const ArcOp& op) {
593    deferStrokeableOp(op, tessBatchId(op));
594}
595
596static bool hasMergeableClip(const BakedOpState& state) {
597    return !state.computedState.clipState
598            || state.computedState.clipState->mode == ClipMode::Rectangle;
599}
600
601void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
602    BakedOpState* bakedState = tryBakeOpState(op);
603    if (!bakedState) return; // quick rejected
604
605    if (op.bitmap->isOpaque()) {
606        bakedState->setupOpacity(op.paint);
607    }
608
609    // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
610    // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
611    // MergingDrawBatch::canMergeWith()
612    if (bakedState->computedState.transform.isSimple()
613            && bakedState->computedState.transform.positiveScale()
614            && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
615            && op.bitmap->colorType() != kAlpha_8_SkColorType
616            && hasMergeableClip(*bakedState)) {
617        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
618        currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
619    } else {
620        currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
621    }
622}
623
624void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
625    BakedOpState* bakedState = tryBakeOpState(op);
626    if (!bakedState) return; // quick rejected
627    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
628}
629
630void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
631    BakedOpState* bakedState = tryBakeOpState(op);
632    if (!bakedState) return; // quick rejected
633    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
634}
635
636void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
637    Bitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
638    SkPaint* paint = op.vectorDrawable->getPaint();
639    const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds,
640            op.localMatrix,
641            op.localClip,
642            paint,
643            &bitmap,
644            Rect(bitmap.width(), bitmap.height()));
645    deferBitmapRectOp(*resolvedOp);
646}
647
648void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
649    // allocate a temporary oval op (with mAllocator, so it persists until render), so the
650    // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
651    float x = *(op.x);
652    float y = *(op.y);
653    float radius = *(op.radius);
654    Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
655    const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>(
656            unmappedBounds,
657            op.localMatrix,
658            op.localClip,
659            op.paint);
660    deferOvalOp(*resolvedOp);
661}
662
663void FrameBuilder::deferColorOp(const ColorOp& op) {
664    BakedOpState* bakedState = tryBakeUnboundedOpState(op);
665    if (!bakedState) return; // quick rejected
666    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
667}
668
669void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
670    BakedOpState* bakedState = tryBakeUnboundedOpState(op);
671    if (!bakedState) return; // quick rejected
672    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
673}
674
675void FrameBuilder::deferLinesOp(const LinesOp& op) {
676    batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
677    deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
678}
679
680void FrameBuilder::deferOvalOp(const OvalOp& op) {
681    deferStrokeableOp(op, tessBatchId(op));
682}
683
684void FrameBuilder::deferPatchOp(const PatchOp& op) {
685    BakedOpState* bakedState = tryBakeOpState(op);
686    if (!bakedState) return; // quick rejected
687
688    if (bakedState->computedState.transform.isPureTranslate()
689            && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
690            && hasMergeableClip(*bakedState)) {
691        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
692
693        // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
694        currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId);
695    } else {
696        // Use Bitmap batchId since Bitmap+Patch use same shader
697        currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
698    }
699}
700
701void FrameBuilder::deferPathOp(const PathOp& op) {
702    auto state = deferStrokeableOp(op, OpBatchType::AlphaMaskTexture);
703    if (CC_LIKELY(state)) {
704        mCaches.pathCache.precache(op.path, op.paint);
705    }
706}
707
708void FrameBuilder::deferPointsOp(const PointsOp& op) {
709    batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
710    deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
711}
712
713void FrameBuilder::deferRectOp(const RectOp& op) {
714    deferStrokeableOp(op, tessBatchId(op));
715}
716
717void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
718    auto state = deferStrokeableOp(op, tessBatchId(op));
719    if (CC_LIKELY(state && !op.paint->getPathEffect())) {
720        // TODO: consider storing tessellation task in BakedOpState
721        mCaches.tessellationCache.precacheRoundRect(state->computedState.transform, *(op.paint),
722                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
723    }
724}
725
726void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
727    // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
728    // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
729    const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>(
730            Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
731            op.localMatrix,
732            op.localClip,
733            op.paint, *op.rx, *op.ry);
734    deferRoundRectOp(*resolvedOp);
735}
736
737void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) {
738    BakedOpState* bakedState = tryBakeOpState(op);
739    if (!bakedState) return; // quick rejected
740    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
741}
742
743static batchid_t textBatchId(const SkPaint& paint) {
744    // TODO: better handling of shader (since we won't care about color then)
745    return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
746}
747
748void FrameBuilder::deferTextOp(const TextOp& op) {
749    BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
750            mAllocator, *mCanvasState.writableSnapshot(), op,
751            BakedOpState::StrokeBehavior::StyleDefined);
752    if (!bakedState) return; // quick rejected
753
754    batchid_t batchId = textBatchId(*(op.paint));
755    if (bakedState->computedState.transform.isPureTranslate()
756            && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
757            && hasMergeableClip(*bakedState)) {
758        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
759        currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId);
760    } else {
761        currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
762    }
763
764    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
765    auto& totalTransform = bakedState->computedState.transform;
766    if (totalTransform.isPureTranslate() || totalTransform.isPerspective()) {
767        fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::I());
768    } else {
769        // Partial transform case, see BakedOpDispatcher::renderTextOp
770        float sx, sy;
771        totalTransform.decomposeScale(sx, sy);
772        fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::MakeScale(
773                roundf(std::max(1.0f, sx)),
774                roundf(std::max(1.0f, sy))));
775    }
776}
777
778void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
779    BakedOpState* bakedState = tryBakeUnboundedOpState(op);
780    if (!bakedState) return; // quick rejected
781    currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
782
783    mCaches.fontRenderer.getFontRenderer().precache(
784            op.paint, op.glyphs, op.glyphCount, SkMatrix::I());
785}
786
787void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
788    GlLayer* layer = static_cast<GlLayer*>(op.layerHandle->backingLayer());
789    if (CC_UNLIKELY(!layer || !layer->isRenderable())) return;
790
791    const TextureLayerOp* textureLayerOp = &op;
792    // Now safe to access transform (which was potentially unready at record time)
793    if (!layer->getTransform().isIdentity()) {
794        // non-identity transform present, so 'inject it' into op by copying + replacing matrix
795        Matrix4 combinedMatrix(op.localMatrix);
796        combinedMatrix.multiply(layer->getTransform());
797        textureLayerOp = mAllocator.create<TextureLayerOp>(op, combinedMatrix);
798    }
799    BakedOpState* bakedState = tryBakeOpState(*textureLayerOp);
800
801    if (!bakedState) return; // quick rejected
802    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
803}
804
805void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
806        float contentTranslateX, float contentTranslateY,
807        const Rect& repaintRect,
808        const Vector3& lightCenter,
809        const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
810    mCanvasState.save(SaveFlags::MatrixClip);
811    mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
812    mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
813    mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter);
814    mCanvasState.writableSnapshot()->transform->loadTranslate(
815            contentTranslateX, contentTranslateY, 0);
816    mCanvasState.writableSnapshot()->setClip(
817            repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom);
818
819    // create a new layer repaint, and push its index on the stack
820    mLayerStack.push_back(mLayerBuilders.size());
821    auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight,
822            repaintRect, beginLayerOp, renderNode);
823    mLayerBuilders.push_back(newFbo);
824}
825
826void FrameBuilder::restoreForLayer() {
827    // restore canvas, and pop finished layer off of the stack
828    mCanvasState.restore();
829    mLayerStack.pop_back();
830}
831
832// TODO: defer time rejection (when bounds become empty) + tests
833// Option - just skip layers with no bounds at playback + defer?
834void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) {
835    uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
836    uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
837
838    auto previous = mCanvasState.currentSnapshot();
839    Vector3 lightCenter = previous->getRelativeLightCenter();
840
841    // Combine all transforms used to present saveLayer content:
842    // parent content transform * canvas transform * bounds offset
843    Matrix4 contentTransform(*(previous->transform));
844    contentTransform.multiply(op.localMatrix);
845    contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top);
846
847    Matrix4 inverseContentTransform;
848    inverseContentTransform.loadInverse(contentTransform);
849
850    // map the light center into layer-relative space
851    inverseContentTransform.mapPoint3d(lightCenter);
852
853    // Clip bounds of temporary layer to parent's clip rect, so:
854    Rect saveLayerBounds(layerWidth, layerHeight);
855    //     1) transform Rect(width, height) into parent's space
856    //        note: left/top offsets put in contentTransform above
857    contentTransform.mapRect(saveLayerBounds);
858    //     2) intersect with parent's clip
859    saveLayerBounds.doIntersect(previous->getRenderTargetClip());
860    //     3) and transform back
861    inverseContentTransform.mapRect(saveLayerBounds);
862    saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight));
863    saveLayerBounds.roundOut();
864
865    // if bounds are reduced, will clip the layer's area by reducing required bounds...
866    layerWidth = saveLayerBounds.getWidth();
867    layerHeight = saveLayerBounds.getHeight();
868    // ...and shifting drawing content to account for left/top side clipping
869    float contentTranslateX = -saveLayerBounds.left;
870    float contentTranslateY = -saveLayerBounds.top;
871
872    saveForLayer(layerWidth, layerHeight,
873            contentTranslateX, contentTranslateY,
874            Rect(layerWidth, layerHeight),
875            lightCenter,
876            &op, nullptr);
877}
878
879void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) {
880    const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
881    int finishedLayerIndex = mLayerStack.back();
882
883    restoreForLayer();
884
885    // saveLayer will clip & translate the draw contents, so we need
886    // to translate the drawLayer by how much the contents was translated
887    // TODO: Unify this with beginLayerOp so we don't have to calculate this
888    // twice
889    uint32_t layerWidth = (uint32_t) beginLayerOp.unmappedBounds.getWidth();
890    uint32_t layerHeight = (uint32_t) beginLayerOp.unmappedBounds.getHeight();
891
892    auto previous = mCanvasState.currentSnapshot();
893    Vector3 lightCenter = previous->getRelativeLightCenter();
894
895    // Combine all transforms used to present saveLayer content:
896    // parent content transform * canvas transform * bounds offset
897    Matrix4 contentTransform(*(previous->transform));
898    contentTransform.multiply(beginLayerOp.localMatrix);
899    contentTransform.translate(beginLayerOp.unmappedBounds.left,
900            beginLayerOp.unmappedBounds.top);
901
902    Matrix4 inverseContentTransform;
903    inverseContentTransform.loadInverse(contentTransform);
904
905    // map the light center into layer-relative space
906    inverseContentTransform.mapPoint3d(lightCenter);
907
908    // Clip bounds of temporary layer to parent's clip rect, so:
909    Rect saveLayerBounds(layerWidth, layerHeight);
910    //     1) transform Rect(width, height) into parent's space
911    //        note: left/top offsets put in contentTransform above
912    contentTransform.mapRect(saveLayerBounds);
913    //     2) intersect with parent's clip
914    saveLayerBounds.doIntersect(previous->getRenderTargetClip());
915    //     3) and transform back
916    inverseContentTransform.mapRect(saveLayerBounds);
917    saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight));
918    saveLayerBounds.roundOut();
919
920    Matrix4 localMatrix(beginLayerOp.localMatrix);
921    localMatrix.translate(saveLayerBounds.left, saveLayerBounds.top);
922
923    // record the draw operation into the previous layer's list of draw commands
924    // uses state from the associated beginLayerOp, since it has all the state needed for drawing
925    LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(
926            beginLayerOp.unmappedBounds,
927            localMatrix,
928            beginLayerOp.localClip,
929            beginLayerOp.paint,
930            &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer));
931    BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
932
933    if (bakedOpState) {
934        // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
935        currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
936    } else {
937        // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
938        // TODO: need to prevent any render work from being done
939        // - create layerop earlier for reject purposes?
940        mLayerBuilders[finishedLayerIndex]->clear();
941        return;
942    }
943}
944
945void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
946    Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
947    boundsTransform.multiply(op.localMatrix);
948
949    Rect dstRect(op.unmappedBounds);
950    boundsTransform.mapRect(dstRect);
951    dstRect.roundOut();
952    dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());
953
954    if (dstRect.isEmpty()) {
955        // Unclipped layer rejected - push a null op, so next EndUnclippedLayerOp is ignored
956        currentLayer().activeUnclippedSaveLayers.push_back(nullptr);
957    } else {
958        // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
959        OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
960
961        /**
962         * First, defer an operation to copy out the content from the rendertarget into a layer.
963         */
964        auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
965        BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
966                &(currentLayer().repaintClip), dstRect, *copyToOp);
967        currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
968
969        /**
970         * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
971         * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
972         */
973        currentLayer().deferLayerClear(dstRect);
974
975        /**
976         * And stash an operation to copy that layer back under the rendertarget until
977         * a balanced EndUnclippedLayerOp is seen
978         */
979        auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
980        bakedState = BakedOpState::directConstruct(mAllocator,
981                &(currentLayer().repaintClip), dstRect, *copyFromOp);
982        currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
983    }
984}
985
986void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
987    LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
988
989    BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
990    currentLayer().activeUnclippedSaveLayers.pop_back();
991    if (copyFromLayerOp) {
992        currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
993    }
994}
995
996void FrameBuilder::finishDefer() {
997    mCaches.fontRenderer.endPrecaching();
998}
999
1000} // namespace uirenderer
1001} // namespace android
1002