Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2eb7845 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add memory tracing in HWUI"

parents e40ad05c e0fae235
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -215,6 +215,7 @@ cc_defaults {
        android: {

            srcs: [
                "pipeline/skia/ATraceMemoryDump.cpp",
                "pipeline/skia/GLFunctorDrawable.cpp",
                "pipeline/skia/LayerDrawable.cpp",
                "pipeline/skia/ShaderCache.cpp",
@@ -244,7 +245,6 @@ cc_defaults {
                "DeviceInfo.cpp",
                "FrameInfo.cpp",
                "FrameInfoVisualizer.cpp",
                "GpuMemoryTracker.cpp",
                "HardwareBitmapUploader.cpp",
                "HWUIProperties.sysprop",
                "JankTracker.cpp",
@@ -325,7 +325,6 @@ cc_test {
        "tests/unit/DamageAccumulatorTests.cpp",
        "tests/unit/DeferredLayerUpdaterTests.cpp",
        "tests/unit/FatVectorTests.cpp",
        "tests/unit/GpuMemoryTrackerTests.cpp",
        "tests/unit/GraphicsStatsServiceTests.cpp",
        "tests/unit/LayerUpdateQueueTests.cpp",
        "tests/unit/LinearAllocatorTests.cpp",

libs/hwui/GpuMemoryTracker.cpp

deleted100644 → 0
+0 −122
Original line number Diff line number Diff line
/*
 * 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 <GpuMemoryTracker.h>
#include <cutils/compiler.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);
        }
    }
}

}  // namespace uirenderer
}  // namespace android;
+175 −0
Original line number Diff line number Diff line
/*
 * 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_TIME it won't be logged at all.
#define INVALID_TIME -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
 * "GPU Memory" category.
 */
static std::unordered_map<const char*, const char*> sResourceMap = {
        {"malloc", "Graphics CPU Memory"},          // taken from setMemoryBacking(backingType)
        {"gl_texture", "Graphics Texture Memory"},  // taken from setMemoryBacking(backingType)
        {"Texture",
         "Graphics Texture Memory"},  // taken from dumpStringValue(value, valueName="type")
        // Uncomment categories below to split "GPU 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.time = 0;
        // If purgeableTime is INVALID_TIME, then logTraces won't log it at all.
        if (it.second.purgeableTime != INVALID_TIME) {
            it.second.purgeableTime = 0;
        }
    }
}

/**
 * logTraces reads from mCurrentValues and logs the counters with ATRACE.
 */
void ATraceMemoryDump::logTraces() {
    // Accumulate data from last dumpName
    recordAndResetCountersIfNeeded("");
    for (auto& it : mCurrentValues) {
        ATRACE_INT64(it.first.c_str(), it.second.time);
        if (it.second.purgeableTime != INVALID_TIME) {
            ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime);
        }
    }
}

/**
 * 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.time += mLastDumpValue;
            if (mLastPurgeableDumpValue != INVALID_TIME) {
                if (memoryCounter->second.purgeableTime == INVALID_TIME) {
                    memoryCounter->second.purgeableTime = mLastPurgeableDumpValue;
                } else {
                    memoryCounter->second.purgeableTime += 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_TIME;
    mLastDumpName = dumpName;
    // Categories not listed in sResourceMap are reported as "GPU memory"
    mCategory = "GPU Memory";
}

} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 * 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.
@@ -13,65 +13,67 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <pthread.h>
#include <ostream>
#include <SkString.h>
#include <SkTraceMemoryDump.h>

#include <log/log.h>
#include <string>
#include <unordered_map>
#include <utility>

namespace android {
namespace uirenderer {
namespace skiapipeline {

extern pthread_t gGpuThread;

#define ASSERT_GPU_THREAD()                                                                    \
    LOG_ALWAYS_FATAL_IF(!pthread_equal(gGpuThread, pthread_self()),                            \
                        "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \
                        "!= gpu thread %lu",                                                   \
                        this, static_cast<int>(mType), mSize, pthread_self(), gGpuThread)
class ATraceMemoryDump : public SkTraceMemoryDump {
public:
    ATraceMemoryDump();
    ~ATraceMemoryDump() override {}

enum class GpuObjectType {
    Texture = 0,
    OffscreenBuffer,
    Layer,
    void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
                          uint64_t value) override;

    TypeCount,
};
    void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override;

class GpuMemoryTracker {
public:
    GpuObjectType objectType() { return mType; }
    int objectSize() { return mSize; }

    static void onGpuContextCreated();
    static void onGpuContextDestroyed();
    static void dump();
    static void dump(std::ostream& stream);
    static int getInstanceCount(GpuObjectType type);
    static int getTotalSize(GpuObjectType type);
    static void onFrameCompleted();

protected:
    explicit GpuMemoryTracker(GpuObjectType type) : mType(type) {
        ASSERT_GPU_THREAD();
        startTrackingObject();
    LevelOfDetail getRequestedDetails() const override {
        return SkTraceMemoryDump::kLight_LevelOfDetail;
    }

    ~GpuMemoryTracker() {
        notifySizeChanged(0);
        stopTrackingObject();
    }
    bool shouldDumpWrappedObjects() const override { return false; }

    void setMemoryBacking(const char* dumpName, const char* backingType,
                          const char* backingObjectId) override;

    void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}

    void notifySizeChanged(int newSize);
    void startFrame();

    void logTraces();

private:
    void startTrackingObject();
    void stopTrackingObject();
    std::string mLastDumpName;

    uint64_t mLastDumpValue;

    uint64_t mLastPurgeableDumpValue;

    std::string mCategory;

    struct TraceValue {
        uint64_t time;
        uint64_t purgeableTime;
    };

    // keys are define in sResourceMap
    std::unordered_map<std::string, TraceValue> mCurrentValues;

    void recordAndResetCountersIfNeeded(const char* dumpName);

    int mSize = 0;
    GpuObjectType mType;
    void resetCurrentCounter(const char* dumpName);
};

}  // namespace uirenderer
}  // namespace android;
} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */
 No newline at end of file
+0 −6
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@
#include "renderstate/RenderState.h"

#include "renderthread/RenderThread.h"
#include "GpuMemoryTracker.h"

namespace android {
namespace uirenderer {
@@ -25,15 +24,10 @@ RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thr
    mThreadId = pthread_self();
}

void RenderState::onContextCreated() {
    GpuMemoryTracker::onGpuContextCreated();
}

void RenderState::onContextDestroyed() {
    for(auto callback : mContextCallbacks) {
        callback->onContextDestroyed();
    }
    GpuMemoryTracker::onGpuContextDestroyed();
}

void RenderState::postDecStrong(VirtualLightRefBase* object) {
Loading