Correctly support pre-multiplied alpha, optimizations, more stuff.
Add support for the following drawing functions:
- drawBitmap(int[]...)
- drawPaint()
Optimizes shader state changes by enabling/disabling attribute arrays
only when needed.
Adds quick rejects when drawing trivial shapes to avoid unnecessary
OpenGL operations.
Change-Id: Ic2c6c2ed1523d08a63a8c95601a1ec40b6c7fbc9
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 117fccd..f7b4455 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -128,6 +128,7 @@
mTextureCache.clear();
mLayerCache.clear();
+ mPatchCache.clear();
}
///////////////////////////////////////////////////////////////////////////////
@@ -242,7 +243,7 @@
const Rect& rect = layer->layer;
drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
- layer->texture, layer->alpha, layer->mode, layer->blend);
+ layer->texture, layer->alpha, layer->mode, layer->blend, true);
LayerSize size(rect.getWidth(), rect.getHeight());
// Failing to add the layer to the cache should happen only if the
@@ -418,8 +419,15 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) {
+ const float right = left + bitmap->width();
+ const float bottom = top + bitmap->height();
+
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
const Texture* texture = mTextureCache.get(bitmap);
- drawTextureRect(left, top, left + texture->width, top + texture->height, texture, paint);
+ drawTextureRect(left, top, right, bottom, texture, paint);
}
void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) {
@@ -427,6 +435,10 @@
const mat4 transform(*matrix);
transform.mapRect(r);
+ if (quickReject(r.left, r.top, r.right, r.bottom)) {
+ return;
+ }
+
const Texture* texture = mTextureCache.get(bitmap);
drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
}
@@ -435,6 +447,10 @@
float srcLeft, float srcTop, float srcRight, float srcBottom,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) {
+ if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
+ return;
+ }
+
const Texture* texture = mTextureCache.get(bitmap);
const float width = texture->width;
@@ -454,6 +470,10 @@
void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, const SkPaint* paint) {
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
const Texture* texture = mTextureCache.get(bitmap);
int alpha;
@@ -477,6 +497,10 @@
}
void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) {
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
SkXfermode::Mode mode;
const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
@@ -495,6 +519,10 @@
drawColorRect(left, top, right, bottom, color, mode);
}
+///////////////////////////////////////////////////////////////////////////////
+// Drawing implementation
+///////////////////////////////////////////////////////////////////////////////
+
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
int color, SkXfermode::Mode mode) {
const int alpha = (color >> 24) & 0xFF;
@@ -503,24 +531,24 @@
const GLfloat g = ((color >> 8) & 0xFF) / 255.0f;
const GLfloat b = ((color ) & 0xFF) / 255.0f;
+ // Pre-multiplication happens when setting the shader color
chooseBlending(alpha < 255, mode, true);
mModelView.loadTranslate(left, top, 0.0f);
mModelView.scale(right - left, bottom - top, 1.0f);
- useShader(mDrawColorShader);
+ const bool inUse = useShader(mDrawColorShader);
mDrawColorShader->set(mOrthoMatrix, mModelView, mSnapshot->transform);
- const GLvoid* p = &gDrawColorVertices[0].position[0];
-
- glEnableVertexAttribArray(mDrawColorShader->position);
- glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE,
- gDrawColorVertexStride, p);
- glUniform4f(mDrawColorShader->color, r, g, b, a);
+ if (!inUse) {
+ const GLvoid* p = &gDrawColorVertices[0].position[0];
+ glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE,
+ gDrawColorVertexStride, p);
+ }
+ // Render using pre-multiplied alpha
+ glUniform4f(mDrawColorShader->color, r * a, g * a, b * a, a);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
-
- glDisableVertexAttribArray(mDrawColorShader->position);
}
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
@@ -529,8 +557,8 @@
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
- isPremultiplied, &mDrawTextureVertices[0].position[0],
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
+ texture->blend, texture->blend, &mDrawTextureVertices[0].position[0],
&mDrawTextureVertices[0].texture[0], NULL);
}
@@ -555,27 +583,24 @@
// TODO handle tiling and filtering here
- glActiveTexture(GL_TEXTURE0);
- glUniform1i(mDrawTextureShader->sampler, 0);
- glUniform4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha);
+ if (isPremultiplied) {
+ glUniform4f(mDrawTextureShader->color, alpha, alpha, alpha, alpha);
+ } else {
+ glUniform4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha);
+ }
- glEnableVertexAttribArray(mDrawTextureShader->position);
glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE,
gDrawTextureVertexStride, vertices);
-
- glEnableVertexAttribArray(mDrawTextureShader->texCoords);
glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE,
gDrawTextureVertexStride, texCoords);
if (!indices) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
} else {
+ // TODO: Use triangle strip instead
glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
}
- glDisableVertexAttribArray(mDrawTextureShader->position);
- glDisableVertexAttribArray(mDrawTextureShader->texCoords);
-
glBindTexture(GL_TEXTURE_2D, 0);
}
@@ -605,12 +630,14 @@
mBlend = blend;
}
-void OpenGLRenderer::useShader(const sp<Program>& shader) {
+bool OpenGLRenderer::useShader(const sp<Program>& shader) {
if (!shader->isInUse()) {
mCurrentShader->remove();
shader->use();
mCurrentShader = shader;
+ return false;
}
+ return true;
}
void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index afb747f..a52489f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -179,7 +179,7 @@
* @param isPremultiplied Indicates whether the texture has premultiplied alpha
*/
void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
- float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied = true);
+ float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied = false);
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
@@ -194,7 +194,7 @@
* @param isPremultiplied Indicates whether the texture has premultiplied alpha
*/
void drawTextureRect(float left, float top, float right, float bottom, const Texture* texture,
- const SkPaint* paint, bool isPremultiplied = true);
+ const SkPaint* paint, bool isPremultiplied = false);
/**
* Draws a textured mesh with the specified texture. If the indices are omitted, the
@@ -252,8 +252,10 @@
* in use, it will not be bound again. If it is not in use, the current shader is
* marked unused and the specified shader becomes used and becomes the new
* current shader.
+ *
+ * @return true If the specified shader was already in use, false otherwise.
*/
- inline void useShader(const sp<Program>& shader);
+ inline bool useShader(const sp<Program>& shader);
// Dimensions of the drawing surface
int mWidth, mHeight;
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 3b5e5da..609b28a 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -146,6 +146,16 @@
glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
}
+void DrawColorProgram::use() {
+ Program::use();
+ glEnableVertexAttribArray(position);
+}
+
+void DrawColorProgram::remove() {
+ Program::remove();
+ glDisableVertexAttribArray(position);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Draw texture
///////////////////////////////////////////////////////////////////////////////
@@ -156,5 +166,17 @@
sampler = addUniform("sampler");
}
+void DrawTextureProgram::use() {
+ DrawColorProgram::use();
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(sampler, 0);
+ glEnableVertexAttribArray(texCoords);
+}
+
+void DrawTextureProgram::remove() {
+ DrawColorProgram::remove();
+ glDisableVertexAttribArray(texCoords);
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 652befe1..d90bcaf 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -39,18 +39,18 @@
* shaders sources.
*/
Program(const char* vertex, const char* fragment);
- ~Program();
+ virtual ~Program();
/**
* Binds this program to the GL context.
*/
- void use();
+ virtual void use();
/**
* Marks this program as unused. This will not unbind
* the program from the GL context.
*/
- void remove();
+ virtual void remove();
/**
* Indicates whether this program is currently in use with
@@ -129,6 +129,17 @@
const mat4& transformMatrix);
/**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
* Name of the position attribute.
*/
int position;
@@ -157,6 +168,17 @@
DrawTextureProgram();
/**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
* Name of the texture sampler uniform.
*/
int sampler;