am 8d4c23b9: Merge "Introduce PixelBuffer API to enable PBOs" into jb-mr2-dev

* commit '8d4c23b9c32f8c0328ebca538bb801716fe4478a':
  Introduce PixelBuffer API to enable PBOs
This commit is contained in:
Romain Guy
2013-04-12 10:37:05 -07:00
committed by Android Git Automerger
16 changed files with 576 additions and 113 deletions

View File

@ -30,6 +30,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
PatchCache.cpp \
PathCache.cpp \
PathTessellator.cpp \
PixelBuffer.cpp \
Program.cpp \
ProgramCache.cpp \
RenderBufferCache.cpp \

View File

@ -70,6 +70,7 @@ void Caches::init() {
mCurrentPositionPointer = this;
mCurrentPositionStride = 0;
mCurrentTexCoordsPointer = this;
mCurrentPixelBuffer = 0;
mTexCoordsArrayEnabled = false;
@ -365,6 +366,28 @@ bool Caches::unbindIndicesBuffer() {
return false;
}
///////////////////////////////////////////////////////////////////////////////
// PBO
///////////////////////////////////////////////////////////////////////////////
bool Caches::bindPixelBuffer(const GLuint buffer) {
if (mCurrentPixelBuffer != buffer) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
mCurrentPixelBuffer = buffer;
return true;
}
return false;
}
bool Caches::unbindPixelBuffer() {
if (mCurrentPixelBuffer) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
mCurrentPixelBuffer = 0;
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// Meshes and textures
///////////////////////////////////////////////////////////////////////////////

View File

@ -175,6 +175,16 @@ public:
bool bindIndicesBuffer(const GLuint buffer);
bool unbindIndicesBuffer();
/**
* Binds the specified buffer as the current GL unpack pixel buffer.
*/
bool bindPixelBuffer(const GLuint buffer);
/**
* Resets the current unpack pixel buffer to 0 (default value.)
*/
bool unbindPixelBuffer();
/**
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
@ -307,6 +317,7 @@ private:
GLuint mCurrentBuffer;
GLuint mCurrentIndicesBuffer;
GLuint mCurrentPixelBuffer;
void* mCurrentPositionPointer;
GLsizei mCurrentPositionStride;
void* mCurrentTexCoordsPointer;

View File

@ -21,6 +21,7 @@
#include <utils/Trace.h>
#include "Caches.h"
#include "Debug.h"
#include "DisplayListOp.h"
#include "OpenGLRenderer.h"
@ -377,6 +378,8 @@ static status_t replayBatchList(Vector<DrawOpBatch*>& batchList,
status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
ATRACE_NAME("flush drawing commands");
Caches::getInstance().fontRenderer->endPrecaching();
status_t status = DrawGlInfo::kStatusDone;
if (isEmpty()) return status; // nothing to flush

View File

@ -33,6 +33,7 @@
#include "Debug.h"
#include "Extensions.h"
#include "FontRenderer.h"
#include "PixelBuffer.h"
#include "Rect.h"
namespace android {
@ -133,26 +134,13 @@ void FontRenderer::flushAllAndInvalidate() {
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
mCacheTextures[i]->init();
}
#if DEBUG_FONT_RENDERER
uint16_t totalGlyphs = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
totalGlyphs += mCacheTextures[i]->getGlyphCount();
// Erase caches, just as a debugging facility
if (mCacheTextures[i]->getTexture()) {
memset(mCacheTextures[i]->getTexture(), 0,
mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
}
}
ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
#endif
}
void FontRenderer::flushLargeCaches() {
// Start from 1; don't deallocate smallest/default texture
for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
if (cacheTexture->getTexture()) {
if (cacheTexture->getPixelBuffer()) {
cacheTexture->init();
LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
while (it.next()) {
@ -226,7 +214,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t cacheWidth = cacheTexture->getWidth();
if (!cacheTexture->getTexture()) {
if (!cacheTexture->getPixelBuffer()) {
Caches::getInstance().activeTexture(0);
// Large-glyph texture memory is allocated only as needed
cacheTexture->allocateTexture();
@ -239,7 +227,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
// or anti-aliased (8 bits per pixel)
SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
uint8_t* cacheBuffer = cacheTexture->getTexture();
uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
// Copy the glyph image, taking the mask format into account
@ -377,56 +365,36 @@ void FontRenderer::checkTextureUpdate() {
Caches& caches = Caches::getInstance();
GLuint lastTextureId = 0;
// OpenGL ES 3.0+ lets us specify the row length for unpack operations such
// as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
// With OpenGL ES 2.0 we have to upload entire stripes instead.
const bool hasUnpackRowLength = Extensions::getInstance().getMajorGlVersion() >= 3;
bool resetPixelStore = false;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Iterate over all the cache textures and see which ones need to be updated
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
const Rect* dirtyRect = cacheTexture->getDirtyRect();
uint32_t x = hasUnpackRowLength ? dirtyRect->left : 0;
uint32_t y = dirtyRect->top;
uint32_t width = cacheTexture->getWidth();
uint32_t height = dirtyRect->getHeight();
void* textureData = cacheTexture->getTexture() + y * width + x;
if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
glBindTexture(GL_TEXTURE_2D, lastTextureId);
// The unpack row length only needs to be specified when a new
// texture is bound
if (hasUnpackRowLength) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
}
}
// If we can upload a sub-rectangle, use the dirty rect width
// instead of the width of the entire texture
if (hasUnpackRowLength) {
width = dirtyRect->getWidth();
if (cacheTexture->upload()) {
resetPixelStore = true;
}
#if DEBUG_FONT_RENDERER
ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
i, x, y, width, height);
#endif
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
cacheTexture->setDirty(false);
}
}
// Unbind any PBO we might have used to update textures
caches.unbindPixelBuffer();
// Reset to default unpack row length to avoid affecting texture
// uploads in other parts of the renderer
if (hasUnpackRowLength) {
if (resetPixelStore) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
@ -539,13 +507,14 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
checkInit();
if (!mCurrentFont) {
DropShadow image;
image.width = 0;
image.height = 0;
image.image = NULL;
image.penX = 0;
image.penY = 0;
if (!mCurrentFont) {
return image;
}
@ -559,6 +528,11 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
uint32_t maxSize = Caches::getInstance().maxTextureSize;
if (paddedWidth > maxSize || paddedHeight > maxSize) {
return image;
}
// Align buffers for renderscript usage
if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
@ -578,10 +552,12 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
// Unbind any PBO we might have used
Caches::getInstance().unbindPixelBuffer();
blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
}
DropShadow image;
image.width = paddedWidth;
image.height = paddedHeight;
image.image = dataBuffer;
@ -612,6 +588,10 @@ void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, con
font->precache(paint, text, numGlyphs);
}
void FontRenderer::endPrecaching() {
checkTextureUpdate();
}
bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
const float* positions, Rect* bounds, Functor* functor) {
@ -690,5 +670,16 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int
*image = outImage;
}
uint32_t FontRenderer::getCacheSize() const {
uint32_t size = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
if (cacheTexture && cacheTexture->getPixelBuffer()) {
size += cacheTexture->getPixelBuffer()->getSize();
}
}
return size;
}
}; // namespace uirenderer
}; // namespace android

View File

@ -61,6 +61,7 @@ public:
void setFont(SkPaint* paint, const mat4& matrix);
void precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix);
void endPrecaching();
// bounds is an out parameter
bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
@ -95,16 +96,7 @@ public:
mLinearFiltering = linearFiltering;
}
uint32_t getCacheSize() const {
uint32_t size = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
if (cacheTexture && cacheTexture->getTexture()) {
size += cacheTexture->getWidth() * cacheTexture->getHeight();
}
}
return size;
}
uint32_t getCacheSize() const;
private:
friend class Font;

View File

@ -129,6 +129,12 @@ void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
}
}
void ShaderGammaFontRenderer::endPrecaching() {
if (mRenderer) {
mRenderer->endPrecaching();
}
}
///////////////////////////////////////////////////////////////////////////////
// Lookup-based renderer
///////////////////////////////////////////////////////////////////////////////
@ -146,6 +152,12 @@ LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() {
mRenderer = NULL;
}
void LookupGammaFontRenderer::endPrecaching() {
if (mRenderer) {
mRenderer->endPrecaching();
}
}
///////////////////////////////////////////////////////////////////////////////
// Lookup-based renderer, using 3 different correction tables
///////////////////////////////////////////////////////////////////////////////
@ -177,6 +189,14 @@ Lookup3GammaFontRenderer::~Lookup3GammaFontRenderer() {
}
}
void Lookup3GammaFontRenderer::endPrecaching() {
for (int i = 0; i < kGammaCount; i++) {
if (mRenderers[i]) {
mRenderers[i]->endPrecaching();
}
}
}
void Lookup3GammaFontRenderer::clear() {
for (int i = 0; i < kGammaCount; i++) {
delete mRenderers[i];

View File

@ -40,6 +40,8 @@ public:
virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
virtual void endPrecaching() = 0;
static GammaFontRenderer* createRenderer();
protected:
@ -86,6 +88,8 @@ public:
void describe(ProgramDescription& description, const SkPaint* paint) const;
void setupProgram(ProgramDescription& description, Program* program) const;
void endPrecaching();
private:
ShaderGammaFontRenderer(bool multiGamma);
@ -134,6 +138,8 @@ public:
void setupProgram(ProgramDescription& description, Program* program) const {
}
void endPrecaching();
private:
LookupGammaFontRenderer();
@ -171,6 +177,8 @@ public:
void setupProgram(ProgramDescription& description, Program* program) const {
}
void endPrecaching();
private:
Lookup3GammaFontRenderer();

View File

@ -2644,6 +2644,9 @@ void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesC
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
const ShadowTexture* shadow = mCaches.dropShadowCache.get(
paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
if (!shadow) return;
const AutoTexture autoCleanup(shadow);
const float sx = x - shadow->left + mDrawModifiers.mShadowDx;

163
libs/hwui/PixelBuffer.cpp Normal file
View File

@ -0,0 +1,163 @@
/*
* Copyright (C) 2013 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 <utils/Log.h>
#include "Caches.h"
#include "Extensions.h"
#include "PixelBuffer.h"
#include "Properties.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// CPU pixel buffer
///////////////////////////////////////////////////////////////////////////////
class CpuPixelBuffer: public PixelBuffer {
public:
CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
~CpuPixelBuffer();
uint8_t* map(AccessMode mode = kAccessMode_ReadWrite);
void unmap();
uint8_t* getMappedPointer() const;
void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset);
private:
uint8_t* mBuffer;
};
CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height):
PixelBuffer(format, width, height) {
mBuffer = new uint8_t[width * height * formatSize(format)];
}
CpuPixelBuffer::~CpuPixelBuffer() {
delete[] mBuffer;
}
uint8_t* CpuPixelBuffer::map(AccessMode mode) {
if (mAccessMode == kAccessMode_None) {
mAccessMode = mode;
}
return mBuffer;
}
void CpuPixelBuffer::unmap() {
mAccessMode = kAccessMode_None;
}
uint8_t* CpuPixelBuffer::getMappedPointer() const {
return mAccessMode == kAccessMode_None ? NULL : mBuffer;
}
void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
mFormat, GL_UNSIGNED_BYTE, mBuffer + offset);
}
///////////////////////////////////////////////////////////////////////////////
// GPU pixel buffer
///////////////////////////////////////////////////////////////////////////////
class GpuPixelBuffer: public PixelBuffer {
public:
GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
~GpuPixelBuffer();
uint8_t* map(AccessMode mode = kAccessMode_ReadWrite);
void unmap();
uint8_t* getMappedPointer() const;
void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset);
private:
GLuint mBuffer;
uint8_t* mMappedPointer;
Caches& mCaches;
};
GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height):
PixelBuffer(format, width, height), mMappedPointer(0), mCaches(Caches::getInstance()) {
glGenBuffers(1, &mBuffer);
mCaches.bindPixelBuffer(mBuffer);
glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), NULL, GL_DYNAMIC_DRAW);
mCaches.unbindPixelBuffer();
}
GpuPixelBuffer::~GpuPixelBuffer() {
glDeleteBuffers(1, &mBuffer);
}
uint8_t* GpuPixelBuffer::map(AccessMode mode) {
if (mAccessMode == kAccessMode_None) {
mCaches.bindPixelBuffer(mBuffer);
mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
mAccessMode = mode;
}
return mMappedPointer;
}
void GpuPixelBuffer::unmap() {
if (mAccessMode != kAccessMode_None) {
if (mMappedPointer) {
mCaches.bindPixelBuffer(mBuffer);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
}
mAccessMode = kAccessMode_None;
mMappedPointer = NULL;
}
}
uint8_t* GpuPixelBuffer::getMappedPointer() const {
return mMappedPointer;
}
void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
// If the buffer is not mapped, unmap() will not bind it
mCaches.bindPixelBuffer(mBuffer);
unmap();
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
GL_UNSIGNED_BYTE, (void*) offset);
}
///////////////////////////////////////////////////////////////////////////////
// Factory
///////////////////////////////////////////////////////////////////////////////
PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
bool gpuBuffer = type == kBufferType_Auto && Extensions::getInstance().getMajorGlVersion() >= 3;
if (gpuBuffer) {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "false") > 0) {
if (!strcmp(property, "true")) {
return new GpuPixelBuffer(format, width, height);
}
}
}
return new CpuPixelBuffer(format, width, height);
}
}; // namespace uirenderer
}; // namespace android

180
libs/hwui/PixelBuffer.h Normal file
View File

@ -0,0 +1,180 @@
/*
* Copyright (C) 2013 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.
*/
#ifndef ANDROID_HWUI_PIXEL_BUFFER_H
#define ANDROID_HWUI_PIXEL_BUFFER_H
#include <GLES3/gl3.h>
namespace android {
namespace uirenderer {
/**
* Represents a pixel buffer. A pixel buffer will be backed either by a
* PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other
* versions. If the buffer is backed by a PBO it will of type
* GL_PIXEL_UNPACK_BUFFER.
*
* To read from or write into a PixelBuffer you must first map the
* buffer using the map(AccessMode) method. This method returns a
* pointer to the beginning of the buffer.
*
* Before the buffer can be used by the GPU, for instance to upload
* a texture, you must first unmap the buffer. To do so, call the
* unmap() method.
*
* Mapping and unmapping a PixelBuffer can have the side effect of
* changing the currently active GL_PIXEL_UNPACK_BUFFER. It is
* therefore recommended to call Caches::unbindPixelbuffer() after
* using a PixelBuffer to upload to a texture.
*/
class PixelBuffer {
public:
enum BufferType {
kBufferType_Auto,
kBufferType_CPU
};
enum AccessMode {
kAccessMode_None = 0,
kAccessMode_Read = GL_MAP_READ_BIT,
kAccessMode_Write = GL_MAP_WRITE_BIT,
kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT
};
/**
* Creates a new PixelBuffer object with the specified format and
* dimensions. The buffer is immediately allocated.
*
* The buffer type specifies how the buffer should be allocated.
* By default this method will automatically choose whether to allocate
* a CPU or GPU buffer.
*/
static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height,
BufferType type = kBufferType_Auto);
virtual ~PixelBuffer() {
}
/**
* Returns the format of this render buffer.
*/
GLenum getFormat() const {
return mFormat;
}
/**
* Maps this before with the specified access mode. This method
* returns a pointer to the region of memory where the buffer was
* mapped.
*
* If the buffer is already mapped when this method is invoked,
* this method will return the previously mapped pointer. The
* access mode can only be changed by calling unmap() first.
*
* The specified access mode cannot be kAccessMode_None.
*/
virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0;
/**
* Unmaps this buffer, if needed. After the buffer is unmapped,
* the pointer previously returned by map() becomes invalid and
* should not be used. After calling this method, getMappedPointer()
* will always return NULL.
*/
virtual void unmap() = 0;
/**
* Returns the current access mode for this buffer. If the buffer
* is not mapped, this method returns kAccessMode_None.
*/
AccessMode getAccessMode() const {
return mAccessMode;
}
/**
* Returns the currently mapped pointer. Returns NULL if the buffer
* is not mapped.
*/
virtual uint8_t* getMappedPointer() const = 0;
/**
* Upload the specified rectangle of this pixe buffer as a
* GL_TEXTURE_2D texture. Calling this method will trigger
* an unmap() if necessary.
*/
virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0;
/**
* Returns the width of the render buffer in pixels.
*/
uint32_t getWidth() const {
return mWidth;
}
/**
* Returns the height of the render buffer in pixels.
*/
uint32_t getHeight() const {
return mHeight;
}
/**
* Returns the size of this pixel buffer in bytes.
*/
uint32_t getSize() const {
return mWidth * mHeight * formatSize(mFormat);
}
/**
* Returns the number of bytes per pixel in the specified format.
*
* Supported formats:
* GL_ALPHA
* GL_RGBA
*/
static uint32_t formatSize(GLenum format) {
switch (format) {
case GL_ALPHA:
return 1;
case GL_RGBA:
return 4;
}
return 0;
}
protected:
/**
* Creates a new render buffer in the specified format and dimensions.
* The format must be GL_ALPHA or GL_RGBA.
*/
PixelBuffer(GLenum format, uint32_t width, uint32_t height):
mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {
}
GLenum mFormat;
uint32_t mWidth;
uint32_t mHeight;
AccessMode mAccessMode;
}; // class PixelBuffer
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_HWUI_PIXEL_BUFFER_H

View File

@ -25,6 +25,10 @@
* the OpenGLRenderer.
*/
///////////////////////////////////////////////////////////////////////////////
// Compile-time properties
///////////////////////////////////////////////////////////////////////////////
// If turned on, text is interpreted as glyphs instead of UTF-16
#define RENDER_TEXT_AS_GLYPHS 1
@ -39,6 +43,10 @@
// to properly implement overdraw debugging
#define STENCIL_BUFFER_SIZE 8
///////////////////////////////////////////////////////////////////////////////
// Debug properties
///////////////////////////////////////////////////////////////////////////////
/**
* Debug level for app developers. The value is a numeric value defined
* by the DebugLevel enum below.
@ -81,6 +89,23 @@ enum DebugLevel {
*/
#define PROPERTY_DEBUG_STENCIL_CLIP "debug.hwui.show_non_rect_clip"
/**
* Disables draw operation deferral if set to "true", forcing draw
* commands to be issued to OpenGL in order, and processed in sequence
* with state-manipulation canvas commands.
*/
#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
/**
* Used to disable draw operation reordering when deferring draw operations
* Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
*/
#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
///////////////////////////////////////////////////////////////////////////////
// Runtime configuration properties
///////////////////////////////////////////////////////////////////////////////
/**
* Used to enable/disable scissor optimization. The accepted values are
* "true" and "false". The default value is "false".
@ -97,17 +122,10 @@ enum DebugLevel {
#define PROPERTY_DISABLE_SCISSOR_OPTIMIZATION "ro.hwui.disable_scissor_opt"
/**
* Disables draw operation deferral if set to "true", forcing draw
* commands to be issued to OpenGL in order, and processed in sequence
* with state-manipulation canvas commands.
* Indicates whether PBOs can be used to back pixel buffers.
* Accepted values are "true" and "false".
*/
#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
/**
* Used to disable draw operation reordering when deferring draw operations
* Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
*/
#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "hwui.use_gpu_pixel_buffers"
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
@ -152,8 +170,9 @@ enum DebugLevel {
// Lumincance threshold above which white gamma correction is applied. Range: [0..255]
#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "hwui.text_gamma.white_threshold"
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
///////////////////////////////////////////////////////////////////////////////
// Default property values
///////////////////////////////////////////////////////////////////////////////
#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
@ -170,6 +189,13 @@ enum DebugLevel {
#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
static DebugLevel readDebugLevel() {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_DEBUG, property, NULL) > 0) {

View File

@ -178,6 +178,10 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32
FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0,
len, numGlyphs, radius, positions);
if (!shadow.image) {
return NULL;
}
texture = new ShadowTexture;
texture->left = shadow.penX;
texture->top = shadow.penY;

View File

@ -18,6 +18,8 @@
#include "CacheTexture.h"
#include "../Debug.h"
#include "../Extensions.h"
#include "../PixelBuffer.h"
namespace android {
namespace uirenderer {
@ -111,6 +113,11 @@ CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCoun
mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
// OpenGL ES 3.0+ lets us specify the row length for unpack operations such
// as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
// With OpenGL ES 2.0 we have to upload entire stripes instead.
mHasES3 = Extensions::getInstance().getMajorGlVersion() >= 3;
}
CacheTexture::~CacheTexture() {
@ -143,7 +150,7 @@ void CacheTexture::releaseMesh() {
void CacheTexture::releaseTexture() {
if (mTexture) {
delete[] mTexture;
delete mTexture;
mTexture = NULL;
}
if (mTextureId) {
@ -154,6 +161,17 @@ void CacheTexture::releaseTexture() {
mCurrentQuad = 0;
}
void CacheTexture::setLinearFiltering(bool linearFiltering, bool bind) {
if (linearFiltering != mLinearFiltering) {
mLinearFiltering = linearFiltering;
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
}
void CacheTexture::allocateMesh() {
if (!mMesh) {
mMesh = new TextureVertex[mMaxQuadCount * 4];
@ -162,7 +180,7 @@ void CacheTexture::allocateMesh() {
void CacheTexture::allocateTexture() {
if (!mTexture) {
mTexture = new uint8_t[mWidth * mHeight];
mTexture = PixelBuffer::create(GL_ALPHA, mWidth, mHeight);
}
if (!mTextureId) {
@ -183,6 +201,34 @@ void CacheTexture::allocateTexture() {
}
}
bool CacheTexture::upload() {
const Rect& dirtyRect = mDirtyRect;
uint32_t x = mHasES3 ? dirtyRect.left : 0;
uint32_t y = dirtyRect.top;
uint32_t width = mHasES3 ? dirtyRect.getWidth() : mWidth;
uint32_t height = dirtyRect.getHeight();
// The unpack row length only needs to be specified when a new
// texture is bound
if (mHasES3) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
}
mTexture->upload(x, y, width, height, y * mWidth + x);
setDirty(false);
return mHasES3;
}
void CacheTexture::setDirty(bool dirty) {
mDirty = dirty;
if (!dirty) {
mDirtyRect.setEmpty();
}
}
bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
return false;

View File

@ -30,6 +30,8 @@
namespace android {
namespace uirenderer {
class PixelBuffer;
/**
* CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
* Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
@ -83,6 +85,10 @@ public:
void allocateTexture();
void allocateMesh();
// Returns true if glPixelStorei(GL_UNPACK_ROW_LENGTH) must be reset
// This method will also call setDirty(false)
bool upload();
bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
inline uint16_t getWidth() const {
@ -97,7 +103,7 @@ public:
return &mDirtyRect;
}
inline uint8_t* getTexture() const {
inline PixelBuffer* getPixelBuffer() const {
return mTexture;
}
@ -110,13 +116,6 @@ public:
return mDirty;
}
inline void setDirty(bool dirty) {
mDirty = dirty;
if (!dirty) {
mDirtyRect.setEmpty();
}
}
inline bool getLinearFiltering() const {
return mLinearFiltering;
}
@ -124,16 +123,7 @@ public:
/**
* This method assumes that the proper texture unit is active.
*/
void setLinearFiltering(bool linearFiltering, bool bind = true) {
if (linearFiltering != mLinearFiltering) {
mLinearFiltering = linearFiltering;
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
}
void setLinearFiltering(bool linearFiltering, bool bind = true);
inline uint16_t getGlyphCount() const {
return mNumGlyphs;
@ -176,7 +166,9 @@ public:
}
private:
uint8_t* mTexture;
void setDirty(bool dirty);
PixelBuffer* mTexture;
GLuint mTextureId;
uint16_t mWidth;
uint16_t mHeight;
@ -188,6 +180,7 @@ private:
uint32_t mMaxQuadCount;
CacheBlock* mCacheBlocks;
Rect mDirtyRect;
bool mHasES3;
};
}; // namespace uirenderer

View File

@ -25,11 +25,12 @@
#include <SkGlyph.h>
#include <SkUtils.h>
#include "Debug.h"
#include "FontUtil.h"
#include "Font.h"
#include "FontRenderer.h"
#include "Properties.h"
#include "../Debug.h"
#include "../FontRenderer.h"
#include "../PixelBuffer.h"
#include "../Properties.h"
namespace android {
namespace uirenderer {
@ -200,25 +201,23 @@ void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
p[3].x(), p[3].y(), u1, v1, glyph->mCacheTexture);
}
void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
int nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop;
uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds, const float* pos) {
int dstX = x + glyph->mBitmapLeft;
int dstY = y + glyph->mBitmapTop;
CacheTexture* cacheTexture = glyph->mCacheTexture;
uint32_t cacheWidth = cacheTexture->getWidth();
const uint8_t* cacheBuffer = cacheTexture->getTexture();
uint32_t cacheX = 0, cacheY = 0;
int32_t bX = 0, bY = 0;
for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
bitmap[bY * bitmapW + bX] = tempCol;
}
uint32_t cacheWidth = cacheTexture->getWidth();
uint32_t startY = glyph->mStartY * cacheWidth;
uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth);
PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
const uint8_t* cacheBuffer = pixelBuffer->map();
for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY;
cacheY += cacheWidth, bitmapY += bitmapWidth) {
memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
}
}