From 0057db22febd44f16e83fdc521782952055d09ad Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Thu, 29 Mar 2018 14:18:44 -0400 Subject: [PATCH] Print detailed memory usage of Skia for dumpsys gfxinfo Bug: 74435803 Test: adb shell dumpsys gfxinfo [package_name] Change-Id: I1f2bcab500fb47c5e0b50c7459d4a876b063916b --- libs/hwui/Android.bp | 1 + libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp | 175 +++++++++++++++++++++++++++ libs/hwui/pipeline/skia/SkiaMemoryTracer.h | 88 ++++++++++++++ libs/hwui/renderthread/CacheManager.cpp | 35 ++++-- 4 files changed, 290 insertions(+), 9 deletions(-) create mode 100644 libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp create mode 100644 libs/hwui/pipeline/skia/SkiaMemoryTracer.h diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 35790b6558c7..592a6e64a58b 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -168,6 +168,7 @@ cc_defaults { "pipeline/skia/ReorderBarrierDrawables.cpp", "pipeline/skia/ShaderCache.cpp", "pipeline/skia/SkiaDisplayList.cpp", + "pipeline/skia/SkiaMemoryTracer.cpp", "pipeline/skia/SkiaOpenGLPipeline.cpp", "pipeline/skia/SkiaOpenGLReadback.cpp", "pipeline/skia/SkiaPipeline.cpp", diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp new file mode 100644 index 000000000000..ee996224fee6 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp @@ -0,0 +1,175 @@ +/* + * 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. + */ + +#include "SkiaMemoryTracer.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +SkiaMemoryTracer::SkiaMemoryTracer(std::vector resourceMap, bool itemizeType) + : mResourceMap(resourceMap) + , mItemizeType(itemizeType) + , mTotalSize("bytes", 0) + , mPurgeableSize("bytes", 0) {} + +SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType) + : mCategoryKey(categoryKey) + , mItemizeType(itemizeType) + , mTotalSize("bytes", 0) + , mPurgeableSize("bytes", 0) {} + +const char* SkiaMemoryTracer::mapName(const char* resourceName) { + for (auto& resource : mResourceMap) { + if (SkStrContains(resourceName, resource.first)) { + return resource.second; + } + } + return nullptr; +} + +void SkiaMemoryTracer::processElement() { + if(!mCurrentElement.empty()) { + // Only count elements that contain "size", other values just provide metadata. + auto sizeResult = mCurrentValues.find("size"); + if (sizeResult != mCurrentValues.end()) { + mTotalSize.value += sizeResult->second.value; + mTotalSize.count++; + } else { + mCurrentElement.clear(); + mCurrentValues.clear(); + return; + } + + // find the purgeable size if one exists + auto purgeableResult = mCurrentValues.find("purgeable_size"); + if (purgeableResult != mCurrentValues.end()) { + mPurgeableSize.value += purgeableResult->second.value; + mPurgeableSize.count++; + } + + // find the type if one exists + const char* type; + auto typeResult = mCurrentValues.find("type"); + if (typeResult != mCurrentValues.end()) { + type = typeResult->second.units; + } else if (mItemizeType) { + type = "Other"; + } + + // compute the type if we are itemizing or use the default "size" if we are not + const char* key = (mItemizeType) ? type : sizeResult->first; + SkASSERT(key != nullptr); + + // compute the top level element name using either the map or category key + const char* resourceName = mapName(mCurrentElement.c_str()); + if (mCategoryKey != nullptr) { + // find the category if one exists + auto categoryResult = mCurrentValues.find(mCategoryKey); + if (categoryResult != mCurrentValues.end()) { + resourceName = categoryResult->second.units; + } else if (mItemizeType) { + resourceName = "Other"; + } + } + + // if we don't have a resource name then we don't know how to label the + // data and should abort. + if (resourceName == nullptr) { + mCurrentElement.clear(); + mCurrentValues.clear(); + return; + } + + auto result = mResults.find(resourceName); + if (result != mResults.end()) { + auto& resourceValues = result->second; + typeResult = resourceValues.find(key); + if (typeResult != resourceValues.end()) { + SkASSERT(sizeResult->second.units == typeResult->second.units); + typeResult->second.value += sizeResult->second.value; + typeResult->second.count++; + } else { + resourceValues.insert({key, sizeResult->second}); + } + } else { + mCurrentValues.clear(); + mCurrentValues.insert({key, sizeResult->second}); + mResults.insert({resourceName, mCurrentValues}); + } + } + + mCurrentElement.clear(); + mCurrentValues.clear(); +} + +void SkiaMemoryTracer::dumpNumericValue(const char* dumpName, const char* valueName, + const char* units, uint64_t value) { + if (mCurrentElement != dumpName) { + processElement(); + mCurrentElement = dumpName; + } + mCurrentValues.insert({valueName, {units, value}}); +} + +void SkiaMemoryTracer::logOutput(String8& log) { + // process any remaining elements + processElement(); + + for (const auto& namedItem : mResults) { + if (mItemizeType) { + log.appendFormat(" %s:\n", namedItem.first.c_str()); + for (const auto& typedValue : namedItem.second) { + TraceValue traceValue = convertUnits(typedValue.second); + const char* entry = (traceValue.count > 1) ? "entries" : "entry"; + log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first, + traceValue.value, traceValue.units, traceValue.count, entry); + } + } else { + auto result = namedItem.second.find("size"); + if (result != namedItem.second.end()) { + TraceValue traceValue = convertUnits(result->second); + const char* entry = (traceValue.count > 1) ? "entries" : "entry"; + log.appendFormat(" %s: %.2f %s (%d %s)\n", namedItem.first.c_str(), + traceValue.value, traceValue.units, traceValue.count, entry); + } + } + } +} + +void SkiaMemoryTracer::logTotals(String8& log) { + TraceValue total = convertUnits(mTotalSize); + TraceValue purgeable = convertUnits(mPurgeableSize); + log.appendFormat(" %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value, + total.value, total.units, purgeable.value, purgeable.units); +} + +SkiaMemoryTracer::TraceValue SkiaMemoryTracer::convertUnits(const TraceValue& value) { + TraceValue output(value); + if (SkString("bytes") == SkString(output.units) && output.value >= 1024) { + output.value = output.value / 1024.0f; + output.units = "KB"; + } + if (SkString("KB") == SkString(output.units) && output.value >= 1024) { + output.value = output.value / 1024.0f; + output.units = "MB"; + } + return output; +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h new file mode 100644 index 000000000000..abf1f4b052ce --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h @@ -0,0 +1,88 @@ +/* + * 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 +#include +#include +#include +#include + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +typedef std::pair ResourcePair; + +class SkiaMemoryTracer : public SkTraceMemoryDump { +public: + SkiaMemoryTracer(std::vector resourceMap, bool itemizeType); + SkiaMemoryTracer(const char* categoryKey, bool itemizeType); + ~SkiaMemoryTracer() override {} + + void logOutput(String8& log); + void logTotals(String8& log); + + void dumpNumericValue(const char* dumpName, const char* valueName, const char* units, + uint64_t value) override; + + void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override { + // for convenience we just store this in the same format as numerical values + dumpNumericValue(dumpName, valueName, value, 0); + } + + LevelOfDetail getRequestedDetails() const override { + return SkTraceMemoryDump::kLight_LevelOfDetail; + } + + bool shouldDumpWrappedObjects() const override { return true; } + void setMemoryBacking(const char*, const char*, const char*) override { } + void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override { } + +private: + struct TraceValue { + TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {} + TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {} + + const char* units; + float value; + int count; + }; + + const char* mapName(const char* resourceName); + void processElement(); + TraceValue convertUnits(const TraceValue& value); + + const std::vector mResourceMap; + const char* mCategoryKey = nullptr; + const bool mItemizeType; + + // variables storing the size of all elements being dumped + TraceValue mTotalSize; + TraceValue mPurgeableSize; + + // variables storing information on the current node being dumped + std::string mCurrentElement; + std::unordered_map mCurrentValues; + + // variable that stores the final format of the data after the individual elements are processed + std::unordered_map> mResults; +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ \ No newline at end of file diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 907f2d2d398f..32d4c77c5fc3 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -20,10 +20,12 @@ #include "Properties.h" #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" +#include "pipeline/skia/SkiaMemoryTracer.h" #include "renderstate/RenderState.h" #include #include +#include #include #include #include @@ -178,12 +180,29 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) return; } - size_t bytesCached; - mGrContext->getResourceCacheUsage(nullptr, &bytesCached); + log.appendFormat("Font Cache (CPU):\n"); + log.appendFormat(" Size: %.2f kB \n", SkGraphics::GetFontCacheUsed() / 1024.0f); + log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed()); - log.appendFormat("Caches:\n"); + log.appendFormat("CPU Caches:\n"); + std::vector cpuResourceMap = { + {"skia/sk_resource_cache/bitmap_", "Bitmaps"}, + {"skia/sk_resource_cache/rrect-blur_", "Masks"}, + {"skia/sk_resource_cache/rects-blur_", "Masks"}, + {"skia/sk_resource_cache/tessellated", "Shadows"}, + }; + skiapipeline::SkiaMemoryTracer cpuTracer(cpuResourceMap, false); + SkGraphics::DumpMemoryStatistics(&cpuTracer); + cpuTracer.logOutput(log); + + log.appendFormat("GPU Caches:\n"); + skiapipeline::SkiaMemoryTracer gpuTracer("category", true); + mGrContext->dumpMemoryStatistics(&gpuTracer); + gpuTracer.logOutput(log); + + log.appendFormat("Other Caches:\n"); log.appendFormat(" Current / Maximum\n"); - log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f kB (entries = %zu)\n", 0.0f, 0.0f, + log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f KB (entries = %zu)\n", 0.0f, 0.0f, (size_t)0); if (renderState) { @@ -200,14 +219,12 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; } - log.appendFormat(" Layers Total %6.2f kB (numLayers = %zu)\n", + log.appendFormat(" Layers Total %6.2f KB (numLayers = %zu)\n", layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size()); } - log.appendFormat("Total memory usage:\n"); - log.appendFormat(" %zu bytes, %.2f MB (%.2f MB is purgeable)\n", bytesCached, - bytesCached / 1024.0f / 1024.0f, - mGrContext->getResourceCachePurgeableBytes() / 1024.0f / 1024.0f); + log.appendFormat("Total GPU memory usage:\n"); + gpuTracer.logTotals(log); } } /* namespace renderthread */ -- 2.11.0