2
* Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
#include "TextureCacheCompositingThread.h"
22
#if USE(ACCELERATED_COMPOSITING)
26
#include <GLES2/gl2.h>
29
#define DEBUG_TEXTURE_MEMORY_USAGE 0
33
static const int defaultMemoryLimit = 64 * 1024 * 1024; // Measured in bytes.
35
// Used to protect a newly created texture from being immediately evicted
36
// before someone has a chance to protect it for legitimate reasons.
37
class TextureProtector {
39
TextureProtector(Texture* texture)
47
m_texture->unprotect();
54
TextureCacheCompositingThread::TextureCacheCompositingThread()
56
, m_memoryLimit(defaultMemoryLimit)
60
unsigned TextureCacheCompositingThread::allocateTextureId()
63
glGenTextures(1, &texid);
64
glBindTexture(GL_TEXTURE_2D, texid);
65
if (!glIsTexture(texid))
68
// Do basic linear filtering on resize.
69
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
70
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
71
// NPOT textures in GL ES only work when the wrap mode is set to GL_CLAMP_TO_EDGE.
72
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
73
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
77
void TextureCacheCompositingThread::freeTextureId(unsigned id)
80
glDeleteTextures(1, &id);
83
void TextureCacheCompositingThread::collectGarbage()
87
for (Garbage::iterator it = m_garbage.begin(); it != m_garbage.end(); ++it) {
88
ZombieTexture& zombie = *it;
89
freeTextureId(zombie.id);
90
delta += zombie.size.width() * zombie.size.height() * Texture::bytesPerPixel();
95
decMemoryUsage(delta);
98
void TextureCacheCompositingThread::textureResized(Texture* texture, const IntSize& oldSize)
100
int delta = (texture->width() * texture->height() - oldSize.width() * oldSize.height()) * Texture::bytesPerPixel();
101
incMemoryUsage(delta);
106
void TextureCacheCompositingThread::textureDestroyed(Texture* texture)
108
if (texture->isColor()) {
109
m_garbage.append(ZombieTexture(texture));
113
evict(m_textures.find(texture));
116
bool TextureCacheCompositingThread::install(Texture* texture)
121
if (!texture->hasTexture()) {
122
unsigned textureId = allocateTextureId();
126
texture->setTextureId(textureId);
129
if (!texture->isColor()) {
130
if (!m_textures.contains(texture))
131
m_textures.add(texture);
137
void TextureCacheCompositingThread::evict(const TextureSet::iterator& it)
139
if (it == m_textures.end())
142
Texture* texture = *it;
143
if (texture->hasTexture())
144
m_garbage.append(ZombieTexture(texture));
146
texture->setTextureId(0);
148
m_textures.remove(it);
151
void TextureCacheCompositingThread::textureAccessed(Texture* texture)
153
if (texture->isColor())
156
TextureSet::iterator it = m_textures.find(texture);
157
if (it == m_textures.end())
160
m_textures.remove(it);
161
m_textures.add(texture);
164
TextureCacheCompositingThread* textureCacheCompositingThread()
166
static TextureCacheCompositingThread* staticCache = new TextureCacheCompositingThread;
170
void TextureCacheCompositingThread::prune(size_t limit)
172
while (m_memoryUsage > limit) {
174
for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
175
Texture* texture = *it;
176
if (texture->isProtected())
187
void TextureCacheCompositingThread::clear()
194
void TextureCacheCompositingThread::setMemoryUsage(size_t memoryUsage)
196
m_memoryUsage = memoryUsage;
197
#if DEBUG_TEXTURE_MEMORY_USAGE
198
fprintf(stderr, "Texture memory usage %u kB\n", m_memoryUsage / 1024);
202
PassRefPtr<Texture> TextureCacheCompositingThread::textureForTiledContents(const SkBitmap& contents, const IntRect& tileRect, const TileIndex& index, bool isOpaque)
204
HashMap<ContentsKey, TextureMap>::iterator it = m_cache.add(key(contents), TextureMap()).iterator;
205
TextureMap& map = (*it).value;
207
TextureMap::iterator jt = map.add(index, RefPtr<Texture>()).iterator;
208
RefPtr<Texture> texture = (*jt).value;
210
texture = createTexture();
211
#if DEBUG_TEXTURE_MEMORY_USAGE
212
fprintf(stderr, "Creating texture 0x%x for 0x%x+%d @ (%d, %d)\n", texture.get(), contents.pixelRef(), contents.pixelRefOffset(), index.i(), index.j());
214
map.set(index, texture);
217
// Protect newly created texture from being evicted.
218
TextureProtector protector(texture.get());
220
IntSize contentsSize(contents.width(), contents.height());
221
IntRect dirtyRect(IntPoint(), contentsSize);
222
if (tileRect.size() != texture->size()) {
223
#if DEBUG_TEXTURE_MEMORY_USAGE
224
fprintf(stderr, "Updating texture 0x%x for 0x%x+%d @ (%d, %d)\n", texture.get(), contents.pixelRef(), contents.pixelRefOffset(), index.i(), index.j());
226
texture->updateContents(contents, dirtyRect, tileRect, isOpaque);
228
return texture.release();
231
PassRefPtr<Texture> TextureCacheCompositingThread::textureForColor(const Color& color)
233
// Just to make sure we don't get fooled by some malicious web page
234
// into caching millions of color textures.
235
if (m_colors.size() > 100)
238
ColorTextureMap::iterator it = m_colors.find(color);
239
RefPtr<Texture> texture;
240
if (it == m_colors.end()) {
241
texture = Texture::create(true /* isColor */);
242
#if DEBUG_TEXTURE_MEMORY_USAGE
243
fprintf(stderr, "Creating texture 0x%x for color 0x%x\n", texture.get(), color.rgb());
245
m_colors.set(color, texture);
247
texture = (*it).value;
249
// Color textures can't be evicted, so no need for TextureProtector.
251
if (texture->size() != IntSize(1, 1))
252
texture->setContentsToColor(color);
254
return texture.release();
257
PassRefPtr<Texture> TextureCacheCompositingThread::updateContents(const RefPtr<Texture>& textureIn, const SkBitmap& contents, const IntRect& dirtyRect, const IntRect& tileRect, bool isOpaque)
259
RefPtr<Texture> texture(textureIn);
261
// If the texture was 0, or needs to transition from a solid color texture to a contents texture,
262
// create a new texture.
263
if (!texture || texture->isColor())
264
texture = createTexture();
266
// Protect newly created texture from being evicted.
267
TextureProtector protector(texture.get());
269
texture->updateContents(contents, dirtyRect, tileRect, isOpaque);
271
return texture.release();
274
TextureCacheCompositingThread::ContentsKey TextureCacheCompositingThread::key(const SkBitmap& contents)
276
// The pixelRef is an address, and addresses can be reused by the allocator
277
// so it's unsuitable to use as a key. Instead, grab the generation, which
278
// is globally unique according to the current implementation in
280
uint32_t generation = contents.getGenerationID();
282
// If the generation is equal to the deleted value, use something else that
283
// is unlikely to correspond to a generation currently in use or soon to be
285
uint32_t deletedValue = 0;
286
HashTraits<uint32_t>::constructDeletedValue(deletedValue);
287
if (generation == deletedValue) {
288
// This strategy works as long as the deleted value is -1.
289
ASSERT(deletedValue == static_cast<uint32_t>(-1));
290
generation = deletedValue / 2;
293
// Now the generation alone does not uniquely identify the texture contents.
294
// The same pixelref can be reused in another bitmap but with a different
295
// offset (somewhat contrived, but possible).
296
return std::make_pair(generation, contents.pixelRefOffset());
299
} // namespace WebCore