/*
* Copyright (C) 2016 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 "utils/StringUtils.h"
#include "Texture.h"
#include <cutils/compiler.h>
#include <GpuMemoryTracker.h>
#include <utils/Trace.h>
#include <array>
#include <sstream>
#include <unordered_set>
#include <vector>
namespace android {
namespace uirenderer {
pthread_t gGpuThread = 0;
#define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount)
const char* TYPE_NAMES[] = {
"Texture",
"OffscreenBuffer",
"Layer",
};
struct TypeStats {
int totalSize = 0;
int count = 0;
};
static std::array<TypeStats, NUM_TYPES> gObjectStats;
static std::unordered_set<GpuMemoryTracker*> gObjectSet;
void GpuMemoryTracker::notifySizeChanged(int newSize) {
int delta = newSize - mSize;
mSize = newSize;
gObjectStats[static_cast<int>(mType)].totalSize += delta;
}
void GpuMemoryTracker::startTrackingObject() {
auto result = gObjectSet.insert(this);
LOG_ALWAYS_FATAL_IF(!result.second,
"startTrackingObject() on %p failed, already being tracked!", this);
gObjectStats[static_cast<int>(mType)].count++;
}
void GpuMemoryTracker::stopTrackingObject() {
size_t removed = gObjectSet.erase(this);
LOG_ALWAYS_FATAL_IF(removed != 1,
"stopTrackingObject removed %zd, is %p not being tracked?",
removed, this);
gObjectStats[static_cast<int>(mType)].count--;
}
void GpuMemoryTracker::onGLContextCreated() {
LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a GL thread? "
"current = %lu, gl thread = %lu", pthread_self(), gGpuThread);
gGpuThread = pthread_self();
}
void GpuMemoryTracker::onGLContextDestroyed() {
gGpuThread = 0;
if (CC_UNLIKELY(gObjectSet.size() > 0)) {
std::stringstream os;
dump(os);
ALOGE("%s", os.str().c_str());
LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size());
}
}
void GpuMemoryTracker::dump() {
std::stringstream strout;
dump(strout);
ALOGD("%s", strout.str().c_str());
}
void GpuMemoryTracker::dump(std::ostream& stream) {
for (int type = 0; type < NUM_TYPES; type++) {
const TypeStats& stats = gObjectStats[type];
stream << TYPE_NAMES[type];
stream << " is using " << SizePrinter{stats.totalSize};
stream << ", count = " << stats.count;
stream << std::endl;
}
}
int GpuMemoryTracker::getInstanceCount(GpuObjectType type) {
return gObjectStats[static_cast<int>(type)].count;
}
int GpuMemoryTracker::getTotalSize(GpuObjectType type) {
return gObjectStats[static_cast<int>(type)].totalSize;
}
void GpuMemoryTracker::onFrameCompleted() {
if (ATRACE_ENABLED()) {
char buf[128];
for (int type = 0; type < NUM_TYPES; type++) {
snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]);
const TypeStats& stats = gObjectStats[type];
ATRACE_INT(buf, stats.totalSize);
snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]);
ATRACE_INT(buf, stats.count);
}
}
std::vector<const Texture*> freeList;
for (const auto& obj : gObjectSet) {
if (obj->objectType() == GpuObjectType::Texture) {
const Texture* texture = static_cast<Texture*>(obj);
if (texture->cleanup) {
ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u",
texture->id(), texture->width(), texture->height());
freeList.push_back(texture);
}
}
}
for (auto& texture : freeList) {
const_cast<Texture*>(texture)->deleteTexture();
delete texture;
}
}
} // namespace uirenderer
} // namespace android;