1/*
2 * Copyright (C) 2013 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/Log.h>
20
21#include "Debug.h"
22#include "Properties.h"
23#include "RenderBufferCache.h"
24
25namespace android {
26namespace uirenderer {
27
28///////////////////////////////////////////////////////////////////////////////
29// Defines
30///////////////////////////////////////////////////////////////////////////////
31
32// Debug
33#if DEBUG_RENDER_BUFFERS
34    #define RENDER_BUFFER_LOGD(...) ALOGD(__VA_ARGS__)
35#else
36    #define RENDER_BUFFER_LOGD(...)
37#endif
38
39///////////////////////////////////////////////////////////////////////////////
40// Constructors/destructor
41///////////////////////////////////////////////////////////////////////////////
42
43RenderBufferCache::RenderBufferCache(): mSize(0), mMaxSize(MB(DEFAULT_RENDER_BUFFER_CACHE_SIZE)) {
44    char property[PROPERTY_VALUE_MAX];
45    if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, NULL) > 0) {
46        INIT_LOGD("  Setting render buffer cache size to %sMB", property);
47        setMaxSize(MB(atof(property)));
48    } else {
49        INIT_LOGD("  Using default render buffer cache size of %.2fMB",
50                DEFAULT_RENDER_BUFFER_CACHE_SIZE);
51    }
52}
53
54RenderBufferCache::~RenderBufferCache() {
55    clear();
56}
57
58///////////////////////////////////////////////////////////////////////////////
59// Size management
60///////////////////////////////////////////////////////////////////////////////
61
62uint32_t RenderBufferCache::getSize() {
63    return mSize;
64}
65
66uint32_t RenderBufferCache::getMaxSize() {
67    return mMaxSize;
68}
69
70void RenderBufferCache::setMaxSize(uint32_t maxSize) {
71    clear();
72    mMaxSize = maxSize;
73}
74
75///////////////////////////////////////////////////////////////////////////////
76// Caching
77///////////////////////////////////////////////////////////////////////////////
78
79int RenderBufferCache::RenderBufferEntry::compare(
80        const RenderBufferCache::RenderBufferEntry& lhs,
81        const RenderBufferCache::RenderBufferEntry& rhs) {
82    int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
83    if (deltaInt != 0) return deltaInt;
84
85    deltaInt = int(lhs.mHeight) - int(rhs.mHeight);
86    if (deltaInt != 0) return deltaInt;
87
88    return int(lhs.mFormat) - int(rhs.mFormat);
89}
90
91void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) {
92    if (buffer) {
93        RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d)",
94                RenderBuffer::formatName(buffer->getFormat()),
95                buffer->getWidth(), buffer->getHeight());
96
97        mSize -= buffer->getSize();
98        delete buffer;
99    }
100}
101
102void RenderBufferCache::clear() {
103    size_t count = mCache.size();
104    for (size_t i = 0; i < count; i++) {
105        deleteBuffer(mCache.itemAt(i).mBuffer);
106    }
107    mCache.clear();
108}
109
110RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) {
111    RenderBuffer* buffer = NULL;
112
113    RenderBufferEntry entry(format, width, height);
114    ssize_t index = mCache.indexOf(entry);
115
116    if (index >= 0) {
117        entry = mCache.itemAt(index);
118        mCache.removeAt(index);
119
120        buffer = entry.mBuffer;
121        mSize -= buffer->getSize();
122
123        RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)",
124                RenderBuffer::formatName(format), width, height);
125    } else {
126        buffer = new RenderBuffer(format, width, height);
127
128        RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)",
129                RenderBuffer::formatName(format), width, height);
130    }
131
132    buffer->bind();
133    buffer->allocate();
134
135    return buffer;
136}
137
138bool RenderBufferCache::put(RenderBuffer* buffer) {
139    if (!buffer) return false;
140
141    const uint32_t size = buffer->getSize();
142    if (size < mMaxSize) {
143        while (mSize + size > mMaxSize) {
144            size_t position = 0;
145
146            RenderBuffer* victim = mCache.itemAt(position).mBuffer;
147            deleteBuffer(victim);
148            mCache.removeAt(position);
149        }
150
151        RenderBufferEntry entry(buffer);
152
153        mCache.add(entry);
154        mSize += size;
155
156        RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)",
157                RenderBuffer::formatName(buffer->getFormat()),
158                buffer->getWidth(), buffer->getHeight());
159
160        return true;
161    }
162    return false;
163}
164
165}; // namespace uirenderer
166}; // namespace android
167