Track texture memory globally
Also mostly consolidates texture creation
Change-Id: Ifea01303afda531dcec99b8fe2a0f64cf2f24420
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
new file mode 100644
index 0000000..4fb5701
--- /dev/null
+++ b/libs/hwui/GpuMemoryTracker.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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;