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

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

Merge "Partial unclipped save layer support"

parents 2e8bafc3 b87eadda
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -753,5 +753,13 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
    }
    }
}
}


void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
    LOG_ALWAYS_FATAL("TODO!");
}

void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
    LOG_ALWAYS_FATAL("TODO!");
}

} // namespace uirenderer
} // namespace uirenderer
} // namespace android
} // namespace android
+2 −1
Original line number Original line Diff line number Diff line
@@ -236,12 +236,13 @@ void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) {
}
}


void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) {
void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) {
    // prepare scissor / stencil
    // Prepare scissor (done before stencil, to simplify filling stencil)
    mRenderState.scissor().setEnabled(clip != nullptr);
    mRenderState.scissor().setEnabled(clip != nullptr);
    if (clip) {
    if (clip) {
        mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect);
        mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect);
    }
    }


    // If stencil may be used for clipping, enable it, fill it, or disable it as appropriate
    if (CC_LIKELY(!Properties::debugOverdraw)) {
    if (CC_LIKELY(!Properties::debugOverdraw)) {
        // only modify stencil mode and content when it's not used for overdraw visualization
        // only modify stencil mode and content when it's not used for overdraw visualization
        if (CC_UNLIKELY(clip && clip->mode != ClipMode::Rectangle)) {
        if (CC_UNLIKELY(clip && clip->mode != ClipMode::Rectangle)) {
+6 −0
Original line number Original line Diff line number Diff line
@@ -75,5 +75,11 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s
    clipSideFlags = OpClipSideFlags::Full;
    clipSideFlags = OpClipSideFlags::Full;
}
}


ResolvedRenderState::ResolvedRenderState(const Rect& dstRect)
        : transform(Matrix4::identity())
        , clipState(nullptr)
        , clippedBounds(dstRect)
        , clipSideFlags(OpClipSideFlags::None) {}

} // namespace uirenderer
} // namespace uirenderer
} // namespace android
} // namespace android
+23 −3
Original line number Original line Diff line number Diff line
@@ -58,6 +58,10 @@ public:
    // Constructor for unbounded ops without transform/clip (namely shadows)
    // Constructor for unbounded ops without transform/clip (namely shadows)
    ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot);
    ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot);


    // Constructor for primitive ops without clip or transform
    // NOTE: these ops can't be queried for RT clip / local clip
    ResolvedRenderState(const Rect& dstRect);

    Rect computeLocalSpaceClip() const {
    Rect computeLocalSpaceClip() const {
        Matrix4 inverse;
        Matrix4 inverse;
        inverse.loadInverse(transform);
        inverse.loadInverse(transform);
@@ -67,10 +71,12 @@ public:
        return outClip;
        return outClip;
    }
    }


    Matrix4 transform;
    // NOTE: Can only be used on clipped/snapshot based ops
    const Rect& clipRect() const {
    const Rect& clipRect() const {
        return clipState->rect;
        return clipState->rect;
    }
    }

    // NOTE: Can only be used on clipped/snapshot based ops
    bool requiresClip() const {
    bool requiresClip() const {
        return clipSideFlags != OpClipSideFlags::None
        return clipSideFlags != OpClipSideFlags::None
                || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle);
                || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle);
@@ -80,9 +86,11 @@ public:
    const ClipBase* getClipIfNeeded() const {
    const ClipBase* getClipIfNeeded() const {
        return requiresClip() ? clipState : nullptr;
        return requiresClip() ? clipState : nullptr;
    }
    }

    Matrix4 transform;
    const ClipBase* clipState = nullptr;
    const ClipBase* clipState = nullptr;
    int clipSideFlags = 0;
    Rect clippedBounds;
    Rect clippedBounds;
    int clipSideFlags = 0;
};
};


