Loading libs/hwui/BakedOpState.cpp +58 −0 Original line number Diff line number Diff line Loading @@ -108,5 +108,63 @@ ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& d clippedBounds.doIntersect(clipRect->rect); } BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, false); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp); } BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) : true; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, expandForStroke); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected // NOTE: this won't succeed if a clip was allocated allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; // clip isn't empty, so construct the op return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr); } BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator, const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp); } void BakedOpState::setupOpacity(const SkPaint* paint) { computedState.opaqueOverClippedBounds = computedState.transform.isSimple() && computedState.clipState->mode == ClipMode::Rectangle && MathUtils::areEqual(alpha, 1.0f) && !roundRectClipState && PaintUtils::isOpaquePaint(paint); } } // namespace uirenderer } // namespace android libs/hwui/BakedOpState.h +9 −40 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ public: Rect clippedBounds; int clipSideFlags = 0; const SkPath* localProjectionPathMask = nullptr; bool opaqueOverClippedBounds = false; }; /** Loading @@ -103,23 +104,10 @@ public: class BakedOpState { public: static BakedOpState* tryConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, false); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } Snapshot& snapshot, const RecordedOp& recordedOp); static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp); } Snapshot& snapshot, const RecordedOp& recordedOp); enum class StrokeBehavior { // stroking is forced, regardless of style on paint (such as for lines) Loading @@ -129,35 +117,16 @@ public: }; static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) : true; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, expandForStroke); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected // NOTE: this won't succeed if a clip was allocated allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior); static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; // clip isn't empty, so construct the op return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr); } Snapshot& snapshot, const ShadowOp* shadowOpPtr); static BakedOpState* directConstruct(LinearAllocator& allocator, const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp); } const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp); // Set opaqueOverClippedBounds. If this method isn't called, the op is assumed translucent. void setupOpacity(const SkPaint* paint); // computed state: ResolvedRenderState computedState; Loading libs/hwui/FrameBuilder.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -481,12 +481,17 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { * Defers an unmergeable, strokeable op, accounting correctly * for paint's style on the bounds being computed. */ const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior) { // Note: here we account for stroke when baking the op BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior); if (!bakedState) return nullptr; // quick rejected if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) { bakedState->setupOpacity(op.paint); } currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); return bakedState; } Loading Loading @@ -516,6 +521,7 @@ static bool hasMergeableClip(const BakedOpState& state) { void FrameBuilder::deferBitmapOp(const BitmapOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected bakedState->setupOpacity(op.paint); // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in Loading libs/hwui/FrameBuilder.h +1 −1 Original line number Diff line number Diff line Loading @@ -201,7 +201,7 @@ private: return mAllocator.create<SkPath>(); } const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined); /** Loading libs/hwui/LayerBuilder.cpp +25 −9 Original line number Diff line number Diff line Loading @@ -236,6 +236,21 @@ void LayerBuilder::deferLayerClear(const Rect& rect) { mClearRects.push_back(rect); } void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState) { if (bakedState->op->opId != RecordedOpId::CopyToLayerOp) { // First non-CopyToLayer, so stop stashing up layer clears for unclipped save layers, // and issue them together in one draw. flushLayerClears(allocator); if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() && bakedState->computedState.opaqueOverClippedBounds && bakedState->computedState.clippedBounds.contains(repaintRect))) { // discard all deferred drawing ops, since new one will occlude them clear(); } } } void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { if (CC_UNLIKELY(!mClearRects.empty())) { const int vertCount = mClearRects.size() * 4; Loading Loading @@ -270,11 +285,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId) { if (batchId != OpBatchType::CopyToLayer) { // if first op after one or more unclipped saveLayers, flush the layer clears flushLayerClears(allocator); } onDeferOp(allocator, op); OpBatch* targetBatch = mBatchLookup[batchId]; size_t insertBatchIndex = mBatches.size(); Loading @@ -295,10 +306,7 @@ void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, 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); } onDeferOp(allocator, op); MergingOpBatch* targetBatch = nullptr; // Try to merge with any existing batch with same mergeId Loading Loading @@ -348,6 +356,14 @@ void LayerBuilder::replayBakedOpsImpl(void* arg, } } void LayerBuilder::clear() { mBatches.clear(); for (int i = 0; i < OpBatchType::Count; i++) { mBatchLookup[i] = nullptr; mMergingBatchLookup[i].clear(); } } void LayerBuilder::dump() const { ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", this, width, height, offscreenBuffer, beginLayerOp, Loading Loading
libs/hwui/BakedOpState.cpp +58 −0 Original line number Diff line number Diff line Loading @@ -108,5 +108,63 @@ ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& d clippedBounds.doIntersect(clipRect->rect); } BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, false); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp); } BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) : true; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, expandForStroke); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected // NOTE: this won't succeed if a clip was allocated allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; // clip isn't empty, so construct the op return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr); } BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator, const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp); } void BakedOpState::setupOpacity(const SkPaint* paint) { computedState.opaqueOverClippedBounds = computedState.transform.isSimple() && computedState.clipState->mode == ClipMode::Rectangle && MathUtils::areEqual(alpha, 1.0f) && !roundRectClipState && PaintUtils::isOpaquePaint(paint); } } // namespace uirenderer } // namespace android
libs/hwui/BakedOpState.h +9 −40 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ public: Rect clippedBounds; int clipSideFlags = 0; const SkPath* localProjectionPathMask = nullptr; bool opaqueOverClippedBounds = false; }; /** Loading @@ -103,23 +104,10 @@ public: class BakedOpState { public: static BakedOpState* tryConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, false); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } Snapshot& snapshot, const RecordedOp& recordedOp); static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp); } Snapshot& snapshot, const RecordedOp& recordedOp); enum class StrokeBehavior { // stroking is forced, regardless of style on paint (such as for lines) Loading @@ -129,35 +117,16 @@ public: }; static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) : true; BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( allocator, snapshot, recordedOp, expandForStroke); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected // NOTE: this won't succeed if a clip was allocated allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior); static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; // clip isn't empty, so construct the op return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr); } Snapshot& snapshot, const ShadowOp* shadowOpPtr); static BakedOpState* directConstruct(LinearAllocator& allocator, const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp); } const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp); // Set opaqueOverClippedBounds. If this method isn't called, the op is assumed translucent. void setupOpacity(const SkPaint* paint); // computed state: ResolvedRenderState computedState; Loading
libs/hwui/FrameBuilder.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -481,12 +481,17 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { * Defers an unmergeable, strokeable op, accounting correctly * for paint's style on the bounds being computed. */ const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior) { // Note: here we account for stroke when baking the op BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior); if (!bakedState) return nullptr; // quick rejected if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) { bakedState->setupOpacity(op.paint); } currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); return bakedState; } Loading Loading @@ -516,6 +521,7 @@ static bool hasMergeableClip(const BakedOpState& state) { void FrameBuilder::deferBitmapOp(const BitmapOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected bakedState->setupOpacity(op.paint); // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in Loading
libs/hwui/FrameBuilder.h +1 −1 Original line number Diff line number Diff line Loading @@ -201,7 +201,7 @@ private: return mAllocator.create<SkPath>(); } const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined); /** Loading
libs/hwui/LayerBuilder.cpp +25 −9 Original line number Diff line number Diff line Loading @@ -236,6 +236,21 @@ void LayerBuilder::deferLayerClear(const Rect& rect) { mClearRects.push_back(rect); } void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState) { if (bakedState->op->opId != RecordedOpId::CopyToLayerOp) { // First non-CopyToLayer, so stop stashing up layer clears for unclipped save layers, // and issue them together in one draw. flushLayerClears(allocator); if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() && bakedState->computedState.opaqueOverClippedBounds && bakedState->computedState.clippedBounds.contains(repaintRect))) { // discard all deferred drawing ops, since new one will occlude them clear(); } } } void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { if (CC_UNLIKELY(!mClearRects.empty())) { const int vertCount = mClearRects.size() * 4; Loading Loading @@ -270,11 +285,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId) { if (batchId != OpBatchType::CopyToLayer) { // if first op after one or more unclipped saveLayers, flush the layer clears flushLayerClears(allocator); } onDeferOp(allocator, op); OpBatch* targetBatch = mBatchLookup[batchId]; size_t insertBatchIndex = mBatches.size(); Loading @@ -295,10 +306,7 @@ void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, 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); } onDeferOp(allocator, op); MergingOpBatch* targetBatch = nullptr; // Try to merge with any existing batch with same mergeId Loading Loading @@ -348,6 +356,14 @@ void LayerBuilder::replayBakedOpsImpl(void* arg, } } void LayerBuilder::clear() { mBatches.clear(); for (int i = 0; i < OpBatchType::Count; i++) { mBatchLookup[i] = nullptr; mMergingBatchLookup[i].clear(); } } void LayerBuilder::dump() const { ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", this, width, height, offscreenBuffer, beginLayerOp, Loading