LayerCache.cpp revision f18fd99b5c182329cd8936a9611f0103d8ece44a
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        glBindTexture(GL_TEXTURE_2D, 0);
119
120        // Bind texture to FBO
121        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
122                layer->texture, 0);
123
124        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
125        if (status != GL_FRAMEBUFFER_COMPLETE) {
126            LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
127
128            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
129
130            glDeleteFramebuffers(1, &layer->fbo);
131            glDeleteTextures(1, &layer->texture);
132            delete layer;
133
134            return NULL;
135        }
136    }
137
138    return layer;
139}
140
141bool LayerCache::put(LayerSize& layerSize, Layer* layer) {
142    const uint32_t size = layerSize.width * layerSize.height * 4;
143    // Don't even try to cache a layer that's bigger than the cache
144    if (size < mMaxSize) {
145        while (mSize + size > mMaxSize) {
146            Layer* oldest = mCache.removeOldest();
147            deleteLayer(oldest);
148        }
149
150        layerSize.id = mIdGenerator++;
151        mCache.put(layerSize, layer);
152        mSize += size;
153
154        return true;
155    }
156    return false;
157}
158
159}; // namespace uirenderer
160}; // namespace android
161