/**
/**
@@ -135,12 +143,17 @@ public:
        return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
        return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
    }
    }


    static BakedOpState* directConstruct(LinearAllocator& allocator,
            const Rect& dstRect, const RecordedOp& recordedOp) {
        return new (allocator) BakedOpState(dstRect, recordedOp);
    }

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


    // computed state:
    // computed state:
    const ResolvedRenderState computedState;
    ResolvedRenderState computedState;


    // simple state (straight pointer/value storage):
    // simple state (straight pointer/value storage):
    const float alpha;
    const float alpha;
@@ -163,6 +176,13 @@ private:
            , roundRectClipState(snapshot.roundRectClipState)
            , roundRectClipState(snapshot.roundRectClipState)
            , projectionPathMask(snapshot.projectionPathMask)
            , projectionPathMask(snapshot.projectionPathMask)
            , op(shadowOpPtr) {}
            , op(shadowOpPtr) {}

    BakedOpState(const Rect& dstRect, const RecordedOp& recordedOp)
            : computedState(dstRect)
            , alpha(1.0f)
            , roundRectClipState(nullptr)
            , projectionPathMask(nullptr)
            , op(&recordedOp) {}
};
};


}; // namespace uirenderer
}; // namespace uirenderer
+103 −7
Original line number Original line Diff line number Diff line
@@ -240,8 +240,50 @@ void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& cli
    }
    }
}
}


void OpReorderer::LayerReorderer::deferLayerClear(const Rect& rect) {
    mClearRects.push_back(rect);
}

void OpReorderer::LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
    if (CC_UNLIKELY(!mClearRects.empty())) {
        const int vertCount = mClearRects.size() * 4;
        // put the verts in the frame allocator, since
        //     1) SimpleRectsOps needs verts, not rects
        //     2) even if mClearRects stored verts, std::vectors will move their contents
        Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));

        Vertex* currentVert = verts;
        Rect bounds = mClearRects[0];
        for (auto&& rect : mClearRects) {
            bounds.unionWith(rect);
            Vertex::set(currentVert++, rect.left, rect.top);
            Vertex::set(currentVert++, rect.right, rect.top);
            Vertex::set(currentVert++, rect.left, rect.bottom);
            Vertex::set(currentVert++, rect.right, rect.bottom);
        }
        mClearRects.clear(); // discard rects before drawing so this method isn't reentrant

        // One or more unclipped saveLayers have been enqueued, with deferred clears.
        // Flush all of these clears with a single draw
        SkPaint* paint = allocator.create<SkPaint>();
        paint->setXfermodeMode(SkXfermode::kClear_Mode);
        SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
                Matrix4::identity(), nullptr, paint,
                verts, vertCount);
        BakedOpState* bakedState = BakedOpState::directConstruct(allocator, bounds, *op);


        deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
    }
}

void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
        BakedOpState* op, batchid_t batchId) {
        BakedOpState* op, batchid_t batchId) {
    if (batchId != OpBatchType::CopyToLayer) {
        // if first op after one or more unclipped saveLayers, flush the layer clears
        flushLayerClears(allocator);
    }

    OpBatch* targetBatch = mBatchLookup[batchId];
    OpBatch* targetBatch = mBatchLookup[batchId];


    size_t insertBatchIndex = mBatches.size();
    size_t insertBatchIndex = mBatches.size();
@@ -260,10 +302,12 @@ void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
    }
    }
}
}


// insertion point of a new batch, will hopefully be immediately after similar batch
// (generally, should be similar shader)
void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
    if (batchId != OpBatchType::CopyToLayer) {
        // if first op after one or more unclipped saveLayers, flush the layer clears
        flushLayerClears(allocator);
    }
    MergingOpBatch* targetBatch = nullptr;
    MergingOpBatch* targetBatch = nullptr;


    // Try to merge with any existing batch with same mergeId
    // Try to merge with any existing batch with same mergeId
@@ -726,6 +770,11 @@ void OpReorderer::deferArcOp(const ArcOp& op) {
    deferStrokeableOp(op, tessBatchId(op));
    deferStrokeableOp(op, tessBatchId(op));
}
}


static bool hasMergeableClip(const BakedOpState& state) {
    return state.computedState.clipState
            || state.computedState.clipState->mode == ClipMode::Rectangle;
}

void OpReorderer::deferBitmapOp(const BitmapOp& op) {
void OpReorderer::deferBitmapOp(const BitmapOp& op) {
    BakedOpState* bakedState = tryBakeOpState(op);
    BakedOpState* bakedState = tryBakeOpState(op);
    if (!bakedState) return; // quick rejected
    if (!bakedState) return; // quick rejected
@@ -736,7 +785,8 @@ void OpReorderer::deferBitmapOp(const BitmapOp& op) {
    if (bakedState->computedState.transform.isSimple()
    if (bakedState->computedState.transform.isSimple()
            && bakedState->computedState.transform.positiveScale()
            && bakedState->computedState.transform.positiveScale()
            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
            && op.bitmap->colorType() != kAlpha_8_SkColorType) {
            && op.bitmap->colorType() != kAlpha_8_SkColorType
            && hasMergeableClip(*bakedState)) {
        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
        // TODO: AssetAtlas in mergeId
        // TODO: AssetAtlas in mergeId
        currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
        currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
@@ -792,7 +842,8 @@ void OpReorderer::deferPatchOp(const PatchOp& op) {
    if (!bakedState) return; // quick rejected
    if (!bakedState) return; // quick rejected


    if (bakedState->computedState.transform.isPureTranslate()
    if (bakedState->computedState.transform.isPureTranslate()
            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) {
            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
            && hasMergeableClip(*bakedState)) {
        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
        // TODO: AssetAtlas in mergeId
        // TODO: AssetAtlas in mergeId


@@ -849,7 +900,8 @@ void OpReorderer::deferTextOp(const TextOp& op) {


    batchid_t batchId = textBatchId(*(op.paint));
    batchid_t batchId = textBatchId(*(op.paint));
    if (bakedState->computedState.transform.isPureTranslate()
    if (bakedState->computedState.transform.isPureTranslate()
            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) {
            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
            && hasMergeableClip(*bakedState)) {
        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
        currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId);
        currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId);
    } else {
    } else {
@@ -894,7 +946,8 @@ void OpReorderer::restoreForLayer() {
    mLayerStack.pop_back();
    mLayerStack.pop_back();
}
}


// TODO: test rejection at defer time, where the bounds become empty
// TODO: defer time rejection (when bounds become empty) + tests
// Option - just skip layers with no bounds at playback + defer?
void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
    uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
    uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
    uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
    uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
@@ -904,7 +957,7 @@ void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {


    // Combine all transforms used to present saveLayer content:
    // Combine all transforms used to present saveLayer content:
    // parent content transform * canvas transform * bounds offset
    // parent content transform * canvas transform * bounds offset
    Matrix4 contentTransform(*previous->transform);
    Matrix4 contentTransform(*(previous->transform));
    contentTransform.multiply(op.localMatrix);
    contentTransform.multiply(op.localMatrix);
    contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top);
    contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top);


@@ -961,10 +1014,53 @@ void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
        currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
        currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
    } else {
    } else {
        // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
        // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
        // TODO: need to prevent any render work from being done
        // - create layerop earlier for reject purposes?
        mLayerReorderers[finishedLayerIndex].clear();
        mLayerReorderers[finishedLayerIndex].clear();
        return;
        return;
    }
    }
}
}


void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
    Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
    boundsTransform.multiply(op.localMatrix);

    Rect dstRect(op.unmappedBounds);
    boundsTransform.mapRect(dstRect);
    dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());

    // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
    OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>();

    /**
     * First, defer an operation to copy out the content from the rendertarget into a layer.
     */
    auto copyToOp = new (mAllocator) CopyToLayerOp(op, layerHandle);
    BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator, dstRect, *copyToOp);
    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);

    /**
     * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
     * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
     */
    currentLayer().deferLayerClear(dstRect);

    /**
     * And stash an operation to copy that layer back under the rendertarget until
     * a balanced EndUnclippedLayerOp is seen
     */
    auto copyFromOp = new (mAllocator) CopyFromLayerOp(op, layerHandle);
    bakedState = BakedOpState::directConstruct(mAllocator, dstRect, *copyFromOp);
    currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
}

void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& op) {
    LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");

    BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
    currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
    currentLayer().activeUnclippedSaveLayers.pop_back();
}

} // namespace uirenderer
} // namespace uirenderer
} // namespace android
} // namespace android
Loading