summaryrefslogtreecommitdiff
path: root/libs/hwui/pipeline
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/pipeline')
-rw-r--r--libs/hwui/pipeline/skia/AnimatedDrawables.h34
-rw-r--r--libs/hwui/pipeline/skia/DumpOpsCanvas.h41
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp58
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.h23
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp67
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.h26
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp119
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h12
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp86
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.h16
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.cpp142
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.h148
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp45
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h38
-rw-r--r--libs/hwui/pipeline/skia/SkiaLayer.h4
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp202
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h17
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp112
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.h4
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp268
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h64
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp4
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.h3
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp153
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h46
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp64
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h17
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanReadback.h44
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp59
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.h44
30 files changed, 1284 insertions, 676 deletions
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index 44c494f77231..efef6de2a9e1 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -16,10 +16,10 @@
#pragma once
-#include "CanvasProperty.h"
-#include <utils/RefBase.h>
#include <SkCanvas.h>
#include <SkDrawable.h>
+#include <utils/RefBase.h>
+#include "CanvasProperty.h"
namespace android {
namespace uirenderer {
@@ -28,16 +28,12 @@ namespace skiapipeline {
class AnimatedRoundRect : public SkDrawable {
public:
AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
- uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
- uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
- uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p)
- : mLeft(left)
- , mTop(top)
- , mRight(right)
- , mBottom(bottom)
- , mRx(rx)
- , mRy(ry)
- , mPaint(p) {}
+ uirenderer::CanvasPropertyPrimitive* top,
+ uirenderer::CanvasPropertyPrimitive* right,
+ uirenderer::CanvasPropertyPrimitive* bottom,
+ uirenderer::CanvasPropertyPrimitive* rx,
+ uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p)
+ : mLeft(left), mTop(top), mRight(right), mBottom(bottom), mRx(rx), mRy(ry), mPaint(p) {}
protected:
virtual SkRect onGetBounds() override {
@@ -61,11 +57,9 @@ private:
class AnimatedCircle : public SkDrawable {
public:
AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
- uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint)
- : mX(x)
- , mY(y)
- , mRadius(radius)
- , mPaint(paint) {}
+ uirenderer::CanvasPropertyPrimitive* radius,
+ uirenderer::CanvasPropertyPaint* paint)
+ : mX(x), mY(y), mRadius(radius), mPaint(paint) {}
protected:
virtual SkRect onGetBounds() override {
@@ -85,6 +79,6 @@ private:
sp<uirenderer::CanvasPropertyPaint> mPaint;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 34fb04ccad55..1f83d1a201b0 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -16,6 +16,7 @@
#pragma once
+#include "RenderNode.h"
#include "SkiaDisplayList.h"
namespace android {
@@ -32,8 +33,7 @@ public:
: mOutput(output)
, mLevel(level)
, mDisplayList(displayList)
- , mIdent((level + 1) * 2, ' ') {
- }
+ , mIdent((level + 1) * 2, ' ') {}
protected:
void onClipRect(const SkRect& rect, SkClipOp, ClipEdgeStyle) override {
@@ -52,9 +52,7 @@ protected:
mOutput << mIdent << "clipRegion" << std::endl;
}
- void onDrawPaint(const SkPaint&) override {
- mOutput << mIdent << "drawPaint" << std::endl;
- }
+ void onDrawPaint(const SkPaint&) override { mOutput << mIdent << "drawPaint" << std::endl; }
void onDrawPath(const SkPath&, const SkPaint&) override {
mOutput << mIdent << "drawPath" << std::endl;
@@ -92,22 +90,21 @@ protected:
mOutput << mIdent << "drawPosText" << std::endl;
}
- void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar,
- const SkPaint&) override {
+ void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override {
mOutput << mIdent << "drawPosTextH" << std::endl;
}
void onDrawTextOnPath(const void*, size_t, const SkPath&, const SkMatrix*,
- const SkPaint&) override {
+ const SkPaint&) override {
mOutput << mIdent << "drawTextOnPath" << std::endl;
}
void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
- const SkPaint&) override {
+ const SkPaint&) override {
mOutput << mIdent << "drawTextRSXform" << std::endl;
}
- void onDrawTextBlob(const SkTextBlob*, SkScalar,SkScalar, const SkPaint&) override {
+ void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override {
mOutput << mIdent << "drawTextBlob" << std::endl;
}
@@ -116,17 +113,17 @@ protected:
}
void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
- const SkPaint*) override {
+ const SkPaint*) override {
mOutput << mIdent << "drawImageNine" << std::endl;
}
void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
- SrcRectConstraint) override {
+ SrcRectConstraint) override {
mOutput << mIdent << "drawImageRect" << std::endl;
}
void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
- const SkPaint*) override {
+ const SkPaint*) override {
mOutput << mIdent << "drawImageLattice" << std::endl;
}
@@ -157,21 +154,21 @@ protected:
private:
RenderNodeDrawable* getRenderNodeDrawable(SkDrawable* drawable) {
- for (auto& child : mDisplayList.mChildNodes) {
+ for (auto& child : mDisplayList.mChildNodes) {
if (drawable == &child) {
return &child;
}
- }
- return nullptr;
+ }
+ return nullptr;
}
GLFunctorDrawable* getGLFunctorDrawable(SkDrawable* drawable) {
- for (auto& child : mDisplayList.mChildFunctors) {
+ for (auto& child : mDisplayList.mChildFunctors) {
if (drawable == &child) {
return &child;
}
- }
- return nullptr;
+ }
+ return nullptr;
}
std::ostream& mOutput;
@@ -180,6 +177,6 @@ private:
std::string mIdent;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index ea302a154616..145e3c485cbc 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -15,18 +15,19 @@
*/
#include "GLFunctorDrawable.h"
+#include <GrContext.h>
+#include <private/hwui/DrawGlInfo.h>
#include "GlFunctorLifecycleListener.h"
#include "RenderNode.h"
+#include "SkAndroidFrameworkUtils.h"
#include "SkClipStack.h"
-#include <private/hwui/DrawGlInfo.h>
-#include <GrContext.h>
namespace android {
namespace uirenderer {
namespace skiapipeline {
GLFunctorDrawable::~GLFunctorDrawable() {
- if(mListener.get() != nullptr) {
+ if (mListener.get() != nullptr) {
mListener->onGlFunctorReleased(mFunctor);
}
}
@@ -49,8 +50,6 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
return;
}
- canvas->flush();
-
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
canvas->clear(SK_ColorRED);
return;
@@ -72,33 +71,33 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
info.height = canvasInfo.height();
mat4.asColMajorf(&info.transform[0]);
- //apply a simple clip with a scissor or a complex clip with a stencil
+ bool clearStencilAfterFunctor = false;
+
+ // apply a simple clip with a scissor or a complex clip with a stencil
SkRegion clipRegion;
canvas->temporary_internal_getRgnClip(&clipRegion);
if (CC_UNLIKELY(clipRegion.isComplex())) {
- //It is only a temporary solution to use a scissor to draw the stencil.
- //There is a bug 31489986 to implement efficiently non-rectangular clips.
glDisable(GL_SCISSOR_TEST);
- glDisable(GL_STENCIL_TEST);
- glStencilMask(0xff);
+ glStencilMask(0x1);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
- glEnable(GL_SCISSOR_TEST);
- SkRegion::Cliperator it(clipRegion, ibounds);
- while (!it.done()) {
- setScissor(info.height, it.rect());
- glClearStencil(0x1);
- glClear(GL_STENCIL_BUFFER_BIT);
- it.next();
+ bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(canvas);
+ canvas->flush();
+ if (stencilWritten) {
+ glStencilMask(0x1);
+ glStencilFunc(GL_EQUAL, 0x1, 0x1);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ clearStencilAfterFunctor = true;
+ glEnable(GL_STENCIL_TEST);
+ } else {
+ glDisable(GL_STENCIL_TEST);
}
- glDisable(GL_SCISSOR_TEST);
- glStencilFunc(GL_EQUAL, 0x1, 0xff);
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
- glEnable(GL_STENCIL_TEST);
} else if (clipRegion.isEmpty()) {
+ canvas->flush();
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
} else {
+ canvas->flush();
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
setScissor(info.height, clipRegion.getBounds());
@@ -106,9 +105,18 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
(*mFunctor)(DrawGlInfo::kModeDraw, &info);
+ if (clearStencilAfterFunctor) {
+ // clear stencil buffer as it may be used by Skia
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_STENCIL_TEST);
+ glStencilMask(0x1);
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ }
+
canvas->getGrContext()->resetContext();
- }
+}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index 012c948be9b3..af57d7d33c2c 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -19,8 +19,8 @@
#include <SkCanvas.h>
#include <SkDrawable.h>
-#include <utils/RefBase.h>
#include <utils/Functor.h>
+#include <utils/RefBase.h>
namespace android {
namespace uirenderer {
@@ -36,24 +36,21 @@ namespace skiapipeline {
class GLFunctorDrawable : public SkDrawable {
public:
GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : mFunctor(functor)
- , mListener(listener)
- , mBounds(canvas->getLocalClipBounds())
- {}
+ : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
virtual ~GLFunctorDrawable();
void syncFunctor() const;
- protected:
+protected:
virtual SkRect onGetBounds() override { return mBounds; }
virtual void onDraw(SkCanvas* canvas) override;
- private:
- Functor* mFunctor;
- sp<GlFunctorLifecycleListener> mListener;
- const SkRect mBounds;
+private:
+ Functor* mFunctor;
+ sp<GlFunctorLifecycleListener> mListener;
+ const SkRect mBounds;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 4feeb2d6facb..d9584db2df9d 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "GlLayer.h"
#include "LayerDrawable.h"
+#include "GlLayer.h"
#include "VkLayer.h"
#include "GrBackendSurface.h"
@@ -28,29 +28,32 @@ namespace uirenderer {
namespace skiapipeline {
void LayerDrawable::onDraw(SkCanvas* canvas) {
- DrawLayer(canvas->getGrContext(), canvas, mLayer.get());
+ Layer* layer = mLayerUpdater->backingLayer();
+ if (layer) {
+ DrawLayer(canvas->getGrContext(), canvas, layer);
+ }
}
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
- // transform the matrix based on the layer
- int saveCount = -1;
- if (!layer->getTransform().isIdentity()) {
- saveCount = canvas->save();
- SkMatrix transform;
- layer->getTransform().copyTo(transform);
- canvas->concat(transform);
+ if (context == nullptr) {
+ SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+ return false;
}
-
+ // transform the matrix based on the layer
+ SkMatrix layerTransform;
+ layer->getTransform().copyTo(layerTransform);
sk_sp<SkImage> layerImage;
+ int layerWidth = layer->getWidth();
+ int layerHeight = layer->getHeight();
if (layer->getApi() == Layer::Api::OpenGL) {
GlLayer* glLayer = static_cast<GlLayer*>(layer);
GrGLTextureInfo externalTexture;
externalTexture.fTarget = glLayer->getRenderTarget();
externalTexture.fID = glLayer->getTextureId();
- GrBackendTexture backendTexture(glLayer->getWidth(), glLayer->getHeight(),
- kRGBA_8888_GrPixelConfig, externalTexture);
+ GrBackendTexture backendTexture(layerWidth, layerHeight, kRGBA_8888_GrPixelConfig,
+ externalTexture);
layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
- kPremul_SkAlphaType, nullptr);
+ kPremul_SkAlphaType, nullptr);
} else {
SkASSERT(layer->getApi() == Layer::Api::Vulkan);
VkLayer* vkLayer = static_cast<VkLayer*>(layer);
@@ -59,20 +62,42 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
}
if (layerImage) {
+ SkMatrix textureMatrix;
+ layer->getTexTransform().copyTo(textureMatrix);
+ // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
+ // use bottom left origin and remove flipV and invert transformations.
+ SkMatrix flipV;
+ flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
+ textureMatrix.preConcat(flipV);
+ textureMatrix.preScale(1.0f / layerWidth, 1.0f / layerHeight);
+ textureMatrix.postScale(layerWidth, layerHeight);
+ SkMatrix textureMatrixInv;
+ if (!textureMatrix.invert(&textureMatrixInv)) {
+ textureMatrixInv = textureMatrix;
+ }
+
+ SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv);
+
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
- canvas->drawImage(layerImage, 0, 0, &paint);
- }
- // restore the original matrix
- if (saveCount >= 0) {
- canvas->restoreToCount(saveCount);
+
+ const bool nonIdentityMatrix = !matrix.isIdentity();
+ if (nonIdentityMatrix) {
+ canvas->save();
+ canvas->concat(matrix);
+ }
+ canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ // restore the original matrix
+ if (nonIdentityMatrix) {
+ canvas->restore();
+ }
}
return layerImage;
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 431989519a70..345038769306 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -16,7 +16,7 @@
#pragma once
-#include "Layer.h"
+#include "DeferredLayerUpdater.h"
#include <SkCanvas.h>
#include <SkDrawable.h>
@@ -29,21 +29,21 @@ namespace skiapipeline {
* Draws a layer backed by an OpenGL texture into a SkCanvas.
*/
class LayerDrawable : public SkDrawable {
- public:
- explicit LayerDrawable(Layer* layer)
- : mLayer(layer) {}
+public:
+ explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer);
- protected:
- virtual SkRect onGetBounds() override {
- return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight());
- }
- virtual void onDraw(SkCanvas* canvas) override;
+
+protected:
+ virtual SkRect onGetBounds() override {
+ return SkRect::MakeWH(mLayerUpdater->getWidth(), mLayerUpdater->getHeight());
+ }
+ virtual void onDraw(SkCanvas* canvas) override;
private:
- sp<Layer> mLayer;
+ sp<DeferredLayerUpdater> mLayerUpdater;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 4ee47afe87fd..77925fd87fc7 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -15,6 +15,7 @@
*/
#include "RenderNodeDrawable.h"
+#include <SkPaintFilterCanvas.h>
#include "RenderNode.h"
#include "SkiaDisplayList.h"
#include "SkiaPipeline.h"
@@ -24,23 +25,24 @@ namespace android {
namespace uirenderer {
namespace skiapipeline {
-void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList,
- int nestLevel) {
+void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas,
+ const SkiaDisplayList& displayList,
+ int nestLevel) {
LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver);
for (auto& child : displayList.mChildNodes) {
const RenderProperties& childProperties = child.getNodeProperties();
- //immediate children cannot be projected on their parent
+ // immediate children cannot be projected on their parent
if (childProperties.getProjectBackwards() && nestLevel > 0) {
SkAutoCanvasRestore acr2(canvas, true);
- //Apply recorded matrix, which is a total matrix saved at recording time to avoid
- //replaying all DL commands.
+ // Apply recorded matrix, which is a total matrix saved at recording time to avoid
+ // replaying all DL commands.
canvas->concat(child.getRecordedMatrix());
child.drawContent(canvas);
}
- //skip walking sub-nodes if current display list contains a receiver with exception of
- //level 0, which is a known receiver
+ // skip walking sub-nodes if current display list contains a receiver with exception of
+ // level 0, which is a known receiver
if (0 == nestLevel || !displayList.containsProjectionReceiver()) {
SkAutoCanvasRestore acr(canvas, true);
SkMatrix nodeMatrix;
@@ -50,9 +52,9 @@ void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const Ski
hwuiMatrix.copyTo(nodeMatrix);
canvas->concat(nodeMatrix);
SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>(
- (const_cast<DisplayList*>(childNode->getDisplayList())));
+ (const_cast<DisplayList*>(childNode->getDisplayList())));
if (childDisplayList) {
- drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel+1);
+ drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel + 1);
}
}
}
@@ -91,8 +93,8 @@ const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
}
void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
- //negative and positive Z order are drawn out of order, if this render node drawable is in
- //a reordering section
+ // negative and positive Z order are drawn out of order, if this render node drawable is in
+ // a reordering section
if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {
this->forceDraw(canvas);
}
@@ -100,7 +102,7 @@ void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
RenderNode* renderNode = mRenderNode.get();
- if (SkiaPipeline::skpCaptureEnabled()) {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
}
@@ -108,7 +110,8 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
// We only respect the nothingToDraw check when we are composing a layer. This
// ensures that we paint the layer even if it is not currently visible in the
// event that the properties change and it becomes visible.
- if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
+ if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
+ (renderNode->nothingToDraw() && mComposeLayer)) {
return;
}
@@ -117,13 +120,13 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
SkAutoCanvasRestore acr(canvas, true);
const RenderProperties& properties = this->getNodeProperties();
- //pass this outline to the children that may clip backward projected nodes
- displayList->mProjectedOutline = displayList->containsProjectionReceiver()
- ? &properties.getOutline() : nullptr;
+ // pass this outline to the children that may clip backward projected nodes
+ displayList->mProjectedOutline =
+ displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr;
if (!properties.getProjectBackwards()) {
drawContent(canvas);
if (mProjectedDisplayList) {
- acr.restore(); //draw projected children using parent matrix
+ acr.restore(); // draw projected children using parent matrix
LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
SkAutoCanvasRestore acr2(canvas, shouldClip);
@@ -137,12 +140,11 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
displayList->mProjectedOutline = nullptr;
}
-static bool layerNeedsPaint(const LayerProperties& properties,
- float alphaMultiplier, SkPaint* paint) {
- if (alphaMultiplier < 1.0f
- || properties.alpha() < 255
- || properties.xferMode() != SkBlendMode::kSrcOver
- || properties.colorFilter() != nullptr) {
+static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
+ SkPaint* paint) {
+ paint->setFilterQuality(kLow_SkFilterQuality);
+ if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
+ properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
paint->setColorFilter(sk_ref_sp(properties.colorFilter()));
@@ -151,6 +153,29 @@ static bool layerNeedsPaint(const LayerProperties& properties,
return false;
}
+class AlphaFilterCanvas : public SkPaintFilterCanvas {
+public:
+ AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {}
+
+protected:
+ bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override {
+ SkTLazy<SkPaint> defaultPaint;
+ if (!*paint) {
+ paint->init(*defaultPaint.init());
+ }
+ paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha() * mAlpha);
+ return true;
+ }
+ void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
+ // We unroll the drawable using "this" canvas, so that draw calls contained inside will
+ // get their alpha applied. THe default SkPaintFilterCanvas::onDrawDrawable does not unroll.
+ drawable->draw(this, matrix);
+ }
+
+private:
+ float mAlpha;
+};
+
void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
RenderNode* renderNode = mRenderNode.get();
float alphaMultiplier = 1.0f;
@@ -167,7 +192,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix();
}
- //TODO should we let the bound of the drawable do this for us?
+ // TODO should we let the bound of the drawable do this for us?
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
if (!quickRejected) {
@@ -176,12 +201,15 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
// composing a hardware layer
if (renderNode->getLayerSurface() && mComposeLayer) {
SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
- SkPaint* paint = nullptr;
- SkPaint tmpPaint;
- if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
- paint = &tmpPaint;
- }
- renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
+ SkPaint paint;
+ layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+
+ // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
+ // we need to restrict the portion of the surface drawn to the size of the renderNode.
+ SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
+ SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
+ canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
+ bounds, bounds, &paint);
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
@@ -199,7 +227,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
}
}
- // composing a software layer with alpha
+ // composing a software layer with alpha
} else if (properties.effectiveLayerType() == LayerType::Software) {
SkPaint paint;
bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
@@ -211,13 +239,20 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
canvas->restore();
}
} else {
- displayList->draw(canvas);
+ if (alphaMultiplier < 1.0f) {
+ // Non-layer draw for a view with getHasOverlappingRendering=false, will apply
+ // the alpha to the paint of each nested draw.
+ AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier);
+ displayList->draw(&alphaCanvas);
+ } else {
+ displayList->draw(canvas);
+ }
}
}
}
void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
- float* alphaMultiplier) {
+ float* alphaMultiplier) {
if (properties.getLeft() != 0 || properties.getTop() != 0) {
canvas->translate(properties.getLeft(), properties.getTop());
}
@@ -237,7 +272,7 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S
int clipFlags = properties.getClippingFlags();
if (properties.getAlpha() < 1) {
if (isLayer) {
- clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
}
if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
*alphaMultiplier = properties.getAlpha();
@@ -246,18 +281,18 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S
Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
if (clipFlags) {
properties.getClippingRectForFlags(clipFlags, &layerBounds);
- clipFlags = 0; // all clipping done by savelayer
+ clipFlags = 0; // all clipping done by savelayer
}
- SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top,
- layerBounds.right, layerBounds.bottom);
- canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255));
+ SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top, layerBounds.right,
+ layerBounds.bottom);
+ canvas->saveLayerAlpha(&bounds, (int)(properties.getAlpha() * 255));
}
if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
// pretend alpha always causes savelayer to warn about
// performance problem affecting old versions
ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
- properties.getHeight());
+ properties.getHeight());
}
}
@@ -283,6 +318,6 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S
}
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index 3eed6476c994..ef21cd8a29b5 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -47,7 +47,7 @@ public:
* layer into the canvas.
*/
explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true,
- bool inReorderingSection = false)
+ bool inReorderingSection = false)
: mRenderNode(node)
, mRecordedTransform(canvas->getTotalMatrix())
, mComposeLayer(composeLayer)
@@ -113,13 +113,13 @@ private:
* @param nestLevel should be always 0. Used to track how far we are from the receiver.
*/
void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList,
- int nestLevel = 0);
+ int nestLevel = 0);
/**
* Applies the rendering properties of a view onto a SkCanvas.
*/
static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
- float* alphaMultiplier);
+ float* alphaMultiplier);
/**
* Stores transform on the canvas at time of recording and is used for
@@ -150,6 +150,6 @@ private:
SkiaDisplayList* mProjectedDisplayList = nullptr;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 374d364787de..25c51f2716e6 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -30,26 +30,24 @@ namespace uirenderer {
namespace skiapipeline {
StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
- : mEndChildIndex(0)
- , mBeginChildIndex(data->mChildNodes.size())
- , mDisplayList(data) {
-}
+ : mEndChildIndex(0), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {}
void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
if (mChildren.empty()) {
- //mChildren is allocated and initialized only the first time onDraw is called and cached for
- //subsequent calls
+ // mChildren is allocated and initialized only the first time onDraw is called and cached
+ // for
+ // subsequent calls
mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
}
}
std::stable_sort(mChildren.begin(), mChildren.end(),
- [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
- const float aZValue = a->getNodeProperties().getZ();
- const float bZValue = b->getNodeProperties().getZ();
- return aZValue < bZValue;
- });
+ [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
+ const float aZValue = a->getNodeProperties().getZ();
+ const float bZValue = b->getNodeProperties().getZ();
+ return aZValue < bZValue;
+ });
size_t drawIndex = 0;
const size_t endIndex = mChildren.size();
@@ -57,7 +55,7 @@ void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
RenderNodeDrawable* childNode = mChildren[drawIndex];
SkASSERT(childNode);
const float casterZ = childNode->getNodeProperties().getZ();
- if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z
+ if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z
return;
}
childNode->forceDraw(canvas);
@@ -85,8 +83,9 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
size_t drawIndex = 0;
const size_t endIndex = zChildren.size();
- while (drawIndex < endIndex //draw only children with positive Z
- && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++;
+ while (drawIndex < endIndex // draw only children with positive Z
+ && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON)
+ drawIndex++;
size_t shadowIndex = drawIndex;
float lastCasterZ = 0.0f;
@@ -98,7 +97,7 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
// OR if its caster's Z value is similar to the previous potential caster
if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
this->drawShadow(canvas, zChildren[shadowIndex]);
- lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
shadowIndex++;
continue;
}
@@ -112,27 +111,29 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
}
}
+static SkColor multiplyAlpha(SkColor color, float alpha) {
+ return SkColorSetA(color, alpha * SkColorGetA(color));
+}
+
// copied from FrameBuilder::deferShadow
void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
const RenderProperties& casterProperties = caster->getNodeProperties();
- if (casterProperties.getAlpha() <= 0.0f
- || casterProperties.getOutline().getAlpha() <= 0.0f
- || !casterProperties.getOutline().getPath()
- || casterProperties.getScaleX() == 0
- || casterProperties.getScaleY() == 0) {
+ if (casterProperties.getAlpha() <= 0.0f || casterProperties.getOutline().getAlpha() <= 0.0f ||
+ !casterProperties.getOutline().getPath() || casterProperties.getScaleX() == 0 ||
+ casterProperties.getScaleY() == 0) {
// no shadow to draw
return;
}
- const SkScalar casterAlpha = casterProperties.getAlpha()
- * casterProperties.getOutline().getAlpha();
+ const SkScalar casterAlpha =
+ casterProperties.getAlpha() * casterProperties.getOutline().getAlpha();
if (casterAlpha <= 0.0f) {
return;
}
- float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha()/255.f)*casterAlpha;
- float spotAlpha = (SkiaPipeline::getSpotShadowAlpha()/255.f)*casterAlpha;
+ float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha() / 255.f) * casterAlpha;
+ float spotAlpha = (SkiaPipeline::getSpotShadowAlpha() / 255.f) * casterAlpha;
const RevealClip& revealClip = casterProperties.getRevealClip();
const SkPath* revealClipPath = revealClip.getPath();
@@ -163,28 +164,22 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
hwuiMatrix.copyTo(shadowMatrix);
canvas->concat(shadowMatrix);
- const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
- // holds temporary SkPath to store the result of intersections
- SkPath tmpPath;
- const SkPath* casterPath = casterOutlinePath;
+ // default the shadow-casting path to the outline of the caster
+ const SkPath* casterPath = casterProperties.getOutline().getPath();
+
+ // intersect the shadow-casting path with the clipBounds, if present
+ if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) {
+ casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect);
+ }
- // TODO: In to following course of code that calculates the final shape, is there an optimal
- // of doing the Op calculations?
// intersect the shadow-casting path with the reveal, if present
+ SkPath tmpPath; // holds temporary SkPath to store the result of intersections
if (revealClipPath) {
Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
tmpPath.setIsVolatile(true);
casterPath = &tmpPath;
}
- // intersect the shadow-casting path with the clipBounds, if present
- if (clippedToBounds) {
- SkPath clipBoundsPath;
- clipBoundsPath.addRect(casterClipRect);
- Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
- tmpPath.setIsVolatile(true);
- casterPath = &tmpPath;
- }
const Vector3 lightPos = SkiaPipeline::getLightCenter();
SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
SkPoint3 zParams;
@@ -196,11 +191,14 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
} else {
zParams = SkPoint3::Make(0, 0, casterProperties.getZ());
}
- SkShadowUtils::DrawShadow(canvas, *casterPath, zParams, skiaLightPos,
- SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
- casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
+ SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha);
+ SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha);
+ SkShadowUtils::DrawShadow(
+ canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(),
+ ambientColor, spotColor,
+ casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index 9f00d23ae985..3c48d3604864 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -41,9 +41,7 @@ public:
explicit StartReorderBarrierDrawable(SkiaDisplayList* data);
protected:
- virtual SkRect onGetBounds() override {
- return SkRect::MakeLargest();
- }
+ virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); }
virtual void onDraw(SkCanvas* canvas) override;
private:
@@ -65,16 +63,16 @@ private:
class EndReorderBarrierDrawable : public SkDrawable {
public:
explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier);
+
protected:
- virtual SkRect onGetBounds() override {
- return SkRect::MakeLargest();
- }
+ virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); }
virtual void onDraw(SkCanvas* canvas) override;
+
private:
void drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster);
StartReorderBarrierDrawable* mStartBarrier;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
new file mode 100644
index 000000000000..2fa56f613144
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ShaderCache.h"
+#include <algorithm>
+#include <log/log.h>
+#include <thread>
+#include "FileBlobCache.h"
+#include "Properties.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// Cache size limits.
+static const size_t maxKeySize = 1024;
+static const size_t maxValueSize = 64 * 1024;
+static const size_t maxTotalSize = 512 * 1024;
+
+ShaderCache::ShaderCache() {
+ // There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
+}
+
+ShaderCache ShaderCache::sCache;
+
+ShaderCache& ShaderCache::get() {
+ return sCache;
+}
+
+void ShaderCache::initShaderDiskCache() {
+ ATRACE_NAME("initShaderDiskCache");
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // Emulators can switch between different renders either as part of config
+ // or snapshot migration. Also, program binaries may not work well on some
+ // desktop / laptop GPUs. Thus, disable the shader disk cache for emulator builds.
+ if (!Properties::runningInEmulator && mFilename.length() > 0) {
+ mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
+ mInitialized = true;
+ }
+}
+
+void ShaderCache::setFilename(const char* filename) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFilename = filename;
+}
+
+BlobCache* ShaderCache::getBlobCacheLocked() {
+ LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized");
+ return mBlobCache.get();
+}
+
+sk_sp<SkData> ShaderCache::load(const SkData& key) {
+ ATRACE_NAME("ShaderCache::load");
+ size_t keySize = key.size();
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mInitialized) {
+ return nullptr;
+ }
+
+ // mObservedBlobValueSize is reasonably big to avoid memory reallocation
+ // Allocate a buffer with malloc. SkData takes ownership of that allocation and will call free.
+ void* valueBuffer = malloc(mObservedBlobValueSize);
+ if (!valueBuffer) {
+ return nullptr;
+ }
+ BlobCache* bc = getBlobCacheLocked();
+ size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+ int maxTries = 3;
+ while (valueSize > mObservedBlobValueSize && maxTries > 0) {
+ mObservedBlobValueSize = std::min(valueSize, maxValueSize);
+ valueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+ if (!valueBuffer) {
+ return nullptr;
+ }
+ valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+ maxTries--;
+ }
+ if (!valueSize) {
+ free(valueBuffer);
+ return nullptr;
+ }
+ if (valueSize > mObservedBlobValueSize) {
+ ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+ free(valueBuffer);
+ return nullptr;
+ }
+ return SkData::MakeFromMalloc(valueBuffer, valueSize);
+}
+
+void ShaderCache::store(const SkData& key, const SkData& data) {
+ ATRACE_NAME("ShaderCache::store");
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mInitialized) {
+ return;
+ }
+
+ size_t valueSize = data.size();
+ size_t keySize = key.size();
+ if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) {
+ ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize);
+ return;
+ }
+
+ const void* value = data.data();
+
+ BlobCache* bc = getBlobCacheLocked();
+ bc->set(key.data(), keySize, value, valueSize);
+
+ if (!mSavePending && mDeferredSaveDelay > 0) {
+ mSavePending = true;
+ std::thread deferredSaveThread([this]() {
+ sleep(mDeferredSaveDelay);
+ std::lock_guard<std::mutex> lock(mMutex);
+ ATRACE_NAME("ShaderCache::saveToDisk");
+ if (mInitialized && mBlobCache) {
+ mBlobCache->writeToFile();
+ }
+ mSavePending = false;
+ });
+ deferredSaveThread.detach();
+ }
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
new file mode 100644
index 000000000000..27473d67bd1a
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cutils/compiler.h>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <GrContextOptions.h>
+
+namespace android {
+
+class BlobCache;
+class FileBlobCache;
+
+namespace uirenderer {
+namespace skiapipeline {
+
+class ShaderCache : public GrContextOptions::PersistentCache {
+public:
+ /**
+ * "get" returns a pointer to the singleton ShaderCache object. This
+ * singleton object will never be destroyed.
+ */
+ ANDROID_API static ShaderCache& get();
+
+ /**
+ * "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache
+ * into an initialized state, such that it is able to insert and retrieve entries from the
+ * cache. This should be called when HWUI pipeline is initialized. When not in the initialized
+ * state the load and store methods will return without performing any cache operations.
+ */
+ virtual void initShaderDiskCache();
+
+ /**
+ * "setFilename" sets the name of the file that should be used to store
+ * cache contents from one program invocation to another. This function does not perform any
+ * disk operation and it should be invoked before "initShaderCache".
+ */
+ virtual void setFilename(const char* filename);
+
+ /**
+ * "load" attempts to retrieve the value blob associated with a given key
+ * blob from cache. This will be called by Skia, when it needs to compile a new SKSL shader.
+ */
+ sk_sp<SkData> load(const SkData& key) override;
+
+ /**
+ * "store" attempts to insert a new key/value blob pair into the cache.
+ * This will be called by Skia after it compiled a new SKSL shader
+ */
+ void store(const SkData& key, const SkData& data) override;
+
+private:
+ // Creation and (the lack of) destruction is handled internally.
+ ShaderCache();
+
+ // Copying is disallowed.
+ ShaderCache(const ShaderCache&) = delete;
+ void operator=(const ShaderCache&) = delete;
+
+ /**
+ * "getBlobCacheLocked" returns the BlobCache object being used to store the
+ * key/value blob pairs. If the BlobCache object has not yet been created,
+ * this will do so, loading the serialized cache contents from disk if
+ * possible.
+ */
+ BlobCache* getBlobCacheLocked();
+
+ /**
+ * "mInitialized" indicates whether the ShaderCache is in the initialized
+ * state. It is initialized to false at construction time, and gets set to
+ * true when initialize is called.
+ * When in this state, the cache behaves as normal. When not,
+ * the load and store methods will return without performing any cache
+ * operations.
+ */
+ bool mInitialized = false;
+
+ /**
+ * "mBlobCache" is the cache in which the key/value blob pairs are stored. It
+ * is initially NULL, and will be initialized by getBlobCacheLocked the
+ * first time it's needed.
+ * The blob cache contains the Android build number. We treat version mismatches as an empty
+ * cache (logic implemented in BlobCache::unflatten).
+ */
+ std::unique_ptr<FileBlobCache> mBlobCache;
+
+ /**
+ * "mFilename" is the name of the file for storing cache contents in between
+ * program invocations. It is initialized to an empty string at
+ * construction time, and can be set with the setCacheFilename method. An
+ * empty string indicates that the cache should not be saved to or restored
+ * from disk.
+ */
+ std::string mFilename;
+
+ /**
+ * "mSavePending" indicates whether or not a deferred save operation is
+ * pending. Each time a key/value pair is inserted into the cache via
+ * load, a deferred save is initiated if one is not already pending.
+ * This will wait some amount of time and then trigger a save of the cache
+ * contents to disk.
+ */
+ bool mSavePending = false;
+
+ /**
+ * "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
+ */
+ size_t mObservedBlobValueSize = 20*1024;
+
+ /**
+ * The time in seconds to wait before saving newly inserted cache entries.
+ */
+ unsigned int mDeferredSaveDelay = 4;
+
+ /**
+ * "mMutex" is the mutex used to prevent concurrent access to the member
+ * variables. It must be locked whenever the member variables are accessed.
+ */
+ mutable std::mutex mMutex;
+
+ /**
+ * "sCache" is the singleton ShaderCache object.
+ */
+ static ShaderCache sCache;
+
+ friend class ShaderCacheTestUtils; //used for unit testing
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 3ddc09fbeca1..aa14699ae4c2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -16,14 +16,13 @@
#include "SkiaDisplayList.h"
-#include "renderthread/CanvasContext.h"
-#include "VectorDrawable.h"
#include "DumpOpsCanvas.h"
#include "SkiaPipeline.h"
+#include "VectorDrawable.h"
+#include "renderthread/CanvasContext.h"
#include <SkImagePriv.h>
-
namespace android {
namespace uirenderer {
namespace skiapipeline {
@@ -32,6 +31,9 @@ void SkiaDisplayList::syncContents() {
for (auto& functor : mChildFunctors) {
functor.syncFunctor();
}
+ for (auto& animatedImage : mAnimatedImages) {
+ animatedImage->syncProperties();
+ }
for (auto& vectorDrawable : mVectorDrawables) {
vectorDrawable->syncProperties();
}
@@ -49,8 +51,8 @@ void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn)
}
}
-bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& info,
- bool functorsNeedLayer,
+bool SkiaDisplayList::prepareListAndChildren(
+ TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
// If the prepare tree is triggered by the UI thread and no previous call to
// pinImages has failed then we must pin all mutable images in the GPU cache
@@ -64,7 +66,7 @@ bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& i
}
bool hasBackwardProjectedNodesHere = false;
- bool hasBackwardProjectedNodesSubtree= false;
+ bool hasBackwardProjectedNodesSubtree = false;
for (auto& child : mChildNodes) {
hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards();
@@ -78,23 +80,35 @@ bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& i
info.damageAccumulator->popTransform();
}
- //The purpose of next block of code is to reset projected display list if there are no
- //backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree
+ // The purpose of next block of code is to reset projected display list if there are no
+ // backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree
if (mProjectionReceiver) {
- mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this : nullptr);
+ mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this
+ : nullptr);
info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere;
} else {
- info.hasBackwardProjectedNodes = hasBackwardProjectedNodesSubtree
- || hasBackwardProjectedNodesHere;
+ info.hasBackwardProjectedNodes =
+ hasBackwardProjectedNodesSubtree || hasBackwardProjectedNodesHere;
}
bool isDirty = false;
+ for (auto& animatedImage : mAnimatedImages) {
+ // If any animated image in the display list needs updated, then damage the node.
+ if (animatedImage->isDirty()) {
+ isDirty = true;
+ }
+ if (animatedImage->isRunning()) {
+ info.out.hasAnimations = true;
+ }
+ }
+
for (auto& vectorDrawable : mVectorDrawables) {
// If any vector drawable in the display list needs update, damage the node.
if (vectorDrawable->isDirty()) {
isDirty = true;
static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
- ->getVectorDrawables()->push_back(vectorDrawable);
+ ->getVectorDrawables()
+ ->push_back(vectorDrawable);
}
vectorDrawable->setPropertyChangeWillBeConsumed(true);
}
@@ -108,6 +122,7 @@ void SkiaDisplayList::reset() {
mMutableImages.clear();
mVectorDrawables.clear();
+ mAnimatedImages.clear();
mChildFunctors.clear();
mChildNodes.clear();
@@ -121,6 +136,6 @@ void SkiaDisplayList::output(std::ostream& output, uint32_t level) {
mDisplayList.draw(&canvas);
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 66375d13826c..818ec114a5b3 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -17,12 +17,13 @@
#pragma once
#include "DisplayList.h"
+#include "hwui/AnimatedImageDrawable.h"
#include "GLFunctorDrawable.h"
#include "RenderNodeDrawable.h"
-#include <deque>
#include <SkLiteDL.h>
#include <SkLiteRecorder.h>
+#include <deque>
namespace android {
namespace uirenderer {
@@ -62,7 +63,7 @@ public:
* need to monitor that they don't extend beyond the lifetime of the class
* that creates them. Allocator dtor invokes all SkDrawable dtors.
*/
- template<class T, typename... Params>
+ template <class T, typename... Params>
SkDrawable* allocateDrawable(Params&&... params) {
return allocator.create<T>(std::forward<Params>(params)...);
}
@@ -113,7 +114,8 @@ public:
* to subclass from DisplayList
*/
- bool prepareListAndChildren(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+ bool prepareListAndChildren(
+ TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) override;
/**
@@ -143,26 +145,28 @@ public:
std::deque<GLFunctorDrawable> mChildFunctors;
std::vector<SkImage*> mMutableImages;
std::vector<VectorDrawableRoot*> mVectorDrawables;
+ std::vector<AnimatedImageDrawable*> mAnimatedImages;
SkLiteDL mDisplayList;
- //mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection
- //receiver. It is set at record time and used at both prepare and draw tree traversals to
- //make sure backward projected nodes are found and drawn immediately after mProjectionReceiver.
+ // mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection
+ // receiver. It is set at record time and used at both prepare and draw tree traversals to
+ // make sure backward projected nodes are found and drawn immediately after mProjectionReceiver.
RenderNodeDrawable* mProjectionReceiver = nullptr;
- //mProjectedOutline is valid only when render node tree is traversed during the draw pass.
- //Render nodes that have a child receiver node, will store a pointer to their outline in
- //mProjectedOutline. Child receiver node will apply the clip before any backward projected
- //node is drawn.
+ // mProjectedOutline is valid only when render node tree is traversed during the draw pass.
+ // Render nodes that have a child receiver node, will store a pointer to their outline in
+ // mProjectedOutline. Child receiver node will apply the clip before any backward projected
+ // node is drawn.
const Outline* mProjectedOutline = nullptr;
- //mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw
- //pass. Render nodes that have a child receiver node, will store their matrix in
- //mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with the
- //outline of their parent.
+ // mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw
+ // pass. Render nodes that have a child receiver node, will store their matrix in
+ // mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with
+ // the
+ // outline of their parent.
SkMatrix mProjectedReceiverParentMatrix;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaLayer.h b/libs/hwui/pipeline/skia/SkiaLayer.h
index 904d57e073ca..82e6914e8b7f 100644
--- a/libs/hwui/pipeline/skia/SkiaLayer.h
+++ b/libs/hwui/pipeline/skia/SkiaLayer.h
@@ -26,14 +26,12 @@ namespace skiapipeline {
/**
* An offscreen rendering target used to contain the contents a RenderNode.
*/
-struct SkiaLayer
-{
+struct SkiaLayer {
sk_sp<SkSurface> layerSurface;
Matrix4 inverseTransformInWindow;
bool hasRenderedSinceRepaint = false;
};
-
} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 925db303461f..365d7403e046 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -16,15 +16,15 @@
#include "SkiaOpenGLPipeline.h"
-#include "hwui/Bitmap.h"
#include "DeferredLayerUpdater.h"
#include "GlLayer.h"
#include "LayerDrawable.h"
-#include "renderthread/EglManager.h"
-#include "renderthread/Frame.h"
-#include "renderstate/RenderState.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
+#include "hwui/Bitmap.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/Frame.h"
#include "utils/TraceUtils.h"
#include <GrBackendSurface.h>
@@ -39,9 +39,7 @@ namespace uirenderer {
namespace skiapipeline {
SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
- : SkiaPipeline(thread)
- , mEglManager(thread.eglManager()) {
-}
+ : SkiaPipeline(thread), mEglManager(thread.eglManager()) {}
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
// TODO: Figure out why this workaround is needed, see b/13913604
@@ -55,27 +53,27 @@ MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
Frame SkiaOpenGLPipeline::getFrame() {
LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
- "drawRenderNode called on a context with no surface!");
+ "drawRenderNode called on a context with no surface!");
return mEglManager.beginFrame(mEglSurface);
}
-bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
- const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
- const std::vector<sp<RenderNode>>& renderNodes,
- FrameInfoVisualizer* profiler) {
-
+bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
+ bool opaque, bool wideColorGamut,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode>>& renderNodes,
+ FrameInfoVisualizer* profiler) {
mEglManager.damageFrame(frame, dirty);
// setup surface for fbo0
GrGLFramebufferInfo fboInfo;
fboInfo.fFBOID = 0;
+ GrPixelConfig pixelConfig =
+ wideColorGamut ? kRGBA_half_GrPixelConfig : kRGBA_8888_GrPixelConfig;
GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE,
- kRGBA_8888_GrPixelConfig, fboInfo);
+ pixelConfig, fboInfo);
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
@@ -84,13 +82,13 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, nullptr, &props));
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut,
- contentDrawBounds, surface);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds,
+ surface);
layerUpdateQueue->clear();
// Draw visual debugging features
- if (CC_UNLIKELY(Properties::showDirtyRegions
- || ProfileType::None != Properties::getProfileType())) {
+ if (CC_UNLIKELY(Properties::showDirtyRegions ||
+ ProfileType::None != Properties::getProfileType())) {
SkCanvas* profileCanvas = surface->getCanvas();
SkiaProfileRenderer profileRenderer(profileCanvas);
profiler->draw(profileRenderer);
@@ -105,9 +103,8 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
return true;
}
-bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew,
- const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
-
+bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) {
GL_CHECKPOINT(LOW);
// Even if we decided to cancel the frame, from the perspective of jank
@@ -132,15 +129,30 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi
deferredLayer->updateTexImage();
deferredLayer->apply();
- SkCanvas canvas(*bitmap);
+ /* This intermediate surface is present to work around a bug in SwiftShader that
+ * prevents us from reading the contents of the layer's texture directly. The
+ * workaround involves first rendering that texture into an intermediate buffer and
+ * then reading from the intermediate buffer into the bitmap.
+ */
+ sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
+ SkBudgeted::kYes, bitmap->info());
+
Layer* layer = deferredLayer->backingLayer();
- return LayerDrawable::DrawLayer(mRenderThread.getGrContext(), &canvas, layer);
+ if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer)) {
+ sk_sp<SkImage> tmpImage = tmpSurface->makeImageSnapshot();
+ if (tmpImage->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
+ bitmap->notifyPixelsChanged();
+ return true;
+ }
+ }
+
+ return false;
}
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
- GlLayer* layer = new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha,
- mode, blend);
+ SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+ GlLayer* layer =
+ new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
layer->generateTexture();
return layer;
}
@@ -157,8 +169,7 @@ void SkiaOpenGLPipeline::onStop() {
}
bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
- ColorMode colorMode) {
-
+ ColorMode colorMode) {
if (mEglSurface != EGL_NO_SURFACE) {
mEglManager.destroySurface(mEglSurface);
mEglSurface = EGL_NO_SURFACE;
@@ -204,8 +215,7 @@ void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* func
class AutoEglFence {
public:
- AutoEglFence(EGLDisplay display)
- : mDisplay(display) {
+ AutoEglFence(EGLDisplay display) : mDisplay(display) {
fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
}
@@ -216,17 +226,17 @@ public:
}
EGLSyncKHR fence = EGL_NO_SYNC_KHR;
+
private:
EGLDisplay mDisplay = EGL_NO_DISPLAY;
};
class AutoEglImage {
public:
- AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer)
- : mDisplay(display) {
- EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
- image = eglCreateImageKHR(display, EGL_NO_CONTEXT,
- EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
+ AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
+ EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
+ imageAttrs);
}
~AutoEglImage() {
@@ -236,6 +246,7 @@ public:
}
EGLImageKHR image = EGL_NO_IMAGE_KHR;
+
private:
EGLDisplay mDisplay = EGL_NO_DISPLAY;
};
@@ -247,16 +258,14 @@ public:
glBindTexture(GL_TEXTURE_2D, mTexture);
}
- ~AutoSkiaGlTexture() {
- glDeleteTextures(1, &mTexture);
- }
+ ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
private:
GLuint mTexture = 0;
};
sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
- SkBitmap& skBitmap) {
+ SkBitmap& skBitmap) {
renderThread.eglManager().initialize();
sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext());
@@ -265,73 +274,67 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr
GLint format, type;
bool isSupported = false;
- //TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
+ // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
switch (info.colorType()) {
- case kRGBA_8888_SkColorType:
- isSupported = true;
- // ARGB_4444 is upconverted to RGBA_8888
- case kARGB_4444_SkColorType:
- pixelFormat = PIXEL_FORMAT_RGBA_8888;
- format = GL_RGBA;
- type = GL_UNSIGNED_BYTE;
- break;
- case kRGBA_F16_SkColorType:
- isSupported = grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig);
- if (isSupported) {
- type = GL_HALF_FLOAT;
- pixelFormat = PIXEL_FORMAT_RGBA_FP16;
- } else {
+ case kRGBA_8888_SkColorType:
+ isSupported = true;
+ // ARGB_4444 is upconverted to RGBA_8888
+ case kARGB_4444_SkColorType:
+ pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ format = GL_RGBA;
type = GL_UNSIGNED_BYTE;
+ break;
+ case kRGBA_F16_SkColorType:
+ isSupported = grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig);
+ if (isSupported) {
+ type = GL_HALF_FLOAT;
+ pixelFormat = PIXEL_FORMAT_RGBA_FP16;
+ } else {
+ type = GL_UNSIGNED_BYTE;
+ pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ }
+ format = GL_RGBA;
+ break;
+ case kRGB_565_SkColorType:
+ isSupported = true;
+ pixelFormat = PIXEL_FORMAT_RGB_565;
+ format = GL_RGB;
+ type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case kGray_8_SkColorType:
+ isSupported = true;
pixelFormat = PIXEL_FORMAT_RGBA_8888;
- }
- format = GL_RGBA;
- break;
- case kRGB_565_SkColorType:
- isSupported = true;
- pixelFormat = PIXEL_FORMAT_RGB_565;
- format = GL_RGB;
- type = GL_UNSIGNED_SHORT_5_6_5;
- break;
- case kGray_8_SkColorType:
- isSupported = true;
- pixelFormat = PIXEL_FORMAT_RGBA_8888;
- format = GL_LUMINANCE;
- type = GL_UNSIGNED_BYTE;
- break;
- default:
- ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
- return nullptr;
- }
-
- auto colorSpace = info.colorSpace();
- bool convertToSRGB = false;
- if (colorSpace && (!colorSpace->isSRGB())) {
- isSupported = false;
- convertToSRGB = true;
+ format = GL_LUMINANCE;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
+ return nullptr;
}
SkBitmap bitmap;
if (isSupported) {
bitmap = skBitmap;
} else {
- bitmap.allocPixels(SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(),
- nullptr));
+ bitmap.allocPixels(
+ SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
bitmap.eraseColor(0);
- if (info.colorType() == kRGBA_F16_SkColorType || convertToSRGB) {
+ if (info.colorType() == kRGBA_F16_SkColorType) {
// Drawing RGBA_F16 onto ARGB_8888 is not supported
skBitmap.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
- bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
+ bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
} else {
SkCanvas canvas(bitmap);
canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
}
}
- sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat,
- GraphicBuffer::USAGE_HW_TEXTURE |
- GraphicBuffer::USAGE_SW_WRITE_NEVER |
- GraphicBuffer::USAGE_SW_READ_NEVER,
- std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) + "]");
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ info.width(), info.height(), pixelFormat,
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER,
+ std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) +
+ "]");
status_t error = buffer->initCheck();
if (error < 0) {
@@ -339,18 +342,17 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr
return nullptr;
}
- //upload the bitmap into a texture
+ // upload the bitmap into a texture
EGLDisplay display = eglGetCurrentDisplay();
- LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
- "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
- uirenderer::renderthread::EglManager::eglErrorString());
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
// We use an EGLImage to access the content of the GraphicBuffer
// The EGL image is later bound to a 2D texture
- EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer();
+ EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer();
AutoEglImage autoImage(display, clientBuffer);
if (autoImage.image == EGL_NO_IMAGE_KHR) {
ALOGW("Could not create EGL image, err =%s",
- uirenderer::renderthread::EglManager::eglErrorString());
+ uirenderer::renderthread::EglManager::eglErrorString());
return nullptr;
}
AutoSkiaGlTexture glTexture;
@@ -361,7 +363,7 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr
// But asynchronous in sense that driver may upload texture onto hardware buffer when we first
// use it in drawing
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, info.width(), info.height(), format, type,
- bitmap.getPixels());
+ bitmap.getPixels());
GL_CHECKPOINT(MODERATE);
// The fence is used to wait for the texture upload to finish
@@ -375,7 +377,7 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr
// The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
// pipeline flush (similar to what a glFlush() would do.)
EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence,
- EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError());
return nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index aa29c8e3babc..5e013b6697a7 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -33,25 +33,24 @@ public:
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
- const std::vector< sp<RenderNode> >& renderNodes,
- FrameInfoVisualizer* profiler) override;
+ const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ FrameInfo* currentFrameInfo, bool* requireSwap) override;
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
- renderthread::ColorMode colorMode) override;
+ renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
- SkBitmap& skBitmap);
+ SkBitmap& skBitmap);
private:
renderthread::EglManager& mEglManager;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 311419dd2b65..107890e57a19 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -16,15 +16,16 @@
#include "SkiaOpenGLReadback.h"
-#include "Matrix.h"
-#include "Properties.h"
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GrBackendSurface.h>
#include <SkCanvas.h>
#include <SkSurface.h>
-#include <GrBackendSurface.h>
#include <gl/GrGLInterface.h>
#include <gl/GrGLTypes.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
+#include "DeviceInfo.h"
+#include "Matrix.h"
+#include "Properties.h"
using namespace android::uirenderer::renderthread;
@@ -33,8 +34,8 @@ namespace uirenderer {
namespace skiapipeline {
CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {
-
+ int imgWidth, int imgHeight, const Rect& srcRect,
+ SkBitmap* bitmap) {
GLuint sourceTexId;
glGenTextures(1, &sourceTexId);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
@@ -44,8 +45,7 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
- grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
- (GrBackendContext)glInterface.get()));
+ grContext = GrContext::MakeGL(std::move(glInterface));
} else {
grContext->resetContext();
}
@@ -54,56 +54,76 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4
externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
externalTexture.fID = sourceTexId;
- GrBackendTexture backendTexture(imgWidth, imgHeight, kRGBA_8888_GrPixelConfig, externalTexture);
+ GrPixelConfig pixelConfig;
+ switch (bitmap->colorType()) {
+ case kRGBA_F16_SkColorType:
+ pixelConfig = kRGBA_half_GrPixelConfig;
+ break;
+ case kN32_SkColorType:
+ default:
+ pixelConfig = kRGBA_8888_GrPixelConfig;
+ break;
+ }
+
+ if (pixelConfig == kRGBA_half_GrPixelConfig &&
+ !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) {
+ ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
+ return CopyResult::DestinationInvalid;
+ }
+
+ GrBackendTexture backendTexture(imgWidth, imgHeight, pixelConfig, externalTexture);
CopyResult copyResult = CopyResult::UnknownError;
sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
- kTopLeft_GrSurfaceOrigin));
+ kTopLeft_GrSurfaceOrigin));
if (image) {
- SkMatrix textureMatrix;
- imgTransform.copyTo(textureMatrix);
-
- // remove the y-flip applied to the matrix
- SkMatrix yFlip = SkMatrix::MakeScale(1, -1);
- yFlip.postTranslate(0,1);
- textureMatrix.preConcat(yFlip);
-
- // multiply by image size, because textureMatrix maps to [0..1] range
- textureMatrix[SkMatrix::kMTransX] *= imgWidth;
- textureMatrix[SkMatrix::kMTransY] *= imgHeight;
-
- // swap rotation and translation part of the matrix, because we convert from
- // right-handed Cartesian to left-handed coordinate system.
- std::swap(textureMatrix[SkMatrix::kMTransX], textureMatrix[SkMatrix::kMTransY]);
- std::swap(textureMatrix[SkMatrix::kMSkewX], textureMatrix[SkMatrix::kMSkewY]);
-
- // convert to Skia data structures
- SkRect skiaSrcRect = srcRect.toSkRect();
- SkMatrix textureMatrixInv;
+ int displayedWidth = imgWidth, displayedHeight = imgHeight;
+ // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
+ // size.
+ if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
+ std::swap(displayedWidth, displayedHeight);
+ }
SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
- bool srcNotEmpty = false;
- if (textureMatrix.invert(&textureMatrixInv)) {
- if (skiaSrcRect.isEmpty()) {
- skiaSrcRect = SkRect::MakeIWH(imgWidth, imgHeight);
- srcNotEmpty = !skiaSrcRect.isEmpty();
- } else {
- // src and dest rectangles need to be converted into texture coordinates before the
- // rotation matrix is applied (because drawImageRect preconcat its matrix).
- textureMatrixInv.mapRect(&skiaSrcRect);
- srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(imgWidth, imgHeight));
- }
- textureMatrixInv.mapRect(&skiaDestRect);
+ SkRect skiaSrcRect = srcRect.toSkRect();
+ if (skiaSrcRect.isEmpty()) {
+ skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
}
+ bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
if (srcNotEmpty) {
+ SkMatrix textureMatrixInv;
+ imgTransform.copyTo(textureMatrixInv);
+ // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
+ // use bottom left origin and remove flipV and invert transformations.
+ SkMatrix flipV;
+ flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
+ textureMatrixInv.preConcat(flipV);
+ textureMatrixInv.preScale(1.0f / displayedWidth, 1.0f / displayedHeight);
+ textureMatrixInv.postScale(imgWidth, imgHeight);
+ SkMatrix textureMatrix;
+ if (!textureMatrixInv.invert(&textureMatrix)) {
+ textureMatrix = textureMatrixInv;
+ }
+
+ textureMatrixInv.mapRect(&skiaSrcRect);
+ textureMatrixInv.mapRect(&skiaDestRect);
+
// we render in an offscreen buffer to scale and to avoid an issue b/62262733
// with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
- sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
- grContext.get(), SkBudgeted::kYes, bitmap->info());
+ sk_sp<SkSurface> scaledSurface =
+ SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, bitmap->info());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
+ // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
+ // is codified by tests using golden images like DecodeAccuracyTest.
+ if (skiaSrcRect.width() != bitmap->width() ||
+ skiaSrcRect.height() != bitmap->height()) {
+ // TODO: apply filter always, but check if tests will be fine
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ }
scaledSurface->getCanvas()->concat(textureMatrix);
- scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint);
+ scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
+ SkCanvas::kFast_SrcRectConstraint);
image = scaledSurface->makeImageSnapshot();
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
index d914409628d0..cc9fb3b0a5e0 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
@@ -25,9 +25,11 @@ namespace skiapipeline {
class SkiaOpenGLReadback : public OpenGLReadback {
public:
SkiaOpenGLReadback(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
+
protected:
virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override;
+ int imgWidth, int imgHeight, const Rect& srcRect,
+ SkBitmap* bitmap) override;
};
} /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 03792e0ed291..9db39d954e4c 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -16,16 +16,14 @@
#include "SkiaPipeline.h"
-#include "utils/TraceUtils.h"
#include <SkImageEncoder.h>
#include <SkImagePriv.h>
#include <SkOverdrawCanvas.h>
#include <SkOverdrawColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
-#include <SkPixelSerializer.h>
-#include <SkStream.h>
#include "VectorDrawable.h"
+#include "utils/TraceUtils.h"
#include <unistd.h>
@@ -35,18 +33,22 @@ namespace android {
namespace uirenderer {
namespace skiapipeline {
-float SkiaPipeline::mLightRadius = 0;
+float SkiaPipeline::mLightRadius = 0;
uint8_t SkiaPipeline::mAmbientShadowAlpha = 0;
uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
-SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
+SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
mVectorDrawables.reserve(30);
}
+SkiaPipeline::~SkiaPipeline() {
+ unpinImages();
+}
+
TaskManager* SkiaPipeline::getTaskManager() {
- return &mTaskManager;
+ return mRenderThread.cacheManager().getTaskManager();
}
void SkiaPipeline::onDestroyHardwareResources() {
@@ -71,9 +73,15 @@ void SkiaPipeline::unpinImages() {
mPinnedImages.clear();
}
+void SkiaPipeline::onPrepareTree() {
+ // The only time mVectorDrawables is not empty is if prepare tree was called 2 times without
+ // a renderFrame in the middle.
+ mVectorDrawables.clear();
+}
+
void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo) {
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo) {
updateLighting(lightGeometry, lightInfo);
ATRACE_NAME("draw layers");
renderVectorDrawableCache();
@@ -81,9 +89,10 @@ void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry
layerUpdateQueue->clear();
}
-void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers,
- bool opaque, bool wideColorGamut) {
- // TODO: Handle wide color gamut
+void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
+ bool wideColorGamut) {
+ sk_sp<GrContext> cachedContext;
+
// Render all layers that need to be updated, in order.
for (size_t i = 0; i < layers.entries().size(); i++) {
RenderNode* layerNode = layers.entries()[i].renderNode.get();
@@ -101,7 +110,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers,
const Rect& layerDamage = layers.entries()[i].damage;
- SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
+ SkCanvas* layerCanvas = tryCapture(layerNode->getLayerSurface());
int saveCount = layerCanvas->save();
SkASSERT(saveCount == 1);
@@ -118,29 +127,55 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers,
return;
}
+ ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
+ bounds.height());
+
layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
layerCanvas->clear(SK_ColorTRANSPARENT);
RenderNodeDrawable root(layerNode, layerCanvas, false);
root.forceDraw(layerCanvas);
layerCanvas->restoreToCount(saveCount);
- layerCanvas->flush();
mLightCenter = savedLightCenter;
+
+ endCapture(layerNode->getLayerSurface());
+
+ // cache the current context so that we can defer flushing it until
+ // either all the layers have been rendered or the context changes
+ GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
+ if (cachedContext.get() != currentContext) {
+ if (cachedContext.get()) {
+ cachedContext->flush();
+ }
+ cachedContext.reset(SkSafeRef(currentContext));
+ }
}
}
+
+ if (cachedContext.get()) {
+ cachedContext->flush();
+ }
}
-bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator, bool wideColorGamut) {
+bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
+ bool wideColorGamut) {
+ // compute the size of the surface (i.e. texture) to be allocated for this layer
+ const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
+ const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
+
SkSurface* layer = node->getLayerSurface();
- if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
- SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
+ if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
+ SkImageInfo info;
+ if (wideColorGamut) {
+ info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType,
+ kPremul_SkAlphaType);
+ } else {
+ info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight);
+ }
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
- // TODO: Handle wide color gamut requests
- node->setLayerSurface(
- SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
- info, 0, &props));
+ node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
+ SkBudgeted::kYes, info, 0, &props));
if (node->getLayerSurface()) {
// update the transform in window of the layer to reset its origin wrt light source
// position
@@ -161,7 +196,8 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
GrContext* context = thread.getGrContext();
if (context) {
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
- auto image = bitmap->makeImage();
+ sk_sp<SkColorFilter> colorFilter;
+ auto image = bitmap->makeImage(&colorFilter);
if (image.get() && !bitmap->isHardware()) {
SkImage_pinAsTexture(image.get(), context);
SkImage_unpinAsTexture(image.get(), context);
@@ -169,92 +205,140 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
}
}
-// Encodes to PNG, unless there is already encoded data, in which case that gets
-// used.
-class PngPixelSerializer : public SkPixelSerializer {
-public:
- bool onUseEncodedData(const void*, size_t) override { return true; }
- SkData* onEncode(const SkPixmap& pixmap) override {
- SkDynamicMemoryWStream buf;
- return SkEncodeImage(&buf, pixmap, SkEncodedImageFormat::kPNG, 100)
- ? buf.detachAsData().release()
- : nullptr;
- }
-};
-
void SkiaPipeline::renderVectorDrawableCache() {
if (!mVectorDrawables.empty()) {
sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas();
auto grContext = mRenderThread.getGrContext();
atlas->prepareForDraw(grContext);
+ ATRACE_NAME("Update VectorDrawables");
for (auto vd : mVectorDrawables) {
vd->updateCache(atlas, grContext);
}
- grContext->flush();
mVectorDrawables.clear();
}
}
-void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
- const Rect &contentDrawBounds, sk_sp<SkSurface> surface) {
+class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> {
+public:
+ explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
+
+ struct SavePictureTask : public Task<bool> {
+ sk_sp<SkData> data;
+ std::string filename;
+ };
+
+ void savePicture(const sk_sp<SkData>& data, const std::string& filename) {
+ sp<SavePictureTask> task(new SavePictureTask());
+ task->data = data;
+ task->filename = filename;
+ TaskProcessor<bool>::add(task);
+ }
- renderVectorDrawableCache();
+ virtual void onProcess(const sp<Task<bool>>& task) override {
+ SavePictureTask* t = static_cast<SavePictureTask*>(task.get());
- // draw all layers up front
- renderLayersImpl(layers, opaque, wideColorGamut);
+ if (0 == access(t->filename.c_str(), F_OK)) {
+ task->setResult(false);
+ return;
+ }
- // initialize the canvas for the current frame
- SkCanvas* canvas = surface->getCanvas();
+ SkFILEWStream stream(t->filename.c_str());
+ if (stream.isValid()) {
+ stream.write(t->data->data(), t->data->size());
+ stream.flush();
+ SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(),
+ t->filename.c_str());
+ }
- std::unique_ptr<SkPictureRecorder> recorder;
- bool recordingPicture = false;
- char prop[PROPERTY_VALUE_MAX];
- if (skpCaptureEnabled()) {
- property_get("debug.hwui.capture_frame_as_skp", prop, "0");
- recordingPicture = prop[0] != '0' && access(prop, F_OK) != 0;
+ task->setResult(true);
+ }
+};
+
+SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ bool recordingPicture = mCaptureSequence > 0;
+ char prop[PROPERTY_VALUE_MAX] = {'\0'};
+ if (!recordingPicture) {
+ property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0");
+ recordingPicture = prop[0] != '0' &&
+ mCapturedFile != prop; // ensure we capture only once per filename
+ if (recordingPicture) {
+ mCapturedFile = prop;
+ mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1);
+ }
+ }
if (recordingPicture) {
- recorder.reset(new SkPictureRecorder());
- canvas = recorder->beginRecording(surface->width(), surface->height(),
- nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
+ mRecorder.reset(new SkPictureRecorder());
+ return mRecorder->beginRecording(surface->width(), surface->height(), nullptr,
+ SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
}
}
+ return surface->getCanvas();
+}
- renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
-
- if (skpCaptureEnabled() && recordingPicture) {
- sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
+void SkiaPipeline::endCapture(SkSurface* surface) {
+ if (CC_UNLIKELY(mRecorder.get())) {
+ sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
+ surface->getCanvas()->drawPicture(picture);
if (picture->approximateOpCount() > 0) {
- SkFILEWStream stream(prop);
- if (stream.isValid()) {
- PngPixelSerializer serializer;
- picture->serialize(&stream, &serializer);
- stream.flush();
- SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
+ auto data = picture->serialize();
+
+ // offload saving to file in a different thread
+ if (!mSavePictureProcessor.get()) {
+ TaskManager* taskManager = getTaskManager();
+ mSavePictureProcessor = new SavePictureProcessor(
+ taskManager->canRunTasks() ? taskManager : nullptr);
+ }
+ if (1 == mCaptureSequence) {
+ mSavePictureProcessor->savePicture(data, mCapturedFile);
+ } else {
+ mSavePictureProcessor->savePicture(
+ data,
+ mCapturedFile + "_" + std::to_string(mCaptureSequence));
}
+ mCaptureSequence--;
}
- surface->getCanvas()->drawPicture(picture);
+ mRecorder.reset();
}
+}
+
+void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque,
+ bool wideColorGamut, const Rect& contentDrawBounds,
+ sk_sp<SkSurface> surface) {
+ renderVectorDrawableCache();
+
+ // draw all layers up front
+ renderLayersImpl(layers, opaque, wideColorGamut);
+
+ // initialize the canvas for the current frame, that might be a recording canvas if SKP
+ // capture is enabled.
+ std::unique_ptr<SkPictureRecorder> recorder;
+ SkCanvas* canvas = tryCapture(surface.get());
+
+ renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
+
+ endCapture(surface.get());
if (CC_UNLIKELY(Properties::debugOverdraw)) {
renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
}
ATRACE_NAME("flush commands");
- canvas->flush();
+ surface->getCanvas()->flush();
}
namespace {
static Rect nodeBounds(RenderNode& node) {
auto& props = node.properties();
- return Rect(props.getLeft(), props.getTop(),
- props.getRight(), props.getBottom());
+ return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom());
}
}
void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
- const Rect &contentDrawBounds, SkCanvas* canvas) {
+ const std::vector<sp<RenderNode>>& nodes, bool opaque,
+ bool wideColorGamut, const Rect& contentDrawBounds,
+ SkCanvas* canvas) {
SkAutoCanvasRestore saver(canvas, true);
canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
@@ -268,15 +352,18 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect&
root.draw(canvas);
}
} else if (0 == nodes.size()) {
- //nothing to draw
+ // nothing to draw
} else {
// It there are multiple render nodes, they are laid out as follows:
// #0 - backdrop (content + caption)
// #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
// #2 - additional overlay nodes
- // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
- // resizing however it might become partially visible. The following render loop will crop the
- // backdrop against the content and draw the remaining part of it. It will then draw the content
+ // Usually the backdrop cannot be seen since it will be entirely covered by the content.
+ // While
+ // resizing however it might become partially visible. The following render loop will crop
+ // the
+ // backdrop against the content and draw the remaining part of it. It will then draw the
+ // content
// cropped to the backdrop (since that indicates a shrinking of the window).
//
// Additional nodes will be drawn on top with no particular clipping semantics.
@@ -288,29 +375,31 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect&
// Backdrop bounds in render target space
const Rect backdrop = nodeBounds(*nodes[0]);
- // Bounds that content will fill in render target space (note content node bounds may be bigger)
+ // Bounds that content will fill in render target space (note content node bounds may be
+ // bigger)
Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
content.translate(backdrop.left, backdrop.top);
if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
// Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
// Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
- // also fill left/top. Currently, both 2up and freeform position content at the top/left of
+ // also fill left/top. Currently, both 2up and freeform position content at the top/left
+ // of
// the backdrop, so this isn't necessary.
RenderNodeDrawable backdropNode(nodes[0].get(), canvas);
if (content.right < backdrop.right) {
// draw backdrop to right side of content
SkAutoCanvasRestore acr(canvas, true);
- canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top,
- backdrop.right, backdrop.bottom));
+ canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right,
+ backdrop.bottom));
backdropNode.draw(canvas);
}
if (content.bottom < backdrop.bottom) {
// draw backdrop to bottom of content
// Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
SkAutoCanvasRestore acr(canvas, true);
- canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom,
- content.right, backdrop.bottom));
+ canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right,
+ backdrop.bottom));
backdropNode.draw(canvas);
}
}
@@ -323,8 +412,9 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect&
SkAutoCanvasRestore acr(canvas, true);
canvas->translate(dx, dy);
- const SkRect contentLocalClip = SkRect::MakeXYWH(contentDrawBounds.left,
- contentDrawBounds.top, backdrop.getWidth(), backdrop.getHeight());
+ const SkRect contentLocalClip =
+ SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
+ backdrop.getWidth(), backdrop.getHeight());
canvas->clipRect(contentLocalClip);
contentNode.draw(canvas);
} else {
@@ -351,8 +441,8 @@ void SkiaPipeline::dumpResourceCacheUsage() const {
SkString log("Resource Cache Usage:\n");
log.appendf("%8d items out of %d maximum items\n", resources, maxResources);
- log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n",
- bytes, bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f)));
+ log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes,
+ bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f)));
ALOGD("%s", log.c_str());
}
@@ -365,13 +455,17 @@ void SkiaPipeline::dumpResourceCacheUsage() const {
// (2) Requires premul colors (instead of unpremul).
// (3) Requires RGBA colors (instead of BGRA).
static const uint32_t kOverdrawColors[2][6] = {
- { 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, },
- { 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, },
+ {
+ 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f,
+ },
+ {
+ 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f,
+ },
};
void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, const Rect &contentDrawBounds,
- sk_sp<SkSurface> surface) {
+ const std::vector<sp<RenderNode>>& nodes,
+ const Rect& contentDrawBounds, sk_sp<SkSurface> surface) {
// Set up the overdraw canvas.
SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 19ffc463c121..3800194440f9 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -16,10 +16,13 @@
#pragma once
-#include "renderthread/CanvasContext.h"
+#include <SkSurface.h>
#include "FrameBuilder.h"
+#include "hwui/AnimatedImageDrawable.h"
+#include "renderthread/CanvasContext.h"
#include "renderthread/IRenderPipeline.h"
-#include <SkSurface.h>
+
+class SkPictureRecorder;
namespace android {
namespace uirenderer {
@@ -28,7 +31,7 @@ namespace skiapipeline {
class SkiaPipeline : public renderthread::IRenderPipeline {
public:
SkiaPipeline(renderthread::RenderThread& thread);
- virtual ~SkiaPipeline() {}
+ virtual ~SkiaPipeline();
TaskManager* getTaskManager() override;
@@ -37,17 +40,18 @@ public:
bool pinImages(std::vector<SkImage*>& mutableImages) override;
bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; }
void unpinImages() override;
+ void onPrepareTree() override;
void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo) override;
+ LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
+ const BakedOpRenderer::LightInfo& lightInfo) override;
- bool createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator, bool wideColorGamut) override;
+ bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
+ bool wideColorGamut) override;
void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector< sp<RenderNode> >& nodes, bool opaque, bool wideColorGamut,
- const Rect &contentDrawBounds, sk_sp<SkSurface> surface);
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const Rect& contentDrawBounds, sk_sp<SkSurface> surface);
std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
@@ -55,9 +59,7 @@ public:
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
- static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
-
- static bool skpCaptureEnabled() { return false; }
+ void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
static float getLightRadius() {
if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
@@ -85,7 +87,7 @@ public:
Vector3 adjustedLightCenter = mLightCenter;
if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) {
// negated since this shifts up
- adjustedLightCenter.y = - Properties::overrideLightPosY;
+ adjustedLightCenter.y = -Properties::overrideLightPosY;
}
if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) {
adjustedLightCenter.z = Properties::overrideLightPosZ;
@@ -96,7 +98,7 @@ public:
}
static void updateLighting(const FrameBuilder::LightGeometry& lightGeometry,
- const BakedOpRenderer::LightInfo& lightInfo) {
+ const BakedOpRenderer::LightInfo& lightInfo) {
mLightRadius = lightGeometry.radius;
mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
mSpotShadowAlpha = lightInfo.spotShadowAlpha;
@@ -110,29 +112,53 @@ protected:
private:
void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector< sp<RenderNode> >& nodes, bool opaque, bool wideColorGamut,
- const Rect &contentDrawBounds, SkCanvas* canvas);
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const Rect& contentDrawBounds, SkCanvas* canvas);
/**
* Debugging feature. Draws a semi-transparent overlay on each pixel, indicating
* how many times it has been drawn.
*/
void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector< sp<RenderNode> >& nodes, const Rect &contentDrawBounds,
- sk_sp<SkSurface>);
+ const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds,
+ sk_sp<SkSurface>);
/**
* Render mVectorDrawables into offscreen buffers.
*/
void renderVectorDrawableCache();
- TaskManager mTaskManager;
+ SkCanvas* tryCapture(SkSurface* surface);
+ void endCapture(SkSurface* surface);
+
std::vector<sk_sp<SkImage>> mPinnedImages;
/**
* populated by prepareTree with dirty VDs
*/
std::vector<VectorDrawableRoot*> mVectorDrawables;
+
+ // Block of properties used only for debugging to record a SkPicture and save it in a file.
+ /**
+ * mCapturedFile is used to enforce we don't capture more than once for a given name (cause
+ * permissions don't allow to reset a property from render thread).
+ */
+ std::string mCapturedFile;
+ /**
+ * mCaptureSequence counts how many frames are left to take in the sequence.
+ */
+ int mCaptureSequence = 0;
+ /**
+ * mSavePictureProcessor is used to run the file saving code in a separate thread.
+ */
+ class SavePictureProcessor;
+ sp<SavePictureProcessor> mSavePictureProcessor;
+ /**
+ * mRecorder holds the current picture recorder. We could store it on the stack to support
+ * parallel tryCapture calls (not really needed).
+ */
+ std::unique_ptr<SkPictureRecorder> mRecorder;
+
static float mLightRadius;
static uint8_t mAmbientShadowAlpha;
static uint8_t mSpotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
index d97fb372fe0c..492c39f1288c 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
@@ -20,7 +20,7 @@ namespace android {
namespace uirenderer {
void SkiaProfileRenderer::drawRect(float left, float top, float right, float bottom,
- const SkPaint& paint) {
+ const SkPaint& paint) {
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
mCanvas->drawRect(rect, paint);
}
@@ -28,7 +28,7 @@ void SkiaProfileRenderer::drawRect(float left, float top, float right, float bot
void SkiaProfileRenderer::drawRects(const float* rects, int count, const SkPaint& paint) {
for (int index = 0; index + 4 <= count; index += 4) {
SkRect rect = SkRect::MakeLTRB(rects[index + 0], rects[index + 1], rects[index + 2],
- rects[index + 3]);
+ rects[index + 3]);
mCanvas->drawRect(rect, paint);
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
index e6b7f8307379..5ae7d6b0b607 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -23,8 +23,7 @@ namespace uirenderer {
class SkiaProfileRenderer : public IProfileRenderer {
public:
- SkiaProfileRenderer(SkCanvas* canvas)
- : mCanvas(canvas) {}
+ SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
void drawRects(const float* rects, int count, const SkPaint& paint) override;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index a0cce98c8d57..25c76eb4db3e 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -16,12 +16,12 @@
#include "SkiaRecordingCanvas.h"
+#include <SkImagePriv.h>
#include "Layer.h"
-#include "RenderNode.h"
#include "LayerDrawable.h"
#include "NinePatchUtils.h"
+#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
-#include <SkImagePriv.h>
namespace android {
namespace uirenderer {
@@ -32,7 +32,7 @@ namespace skiapipeline {
// ----------------------------------------------------------------------------
void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
- int height) {
+ int height) {
mCurrentBarrier = nullptr;
SkASSERT(mDisplayList.get() == nullptr);
@@ -59,17 +59,21 @@ uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
// ----------------------------------------------------------------------------
void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
- uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
- uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
- uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
+ uirenderer::CanvasPropertyPrimitive* top,
+ uirenderer::CanvasPropertyPrimitive* right,
+ uirenderer::CanvasPropertyPrimitive* bottom,
+ uirenderer::CanvasPropertyPrimitive* rx,
+ uirenderer::CanvasPropertyPrimitive* ry,
+ uirenderer::CanvasPropertyPaint* paint) {
// Destructor of drawables created with allocateDrawable, will be invoked by ~LinearAllocator.
- drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom,
- rx, ry, paint));
+ drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom, rx, ry,
+ paint));
}
void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
- uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
- uirenderer::CanvasPropertyPaint* paint) {
+ uirenderer::CanvasPropertyPrimitive* y,
+ uirenderer::CanvasPropertyPrimitive* radius,
+ uirenderer::CanvasPropertyPaint* paint) {
drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
}
@@ -77,24 +81,22 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
if (nullptr != mCurrentBarrier) {
// finish off the existing chunk
SkDrawable* drawable =
- mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(
- mCurrentBarrier);
+ mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(mCurrentBarrier);
mCurrentBarrier = nullptr;
drawDrawable(drawable);
}
if (enableReorder) {
mCurrentBarrier = (StartReorderBarrierDrawable*)
- mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
- mDisplayList.get());
+ mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+ mDisplayList.get());
drawDrawable(mCurrentBarrier);
}
}
void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
- if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) {
- uirenderer::Layer* layer = layerUpdater->backingLayer();
+ if (layerUpdater != nullptr) {
// Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL.
- sk_sp<SkDrawable> drawable(new LayerDrawable(layer));
+ sk_sp<SkDrawable> drawable(new LayerDrawable(layerUpdater));
drawDrawable(drawable.get());
}
}
@@ -114,26 +116,27 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
}
void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) {
+ uirenderer::GlFunctorLifecycleListener* listener) {
// Drawable dtor will be invoked when mChildFunctors deque is cleared.
mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
drawDrawable(&mDisplayList->mChildFunctors.back());
}
class VectorDrawable : public SkDrawable {
- public:
- VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
-
- protected:
- virtual SkRect onGetBounds() override {
- return SkRect::MakeLargest();
- }
- virtual void onDraw(SkCanvas* canvas) override {
- mRoot->draw(canvas);
- }
-
- private:
+public:
+ VectorDrawable(VectorDrawableRoot* tree)
+ : mRoot(tree)
+ , mBounds(tree->stagingProperties()->getBounds()) {}
+
+protected:
+ virtual SkRect onGetBounds() override { return mBounds; }
+ virtual void onDraw(SkCanvas* canvas) override {
+ mRoot->draw(canvas, mBounds);
+ }
+
+private:
sp<VectorDrawableRoot> mRoot;
+ SkRect mBounds;
};
void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
@@ -145,9 +148,25 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
// Recording Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) {
- if (origPaint && origPaint->isAntiAlias()) {
- *tmpPaint = *origPaint;
+inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
+ sk_sp<SkColorFilter> colorSpaceFilter) {
+ if ((origPaint && origPaint->isAntiAlias()) || colorSpaceFilter) {
+ if (origPaint) {
+ *tmpPaint = *origPaint;
+ }
+
+ if (colorSpaceFilter) {
+ if (tmpPaint->getColorFilter()) {
+ tmpPaint->setColorFilter(
+ SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
+ } else {
+ tmpPaint->setColorFilter(colorSpaceFilter);
+ }
+ LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
+ }
+
+
+ // disabling AA on bitmap draws matches legacy HWUI behavior
tmpPaint->setAntiAlias(false);
return tmpPaint;
} else {
@@ -156,9 +175,10 @@ inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPa
}
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- sk_sp<SkImage> image = bitmap.makeImage();
SkPaint tmpPaint;
- mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint));
+ sk_sp<SkColorFilter> colorFilter;
+ sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ mRecorder.drawImage(image, left, top, bitmapPaint(paint, &tmpPaint, colorFilter));
// if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
// it is not safe to store a raw SkImage pointer, because the image object will be destroyed
// when this function ends.
@@ -167,38 +187,44 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, cons
}
}
-void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix,
- const SkPaint* paint) {
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
SkAutoCanvasRestore acr(&mRecorder, true);
concat(matrix);
- sk_sp<SkImage> image = hwuiBitmap.makeImage();
+
SkPaint tmpPaint;
- mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint));
- if (!hwuiBitmap.isImmutable() && image.get() && !image->unique()) {
+ sk_sp<SkColorFilter> colorFilter;
+ sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ mRecorder.drawImage(image, 0, 0, bitmapPaint(paint, &tmpPaint, colorFilter));
+ if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
}
}
-void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
- float dstBottom, const SkPaint* paint) {
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
+ float srcBottom, float dstLeft, float dstTop, float dstRight,
+ float dstBottom, const SkPaint* paint) {
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkImage> image = hwuiBitmap.makeImage();
+
SkPaint tmpPaint;
- mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint));
- if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty()
- && !dstRect.isEmpty()) {
+ sk_sp<SkColorFilter> colorFilter;
+ sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ mRecorder.drawImageRect(image, srcRect, dstRect, bitmapPaint(paint, &tmpPaint, colorFilter),
+ SkCanvas::kFast_SrcRectConstraint);
+ if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
+ !dstRect.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
}
}
-void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
- float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
+void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
+ float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) {
SkCanvas::Lattice lattice;
- NinePatchUtils::SetLatticeDivs(&lattice, chunk, hwuiBitmap.width(), hwuiBitmap.height());
+ NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
- lattice.fFlags = nullptr;
+ lattice.fRectTypes = nullptr;
+ lattice.fColors = nullptr;
int numFlags = 0;
if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
// We can expect the framework to give us a color for every distinct rect.
@@ -206,22 +232,31 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch
numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
}
- SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
+ SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
+ SkAutoSTMalloc<25, SkColor> colors(numFlags);
if (numFlags > 0) {
- NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
+ NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
}
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkImage> image = hwuiBitmap.makeImage();
SkPaint tmpPaint;
- mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint));
- if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
+ sk_sp<SkColorFilter> colorFilter;
+ sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ mRecorder.drawImageLattice(image.get(), lattice, dst,
+ bitmapPaint(paint, &tmpPaint, colorFilter));
+ if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
}
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedImage) {
+ drawDrawable(animatedImage);
+ mDisplayList->mAnimatedImages.push_back(animatedImage);
+ return 0;
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 10829f87efb9..0e5dbdbab078 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -15,10 +15,10 @@
*/
#pragma once
+#include <SkLiteRecorder.h>
+#include "ReorderBarrierDrawables.h"
#include "SkiaCanvas.h"
#include "SkiaDisplayList.h"
-#include "ReorderBarrierDrawables.h"
-#include <SkLiteRecorder.h>
namespace android {
namespace uirenderer {
@@ -29,7 +29,7 @@ namespace skiapipeline {
* SkLiteRecorder and a SkiaDisplayList.
*/
class SkiaRecordingCanvas : public SkiaCanvas {
- public:
+public:
explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) {
initDisplayList(renderNode, width, height);
}
@@ -39,31 +39,33 @@ class SkiaRecordingCanvas : public SkiaCanvas {
}
virtual void resetRecording(int width, int height,
- uirenderer::RenderNode* renderNode) override {
+ uirenderer::RenderNode* renderNode) override {
initDisplayList(renderNode, width, height);
}
virtual uirenderer::DisplayList* finishRecording() override;
- virtual void drawBitmap(Bitmap& bitmap, float left, float top,
- const SkPaint* paint) override;
- virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
- const SkPaint* paint) override;
- virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
+ float srcBottom, float dstLeft, float dstTop, float dstRight,
+ float dstBottom, const SkPaint* paint) override;
virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk,
- float dstLeft, float dstTop, float dstRight, float dstBottom,
- const SkPaint* paint) override;
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) override;
+ virtual double drawAnimatedImage(AnimatedImageDrawable* animatedImage) override;
virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
- uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
- uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
- uirenderer::CanvasPropertyPrimitive* ry,
- uirenderer::CanvasPropertyPaint* paint) override;
+ uirenderer::CanvasPropertyPrimitive* top,
+ uirenderer::CanvasPropertyPrimitive* right,
+ uirenderer::CanvasPropertyPrimitive* bottom,
+ uirenderer::CanvasPropertyPrimitive* rx,
+ uirenderer::CanvasPropertyPrimitive* ry,
+ uirenderer::CanvasPropertyPaint* paint) override;
virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x,
- uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
- uirenderer::CanvasPropertyPaint* paint) override;
+ uirenderer::CanvasPropertyPrimitive* y,
+ uirenderer::CanvasPropertyPrimitive* radius,
+ uirenderer::CanvasPropertyPaint* paint) override;
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
@@ -88,6 +90,6 @@ private:
void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index e1ef71f7d3ab..653007482c0a 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -17,12 +17,12 @@
#include "SkiaVulkanPipeline.h"
#include "DeferredLayerUpdater.h"
-#include "renderthread/Frame.h"
#include "Readback.h"
-#include "renderstate/RenderState.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
#include "VkLayer.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/Frame.h"
#include <SkSurface.h>
#include <SkTypes.h>
@@ -41,8 +41,7 @@ namespace uirenderer {
namespace skiapipeline {
SkiaVulkanPipeline::SkiaVulkanPipeline(renderthread::RenderThread& thread)
- : SkiaPipeline(thread)
- , mVkManager(thread.vulkanManager()) {}
+ : SkiaPipeline(thread), mVkManager(thread.vulkanManager()) {}
MakeCurrentResult SkiaVulkanPipeline::makeCurrent() {
return MakeCurrentResult::AlreadyCurrent;
@@ -50,7 +49,7 @@ MakeCurrentResult SkiaVulkanPipeline::makeCurrent() {
Frame SkiaVulkanPipeline::getFrame() {
LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
- "drawRenderNode called on a context with no surface!");
+ "drawRenderNode called on a context with no surface!");
SkSurface* backBuffer = mVkManager.getBackbufferSurface(mVkSurface);
if (backBuffer == nullptr) {
@@ -62,27 +61,25 @@ Frame SkiaVulkanPipeline::getFrame() {
return frame;
}
-bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
- const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
- const std::vector<sp<RenderNode>>& renderNodes,
- FrameInfoVisualizer* profiler) {
-
+bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
+ bool opaque, bool wideColorGamut,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode>>& renderNodes,
+ FrameInfoVisualizer* profiler) {
sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface();
if (backBuffer.get() == nullptr) {
return false;
}
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut,
- contentDrawBounds, backBuffer);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds,
+ backBuffer);
layerUpdateQueue->clear();
// Draw visual debugging features
- if (CC_UNLIKELY(Properties::showDirtyRegions
- || ProfileType::None != Properties::getProfileType())) {
+ if (CC_UNLIKELY(Properties::showDirtyRegions ||
+ ProfileType::None != Properties::getProfileType())) {
SkCanvas* profileCanvas = backBuffer->getCanvas();
SkiaProfileRenderer profileRenderer(profileCanvas);
profiler->draw(profileRenderer);
@@ -97,9 +94,8 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
return true;
}
-bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew,
- const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
-
+bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) {
*requireSwap = drew;
// Even if we decided to cancel the frame, from the perspective of jank
@@ -119,7 +115,7 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi
}
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+ SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
}
@@ -129,11 +125,10 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan);
}
-void SkiaVulkanPipeline::onStop() {
-}
+void SkiaVulkanPipeline::onStop() {}
bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
- ColorMode colorMode) {
+ ColorMode colorMode) {
if (mVkSurface) {
mVkManager.destroySurface(mVkSurface);
mVkSurface = nullptr;
@@ -162,16 +157,15 @@ void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* func
}
sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
- SkBitmap& skBitmap) {
- //TODO: implement this function for Vulkan pipeline
- //code below is a hack to avoid crashing because of missing HW Bitmap support
- sp<GraphicBuffer> buffer = new GraphicBuffer(skBitmap.info().width(), skBitmap.info().height(),
- PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_HW_TEXTURE |
- GraphicBuffer::USAGE_SW_WRITE_NEVER |
- GraphicBuffer::USAGE_SW_READ_NEVER,
- std::string("SkiaVulkanPipeline::allocateHardwareBitmap pid [")
- + std::to_string(getpid()) + "]");
+ SkBitmap& skBitmap) {
+ // TODO: implement this function for Vulkan pipeline
+ // code below is a hack to avoid crashing because of missing HW Bitmap support
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ skBitmap.info().width(), skBitmap.info().height(), PIXEL_FORMAT_RGBA_8888,
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER,
+ std::string("SkiaVulkanPipeline::allocateHardwareBitmap pid [") +
+ std::to_string(getpid()) + "]");
status_t error = buffer->initCheck();
if (error < 0) {
ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()");
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 263206d97571..03b4c79f2beb 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -31,25 +31,24 @@ public:
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
- const std::vector< sp<RenderNode> >& renderNodes,
- FrameInfoVisualizer* profiler) override;
+ const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ FrameInfo* currentFrameInfo, bool* requireSwap) override;
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
- renderthread::ColorMode colorMode) override;
+ renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
- SkBitmap& skBitmap);
+ SkBitmap& skBitmap);
private:
renderthread::VulkanManager& mVkManager;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
new file mode 100644
index 000000000000..65b89d617f7b
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Readback.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaVulkanReadback : public Readback {
+public:
+ SkiaVulkanReadback(renderthread::RenderThread& thread) : Readback(thread) {}
+
+ virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
+ SkBitmap* bitmap) override {
+ //TODO: implement Vulkan readback.
+ return CopyResult::UnknownError;
+ }
+
+ virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
+ SkBitmap* bitmap) override {
+ //TODO: implement Vulkan readback.
+ return CopyResult::UnknownError;
+ }
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
index 437653a8dfa8..8fb621d24866 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
@@ -19,8 +19,9 @@
#include <GrRectanizer_pow2.h>
#include <SkCanvas.h>
#include <cmath>
-#include "utils/TraceUtils.h"
#include "renderthread/RenderProxy.h"
+#include "renderthread/RenderThread.h"
+#include "utils/TraceUtils.h"
namespace android {
namespace uirenderer {
@@ -29,8 +30,7 @@ namespace skiapipeline {
VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
: mWidth((int)std::sqrt(surfaceArea))
, mHeight((int)std::sqrt(surfaceArea))
- , mStorageMode(storageMode) {
-}
+ , mStorageMode(storageMode) {}
void VectorDrawableAtlas::prepareForDraw(GrContext* context) {
if (StorageMode::allowSharedSurface == mStorageMode) {
@@ -54,8 +54,8 @@ void VectorDrawableAtlas::prepareForDraw(GrContext* context) {
#define MAX_UNUSED_RATIO 2.0f
bool VectorDrawableAtlas::isFragmented() {
- return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES
- && mPixelUsedByVDs*MAX_UNUSED_RATIO < mPixelAllocated;
+ return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES &&
+ mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated;
}
void VectorDrawableAtlas::repackIfNeeded(GrContext* context) {
@@ -68,9 +68,9 @@ void VectorDrawableAtlas::repackIfNeeded(GrContext* context) {
}
// compare to CacheEntry objects based on VD area.
-bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second)
-{
- return first.VDrect.width()*first.VDrect.height() < second.VDrect.width()*second.VDrect.height();
+bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) {
+ return first.VDrect.width() * first.VDrect.height() <
+ second.VDrect.width() * second.VDrect.height();
}
void VectorDrawableAtlas::repack(GrContext* context) {
@@ -87,7 +87,7 @@ void VectorDrawableAtlas::repack(GrContext* context) {
mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
} else {
if (!mSurface) {
- return; //nothing to repack
+ return; // nothing to repack
}
mRectanizer.reset();
}
@@ -105,20 +105,20 @@ void VectorDrawableAtlas::repack(GrContext* context) {
for (CacheEntry& entry : mRects) {
SkRect currentVDRect = entry.VDrect;
- SkImage* sourceImage; //copy either from the atlas or from a standalone surface
+ SkImage* sourceImage; // copy either from the atlas or from a standalone surface
if (entry.surface) {
if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) {
- continue; //don't even try to repack huge VD
+ continue; // don't even try to repack huge VD
}
sourceImage = entry.surface->makeImageSnapshot().get();
} else {
sourceImage = sourceImageAtlas;
}
- size_t VDRectArea = currentVDRect.width()*currentVDRect.height();
+ size_t VDRectArea = currentVDRect.width() * currentVDRect.height();
SkIPoint16 pos;
if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) {
- SkRect newRect = SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(),
- currentVDRect.height());
+ SkRect newRect =
+ SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height());
canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr);
entry.VDrect = newRect;
entry.rect = newRect;
@@ -133,8 +133,7 @@ void VectorDrawableAtlas::repack(GrContext* context) {
if (!entry.surface) {
// A rectangle moved from an atlas to a standalone surface.
mPixelUsedByVDs -= VDRectArea;
- SkRect newRect = SkRect::MakeWH(currentVDRect.width(),
- currentVDRect.height());
+ SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height());
entry.surface = createSurface(newRect.width(), newRect.height(), context);
auto tempCanvas = entry.surface->getCanvas();
tempCanvas->clear(SK_ColorTRANSPARENT);
@@ -157,7 +156,7 @@ AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext
}
if (mSurface) {
- const size_t area = width*height;
+ const size_t area = width * height;
// Use a rectanizer to allocate unused space from the atlas surface.
bool notTooBig = fitInAtlas(width, height);
@@ -228,15 +227,24 @@ AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) {
void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
if (INVALID_ATLAS_KEY != atlasKey) {
+ if (!renderthread::RenderThread::isCurrent()) {
+ {
+ AutoMutex _lock(mReleaseKeyLock);
+ mKeysForRelease.push_back(atlasKey);
+ }
+ // invoke releaseEntry on the renderthread
+ renderthread::RenderProxy::releaseVDAtlasEntries();
+ return;
+ }
CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
if (!entry->surface) {
// Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas
// is full.
SkRect& removedRect = entry->rect;
- size_t rectArea = removedRect.width()*removedRect.height();
+ size_t rectArea = removedRect.width() * removedRect.height();
mFreeRects.emplace(rectArea, removedRect);
SkRect& removedVDRect = entry->VDrect;
- size_t VDRectArea = removedVDRect.width()*removedVDRect.height();
+ size_t VDRectArea = removedVDRect.width() * removedVDRect.height();
mPixelUsedByVDs -= VDRectArea;
mConsecutiveFailures = 0;
}
@@ -245,6 +253,14 @@ void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
}
}
+void VectorDrawableAtlas::delayedReleaseEntries() {
+ AutoMutex _lock(mReleaseKeyLock);
+ for (auto key : mKeysForRelease) {
+ releaseEntry(key);
+ }
+ mKeysForRelease.clear();
+}
+
sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
#ifndef ANDROID_ENABLE_LINEAR_BLENDING
sk_sp<SkColorSpace> colorSpace = nullptr;
@@ -252,7 +268,10 @@ sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrCon
sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
#endif
SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
- return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
+ // This must have a top-left origin so that calls to surface->canvas->writePixels
+ // performs a basic texture upload instead of a more complex drawing operation
+ return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin,
+ nullptr);
}
void VectorDrawableAtlas::setStorageMode(StorageMode mode) {
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
index 496c55742748..74e48cea2a87 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
@@ -16,11 +16,12 @@
#pragma once
-#include <map>
#include <SkSurface.h>
#include <utils/FatVector.h>
#include <utils/RefBase.h>
+#include <utils/Thread.h>
#include <list>
+#include <map>
class GrRectanizer;
@@ -54,18 +55,15 @@ struct AtlasEntry {
* When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping
* track of free spaces and allow to reuse the surface for another VD.
*/
- //TODO: Check if not using atlas for AnimatedVD is more efficient.
- //TODO: For low memory situations, when there are no paint effects in VD, we may render without an
- //TODO: offscreen surface.
+// TODO: Check if not using atlas for AnimatedVD is more efficient.
+// TODO: For low memory situations, when there are no paint effects in VD, we may render without an
+// TODO: offscreen surface.
class VectorDrawableAtlas : public virtual RefBase {
public:
- enum class StorageMode {
- allowSharedSurface,
- disallowSharedSurface
- };
+ enum class StorageMode { allowSharedSurface, disallowSharedSurface };
VectorDrawableAtlas(size_t surfaceArea,
- StorageMode storageMode = StorageMode::allowSharedSurface);
+ StorageMode storageMode = StorageMode::allowSharedSurface);
/**
* "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
@@ -103,19 +101,24 @@ public:
/**
* "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey"
- * is causing an undefined behaviour.
+ * is causing an undefined behaviour. This is the only function in the class that can be
+ * invoked from any thread. It will marshal internally to render thread if needed.
*/
void releaseEntry(AtlasKey atlasKey);
void setStorageMode(StorageMode mode);
+ /**
+ * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is
+ * invoked from a non render thread.
+ */
+ void delayedReleaseEntries();
+
private:
struct CacheEntry {
CacheEntry(const SkRect& newVDrect, const SkRect& newRect,
- const sk_sp<SkSurface>& newSurface)
- : VDrect(newVDrect)
- , rect(newRect)
- , surface(newSurface) { }
+ const sk_sp<SkSurface>& newSurface)
+ : VDrect(newVDrect), rect(newRect), surface(newSurface) {}
/**
* size and position of VectorDrawable into the atlas or in "this.surface"
@@ -182,10 +185,21 @@ private:
*/
StorageMode mStorageMode;
+ /**
+ * mKeysForRelease is used by releaseEntry implementation to pass atlas keys from an arbitrary
+ * calling thread to the render thread.
+ */
+ std::vector<AtlasKey> mKeysForRelease;
+
+ /**
+ * A lock used to protect access to mKeysForRelease.
+ */
+ Mutex mReleaseKeyLock;
+
sk_sp<SkSurface> createSurface(int width, int height, GrContext* context);
inline bool fitInAtlas(int width, int height) {
- return 2*width < mWidth && 2*height < mHeight;
+ return 2 * width < mWidth && 2 * height < mHeight;
}
void repack(GrContext* context);