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 "RenderBufferCache.h"
18#include "Debug.h"
19#include "DeviceInfo.h"
20#include "Properties.h"
21
22#include <utils/Log.h>
23
24#include <cstdlib>
25
26namespace android {
27namespace uirenderer {
28
29///////////////////////////////////////////////////////////////////////////////
30// Defines
31///////////////////////////////////////////////////////////////////////////////
32
33// Debug
34#if DEBUG_RENDER_BUFFERS
35#define RENDER_BUFFER_LOGD(...) ALOGD(__VA_ARGS__)
36#else
37#define RENDER_BUFFER_LOGD(...)
38#endif
39
40static uint32_t calculateRboCacheSize() {
41    // TODO: Do we need to use extensions().has4BitStencil() here?
42    // The tuning guide recommends it, but all real devices are configured
43    // with a larger cache than necessary by 4x, so keep the 2x for now regardless
44    return DeviceInfo::multiplyByResolution(2);
45}
46
47///////////////////////////////////////////////////////////////////////////////
48// Constructors/destructor
49///////////////////////////////////////////////////////////////////////////////
50
51RenderBufferCache::RenderBufferCache() : mSize(0), mMaxSize(calculateRboCacheSize()) {}
52
53RenderBufferCache::~RenderBufferCache() {
54    clear();
55}
56
57///////////////////////////////////////////////////////////////////////////////
58// Size management
59///////////////////////////////////////////////////////////////////////////////
60
61uint32_t RenderBufferCache::getSize() {
62    return mSize;
63}
64
65uint32_t RenderBufferCache::getMaxSize() {
66    return mMaxSize;
67}
68
69///////////////////////////////////////////////////////////////////////////////
70// Caching
71///////////////////////////////////////////////////////////////////////////////
72
73int RenderBufferCache::RenderBufferEntry::compare(const RenderBufferCache::RenderBufferEntry& lhs,
74                                                  const RenderBufferCache::RenderBufferEntry& rhs) {
75    int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
76    if (deltaInt != 0) return deltaInt;
77
78    deltaInt = int(lhs.mHeight) - int(rhs.mHeight);
79    if (deltaInt != 0) return deltaInt;
80
81    return int(lhs.mFormat) - int(rhs.mFormat);
82}
83
84void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) {
85    if (buffer) {
86        RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d)",
87                           RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(),
88                           buffer->getHeight());
89
90        mSize -= buffer->getSize();
91        delete buffer;
92    }
93}
94
95void RenderBufferCache::clear() {
96    for (auto entry : mCache) {
97        deleteBuffer(entry.mBuffer);
98    }
99    mCache.clear();
100}
101
102RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) {
103    RenderBuffer* buffer = nullptr;
104
105    RenderBufferEntry entry(format, width, height);
106    auto iter = mCache.find(entry);
107
108    if (iter != mCache.end()) {
109        entry = *iter;
110        mCache.erase(iter);
111
112        buffer = entry.mBuffer;
113        mSize -= buffer->getSize();
114
115        RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)", RenderBuffer::formatName(format),
116                           width, height);
117    } else {
118        buffer = new RenderBuffer(format, width, height);
119
120        RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)", RenderBuffer::formatName(format),
121                           width, height);
122    }
123
124    buffer->bind();
125    buffer->allocate();
126
127    return buffer;
128}
129
130bool RenderBufferCache::put(RenderBuffer* buffer) {
131    if (!buffer) return false;
132
133    const uint32_t size = buffer->getSize();
134    if (size < mMaxSize) {
135        while (mSize + size > mMaxSize) {
136            RenderBuffer* victim = mCache.begin()->mBuffer;
137            deleteBuffer(victim);
138            mCache.erase(mCache.begin());
139        }
140
141        RenderBufferEntry entry(buffer);
142
143        mCache.insert(entry);
144        mSize += size;
145
146        RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)",
147                           RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(),
148                           buffer->getHeight());
149
150        return true;
151    } else {
152        RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d",
153                           RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(),
154                           buffer->getHeight(), size, mMaxSize);
155        delete buffer;
156    }
157    return false;
158}
159
160};  // namespace uirenderer
161};  // namespace android
162