Merge "Reduce number of glDraw calls when drawing text" into jb-mr2-dev
This commit is contained in:
@@ -55,10 +55,7 @@ FontRenderer::FontRenderer() :
|
||||
mGammaTable = NULL;
|
||||
mInitialized = false;
|
||||
mMaxNumberOfQuads = 1024;
|
||||
mCurrentQuadIndex = 0;
|
||||
mLastQuadIndex = 0;
|
||||
|
||||
mTextMesh = NULL;
|
||||
mCurrentCacheTexture = NULL;
|
||||
|
||||
mLinearFiltering = false;
|
||||
@@ -114,8 +111,6 @@ FontRenderer::~FontRenderer() {
|
||||
// Unbinding the buffer shouldn't be necessary but it crashes with some drivers
|
||||
Caches::getInstance().unbindIndicesBuffer();
|
||||
glDeleteBuffers(1, &mIndexBufferID);
|
||||
|
||||
delete[] mTextMesh;
|
||||
}
|
||||
|
||||
LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
|
||||
@@ -126,9 +121,7 @@ FontRenderer::~FontRenderer() {
|
||||
}
|
||||
|
||||
void FontRenderer::flushAllAndInvalidate() {
|
||||
if (mCurrentQuadIndex != 0) {
|
||||
issueDrawCommand();
|
||||
}
|
||||
issueDrawCommand();
|
||||
|
||||
LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
|
||||
while (it.next()) {
|
||||
@@ -236,6 +229,9 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
|
||||
// Large-glyph texture memory is allocated only as needed
|
||||
cacheTexture->allocateTexture();
|
||||
}
|
||||
if (!cacheTexture->mesh()) {
|
||||
cacheTexture->allocateMesh();
|
||||
}
|
||||
|
||||
// Tells us whether the glyphs is B&W (1 bit per pixel)
|
||||
// or anti-aliased (8 bits per pixel)
|
||||
@@ -307,11 +303,12 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
|
||||
}
|
||||
|
||||
CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
|
||||
CacheTexture* cacheTexture = new CacheTexture(width, height);
|
||||
CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
|
||||
|
||||
if (allocate) {
|
||||
Caches::getInstance().activeTexture(0);
|
||||
cacheTexture->allocateTexture();
|
||||
cacheTexture->allocateMesh();
|
||||
}
|
||||
|
||||
return cacheTexture;
|
||||
@@ -356,12 +353,6 @@ void FontRenderer::initVertexArrayBuffers() {
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
|
||||
|
||||
free(indexBufferData);
|
||||
|
||||
uint32_t coordSize = 2;
|
||||
uint32_t uvSize = 2;
|
||||
uint32_t vertsPerQuad = 4;
|
||||
uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
|
||||
mTextMesh = new float[vertexBufferSize];
|
||||
}
|
||||
|
||||
// We don't want to allocate anything unless we actually draw text
|
||||
@@ -376,15 +367,6 @@ void FontRenderer::checkInit() {
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
void FontRenderer::updateDrawParams() {
|
||||
if (mCurrentQuadIndex != mLastQuadIndex) {
|
||||
uint16_t* offset = (uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6);
|
||||
uint32_t count = mCurrentQuadIndex - mLastQuadIndex;
|
||||
mDrawBatch.add(TextBatch(offset, count, mCurrentCacheTexture));
|
||||
mLastQuadIndex = mCurrentQuadIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void FontRenderer::checkTextureUpdate() {
|
||||
if (!mUploadTexture) {
|
||||
return;
|
||||
@@ -424,77 +406,58 @@ void FontRenderer::checkTextureUpdate() {
|
||||
}
|
||||
|
||||
void FontRenderer::issueDrawCommand() {
|
||||
updateDrawParams();
|
||||
checkTextureUpdate();
|
||||
bool first = true;
|
||||
bool force = false;
|
||||
|
||||
Caches& caches = Caches::getInstance();
|
||||
caches.bindIndicesBuffer(mIndexBufferID);
|
||||
if (!mDrawn) {
|
||||
float* buffer = mTextMesh;
|
||||
int offset = 2;
|
||||
|
||||
bool force = caches.unbindMeshBuffer();
|
||||
caches.bindPositionVertexPointer(force, buffer);
|
||||
caches.bindTexCoordsVertexPointer(force, buffer + offset);
|
||||
}
|
||||
|
||||
caches.activeTexture(0);
|
||||
GLuint lastId = 0;
|
||||
Caches& caches = Caches::getInstance();
|
||||
|
||||
for (uint32_t i = 0; i < mDrawBatch.size(); i++) {
|
||||
const TextBatch& batch = mDrawBatch[i];
|
||||
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
|
||||
CacheTexture* texture = mCacheTextures[i];
|
||||
if (texture->canDraw()) {
|
||||
if (first) {
|
||||
checkTextureUpdate();
|
||||
caches.bindIndicesBuffer(mIndexBufferID);
|
||||
|
||||
GLuint id = batch.texture->getTextureId();
|
||||
if (id != lastId) {
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
batch.texture->setLinearFiltering(mLinearFiltering, false);
|
||||
lastId = id;
|
||||
if (!mDrawn) {
|
||||
// If returns true, a VBO was bound and we must
|
||||
// rebind our vertex attrib pointers even if
|
||||
// they have the same values as the current pointers
|
||||
force = caches.unbindMeshBuffer();
|
||||
}
|
||||
|
||||
caches.activeTexture(0);
|
||||
first = false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
|
||||
texture->setLinearFiltering(mLinearFiltering, false);
|
||||
|
||||
TextureVertex* mesh = texture->mesh();
|
||||
caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
|
||||
caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
|
||||
force = false;
|
||||
|
||||
glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
|
||||
GL_UNSIGNED_SHORT, texture->indices());
|
||||
|
||||
texture->resetMesh();
|
||||
}
|
||||
|
||||
glDrawElements(GL_TRIANGLES, batch.count * 6, GL_UNSIGNED_SHORT, batch.offset);
|
||||
}
|
||||
|
||||
mDrawn = true;
|
||||
|
||||
mCurrentQuadIndex = 0;
|
||||
mLastQuadIndex = 0;
|
||||
mDrawBatch.clear();
|
||||
}
|
||||
|
||||
void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
|
||||
float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
|
||||
float x4, float y4, float u4, float v4, CacheTexture* texture) {
|
||||
if (texture != mCurrentCacheTexture) {
|
||||
updateDrawParams();
|
||||
// Now use the new texture id
|
||||
mCurrentCacheTexture = texture;
|
||||
}
|
||||
|
||||
const uint32_t vertsPerQuad = 4;
|
||||
const uint32_t floatsPerVert = 4;
|
||||
float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
|
||||
|
||||
(*currentPos++) = x1;
|
||||
(*currentPos++) = y1;
|
||||
(*currentPos++) = u1;
|
||||
(*currentPos++) = v1;
|
||||
|
||||
(*currentPos++) = x2;
|
||||
(*currentPos++) = y2;
|
||||
(*currentPos++) = u2;
|
||||
(*currentPos++) = v2;
|
||||
|
||||
(*currentPos++) = x3;
|
||||
(*currentPos++) = y3;
|
||||
(*currentPos++) = u3;
|
||||
(*currentPos++) = v3;
|
||||
|
||||
(*currentPos++) = x4;
|
||||
(*currentPos++) = y4;
|
||||
(*currentPos++) = u4;
|
||||
(*currentPos++) = v4;
|
||||
|
||||
mCurrentQuadIndex++;
|
||||
mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
|
||||
x3, y3, u3, v3, x4, y4, u4, v4);
|
||||
}
|
||||
|
||||
void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
|
||||
@@ -515,7 +478,7 @@ void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
|
||||
mBounds->bottom = fmax(mBounds->bottom, y1);
|
||||
}
|
||||
|
||||
if (mCurrentQuadIndex == mMaxNumberOfQuads) {
|
||||
if (mCurrentCacheTexture->endOfMesh()) {
|
||||
issueDrawCommand();
|
||||
}
|
||||
}
|
||||
@@ -533,7 +496,7 @@ void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
|
||||
mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
|
||||
}
|
||||
|
||||
if (mCurrentQuadIndex == mMaxNumberOfQuads) {
|
||||
if (mCurrentCacheTexture->endOfMesh()) {
|
||||
issueDrawCommand();
|
||||
}
|
||||
}
|
||||
@@ -610,9 +573,7 @@ void FontRenderer::finishRender() {
|
||||
mBounds = NULL;
|
||||
mClip = NULL;
|
||||
|
||||
if (mCurrentQuadIndex != 0) {
|
||||
issueDrawCommand();
|
||||
}
|
||||
issueDrawCommand();
|
||||
}
|
||||
|
||||
void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
|
||||
|
||||
@@ -144,7 +144,6 @@ private:
|
||||
|
||||
void removeFont(const Font* font);
|
||||
|
||||
void updateDrawParams();
|
||||
void checkTextureUpdate();
|
||||
|
||||
void setTextureDirty() {
|
||||
@@ -165,12 +164,7 @@ private:
|
||||
|
||||
bool mUploadTexture;
|
||||
|
||||
// Pointer to vertex data to speed up frame to frame work
|
||||
float* mTextMesh;
|
||||
uint32_t mCurrentQuadIndex;
|
||||
uint32_t mLastQuadIndex;
|
||||
uint32_t mMaxNumberOfQuads;
|
||||
|
||||
uint32_t mIndexBufferID;
|
||||
|
||||
const Rect* mClip;
|
||||
@@ -181,33 +175,6 @@ private:
|
||||
|
||||
bool mLinearFiltering;
|
||||
|
||||
struct TextBatch {
|
||||
TextBatch(): offset(NULL), count(0), texture(NULL) {
|
||||
}
|
||||
|
||||
TextBatch(uint16_t* offset, uint32_t count, CacheTexture* texture):
|
||||
offset(offset), count(count), texture(texture) {
|
||||
}
|
||||
|
||||
static int compare(const TextBatch& lhs, const TextBatch& rhs) {
|
||||
return lhs.texture->getTextureId() - rhs.texture->getTextureId();
|
||||
}
|
||||
|
||||
friend inline int strictly_order_type(const TextBatch& lhs, const TextBatch& rhs) {
|
||||
return compare(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
friend inline int compare_type(const TextBatch& lhs, const TextBatch& rhs) {
|
||||
return compare(lhs, rhs);
|
||||
}
|
||||
|
||||
uint16_t* offset;
|
||||
uint32_t count;
|
||||
CacheTexture* texture;
|
||||
};
|
||||
|
||||
SortedList<TextBatch> mDrawBatch;
|
||||
|
||||
// RS constructs
|
||||
sp<RSC::RS> mRs;
|
||||
sp<const RSC::Element> mRsElement;
|
||||
|
||||
@@ -106,6 +106,84 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove)
|
||||
// CacheTexture
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount) :
|
||||
mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
|
||||
mLinearFiltering(false), mDirty(false), mNumGlyphs(0),
|
||||
mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount) {
|
||||
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
|
||||
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
|
||||
}
|
||||
|
||||
CacheTexture::~CacheTexture() {
|
||||
releaseMesh();
|
||||
releaseTexture();
|
||||
reset();
|
||||
}
|
||||
|
||||
void CacheTexture::reset() {
|
||||
// Delete existing cache blocks
|
||||
while (mCacheBlocks != NULL) {
|
||||
CacheBlock* tmpBlock = mCacheBlocks;
|
||||
mCacheBlocks = mCacheBlocks->mNext;
|
||||
delete tmpBlock;
|
||||
}
|
||||
mNumGlyphs = 0;
|
||||
mCurrentQuad = 0;
|
||||
}
|
||||
|
||||
void CacheTexture::init() {
|
||||
// reset, then create a new remainder space to start again
|
||||
reset();
|
||||
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
|
||||
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
|
||||
}
|
||||
|
||||
void CacheTexture::releaseMesh() {
|
||||
delete[] mMesh;
|
||||
}
|
||||
|
||||
void CacheTexture::releaseTexture() {
|
||||
if (mTexture) {
|
||||
delete[] mTexture;
|
||||
mTexture = NULL;
|
||||
}
|
||||
if (mTextureId) {
|
||||
glDeleteTextures(1, &mTextureId);
|
||||
mTextureId = 0;
|
||||
}
|
||||
mDirty = false;
|
||||
mCurrentQuad = 0;
|
||||
}
|
||||
|
||||
void CacheTexture::allocateMesh() {
|
||||
if (!mMesh) {
|
||||
mMesh = new TextureVertex[mMaxQuadCount * 4];
|
||||
}
|
||||
}
|
||||
|
||||
void CacheTexture::allocateTexture() {
|
||||
if (!mTexture) {
|
||||
mTexture = new uint8_t[mWidth * mHeight];
|
||||
}
|
||||
|
||||
if (!mTextureId) {
|
||||
glGenTextures(1, &mTextureId);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureId);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
// Initialize texture dimensions
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0,
|
||||
GL_ALPHA, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
|
||||
bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
|
||||
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
|
||||
return false;
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "FontUtil.h"
|
||||
#include "Rect.h"
|
||||
#include "../Rect.h"
|
||||
#include "../Vertex.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -55,14 +56,14 @@ struct CacheBlock {
|
||||
}
|
||||
|
||||
static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
|
||||
|
||||
static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove);
|
||||
|
||||
void output() {
|
||||
CacheBlock* currBlock = this;
|
||||
while (currBlock) {
|
||||
ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
|
||||
currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
|
||||
currBlock, currBlock->mX, currBlock->mY,
|
||||
currBlock->mWidth, currBlock->mHeight);
|
||||
currBlock = currBlock->mNext;
|
||||
}
|
||||
}
|
||||
@@ -70,72 +71,17 @@ struct CacheBlock {
|
||||
|
||||
class CacheTexture {
|
||||
public:
|
||||
CacheTexture(uint16_t width, uint16_t height) :
|
||||
mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
|
||||
mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
|
||||
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
|
||||
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
|
||||
}
|
||||
CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount);
|
||||
~CacheTexture();
|
||||
|
||||
~CacheTexture() {
|
||||
releaseTexture();
|
||||
reset();
|
||||
}
|
||||
void reset();
|
||||
void init();
|
||||
|
||||
void reset() {
|
||||
// Delete existing cache blocks
|
||||
while (mCacheBlocks != NULL) {
|
||||
CacheBlock* tmpBlock = mCacheBlocks;
|
||||
mCacheBlocks = mCacheBlocks->mNext;
|
||||
delete tmpBlock;
|
||||
}
|
||||
mNumGlyphs = 0;
|
||||
}
|
||||
void releaseMesh();
|
||||
void releaseTexture();
|
||||
|
||||
void init() {
|
||||
// reset, then create a new remainder space to start again
|
||||
reset();
|
||||
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
|
||||
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
|
||||
}
|
||||
|
||||
void releaseTexture() {
|
||||
if (mTexture) {
|
||||
delete[] mTexture;
|
||||
mTexture = NULL;
|
||||
}
|
||||
if (mTextureId) {
|
||||
glDeleteTextures(1, &mTextureId);
|
||||
mTextureId = 0;
|
||||
}
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method assumes that the proper texture unit is active.
|
||||
*/
|
||||
void allocateTexture() {
|
||||
if (!mTexture) {
|
||||
mTexture = new uint8_t[mWidth * mHeight];
|
||||
}
|
||||
|
||||
if (!mTextureId) {
|
||||
glGenTextures(1, &mTextureId);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureId);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
// Initialize texture dimensions
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0,
|
||||
GL_ALPHA, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
void allocateTexture();
|
||||
void allocateMesh();
|
||||
|
||||
bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
|
||||
|
||||
@@ -193,6 +139,42 @@ public:
|
||||
return mNumGlyphs;
|
||||
}
|
||||
|
||||
TextureVertex* mesh() const {
|
||||
return mMesh;
|
||||
}
|
||||
|
||||
uint32_t meshElementCount() const {
|
||||
return mCurrentQuad * 6;
|
||||
}
|
||||
|
||||
uint16_t* indices() const {
|
||||
return (uint16_t*) 0;
|
||||
}
|
||||
|
||||
void resetMesh() {
|
||||
mCurrentQuad = 0;
|
||||
}
|
||||
|
||||
inline void addQuad(float x1, float y1, float u1, float v1,
|
||||
float x2, float y2, float u2, float v2,
|
||||
float x3, float y3, float u3, float v3,
|
||||
float x4, float y4, float u4, float v4) {
|
||||
TextureVertex* mesh = mMesh + mCurrentQuad * 4;
|
||||
TextureVertex::set(mesh++, x1, y1, u1, v1);
|
||||
TextureVertex::set(mesh++, x2, y2, u2, v2);
|
||||
TextureVertex::set(mesh++, x3, y3, u3, v3);
|
||||
TextureVertex::set(mesh++, x4, y4, u4, v4);
|
||||
mCurrentQuad++;
|
||||
}
|
||||
|
||||
bool canDraw() const {
|
||||
return mCurrentQuad > 0;
|
||||
}
|
||||
|
||||
bool endOfMesh() const {
|
||||
return mCurrentQuad == mMaxQuadCount;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* mTexture;
|
||||
GLuint mTextureId;
|
||||
@@ -201,6 +183,9 @@ private:
|
||||
bool mLinearFiltering;
|
||||
bool mDirty;
|
||||
uint16_t mNumGlyphs;
|
||||
TextureVertex* mMesh;
|
||||
uint32_t mCurrentQuad;
|
||||
uint32_t mMaxQuadCount;
|
||||
CacheBlock* mCacheBlocks;
|
||||
Rect mDirtyRect;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user