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#include "Debug.h"
18#include "Properties.h"
19#include "RenderBufferCache.h"
20
21#include <utils/Log.h>
22
23#include <cstdlib>
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()
44        : mSize(0)
45        , mMaxSize(Properties::renderBufferCacheSize) {}
46
47RenderBufferCache::~RenderBufferCache() {
48    clear();
49}
50
51///////////////////////////////////////////////////////////////////////////////
52// Size management
53///////////////////////////////////////////////////////////////////////////////
54
55uint32_t RenderBufferCache::getSize() {
56    return mSize;
57}
58
59uint32_t RenderBufferCache::getMaxSize() {
60    return mMaxSize;
61}
62
63///////////////////////////////////////////////////////////////////////////////
64// Caching
65///////////////////////////////////////////////////////////////////////////////
66
67int RenderBufferCache::RenderBufferEntry::compare(
68        const RenderBufferCache::RenderBufferEntry& lhs,
69        const RenderBufferCache::RenderBufferEntry& rhs) {
70    int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
71    if (deltaInt != 0) return deltaInt;
72
73    deltaInt = int(lhs.mHeight) - int(rhs.mHeight);
74    if (deltaInt != 0) return deltaInt;
75
76    return int(lhs.mFormat) - int(rhs.mFormat);
77}
78
79void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) {
80    if (buffer) {
81        RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d)",
82                RenderBuffer::formatName(buffer->getFormat()),
83                buffer->getWidth(), buffer->getHeight());
84
85        mSize -= buffer->getSize();
86        delete buffer;
87    }
88}
89
90void RenderBufferCache::clear() {
91    for (auto entry : mCache) {
92        deleteBuffer(entry.mBuffer);
93    }
94    mCache.clear();
95}
96
97RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) {
98    RenderBuffer* buffer = nullptr;
99
100    RenderBufferEntry entry(format, width, height);
101    auto iter = mCache.find(entry);
102
103    if (iter != mCache.end()) {
104        entry = *iter;
105        mCache.erase(iter);
106
107        buffer = entry.mBuffer;
108        mSize -= buffer->getSize();
109
110        RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)",
111                RenderBuffer::formatName(format), width, height);
112    } else {
113        buffer = new RenderBuffer(format, width, height);
114
115        RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)",
116                RenderBuffer::formatName(format), width, height);
117    }
118
119    buffer->bind();
120    buffer->allocate();
121
122    return buffer;
123}
124
125bool RenderBufferCache::put(RenderBuffer* buffer) {
126    if (!buffer) return false;
127
128    const uint32_t size = buffer->getSize();
129    if (size < mMaxSize) {
130        while (mSize + size > mMaxSize) {
131            RenderBuffer* victim = mCache.begin()->mBuffer;
132            deleteBuffer(victim);
133            mCache.erase(mCache.begin());
134        }
135
136        RenderBufferEntry entry(buffer);
137
138        mCache.insert(entry);
139        mSize += size;
140
141        RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)",
142                RenderBuffer::formatName(buffer->getFormat()),
143                buffer->getWidth(), buffer->getHeight());
144
145        return true;
146    } else {
147        RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d",
148        RenderBuffer::formatName(buffer->getFormat()),
149                 buffer->getWidth(), buffer->getHeight(), size, mMaxSize);
150        delete buffer;
151    }
152    return false;
153}
154
155}; // namespace uirenderer
156}; // namespace android
157