GradientCache.cpp revision 38e0c32852e3b9d8ca4a9d3791577f52536419cb
116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood/* 216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * Copyright (C) 2010 The Android Open Source Project 316864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * 416864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License"); 516864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * you may not use this file except in compliance with the License. 616864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * You may obtain a copy of the License at 716864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * 816864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * http://www.apache.org/licenses/LICENSE-2.0 916864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * 1016864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * Unless required by applicable law or agreed to in writing, software 1116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS, 1216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1316864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * See the License for the specific language governing permissions and 1416864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood * limitations under the License. 1516864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood */ 1616864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 1716864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood#include <utils/JenkinsHash.h> 1816864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 1916864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood#include "Caches.h" 2016864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood#include "Debug.h" 2116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood#include "GradientCache.h" 2216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood#include "Properties.h" 23487be61fb0a38873aec1d12da92437fba5e728f2Jerry Zhang 2442d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwood#include <cutils/properties.h> 2542d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwood 2642d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwoodnamespace android { 277850ef999740f214a1990a9c090d3f3865d435aaMike Lockwoodnamespace uirenderer { 287850ef999740f214a1990a9c090d3f3865d435aaMike Lockwood 295cdceca217319bf6a22caf1acadc38c8dc259316Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 305cdceca217319bf6a22caf1acadc38c8dc259316Mike Lockwood// Functions 3116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 3216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 3316864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwoodtemplate<typename T> 34ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwoodstatic inline T min(T a, T b) { 3516864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood return a < b ? a : b; 3616864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood} 3716864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 3816864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 3916864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood// Cache entry 4016864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 4116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 4216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwoodhash_t GradientCacheEntry::hash() const { 4316864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood uint32_t hash = JenkinsHashMix(0, count); 4416864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood for (uint32_t i = 0; i < count; i++) { 45ef441d965504dbf31c5db690e5b34fcdcecd92ffMike Lockwood hash = JenkinsHashMix(hash, android::hash_type(colors[i])); 46ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood hash = JenkinsHashMix(hash, android::hash_type(positions[i])); 47ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood } 48ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood return JenkinsHashWhiten(hash); 49ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood} 50ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood 51ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwoodint GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) { 52ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood int deltaInt = int(lhs.count) - int(rhs.count); 53ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood if (deltaInt != 0) return deltaInt; 54ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood 55ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood deltaInt = memcmp(lhs.colors.get(), rhs.colors.get(), lhs.count * sizeof(uint32_t)); 56ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood if (deltaInt != 0) return deltaInt; 57ab063847e6e893740749029a04cce1f6b7345ed5Mike Lockwood 5816864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood return memcmp(lhs.positions.get(), rhs.positions.get(), lhs.count * sizeof(float)); 59335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood} 60335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood 61335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 62335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood// Constructors/destructor 63335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 64335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood 65335dd2be955607f2632eabc25045857f2cc8b674Mike LockwoodGradientCache::GradientCache(Extensions& extensions) 66335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood : mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity) 67335dd2be955607f2632eabc25045857f2cc8b674Mike Lockwood , mSize(0) 6816864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood , mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) 6916864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood , mUseFloatTexture(extensions.hasFloatTextures()) 7016864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood , mHasNpot(extensions.hasNPot()){ 7116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood char property[PROPERTY_VALUE_MAX]; 7216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, nullptr) > 0) { 7316864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood INIT_LOGD(" Setting gradient cache size to %sMB", property); 7416864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood setMaxSize(MB(atof(property))); 7516864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood } else { 76a6c490b8b2d96ebaab632286029463f932ae3b6bMike Lockwood INIT_LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); 77a6c490b8b2d96ebaab632286029463f932ae3b6bMike Lockwood } 788277cec96ffa55082962591bca1c55abbeec8c26Mike Lockwood 798277cec96ffa55082962591bca1c55abbeec8c26Mike Lockwood glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); 8016864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 8116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood mCache.setOnEntryRemovedListener(this); 8216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood} 8316864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 8416864bae0f51c32c456da2c43adf7a057c0c4882Mike LockwoodGradientCache::~GradientCache() { 85782aef17c9921a3bf401a0432878df5031f2328bMike Lockwood mCache.clear(); 8616864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood} 8716864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 8816864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 8916864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood// Size management 9016864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 9116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 9216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwooduint32_t GradientCache::getSize() { 931865a5ddcfe7b0e8dc211419aea1094b1491a5fdMike Lockwood return mSize; 94de1e37aad04640ef76f3c017b65adca087c7be0fMike Lockwood} 9516864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 9616864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwooduint32_t GradientCache::getMaxSize() { 9716864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood return mMaxSize; 98487be61fb0a38873aec1d12da92437fba5e728f2Jerry Zhang} 99487be61fb0a38873aec1d12da92437fba5e728f2Jerry Zhang 10016864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwoodvoid GradientCache::setMaxSize(uint32_t maxSize) { 101487be61fb0a38873aec1d12da92437fba5e728f2Jerry Zhang mMaxSize = maxSize; 102487be61fb0a38873aec1d12da92437fba5e728f2Jerry Zhang while (mSize > mMaxSize) { 103487be61fb0a38873aec1d12da92437fba5e728f2Jerry Zhang mCache.removeOldest(); 10416864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood } 10516864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood} 10616864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 10742d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 10842d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwood// Callbacks 10942d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwood/////////////////////////////////////////////////////////////////////////////// 11042d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwood 11142d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwoodvoid GradientCache::operator()(GradientCacheEntry&, Texture*& texture) { 11242d0b79a787814d42e4c6f9dfe14f13cc0f6a758Mike Lockwood if (texture) { 113d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono mSize -= texture->objectSize(); 114d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono texture->deleteTexture(); 115d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono delete texture; 116d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono } 117d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono} 118d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono 119d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono/////////////////////////////////////////////////////////////////////////////// 120d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono// Caching 121d4b4296b401162a7a42f757c96e3652b82255b13Daichi Hirono/////////////////////////////////////////////////////////////////////////////// 12216864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood 12316864bae0f51c32c456da2c43adf7a057c0c4882Mike LockwoodTexture* GradientCache::get(uint32_t* colors, float* positions, int count) { 12416864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood GradientCacheEntry gradient(colors, positions, count); 125b9ff444a7eaf7ffd43970c0477110c6808bd4a7cMike Lockwood Texture* texture = mCache.get(gradient); 1264fd9a8b9865addfedbcd84d5c9efea0f647086a0Daichi Hirono 12716864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood if (!texture) { 12816864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood texture = addLinearGradient(gradient, colors, positions, count); 1297850ef999740f214a1990a9c090d3f3865d435aaMike Lockwood } 1307850ef999740f214a1990a9c090d3f3865d435aaMike Lockwood 13116864bae0f51c32c456da2c43adf7a057c0c4882Mike Lockwood return texture; 132} 133 134void GradientCache::clear() { 135 mCache.clear(); 136} 137 138void GradientCache::getGradientInfo(const uint32_t* colors, const int count, 139 GradientInfo& info) { 140 uint32_t width = 256 * (count - 1); 141 142 // If the npot extension is not supported we cannot use non-clamp 143 // wrap modes. We therefore find the nearest largest power of 2 144 // unless width is already a power of 2 145 if (!mHasNpot && (width & (width - 1)) != 0) { 146 width = 1 << (32 - __builtin_clz(width)); 147 } 148 149 bool hasAlpha = false; 150 for (int i = 0; i < count; i++) { 151 if (((colors[i] >> 24) & 0xff) < 255) { 152 hasAlpha = true; 153 break; 154 } 155 } 156 157 info.width = min(width, uint32_t(mMaxTextureSize)); 158 info.hasAlpha = hasAlpha; 159} 160 161Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, 162 uint32_t* colors, float* positions, int count) { 163 164 GradientInfo info; 165 getGradientInfo(colors, count, info); 166 167 Texture* texture = new Texture(Caches::getInstance()); 168 texture->blend = info.hasAlpha; 169 texture->generation = 1; 170 171 // Asume the cache is always big enough 172 const uint32_t size = info.width * 2 * bytesPerPixel(); 173 while (getSize() + size > mMaxSize) { 174 mCache.removeOldest(); 175 } 176 177 generateTexture(colors, positions, info.width, 2, texture); 178 179 mSize += size; 180 mCache.put(gradient, texture); 181 182 return texture; 183} 184 185size_t GradientCache::bytesPerPixel() const { 186 // We use 4 channels (RGBA) 187 return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); 188} 189 190void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const { 191 outColor.r = (inColor >> 16) & 0xff; 192 outColor.g = (inColor >> 8) & 0xff; 193 outColor.b = (inColor >> 0) & 0xff; 194 outColor.a = (inColor >> 24) & 0xff; 195} 196 197void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const { 198 outColor.r = ((inColor >> 16) & 0xff) / 255.0f; 199 outColor.g = ((inColor >> 8) & 0xff) / 255.0f; 200 outColor.b = ((inColor >> 0) & 0xff) / 255.0f; 201 outColor.a = ((inColor >> 24) & 0xff) / 255.0f; 202} 203 204void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount, 205 uint8_t*& dst) const { 206 float oppAmount = 1.0f - amount; 207 const float alpha = start.a * oppAmount + end.a * amount; 208 const float a = alpha / 255.0f; 209 210 *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount)); 211 *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount)); 212 *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount)); 213 *dst++ = uint8_t(alpha); 214} 215 216void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount, 217 uint8_t*& dst) const { 218 float oppAmount = 1.0f - amount; 219 const float a = start.a * oppAmount + end.a * amount; 220 221 float* d = (float*) dst; 222 *d++ = a * (start.r * oppAmount + end.r * amount); 223 *d++ = a * (start.g * oppAmount + end.g * amount); 224 *d++ = a * (start.b * oppAmount + end.b * amount); 225 *d++ = a; 226 227 dst += 4 * sizeof(float); 228} 229 230void GradientCache::generateTexture(uint32_t* colors, float* positions, 231 const uint32_t width, const uint32_t height, Texture* texture) { 232 const GLsizei rowBytes = width * bytesPerPixel(); 233 uint8_t pixels[rowBytes * height]; 234 235 static ChannelSplitter gSplitters[] = { 236 &android::uirenderer::GradientCache::splitToBytes, 237 &android::uirenderer::GradientCache::splitToFloats, 238 }; 239 ChannelSplitter split = gSplitters[mUseFloatTexture]; 240 241 static ChannelMixer gMixers[] = { 242 &android::uirenderer::GradientCache::mixBytes, 243 &android::uirenderer::GradientCache::mixFloats, 244 }; 245 ChannelMixer mix = gMixers[mUseFloatTexture]; 246 247 GradientColor start; 248 (this->*split)(colors[0], start); 249 250 GradientColor end; 251 (this->*split)(colors[1], end); 252 253 int currentPos = 1; 254 float startPos = positions[0]; 255 float distance = positions[1] - startPos; 256 257 uint8_t* dst = pixels; 258 for (uint32_t x = 0; x < width; x++) { 259 float pos = x / float(width - 1); 260 if (pos > positions[currentPos]) { 261 start = end; 262 startPos = positions[currentPos]; 263 264 currentPos++; 265 266 (this->*split)(colors[currentPos], end); 267 distance = positions[currentPos] - startPos; 268 } 269 270 float amount = (pos - startPos) / distance; 271 (this->*mix)(start, end, amount, dst); 272 } 273 274 memcpy(pixels + rowBytes, pixels, rowBytes); 275 276 glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 277 278 if (mUseFloatTexture) { 279 // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering 280 texture->upload(width, height, GL_RGBA16F, GL_RGBA, GL_FLOAT, pixels); 281 } else { 282 texture->upload(width, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 283 } 284 285 texture->setFilter(GL_LINEAR); 286 texture->setWrap(GL_CLAMP_TO_EDGE); 287} 288 289}; // namespace uirenderer 290}; // namespace android 291