2843c44b36
"HWUI GPU Memory" feels like total GPU memory used by HWUI, but it's actually the total of the memory categories not explictly listed. So this change renames it to "HWUI Misc Memory". Bug: 147833967 Test: take a systrace Change-Id: Id6b5f09981e148373dbc4e7f482e3da35740a566
178 lines
6.9 KiB
C++
178 lines
6.9 KiB
C++
/*
|
|
* Copyright (C) 2020 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 "ATraceMemoryDump.h"
|
|
|
|
#include <utils/Trace.h>
|
|
|
|
#include <cstring>
|
|
|
|
namespace android {
|
|
namespace uirenderer {
|
|
namespace skiapipeline {
|
|
|
|
// When purgeable is INVALID_MEMORY_SIZE it won't be logged at all.
|
|
#define INVALID_MEMORY_SIZE -1
|
|
|
|
/**
|
|
* Skia invokes the following SkTraceMemoryDump functions:
|
|
* 1. dumpNumericValue (dumpName, units="bytes", valueName="size")
|
|
* 2. dumpStringValue (dumpName, valueName="type") [optional -> for example CPU memory does not
|
|
* invoke dumpStringValue]
|
|
* 3. dumpNumericValue (dumpName, units="bytes", valueName="purgeable_size") [optional]
|
|
* 4. setMemoryBacking(dumpName, backingType) [optional -> for example Vulkan GPU resources do not
|
|
* invoke setMemoryBacking]
|
|
*
|
|
* ATraceMemoryDump calculates memory category first by looking at the "type" string passed to
|
|
* dumpStringValue and then by looking at "backingType" passed to setMemoryBacking.
|
|
* Only GPU Texture memory is tracked separately and everything else is grouped as one
|
|
* "Misc Memory" category.
|
|
*/
|
|
static std::unordered_map<const char*, const char*> sResourceMap = {
|
|
{"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType)
|
|
{"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType)
|
|
{"Texture", "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type")
|
|
// Uncomment categories below to split "Misc Memory" into more brackets for debugging.
|
|
/*{"vk_buffer", "vk_buffer"},
|
|
{"gl_renderbuffer", "gl_renderbuffer"},
|
|
{"gl_buffer", "gl_buffer"},
|
|
{"RenderTarget", "RenderTarget"},
|
|
{"Stencil", "Stencil"},
|
|
{"Path Data", "Path Data"},
|
|
{"Buffer Object", "Buffer Object"},
|
|
{"Surface", "Surface"},*/
|
|
};
|
|
|
|
ATraceMemoryDump::ATraceMemoryDump() {
|
|
mLastDumpName.reserve(100);
|
|
mCategory.reserve(100);
|
|
}
|
|
|
|
void ATraceMemoryDump::dumpNumericValue(const char* dumpName, const char* valueName,
|
|
const char* units, uint64_t value) {
|
|
if (!strcmp(units, "bytes")) {
|
|
recordAndResetCountersIfNeeded(dumpName);
|
|
if (!strcmp(valueName, "size")) {
|
|
mLastDumpValue = value;
|
|
} else if (!strcmp(valueName, "purgeable_size")) {
|
|
mLastPurgeableDumpValue = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ATraceMemoryDump::dumpStringValue(const char* dumpName, const char* valueName,
|
|
const char* value) {
|
|
if (!strcmp(valueName, "type")) {
|
|
recordAndResetCountersIfNeeded(dumpName);
|
|
auto categoryIt = sResourceMap.find(value);
|
|
if (categoryIt != sResourceMap.end()) {
|
|
mCategory = categoryIt->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ATraceMemoryDump::setMemoryBacking(const char* dumpName, const char* backingType,
|
|
const char* backingObjectId) {
|
|
recordAndResetCountersIfNeeded(dumpName);
|
|
auto categoryIt = sResourceMap.find(backingType);
|
|
if (categoryIt != sResourceMap.end()) {
|
|
mCategory = categoryIt->second;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* startFrame is invoked before dumping anything. It resets counters from the previous frame.
|
|
* This is important, because if there is no new data for a given category trace would assume
|
|
* usage has not changed (instead of reporting 0).
|
|
*/
|
|
void ATraceMemoryDump::startFrame() {
|
|
resetCurrentCounter("");
|
|
for (auto& it : mCurrentValues) {
|
|
// Once a category is observed in at least one frame, it is always reported in subsequent
|
|
// frames (even if it is 0). Not logging a category to ATRACE would mean its value has not
|
|
// changed since the previous frame, which is not what we want.
|
|
it.second.memory = 0;
|
|
// If purgeableMemory is INVALID_MEMORY_SIZE, then logTraces won't log it at all.
|
|
if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) {
|
|
it.second.purgeableMemory = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* logTraces reads from mCurrentValues and logs the counters with ATRACE.
|
|
*/
|
|
void ATraceMemoryDump::logTraces() {
|
|
// Accumulate data from last dumpName
|
|
recordAndResetCountersIfNeeded("");
|
|
uint64_t hwui_all_frame_memory = 0;
|
|
for (auto& it : mCurrentValues) {
|
|
hwui_all_frame_memory += it.second.memory;
|
|
ATRACE_INT64(it.first.c_str(), it.second.memory);
|
|
if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) {
|
|
ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory);
|
|
}
|
|
}
|
|
ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory);
|
|
}
|
|
|
|
/**
|
|
* recordAndResetCountersIfNeeded reads memory usage from mLastDumpValue/mLastPurgeableDumpValue and
|
|
* accumulates in mCurrentValues[category]. It makes provision to create a new category and track
|
|
* purgeable memory only if there is at least one observation.
|
|
* recordAndResetCountersIfNeeded won't do anything until all the information for a given dumpName
|
|
* is received.
|
|
*/
|
|
void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) {
|
|
if (!mLastDumpName.compare(dumpName)) {
|
|
// Still waiting for more data for current dumpName.
|
|
return;
|
|
}
|
|
|
|
// First invocation will have an empty mLastDumpName.
|
|
if (!mLastDumpName.empty()) {
|
|
// A new dumpName observed -> store the data already collected.
|
|
auto memoryCounter = mCurrentValues.find(mCategory);
|
|
if (memoryCounter != mCurrentValues.end()) {
|
|
memoryCounter->second.memory += mLastDumpValue;
|
|
if (mLastPurgeableDumpValue != INVALID_MEMORY_SIZE) {
|
|
if (memoryCounter->second.purgeableMemory == INVALID_MEMORY_SIZE) {
|
|
memoryCounter->second.purgeableMemory = mLastPurgeableDumpValue;
|
|
} else {
|
|
memoryCounter->second.purgeableMemory += mLastPurgeableDumpValue;
|
|
}
|
|
}
|
|
} else {
|
|
mCurrentValues[mCategory] = {mLastDumpValue, mLastPurgeableDumpValue};
|
|
}
|
|
}
|
|
|
|
// Reset counters and default category for the newly observed "dumpName".
|
|
resetCurrentCounter(dumpName);
|
|
}
|
|
|
|
void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) {
|
|
mLastDumpValue = 0;
|
|
mLastPurgeableDumpValue = INVALID_MEMORY_SIZE;
|
|
mLastDumpName = dumpName;
|
|
// Categories not listed in sResourceMap are reported as "Misc Memory"
|
|
mCategory = "HWUI Misc Memory";
|
|
}
|
|
|
|
} /* namespace skiapipeline */
|
|
} /* namespace uirenderer */
|
|
} /* namespace android */
|