summaryrefslogtreecommitdiffstats
path: root/libs/hwui
diff options
context:
space:
mode:
authorRomain Guy <romainguy@google.com>2010-09-09 14:42:43 -0700
committerRomain Guy <romainguy@google.com>2010-09-09 14:42:43 -0700
commita5aed0d58962a24c44728ffc46dc9e1ba2f9fda5 (patch)
tree4194d14711b2cba0e8192e5f1c0764f530bc7309 /libs/hwui
parentd90f23e24a4d1768d5a7ed0e7072e67af6330a45 (diff)
downloadframeworks_base-a5aed0d58962a24c44728ffc46dc9e1ba2f9fda5.zip
frameworks_base-a5aed0d58962a24c44728ffc46dc9e1ba2f9fda5.tar.gz
frameworks_base-a5aed0d58962a24c44728ffc46dc9e1ba2f9fda5.tar.bz2
Add support for advanced blend modes with the framebuffer.
This adds the ability to blend with the framebuffer using Darken, Lighten, Add, Multiply, Overlay and Screen. Change-Id: Iae01a53797d4ad39c373cba6ff2a42293129da1a
Diffstat (limited to 'libs/hwui')
-rw-r--r--libs/hwui/Extensions.h19
-rw-r--r--libs/hwui/OpenGLRenderer.cpp97
-rw-r--r--libs/hwui/OpenGLRenderer.h4
-rw-r--r--libs/hwui/ProgramCache.cpp22
-rw-r--r--libs/hwui/ProgramCache.h11
5 files changed, 115 insertions, 38 deletions
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 7778290..d50d36e 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -26,17 +26,33 @@
namespace android {
namespace uirenderer {
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_EXTENSIONS 0
+
+// Debug
+#if DEBUG_EXTENSIONS
+ #define EXT_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define EXT_LOGD(...)
+#endif
+
class Extensions {
public:
Extensions() {
const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
const char* current = buffer;
const char* head = current;
+ EXT_LOGD("Available GL extensions:");
do {
head = strchr(current, ' ');
String8 s(current, head ? head - current : strlen(current));
if (s.length()) {
mExtensionList.add(s);
+ EXT_LOGD(" %s", s.string());
}
current = head + 1;
} while (head);
@@ -44,6 +60,7 @@ public:
mHasNPot = hasExtension("GL_OES_texture_npot");
mHasDrawPath = hasExtension("GL_NV_draw_path");
mHasCoverageSample = hasExtension("GL_NV_coverage_sample");
+ mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
mExtensions = buffer;
}
@@ -51,6 +68,7 @@ public:
inline bool hasNPot() const { return mHasNPot; }
inline bool hasDrawPath() const { return mHasDrawPath; }
inline bool hasCoverageSample() const { return mHasCoverageSample; }
+ inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
bool hasExtension(const char* extension) const {
const String8 s(extension);
@@ -69,6 +87,7 @@ private:
bool mHasNPot;
bool mHasDrawPath;
bool mHasCoverageSample;
+ bool mHasFramebufferFetch;
}; // class Extensions
}; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f70bca7..6c90704 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -240,10 +240,14 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
if (p) {
alpha = p->getAlpha();
- const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
- if (!isMode) {
- // Assume SRC_OVER
- mode = SkXfermode::kSrcOver_Mode;
+ if (!mExtensions.hasFramebufferFetch()) {
+ const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
+ if (!isMode) {
+ // Assume SRC_OVER
+ mode = SkXfermode::kSrcOver_Mode;
+ }
+ } else {
+ mode = getXfermode(p->getXfermode());
}
} else {
mode = SkXfermode::kSrcOver_Mode;
@@ -519,11 +523,14 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
}
SkXfermode::Mode mode;
-
- const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
- if (!isMode) {
- // Assume SRC_OVER
- mode = SkXfermode::kSrcOver_Mode;
+ if (!mExtensions.hasFramebufferFetch()) {
+ const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
+ if (!isMode) {
+ // Assume SRC_OVER
+ mode = SkXfermode::kSrcOver_Mode;
+ }
+ } else {
+ mode = getXfermode(p->getXfermode());
}
// Skia draws using the color's alpha channel if < 255
@@ -731,11 +738,12 @@ void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t
}
}
+ // Setup the blending mode
+ chooseBlending(true, mode, description);
+
// Build and use the appropriate shader
useProgram(mCaches.programCache.get(description));
- // Setup the blending mode
- chooseBlending(true, mode);
bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit);
@@ -837,9 +845,6 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
GLuint textureUnit = 0;
- // Setup the blending mode
- chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode);
-
// Describe the required shaders
ProgramDescription description;
if (mShader) {
@@ -849,6 +854,9 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
mColorFilter->describe(description, mExtensions);
}
+ // Setup the blending mode
+ chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
+
// Build and use the appropriate shader
useProgram(mCaches.programCache.get(description));
@@ -907,11 +915,11 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
mModelView.loadTranslate(left, top, 0.0f);
mModelView.scale(right - left, bottom - top, 1.0f);
+ chooseBlending(blend || alpha < 1.0f, mode, description);
+
useProgram(mCaches.programCache.get(description));
mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
- chooseBlending(blend || alpha < 1.0f, mode);
-
// Texture
bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
@@ -939,23 +947,35 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
glDisableVertexAttribArray(texCoordsSlot);
}
-void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
+void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
+ ProgramDescription& description) {
blend = blend || mode != SkXfermode::kSrcOver_Mode;
if (blend) {
- if (!mCaches.blend) {
- glEnable(GL_BLEND);
- }
+ if (mode < SkXfermode::kPlus_Mode) {
+ if (!mCaches.blend) {
+ glEnable(GL_BLEND);
+ }
- GLenum sourceMode = gBlends[mode].src;
- GLenum destMode = gBlends[mode].dst;
- if (!isPremultiplied && sourceMode == GL_ONE) {
- sourceMode = GL_SRC_ALPHA;
- }
+ GLenum sourceMode = gBlends[mode].src;
+ GLenum destMode = gBlends[mode].dst;
- if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
- glBlendFunc(sourceMode, destMode);
- mCaches.lastSrcMode = sourceMode;
- mCaches.lastDstMode = destMode;
+ if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
+ glBlendFunc(sourceMode, destMode);
+ mCaches.lastSrcMode = sourceMode;
+ mCaches.lastDstMode = destMode;
+ }
+ } else {
+ // These blend modes are not supported by OpenGL directly and have
+ // to be implemented using shaders. Since the shader will perform
+ // the blending, turn blending off here
+ if (mExtensions.hasFramebufferFetch()) {
+ description.framebufferMode = mode;
+ }
+
+ if (mCaches.blend) {
+ glDisable(GL_BLEND);
+ }
+ blend = false;
}
} else if (mCaches.blend) {
glDisable(GL_BLEND);
@@ -983,10 +1003,14 @@ void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, flo
void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
if (paint) {
- const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
- if (!isMode) {
- // Assume SRC_OVER
- *mode = SkXfermode::kSrcOver_Mode;
+ if (!mExtensions.hasFramebufferFetch()) {
+ const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
+ if (!isMode) {
+ // Assume SRC_OVER
+ *mode = SkXfermode::kSrcOver_Mode;
+ }
+ } else {
+ *mode = getXfermode(paint->getXfermode());
}
// Skia draws using the color's alpha channel if < 255
@@ -1002,6 +1026,13 @@ void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermod
}
}
+SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) {
+ if (mode == NULL) {
+ return SkXfermode::kSrcOver_Mode;
+ }
+ return mode->fMode;
+}
+
void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) {
glActiveTexture(gTextureUnits[textureUnit]);
glBindTexture(GL_TEXTURE_2D, texture);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0e90d20..50f42c2 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -322,7 +322,9 @@ private:
* Enable or disable blending as necessary. This function sets the appropriate
* blend function based on the specified xfermode.
*/
- inline void chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied = true);
+ inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description);
+
+ inline SkXfermode::Mode getXfermode(SkXfermode* mode);
/**
* Use the specified program with the current GL context. If the program is already
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 39fe85a..ff65c1b 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -66,6 +66,8 @@ const char* gVS_Footer =
// Fragment shaders snippets
///////////////////////////////////////////////////////////////////////////////
+const char* gFS_Header_Extension_FramebufferFetch =
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n\n";
const char* gFS_Header =
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
@@ -115,6 +117,8 @@ const char* gFS_Main_BitmapShader_Modulate =
" fragColor = bitmapColor * fragColor.a;\n";
const char* gFS_Main_FragColor =
" gl_FragColor = fragColor;\n";
+const char* gFS_Main_FragColor_Blend =
+ " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n";
const char* gFS_Main_ApplyColorOp[4] = {
// None
"",
@@ -281,7 +285,14 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
// Set the default precision
- String8 shader(gFS_Header);
+ String8 shader;
+
+ bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
+ if (blendFramebuffer) {
+ shader.append(gFS_Header_Extension_FramebufferFetch);
+ }
+
+ shader.append(gFS_Header);
// Varyings
if (description.hasTexture) {
@@ -315,6 +326,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.colorOp == ProgramDescription::kColorBlend) {
generateBlend(shader, "blendColors", description.colorMode);
}
+ if (blendFramebuffer) {
+ generateBlend(shader, "blendFramebuffer", description.framebufferMode);
+ }
if (description.isBitmapNpot) {
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
}
@@ -359,7 +373,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Apply the color op if needed
shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
// Output the fragment
- shader.append(gFS_Main_FragColor);
+ if (!blendFramebuffer) {
+ shader.append(gFS_Main_FragColor);
+ } else {
+ shader.append(gFS_Main_FragColor_Blend);
+ }
}
// End the shader
shader.append(gFS_Footer);
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index f211ab6..8f5304d 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -35,7 +35,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Debug
-#define DEBUG_PROGRAM_CACHE 0
+#define DEBUG_PROGRAM_CACHE 1
// Debug
#if DEBUG_PROGRAM_CACHE
@@ -61,6 +61,7 @@ namespace uirenderer {
#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
@@ -93,7 +94,8 @@ struct ProgramDescription {
hasBitmap(false), isBitmapNpot(false), hasGradient(false),
shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false),
bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE),
- colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode) {
+ colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode),
+ framebufferMode(SkXfermode::kClear_Mode) {
}
// Texturing
@@ -113,6 +115,10 @@ struct ProgramDescription {
int colorOp;
SkXfermode::Mode colorMode;
+ // Framebuffer blending (requires Extensions.hasFramebufferFetch())
+ // Ignored for all values < SkXfermode::kPlus_Mode
+ SkXfermode::Mode framebufferMode;
+
inline uint32_t getEnumForWrap(GLenum wrap) const {
switch (wrap) {
case GL_CLAMP_TO_EDGE:
@@ -156,6 +162,7 @@ struct ProgramDescription {
case kColorNone:
break;
}
+ key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
return key;
}
}; // struct ProgramDescription