diff options
Diffstat (limited to 'libs/hwui/pipeline')
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); |