Jens Gulin 6056e10271 Solve three memory leaks related to PatchCache
A Patch can be fairly large, holding bitmap data, but
is also frequently leaked which adds to the severity.
The feature is used in many important processes such
as Home, SystemUI and Chrome.

The following leaks are solved:

1. The Patch itself was not always freed.
PatchCache::removeDeferred() can mark patches to be
cared for by PatchCache::clearGarbage(). But
mCache.remove() would only destroy the container
and the pointer, not the Patch object itself.

2. The vertices stored in the Patch at Patch::createMesh()
would always leak. The empty/default destructor in Patch
would not properly destroy "vertices" since it's just a
pointer.

3. A BufferBlock that's added to the mFreeBlocks
in PatchCache could leak. The leak happened when a
patch later needed the entire free block, because the
object was removed from the list but never deleted
in PatchCache::setupMesh().

Change-Id: I41e60824479230b67426fc546d3dbff294c8891f
2014-03-25 09:37:02 +09:00

256 lines
7.6 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 <cmath>
#include <utils/Log.h>
#include "Caches.h"
#include "Patch.h"
#include "Properties.h"
#include "UvMapper.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
Patch::Patch(): vertices(NULL), verticesCount(0), indexCount(0), hasEmptyQuads(false) {
}
Patch::~Patch() {
delete[] vertices;
}
///////////////////////////////////////////////////////////////////////////////
// Vertices management
///////////////////////////////////////////////////////////////////////////////
uint32_t Patch::getSize() const {
return verticesCount * sizeof(TextureVertex);
}
TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
float width, float height, const Res_png_9patch* patch) {
UvMapper mapper;
return createMesh(bitmapWidth, bitmapHeight, width, height, mapper, patch);
}
TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) {
if (vertices) return vertices;
int8_t emptyQuads = 0;
mColors = patch->getColors();
const int8_t numColors = patch->numColors;
if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
for (int8_t i = 0; i < numColors; i++) {
if (mColors[i] == 0x0) {
emptyQuads++;
}
}
}
hasEmptyQuads = emptyQuads > 0;
uint32_t xCount = patch->numXDivs;
uint32_t yCount = patch->numYDivs;
uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4;
if (maxVertices == 0) return NULL;
TextureVertex* tempVertices = new TextureVertex[maxVertices];
TextureVertex* vertex = tempVertices;
const int32_t* xDivs = patch->getXDivs();
const int32_t* yDivs = patch->getYDivs();
const uint32_t xStretchCount = (xCount + 1) >> 1;
const uint32_t yStretchCount = (yCount + 1) >> 1;
float stretchX = 0.0f;
float stretchY = 0.0f;
float rescaleX = 1.0f;
float rescaleY = 1.0f;
if (xStretchCount > 0) {
uint32_t stretchSize = 0;
for (uint32_t i = 1; i < xCount; i += 2) {
stretchSize += xDivs[i] - xDivs[i - 1];
}
const float xStretchTex = stretchSize;
const float fixed = bitmapWidth - stretchSize;
const float xStretch = fmaxf(width - fixed, 0.0f);
stretchX = xStretch / xStretchTex;
rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(width, 0.0f) / fixed, 1.0f);
}
if (yStretchCount > 0) {
uint32_t stretchSize = 0;
for (uint32_t i = 1; i < yCount; i += 2) {
stretchSize += yDivs[i] - yDivs[i - 1];
}
const float yStretchTex = stretchSize;
const float fixed = bitmapHeight - stretchSize;
const float yStretch = fmaxf(height - fixed, 0.0f);
stretchY = yStretch / yStretchTex;
rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(height, 0.0f) / fixed, 1.0f);
}
uint32_t quadCount = 0;
float previousStepY = 0.0f;
float y1 = 0.0f;
float y2 = 0.0f;
float v1 = 0.0f;
mUvMapper = mapper;
for (uint32_t i = 0; i < yCount; i++) {
float stepY = yDivs[i];
const float segment = stepY - previousStepY;
if (i & 1) {
y2 = y1 + floorf(segment * stretchY + 0.5f);
} else {
y2 = y1 + segment * rescaleY;
}
float vOffset = y1 == y2 ? 0.0f : 0.5 - (0.5 * segment / (y2 - y1));
float v2 = fmax(0.0f, stepY - vOffset) / bitmapHeight;
v1 += vOffset / bitmapHeight;
if (stepY > 0.0f) {
generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX,
width, bitmapWidth, quadCount);
}
y1 = y2;
v1 = stepY / bitmapHeight;
previousStepY = stepY;
}
if (previousStepY != bitmapHeight) {
y2 = height;
generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX,
width, bitmapWidth, quadCount);
}
if (verticesCount == maxVertices) {
vertices = tempVertices;
} else {
vertices = new TextureVertex[verticesCount];
memcpy(vertices, tempVertices, verticesCount * sizeof(TextureVertex));
delete[] tempVertices;
}
return vertices;
}
void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
float y1, float y2, float v1, float v2, float stretchX, float rescaleX,
float width, float bitmapWidth, uint32_t& quadCount) {
float previousStepX = 0.0f;
float x1 = 0.0f;
float x2 = 0.0f;
float u1 = 0.0f;
// Generate the row quad by quad
for (uint32_t i = 0; i < xCount; i++) {
float stepX = xDivs[i];
const float segment = stepX - previousStepX;
if (i & 1) {
x2 = x1 + floorf(segment * stretchX + 0.5f);
} else {
x2 = x1 + segment * rescaleX;
}
float uOffset = x1 == x2 ? 0.0f : 0.5 - (0.5 * segment / (x2 - x1));
float u2 = fmax(0.0f, stepX - uOffset) / bitmapWidth;
u1 += uOffset / bitmapWidth;
if (stepX > 0.0f) {
generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount);
}
x1 = x2;
u1 = stepX / bitmapWidth;
previousStepX = stepX;
}
if (previousStepX != bitmapWidth) {
x2 = width;
generateQuad(vertex, x1, y1, x2, y2, u1, v1, 1.0f, v2, quadCount);
}
}
void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
float u1, float v1, float u2, float v2, uint32_t& quadCount) {
const uint32_t oldQuadCount = quadCount;
quadCount++;
if (x1 < 0.0f) x1 = 0.0f;
if (x2 < 0.0f) x2 = 0.0f;
if (y1 < 0.0f) y1 = 0.0f;
if (y2 < 0.0f) y2 = 0.0f;
// Skip degenerate and transparent (empty) quads
if ((mColors[oldQuadCount] == 0) || x1 >= x2 || y1 >= y2) {
#if DEBUG_PATCHES_EMPTY_VERTICES
PATCH_LOGD(" quad %d (empty)", oldQuadCount);
PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.8f, %.8f", x1, y1, u1, v1);
PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.8f, %.8f", x2, y2, u2, v2);
#endif
return;
}
// Record all non empty quads
if (hasEmptyQuads) {
Rect bounds(x1, y1, x2, y2);
quads.add(bounds);
}
mUvMapper.map(u1, v1, u2, v2);
TextureVertex::set(vertex++, x1, y1, u1, v1);
TextureVertex::set(vertex++, x2, y1, u2, v1);
TextureVertex::set(vertex++, x1, y2, u1, v2);
TextureVertex::set(vertex++, x2, y2, u2, v2);
verticesCount += 4;
indexCount += 6;
#if DEBUG_PATCHES_VERTICES
PATCH_LOGD(" quad %d", oldQuadCount);
PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.8f, %.8f", x1, y1, u1, v1);
PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.8f, %.8f", x2, y2, u2, v2);
#endif
}
}; // namespace uirenderer
}; // namespace android