Loading libs/hwui/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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", Loading libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * 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<ResourcePair> 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 */ libs/hwui/pipeline/skia/SkiaMemoryTracer.h 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * 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 <SkString.h> #include <SkTraceMemoryDump.h> #include <utils/String8.h> #include <unordered_map> #include <vector> namespace android { namespace uirenderer { namespace skiapipeline { typedef std::pair<const char*, const char*> ResourcePair; class SkiaMemoryTracer : public SkTraceMemoryDump { public: SkiaMemoryTracer(std::vector<ResourcePair> 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<ResourcePair> 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<const char*, TraceValue> mCurrentValues; // variable that stores the final format of the data after the individual elements are processed std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mResults; }; } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ No newline at end of file libs/hwui/renderthread/CacheManager.cpp +26 −9 Original line number Diff line number Diff line Loading @@ -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 <GrContextOptions.h> #include <SkExecutor.h> #include <SkGraphics.h> #include <gui/Surface.h> #include <math.h> #include <set> Loading Loading @@ -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<skiapipeline::ResourcePair> 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) { Loading @@ -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 */ Loading Loading
libs/hwui/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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", Loading
libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * 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<ResourcePair> 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 */
libs/hwui/pipeline/skia/SkiaMemoryTracer.h 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * 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 <SkString.h> #include <SkTraceMemoryDump.h> #include <utils/String8.h> #include <unordered_map> #include <vector> namespace android { namespace uirenderer { namespace skiapipeline { typedef std::pair<const char*, const char*> ResourcePair; class SkiaMemoryTracer : public SkTraceMemoryDump { public: SkiaMemoryTracer(std::vector<ResourcePair> 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<ResourcePair> 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<const char*, TraceValue> mCurrentValues; // variable that stores the final format of the data after the individual elements are processed std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mResults; }; } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ No newline at end of file
libs/hwui/renderthread/CacheManager.cpp +26 −9 Original line number Diff line number Diff line Loading @@ -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 <GrContextOptions.h> #include <SkExecutor.h> #include <SkGraphics.h> #include <gui/Surface.h> #include <math.h> #include <set> Loading Loading @@ -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<skiapipeline::ResourcePair> 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) { Loading @@ -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 */ Loading