From f58cc92066903b900396f640159ea3ea992fc67d Mon Sep 17 00:00:00 2001 From: Matt Sarett Date: Mon, 14 Nov 2016 18:33:38 -0500 Subject: [PATCH] Add overdraw debugging feature to Skia pipelines Test: Compared to OpenGL pipeline and sanity checked with understanding of the drawing pipeline. Also wrote a unit test. BUG:32370375 Change-Id: Iab397d21f0def725fa89551d48c764c67fd2bda8 --- libs/hwui/RenderNode.cpp | 5 +- .../hwui/pipeline/skia/RenderNodeDrawable.cpp | 19 +++-- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 81 +++++++++++++++---- libs/hwui/pipeline/skia/SkiaPipeline.h | 12 +++ libs/hwui/tests/unit/SkiaPipelineTests.cpp | 50 ++++++++++++ 5 files changed, 143 insertions(+), 24 deletions(-) diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index a03ded6435213..a5443d9ff6f17 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -188,8 +188,9 @@ void RenderNode::prepareTree(TreeInfo& info) { ATRACE_CALL(); LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing"); - // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer. - bool functorsNeedLayer = Properties::debugOverdraw; + // The OpenGL renderer reserves the stencil buffer for overdraw debugging. Functors + // will need to be drawn in a layer. + bool functorsNeedLayer = Properties::debugOverdraw && !Properties::isSkiaEnabled(); prepareTreeImpl(info, functorsNeedLayer); } diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 7dcbbd059e888..adb7e266e63c5 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -165,13 +165,22 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { } renderNode->getLayerSurface()->draw(canvas, 0, 0, paint); - if (CC_UNLIKELY(Properties::debugLayersUpdates - && !renderNode->getSkiaLayer()->hasRenderedSinceRepaint)) { + if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true; - SkPaint layerPaint; - layerPaint.setColor(0x7f00ff00); - canvas->drawRect(bounds, layerPaint); + if (CC_UNLIKELY(Properties::debugLayersUpdates)) { + SkPaint layerPaint; + layerPaint.setColor(0x7f00ff00); + canvas->drawRect(bounds, layerPaint); + } else if (CC_UNLIKELY(Properties::debugOverdraw)) { + // Render transparent rect to increment overdraw for repaint area. + // This can be "else if" because flashing green on layer updates + // will also increment the overdraw if it happens to be turned on. + SkPaint transparentPaint; + transparentPaint.setColor(SK_ColorTRANSPARENT); + canvas->drawRect(bounds, transparentPaint); + } } + // composing a software layer with alpha } else if (properties.effectiveLayerType() == LayerType::Software) { SkPaint paint; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index c5a40d46dbd0c..0f2d09d69bd7d 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -18,6 +18,8 @@ #include "utils/TraceUtils.h" #include +#include +#include #include #include #include @@ -192,6 +194,34 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli } } + renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas); + + if (skpCaptureEnabled() && recordingPicture) { + sk_sp picture = recorder->finishRecordingAsPicture(); + 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); + } + } + surface->getCanvas()->drawPicture(picture); + } + + if (CC_UNLIKELY(Properties::debugOverdraw)) { + renderOverdraw(layers, clip, nodes, contentDrawBounds, surface); + } + + ATRACE_NAME("flush commands"); + canvas->flush(); +} + +void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector>& nodes, bool opaque, const Rect &contentDrawBounds, + SkCanvas* canvas) { + canvas->clipRect(clip, SkRegion::kReplace_Op); if (!opaque) { @@ -250,23 +280,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli canvas->restoreToCount(count); layer++; } - - if (skpCaptureEnabled() && recordingPicture) { - sk_sp picture = recorder->finishRecordingAsPicture(); - 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); - } - } - surface->getCanvas()->drawPicture(picture); - } - - ATRACE_NAME("flush commands"); - canvas->flush(); } void SkiaPipeline::dumpResourceCacheUsage() const { @@ -283,6 +296,40 @@ void SkiaPipeline::dumpResourceCacheUsage() const { ALOGD("%s", log.c_str()); } +// Overdraw debugging + +// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences. +// This implementation: +// (1) Requires transparent entries for "no overdraw" and "single draws". +// (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, }, +}; + +void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector>& nodes, const Rect &contentDrawBounds, + sk_sp surface) { + // Set up the overdraw canvas. + SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); + sk_sp offscreen = surface->makeSurface(offscreenInfo); + SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas()); + + // Fake a redraw to replay the draw commands. This will increment the alpha channel + // each time a pixel would have been drawn. + // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero + // initialized. + renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas); + sk_sp counts = offscreen->makeImageSnapshot(); + + // Draw overdraw colors to the canvas. The color filter will convert counts to colors. + SkPaint paint; + const SkPMColor* colors = kOverdrawColors[static_cast(Properties::overdrawColorSet)]; + paint.setColorFilter(SkOverdrawColorFilter::Make(colors)); + surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint); +} + } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index c1c8cbef4d70d..c58fedf834ff7 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -107,6 +107,18 @@ protected: renderthread::RenderThread& mRenderThread; private: + void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector< sp >& nodes, bool opaque, 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 >& nodes, const Rect &contentDrawBounds, + sk_sp); + TaskManager mTaskManager; std::vector> mPinnedImages; static float mLightRadius; diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 7a2fa5792f677..17801aff40eaf 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -179,3 +179,53 @@ RENDERTHREAD_TEST(SkiaPipeline, renderLayer) { redNode->setLayerSurface(sk_sp()); blueNode->setLayerSurface(sk_sp()); } + +RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) { + ScopedProperty prop(Properties::debugOverdraw, true); + + auto whiteNode = TestUtils::createSkiaNode(0, 0, 1, 1, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRect::MakeXYWH(0, 0, 1, 1); + std::vector> renderNodes; + renderNodes.push_back(whiteNode); + bool opaque = true; + android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); + auto pipeline = std::make_unique(renderThread); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); + + // Initialize the canvas to blue. + surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); + + // Single draw, should be white. + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); + + // 1 Overdraw, should be blue blended onto white. + renderNodes.push_back(whiteNode); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0d0ff); + + // 2 Overdraw, should be green blended onto white + renderNodes.push_back(whiteNode); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0ffd0); + + // 3 Overdraw, should be pink blended onto white. + renderNodes.push_back(whiteNode); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffffc0c0); + + // 4 Overdraw, should be red blended onto white. + renderNodes.push_back(whiteNode); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080); + + // 5 Overdraw, should be red blended onto white. + renderNodes.push_back(whiteNode); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080); +}