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

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

Merge "Stencil support in new recorder/reorderer"

parents ce7d682a e4db79de
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@ ifeq (true, $(HWUI_NEW_OPS))
    hwui_src_files += \
        BakedOpDispatcher.cpp \
        BakedOpRenderer.cpp \
        BakedOpState.cpp \
        OpReorderer.cpp \
        RecordingCanvas.cpp

+12 −8
Original line number Diff line number Diff line
@@ -79,7 +79,9 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
            .setTransform(Matrix4::identity(), TransformFlags::None)
            .setModelViewIdentityEmptyBounds()
            .build();
    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
    ClipRect renderTargetClip(opList.clip);
    const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
    renderer.renderGlop(nullptr, clip, glop);
}

void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
@@ -183,7 +185,9 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
            .setTransform(Matrix4::identity(), TransformFlags::None)
            .setModelViewIdentityEmptyBounds()
            .build();
    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
    ClipRect renderTargetClip(opList.clip);
    const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
    renderer.renderGlop(nullptr, clip, glop);
}

static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
@@ -224,7 +228,7 @@ enum class TextRenderType {
};

static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
        const Rect* renderClip, TextRenderType renderType) {
        const ClipBase* renderClip, TextRenderType renderType) {
    FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();

    if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
@@ -272,7 +276,7 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake

    bool forceFinish = (renderType == TextRenderType::Flush);
    bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
    const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr;
    const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
    fontRenderer.renderPosText(op.paint, localOpClip,
            (const char*) op.glyphs, op.glyphCount, x, y,
            op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
@@ -287,7 +291,8 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake

void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
        const MergedBakedOpList& opList) {
    const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr;
    ClipRect renderTargetClip(opList.clip);
    const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
    for (size_t i = 0; i < opList.count; i++) {
        const BakedOpState& state = *(opList.states[i]);
        const TextOp& op = *(static_cast<const TextOp*>(state.op));
@@ -701,14 +706,13 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR
}

void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
    const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr;
    renderTextOp(renderer, op, state, clip, TextRenderType::Flush);
    renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
}

void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
    // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
    // TODO: respect clipSideFlags, once we record with bounds
    const Rect* renderTargetClip = &state.computedState.clipRect;
    auto renderTargetClip = state.computedState.clipState;

    FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
    fontRenderer.setFont(op.paint, SkMatrix::I());
+133 −8
Original line number Diff line number Diff line
@@ -62,15 +62,17 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const
void BakedOpRenderer::endLayer() {
    mRenderTarget.offscreenBuffer->updateMeshFromRegion();
    mRenderTarget.offscreenBuffer = nullptr;
    mRenderTarget.lastStencilClip = nullptr;

    // Detach the texture from the FBO
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
    mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
    mRenderTarget.frameBufferId = -1;
    mRenderTarget.frameBufferId = 0;
}

void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {
    LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0");
    mRenderState.bindFramebuffer(0);
    setViewport(width, height);
    mCaches.clearGarbage();
@@ -78,9 +80,39 @@ void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& re
    if (!mOpaque) {
        clearColorBuffer(repaintRect);
    }

    mRenderState.debugOverdraw(true, true);
}

void BakedOpRenderer::endFrame(const Rect& repaintRect) {
    if (CC_UNLIKELY(Properties::debugOverdraw)) {
        ClipRect overdrawClip(repaintRect);
        Rect viewportRect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight);
        // overdraw visualization
        for (int i = 1; i <= 4; i++) {
            if (i < 4) {
                // nth level of overdraw tests for n+1 draws per pixel
                mRenderState.stencil().enableDebugTest(i + 1, false);
            } else {
                // 4th level tests for 4 or higher draws per pixel
                mRenderState.stencil().enableDebugTest(4, true);
            }

            SkPaint paint;
            paint.setColor(mCaches.getOverdrawColor(i));
            Glop glop;
            GlopBuilder(mRenderState, mCaches, &glop)
                    .setRoundRectClipState(nullptr)
                    .setMeshUnitQuad()
                    .setFillPaint(paint, 1.0f)
                    .setTransform(Matrix4::identity(), TransformFlags::None)
                    .setModelViewMapUnitToRect(viewportRect)
                    .build();
            renderGlop(nullptr, &overdrawClip, glop);
        }
        mRenderState.stencil().disable();
    }

void BakedOpRenderer::endFrame() {
    mCaches.pathCache.trim();
    mCaches.tessellationCache.trim();

@@ -128,12 +160,104 @@ Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
    return texture;
}

void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) {
// clears and re-fills stencil with provided rendertarget space quads,
// and then put stencil into test mode
void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices,
        int incrementThreshold) {
    mRenderState.stencil().enableWrite(incrementThreshold);
    mRenderState.stencil().clear();
    Glop glop;
    GlopBuilder(mRenderState, mCaches, &glop)
            .setRoundRectClipState(nullptr)
            .setMeshIndexedQuads(quadVertices.data(), quadVertices.size() / 4)
            .setFillBlack()
            .setTransform(Matrix4::identity(), TransformFlags::None)
            .setModelViewIdentityEmptyBounds()
            .build();
    mRenderState.render(glop, mRenderTarget.orthoMatrix);
    mRenderState.stencil().enableTest(incrementThreshold);
}

