Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c044667f authored by Chris Craik's avatar Chris Craik Committed by Android (Google) Code Review
Browse files

Merge "Initial commit of new Canvas operation recording / replay"

parents b60d9855 b565df13
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -2,6 +2,8 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk

HWUI_NEW_OPS := false

hwui_src_files := \
    font/CacheTexture.cpp \
    font/Font.cpp \
@@ -85,6 +87,17 @@ hwui_cflags := \
    -Wall -Wno-unused-parameter -Wunreachable-code \
    -ffast-math -O3 -Werror


ifeq (true, $(HWUI_NEW_OPS))
    hwui_src_files += \
        BakedOpRenderer.cpp \
        OpReorderer.cpp \
        RecordingCanvas.cpp

    hwui_cflags += -DHWUI_NEW_OPS

endif

ifndef HWUI_COMPILE_SYMBOLS
    hwui_cflags += -fvisibility=hidden
endif
@@ -172,6 +185,13 @@ LOCAL_SRC_FILES += \
    unit_tests/LinearAllocatorTests.cpp \
    unit_tests/StringUtilsTests.cpp

ifeq (true, $(HWUI_NEW_OPS))
    LOCAL_SRC_FILES += \
        unit_tests/BakedOpStateTests.cpp \
        unit_tests/RecordingCanvasTests.cpp \
        unit_tests/OpReordererTests.cpp
endif

include $(BUILD_NATIVE_TEST)

# ------------------------
+126 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "BakedOpRenderer.h"

#include "Caches.h"
#include "Glop.h"
#include "GlopBuilder.h"
#include "renderstate/RenderState.h"
#include "utils/GLUtils.h"

namespace android {
namespace uirenderer {

Texture* BakedOpRenderer::Info::getTexture(const SkBitmap* bitmap) {
    Texture* texture = renderState.assetAtlas().getEntryTexture(bitmap);
    if (!texture) {
        return caches.textureCache.get(bitmap);
    }
    return texture;
}

void BakedOpRenderer::Info::renderGlop(const BakedOpState& state, const Glop& glop) {
    bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
    renderState.scissor().setEnabled(useScissor);
    if (useScissor) {
        const Rect& clip = state.computedState.clipRect;
        renderState.scissor().set(clip.left, viewportHeight - clip.bottom,
            clip.getWidth(), clip.getHeight());
    }
    renderState.render(glop, orthoMatrix);
    didDraw = true;
}

void BakedOpRenderer::startFrame(Info& info) {
    info.renderState.setViewport(info.viewportWidth, info.viewportHeight);
    info.renderState.blend().syncEnabled();
    Caches::getInstance().clearGarbage();

    if (!info.opaque) {
        // TODO: partial invalidate!
        info.renderState.scissor().setEnabled(false);
        glClear(GL_COLOR_BUFFER_BIT);
        info.didDraw = true;
    }
}
void BakedOpRenderer::endFrame(Info& info) {
    info.caches.pathCache.trim();
    info.caches.tessellationCache.trim();

#if DEBUG_OPENGL
    GLUtils::dumpGLErrors();
#endif

#if DEBUG_MEMORY_USAGE
    info.caches.dumpMemoryUsage();
#else
    if (Properties::debugLevel & kDebugMemory) {
        info.caches.dumpMemoryUsage();
    }
#endif
}

void BakedOpRenderer::onRenderNodeOp(Info*, const RenderNodeOp&, const BakedOpState&) {
    LOG_ALWAYS_FATAL("unsupported operation");
}

void BakedOpRenderer::onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
    info->caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
    Texture* texture = info->getTexture(op.bitmap);
    if (!texture) return;
    const AutoTexture autoCleanup(texture);

