diff options
| -rw-r--r-- | libs/hwui/BakedOpDispatcher.cpp | 29 | ||||
| -rw-r--r-- | libs/hwui/BakedOpRenderer.cpp | 14 | ||||
| -rw-r--r-- | libs/hwui/BakedOpRenderer.h | 4 | ||||
| -rw-r--r-- | libs/hwui/BakedOpState.cpp | 4 | ||||
| -rw-r--r-- | libs/hwui/BakedOpState.h | 17 | ||||
| -rw-r--r-- | libs/hwui/OpReorderer.cpp | 17 | ||||
| -rw-r--r-- | libs/hwui/OpReorderer.h | 1 | ||||
| -rw-r--r-- | libs/hwui/renderstate/OffscreenBufferPool.cpp | 6 | ||||
| -rw-r--r-- | libs/hwui/renderstate/OffscreenBufferPool.h | 2 | ||||
| -rw-r--r-- | libs/hwui/tests/common/TestUtils.h | 7 | ||||
| -rw-r--r-- | libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp | 25 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp | 15 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/OpReordererTests.cpp | 4 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/RecordingCanvasTests.cpp | 8 |
14 files changed, 113 insertions, 40 deletions
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index f58ca31a75cf..195d235fc7c1 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -736,6 +736,7 @@ void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const Textur void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { OffscreenBuffer* buffer = *op.layerHandle; + // Note that we don't use op->paint here - it's never set on a LayerOp float layerAlpha = op.alpha * state.alpha; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) @@ -754,11 +755,35 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, } void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) { - LOG_ALWAYS_FATAL("TODO!"); + LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!"); + *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds); + LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed"); } void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) { - LOG_ALWAYS_FATAL("TODO!"); + LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!"); + if (!state.computedState.clippedBounds.isEmpty()) { + if (op.paint && op.paint->getAlpha() < 255) { + SkPaint layerPaint; + layerPaint.setAlpha(op.paint->getAlpha()); + layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode); + layerPaint.setColorFilter(op.paint->getColorFilter()); + RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint); + BakedOpDispatcher::onRectOp(renderer, rectOp, state); + } + + OffscreenBuffer& layer = **(op.layerHandle); + auto mode = PaintUtils::getXfermodeDirect(op.paint); + Glop glop; + GlopBuilder(renderer.renderState(), renderer.caches(), &glop) + .setRoundRectClipState(state.roundRectClipState) + .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates()) + .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap) + .setTransform(state.computedState.transform, TransformFlags::None) + .setModelViewMapUnitToRect(state.computedState.clippedBounds) + .build(); + renderer.renderGlop(state, glop); + } } } // namespace uirenderer diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 4aebe3cca4cc..b9c13e6cf847 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -79,6 +79,20 @@ void BakedOpRenderer::endLayer() { mRenderTarget.frameBufferId = 0; } +OffscreenBuffer* BakedOpRenderer::copyToLayer(const Rect& area) { + OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState, + area.getWidth(), area.getHeight()); + if (!area.isEmpty()) { + mCaches.textureState().activateTexture(0); + mCaches.textureState().bindTexture(buffer->texture.id); + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + area.left, mRenderTarget.viewportHeight - area.bottom, + area.getWidth(), area.getHeight()); + } + return buffer; +} + 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); diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h index 65e8b29a9ed2..e857f6b2a506 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -19,6 +19,7 @@ #include "BakedOpState.h" #include "Matrix.h" +#include "utils/Macros.h" namespace android { namespace uirenderer { @@ -61,9 +62,10 @@ public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect); void endFrame(const Rect& repaintRect); - OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height); + WARN_UNUSED_RESULT OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height); void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect); void endLayer(); + WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area); Texture* getTexture(const SkBitmap* bitmap); const LightInfo& getLightInfo() const { return mLightInfo; } diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp index b7c9281d4a4e..f1cc846593db 100644 --- a/libs/hwui/BakedOpState.cpp +++ b/libs/hwui/BakedOpState.cpp @@ -75,9 +75,9 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s clipSideFlags = OpClipSideFlags::Full; } -ResolvedRenderState::ResolvedRenderState(const Rect& dstRect) +ResolvedRenderState::ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect) : transform(Matrix4::identity()) - , clipState(nullptr) + , clipState(viewportRect) , clippedBounds(dstRect) , clipSideFlags(OpClipSideFlags::None) {} diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h index 70b0484d3695..5c7b43f5a034 100644 --- a/libs/hwui/BakedOpState.h +++ b/libs/hwui/BakedOpState.h @@ -58,9 +58,8 @@ public: // Constructor for unbounded ops without transform/clip (namely shadows) 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); + // Constructor for primitive ops provided clip, and no transform + ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect); Rect computeLocalSpaceClip() const { Matrix4 inverse; @@ -71,15 +70,13 @@ public: return outClip; } - // NOTE: Can only be used on clipped/snapshot based ops const Rect& clipRect() const { return clipState->rect; } - // NOTE: Can only be used on clipped/snapshot based ops bool requiresClip() const { return clipSideFlags != OpClipSideFlags::None - || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle); + || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle); } // returns the clip if it's needed to draw the operation, otherwise nullptr @@ -144,8 +141,8 @@ public: } static BakedOpState* directConstruct(LinearAllocator& allocator, - const Rect& dstRect, const RecordedOp& recordedOp) { - return new (allocator) BakedOpState(dstRect, recordedOp); + const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { + return new (allocator) BakedOpState(clip, dstRect, recordedOp); } static void* operator new(size_t size, LinearAllocator& allocator) { @@ -177,8 +174,8 @@ private: , projectionPathMask(snapshot.projectionPathMask) , op(shadowOpPtr) {} - BakedOpState(const Rect& dstRect, const RecordedOp& recordedOp) - : computedState(dstRect) + BakedOpState(const ClipRect* viewportRect, const Rect& dstRect, const RecordedOp& recordedOp) + : computedState(viewportRect, dstRect) , alpha(1.0f) , roundRectClipState(nullptr) , projectionPathMask(nullptr) diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp index 34c3d6004108..cff4f3c47d60 100644 --- a/libs/hwui/OpReorderer.cpp +++ b/libs/hwui/OpReorderer.cpp @@ -31,7 +31,6 @@ namespace android { namespace uirenderer { class BatchBase { - public: BatchBase(batchid_t batchId, BakedOpState* op, bool merging) : mBatchId(batchId) @@ -213,7 +212,8 @@ OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height, , repaintRect(repaintRect) , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr) , beginLayerOp(beginLayerOp) - , renderNode(renderNode) {} + , renderNode(renderNode) + , viewportClip(Rect(width, height)) {} // iterate back toward target to see if anything drawn since should overlap the new op // if no target, merging ops still iterate to find similar batch to insert after @@ -270,7 +270,8 @@ void OpReorderer::LayerReorderer::flushLayerClears(LinearAllocator& allocator) { SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds, Matrix4::identity(), nullptr, paint, verts, vertCount); - BakedOpState* bakedState = BakedOpState::directConstruct(allocator, bounds, *op); + BakedOpState* bakedState = BakedOpState::directConstruct(allocator, + &viewportClip, bounds, *op); deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices); @@ -1030,13 +1031,14 @@ void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip()); // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume) - OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(); + OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr); /** * 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); + BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator, + &(currentLayer().viewportClip), dstRect, *copyToOp); currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer); /** @@ -1050,11 +1052,12 @@ void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { * a balanced EndUnclippedLayerOp is seen */ auto copyFromOp = new (mAllocator) CopyFromLayerOp(op, layerHandle); - bakedState = BakedOpState::directConstruct(mAllocator, dstRect, *copyFromOp); + bakedState = BakedOpState::directConstruct(mAllocator, + &(currentLayer().viewportClip), dstRect, *copyFromOp); currentLayer().activeUnclippedSaveLayers.push_back(bakedState); } -void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& op) { +void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) { LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!"); BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back(); diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h index b824d02eb936..8d9d90be057c 100644 --- a/libs/hwui/OpReorderer.h +++ b/libs/hwui/OpReorderer.h @@ -111,6 +111,7 @@ class OpReorderer : public CanvasStateClient { OffscreenBuffer* offscreenBuffer; const BeginLayerOp* beginLayerOp; const RenderNode* renderNode; + const ClipRect viewportClip; // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps std::vector<BakedOpState*> activeUnclippedSaveLayers; diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp index 6b44557a377b..227b6409b893 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.cpp +++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp @@ -54,6 +54,12 @@ OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } +Rect OffscreenBuffer::getTextureCoordinates() { + const float texX = 1.0f / float(texture.width); + const float texY = 1.0f / float(texture.height); + return Rect(0, viewportHeight * texY, viewportWidth * texX, 0); +} + void OffscreenBuffer::updateMeshFromRegion() { // avoid T-junctions as they cause artifacts in between the resultant // geometry when complex transforms occur. diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h index fac6c35179d5..2d8d5295e1f8 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.h +++ b/libs/hwui/renderstate/OffscreenBufferPool.h @@ -46,6 +46,8 @@ public: uint32_t viewportWidth, uint32_t viewportHeight); ~OffscreenBuffer(); + Rect getTextureCoordinates(); + // must be called prior to rendering, to construct/update vertex buffer void updateMeshFromRegion(); diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index ac14fc84b8a3..edde31e502e0 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -53,6 +53,13 @@ typedef DisplayListCanvas TestCanvas; && MathUtils::areEqual(a.right, b.right) \ && MathUtils::areEqual(a.bottom, b.bottom)); +#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \ + EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \ + if ((clipStatePtr)->mode == ClipMode::Rectangle) { \ + EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \ + } else { \ + ADD_FAILURE() << "ClipState not a rect"; \ + } /** * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope * (for e.g. accessing its RenderState) diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp index 78fcd8bf30ac..c89985009fbd 100644 --- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp @@ -29,14 +29,27 @@ class SaveLayerAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background + canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); // background - card = TestUtils::createNode(0, 0, 200, 200, + card = TestUtils::createNode(0, 0, 400, 800, [](RenderProperties& props, TestCanvas& canvas) { - canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag); - canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped - canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag); - canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped + // nested clipped saveLayers + canvas.saveLayerAlpha(0, 0, 400, 400, 200, SkCanvas::kClipToLayer_SaveFlag); + canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode); + canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op); + canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode); + canvas.restore(); + canvas.restore(); + + // single unclipped saveLayer + canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.translate(0, 400); + canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::SaveFlags(0)); // unclipped + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(Color::Green_700); + canvas.drawCircle(200, 200, 200, paint); canvas.restore(); canvas.restore(); }); diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp index 2187654fb36c..e96e9ba55361 100644 --- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp +++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp @@ -15,11 +15,11 @@ */ #include <gtest/gtest.h> +#include <Rect.h> #include <renderstate/OffscreenBufferPool.h> #include <tests/common/TestUtils.h> -using namespace android; using namespace android::uirenderer; TEST(OffscreenBuffer, computeIdealDimension) { @@ -43,6 +43,18 @@ TEST(OffscreenBuffer, construct) { }); } +TEST(OffscreenBuffer, getTextureCoordinates) { + TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) { + OffscreenBuffer layerAligned(thread.renderState(), Caches::getInstance(), 256u, 256u); + EXPECT_EQ(Rect(0, 1, 1, 0), + layerAligned.getTextureCoordinates()); + + OffscreenBuffer layerUnaligned(thread.renderState(), Caches::getInstance(), 200u, 225u); + EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0), + layerUnaligned.getTextureCoordinates()); + }); +} + TEST(OffscreenBufferPool, construct) { TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) { OffscreenBufferPool pool; @@ -51,7 +63,6 @@ TEST(OffscreenBufferPool, construct) { EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize()) << "pool must read size from Properties"; }); - } TEST(OffscreenBufferPool, getPutClear) { diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp index 0ed70a08c832..701e4460c35f 100644 --- a/libs/hwui/tests/unit/OpReordererTests.cpp +++ b/libs/hwui/tests/unit/OpReordererTests.cpp @@ -565,7 +565,7 @@ TEST(OpReorderer, saveLayerUnclipped_simple) { void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { EXPECT_EQ(0, mIndex++); EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); - EXPECT_EQ(nullptr, state.computedState.clipState); + EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); EXPECT_TRUE(state.computedState.transform.isIdentity()); } void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { @@ -583,7 +583,7 @@ TEST(OpReorderer, saveLayerUnclipped_simple) { void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { EXPECT_EQ(3, mIndex++); EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); - EXPECT_EQ(nullptr, state.computedState.clipState); + EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); EXPECT_TRUE(state.computedState.transform.isIdentity()); } }; diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index 795ac30e317c..ff098c8bb3d5 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -33,14 +33,6 @@ static void playbackOps(const DisplayList& displayList, } } -#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \ - EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \ - if ((clipStatePtr)->mode == ClipMode::Rectangle) { \ - EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \ - } else { \ - ADD_FAILURE() << "ClipState not a rect"; \ - } - TEST(RecordingCanvas, emptyPlayback) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); |