LayerCache.cpp revision 1e79386ba34f0db38c1b35b22cdf122632534354
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 <GLES2/gl2.h>
20
21#include <utils/Log.h>
22
23#include "LayerCache.h"
24
25namespace android {
26namespace uirenderer {
27
28///////////////////////////////////////////////////////////////////////////////
29// Constructors/destructor
30///////////////////////////////////////////////////////////////////////////////
31
32LayerCache::LayerCache(uint32_t maxByteSize):
33        mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity),
34        mIdGenerator(1), mSize(0), mMaxSize(maxByteSize) {
35}
36
37LayerCache::~LayerCache() {
38    clear();
39}
40
41///////////////////////////////////////////////////////////////////////////////
42// Size management
43///////////////////////////////////////////////////////////////////////////////
44
45uint32_t LayerCache::getSize() {
46    return mSize;
47}
48
49uint32_t LayerCache::getMaxSize() {
50    return mMaxSize;
51}
52
53void LayerCache::setMaxSize(uint32_t maxSize) {
54    mMaxSize = maxSize;
55    while (mSize > mMaxSize) {
56        Layer* oldest = mCache.removeOldest();
57        deleteLayer(oldest);
58    }
59}
60
61///////////////////////////////////////////////////////////////////////////////
62// Callbacks
63///////////////////////////////////////////////////////////////////////////////
64
65void LayerCache::operator()(LayerSize& size, Layer*& layer) {
66    deleteLayer(layer);
67}
68
69///////////////////////////////////////////////////////////////////////////////
70// Caching
71///////////////////////////////////////////////////////////////////////////////
72
73void LayerCache::deleteLayer(Layer* layer) {
74    if (layer) {
75        mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
76
77        glDeleteFramebuffers(1, &layer->fbo);
78        glDeleteTextures(1, &layer->texture);
79        delete layer;
80    }
81}
82
83void LayerCache::clear() {
84    mCache.setOnEntryRemovedListener(this);
85    mCache.clear();
86    mCache.setOnEntryRemovedListener(NULL);
87}
88
89Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) {
90    Layer* layer = mCache.remove(size);
91    if (layer) {
92        LAYER_LOGD("Reusing layer");
93
94        mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
95    } else {
96        LAYER_LOGD("Creating new layer");
97
98        layer = new Layer;
99        layer->blend = true;
100
101        // Generate the FBO and attach the texture
102        glGenFramebuffers(1, &layer->fbo);
103        glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
104
105        // Generate the texture in which the FBO will draw
106        glGenTextures(1, &layer->texture);
107        glBindTexture(GL_TEXTURE_2D, layer->texture);
108
109        // The FBO will not be scaled, so we can use lower quality filtering
110        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
111        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
112
113        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
114        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
115
116        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
117                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
118
119        // Bind texture to FBO
120        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
121                layer->texture, 0);
122
123        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
124        if (status != GL_FRAMEBUFFER_COMPLETE) {
125            LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
126
127            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
128
129            glDeleteFramebuffers(1, &layer->fbo);
130            glDeleteTextures(1, &layer->texture);
131            delete layer;
132
133            return NULL;
134        }
135    }
136
137    return layer;
138}
139
140bool LayerCache::put(LayerSize& layerSize, Layer* layer) {
141    const uint32_t size = layerSize.width * layerSize.height * 4;
142    // Don't even try to cache a layer that's bigger than the cache
143    if (size < mMaxSize) {
144        while (mSize + size > mMaxSize) {
145            Layer* oldest = mCache.removeOldest();
146            deleteLayer(oldest);
147        }
148
149        layerSize.id = mIdGenerator++;
150        mCache.put(layerSize, layer);
151        mSize += size;
152
153        return true;
154    }
155    return false;
156}
157
158}; // namespace uirenderer
159}; // namespace android
160