Loading libs/renderengine/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -91,6 +91,7 @@ filegroup { "skia/debug/CaptureTimer.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", "skia/debug/CommonPool.cpp", "skia/debug/SkiaCapture.cpp", "skia/debug/SkiaCapture.cpp", "skia/debug/SkiaMemoryReporter.cpp", "skia/filters/BlurFilter.cpp", "skia/filters/BlurFilter.cpp", "skia/filters/LinearEffect.cpp", "skia/filters/LinearEffect.cpp", ], ], Loading libs/renderengine/skia/SkiaGLRenderEngine.cpp +43 −3 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <SkColorFilter.h> #include <SkColorFilter.h> #include <SkColorMatrix.h> #include <SkColorMatrix.h> #include <SkColorSpace.h> #include <SkColorSpace.h> #include <SkGraphics.h> #include <SkImage.h> #include <SkImage.h> #include <SkImageFilters.h> #include <SkImageFilters.h> #include <SkRegion.h> #include <SkRegion.h> Loading @@ -40,13 +41,13 @@ #include <ui/DebugUtils.h> #include <ui/DebugUtils.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicBuffer.h> #include <utils/Trace.h> #include <utils/Trace.h> #include "Cache.h" #include <cmath> #include <cmath> #include <cstdint> #include <cstdint> #include <memory> #include <memory> #include "../gl/GLExtensions.h" #include "../gl/GLExtensions.h" #include "Cache.h" #include "ColorSpaces.h" #include "ColorSpaces.h" #include "SkBlendMode.h" #include "SkBlendMode.h" #include "SkImageInfo.h" #include "SkImageInfo.h" Loading @@ -54,6 +55,7 @@ #include "filters/LinearEffect.h" #include "filters/LinearEffect.h" #include "log/log_main.h" #include "log/log_main.h" #include "skia/debug/SkiaCapture.h" #include "skia/debug/SkiaCapture.h" #include "skia/debug/SkiaMemoryReporter.h" #include "system/graphics-base-v1.0.h" #include "system/graphics-base-v1.0.h" namespace { namespace { Loading Loading @@ -1249,9 +1251,38 @@ void SkiaGLRenderEngine::dump(std::string& result) { StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n", StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n", mSkSLCacheMonitor.shadersCachedSinceLastCall()); mSkSLCacheMonitor.shadersCachedSinceLastCall()); std::vector<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"}, {"skia", "Other"}, }; SkiaMemoryReporter cpuReporter(cpuResourceMap, false); SkGraphics::DumpMemoryStatistics(&cpuReporter); StringAppendF(&result, "Skia CPU Caches: "); cpuReporter.logTotals(result); cpuReporter.logOutput(result); { { std::lock_guard<std::mutex> lock(mRenderingMutex); std::lock_guard<std::mutex> lock(mRenderingMutex); StringAppendF(&result, "RenderEngine texture cache size: %zu\n", mTextureCache.size()); std::vector<ResourcePair> gpuResourceMap = { {"texture_renderbuffer", "Texture/RenderBuffer"}, {"texture", "Texture"}, {"gr_text_blob_cache", "Text"}, {"skia", "Other"}, }; SkiaMemoryReporter gpuReporter(gpuResourceMap, true); mGrContext->dumpMemoryStatistics(&gpuReporter); StringAppendF(&result, "Skia's GPU Caches: "); gpuReporter.logTotals(result); gpuReporter.logOutput(result); StringAppendF(&result, "Skia's Wrapped Objects:\n"); gpuReporter.logOutput(result, true); StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n", mTextureCache.size()); StringAppendF(&result, "Dumping buffer ids...\n"); StringAppendF(&result, "Dumping buffer ids...\n"); // TODO(178539829): It would be nice to know which layer these are coming from and what // TODO(178539829): It would be nice to know which layer these are coming from and what // the texture sizes are. // the texture sizes are. Loading @@ -1259,7 +1290,16 @@ void SkiaGLRenderEngine::dump(std::string& result) { StringAppendF(&result, "- 0x%" PRIx64 "\n", id); StringAppendF(&result, "- 0x%" PRIx64 "\n", id); } } StringAppendF(&result, "\n"); StringAppendF(&result, "\n"); StringAppendF(&result, "RenderEngine protected texture cache size: %zu\n", SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true); mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter); StringAppendF(&result, "Skia's GPU Protected Caches: "); gpuProtectedReporter.logTotals(result); gpuProtectedReporter.logOutput(result); StringAppendF(&result, "Skia's Protected Wrapped Objects:\n"); gpuProtectedReporter.logOutput(result, true); StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n", mProtectedTextureCache.size()); mProtectedTextureCache.size()); StringAppendF(&result, "Dumping buffer ids...\n"); StringAppendF(&result, "Dumping buffer ids...\n"); for (const auto& [id, unused] : mProtectedTextureCache) { for (const auto& [id, unused] : mProtectedTextureCache) { Loading libs/renderengine/skia/debug/SkiaMemoryReporter.cpp 0 → 100644 +205 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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. */ #undef LOG_TAG #define LOG_TAG "RenderEngine" #include "SkiaMemoryReporter.h" #include <SkString.h> #include <android-base/stringprintf.h> #include <log/log_main.h> namespace android { namespace renderengine { namespace skia { using base::StringAppendF; SkiaMemoryReporter::SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize) : mResourceMap(resourceMap), mItemize(itemize), mTotalSize("bytes", 0), mPurgeableSize("bytes", 0) {} const char* SkiaMemoryReporter::mapName(const char* resourceName) { for (auto& resource : mResourceMap) { if (SkStrContains(resourceName, resource.first)) { return resource.second; } } return nullptr; } void SkiaMemoryReporter::resetCurrentElement() { mCurrentElement.clear(); mCurrentValues.clear(); mIsCurrentValueWrapped = false; } void SkiaMemoryReporter::processCurrentElement() { // compute the top level element name using the map const char* resourceName = mCurrentElement.empty() ? nullptr : mapName(mCurrentElement.c_str()); // if we don't have a resource name then we don't know how to label the // data and should abort. if (resourceName == nullptr) { resetCurrentElement(); return; } // Only count elements that contain "size"; other values just provide metadata. auto sizeResult = mCurrentValues.find("size"); if (sizeResult != mCurrentValues.end() && sizeResult->second.value > 0) { if (!mIsCurrentValueWrapped) { mTotalSize.value += sizeResult->second.value; mTotalSize.count++; } } else { resetCurrentElement(); return; } // find the purgeable size if one exists auto purgeableResult = mCurrentValues.find("purgeable_size"); if (!mIsCurrentValueWrapped && purgeableResult != mCurrentValues.end()) { mPurgeableSize.value += purgeableResult->second.value; mPurgeableSize.count++; } // do we store this element in the wrapped list or the skia managed list auto& results = mIsCurrentValueWrapped ? mWrappedResults : mResults; // insert a copy of the element and all of its keys. We must make a copy here instead of // std::move() as we will continue to use these values later in the function and again // when we move on to process the next element. results.insert({mCurrentElement, mCurrentValues}); // insert the item into its mapped category auto result = results.find(resourceName); if (result != results.end()) { auto& resourceValues = result->second; auto totalResult = resourceValues.find(sizeResult->first); if (totalResult != resourceValues.end()) { ALOGE_IF(sizeResult->second.units != totalResult->second.units, "resource units do not match so the sum of resource type (%s) will be invalid", resourceName); totalResult->second.value += sizeResult->second.value; totalResult->second.count++; } else { ALOGE("an entry (%s) should not exist in the results without a size", resourceName); } } else { // only store the size for the top level resource results.insert({resourceName, {{sizeResult->first, sizeResult->second}}}); } resetCurrentElement(); } void SkiaMemoryReporter::dumpNumericValue(const char* dumpName, const char* valueName, const char* units, uint64_t value) { if (mCurrentElement != dumpName) { processCurrentElement(); mCurrentElement = dumpName; } mCurrentValues.insert({valueName, {units, value}}); } void SkiaMemoryReporter::dumpWrappedState(const char* dumpName, bool isWrappedObject) { if (mCurrentElement != dumpName) { processCurrentElement(); mCurrentElement = dumpName; } mIsCurrentValueWrapped = isWrappedObject; } void SkiaMemoryReporter::logOutput(std::string& log, bool wrappedResources) { // process the current element before logging processCurrentElement(); const auto& resultsMap = wrappedResources ? mWrappedResults : mResults; // log each individual element based on the resource map for (const auto& resourceCategory : mResourceMap) { // find the named item and print the totals const auto categoryItem = resultsMap.find(resourceCategory.second); if (categoryItem != resultsMap.end()) { auto result = categoryItem->second.find("size"); if (result != categoryItem->second.end()) { TraceValue traceValue = convertUnits(result->second); const char* entry = (traceValue.count > 1) ? "entries" : "entry"; StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(), traceValue.value, traceValue.units, traceValue.count, entry); } if (mItemize) { for (const auto& individualItem : resultsMap) { // if the individual item matches the category then print all its details or // in the case of wrapped resources just print the wrapped size const char* categoryMatch = mapName(individualItem.first.c_str()); if (categoryMatch && strcmp(categoryMatch, resourceCategory.second) == 0) { auto result = individualItem.second.find("size"); TraceValue size = convertUnits(result->second); StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(), size.value, size.units); if (!wrappedResources) { for (const auto& itemValues : individualItem.second) { if (strcmp("size", itemValues.first) == 0) { continue; } TraceValue traceValue = convertUnits(itemValues.second); if (traceValue.value == 0.0f) { StringAppendF(&log, " %s[%s]", itemValues.first, traceValue.units); } else { StringAppendF(&log, " %s[%.2f %s]", itemValues.first, traceValue.value, traceValue.units); } } } StringAppendF(&log, "\n"); } } } } } } void SkiaMemoryReporter::logTotals(std::string& log) { // process the current element before logging processCurrentElement(); TraceValue total = convertUnits(mTotalSize); TraceValue purgeable = convertUnits(mPurgeableSize); StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value, total.value, total.units, purgeable.value, purgeable.units); } SkiaMemoryReporter::TraceValue SkiaMemoryReporter::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 skia */ } /* namespace renderengine */ } /* namespace android */ libs/renderengine/skia/debug/SkiaMemoryReporter.h 0 → 100644 +107 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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 <SkTraceMemoryDump.h> #include <string> #include <unordered_map> #include <vector> namespace android { namespace renderengine { namespace skia { // Mapping of resource substrings (1st element) that if found within a trace "dumpName" // should be mapped to the category name (2nd element). All char* used in a resourcePair // are expected to have a lifetime longer than the SkiaMemoryReporter in which they are used. typedef std::pair<const char*, const char*> ResourcePair; /* * Utility class for logging the CPU/GPU usage of Skia caches in a format that is specific * to RenderEngine. HWUI has a similar logging class, but the data collected and the way * it is formatted and reported on are intended to be unique to each use case. */ class SkiaMemoryReporter : public SkTraceMemoryDump { public: /** * Creates the reporter class that can be populated by various Skia entry points, like * SkGraphics and GrContext, as well as format and log the results. * @param resourceMap An array of values that maps a Skia dumpName into a user defined category. * The first vector entry that matches the dumpName is used for the mapping. * @param itemize if true when logging the categories the individual elements will be printed * directly after the category details are printed. Otherwise, only the category * totals will be printed. */ SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize); ~SkiaMemoryReporter() override {} void logOutput(std::string& log, bool wrappedResources = false); void logTotals(std::string& 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); } void dumpWrappedState(const char* dumpName, bool isWrappedObject) override; 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 processCurrentElement(); void resetCurrentElement(); TraceValue convertUnits(const TraceValue& value); const std::vector<ResourcePair>& mResourceMap; const bool mItemize; // variables storing the size of all non-wrapped 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; bool mIsCurrentValueWrapped = false; // 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; std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mWrappedResults; }; } /* namespace skia */ } /* namespace renderengine */ } /* namespace android */ No newline at end of file Loading
libs/renderengine/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -91,6 +91,7 @@ filegroup { "skia/debug/CaptureTimer.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", "skia/debug/CommonPool.cpp", "skia/debug/SkiaCapture.cpp", "skia/debug/SkiaCapture.cpp", "skia/debug/SkiaMemoryReporter.cpp", "skia/filters/BlurFilter.cpp", "skia/filters/BlurFilter.cpp", "skia/filters/LinearEffect.cpp", "skia/filters/LinearEffect.cpp", ], ], Loading
libs/renderengine/skia/SkiaGLRenderEngine.cpp +43 −3 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <SkColorFilter.h> #include <SkColorFilter.h> #include <SkColorMatrix.h> #include <SkColorMatrix.h> #include <SkColorSpace.h> #include <SkColorSpace.h> #include <SkGraphics.h> #include <SkImage.h> #include <SkImage.h> #include <SkImageFilters.h> #include <SkImageFilters.h> #include <SkRegion.h> #include <SkRegion.h> Loading @@ -40,13 +41,13 @@ #include <ui/DebugUtils.h> #include <ui/DebugUtils.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicBuffer.h> #include <utils/Trace.h> #include <utils/Trace.h> #include "Cache.h" #include <cmath> #include <cmath> #include <cstdint> #include <cstdint> #include <memory> #include <memory> #include "../gl/GLExtensions.h" #include "../gl/GLExtensions.h" #include "Cache.h" #include "ColorSpaces.h" #include "ColorSpaces.h" #include "SkBlendMode.h" #include "SkBlendMode.h" #include "SkImageInfo.h" #include "SkImageInfo.h" Loading @@ -54,6 +55,7 @@ #include "filters/LinearEffect.h" #include "filters/LinearEffect.h" #include "log/log_main.h" #include "log/log_main.h" #include "skia/debug/SkiaCapture.h" #include "skia/debug/SkiaCapture.h" #include "skia/debug/SkiaMemoryReporter.h" #include "system/graphics-base-v1.0.h" #include "system/graphics-base-v1.0.h" namespace { namespace { Loading Loading @@ -1249,9 +1251,38 @@ void SkiaGLRenderEngine::dump(std::string& result) { StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n", StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n", mSkSLCacheMonitor.shadersCachedSinceLastCall()); mSkSLCacheMonitor.shadersCachedSinceLastCall()); std::vector<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"}, {"skia", "Other"}, }; SkiaMemoryReporter cpuReporter(cpuResourceMap, false); SkGraphics::DumpMemoryStatistics(&cpuReporter); StringAppendF(&result, "Skia CPU Caches: "); cpuReporter.logTotals(result); cpuReporter.logOutput(result); { { std::lock_guard<std::mutex> lock(mRenderingMutex); std::lock_guard<std::mutex> lock(mRenderingMutex); StringAppendF(&result, "RenderEngine texture cache size: %zu\n", mTextureCache.size()); std::vector<ResourcePair> gpuResourceMap = { {"texture_renderbuffer", "Texture/RenderBuffer"}, {"texture", "Texture"}, {"gr_text_blob_cache", "Text"}, {"skia", "Other"}, }; SkiaMemoryReporter gpuReporter(gpuResourceMap, true); mGrContext->dumpMemoryStatistics(&gpuReporter); StringAppendF(&result, "Skia's GPU Caches: "); gpuReporter.logTotals(result); gpuReporter.logOutput(result); StringAppendF(&result, "Skia's Wrapped Objects:\n"); gpuReporter.logOutput(result, true); StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n", mTextureCache.size()); StringAppendF(&result, "Dumping buffer ids...\n"); StringAppendF(&result, "Dumping buffer ids...\n"); // TODO(178539829): It would be nice to know which layer these are coming from and what // TODO(178539829): It would be nice to know which layer these are coming from and what // the texture sizes are. // the texture sizes are. Loading @@ -1259,7 +1290,16 @@ void SkiaGLRenderEngine::dump(std::string& result) { StringAppendF(&result, "- 0x%" PRIx64 "\n", id); StringAppendF(&result, "- 0x%" PRIx64 "\n", id); } } StringAppendF(&result, "\n"); StringAppendF(&result, "\n"); StringAppendF(&result, "RenderEngine protected texture cache size: %zu\n", SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true); mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter); StringAppendF(&result, "Skia's GPU Protected Caches: "); gpuProtectedReporter.logTotals(result); gpuProtectedReporter.logOutput(result); StringAppendF(&result, "Skia's Protected Wrapped Objects:\n"); gpuProtectedReporter.logOutput(result, true); StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n", mProtectedTextureCache.size()); mProtectedTextureCache.size()); StringAppendF(&result, "Dumping buffer ids...\n"); StringAppendF(&result, "Dumping buffer ids...\n"); for (const auto& [id, unused] : mProtectedTextureCache) { for (const auto& [id, unused] : mProtectedTextureCache) { Loading
libs/renderengine/skia/debug/SkiaMemoryReporter.cpp 0 → 100644 +205 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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. */ #undef LOG_TAG #define LOG_TAG "RenderEngine" #include "SkiaMemoryReporter.h" #include <SkString.h> #include <android-base/stringprintf.h> #include <log/log_main.h> namespace android { namespace renderengine { namespace skia { using base::StringAppendF; SkiaMemoryReporter::SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize) : mResourceMap(resourceMap), mItemize(itemize), mTotalSize("bytes", 0), mPurgeableSize("bytes", 0) {} const char* SkiaMemoryReporter::mapName(const char* resourceName) { for (auto& resource : mResourceMap) { if (SkStrContains(resourceName, resource.first)) { return resource.second; } } return nullptr; } void SkiaMemoryReporter::resetCurrentElement() { mCurrentElement.clear(); mCurrentValues.clear(); mIsCurrentValueWrapped = false; } void SkiaMemoryReporter::processCurrentElement() { // compute the top level element name using the map const char* resourceName = mCurrentElement.empty() ? nullptr : mapName(mCurrentElement.c_str()); // if we don't have a resource name then we don't know how to label the // data and should abort. if (resourceName == nullptr) { resetCurrentElement(); return; } // Only count elements that contain "size"; other values just provide metadata. auto sizeResult = mCurrentValues.find("size"); if (sizeResult != mCurrentValues.end() && sizeResult->second.value > 0) { if (!mIsCurrentValueWrapped) { mTotalSize.value += sizeResult->second.value; mTotalSize.count++; } } else { resetCurrentElement(); return; } // find the purgeable size if one exists auto purgeableResult = mCurrentValues.find("purgeable_size"); if (!mIsCurrentValueWrapped && purgeableResult != mCurrentValues.end()) { mPurgeableSize.value += purgeableResult->second.value; mPurgeableSize.count++; } // do we store this element in the wrapped list or the skia managed list auto& results = mIsCurrentValueWrapped ? mWrappedResults : mResults; // insert a copy of the element and all of its keys. We must make a copy here instead of // std::move() as we will continue to use these values later in the function and again // when we move on to process the next element. results.insert({mCurrentElement, mCurrentValues}); // insert the item into its mapped category auto result = results.find(resourceName); if (result != results.end()) { auto& resourceValues = result->second; auto totalResult = resourceValues.find(sizeResult->first); if (totalResult != resourceValues.end()) { ALOGE_IF(sizeResult->second.units != totalResult->second.units, "resource units do not match so the sum of resource type (%s) will be invalid", resourceName); totalResult->second.value += sizeResult->second.value; totalResult->second.count++; } else { ALOGE("an entry (%s) should not exist in the results without a size", resourceName); } } else { // only store the size for the top level resource results.insert({resourceName, {{sizeResult->first, sizeResult->second}}}); } resetCurrentElement(); } void SkiaMemoryReporter::dumpNumericValue(const char* dumpName, const char* valueName, const char* units, uint64_t value) { if (mCurrentElement != dumpName) { processCurrentElement(); mCurrentElement = dumpName; } mCurrentValues.insert({valueName, {units, value}}); } void SkiaMemoryReporter::dumpWrappedState(const char* dumpName, bool isWrappedObject) { if (mCurrentElement != dumpName) { processCurrentElement(); mCurrentElement = dumpName; } mIsCurrentValueWrapped = isWrappedObject; } void SkiaMemoryReporter::logOutput(std::string& log, bool wrappedResources) { // process the current element before logging processCurrentElement(); const auto& resultsMap = wrappedResources ? mWrappedResults : mResults; // log each individual element based on the resource map for (const auto& resourceCategory : mResourceMap) { // find the named item and print the totals const auto categoryItem = resultsMap.find(resourceCategory.second); if (categoryItem != resultsMap.end()) { auto result = categoryItem->second.find("size"); if (result != categoryItem->second.end()) { TraceValue traceValue = convertUnits(result->second); const char* entry = (traceValue.count > 1) ? "entries" : "entry"; StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(), traceValue.value, traceValue.units, traceValue.count, entry); } if (mItemize) { for (const auto& individualItem : resultsMap) { // if the individual item matches the category then print all its details or // in the case of wrapped resources just print the wrapped size const char* categoryMatch = mapName(individualItem.first.c_str()); if (categoryMatch && strcmp(categoryMatch, resourceCategory.second) == 0) { auto result = individualItem.second.find("size"); TraceValue size = convertUnits(result->second); StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(), size.value, size.units); if (!wrappedResources) { for (const auto& itemValues : individualItem.second) { if (strcmp("size", itemValues.first) == 0) { continue; } TraceValue traceValue = convertUnits(itemValues.second); if (traceValue.value == 0.0f) { StringAppendF(&log, " %s[%s]", itemValues.first, traceValue.units); } else { StringAppendF(&log, " %s[%.2f %s]", itemValues.first, traceValue.value, traceValue.units); } } } StringAppendF(&log, "\n"); } } } } } } void SkiaMemoryReporter::logTotals(std::string& log) { // process the current element before logging processCurrentElement(); TraceValue total = convertUnits(mTotalSize); TraceValue purgeable = convertUnits(mPurgeableSize); StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value, total.value, total.units, purgeable.value, purgeable.units); } SkiaMemoryReporter::TraceValue SkiaMemoryReporter::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 skia */ } /* namespace renderengine */ } /* namespace android */
libs/renderengine/skia/debug/SkiaMemoryReporter.h 0 → 100644 +107 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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 <SkTraceMemoryDump.h> #include <string> #include <unordered_map> #include <vector> namespace android { namespace renderengine { namespace skia { // Mapping of resource substrings (1st element) that if found within a trace "dumpName" // should be mapped to the category name (2nd element). All char* used in a resourcePair // are expected to have a lifetime longer than the SkiaMemoryReporter in which they are used. typedef std::pair<const char*, const char*> ResourcePair; /* * Utility class for logging the CPU/GPU usage of Skia caches in a format that is specific * to RenderEngine. HWUI has a similar logging class, but the data collected and the way * it is formatted and reported on are intended to be unique to each use case. */ class SkiaMemoryReporter : public SkTraceMemoryDump { public: /** * Creates the reporter class that can be populated by various Skia entry points, like * SkGraphics and GrContext, as well as format and log the results. * @param resourceMap An array of values that maps a Skia dumpName into a user defined category. * The first vector entry that matches the dumpName is used for the mapping. * @param itemize if true when logging the categories the individual elements will be printed * directly after the category details are printed. Otherwise, only the category * totals will be printed. */ SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize); ~SkiaMemoryReporter() override {} void logOutput(std::string& log, bool wrappedResources = false); void logTotals(std::string& 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); } void dumpWrappedState(const char* dumpName, bool isWrappedObject) override; 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 processCurrentElement(); void resetCurrentElement(); TraceValue convertUnits(const TraceValue& value); const std::vector<ResourcePair>& mResourceMap; const bool mItemize; // variables storing the size of all non-wrapped 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; bool mIsCurrentValueWrapped = false; // 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; std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mWrappedResults; }; } /* namespace skia */ } /* namespace renderengine */ } /* namespace android */ No newline at end of file