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); |