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

Commit 220e9018 authored by Derek Sollenberger's avatar Derek Sollenberger Committed by Android (Google) Code Review
Browse files

Merge "Add additional logging to RenderEngine regarding Skia's cache contents" into sc-dev

parents d84eb5e0 0e6d356a
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -91,6 +91,7 @@ filegroup {
        "skia/debug/CaptureTimer.cpp",
        "skia/debug/CaptureTimer.cpp",
        "skia/debug/CommonPool.cpp",
        "skia/debug/CommonPool.cpp",
        "skia/debug/SkiaCapture.cpp",
        "skia/debug/SkiaCapture.cpp",
        "skia/debug/SkiaMemoryReporter.cpp",
        "skia/filters/BlurFilter.cpp",
        "skia/filters/BlurFilter.cpp",
        "skia/filters/LinearEffect.cpp",
        "skia/filters/LinearEffect.cpp",
    ],
    ],
+43 −3
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@
#include <SkColorFilter.h>
#include <SkColorFilter.h>
#include <SkColorMatrix.h>
#include <SkColorMatrix.h>
#include <SkColorSpace.h>
#include <SkColorSpace.h>
#include <SkGraphics.h>
#include <SkImage.h>
#include <SkImage.h>
#include <SkImageFilters.h>
#include <SkImageFilters.h>
#include <SkRegion.h>
#include <SkRegion.h>
@@ -40,13 +41,13 @@
#include <ui/DebugUtils.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
#include <utils/Trace.h>
#include "Cache.h"


#include <cmath>
#include <cmath>
#include <cstdint>
#include <cstdint>
#include <memory>
#include <memory>


#include "../gl/GLExtensions.h"
#include "../gl/GLExtensions.h"
#include "Cache.h"
#include "ColorSpaces.h"
#include "ColorSpaces.h"
#include "SkBlendMode.h"
#include "SkBlendMode.h"
#include "SkImageInfo.h"
#include "SkImageInfo.h"
@@ -54,6 +55,7 @@
#include "filters/LinearEffect.h"
#include "filters/LinearEffect.h"
#include "log/log_main.h"
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
#include "skia/debug/SkiaCapture.h"
#include "skia/debug/SkiaMemoryReporter.h"
#include "system/graphics-base-v1.0.h"
#include "system/graphics-base-v1.0.h"


namespace {
namespace {
@@ -1249,9 +1251,38 @@ void SkiaGLRenderEngine::dump(std::string& result) {
    StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
    StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
                  mSkSLCacheMonitor.shadersCachedSinceLastCall());
                  mSkSLCacheMonitor.shadersCachedSinceLastCall());


