Enforce maximum texture size.
When an app tries to render a bitmap or path larger than the GPU's maximum texture size, the drawing command is ignored and a warning is logged. This change also makes texture drawing more robust by catching potential errors during texture creation. This change also fixes a crash in the FontRenderer. The destructor would sometimes try to free an uninitialized array. Change-Id: I95ae0939c52192d97b340aa02417bf6d0c962c57
This commit is contained in:
@@ -292,6 +292,9 @@ FontRenderer::FontRenderer() {
|
||||
mCurrentQuadIndex = 0;
|
||||
mTextureId = 0;
|
||||
|
||||
mTextMeshPtr = NULL;
|
||||
mTextTexture = NULL;
|
||||
|
||||
mIndexBufferID = 0;
|
||||
|
||||
mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
|
||||
@@ -319,10 +322,12 @@ FontRenderer::~FontRenderer() {
|
||||
}
|
||||
mCacheLines.clear();
|
||||
|
||||
delete[] mTextMeshPtr;
|
||||
delete[] mTextTexture;
|
||||
if (mInitialized) {
|
||||
delete[] mTextMeshPtr;
|
||||
delete[] mTextTexture;
|
||||
}
|
||||
|
||||
if(mTextureId) {
|
||||
if (mTextureId) {
|
||||
glDeleteTextures(1, &mTextureId);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,8 +80,7 @@ void GradientCache::operator()(SkShader*& shader, Texture*& texture) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Texture* GradientCache::get(SkShader* shader) {
|
||||
Texture* texture = mCache.get(shader);
|
||||
return texture;
|
||||
return mCache.get(shader);
|
||||
}
|
||||
|
||||
void GradientCache::remove(SkShader* shader) {
|
||||
|
||||
@@ -436,6 +436,7 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const S
|
||||
}
|
||||
|
||||
const Texture* texture = mTextureCache.get(bitmap);
|
||||
if (!texture) return;
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
drawTextureRect(left, top, right, bottom, texture, paint);
|
||||
@@ -451,6 +452,7 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const
|
||||
}
|
||||
|
||||
const Texture* texture = mTextureCache.get(bitmap);
|
||||
if (!texture) return;
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
|
||||
@@ -465,6 +467,7 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
|
||||
}
|
||||
|
||||
const Texture* texture = mTextureCache.get(bitmap);
|
||||
if (!texture) return;
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
const float width = texture->width;
|
||||
@@ -489,6 +492,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
|
||||
}
|
||||
|
||||
const Texture* texture = mTextureCache.get(bitmap);
|
||||
if (!texture) return;
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
int alpha;
|
||||
@@ -617,6 +621,7 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
|
||||
glActiveTexture(gTextureUnits[textureUnit]);
|
||||
|
||||
const PathTexture* texture = mPathCache.get(path, paint);
|
||||
if (!texture) return;
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
int alpha;
|
||||
|
||||
@@ -34,6 +34,10 @@ PathCache::PathCache(uint32_t maxByteSize):
|
||||
mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
|
||||
mSize(0), mMaxSize(maxByteSize) {
|
||||
mCache.setOnEntryRemovedListener(this);
|
||||
|
||||
GLint maxTextureSize;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
mMaxTextureSize = maxTextureSize;
|
||||
}
|
||||
|
||||
PathCache::~PathCache() {
|
||||
@@ -94,9 +98,18 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
|
||||
PathTexture* PathCache::addTexture(const PathCacheEntry& entry,
|
||||
const SkPath *path, const SkPaint* paint) {
|
||||
const SkRect& bounds = path->getBounds();
|
||||
|
||||
const float pathWidth = bounds.width();
|
||||
const float pathHeight = bounds.height();
|
||||
|
||||
if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
|
||||
LOGW("Path too large to be rendered into a texture");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const float offset = entry.strokeWidth * 1.5f;
|
||||
const uint32_t width = uint32_t(bounds.width() + offset * 2.0 + 0.5);
|
||||
const uint32_t height = uint32_t(bounds.height() + offset * 2.0 + 0.5);
|
||||
const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
|
||||
const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
|
||||
|
||||
const uint32_t size = width * height;
|
||||
// Don't even try to cache a bitmap that's bigger than the cache
|
||||
|
||||
@@ -139,6 +139,7 @@ private:
|
||||
|
||||
uint32_t mSize;
|
||||
uint32_t mMaxSize;
|
||||
GLuint mMaxTextureSize;
|
||||
}; // class PathCache
|
||||
|
||||
}; // namespace uirenderer
|
||||
|
||||
@@ -75,11 +75,13 @@ void SkiaShader::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint
|
||||
|
||||
SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
|
||||
SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
|
||||
SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap) {
|
||||
SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
|
||||
}
|
||||
|
||||
void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
|
||||
const Texture* texture = mTextureCache->get(mBitmap);
|
||||
if (!texture) return;
|
||||
mTexture = texture;
|
||||
|
||||
const float width = texture->width;
|
||||
const float height = texture->height;
|
||||
@@ -98,7 +100,11 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
|
||||
const Snapshot& snapshot, GLuint* textureUnit) {
|
||||
GLuint textureSlot = (*textureUnit)++;
|
||||
glActiveTexture(gTextureUnitsMap[textureSlot]);
|
||||
const Texture* texture = mTextureCache->get(mBitmap);
|
||||
|
||||
const Texture* texture = mTexture;
|
||||
mTexture = NULL;
|
||||
if (!texture) return;
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
const float width = texture->width;
|
||||
const float height = texture->height;
|
||||
|
||||
@@ -116,6 +116,7 @@ private:
|
||||
}
|
||||
|
||||
SkBitmap* mBitmap;
|
||||
const Texture* mTexture;
|
||||
}; // struct SkiaBitmapShader
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,11 @@ TextureCache::TextureCache(uint32_t maxByteSize):
|
||||
mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
|
||||
mSize(0), mMaxSize(maxByteSize) {
|
||||
mCache.setOnEntryRemovedListener(this);
|
||||
|
||||
GLint maxTextureSize;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
LOGD("Maximum texture dimension is %d pixels", maxTextureSize);
|
||||
mMaxTextureSize = maxTextureSize;
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() {
|
||||
@@ -79,6 +84,11 @@ void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
|
||||
Texture* TextureCache::get(SkBitmap* bitmap) {
|
||||
Texture* texture = mCache.get(bitmap);
|
||||
if (!texture) {
|
||||
if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
|
||||
LOGW("Bitmap too large to be uploaded into a texture");
|
||||
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) {
|
||||
|
||||
@@ -82,6 +82,7 @@ private:
|
||||
|
||||
uint32_t mSize;
|
||||
uint32_t mMaxSize;
|
||||
GLuint mMaxTextureSize;
|
||||
}; // class TextureCache
|
||||
|
||||
}; // namespace uirenderer
|
||||
|
||||
Reference in New Issue
Block a user