| /* |
| * 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::onGpuContextCreated() { |
| LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a gpu thread? " |
| "current = %lu, gpu thread = %lu", pthread_self(), gGpuThread); |
| gGpuThread = pthread_self(); |
| } |
| |
| void GpuMemoryTracker::onGpuContextDestroyed() { |
| 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; |