    std::vector<ResourcePair> cpuResourceMap = {
            {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
            {"skia/sk_resource_cache/rrect-blur_", "Masks"},
            {"skia/sk_resource_cache/rects-blur_", "Masks"},
            {"skia/sk_resource_cache/tessellated", "Shadows"},
            {"skia", "Other"},
    };
    SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
    SkGraphics::DumpMemoryStatistics(&cpuReporter);
    StringAppendF(&result, "Skia CPU Caches: ");
    cpuReporter.logTotals(result);
    cpuReporter.logOutput(result);

    {
    {
        std::lock_guard<std::mutex> lock(mRenderingMutex);
        std::lock_guard<std::mutex> lock(mRenderingMutex);
        StringAppendF(&result, "RenderEngine texture cache size: %zu\n", mTextureCache.size());

        std::vector<ResourcePair> gpuResourceMap = {
                {"texture_renderbuffer", "Texture/RenderBuffer"},
                {"texture", "Texture"},
                {"gr_text_blob_cache", "Text"},
                {"skia", "Other"},
        };
        SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
        mGrContext->dumpMemoryStatistics(&gpuReporter);
        StringAppendF(&result, "Skia's GPU Caches: ");
        gpuReporter.logTotals(result);
        gpuReporter.logOutput(result);
        StringAppendF(&result, "Skia's Wrapped Objects:\n");
        gpuReporter.logOutput(result, true);

        StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
                      mTextureCache.size());
        StringAppendF(&result, "Dumping buffer ids...\n");
        StringAppendF(&result, "Dumping buffer ids...\n");
        // TODO(178539829): It would be nice to know which layer these are coming from and what
        // TODO(178539829): It would be nice to know which layer these are coming from and what
        // the texture sizes are.
        // the texture sizes are.
@@ -1259,7 +1290,16 @@ void SkiaGLRenderEngine::dump(std::string& result) {
            StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
            StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
        }
        }
        StringAppendF(&result, "\n");
        StringAppendF(&result, "\n");
        StringAppendF(&result, "RenderEngine protected texture cache size: %zu\n",

        SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
        mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
        StringAppendF(&result, "Skia's GPU Protected Caches: ");
        gpuProtectedReporter.logTotals(result);
        gpuProtectedReporter.logOutput(result);
        StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
        gpuProtectedReporter.logOutput(result, true);

        StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n",
                      mProtectedTextureCache.size());
                      mProtectedTextureCache.size());
        StringAppendF(&result, "Dumping buffer ids...\n");
        StringAppendF(&result, "Dumping buffer ids...\n");
        for (const auto& [id, unused] : mProtectedTextureCache) {
        for (const auto& [id, unused] : mProtectedTextureCache) {
+205 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */
#undef LOG_TAG
#define LOG_TAG "RenderEngine"

#include "SkiaMemoryReporter.h"

#include <SkString.h>
#include <android-base/stringprintf.h>
#include <log/log_main.h>

namespace android {
namespace renderengine {
namespace skia {

using base::StringAppendF;

SkiaMemoryReporter::SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize)
      : mResourceMap(resourceMap),
        mItemize(itemize),
        mTotalSize("bytes", 0),
        mPurgeableSize("bytes", 0) {}

const char* SkiaMemoryReporter::mapName(const char* resourceName) {
    for (auto& resource : mResourceMap) {
        if (SkStrContains(resourceName, resource.first)) {
            return resource.second;
        }
    }
    return nullptr;
}

void SkiaMemoryReporter::resetCurrentElement() {
    mCurrentElement.clear();
    mCurrentValues.clear();
    mIsCurrentValueWrapped = false;
}

void SkiaMemoryReporter::processCurrentElement() {
    // compute the top level element name using the map
    const char* resourceName = mCurrentElement.empty() ? nullptr : mapName(mCurrentElement.c_str());

    // if we don't have a resource name then we don't know how to label the
    // data and should abort.
    if (resourceName == nullptr) {
        resetCurrentElement();
        return;
    }

    // Only count elements that contain "size"; other values just provide metadata.
    auto sizeResult = mCurrentValues.find("size");
    if (sizeResult != mCurrentValues.end() && sizeResult->second.value > 0) {
        if (!mIsCurrentValueWrapped) {
            mTotalSize.value += sizeResult->second.value;
            mTotalSize.count++;
        }
    } else {
        resetCurrentElement();
        return;
    }

    // find the purgeable size if one exists
    auto purgeableResult = mCurrentValues.find("purgeable_size");
    if (!mIsCurrentValueWrapped && purgeableResult != mCurrentValues.end()) {
        mPurgeableSize.value += purgeableResult->second.value;
        mPurgeableSize.count++;
    }

    // do we store this element in the wrapped list or the skia managed list
    auto& results = mIsCurrentValueWrapped ? mWrappedResults : mResults;

    // insert a copy of the element and all of its keys. We must make a copy here instead of
    // std::move() as we will continue to use these values later in the function and again
    // when we move on to process the next element.
    results.insert({mCurrentElement, mCurrentValues});

    // insert the item into its mapped category
    auto result = results.find(resourceName);
    if (result != results.end()) {
        auto& resourceValues = result->second;
        auto totalResult = resourceValues.find(sizeResult->first);
        if (totalResult != resourceValues.end()) {
            ALOGE_IF(sizeResult->second.units != totalResult->second.units,
                     "resource units do not match so the sum of resource type (%s) will be invalid",
                     resourceName);
            totalResult->second.value += sizeResult->second.value;
            totalResult->second.count++;
        } else {
            ALOGE("an entry (%s) should not exist in the results without a size", resourceName);
        }
    } else {
        // only store the size for the top level resource
        results.insert({resourceName, {{sizeResult->first, sizeResult->second}}});
    }

    resetCurrentElement();
}

void SkiaMemoryReporter::dumpNumericValue(const char* dumpName, const char* valueName,
                                          const char* units, uint64_t value) {
    if (mCurrentElement != dumpName) {
        processCurrentElement();
        mCurrentElement = dumpName;
    }
    mCurrentValues.insert({valueName, {units, value}});
}

void SkiaMemoryReporter::dumpWrappedState(const char* dumpName, bool isWrappedObject) {
    if (mCurrentElement != dumpName) {
        processCurrentElement();
        mCurrentElement = dumpName;
    }
    mIsCurrentValueWrapped = isWrappedObject;
}

void SkiaMemoryReporter::logOutput(std::string& log, bool wrappedResources) {
    // process the current element before logging
    processCurrentElement();

    const auto& resultsMap = wrappedResources ? mWrappedResults : mResults;

    // log each individual element based on the resource map
    for (const auto& resourceCategory : mResourceMap) {
        // find the named item and print the totals
        const auto categoryItem = resultsMap.find(resourceCategory.second);
        if (categoryItem != resultsMap.end()) {
            auto result = categoryItem->second.find("size");
            if (result != categoryItem->second.end()) {
                TraceValue traceValue = convertUnits(result->second);
                const char* entry = (traceValue.count > 1) ? "entries" : "entry";
                StringAppendF(&log, "  %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
                              traceValue.value, traceValue.units, traceValue.count, entry);
            }
            if (mItemize) {
                for (const auto& individualItem : resultsMap) {
                    // if the individual item matches the category then print all its details or
                    // in the case of wrapped resources just print the wrapped size
                    const char* categoryMatch = mapName(individualItem.first.c_str());
                    if (categoryMatch && strcmp(categoryMatch, resourceCategory.second) == 0) {
                        auto result = individualItem.second.find("size");
                        TraceValue size = convertUnits(result->second);
                        StringAppendF(&log, "    %s: size[%.2f %s]", individualItem.first.c_str(),
                                      size.value, size.units);
                        if (!wrappedResources) {
                            for (const auto& itemValues : individualItem.second) {
                                if (strcmp("size", itemValues.first) == 0) {
                                    continue;
                                }
                                TraceValue traceValue = convertUnits(itemValues.second);
                                if (traceValue.value == 0.0f) {
                                    StringAppendF(&log, " %s[%s]", itemValues.first,
                                                  traceValue.units);
                                } else {
                                    StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
                                                  traceValue.value, traceValue.units);
                                }
                            }
                        }
                        StringAppendF(&log, "\n");
                    }
                }
            }
        }
    }
}

void SkiaMemoryReporter::logTotals(std::string& log) {
    // process the current element before logging
    processCurrentElement();

    TraceValue total = convertUnits(mTotalSize);
    TraceValue purgeable = convertUnits(mPurgeableSize);
    StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
                  total.value, total.units, purgeable.value, purgeable.units);
}

SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
    TraceValue output(value);
    if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
        output.value = output.value / 1024.0f;
        output.units = "KB";
    }
    if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
        output.value = output.value / 1024.0f;
        output.units = "MB";
    }
    return output;
}

} /* namespace skia */
} /* namespace renderengine */
} /* namespace android */
+107 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