void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) {
    auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
    int quadCount = rectList.getTransformedRectanglesCount();
    std::vector<Vertex> rectangleVertices;
    rectangleVertices.reserve(quadCount * 4);
    for (int i = 0; i < quadCount; i++) {
        const TransformedRectangle& tr(rectList.getTransformedRectangle(i));
        const Matrix4& transform = tr.getTransform();
        Rect bounds = tr.getBounds();
        if (transform.rectToRect()) {
            // If rectToRect, can simply map bounds before storing verts
            transform.mapRect(bounds);
            bounds.doIntersect(clip->rect);
            if (bounds.isEmpty()) {
                continue; // will be outside of scissor, skip
            }
        }

        rectangleVertices.push_back(Vertex{bounds.left, bounds.top});
        rectangleVertices.push_back(Vertex{bounds.right, bounds.top});
        rectangleVertices.push_back(Vertex{bounds.left, bounds.bottom});
        rectangleVertices.push_back(Vertex{bounds.right, bounds.bottom});

        if (!transform.rectToRect()) {
            // If not rectToRect, must map each point individually
            for (auto cur = rectangleVertices.end() - 4; cur < rectangleVertices.end(); cur++) {
                transform.mapPoint(cur->x, cur->y);
            }
        }
    }
    setupStencilQuads(rectangleVertices, rectList.getTransformedRectanglesCount());
}

void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) {
    auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region;

    std::vector<Vertex> regionVertices;
    SkRegion::Cliperator it(region, clip->rect.toSkIRect());
    while (!it.done()) {
        const SkIRect& r = it.rect();
        regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fTop});
        regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fTop});
        regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fBottom});
        regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fBottom});
        it.next();
    }
    setupStencilQuads(regionVertices, 0);
}

void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) {
    // prepare scissor / stencil
    mRenderState.scissor().setEnabled(clip != nullptr);
    if (clip) {
        mRenderState.scissor().set(clip->left, mRenderTarget.viewportHeight - clip->bottom,
            clip->getWidth(), clip->getHeight());
        mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect);
        if (CC_LIKELY(!Properties::debugOverdraw)) {
            // only modify stencil mode and content when it's not used for overdraw visualization
            if (CC_UNLIKELY(clip->mode != ClipMode::Rectangle)) {
                // NOTE: this pointer check is only safe for non-rect clips,
                // since rect clips may be created on the stack
                if (mRenderTarget.lastStencilClip != clip) {
                    // Stencil needed, but current stencil isn't up to date
                    mRenderTarget.lastStencilClip = clip;

                    if (mRenderTarget.offscreenBuffer) {
                        LOG_ALWAYS_FATAL("prepare layer stencil");
                    }

                    if (clip->mode == ClipMode::RectangleList) {
                        setupStencilRectList(clip);
                    } else {
                        setupStencilRegion(clip);
                    }
                }
            } else {
                mRenderState.stencil().disable();
            }
        }
    }

    // dirty offscreenbuffer
    if (dirtyBounds && mRenderTarget.offscreenBuffer) {
        // register layer damage to draw-back region
        android::Rect dirty(dirtyBounds->left, dirtyBounds->top,
@@ -142,17 +266,18 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) {
    }
}

