1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "Texture.h"
18 #include "utils/StringUtils.h"
19
20 #include <GpuMemoryTracker.h>
21 #include <cutils/compiler.h>
22 #include <utils/Trace.h>
23 #include <array>
24 #include <sstream>
25 #include <unordered_set>
26 #include <vector>
27
28 namespace android {
29 namespace uirenderer {
30
31 pthread_t gGpuThread = 0;
32
33 #define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount)
34
35 const char* TYPE_NAMES[] = {
36 "Texture", "OffscreenBuffer", "Layer",
37 };
38
39 struct TypeStats {
40 int totalSize = 0;
41 int count = 0;
42 };
43
44 static std::array<TypeStats, NUM_TYPES> gObjectStats;
45 static std::unordered_set<GpuMemoryTracker*> gObjectSet;
46
notifySizeChanged(int newSize)47 void GpuMemoryTracker::notifySizeChanged(int newSize) {
48 int delta = newSize - mSize;
49 mSize = newSize;
50 gObjectStats[static_cast<int>(mType)].totalSize += delta;
51 }
52
startTrackingObject()53 void GpuMemoryTracker::startTrackingObject() {
54 auto result = gObjectSet.insert(this);
55 LOG_ALWAYS_FATAL_IF(!result.second,
56 "startTrackingObject() on %p failed, already being tracked!", this);
57 gObjectStats[static_cast<int>(mType)].count++;
58 }
59
stopTrackingObject()60 void GpuMemoryTracker::stopTrackingObject() {
61 size_t removed = gObjectSet.erase(this);
62 LOG_ALWAYS_FATAL_IF(removed != 1, "stopTrackingObject removed %zd, is %p not being tracked?",
63 removed, this);
64 gObjectStats[static_cast<int>(mType)].count--;
65 }
66
onGpuContextCreated()67 void GpuMemoryTracker::onGpuContextCreated() {
68 LOG_ALWAYS_FATAL_IF(gGpuThread != 0,
69 "We already have a gpu thread? "
70 "current = %lu, gpu thread = %lu",
71 pthread_self(), gGpuThread);
72 gGpuThread = pthread_self();
73 }
74
onGpuContextDestroyed()75 void GpuMemoryTracker::onGpuContextDestroyed() {
76 gGpuThread = 0;
77 if (CC_UNLIKELY(gObjectSet.size() > 0)) {
78 std::stringstream os;
79 dump(os);
80 ALOGE("%s", os.str().c_str());
81 LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size());
82 }
83 }
84
dump()85 void GpuMemoryTracker::dump() {
86 std::stringstream strout;
87 dump(strout);
88 ALOGD("%s", strout.str().c_str());
89 }
90
dump(std::ostream & stream)91 void GpuMemoryTracker::dump(std::ostream& stream) {
92 for (int type = 0; type < NUM_TYPES; type++) {
93 const TypeStats& stats = gObjectStats[type];
94 stream << TYPE_NAMES[type];
95 stream << " is using " << SizePrinter{stats.totalSize};
96 stream << ", count = " << stats.count;
97 stream << std::endl;
98 }
99 }
100
getInstanceCount(GpuObjectType type)101 int GpuMemoryTracker::getInstanceCount(GpuObjectType type) {
102 return gObjectStats[static_cast<int>(type)].count;
103 }
104
getTotalSize(GpuObjectType type)105 int GpuMemoryTracker::getTotalSize(GpuObjectType type) {
106 return gObjectStats[static_cast<int>(type)].totalSize;
107 }
108
onFrameCompleted()109 void GpuMemoryTracker::onFrameCompleted() {
110 if (ATRACE_ENABLED()) {
111 char buf[128];
112 for (int type = 0; type < NUM_TYPES; type++) {
113 snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]);
114 const TypeStats& stats = gObjectStats[type];
115 ATRACE_INT(buf, stats.totalSize);
116 snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]);
117 ATRACE_INT(buf, stats.count);
118 }
119 }
120
121 std::vector<const Texture*> freeList;
122 for (const auto& obj : gObjectSet) {
123 if (obj->objectType() == GpuObjectType::Texture) {
124 const Texture* texture = static_cast<Texture*>(obj);
125 if (texture->cleanup) {
126 ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(),
127 texture->width(), texture->height());
128 freeList.push_back(texture);
129 }
130 }
131 }
132 for (auto& texture : freeList) {
133 const_cast<Texture*>(texture)->deleteTexture();
134 delete texture;
135 }
136 }
137
138 } // namespace uirenderer
139 } // namespace android;
140