Loading libs/hwui/Properties.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ bool Properties::forceDrawFrame = false; bool Properties::filterOutTestOverhead = false; bool Properties::disableVsync = false; bool Properties::skpCaptureEnabled = false; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = {'\0',}; Loading Loading @@ -128,6 +129,9 @@ bool Properties::load() { filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false); skpCaptureEnabled = property_get_bool("ro.debuggable", false) && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) || (prevDebugStencilClip != debugStencilClip); Loading libs/hwui/Properties.h +18 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,22 @@ enum DebugLevel { */ #define PROPERTY_RENDERER "debug.hwui.renderer" /** * Allows to collect a recording of Skia drawing commands. */ #define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled" /** * Defines how many frames in a sequence to capture. */ #define PROPERTY_CAPTURE_SKP_FRAMES "debug.hwui.capture_skp_frames" /** * File name and location, where a SKP recording will be saved. */ #define PROPERTY_CAPTURE_SKP_FILENAME "debug.hwui.skp_filename" /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -254,6 +270,8 @@ public: // created after changing this. static bool disableVsync; static bool skpCaptureEnabled; // Used for testing only to change the render pipeline. #ifdef HWUI_GLES_WRAP_ENABLED static void overrideRenderPipelineType(RenderPipelineType); Loading libs/hwui/pipeline/skia/LayerDrawable.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,10 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { } bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) { 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); Loading libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -101,7 +101,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); } Loading libs/hwui/pipeline/skia/SkiaPipeline.cpp +94 −30 Original line number Diff line number Diff line Loading @@ -112,7 +112,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); Loading @@ -139,9 +139,11 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, layerCanvas->restoreToCount(saveCount); 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 = layerCanvas->getGrContext(); GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext(); if (cachedContext.get() != currentContext) { if (cachedContext.get()) { cachedContext->flush(); Loading Loading @@ -227,53 +229,115 @@ void SkiaPipeline::renderVectorDrawableCache() { } } 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) {} renderVectorDrawableCache(); struct SavePictureTask : public Task<bool> { sk_sp<SkData> data; std::string filename; }; // draw all layers up front renderLayersImpl(layers, opaque, wideColorGamut); 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); } // initialize the canvas for the current frame SkCanvas* canvas = surface->getCanvas(); virtual void onProcess(const sp<Task<bool> >& task) override { SavePictureTask* t = static_cast<SavePictureTask*>(task.get()); 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; if (recordingPicture) { recorder.reset(new SkPictureRecorder()); canvas = recorder->beginRecording(surface->width(), surface->height(), nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); if (0 == access(t->filename.c_str(), F_OK)) { task->setResult(false); return; } 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()); } renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas); task->setResult(true); } }; if (skpCaptureEnabled() && recordingPicture) { sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture(); 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) { mRecorder.reset(new SkPictureRecorder()); return mRecorder->beginRecording(surface->width(), surface->height(), nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); } } return surface->getCanvas(); } 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()) { SkDynamicMemoryWStream stream; PngPixelSerializer serializer; picture->serialize(&stream, &serializer); stream.flush(); SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop); //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(stream.detachAsData(), mCapturedFile); } else { mSavePictureProcessor->savePicture(stream.detachAsData(), mCapturedFile + "_" + std::to_string(mCaptureSequence)); } surface->getCanvas()->drawPicture(picture); mCaptureSequence--; } 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 { Loading Loading
libs/hwui/Properties.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ bool Properties::forceDrawFrame = false; bool Properties::filterOutTestOverhead = false; bool Properties::disableVsync = false; bool Properties::skpCaptureEnabled = false; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = {'\0',}; Loading Loading @@ -128,6 +129,9 @@ bool Properties::load() { filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false); skpCaptureEnabled = property_get_bool("ro.debuggable", false) && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) || (prevDebugStencilClip != debugStencilClip); Loading
libs/hwui/Properties.h +18 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,22 @@ enum DebugLevel { */ #define PROPERTY_RENDERER "debug.hwui.renderer" /** * Allows to collect a recording of Skia drawing commands. */ #define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled" /** * Defines how many frames in a sequence to capture. */ #define PROPERTY_CAPTURE_SKP_FRAMES "debug.hwui.capture_skp_frames" /** * File name and location, where a SKP recording will be saved. */ #define PROPERTY_CAPTURE_SKP_FILENAME "debug.hwui.skp_filename" /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -254,6 +270,8 @@ public: // created after changing this. static bool disableVsync; static bool skpCaptureEnabled; // Used for testing only to change the render pipeline. #ifdef HWUI_GLES_WRAP_ENABLED static void overrideRenderPipelineType(RenderPipelineType); Loading
libs/hwui/pipeline/skia/LayerDrawable.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,10 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { } bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) { 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); Loading
libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -101,7 +101,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); } Loading
libs/hwui/pipeline/skia/SkiaPipeline.cpp +94 −30 Original line number Diff line number Diff line Loading @@ -112,7 +112,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); Loading @@ -139,9 +139,11 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, layerCanvas->restoreToCount(saveCount); 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 = layerCanvas->getGrContext(); GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext(); if (cachedContext.get() != currentContext) { if (cachedContext.get()) { cachedContext->flush(); Loading Loading @@ -227,53 +229,115 @@ void SkiaPipeline::renderVectorDrawableCache() { } } 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) {} renderVectorDrawableCache(); struct SavePictureTask : public Task<bool> { sk_sp<SkData> data; std::string filename; }; // draw all layers up front renderLayersImpl(layers, opaque, wideColorGamut); 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); } // initialize the canvas for the current frame SkCanvas* canvas = surface->getCanvas(); virtual void onProcess(const sp<Task<bool> >& task) override { SavePictureTask* t = static_cast<SavePictureTask*>(task.get()); 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; if (recordingPicture) { recorder.reset(new SkPictureRecorder()); canvas = recorder->beginRecording(surface->width(), surface->height(), nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); if (0 == access(t->filename.c_str(), F_OK)) { task->setResult(false); return; } 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()); } renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas); task->setResult(true); } }; if (skpCaptureEnabled() && recordingPicture) { sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture(); 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) { mRecorder.reset(new SkPictureRecorder()); return mRecorder->beginRecording(surface->width(), surface->height(), nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); } } return surface->getCanvas(); } 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()) { SkDynamicMemoryWStream stream; PngPixelSerializer serializer; picture->serialize(&stream, &serializer); stream.flush(); SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop); //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(stream.detachAsData(), mCapturedFile); } else { mSavePictureProcessor->savePicture(stream.detachAsData(), mCapturedFile + "_" + std::to_string(mCaptureSequence)); } surface->getCanvas()->drawPicture(picture); mCaptureSequence--; } 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 { Loading