Add a RenderBuffer object to store stencil buffers.

Bug #7146141

This change is needed to add a render buffer cache to avoid
creating and destroying stencil buffers on every frame.

This change also allows the renderer to use a 1 bit or 4 bit
stencil buffer whenever possible.

Finally this change fixes a bug introduced by a previous CL
which causes the stencil buffer to not be updated in certain
conditions. The fix relies on a new optional parameter in
drawColorRects() that can be used to avoid performing a
quickReject on rectangles generated by the clip region.

Change-Id: I2f55a8e807009887b276a83cde9f53fd5c01199f
This commit is contained in:
Romain Guy
2013-02-06 16:51:04 -08:00
parent c2a972131f
commit 3bbacf27c0
17 changed files with 369 additions and 114 deletions

View File

@@ -14,6 +14,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
DisplayListLogBuffer.cpp \
DisplayListRenderer.cpp \
Dither.cpp \
Extensions.cpp \
FboCache.cpp \
GradientCache.cpp \
Layer.cpp \

View File

@@ -47,7 +47,7 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
Caches::Caches(): Singleton<Caches>(), mInitialized(false) {
Caches::Caches(): Singleton<Caches>(), mExtensions(Extensions::getInstance()), mInitialized(false) {
init();
initFont();
initExtensions();
@@ -100,7 +100,7 @@ void Caches::initFont() {
}
void Caches::initExtensions() {
if (extensions.hasDebugMarker()) {
if (mExtensions.hasDebugMarker()) {
eventMark = glInsertEventMarkerEXT;
startMark = glPushGroupMarkerEXT;
endMark = glPopGroupMarkerEXT;
@@ -110,7 +110,7 @@ void Caches::initExtensions() {
endMark = endMarkNull;
}
if (extensions.hasDebugLabel()) {
if (mExtensions.hasDebugLabel()) {
setLabel = glLabelObjectEXT;
getLabel = glGetObjectLabelEXT;
} else {
@@ -470,13 +470,13 @@ void Caches::resetScissor() {
///////////////////////////////////////////////////////////////////////////////
void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
if (extensions.hasTiledRendering() && !debugOverdraw) {
if (mExtensions.hasTiledRendering() && !debugOverdraw) {
glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
}
}
void Caches::endTiling() {
if (extensions.hasTiledRendering() && !debugOverdraw) {
if (mExtensions.hasTiledRendering() && !debugOverdraw) {
glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
}
}

View File

@@ -25,7 +25,6 @@
#include <cutils/compiler.h>
#include "Extensions.h"
#include "FontRenderer.h"
#include "GammaFontRenderer.h"
#include "TextureCache.h"
@@ -244,9 +243,6 @@ public:
// VBO to draw with
GLuint meshBuffer;
// GL extensions
Extensions extensions;
// Misc
GLint maxTextureSize;
bool debugLayersUpdates;
@@ -312,6 +308,8 @@ private:
GLint mScissorWidth;
GLint mScissorHeight;
Extensions& mExtensions;
// Used to render layers
TextureVertex* mRegionMesh;
GLuint mRegionMeshIndices;

View File

@@ -44,6 +44,9 @@
// Turn on to display info about layers
#define DEBUG_LAYERS 0
// Turn on to make stencil operations easier to debug
#define DEBUG_STENCIL 0
// Turn on to display debug info about 9patch objects
#define DEBUG_PATCHES 0
// Turn on to "explode" 9patch objects

87
libs/hwui/Extensions.cpp Normal file
View File

@@ -0,0 +1,87 @@
/*
* 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.
*/
#include "Debug.h"
#include "Extensions.h"
namespace android {
using namespace uirenderer;
ANDROID_SINGLETON_STATIC_INSTANCE(Extensions);
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
// Debug
#if DEBUG_EXTENSIONS
#define EXT_LOGD(...) ALOGD(__VA_ARGS__)
#else
#define EXT_LOGD(...)
#endif
///////////////////////////////////////////////////////////////////////////////
// Constructors
///////////////////////////////////////////////////////////////////////////////
Extensions::Extensions(): Singleton<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);
mHasNPot = hasExtension("GL_OES_texture_npot");
mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
mHasDebugLabel = hasExtension("GL_EXT_debug_label");
mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
mHas1BitStencil = hasExtension("GL_OES_stencil1");
mHas4BitStencil = hasExtension("GL_OES_stencil4");
mExtensions = strdup(buffer);
}
Extensions::~Extensions() {
free(mExtensions);
}
///////////////////////////////////////////////////////////////////////////////
// Methods
///////////////////////////////////////////////////////////////////////////////
bool Extensions::hasExtension(const char* extension) const {
const String8 s(extension);
return mExtensionList.indexOf(s) >= 0;
}
void Extensions::dump() const {
ALOGD("Supported extensions:\n%s", mExtensions);
}
}; // namespace uirenderer
}; // namespace android

View File

@@ -17,62 +17,24 @@
#ifndef ANDROID_HWUI_EXTENSIONS_H
#define ANDROID_HWUI_EXTENSIONS_H
#include <utils/Singleton.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include "Debug.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
// Debug
#if DEBUG_EXTENSIONS
#define EXT_LOGD(...) ALOGD(__VA_ARGS__)
#else
#define EXT_LOGD(...)
#endif
///////////////////////////////////////////////////////////////////////////////
// Classes
///////////////////////////////////////////////////////////////////////////////
class Extensions {
class Extensions: public Singleton<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);
mHasNPot = hasExtension("GL_OES_texture_npot");
mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
mHasDebugLabel = hasExtension("GL_EXT_debug_label");
mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
mExtensions = strdup(buffer);
}
~Extensions() {
free(mExtensions);
}
Extensions();
~Extensions();
inline bool hasNPot() const { return mHasNPot; }
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
@@ -80,17 +42,16 @@ public:
inline bool hasDebugMarker() const { return mHasDebugMarker; }
inline bool hasDebugLabel() const { return mHasDebugLabel; }
inline bool hasTiledRendering() const { return mHasTiledRendering; }
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
bool hasExtension(const char* extension) const {
const String8 s(extension);
return mExtensionList.indexOf(s) >= 0;
}
bool hasExtension(const char* extension) const;
void dump() {
ALOGD("Supported extensions:\n%s", mExtensions);
}
void dump() const;
private:
friend class Singleton<Extensions>;
SortedVector<String8> mExtensionList;
char* mExtensions;
@@ -101,6 +62,8 @@ private:
bool mHasDebugMarker;
bool mHasDebugLabel;
bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
}; // class Extensions
}; // namespace uirenderer

View File

@@ -152,7 +152,7 @@ void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
GradientInfo& info) {
uint32_t width = 256 * (count - 1);
if (!Caches::getInstance().extensions.hasNPot()) {
if (!Extensions::getInstance().hasNPot()) {
width = 1 << (31 - __builtin_clz(width));
}

View File

@@ -41,7 +41,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
renderer = NULL;
displayList = NULL;
fbo = 0;
stencil = 0;
stencil = NULL;
debugDrawUpdate = false;
Caches::getInstance().resourceCache.incrementRefcount(this);
}
@@ -87,8 +87,8 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
}
if (stencil) {
bindStencilRenderBuffer();
allocateStencilRenderBuffer();
stencil->bind();
stencil->resize(desiredWidth, desiredHeight);
if (glGetError() != GL_NO_ERROR) {
setSize(oldWidth, oldHeight);
@@ -108,8 +108,8 @@ void Layer::removeFbo(bool flush) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
glDeleteRenderbuffers(1, &stencil);
stencil = 0;
delete stencil;
stencil = NULL;
}
if (fbo) {

View File

@@ -26,6 +26,7 @@
#include <SkXfermode.h>
#include "Rect.h"
#include "RenderBuffer.h"
#include "SkiaColorFilter.h"
#include "Texture.h"
#include "Vertex.h"
@@ -86,11 +87,11 @@ struct Layer {
deferredUpdateScheduled = true;
}
inline uint32_t getWidth() {
inline uint32_t getWidth() const {
return texture.width;
}
inline uint32_t getHeight() {
inline uint32_t getHeight() const {
return texture.height;
}
@@ -116,7 +117,7 @@ struct Layer {
texture.blend = blend;
}
inline bool isBlend() {
inline bool isBlend() const {
return texture.blend;
}
@@ -129,11 +130,11 @@ struct Layer {
this->mode = mode;
}
inline int getAlpha() {
inline int getAlpha() const {
return alpha;
}
inline SkXfermode::Mode getMode() {
inline SkXfermode::Mode getMode() const {
return mode;
}
@@ -141,7 +142,7 @@ struct Layer {
this->empty = empty;
}
inline bool isEmpty() {
inline bool isEmpty() const {
return empty;
}
@@ -149,23 +150,29 @@ struct Layer {
this->fbo = fbo;
}
inline GLuint getFbo() {
inline GLuint getFbo() const {
return fbo;
}
inline void setStencilRenderBuffer(GLuint renderBuffer) {
this->stencil = renderBuffer;
inline void setStencilRenderBuffer(RenderBuffer* renderBuffer) {
if (RenderBuffer::isStencilBuffer(renderBuffer->getFormat())) {
this->stencil = renderBuffer;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, stencil->getName());
} else {
ALOGE("The specified render buffer is not a stencil buffer");
}
}
inline GLuint getStencilRenderBuffer() {
inline RenderBuffer* getStencilRenderBuffer() const {
return stencil;
}
inline GLuint getTexture() {
inline GLuint getTexture() const {
return texture.id;
}
inline GLenum getRenderTarget() {
inline GLenum getRenderTarget() const {
return renderTarget;
}
@@ -181,7 +188,7 @@ struct Layer {
texture.setFilter(filter, bindTexture, force, renderTarget);
}
inline bool isCacheable() {
inline bool isCacheable() const {
return cacheable;
}
@@ -189,7 +196,7 @@ struct Layer {
this->cacheable = cacheable;
}
inline bool isDirty() {
inline bool isDirty() const {
return dirty;
}
@@ -197,7 +204,7 @@ struct Layer {
this->dirty = dirty;
}
inline bool isTextureLayer() {
inline bool isTextureLayer() const {
return textureLayer;
}
@@ -205,21 +212,21 @@ struct Layer {
this->textureLayer = textureLayer;
}
inline SkiaColorFilter* getColorFilter() {
inline SkiaColorFilter* getColorFilter() const {
return colorFilter;
}
ANDROID_API void setColorFilter(SkiaColorFilter* filter);
inline void bindTexture() {
inline void bindTexture() const {
if (texture.id) {
glBindTexture(renderTarget, texture.id);
}
}
inline void bindStencilRenderBuffer() {
inline void bindStencilRenderBuffer() const {
if (stencil) {
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
stencil->bind();
}
}
@@ -255,12 +262,6 @@ struct Layer {
}
}
inline void allocateStencilRenderBuffer() {
if (stencil) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, getWidth(), getHeight());
}
}
inline mat4& getTexTransform() {
return texTransform;
}
@@ -317,10 +318,9 @@ private:
GLuint fbo;
/**
* Name of the render buffer used as the stencil buffer. If the
* name is 0, this layer does not have a stencil buffer.
* The render buffer used as the stencil buffer.
*/
GLuint stencil;
RenderBuffer* stencil;
/**
* Indicates whether this layer has been used already.

View File

@@ -351,7 +351,7 @@ void LayerRenderer::flushLayer(Layer* layer) {
if (layer && fbo) {
// If possible, discard any enqueud operations on deferred
// rendering architectures
if (Caches::getInstance().extensions.hasDiscardFramebuffer()) {
if (Extensions::getInstance().hasDiscardFramebuffer()) {
GLuint previousFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);

View File

@@ -109,7 +109,8 @@ static const Blender gBlendsSwap[] = {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
OpenGLRenderer::OpenGLRenderer():
mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
mShader = NULL;
mColorFilter = NULL;
mHasShadow = false;
@@ -216,7 +217,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa
// If we know that we are going to redraw the entire framebuffer,
// perform a discard to let the driver know we don't need to preserve
// the back buffer for this frame.
if (mCaches.extensions.hasDiscardFramebuffer() &&
if (mExtensions.hasDiscardFramebuffer() &&
left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
const bool isFbo = getTargetFbo() == 0;
const GLenum attachments[] = {
@@ -1119,7 +1120,7 @@ void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
it.next();
}
drawColorRects(rects.array(), count, color, mode, true, dirty);
drawColorRects(rects.array(), count, color, mode, true, dirty, false);
}
void OpenGLRenderer::dirtyLayer(const float left, const float top,
@@ -1269,15 +1270,12 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
// attach the new render buffer then turn tiling back on
endTiling();
// TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
GLuint buffer;
glGenRenderbuffers(1, &buffer);
RenderBuffer* buffer = new RenderBuffer(
Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
buffer->bind();
buffer->allocate();
layer->setStencilRenderBuffer(buffer);
layer->bindStencilRenderBuffer();
layer->allocateStencilRenderBuffer();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
startTiling(layer->clipRect, layer->layer.getHeight());
}
@@ -1525,13 +1523,13 @@ void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) {
void OpenGLRenderer::setupDrawShader() {
if (mShader) {
mShader->describe(mDescription, mCaches.extensions);
mShader->describe(mDescription, mExtensions);
}
}
void OpenGLRenderer::setupDrawColorFilter() {
if (mColorFilter) {
mColorFilter->describe(mDescription, mCaches.extensions);
mColorFilter->describe(mDescription, mExtensions);
}
}
@@ -3207,7 +3205,7 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint
}
status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
SkXfermode::Mode mode, bool ignoreTransform, bool dirty) {
SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) {
float left = FLT_MAX;
float top = FLT_MAX;
@@ -3241,7 +3239,7 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
}
}
if (count == 0 || quickReject(left, top, right, bottom)) {
if (count == 0 || (clip && quickReject(left, top, right, bottom))) {
return DrawGlInfo::kStatusDone;
}
@@ -3386,7 +3384,7 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
// If the blend mode cannot be implemented using shaders, fall
// back to the default SrcOver blend mode instead
if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
if (CC_UNLIKELY(mCaches.extensions.hasFramebufferFetch())) {
if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) {
description.framebufferMode = mode;
description.swapSrcDst = swapSrcDst;

View File

@@ -566,9 +566,11 @@ private:
* @param mode The Skia xfermode to use
* @param ignoreTransform True if the current transform should be ignored
* @param dirty True if calling this method should dirty the current layer
* @param clip True if the rects should be clipped, false otherwise
*/
status_t drawColorRects(const float* rects, int count, int color,
SkXfermode::Mode mode, bool ignoreTransform = false, bool dirty = true);
SkXfermode::Mode mode, bool ignoreTransform = false,
bool dirty = true, bool clip = true);
/**
* Draws the shape represented by the specified path texture.
@@ -881,6 +883,7 @@ private:
// Various caches
Caches& mCaches;
Extensions& mExtensions;
// List of rectangles to clear after saveLayer() is invoked
Vector<Rect*> mLayers;

171
libs/hwui/RenderBuffer.h Normal file
View File

@@ -0,0 +1,171 @@
/*
* 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_RENDER_BUFFER_H
#define ANDROID_HWUI_RENDER_BUFFER_H
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
namespace android {
namespace uirenderer {
/**
* Represents an OpenGL render buffer. Render buffers are attached
* to layers to perform stencil work.
*/
struct RenderBuffer {
/**
* Creates a new render buffer in the specified format and dimensions.
* The format must be one of the formats allowed by glRenderbufferStorage().
*/
RenderBuffer(GLenum format, uint32_t width, uint32_t height):
mFormat(format), mWidth(width), mHeight(height), mAllocated(false) {
glGenRenderbuffers(1, &mName);
}
~RenderBuffer() {
if (mName && mAllocated) {
glDeleteRenderbuffers(1, &mName);
}
}
/**
* Returns the GL name of this render buffer.
*/
GLuint getName() const {
return mName;
}
/**
* Returns the format of this render buffer.
*/
GLenum getFormat() const {
return mFormat;
}
/**
* Binds this render buffer to the current GL context.
*/
void bind() const {
glBindRenderbuffer(GL_RENDERBUFFER, mName);
}
/**
* Indicates whether this render buffer has allocated its
* storage. See allocate() and resize().
*/
bool isAllocated() const {
return mAllocated;
}
/**
* Allocates this render buffer's storage if needed.
* This method doesn't do anything if isAllocated() returns true.
*/
void allocate() {
if (!mAllocated) {
glRenderbufferStorage(GL_RENDERBUFFER, mFormat, mWidth, mHeight);
mAllocated = true;
}
}
/**
* Resizes this render buffer. If the buffer was previously allocated,
* the storage is re-allocated wit the new specified dimensions. If the
* buffer wasn't previously allocated, the buffer remains unallocated.
*/
void resize(uint32_t width, uint32_t height) {
if (isAllocated() && (width != mWidth || height != mHeight)) {
glRenderbufferStorage(GL_RENDERBUFFER, mFormat, width, height);
}
mWidth = width;
mHeight = height;
}
/**
* 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 render buffer in bytes.
*/
uint32_t getSize() const {
// Round to the nearest byte
return (uint32_t) ((mWidth * mHeight * formatSize(mFormat)) / 8.0f + 0.5f);
}
/**
* Returns the number of bits per component in the specified format.
* The format must be one of the formats allowed by glRenderbufferStorage().
*/
static uint32_t formatSize(GLenum format) {
switch (format) {
case GL_STENCIL_INDEX8:
return 8;
case GL_STENCIL_INDEX1_OES:
return 1;
case GL_STENCIL_INDEX4_OES:
return 4;
case GL_DEPTH_COMPONENT16:
case GL_RGBA4:
case GL_RGB565:
case GL_RGB5_A1:
return 16;
}
return 0;
}
/**
* Indicates whether the specified format represents a stencil buffer.
*/
static bool isStencilBuffer(GLenum format) {
switch (format) {
case GL_STENCIL_INDEX8:
case GL_STENCIL_INDEX1_OES:
case GL_STENCIL_INDEX4_OES:
return true;
}
return false;
}
private:
GLenum mFormat;
uint32_t mWidth;
uint32_t mHeight;
bool mAllocated;
GLuint mName;
}; // struct RenderBuffer
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_HWUI_RENDER_BUFFER_H

View File

@@ -183,6 +183,9 @@ const Rect& Snapshot::getLocalClip() {
}
void Snapshot::resetClip(float left, float top, float right, float bottom) {
// TODO: This is incorrect, when we start rendering into a new layer,
// we may have to modify the previous snapshot's clip rect and clip
// region if the previous restore() call did not restore the clip
clipRect = &mClipRectRoot;
clipRegion = &mClipRegionRoot;
setClip(left, top, right, bottom);

View File

@@ -14,14 +14,23 @@
* limitations under the License.
*/
#include <GLES2/gl2.h>
#include "Extensions.h"
#include "Properties.h"
#include "Stencil.h"
#include <GLES2/gl2ext.h>
namespace android {
namespace uirenderer {
#if DEBUG_STENCIL
#define STENCIL_WRITE_VALUE 0xff
#define STENCIL_MASK_VALUE 0xff
#else
#define STENCIL_WRITE_VALUE 0x1
#define STENCIL_MASK_VALUE 0x1
#endif
Stencil::Stencil(): mState(kDisabled) {
}
@@ -29,6 +38,18 @@ uint32_t Stencil::getStencilSize() {
return STENCIL_BUFFER_SIZE;
}
GLenum Stencil::getSmallestStencilFormat() {
#if !DEBUG_STENCIL
const Extensions& extensions = Extensions::getInstance();
if (extensions.has1BitStencil()) {
return GL_STENCIL_INDEX1_OES;
} else if (extensions.has4BitStencil()) {
return GL_STENCIL_INDEX4_OES;
}
#endif
return GL_STENCIL_INDEX8;
}
void Stencil::clear() {
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
@@ -37,7 +58,7 @@ void Stencil::clear() {
void Stencil::enableTest() {
if (mState != kTest) {
enable();
glStencilFunc(GL_EQUAL, 0xff, 0xff);
glStencilFunc(GL_EQUAL, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE);
// We only want to test, let's keep everything
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@@ -48,7 +69,7 @@ void Stencil::enableTest() {
void Stencil::enableWrite() {
if (mState != kWrite) {
enable();
glStencilFunc(GL_ALWAYS, 0xff, 0xff);
glStencilFunc(GL_ALWAYS, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE);
// The test always passes so the first two values are meaningless
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

View File

@@ -21,6 +21,8 @@
#define LOG_TAG "OpenGLRenderer"
#endif
#include <GLES2/gl2.h>
#include <cutils/compiler.h>
namespace android {
@@ -40,6 +42,11 @@ public:
*/
ANDROID_API static uint32_t getStencilSize();
/**
* Returns the smallest stencil format accepted by render buffers.
*/
static GLenum getSmallestStencilFormat();
/**
* Clears the stencil buffer.
*/

View File

@@ -219,7 +219,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege
// We could also enable mipmapping if both bitmap dimensions are powers
// of 2 but we'd have to deal with size changes. Let's keep this simple
const bool canMipMap = Caches::getInstance().extensions.hasNPot();
const bool canMipMap = Extensions::getInstance().hasNPot();
// If the texture had mipmap enabled but not anymore,
// force a glTexImage2D to discard the mipmap levels