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