1/* 2 * Copyright (C) 2017 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 "CacheManager.h" 18 19#include "Layer.h" 20#include "Properties.h" 21#include "RenderThread.h" 22#include "pipeline/skia/ShaderCache.h" 23#include "pipeline/skia/SkiaMemoryTracer.h" 24#include "renderstate/RenderState.h" 25 26#include <GrContextOptions.h> 27#include <SkExecutor.h> 28#include <SkGraphics.h> 29#include <gui/Surface.h> 30#include <math.h> 31#include <set> 32 33namespace android { 34namespace uirenderer { 35namespace renderthread { 36 37// This multiplier was selected based on historical review of cache sizes relative 38// to the screen resolution. This is meant to be a conservative default based on 39// that analysis. The 4.0f is used because the default pixel format is assumed to 40// be ARGB_8888. 41#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) 42#define BACKGROUND_RETENTION_PERCENTAGE (0.5f) 43 44// for super large fonts we will draw them as paths so no need to keep linearly 45// increasing the font cache size. 46#define FONT_CACHE_MIN_MB (0.5f) 47#define FONT_CACHE_MAX_MB (4.0f) 48 49CacheManager::CacheManager(const DisplayInfo& display) : mMaxSurfaceArea(display.w * display.h) { 50 mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( 51 mMaxSurfaceArea / 2, 52 skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); 53 if (Properties::isSkiaEnabled()) { 54 skiapipeline::ShaderCache::get().initShaderDiskCache(); 55 } 56} 57 58void CacheManager::reset(sk_sp<GrContext> context) { 59 if (context != mGrContext) { 60 destroy(); 61 } 62 63 if (context) { 64 mGrContext = std::move(context); 65 mGrContext->getResourceCacheLimits(&mMaxResources, nullptr); 66 updateContextCacheSizes(); 67 } 68} 69 70void CacheManager::destroy() { 71 // cleanup any caches here as the GrContext is about to go away... 72 mGrContext.reset(nullptr); 73 mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( 74 mMaxSurfaceArea / 2, 75 skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); 76} 77 78void CacheManager::updateContextCacheSizes() { 79 mMaxResourceBytes = mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER; 80 mBackgroundResourceBytes = mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE; 81 82 mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); 83} 84 85class CacheManager::SkiaTaskProcessor : public TaskProcessor<bool>, public SkExecutor { 86public: 87 explicit SkiaTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} 88 89 // This is really a Task<void> but that doesn't really work when Future<> 90 // expects to be able to get/set a value 91 struct SkiaTask : public Task<bool> { 92 std::function<void()> func; 93 }; 94 95 virtual void add(std::function<void(void)> func) override { 96 sp<SkiaTask> task(new SkiaTask()); 97 task->func = func; 98 TaskProcessor<bool>::add(task); 99 } 100 101 virtual void onProcess(const sp<Task<bool> >& task) override { 102 SkiaTask* t = static_cast<SkiaTask*>(task.get()); 103 t->func(); 104 task->setResult(true); 105 } 106}; 107 108void CacheManager::configureContext(GrContextOptions* contextOptions) { 109 contextOptions->fAllowPathMaskCaching = true; 110 111 float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f; 112 float fontCacheMB = 0; 113 float decimalVal = std::modf(screenMP, &fontCacheMB); 114 115 // This is a basic heuristic to size the cache to a multiple of 512 KB 116 if (decimalVal > 0.8f) { 117 fontCacheMB += 1.0f; 118 } else if (decimalVal > 0.5f) { 119 fontCacheMB += 0.5f; 120 } 121 122 // set limits on min/max size of the cache 123 fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB)); 124 125 // We must currently set the size of the text cache based on the size of the 126 // display even though we like to be dynamicallysizing it to the size of the window. 127 // Skia's implementation doesn't provide a mechanism to resize the font cache due to 128 // the potential cost of recreating the glyphs. 129 contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024; 130 131 if (mTaskManager.canRunTasks()) { 132 if (!mTaskProcessor.get()) { 133 mTaskProcessor = new SkiaTaskProcessor(&mTaskManager); 134 } 135 contextOptions->fExecutor = mTaskProcessor.get(); 136 } 137 138 contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get(); 139} 140 141void CacheManager::trimMemory(TrimMemoryMode mode) { 142 if (!mGrContext) { 143 return; 144 } 145 146 mGrContext->flush(); 147 148 switch (mode) { 149 case TrimMemoryMode::Complete: 150 mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea / 2); 151 mGrContext->freeGpuResources(); 152 break; 153 case TrimMemoryMode::UiHidden: 154 // Here we purge all the unlocked scratch resources and then toggle the resources cache 155 // limits between the background and max amounts. This causes the unlocked resources 156 // that have persistent data to be purged in LRU order. 157 mGrContext->purgeUnlockedResources(true); 158 mGrContext->setResourceCacheLimits(mMaxResources, mBackgroundResourceBytes); 159 mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); 160 break; 161 } 162} 163 164void CacheManager::trimStaleResources() { 165 if (!mGrContext) { 166 return; 167 } 168 mGrContext->flush(); 169 mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); 170} 171 172sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() { 173 LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr); 174 LOG_ALWAYS_FATAL_IF(mGrContext == nullptr); 175 176 /** 177 * TODO: define memory conditions where we clear the cache (e.g. surface->reset()) 178 */ 179 return mVectorDrawableAtlas; 180} 181 182void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) { 183 if (!mGrContext) { 184 log.appendFormat("No valid cache instance.\n"); 185 return; 186 } 187 188 log.appendFormat("Font Cache (CPU):\n"); 189 log.appendFormat(" Size: %.2f kB \n", SkGraphics::GetFontCacheUsed() / 1024.0f); 190 log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed()); 191 192 log.appendFormat("CPU Caches:\n"); 193 std::vector<skiapipeline::ResourcePair> cpuResourceMap = { 194 {"skia/sk_resource_cache/bitmap_", "Bitmaps"}, 195 {"skia/sk_resource_cache/rrect-blur_", "Masks"}, 196 {"skia/sk_resource_cache/rects-blur_", "Masks"}, 197 {"skia/sk_resource_cache/tessellated", "Shadows"}, 198 }; 199 skiapipeline::SkiaMemoryTracer cpuTracer(cpuResourceMap, false); 200 SkGraphics::DumpMemoryStatistics(&cpuTracer); 201 cpuTracer.logOutput(log); 202 203 log.appendFormat("GPU Caches:\n"); 204 skiapipeline::SkiaMemoryTracer gpuTracer("category", true); 205 mGrContext->dumpMemoryStatistics(&gpuTracer); 206 gpuTracer.logOutput(log); 207 208 log.appendFormat("Other Caches:\n"); 209 log.appendFormat(" Current / Maximum\n"); 210 log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f KB (entries = %zu)\n", 0.0f, 0.0f, 211 (size_t)0); 212 213 if (renderState) { 214 if (renderState->mActiveLayers.size() > 0) { 215 log.appendFormat(" Layer Info:\n"); 216 } 217 218 size_t layerMemoryTotal = 0; 219 for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin(); 220 it != renderState->mActiveLayers.end(); it++) { 221 const Layer* layer = *it; 222 const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; 223 log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), 224 layer->getHeight()); 225 layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; 226 } 227 log.appendFormat(" Layers Total %6.2f KB (numLayers = %zu)\n", 228 layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size()); 229 } 230 231 log.appendFormat("Total GPU memory usage:\n"); 232 gpuTracer.logTotals(log); 233} 234 235} /* namespace renderthread */ 236} /* namespace uirenderer */ 237} /* namespace android */ 238