Unclipped savelayer support in new renderer
bug:22480459
Change-Id: I89dd5de8d7d008a1e298d227d767aabff5c96e27
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index f58ca31..195d235 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -736,6 +736,7 @@
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::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 4aebe3c..b9c13e6 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -79,6 +79,20 @@
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 65e8b29..e857f6b 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 @@
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 b7c9281..f1cc846 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -75,9 +75,9 @@
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 70b0484..5c7b43f 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -58,9 +58,8 @@
// 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 @@
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 @@
}
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 @@
, 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 34c3d60..cff4f3c 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -31,7 +31,6 @@
namespace uirenderer {
class BatchBase {
-
public:
BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
: mBatchId(batchId)
@@ -213,7 +212,8 @@
, 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 @@
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 @@
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 @@
* 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 b824d02..8d9d90b 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -111,6 +111,7 @@
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 6b44557..227b640 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -54,6 +54,12 @@
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 fac6c35..2d8d529 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -46,6 +46,8 @@
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 ac14fc8..edde31e 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -53,6 +53,13 @@
&& 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 78fcd8b..c899850 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -29,14 +29,27 @@
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 2187654..e96e9ba 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, 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 @@
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 0ed70a0..701e446 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -565,7 +565,7 @@
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 @@
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 795ac30..ff098c8 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -33,14 +33,6 @@
}
}
-#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);