Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2010 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 #define LOG_TAG "OpenGLRenderer"
     18 
     19 #include <utils/JenkinsHash.h>
     20 
     21 #include "Caches.h"
     22 #include "Debug.h"
     23 #include "GradientCache.h"
     24 #include "Properties.h"
     25 
     26 namespace android {
     27 namespace uirenderer {
     28 
     29 ///////////////////////////////////////////////////////////////////////////////
     30 // Functions
     31 ///////////////////////////////////////////////////////////////////////////////
     32 
     33 template<typename T>
     34 static inline T min(T a, T b) {
     35     return a < b ? a : b;
     36 }
     37 
     38 ///////////////////////////////////////////////////////////////////////////////
     39 // Cache entry
     40 ///////////////////////////////////////////////////////////////////////////////
     41 
     42 hash_t GradientCacheEntry::hash() const {
     43     uint32_t hash = JenkinsHashMix(0, count);
     44     for (uint32_t i = 0; i < count; i++) {
     45         hash = JenkinsHashMix(hash, android::hash_type(colors[i]));
     46         hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
     47     }
     48     return JenkinsHashWhiten(hash);
     49 }
     50 
     51 int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
     52     int deltaInt = int(lhs.count) - int(rhs.count);
     53     if (deltaInt != 0) return deltaInt;
     54 
     55     deltaInt = memcmp(lhs.colors, rhs.colors, lhs.count * sizeof(uint32_t));
     56     if (deltaInt != 0) return deltaInt;
     57 
     58     return memcmp(lhs.positions, rhs.positions, lhs.count * sizeof(float));
     59 }
     60 
     61 ///////////////////////////////////////////////////////////////////////////////
     62 // Constructors/destructor
     63 ///////////////////////////////////////////////////////////////////////////////
     64 
     65 GradientCache::GradientCache():
     66         mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
     67         mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
     68     char property[PROPERTY_VALUE_MAX];
     69     if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
     70         INIT_LOGD("  Setting gradient cache size to %sMB", property);
     71         setMaxSize(MB(atof(property)));
     72     } else {
     73         INIT_LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
     74     }
     75 
     76     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     77 
     78     mCache.setOnEntryRemovedListener(this);
     79 
     80     const Extensions& extensions = Extensions::getInstance();
     81     mUseFloatTexture = extensions.getMajorGlVersion() >= 3;
     82     mHasNpot = extensions.hasNPot();
     83 }
     84 
     85 GradientCache::GradientCache(uint32_t maxByteSize):
     86         mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
     87         mSize(0), mMaxSize(maxByteSize) {
     88     mCache.setOnEntryRemovedListener(this);
     89 }
     90 
     91 GradientCache::~GradientCache() {
     92     mCache.clear();
     93 }
     94 
     95 ///////////////////////////////////////////////////////////////////////////////
     96 // Size management
     97 ///////////////////////////////////////////////////////////////////////////////
     98 
     99 uint32_t GradientCache::getSize() {
    100     return mSize;
    101 }
    102 
    103 uint32_t GradientCache::getMaxSize() {
    104     return mMaxSize;
    105 }
    106 
    107 void GradientCache::setMaxSize(uint32_t maxSize) {
    108     mMaxSize = maxSize;
    109     while (mSize > mMaxSize) {
    110         mCache.removeOldest();
    111     }
    112 }
    113 
    114 ///////////////////////////////////////////////////////////////////////////////
    115 // Callbacks
    116 ///////////////////////////////////////////////////////////////////////////////
    117 
    118 void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
    119     if (texture) {
    120         const uint32_t size = texture->width * texture->height * bytesPerPixel();
    121         mSize -= size;
    122 
    123         glDeleteTextures(1, &texture->id);
    124         delete texture;
    125     }
    126 }
    127 
    128 ///////////////////////////////////////////////////////////////////////////////
    129 // Caching
    130 ///////////////////////////////////////////////////////////////////////////////
    131 
    132 Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
    133     GradientCacheEntry gradient(colors, positions, count);
    134     Texture* texture = mCache.get(gradient);
    135 
    136     if (!texture) {
    137         texture = addLinearGradient(gradient, colors, positions, count);
    138     }
    139 
    140     return texture;
    141 }
    142 
    143 void GradientCache::clear() {
    144     mCache.clear();
    145 }
    146 
    147 void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
    148         GradientInfo& info) {
    149     uint32_t width = 256 * (count - 1);
    150 
    151     // If the npot extension is not supported we cannot use non-clamp
    152     // wrap modes. We therefore find the nearest largest power of 2
    153     // unless width is already a power of 2
    154     if (!mHasNpot && (width & (width - 1)) != 0) {
    155         width = 1 << (32 - __builtin_clz(width));
    156     }
    157 
    158     bool hasAlpha = false;
    159     for (int i = 0; i < count; i++) {
    160         if (((colors[i] >> 24) & 0xff) < 255) {
    161             hasAlpha = true;
    162             break;
    163         }
    164     }
    165 
    166     info.width = min(width, uint32_t(mMaxTextureSize));
    167     info.hasAlpha = hasAlpha;
    168 }
    169 
    170 Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
    171         uint32_t* colors, float* positions, int count) {
    172 
    173     GradientInfo info;
    174     getGradientInfo(colors, count, info);
    175 
    176     Texture* texture = new Texture;
    177     texture->width = info.width;
    178     texture->height = 2;
    179     texture->blend = info.hasAlpha;
    180     texture->generation = 1;
    181 
    182     // Asume the cache is always big enough
    183     const uint32_t size = texture->width * texture->height * bytesPerPixel();
    184     while (getSize() + size > mMaxSize) {
    185         mCache.removeOldest();
    186     }
    187 
    188     generateTexture(colors, positions, count, texture);
    189 
    190     mSize += size;
    191     mCache.put(gradient, texture);
    192 
    193     return texture;
    194 }
    195 
    196 size_t GradientCache::bytesPerPixel() const {
    197     // We use 4 channels (RGBA)
    198     return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
    199 }
    200 
    201 void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
    202     outColor.r = (inColor >> 16) & 0xff;
    203     outColor.g = (inColor >>  8) & 0xff;
    204     outColor.b = (inColor >>  0) & 0xff;
    205     outColor.a = (inColor >> 24) & 0xff;
    206 }
    207 
    208 void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
    209     outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
    210     outColor.g = ((inColor >>  8) & 0xff) / 255.0f;
    211     outColor.b = ((inColor >>  0) & 0xff) / 255.0f;
    212     outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
    213 }
    214 
    215 void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
    216         uint8_t*& dst) const {
    217     float oppAmount = 1.0f - amount;
    218     const float alpha = start.a * oppAmount + end.a * amount;
    219     const float a = alpha / 255.0f;
    220 
    221     *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
    222     *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
    223     *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
    224     *dst++ = uint8_t(alpha);
    225 }
    226 
    227 void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
    228         uint8_t*& dst) const {
    229     float oppAmount = 1.0f - amount;
    230     const float a = start.a * oppAmount + end.a * amount;
    231 
    232     float* d = (float*) dst;
    233     *d++ = a * (start.r * oppAmount + end.r * amount);
    234     *d++ = a * (start.g * oppAmount + end.g * amount);
    235     *d++ = a * (start.b * oppAmount + end.b * amount);
    236     *d++ = a;
    237 
    238     dst += 4 * sizeof(float);
    239 }
    240 
    241 void GradientCache::generateTexture(uint32_t* colors, float* positions,
    242         int count, Texture* texture) {
    243     const uint32_t width = texture->width;
    244     const GLsizei rowBytes = width * bytesPerPixel();
    245     uint8_t pixels[rowBytes * texture->height];
    246 
    247     static ChannelSplitter gSplitters[] = {
    248             &android::uirenderer::GradientCache::splitToBytes,
    249             &android::uirenderer::GradientCache::splitToFloats,
    250     };
    251     ChannelSplitter split = gSplitters[mUseFloatTexture];
    252 
    253     static ChannelMixer gMixers[] = {
    254             &android::uirenderer::GradientCache::mixBytes,
    255             &android::uirenderer::GradientCache::mixFloats,
    256     };
    257     ChannelMixer mix = gMixers[mUseFloatTexture];
    258 
    259     GradientColor start;
    260     (this->*split)(colors[0], start);
    261 
    262     GradientColor end;
    263     (this->*split)(colors[1], end);
    264 
    265     int currentPos = 1;
    266     float startPos = positions[0];
    267     float distance = positions[1] - startPos;
    268 
    269     uint8_t* dst = pixels;
    270     for (uint32_t x = 0; x < width; x++) {
    271         float pos = x / float(width - 1);
    272         if (pos > positions[currentPos]) {
    273             start = end;
    274             startPos = positions[currentPos];
    275 
    276             currentPos++;
    277 
    278             (this->*split)(colors[currentPos], end);
    279             distance = positions[currentPos] - startPos;
    280         }
    281 
    282         float amount = (pos - startPos) / distance;
    283         (this->*mix)(start, end, amount, dst);
    284     }
    285 
    286     memcpy(pixels + rowBytes, pixels, rowBytes);
    287 
    288     glGenTextures(1, &texture->id);
    289     glBindTexture(GL_TEXTURE_2D, texture->id);
    290     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    291 
    292     if (mUseFloatTexture) {
    293         // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
    294         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
    295                 GL_RGBA, GL_FLOAT, pixels);
    296     } else {
    297         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
    298                 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    299     }
    300 
    301     texture->setFilter(GL_LINEAR);
    302     texture->setWrap(GL_CLAMP_TO_EDGE);
    303 }
    304 
    305 }; // namespace uirenderer
    306 }; // namespace android
    307