Merge "Optimize state changes"

This commit is contained in:
Romain Guy
2011-12-12 20:36:22 -08:00
committed by Android (Google) Code Review
10 changed files with 365 additions and 296 deletions

View File

@@ -73,6 +73,9 @@ void Caches::init() {
glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
mCurrentBuffer = meshBuffer; mCurrentBuffer = meshBuffer;
mCurrentPositionPointer = this;
mCurrentTexCoordsPointer = this;
mRegionMesh = NULL; mRegionMesh = NULL;
blend = false; blend = false;
@@ -218,22 +221,49 @@ void Caches::flush(FlushMode mode) {
// VBO // VBO
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void Caches::bindMeshBuffer() { bool Caches::bindMeshBuffer() {
bindMeshBuffer(meshBuffer); return bindMeshBuffer(meshBuffer);
} }
void Caches::bindMeshBuffer(const GLuint buffer) { bool Caches::bindMeshBuffer(const GLuint buffer) {
if (mCurrentBuffer != buffer) { if (mCurrentBuffer != buffer) {
glBindBuffer(GL_ARRAY_BUFFER, buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer);
mCurrentBuffer = buffer; mCurrentBuffer = buffer;
return true;
} }
return false;
} }
void Caches::unbindMeshBuffer() { bool Caches::unbindMeshBuffer() {
if (mCurrentBuffer) { if (mCurrentBuffer) {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
mCurrentBuffer = 0; mCurrentBuffer = 0;
return true;
} }
return false;
}
void Caches::bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices, GLsizei stride) {
if (force || vertices != mCurrentPositionPointer) {
glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
mCurrentPositionPointer = vertices;
}
}
void Caches::bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices) {
if (force || vertices != mCurrentTexCoordsPointer) {
glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, gMeshStride, vertices);
mCurrentTexCoordsPointer = vertices;
}
}
void Caches::resetVertexPointers() {
mCurrentPositionPointer = this;
mCurrentTexCoordsPointer = this;
}
void Caches::resetTexCoordsVertexPointer() {
mCurrentTexCoordsPointer = this;
} }
TextureVertex* Caches::getRegionMesh() { TextureVertex* Caches::getRegionMesh() {

View File

@@ -91,15 +91,6 @@ class ANDROID_API Caches: public Singleton<Caches> {
CacheLogger mLogger; CacheLogger mLogger;
GLuint mCurrentBuffer;
// Used to render layers
TextureVertex* mRegionMesh;
GLuint mRegionMeshIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
public: public:
enum FlushMode { enum FlushMode {
kFlushMode_Layers = 0, kFlushMode_Layers = 0,
@@ -147,17 +138,36 @@ public:
/** /**
* Binds the VBO used to render simple textured quads. * Binds the VBO used to render simple textured quads.
*/ */
void bindMeshBuffer(); bool bindMeshBuffer();
/** /**
* Binds the specified VBO if needed. * Binds the specified VBO if needed.
*/ */
void bindMeshBuffer(const GLuint buffer); bool bindMeshBuffer(const GLuint buffer);
/** /**
* Unbinds the VBO used to render simple textured quads. * Unbinds the VBO used to render simple textured quads.
*/ */
void unbindMeshBuffer(); bool unbindMeshBuffer();
/**
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
*/
void bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices,
GLsizei stride = gMeshStride);
/**
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
*/
void bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices);
/**
* Resets the vertex pointers.
*/
void resetVertexPointers();
void resetTexCoordsVertexPointer();
/** /**
* Returns the mesh used to draw regions. Calling this method will * Returns the mesh used to draw regions. Calling this method will
@@ -203,6 +213,17 @@ public:
ResourceCache resourceCache; ResourceCache resourceCache;
private: private:
GLuint mCurrentBuffer;
void* mCurrentPositionPointer;
void* mCurrentTexCoordsPointer;
// Used to render layers
TextureVertex* mRegionMesh;
GLuint mRegionMeshIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
DebugLevel mDebugLevel; DebugLevel mDebugLevel;
bool mInitialized; bool mInitialized;
}; // class Caches }; // class Caches

View File

@@ -325,8 +325,6 @@ FontRenderer::FontRenderer() {
mTextTexture = NULL; mTextTexture = NULL;
mIndexBufferID = 0; mIndexBufferID = 0;
mPositionAttrSlot = -1;
mTexcoordAttrSlot = -1;
mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
@@ -599,12 +597,6 @@ void FontRenderer::checkTextureUpdate() {
void FontRenderer::issueDrawCommand() { void FontRenderer::issueDrawCommand() {
checkTextureUpdate(); checkTextureUpdate();
float* vtx = mTextMeshPtr;
float* tex = vtx + 2;
glVertexAttribPointer(mPositionAttrSlot, 2, GL_FLOAT, GL_FALSE, 16, vtx);
glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, GL_FALSE, 16, tex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
@@ -760,11 +752,6 @@ bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text
return false; return false;
} }
if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
LOGE("Font renderer unable to draw, attribute slots undefined");
return false;
}
mDrawn = false; mDrawn = false;
mBounds = bounds; mBounds = bounds;
mClip = clip; mClip = clip;

View File

@@ -178,9 +178,13 @@ public:
mGammaTable = gammaTable; mGammaTable = gammaTable;
} }
void setAttributeBindingSlots(int positionSlot, int texCoordSlot) { inline float* getMeshBuffer() {
mPositionAttrSlot = positionSlot; checkInit();
mTexcoordAttrSlot = texCoordSlot; return mTextMeshPtr;
}
inline int getMeshTexCoordsOffset() const {
return 2;
} }
void setFont(SkPaint* paint, uint32_t fontId, float fontSize); void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
@@ -309,9 +313,6 @@ protected:
uint32_t mIndexBufferID; uint32_t mIndexBufferID;
int32_t mPositionAttrSlot;
int32_t mTexcoordAttrSlot;
const Rect* mClip; const Rect* mClip;
Rect* mBounds; Rect* mBounds;
bool mDrawn; bool mDrawn;

View File

@@ -199,13 +199,13 @@ void OpenGLRenderer::interrupt() {
} }
} }
mCaches.unbindMeshBuffer(); mCaches.unbindMeshBuffer();
mCaches.resetVertexPointers();
} }
void OpenGLRenderer::resume() { void OpenGLRenderer::resume() {
sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot; sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
@@ -1201,16 +1201,15 @@ void OpenGLRenderer::setupDrawColorFilterUniforms() {
} }
void OpenGLRenderer::setupDrawSimpleMesh() { void OpenGLRenderer::setupDrawSimpleMesh() {
mCaches.bindMeshBuffer(); bool force = mCaches.bindMeshBuffer();
glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, 0);
gMeshStride, 0);
} }
void OpenGLRenderer::setupDrawTexture(GLuint texture) { void OpenGLRenderer::setupDrawTexture(GLuint texture) {
bindTexture(texture); bindTexture(texture);
glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++); glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); mTexCoordsSlot = mCaches.currentProgram->texCoords;
glEnableVertexAttribArray(mTexCoordsSlot); glEnableVertexAttribArray(mTexCoordsSlot);
} }
@@ -1218,7 +1217,7 @@ void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
bindExternalTexture(texture); bindExternalTexture(texture);
glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++); glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); mTexCoordsSlot = mCaches.currentProgram->texCoords;
glEnableVertexAttribArray(mTexCoordsSlot); glEnableVertexAttribArray(mTexCoordsSlot);
} }
@@ -1232,23 +1231,23 @@ void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
} }
void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
bool force = false;
if (!vertices) { if (!vertices) {
mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
} else { } else {
mCaches.unbindMeshBuffer(); force = mCaches.unbindMeshBuffer();
} }
glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
gMeshStride, vertices);
if (mTexCoordsSlot >= 0) { if (mTexCoordsSlot >= 0) {
glVertexAttribPointer(mTexCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords); mCaches.bindTexCoordsVertexPointer(force, mTexCoordsSlot, texCoords);
} }
} }
void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) { void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
mCaches.unbindMeshBuffer(); bool force = mCaches.unbindMeshBuffer();
glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
gVertexStride, vertices); vertices, gVertexStride);
} }
/** /**
@@ -1264,10 +1263,10 @@ void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
*/ */
void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords, void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
GLvoid* lengthCoords, float boundaryWidthProportion) { GLvoid* lengthCoords, float boundaryWidthProportion) {
mCaches.unbindMeshBuffer(); bool force = mCaches.unbindMeshBuffer();
mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, vertices, gAAVertexStride);
gAAVertexStride, vertices); mCaches.resetTexCoordsVertexPointer();
int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth"); int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
glEnableVertexAttribArray(widthSlot); glEnableVertexAttribArray(widthSlot);
@@ -2186,12 +2185,14 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
#else #else
bool hasActiveLayer = false; bool hasActiveLayer = false;
#endif #endif
mCaches.unbindMeshBuffer();
// Tell font renderer the locations of position and texture coord float* buffer = fontRenderer.getMeshBuffer();
// attributes so it can bind its data properly int offset = fontRenderer.getMeshTexCoordsOffset();
int positionSlot = mCaches.currentProgram->position;
fontRenderer.setAttributeBindingSlots(positionSlot, mTexCoordsSlot); bool force = mCaches.unbindMeshBuffer();
mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, buffer);
mCaches.bindTexCoordsVertexPointer(force, mTexCoordsSlot, buffer + offset);
if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y, if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
hasActiveLayer ? &bounds : NULL)) { hasActiveLayer ? &bounds : NULL)) {
#if RENDER_LAYERS_AS_REGIONS #if RENDER_LAYERS_AS_REGIONS
@@ -2205,7 +2206,7 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
} }
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); glDisableVertexAttribArray(mCaches.currentProgram->texCoords);
drawTextDecorations(text, bytesCount, length, oldX, oldY, paint); drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
} }

