Merge "Print detailed memory usage of Skia for dumpsys gfxinfo" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
8ec341627c
@@ -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",
|
||||
|
||||
175
libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
Normal file
175
libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
Normal file
@@ -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<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 */
|
||||
88
libs/hwui/pipeline/skia/SkiaMemoryTracer.h
Normal file
88
libs/hwui/pipeline/skia/SkiaMemoryTracer.h
Normal file
@@ -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 <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 */
|
||||
@@ -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>
|
||||
@@ -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) {
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user