5e49b497ae
(These CLs are already in master.) Bug: 13246311 This cherry-picks 7 CLs: ----------------------------------------------------------------------- Remove calls to deprecated SkBitmap::setIsOpaque() setIsOpaque() has been removed from ToT Skia. Update setters for mIsPremultiplied and hasAlpha to take the other into consideration. cherry-pick from: I1b36b0b0ce7126031eb7b769b563c17dcd4b306a ----------------------------------------------------------------------- Merge AssetStream with AssetStreamAdaptor. Add enums to the constructor for AssetStreamAdaptor to choose the different behaviors used by the (former) two different classes. The old clients of AssetStream now get the following features of AssetStreamAdaptor - Debugging statements on error. - The stream is an SkStreamRewindable. - getLength() returns the correct value, and the old way of getting the length (read(NULL, 0)) is no longer implemented, since it is no longer used. - isAtEnd() returns the correct value. ToT Skia makes it pure virtual, so some implementation is necessary. cherry-pick from: I2a5395914e4f53830aaefee396556459083a1c56 ----------------------------------------------------------------------- Deprecate Android-specific SkPaint functions. The following functions were problematic: const SkGlyph& getUnicharMetrics(SkUnichar, const SkMatrix*); const SkGlyph& getGlyphMetrics(uint16_t, const SkMatrix*); const void* findImage(const SkGlyph&, const SkMatrix*); Replacing them with calls through SkGlyphCache solved a nasty crash bug, so they have all been deprecated. Bug: 11968757 cherry-pick from: Id746315d41aec5b211b78b172a883c2061130f08 ----------------------------------------------------------------------- pass SkGlyphCache into updateGlyphCache() Doing so prevents us from double-locking the glyph cache, thereby effectively locking ourselves out of reusing work that we'd just done. Bug: 11968757 cherry-pick from: I5c552f2d0bbe30af2ce9054ba684e7da756a0d89 ----------------------------------------------------------------------- Updates to the Skia API needed to merge the WebView m33 version of Skia. cherry-pick from: I0f63b53f2aae58871413b132742fc84138f069a3 Bugfix for screenshots (recent apps) due to incorrect rowBytes computation bug: 12915192 cherry-pick from: I4d5fe2a2f75baf66099e0970fb646686a1992714 ----------------------------------------------------------------------- Fix bug in AndroidPixelRef where we did not store the correct imageInfo for a recycled bitmap. cherry-pick from: I882483b78886e2f19fa4e43a86e69f5a82b3b7e5 ----------------------------------------------------------------------- Change-Id: Ie2b731a9f0795802418cfecddb4b684c92c64d33
321 lines
10 KiB
C++
321 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "OpenGLRenderer"
|
|
|
|
#include <GLES2/gl2.h>
|
|
|
|
#include <SkCanvas.h>
|
|
|
|
#include <utils/Mutex.h>
|
|
|
|
#include "Caches.h"
|
|
#include "TextureCache.h"
|
|
#include "Properties.h"
|
|
|
|
namespace android {
|
|
namespace uirenderer {
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Constructors/destructor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
TextureCache::TextureCache():
|
|
mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
|
|
mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
|
|
mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
|
|
char property[PROPERTY_VALUE_MAX];
|
|
if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
|
|
INIT_LOGD(" Setting texture cache size to %sMB", property);
|
|
setMaxSize(MB(atof(property)));
|
|
} else {
|
|
INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
|
|
}
|
|
|
|
if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, NULL) > 0) {
|
|
float flushRate = atof(property);
|
|
INIT_LOGD(" Setting texture cache flush rate to %.2f%%", flushRate * 100.0f);
|
|
setFlushRate(flushRate);
|
|
} else {
|
|
INIT_LOGD(" Using default texture cache flush rate of %.2f%%",
|
|
DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f);
|
|
}
|
|
|
|
init();
|
|
}
|
|
|
|
TextureCache::TextureCache(uint32_t maxByteSize):
|
|
mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
|
|
mSize(0), mMaxSize(maxByteSize) {
|
|
init();
|
|
}
|
|
|
|
TextureCache::~TextureCache() {
|
|
mCache.clear();
|
|
}
|
|
|
|
void TextureCache::init() {
|
|
mCache.setOnEntryRemovedListener(this);
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
|
|
INIT_LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize);
|
|
|
|
mDebugEnabled = readDebugLevel() & kDebugCaches;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Size management
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
uint32_t TextureCache::getSize() {
|
|
return mSize;
|
|
}
|
|
|
|
uint32_t TextureCache::getMaxSize() {
|
|
return mMaxSize;
|
|
}
|
|
|
|
void TextureCache::setMaxSize(uint32_t maxSize) {
|
|
mMaxSize = maxSize;
|
|
while (mSize > mMaxSize) {
|
|
mCache.removeOldest();
|
|
}
|
|
}
|
|
|
|
void TextureCache::setFlushRate(float flushRate) {
|
|
mFlushRate = fmaxf(0.0f, fminf(1.0f, flushRate));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Callbacks
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
|
|
// This will be called already locked
|
|
if (texture) {
|
|
mSize -= texture->bitmapSize;
|
|
TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d",
|
|
texture->id, texture->bitmapSize, mSize);
|
|
if (mDebugEnabled) {
|
|
ALOGD("Texture deleted, size = %d", texture->bitmapSize);
|
|
}
|
|
texture->deleteTexture();
|
|
delete texture;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Caching
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
Texture* TextureCache::get(SkBitmap* bitmap) {
|
|
Texture* texture = mCache.get(bitmap);
|
|
|
|
if (!texture) {
|
|
if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
|
|
ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
|
|
bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
|
|
return NULL;
|
|
}
|
|
|
|
const uint32_t size = bitmap->rowBytes() * bitmap->height();
|
|
// Don't even try to cache a bitmap that's bigger than the cache
|
|
if (size < mMaxSize) {
|
|
while (mSize + size > mMaxSize) {
|
|
mCache.removeOldest();
|
|
}
|
|
}
|
|
|
|
texture = new Texture();
|
|
texture->bitmapSize = size;
|
|
generateTexture(bitmap, texture, false);
|
|
|
|
if (size < mMaxSize) {
|
|
mSize += size;
|
|
TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
|
|
bitmap, texture->id, size, mSize);
|
|
if (mDebugEnabled) {
|
|
ALOGD("Texture created, size = %d", size);
|
|
}
|
|
mCache.put(bitmap, texture);
|
|
} else {
|
|
texture->cleanup = true;
|
|
}
|
|
} else if (bitmap->getGenerationID() != texture->generation) {
|
|
generateTexture(bitmap, texture, true);
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
Texture* TextureCache::getTransient(SkBitmap* bitmap) {
|
|
Texture* texture = new Texture();
|
|
texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
|
|
texture->cleanup = true;
|
|
|
|
generateTexture(bitmap, texture, false);
|
|
|
|
return texture;
|
|
}
|
|
|
|
void TextureCache::remove(SkBitmap* bitmap) {
|
|
mCache.remove(bitmap);
|
|
}
|
|
|
|
void TextureCache::removeDeferred(SkBitmap* bitmap) {
|
|
Mutex::Autolock _l(mLock);
|
|
mGarbage.push(bitmap);
|
|
}
|
|
|
|
void TextureCache::clearGarbage() {
|
|
Mutex::Autolock _l(mLock);
|
|
size_t count = mGarbage.size();
|
|
for (size_t i = 0; i < count; i++) {
|
|
mCache.remove(mGarbage.itemAt(i));
|
|
}
|
|
mGarbage.clear();
|
|
}
|
|
|
|
void TextureCache::clear() {
|
|
mCache.clear();
|
|
TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize);
|
|
}
|
|
|
|
void TextureCache::flush() {
|
|
if (mFlushRate >= 1.0f || mCache.size() == 0) return;
|
|
if (mFlushRate <= 0.0f) {
|
|
clear();
|
|
return;
|
|
}
|
|
|
|
uint32_t targetSize = uint32_t(mSize * mFlushRate);
|
|
TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize);
|
|
|
|
while (mSize > targetSize) {
|
|
mCache.removeOldest();
|
|
}
|
|
}
|
|
|
|
void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
|
|
SkAutoLockPixels alp(*bitmap);
|
|
|
|
if (!bitmap->readyToDraw()) {
|
|
ALOGE("Cannot generate texture from bitmap");
|
|
return;
|
|
}
|
|
|
|
// We could also enable mipmapping if both bitmap dimensions are powers
|
|
// of 2 but we'd have to deal with size changes. Let's keep this simple
|
|
const bool canMipMap = Extensions::getInstance().hasNPot();
|
|
|
|
// If the texture had mipmap enabled but not anymore,
|
|
// force a glTexImage2D to discard the mipmap levels
|
|
const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
|
|
bitmap->height() != int(texture->height) ||
|
|
(regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
|
|
|
|
if (!regenerate) {
|
|
glGenTextures(1, &texture->id);
|
|
}
|
|
|
|
texture->generation = bitmap->getGenerationID();
|
|
texture->width = bitmap->width();
|
|
texture->height = bitmap->height();
|
|
|
|
Caches::getInstance().bindTexture(texture->id);
|
|
|
|
switch (bitmap->getConfig()) {
|
|
case SkBitmap::kA8_Config:
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
|
|
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
|
|
texture->blend = true;
|
|
break;
|
|
case SkBitmap::kRGB_565_Config:
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
|
|
uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
|
|
texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
|
|
texture->blend = false;
|
|
break;
|
|
case SkBitmap::kARGB_8888_Config:
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
|
|
uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
|
|
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
|
|
// Do this after calling getPixels() to make sure Skia's deferred
|
|
// decoding happened
|
|
texture->blend = !bitmap->isOpaque();
|
|
break;
|
|
case SkBitmap::kARGB_4444_Config:
|
|
case SkBitmap::kIndex8_Config:
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
|
|
uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
|
|
texture->blend = !bitmap->isOpaque();
|
|
break;
|
|
default:
|
|
ALOGW("Unsupported bitmap config: %d", bitmap->getConfig());
|
|
break;
|
|
}
|
|
|
|
if (canMipMap) {
|
|
texture->mipMap = bitmap->hasHardwareMipMap();
|
|
if (texture->mipMap) {
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
}
|
|
}
|
|
|
|
if (!regenerate) {
|
|
texture->setFilter(GL_NEAREST);
|
|
texture->setWrap(GL_CLAMP_TO_EDGE);
|
|
}
|
|
}
|
|
|
|
void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap,
|
|
uint32_t width, uint32_t height) {
|
|
SkBitmap rgbaBitmap;
|
|
rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType());
|
|
rgbaBitmap.allocPixels();
|
|
rgbaBitmap.eraseColor(0);
|
|
|
|
SkCanvas canvas(rgbaBitmap);
|
|
canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
|
|
|
|
uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
|
|
GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
|
|
}
|
|
|
|
void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
|
|
GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
|
|
// TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
|
|
// if the stride doesn't match the width
|
|
const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
|
|
if (useStride) {
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
|
|
}
|
|
|
|
if (resize) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
|
|
} else {
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
|
|
}
|
|
|
|
if (useStride) {
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
}
|
|
}
|
|
|
|
}; // namespace uirenderer
|
|
}; // namespace android
|