#pragma once

#include <SkTraceMemoryDump.h>

#include <string>
#include <unordered_map>
#include <vector>

namespace android {
namespace renderengine {
namespace skia {

// Mapping of resource substrings (1st element) that if found within a trace "dumpName"
// should be mapped to the category name (2nd element). All char* used in a resourcePair
// are expected to have a lifetime longer than the SkiaMemoryReporter in which they are used.
typedef std::pair<const char*, const char*> ResourcePair;

/*
 * Utility class for logging the CPU/GPU usage of Skia caches in a format that is specific
 * to RenderEngine.  HWUI has a similar logging class, but the data collected and the way
 * it is formatted and reported on are intended to be unique to each use case.
 */
class SkiaMemoryReporter : public SkTraceMemoryDump {
public:
    /**
     * Creates the reporter class that can be populated by various Skia entry points, like
     * SkGraphics and GrContext, as well as format and log the results.
     * @param resourceMap An array of values that maps a Skia dumpName into a user defined category.
     *                    The first vector entry that matches the dumpName is used for the mapping.
     * @param itemize if true when logging the categories the individual elements will be printed
     *                directly after the category details are printed.  Otherwise, only the category
     *                totals will be printed.
     */
    SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize);
    ~SkiaMemoryReporter() override {}

    void logOutput(std::string& log, bool wrappedResources = false);
    void logTotals(std::string& log);

    void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
                          uint64_t value) override;

    void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override {
        // for convenience we just store this in the same format as numerical values
        dumpNumericValue(dumpName, valueName, value, 0);
    }
    void dumpWrappedState(const char* dumpName, bool isWrappedObject) override;

    LevelOfDetail getRequestedDetails() const override {
        return SkTraceMemoryDump::kLight_LevelOfDetail;
    }

    bool shouldDumpWrappedObjects() const override { return true; }
    void setMemoryBacking(const char*, const char*, const char*) override {}
    void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}

private:
    struct TraceValue {
        TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {}
        TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {}

        const char* units;
        float value;
        int count;
    };

    const char* mapName(const char* resourceName);
    void processCurrentElement();
    void resetCurrentElement();
    TraceValue convertUnits(const TraceValue& value);

    const std::vector<ResourcePair>& mResourceMap;
    const bool mItemize;

    // variables storing the size of all non-wrapped elements being dumped
    TraceValue mTotalSize;
    TraceValue mPurgeableSize;

    // variables storing information on the current node being dumped
    std::string mCurrentElement;
    std::unordered_map<const char*, TraceValue> mCurrentValues;
    bool mIsCurrentValueWrapped = false;

    // variable that stores the final format of the data after the individual elements are processed
    std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mResults;
    std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mWrappedResults;
};

} /* namespace skia */
} /* namespace renderengine */
} /* namespace android */
 No newline at end of file