summaryrefslogtreecommitdiff
path: root/libs/hwui/pipeline/skia/SkiaPipeline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/pipeline/skia/SkiaPipeline.cpp')
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp268
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);