void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop) {
void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const ClipBase* clip,
        const Glop& glop) {
    prepareRender(dirtyBounds, clip);
    mRenderState.render(glop, mRenderTarget.orthoMatrix);
    if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}

void BakedOpRenderer::renderFunctor(const FunctorOp& op, const BakedOpState& state) {
    prepareRender(&state.computedState.clippedBounds, &state.computedState.clipRect);
    prepareRender(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded());

    DrawGlInfo info;
    auto&& clip = state.computedState.clipRect;
    auto&& clip = state.computedState.clipRect();
    info.clipLeft = clip.left;
    info.clipTop = clip.top;
    info.clipRight = clip.right;
+9 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ class Caches;
struct Glop;
class Layer;
class RenderState;
struct ClipBase;

/**
 * Main rendering manager for a collection of work - one frame + any contained FBOs.
@@ -59,7 +60,7 @@ public:
    Caches& caches() { return mCaches; }

    void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
    void endFrame();
    void endFrame(const Rect& repaintRect);
    OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
    void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
    void endLayer();
@@ -68,21 +69,23 @@ public:
    const LightInfo& getLightInfo() const { return mLightInfo; }

    void renderGlop(const BakedOpState& state, const Glop& glop) {
        bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
        renderGlop(&state.computedState.clippedBounds,
                useScissor ? &state.computedState.clipRect : nullptr,
                state.computedState.getClipIfNeeded(),
                glop);
    }
    void renderFunctor(const FunctorOp& op, const BakedOpState& state);

    void renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop);
    void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop);
    bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; }
    void dirtyRenderTarget(const Rect& dirtyRect);
    bool didDraw() const { return mHasDrawn; }
private:
    void setViewport(uint32_t width, uint32_t height);
    void clearColorBuffer(const Rect& clearRect);
    void prepareRender(const Rect* dirtyBounds, const Rect* clip);
    void prepareRender(const Rect* dirtyBounds, const ClipBase* clip);
    void setupStencilRectList(const ClipBase* clip);
    void setupStencilRegion(const ClipBase* clip);
    void setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold);

    RenderState& mRenderState;
    Caches& mCaches;
@@ -97,6 +100,7 @@ private:
        uint32_t viewportWidth = 0;
        uint32_t viewportHeight = 0;
        Matrix4 orthoMatrix;
        const ClipBase* lastStencilClip = nullptr;
    } mRenderTarget;

    const LightInfo mLightInfo;
+79 −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 "BakedOpState.h"

#include "ClipArea.h"

namespace android {
namespace uirenderer {

ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
        const RecordedOp& recordedOp, bool expandForStroke) {
    // resolvedMatrix = parentMatrix * localMatrix
    transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);

    // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
    clippedBounds = recordedOp.unmappedBounds;
    if (CC_UNLIKELY(expandForStroke)) {
        // account for non-hairline stroke
        clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
    }
    transform.mapRect(clippedBounds);
    if (CC_UNLIKELY(expandForStroke
            && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
        // account for hairline stroke when stroke may be < 1 scaled pixel
        // Non translate || strokeWidth < 1 is conservative, but will cover all cases
        clippedBounds.outset(0.5f);
    }

    // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
    clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator,
            recordedOp.localClip, *(snapshot.transform));
    LOG_ALWAYS_FATAL_IF(!clipState, "must clip!");

    const Rect& clipRect = clipState->rect;
    if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) {
        // Rejected based on either empty clip, or bounds not intersecting with clip
        if (clipState) {
            allocator.rewindIfLastAlloc(clipState);
            clipState = nullptr;
        }
        clippedBounds.setEmpty();
    } else {
        // Not rejected! compute true clippedBounds and clipSideFlags
        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);
    }
}

ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot) {
    transform = *snapshot.transform;

    // Since the op doesn't have known bounds, we conservatively set the mapped bounds
    // to the current clipRect, and clipSideFlags to Full.
    clipState = snapshot.mutateClipArea().serializeClip(allocator);
    LOG_ALWAYS_FATAL_IF(!clipState, "clipState required");
    clippedBounds = clipState->rect;
    transform.mapRect(clippedBounds);
    clipSideFlags = OpClipSideFlags::Full;
}

} // namespace uirenderer
} // namespace android
Loading