View File

@@ -197,7 +197,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
} }
if (verticesCount > 0) { if (verticesCount > 0) {
Caches::getInstance().bindMeshBuffer(meshBuffer); Caches& caches = Caches::getInstance();
caches.bindMeshBuffer(meshBuffer);
if (!mUploaded) { if (!mUploaded) {
glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount, glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
mVertices, GL_DYNAMIC_DRAW); mVertices, GL_DYNAMIC_DRAW);
@@ -206,6 +207,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
glBufferSubData(GL_ARRAY_BUFFER, 0, glBufferSubData(GL_ARRAY_BUFFER, 0,
sizeof(TextureVertex) * verticesCount, mVertices); sizeof(TextureVertex) * verticesCount, mVertices);
} }
caches.resetVertexPointers();
} }
PATCH_LOGD(" patch: new vertices count = %d", verticesCount); PATCH_LOGD(" patch: new vertices count = %d", verticesCount);

View File

@@ -26,7 +26,7 @@ namespace uirenderer {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// TODO: Program instance should be created from a factory method // TODO: Program instance should be created from a factory method
Program::Program(const char* vertex, const char* fragment) { Program::Program(const ProgramDescription& description, const char* vertex, const char* fragment) {
mInitialized = false; mInitialized = false;
mHasColorUniform = false; mHasColorUniform = false;
mUse = false; mUse = false;
@@ -43,6 +43,12 @@ Program::Program(const char* vertex, const char* fragment) {
glAttachShader(mProgramId, mFragmentShader); glAttachShader(mProgramId, mFragmentShader);
position = bindAttrib("position", kBindingPosition); position = bindAttrib("position", kBindingPosition);
if (description.hasTexture || description.hasExternalTexture) {
texCoords = bindAttrib("texCoords", kBindingTexCoords);
} else {
texCoords = -1;
}
glLinkProgram(mProgramId); glLinkProgram(mProgramId);
GLint status; GLint status;

View File

@@ -17,16 +17,264 @@
#ifndef ANDROID_HWUI_PROGRAM_H #ifndef ANDROID_HWUI_PROGRAM_H
#define ANDROID_HWUI_PROGRAM_H #define ANDROID_HWUI_PROGRAM_H
#include <utils/KeyedVector.h>
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <GLES2/gl2ext.h> #include <GLES2/gl2ext.h>
#include <utils/KeyedVector.h> #include <SkXfermode.h>
#include "Matrix.h" #include "Matrix.h"
#include "Properties.h"
namespace android { namespace android {
namespace uirenderer { namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
// Debug
#if DEBUG_PROGRAMS
#define PROGRAM_LOGD(...) LOGD(__VA_ARGS__)
#else
#define PROGRAM_LOGD(...)
#endif
#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH))
#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH)
#define PROGRAM_KEY_TEXTURE 0x1
#define PROGRAM_KEY_A8_TEXTURE 0x2
#define PROGRAM_KEY_BITMAP 0x4
#define PROGRAM_KEY_GRADIENT 0x8
#define PROGRAM_KEY_BITMAP_FIRST 0x10
#define PROGRAM_KEY_COLOR_MATRIX 0x20
#define PROGRAM_KEY_COLOR_LIGHTING 0x40
#define PROGRAM_KEY_COLOR_BLEND 0x80
#define PROGRAM_KEY_BITMAP_NPOT 0x100
#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
// Encode the xfermodes on 6 bits
#define PROGRAM_MAX_XFERMODE 0x1f
#define PROGRAM_XFERMODE_SHADER_SHIFT 26
#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
#define PROGRAM_GRADIENT_TYPE_SHIFT 33
#define PROGRAM_MODULATE_SHIFT 35
#define PROGRAM_IS_POINT_SHIFT 36
#define PROGRAM_HAS_AA_SHIFT 37
#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
typedef uint64_t programid;
///////////////////////////////////////////////////////////////////////////////
// Program description
///////////////////////////////////////////////////////////////////////////////
/**
* Describe the features required for a given program. The features
* determine the generation of both the vertex and fragment shaders.
* A ProgramDescription must be used in conjunction with a ProgramCache.
*/
struct ProgramDescription {
enum ColorModifier {
kColorNone,
kColorMatrix,
kColorLighting,
kColorBlend
};
enum Gradient {
kGradientLinear,
kGradientCircular,
kGradientSweep
};
ProgramDescription() {
reset();
}
// Texturing
bool hasTexture;
bool hasAlpha8Texture;
bool hasExternalTexture;
bool hasTextureTransform;
// Modulate, this should only be set when setColor() return true
bool modulate;
// Shaders
bool hasBitmap;
bool isBitmapNpot;
bool isAA;
bool hasGradient;
Gradient gradientType;
SkXfermode::Mode shadersMode;
bool isBitmapFirst;
GLenum bitmapWrapS;
GLenum bitmapWrapT;
// Color operations
ColorModifier colorOp;
SkXfermode::Mode colorMode;
// Framebuffer blending (requires Extensions.hasFramebufferFetch())
// Ignored for all values < SkXfermode::kPlus_Mode
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
bool isPoint;
float pointSize;
/**
* Resets this description. All fields are reset back to the default
* values they hold after building a new instance.
*/
void reset() {
hasTexture = false;
hasAlpha8Texture = false;
hasExternalTexture = false;
hasTextureTransform = false;
isAA = false;
modulate = false;
hasBitmap = false;
isBitmapNpot = false;
hasGradient = false;
gradientType = kGradientLinear;
shadersMode = SkXfermode::kClear_Mode;
isBitmapFirst = false;
bitmapWrapS = GL_CLAMP_TO_EDGE;
bitmapWrapT = GL_CLAMP_TO_EDGE;
colorOp = kColorNone;
colorMode = SkXfermode::kClear_Mode;
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
isPoint = false;
pointSize = 0.0f;
}
/**
* Indicates, for a given color, whether color modulation is required in
* the fragment shader. When this method returns true, the program should
* be provided with a modulation color.
*/
bool setColor(const float r, const float g, const float b, const float a) {
modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD ||
g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD;
return modulate;
}
/**
* Indicates, for a given color, whether color modulation is required in
* the fragment shader. When this method returns true, the program should
* be provided with a modulation color.
*/
bool setAlpha8Color(const float r, const float g, const float b, const float a) {
modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
return modulate;
}
/**
* Computes the unique key identifying this program.
*/
programid key() const {
programid key = 0;
if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
if (hasBitmap) {
key |= PROGRAM_KEY_BITMAP;
if (isBitmapNpot) {
key |= PROGRAM_KEY_BITMAP_NPOT;
key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
}
}
if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
if (hasBitmap && hasGradient) {
key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
}
switch (colorOp) {
case kColorMatrix:
key |= PROGRAM_KEY_COLOR_MATRIX;
break;
case kColorLighting:
key |= PROGRAM_KEY_COLOR_LIGHTING;
break;
case kColorBlend:
key |= PROGRAM_KEY_COLOR_BLEND;
key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
break;
case kColorNone:
break;
}
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
return key;
}
/**
* Logs the specified message followed by the key identifying this program.
*/
void log(const char* message) const {
#if DEBUG_PROGRAMS
programid k = key();
PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
uint32_t(k & 0xffffffff));
#endif
}
private:
inline uint32_t getEnumForWrap(GLenum wrap) const {
switch (wrap) {
case GL_CLAMP_TO_EDGE:
return 0;
case GL_REPEAT:
return 1;
case GL_MIRRORED_REPEAT:
return 2;
}
return 0;
}
}; // struct ProgramDescription
/** /**
* A program holds a vertex and a fragment shader. It offers several utility * A program holds a vertex and a fragment shader. It offers several utility
* methods to query attributes and uniforms. * methods to query attributes and uniforms.
@@ -42,7 +290,7 @@ public:
* Creates a new program with the specified vertex and fragment * Creates a new program with the specified vertex and fragment
* shaders sources. * shaders sources.
*/ */
Program(const char* vertex, const char* fragment); Program(const ProgramDescription& description, const char* vertex, const char* fragment);
virtual ~Program(); virtual ~Program();
/** /**
@@ -98,6 +346,11 @@ public:
*/ */
int position; int position;
/**
* Name of the texCoords attribute if it exists, -1 otherwise.
*/
int texCoords;
/** /**
* Name of the transform uniform. * Name of the transform uniform.
*/ */

View File

@@ -388,7 +388,7 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr
String8 vertexShader = generateVertexShader(description); String8 vertexShader = generateVertexShader(description);
String8 fragmentShader = generateFragmentShader(description); String8 fragmentShader = generateFragmentShader(description);
Program* program = new Program(vertexShader.string(), fragmentShader.string()); Program* program = new Program(description, vertexShader.string(), fragmentShader.string());
return program; return program;
} }

View File

@@ -23,8 +23,6 @@
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <SkXfermode.h>
#include "Debug.h" #include "Debug.h"
#include "Program.h" #include "Program.h"
#include "Properties.h" #include "Properties.h"
@@ -43,240 +41,10 @@ namespace uirenderer {
#define PROGRAM_LOGD(...) #define PROGRAM_LOGD(...)
#endif #endif
#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH))
#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH)
#define PROGRAM_KEY_TEXTURE 0x1
#define PROGRAM_KEY_A8_TEXTURE 0x2
#define PROGRAM_KEY_BITMAP 0x4
#define PROGRAM_KEY_GRADIENT 0x8
#define PROGRAM_KEY_BITMAP_FIRST 0x10
#define PROGRAM_KEY_COLOR_MATRIX 0x20
#define PROGRAM_KEY_COLOR_LIGHTING 0x40
#define PROGRAM_KEY_COLOR_BLEND 0x80
#define PROGRAM_KEY_BITMAP_NPOT 0x100
#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
// Encode the xfermodes on 6 bits
#define PROGRAM_MAX_XFERMODE 0x1f
#define PROGRAM_XFERMODE_SHADER_SHIFT 26
#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
#define PROGRAM_GRADIENT_TYPE_SHIFT 33
#define PROGRAM_MODULATE_SHIFT 35
#define PROGRAM_IS_POINT_SHIFT 36
#define PROGRAM_HAS_AA_SHIFT 37
#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
typedef uint64_t programid;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Cache // Cache
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/**
* Describe the features required for a given program. The features
* determine the generation of both the vertex and fragment shaders.
* A ProgramDescription must be used in conjunction with a ProgramCache.
*/
struct ProgramDescription {
enum ColorModifier {
kColorNone,
kColorMatrix,
kColorLighting,
kColorBlend
};
enum Gradient {
kGradientLinear,
kGradientCircular,
kGradientSweep
};
ProgramDescription() {
reset();
}
// Texturing
bool hasTexture;
bool hasAlpha8Texture;
bool hasExternalTexture;
bool hasTextureTransform;
// Modulate, this should only be set when setColor() return true
bool modulate;
// Shaders
bool hasBitmap;
bool isBitmapNpot;
bool isAA;
bool hasGradient;
Gradient gradientType;
SkXfermode::Mode shadersMode;
bool isBitmapFirst;
GLenum bitmapWrapS;
GLenum bitmapWrapT;
// Color operations
ColorModifier colorOp;
SkXfermode::Mode colorMode;
// Framebuffer blending (requires Extensions.hasFramebufferFetch())
// Ignored for all values < SkXfermode::kPlus_Mode
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
bool isPoint;
float pointSize;
/**
* Resets this description. All fields are reset back to the default
* values they hold after building a new instance.
*/
void reset() {
hasTexture = false;
hasAlpha8Texture = false;
hasExternalTexture = false;
hasTextureTransform = false;
isAA = false;
modulate = false;
hasBitmap = false;
isBitmapNpot = false;
hasGradient = false;
gradientType = kGradientLinear;
shadersMode = SkXfermode::kClear_Mode;
isBitmapFirst = false;
bitmapWrapS = GL_CLAMP_TO_EDGE;
bitmapWrapT = GL_CLAMP_TO_EDGE;
colorOp = kColorNone;
colorMode = SkXfermode::kClear_Mode;
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
isPoint = false;
pointSize = 0.0f;
}
/**
* Indicates, for a given color, whether color modulation is required in
* the fragment shader. When this method returns true, the program should
* be provided with a modulation color.
*/
bool setColor(const float r, const float g, const float b, const float a) {
modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD ||
g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD;
return modulate;
}
/**
* Indicates, for a given color, whether color modulation is required in
* the fragment shader. When this method returns true, the program should
* be provided with a modulation color.
*/
bool setAlpha8Color(const float r, const float g, const float b, const float a) {
modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
return modulate;
}
/**
* Computes the unique key identifying this program.
*/
programid key() const {
programid key = 0;
if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
if (hasBitmap) {
key |= PROGRAM_KEY_BITMAP;
if (isBitmapNpot) {
key |= PROGRAM_KEY_BITMAP_NPOT;
key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
}
}
if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
if (hasBitmap && hasGradient) {
key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
}
switch (colorOp) {
case kColorMatrix:
key |= PROGRAM_KEY_COLOR_MATRIX;
break;
case kColorLighting:
key |= PROGRAM_KEY_COLOR_LIGHTING;
break;
case kColorBlend:
key |= PROGRAM_KEY_COLOR_BLEND;
key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
break;
case kColorNone:
break;
}
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
return key;
}
/**
* Logs the specified message followed by the key identifying this program.
*/
void log(const char* message) const {
#if DEBUG_PROGRAMS
programid k = key();
PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
uint32_t(k & 0xffffffff));
#endif
}
private:
inline uint32_t getEnumForWrap(GLenum wrap) const {
switch (wrap) {
case GL_CLAMP_TO_EDGE:
return 0;
case GL_REPEAT:
return 1;
case GL_MIRRORED_REPEAT:
return 2;
}
return 0;
}
}; // struct ProgramDescription
/** /**
* Generates and caches program. Programs are generated based on * Generates and caches program. Programs are generated based on
* ProgramDescriptions. * ProgramDescriptions.