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 "RenderThread.h" 21#include "renderstate/RenderState.h" 22 23#include <gui/Surface.h> 24#include <GrContextOptions.h> 25#include <math.h> 26#include <set> 27 28namespace android { 29namespace uirenderer { 30namespace renderthread { 31 32// This multiplier was selected based on historical review of cache sizes relative 33// to the screen resolution. This is meant to be a conservative default based on 34// that analysis. The 4.0f is used because the default pixel format is assumed to 35// be ARGB_8888. 36#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) 37#define BACKGROUND_RETENTION_PERCENTAGE (0.5f) 38 39// for super large fonts we will draw them as paths so no need to keep linearly 40// increasing the font cache size. 41#define FONT_CACHE_MIN_MB (0.5f) 42#define FONT_CACHE_MAX_MB (4.0f) 43 44CacheManager::CacheManager(const DisplayInfo& display) 45 : mMaxSurfaceArea(display.w * display.h) { 46 mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2, 47 skiapipeline::VectorDrawableAtlas::StorageMode::allowSharedSurface); 48} 49 50void CacheManager::reset(GrContext* context) { 51 if (context != mGrContext.get()) { 52 destroy(); 53 } 54 55 if (context) { 56 mGrContext = sk_ref_sp(context); 57 mGrContext->getResourceCacheLimits(&mMaxResources, nullptr); 58 updateContextCacheSizes(); 59 } 60} 61 62void CacheManager::destroy() { 63 // cleanup any caches here as the GrContext is about to go away... 64 mGrContext.reset(nullptr); 65 mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2); 66} 67 68void CacheManager::updateContextCacheSizes() { 69 mMaxResourceBytes = mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER; 70 mBackgroundResourceBytes = mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE; 71 72 mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); 73} 74 75void CacheManager::configureContext(GrContextOptions* contextOptions) { 76 contextOptions->fAllowPathMaskCaching = true; 77 78 float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f; 79 float fontCacheMB = 0; 80 float decimalVal = std::modf(screenMP, &fontCacheMB); 81 82 // This is a basic heuristic to size the cache to a multiple of 512 KB 83 if (decimalVal > 0.8f) { 84 fontCacheMB += 1.0f; 85 } else if (decimalVal > 0.5f) { 86 fontCacheMB += 0.5f; 87 } 88 89 // set limits on min/max size of the cache 90 fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB)); 91 92 // We must currently set the size of the text cache based on the size of the 93 // display even though we like to be dynamicallysizing it to the size of the window. 94 // Skia's implementation doesn't provide a mechanism to resize the font cache due to 95 // the potential cost of recreating the glyphs. 96 contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024; 97} 98 99void CacheManager::trimMemory(TrimMemoryMode mode) { 100 if (!mGrContext) { 101 return; 102 } 103 104 mGrContext->flush(); 105 106 switch (mode) { 107 case TrimMemoryMode::Complete: 108 mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2); 109 mGrContext->freeGpuResources(); 110 break; 111 case TrimMemoryMode::UiHidden: 112 mGrContext->purgeUnlockedResources(mMaxResourceBytes - mBackgroundResourceBytes, true); 113 break; 114 } 115} 116 117void CacheManager::trimStaleResources() { 118 if (!mGrContext) { 119 return; 120 } 121 mGrContext->flush(); 122 mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); 123} 124 125sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() { 126 LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr); 127 LOG_ALWAYS_FATAL_IF(mGrContext == nullptr); 128 129 /** 130 * TODO: define memory conditions where we clear the cache (e.g. surface->reset()) 131 */ 132 return mVectorDrawableAtlas; 133} 134 135void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) { 136 if (!mGrContext) { 137 log.appendFormat("No valid cache instance.\n"); 138 return; 139 } 140 141 size_t bytesCached; 142 mGrContext->getResourceCacheUsage(nullptr, &bytesCached); 143 144 log.appendFormat("Caches:\n"); 145 log.appendFormat(" Current / Maximum\n"); 146 log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f kB (entries = %zu)\n", 147 0.0f, 0.0f, (size_t)0); 148 149 if (renderState) { 150 if (renderState->mActiveLayers.size() > 0) { 151 log.appendFormat(" Layer Info:\n"); 152 } 153 154 size_t layerMemoryTotal = 0; 155 for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin(); 156 it != renderState->mActiveLayers.end(); it++) { 157 const Layer* layer = *it; 158 const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; 159 log.appendFormat(" %s size %dx%d\n", layerType, 160 layer->getWidth(), layer->getHeight()); 161 layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; 162 } 163 log.appendFormat(" Layers Total %6.2f kB (numLayers = %zu)\n", 164 layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size()); 165 } 166 167 168 log.appendFormat("Total memory usage:\n"); 169 log.appendFormat(" %zu bytes, %.2f MB (%.2f MB is purgeable)\n", 170 bytesCached, bytesCached / 1024.0f / 1024.0f, 171 mGrContext->getResourceCachePurgeableBytes() / 1024.0f / 1024.0f); 172 173 174} 175 176} /* namespace renderthread */ 177} /* namespace uirenderer */ 178} /* namespace android */ 179