diff options
Diffstat (limited to 'libs/hwui/pipeline/skia/SkiaPipeline.cpp')
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.cpp | 268 |
1 files changed, 181 insertions, 87 deletions
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); |