    const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
    Glop glop;
    GlopBuilder(info->renderState, info->caches, &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshTexturedUnitQuad(texture->uvMapper)
            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
            .build();
    info->renderGlop(state, glop);
}

void BakedOpRenderer::onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
    Glop glop;
    GlopBuilder(info->renderState, info->caches, &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshUnitQuad()
            .setFillPaint(*op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewMapUnitToRect(op.unmappedBounds)
            .build();
    info->renderGlop(state, glop);
}

void BakedOpRenderer::onSimpleRectsOp(Info* info, const SimpleRectsOp& op, const BakedOpState& state) {
    Glop glop;
    GlopBuilder(info->renderState, info->caches, &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
            .setFillPaint(*op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewOffsetRect(0, 0, op.unmappedBounds)
            .build();
    info->renderGlop(state, glop);
}


} // namespace uirenderer
} // namespace android
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_HWUI_BAKED_OP_RENDERER_H
#define ANDROID_HWUI_BAKED_OP_RENDERER_H

#include "BakedOpState.h"
#include "Matrix.h"

namespace android {
namespace uirenderer {

class Caches;
struct Glop;
class RenderState;

class BakedOpRenderer {
public:
    class Info {
    public:
        Info(Caches& caches, RenderState& renderState, int viewportWidth, int viewportHeight, bool opaque)
                : renderState(renderState)
                , caches(caches)
                , opaque(opaque)
                , viewportWidth(viewportWidth)
                , viewportHeight(viewportHeight) {
            orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
        }

        Texture* getTexture(const SkBitmap* bitmap);

        void renderGlop(const BakedOpState& state, const Glop& glop);
        RenderState& renderState;
        Caches& caches;

        bool didDraw = false;
        bool opaque;


        // where should these live? layer state object?
        int viewportWidth;
        int viewportHeight;
        Matrix4 orthoMatrix;
    };

    static void startFrame(Info& info);
    static void endFrame(Info& info);

    /**
     * Declare all "onBitmapOp(...)" style function for every op type.
     *
     * These functions will perform the actual rendering of the individual operations in OpenGL,
     * given the transform/clip and other state built into the BakedOpState object passed in.
     */
    #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info* info, const Type& op, const BakedOpState& state);
    MAP_OPS(BAKED_OP_RENDERER_METHOD);
};

}; // namespace uirenderer
}; // namespace android

#endif // ANDROID_HWUI_BAKED_OP_RENDERER_H
+142 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_HWUI_BAKED_OP_STATE_H
#define ANDROID_HWUI_BAKED_OP_STATE_H

#include "Matrix.h"
#include "RecordedOp.h"
#include "Rect.h"
#include "Snapshot.h"

namespace android {
namespace uirenderer {

namespace OpClipSideFlags {
    enum {
        None = 0x0,
        Left = 0x1,
        Top = 0x2,
        Right = 0x4,
        Bottom = 0x8,
        Full = 0xF,
        // ConservativeFull = 0x1F  needed?
    };
}

/**
 * Holds the resolved clip, transform, and bounds of a recordedOp, when replayed with a snapshot
 */
class ResolvedRenderState {
public:
    // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
    ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp) {
        /* TODO: benchmark a fast path for translate-only matrices, such as:
        if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
                && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
            float translateX = snapshot.transform->getTranslateX() + recordedOp.localMatrix.getTranslateX();
            float translateY = snapshot.transform->getTranslateY() + recordedOp.localMatrix.getTranslateY();
            transform.loadTranslate(translateX, translateY, 0);

            // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
            clipRect = recordedOp.localClipRect;
            clipRect.translate(translateX, translateY);
            clipRect.doIntersect(snapshot.getClipRect());
            clipRect.snapToPixelBoundaries();

            // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
            clippedBounds = recordedOp.unmappedBounds;
            clippedBounds.translate(translateX, translateY);
        } ... */

        // resolvedMatrix = parentMatrix * localMatrix
        transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);

        // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
        clipRect = recordedOp.localClipRect;
        snapshot.transform->mapRect(clipRect);
        clipRect.doIntersect(snapshot.getClipRect());
        clipRect.snapToPixelBoundaries();

        // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
        clippedBounds = recordedOp.unmappedBounds;
        transform.mapRect(clippedBounds);

        if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
        if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
        if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
        if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
        clippedBounds.doIntersect(clipRect);

        /**
         * TODO: once we support complex clips, we may want to reject to avoid that work where
         * possible. Should we:
         * 1 - quickreject based on clippedBounds, quick early (duplicating logic in resolvedOp)
         * 2 - merge stuff into tryConstruct factory method, so it can handle quickRejection
         *         and early return null in one place.
         */
    }
    Matrix4 transform;
    Rect clipRect;
    int clipSideFlags = 0;
    Rect clippedBounds;
};

/**
 * Self-contained op wrapper, containing all resolved state required to draw the op.
 *
 * Stashed pointers within all point to longer lived objects, with no ownership implied.
 */
class BakedOpState {
public:
    static BakedOpState* tryConstruct(LinearAllocator& allocator,
            const Snapshot& snapshot, const RecordedOp& recordedOp) {
        BakedOpState* bakedOp = new (allocator) BakedOpState(
                snapshot, recordedOp);
        if (bakedOp->computedState.clippedBounds.isEmpty()) {
            // bounds are empty, so op is rejected
            allocator.rewindIfLastAlloc(bakedOp);
            return nullptr;
        }
        return bakedOp;
    }

    static void* operator new(size_t size, LinearAllocator& allocator) {
        return allocator.alloc(size);
    }

    // computed state:
    const ResolvedRenderState computedState;

    // simple state (straight pointer/value storage):
    const float alpha;
    const RoundRectClipState* roundRectClipState;
    const ProjectionPathMask* projectionPathMask;
    const RecordedOp* op;

private:
    BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp)
            : computedState(snapshot, recordedOp)
            , alpha(snapshot.alpha)
            , roundRectClipState(snapshot.roundRectClipState)
            , projectionPathMask(snapshot.projectionPathMask)
            , op(&recordedOp) {}
};

}; // namespace uirenderer
}; // namespace android

#endif // ANDROID_HWUI_BAKED_OP_STATE_H
+1 −1
Original line number Diff line number Diff line
@@ -528,7 +528,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
    int insertBatchIndex = mBatches.size();
    if (!mBatches.empty()) {
        if (state->mBounds.isEmpty()) {
            // don't know the bounds for op, so add to last batch and start from scratch on next op
            // don't know the bounds for op, so create new batch and start from scratch on next op
            DrawBatch* b = new DrawBatch(deferInfo);
            b->add(op, state, deferInfo.opaqueOverBounds);
            mBatches.push_back(b);
Loading