diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Caches.h | 2 | ||||
-rw-r--r-- | libs/hwui/Layer.cpp | 20 | ||||
-rw-r--r-- | libs/hwui/Layer.h | 25 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.h | 1 | ||||
-rw-r--r-- | libs/hwui/Matrix.cpp | 139 | ||||
-rw-r--r-- | libs/hwui/Matrix.h | 30 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 180 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 35 | ||||
-rw-r--r-- | libs/hwui/Snapshot.cpp | 44 | ||||
-rw-r--r-- | libs/hwui/Snapshot.h | 12 | ||||
-rw-r--r-- | libs/hwui/Stencil.cpp | 4 |
12 files changed, 377 insertions, 125 deletions
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 628d8a0..ae188be 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -270,9 +270,7 @@ public: GammaFontRenderer* fontRenderer; Dither dither; -#if STENCIL_BUFFER_SIZE Stencil stencil; -#endif // Debug methods PFNGLINSERTEVENTMARKEREXTPROC eventMark; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index ee1d391..1d85b70 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -41,6 +41,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) { renderer = NULL; displayList = NULL; fbo = 0; + stencil = 0; debugDrawUpdate = false; Caches::getInstance().resourceCache.incrementRefcount(this); } @@ -53,9 +54,22 @@ Layer::~Layer() { deleteTexture(); } -void Layer::removeFbo() { +void Layer::removeFbo(bool flush) { + if (stencil) { + // TODO: recycle & cache instead of simply deleting + GLuint previousFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); + if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); + if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + + glDeleteRenderbuffers(1, &stencil); + stencil = 0; + } + if (fbo) { - LayerRenderer::flushLayer(this); + if (flush) LayerRenderer::flushLayer(this); + // If put fails the cache will delete the FBO Caches::getInstance().fboCache.put(fbo); fbo = 0; } @@ -75,7 +89,5 @@ void Layer::setColorFilter(SkiaColorFilter* filter) { } } - - }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 181eb6c..9ef4894 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -48,7 +48,12 @@ struct Layer { Layer(const uint32_t layerWidth, const uint32_t layerHeight); ~Layer(); - void removeFbo(); + /** + * Calling this method will remove (either by recycling or + * destroying) the associated FBO, if present, and any render + * buffer (stencil for instance.) + */ + void removeFbo(bool flush = true); /** * Sets this layer's region to a rectangle. Computes the appropriate @@ -134,6 +139,14 @@ struct Layer { return fbo; } + inline void setStencilRenderBuffer(GLuint renderBuffer) { + this->stencil = renderBuffer; + } + + inline GLuint getStencilRenderBuffer() { + return stencil; + } + inline GLuint getTexture() { return texture.id; } @@ -212,10 +225,6 @@ struct Layer { texture.id = 0; } - inline void deleteFbo() { - if (fbo) glDeleteFramebuffers(1, &fbo); - } - inline void allocateTexture(GLenum format, GLenum storage) { #if DEBUG_LAYERS ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight()); @@ -275,6 +284,12 @@ 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. + */ + GLuint stencil; + + /** * Indicates whether this layer has been used already. */ bool empty; diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 3484d41..ba59bb3 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -100,13 +100,21 @@ bool LayerRenderer::suppressErrorChecks() { } /////////////////////////////////////////////////////////////////////////////// -// Dirty region tracking +// Layer support /////////////////////////////////////////////////////////////////////////////// bool LayerRenderer::hasLayer() { return true; } +void LayerRenderer::ensureStencilBuffer() { + attachStencilBufferToLayer(mLayer); +} + +/////////////////////////////////////////////////////////////////////////////// +// Dirty region tracking +/////////////////////////////////////////////////////////////////////////////// + Region* LayerRenderer::getRegion() { if (getSnapshot()->flags & Snapshot::kFlagFboTarget) { return OpenGLRenderer::getRegion(); diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index c44abce..7a8bdc5 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -64,6 +64,7 @@ public: static void flushLayer(Layer* layer); protected: + virtual void ensureStencilBuffer(); virtual bool hasLayer(); virtual Region* getRegion(); virtual GLint getTargetFbo(); diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index a924362..79fae2b 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -30,6 +30,16 @@ namespace android { namespace uirenderer { +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +static const float EPSILON = 0.0000001f; + +/////////////////////////////////////////////////////////////////////////////// +// Matrix +/////////////////////////////////////////////////////////////////////////////// + void Matrix4::loadIdentity() { data[kScaleX] = 1.0f; data[kSkewY] = 0.0f; @@ -51,44 +61,91 @@ void Matrix4::loadIdentity() { data[kTranslateZ] = 0.0f; data[kPerspective2] = 1.0f; - mIsIdentity = true; - mSimpleMatrix = true; + mType = kTypeIdentity | kTypeRectToRect; +} + +static bool isZero(float f) { + return fabs(f) <= EPSILON; +} + +uint32_t Matrix4::getType() const { + if (mType & kTypeUnknown) { + mType = kTypeIdentity; + + if (data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f || + data[kPerspective2] != 1.0f) { + mType |= kTypePerspective; + } + + if (data[kTranslateX] != 0.0f || data[kTranslateY] != 0.0f) { + mType |= kTypeTranslate; + } + + float m00 = data[kScaleX]; + float m01 = data[kSkewX]; + float m10 = data[kSkewY]; + float m11 = data[kScaleY]; + + if (m01 != 0.0f || m10 != 0.0f) { + mType |= kTypeAffine; + } + + if (m00 != 1.0f || m11 != 1.0f) { + mType |= kTypeScale; + } + + // The following section determines whether the matrix will preserve + // rectangles. For instance, a rectangle transformed by a pure + // translation matrix will result in a rectangle. A rectangle + // transformed by a 45 degrees rotation matrix is not a rectangle. + // If the matrix has a perspective component then we already know + // it doesn't preserve rectangles. + if (!(mType & kTypePerspective)) { + if ((isZero(m00) && isZero(m11) && !isZero(m01) && !isZero(m10)) || + (isZero(m01) && isZero(m10) && !isZero(m00) && !isZero(m11))) { + mType |= kTypeRectToRect; + } + } + } + return mType; +} + +uint32_t Matrix4::getGeometryType() const { + return getType() & sGeometryMask; +} + +bool Matrix4::rectToRect() const { + return getType() & kTypeRectToRect; } bool Matrix4::changesBounds() const { - return !(data[0] == 1.0f && data[1] == 0.0f && data[2] == 0.0f && data[4] == 0.0f && - data[5] == 1.0f && data[6] == 0.0f && data[8] == 0.0f && data[9] == 0.0f && - data[10] == 1.0f); + return getType() & (kTypeScale | kTypeAffine | kTypePerspective); } bool Matrix4::isPureTranslate() const { - return mSimpleMatrix && data[kScaleX] == 1.0f && data[kScaleY] == 1.0f; + return getGeometryType() == kTypeTranslate; } bool Matrix4::isSimple() const { - return mSimpleMatrix; + return getGeometryType() <= (kTypeScale | kTypeTranslate); } bool Matrix4::isIdentity() const { - return mIsIdentity; + return getGeometryType() == kTypeIdentity; } bool Matrix4::isPerspective() const { - return data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f || - data[kPerspective2] != 1.0f; + return getType() & kTypePerspective; } void Matrix4::load(const float* v) { memcpy(data, v, sizeof(data)); - // TODO: Do something smarter here - mSimpleMatrix = false; - mIsIdentity = false; + mType = kTypeUnknown; } void Matrix4::load(const Matrix4& v) { memcpy(data, v.data, sizeof(data)); - mSimpleMatrix = v.mSimpleMatrix; - mIsIdentity = v.mIsIdentity; + mType = v.getType(); } void Matrix4::load(const SkMatrix& v) { @@ -108,8 +165,14 @@ void Matrix4::load(const SkMatrix& v) { data[kScaleZ] = 1.0f; - mSimpleMatrix = (v.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)); - mIsIdentity = v.isIdentity(); + // NOTE: The flags are compatible between SkMatrix and this class. + // However, SkMatrix::getType() does not return the flag + // kRectStaysRect. The return value is masked with 0xF + // so we need the extra rectStaysRect() check + mType = v.getType(); + if (v.rectStaysRect()) { + mType |= kTypeRectToRect; + } } void Matrix4::copyTo(SkMatrix& v) const { @@ -158,8 +221,7 @@ void Matrix4::loadInverse(const Matrix4& v) { data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] - v.data[kSkewX] * v.data[kSkewY]) * scale; - mSimpleMatrix = v.mSimpleMatrix; - mIsIdentity = v.mIsIdentity; + mType = kTypeUnknown; } void Matrix4::copyTo(float* v) const { @@ -178,7 +240,7 @@ void Matrix4::multiply(float v) { for (int i = 0; i < 16; i++) { data[i] *= v; } - mIsIdentity = false; + mType = kTypeUnknown; } void Matrix4::loadTranslate(float x, float y, float z) { @@ -188,7 +250,7 @@ void Matrix4::loadTranslate(float x, float y, float z) { data[kTranslateY] = y; data[kTranslateZ] = z; - mIsIdentity = false; + mType = kTypeTranslate | kTypeRectToRect; } void Matrix4::loadScale(float sx, float sy, float sz) { @@ -198,7 +260,7 @@ void Matrix4::loadScale(float sx, float sy, float sz) { data[kScaleY] = sy; data[kScaleZ] = sz; - mIsIdentity = false; + mType = kTypeScale | kTypeRectToRect; } void Matrix4::loadSkew(float sx, float sy) { @@ -216,8 +278,23 @@ void Matrix4::loadSkew(float sx, float sy) { data[kPerspective1] = 0.0f; data[kPerspective2] = 1.0f; - mSimpleMatrix = false; - mIsIdentity = false; + mType = kTypeUnknown; +} + +void Matrix4::loadRotate(float angle) { + angle *= float(M_PI / 180.0f); + float c = cosf(angle); + float s = sinf(angle); + + loadIdentity(); + + data[kScaleX] = c; + data[kSkewX] = -s; + + data[kSkewY] = s; + data[kScaleY] = c; + + mType = kTypeUnknown; } void Matrix4::loadRotate(float angle, float x, float y, float z) { @@ -257,8 +334,7 @@ void Matrix4::loadRotate(float angle, float x, float y, float z) { data[6] = yz * nc + xs; data[kScaleZ] = z * z * nc + c; - mSimpleMatrix = false; - mIsIdentity = false; + mType = kTypeUnknown; } void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { @@ -282,8 +358,7 @@ void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { set(i, 3, w); } - mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix; - mIsIdentity = false; + mType = kTypeUnknown; } void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) { @@ -296,13 +371,13 @@ void Matrix4::loadOrtho(float left, float right, float bottom, float top, float data[kTranslateY] = -(top + bottom) / (top - bottom); data[kTranslateZ] = -(far + near) / (far - near); - mIsIdentity = false; + mType = kTypeTranslate | kTypeScale | kTypeRectToRect; } #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c) void Matrix4::mapPoint(float& x, float& y) const { - if (mSimpleMatrix) { + if (isSimple()) { MUL_ADD_STORE(x, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(y, data[kScaleY], data[kTranslateY]); return; @@ -318,7 +393,7 @@ void Matrix4::mapPoint(float& x, float& y) const { } void Matrix4::mapRect(Rect& r) const { - if (mSimpleMatrix) { + if (isSimple()) { MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]); @@ -376,7 +451,7 @@ void Matrix4::mapRect(Rect& r) const { } void Matrix4::dump() const { - ALOGD("Matrix4[simple=%d", mSimpleMatrix); + ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType()); ALOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]); ALOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]); ALOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index f86823d..46a5597 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -48,6 +48,21 @@ public: kPerspective2 = 15 }; + // NOTE: The flags from kTypeIdentity to kTypePerspective + // must be kept in sync with the type flags found + // in SkMatrix + enum Type { + kTypeIdentity = 0, + kTypeTranslate = 0x1, + kTypeScale = 0x2, + kTypeAffine = 0x4, + kTypePerspective = 0x8, + kTypeRectToRect = 0x10, + kTypeUnknown = 0x20, + }; + + static const int sGeometryMask = 0xf; + Matrix4() { loadIdentity(); } @@ -75,11 +90,14 @@ public: void loadTranslate(float x, float y, float z); void loadScale(float sx, float sy, float sz); void loadSkew(float sx, float sy); + void loadRotate(float angle); void loadRotate(float angle, float x, float y, float z); void loadMultiply(const Matrix4& u, const Matrix4& v); void loadOrtho(float left, float right, float bottom, float top, float near, float far); + uint32_t getType() const; + void multiply(const Matrix4& v) { Matrix4 u; u.loadMultiply(*this, v); @@ -112,10 +130,14 @@ public: multiply(u); } - bool isPureTranslate() const; + /** + * If the matrix is identity or translate and/or scale. + */ bool isSimple() const; + bool isPureTranslate() const; bool isIdentity() const; bool isPerspective() const; + bool rectToRect() const; bool changesBounds() const; @@ -131,8 +153,7 @@ public: void dump() const; private: - bool mSimpleMatrix; - bool mIsIdentity; + mutable uint32_t mType; inline float get(int i, int j) const { return data[i * 4 + j]; @@ -141,6 +162,9 @@ private: inline void set(int i, int j, float v) { data[i * 4 + j] = v; } + + uint32_t getGeometryType() const; + }; // class Matrix4 /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index bb1edbb..be34b40 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -837,6 +837,8 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { return; } + Layer* layer = current->layer; + const Rect& rect = layer->layer; const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer; if (fboLayer) { @@ -844,6 +846,9 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { // Detach the texture from the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + + layer->removeFbo(false); + // Unbind current FBO and restore previous one glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); debugOverdraw(true, false); @@ -851,9 +856,6 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { startTiling(previous); } - Layer* layer = current->layer; - const Rect& rect = layer->layer; - if (!fboLayer && layer->getAlpha() < 255) { drawColorRect(rect.left, rect.top, rect.right, rect.bottom, layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true); @@ -881,17 +883,6 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { composeLayerRect(layer, rect, true); } - if (fboLayer) { - // Note: No need to use glDiscardFramebufferEXT() since we never - // create/compose layers that are not on screen with this - // code path - // See LayerRenderer::destroyLayer(Layer*) - - // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed - mCaches.fboCache.put(current->fbo); - layer->setFbo(0); - } - dirtyClip(); // Failing to add the layer to the cache should happen only if the layer is too large @@ -1001,10 +992,14 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { const float texY = 1.0f / float(layer->getHeight()); const float height = rect.getHeight(); + setupDraw(); + + // We must get (and therefore bind) the region mesh buffer + // after we setup drawing in case we need to mess with the + // stencil buffer in setupDraw() TextureVertex* mesh = mCaches.getRegionMesh(); GLsizei numQuads = 0; - setupDraw(); setupDrawWithTexture(); setupDrawColor(alpha, alpha, alpha, alpha); setupDrawColorFilter(); @@ -1089,6 +1084,25 @@ void OpenGLRenderer::drawRegionRects(const Region& region) { #endif } +void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color, + SkXfermode::Mode mode, bool dirty) { + int count = 0; + Vector<float> rects; + + SkRegion::Iterator it(region); + while (!it.done()) { + const SkIRect& r = it.rect(); + rects.push(r.fLeft); + rects.push(r.fTop); + rects.push(r.fRight); + rects.push(r.fBottom); + count++; + it.next(); + } + + drawColorRects(rects.array(), count, color, mode, true, dirty); +} + void OpenGLRenderer::dirtyLayer(const float left, const float top, const float right, const float bottom, const mat4 transform) { if (hasLayer()) { @@ -1219,6 +1233,65 @@ void OpenGLRenderer::setScissorFromClip() { } } +void OpenGLRenderer::ensureStencilBuffer() { + // Thanks to the mismatch between EGL and OpenGL ES FBO we + // cannot attach a stencil buffer to fbo0 dynamically. Let's + // just hope we have one when hasLayer() returns false. + if (hasLayer()) { + attachStencilBufferToLayer(mSnapshot->layer); + } +} + +void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) { + // The layer's FBO is already bound when we reach this stage + if (!layer->getStencilRenderBuffer()) { + // TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached + GLuint buffer; + glGenRenderbuffers(1, &buffer); + glBindRenderbuffer(GL_RENDERBUFFER, buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, + layer->getWidth(), layer->getHeight()); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer); + layer->setStencilRenderBuffer(buffer); + } +} + +void OpenGLRenderer::setStencilFromClip() { + if (!mCaches.debugOverdraw) { + if (!mSnapshot->clipRegion->isEmpty()) { + // NOTE: The order here is important, we must set dirtyClip to false + // before any draw call to avoid calling back into this method + mDirtyClip = false; + + ensureStencilBuffer(); + + mCaches.stencil.enableWrite(); + + // Clear the stencil but first make sure we restrict drawing + // to the region's bounds + bool resetScissor = mCaches.enableScissor(); + if (resetScissor) { + // The scissor was not set so we now need to update it + setScissorFromClip(); + } + mCaches.stencil.clear(); + if (resetScissor) mCaches.disableScissor(); + + // NOTE: We could use the region contour path to generate a smaller mesh + // Since we are using the stencil we could use the red book path + // drawing technique. It might increase bandwidth usage though. + + // The last parameter is important: we are not drawing in the color buffer + // so we don't want to dirty the current layer, if any + drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false); + + mCaches.stencil.enableTest(); + } else { + mCaches.stencil.disable(); + } + } +} + const Rect& OpenGLRenderer::getClipBounds() { return mSnapshot->getLocalClip(); } @@ -1284,40 +1357,60 @@ bool OpenGLRenderer::quickReject(float left, float top, float right, float botto return rejected; } -bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - bool clipped = mSnapshot->clip(left, top, right, bottom, op); - if (clipped) { - dirtyClip(); +void OpenGLRenderer::debugClip() { #if DEBUG_CLIP_REGIONS - if (!isDeferred() && mSnapshot->clipRegion && !mSnapshot->clipRegion->isRect()) { - int count = 0; - Vector<float> rects; - SkRegion::Iterator it(*mSnapshot->clipRegion); - while (!it.done()) { - const SkIRect& r = it.rect(); - rects.push(r.fLeft); - rects.push(r.fTop); - rects.push(r.fRight); - rects.push(r.fBottom); - count++; - it.next(); - } + if (!isDeferred() && !mSnapshot->clipRegion->isEmpty()) { + drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode); + } +#endif +} - drawColorRects(rects.array(), count, 0x7f00ff00, SkXfermode::kSrcOver_Mode, true); +bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { + if (CC_LIKELY(mSnapshot->transform->rectToRect())) { + bool clipped = mSnapshot->clip(left, top, right, bottom, op); + if (clipped) { + dirtyClip(); } -#endif + return !mSnapshot->clipRect->isEmpty(); } - return !mSnapshot->clipRect->isEmpty(); + + SkPath path; + path.addRect(left, top, right, bottom); + + return clipPath(&path, op); } bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) { - const SkRect& bounds = path->getBounds(); - return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op); + SkMatrix transform; + mSnapshot->transform->copyTo(transform); + + SkPath transformed; + path->transform(transform, &transformed); + + SkRegion clip; + if (!mSnapshot->clipRegion->isEmpty()) { + clip.setRegion(*mSnapshot->clipRegion); + } else { + Rect* bounds = mSnapshot->clipRect; + clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); + } + + SkRegion region; + region.setPath(transformed, clip); + + bool clipped = mSnapshot->clipRegionTransformed(region, op); + if (clipped) { + dirtyClip(); + } + return !mSnapshot->clipRect->isEmpty(); } bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) { - const SkIRect& bounds = region->getBounds(); - return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op); + bool clipped = mSnapshot->clipRegionTransformed(*region, op); + if (clipped) { + dirtyClip(); + } + return !mSnapshot->clipRect->isEmpty(); } Rect* OpenGLRenderer::getClipRect() { @@ -1332,8 +1425,11 @@ void OpenGLRenderer::setupDraw(bool clear) { // TODO: It would be best if we could do this before quickReject() // changes the scissor test state if (clear) clearLayerRegions(); + // Make sure setScissor & setStencil happen at the beginning of + // this method if (mDirtyClip) { setScissorFromClip(); + setStencilFromClip(); } mDescription.reset(); mSetShaderColor = false; @@ -3085,7 +3181,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) { + SkXfermode::Mode mode, bool ignoreTransform, bool dirty) { float left = FLT_MAX; float top = FLT_MAX; @@ -3103,7 +3199,7 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color float r = rects[index + 2]; float b = rects[index + 3]; - if (!quickRejectNoScissor(left, top, right, bottom)) { + if (ignoreTransform || !quickRejectNoScissor(left, top, right, bottom)) { Vertex::set(vertex++, l, b); Vertex::set(vertex++, l, t); Vertex::set(vertex++, r, t); @@ -3136,7 +3232,7 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color setupDrawColorFilterUniforms(); setupDrawVertices((GLvoid*) &mesh[0].position[0]); - if (hasLayer()) { + if (dirty && hasLayer()) { dirtyLayer(left, top, right, bottom, *mSnapshot->transform); } diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index f07325f..d4e1eb5 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -287,6 +287,19 @@ protected: void resumeAfterLayer(); /** + * This method is called whenever a stencil buffer is required. Subclasses + * should override this method and call attachStencilBufferToLayer() on the + * appropriate layer(s). + */ + virtual void ensureStencilBuffer(); + + /** + * Obtains a stencil render buffer (allocating it if necessary) and + * attaches it to the specified layer. + */ + void attachStencilBufferToLayer(Layer* layer); + + /** * Compose the layer defined in the current snapshot with the layer * defined by the previous snapshot. * @@ -423,6 +436,12 @@ private: void setScissorFromClip(); /** + * Sets the clipping region using the stencil buffer. The clip region + * is defined by the current snapshot's clipRegion member. + */ + void setStencilFromClip(); + + /** * Performs a quick reject but does not affect the scissor. Returns * the transformed rect to test and the current clip. */ @@ -524,9 +543,10 @@ private: * @param color The rectangles' ARGB color, defined as a packed 32 bits word * @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 */ status_t drawColorRects(const float* rects, int count, int color, - SkXfermode::Mode mode, bool ignoreTransform = false); + SkXfermode::Mode mode, bool ignoreTransform = false, bool dirty = true); /** * Draws the shape represented by the specified path texture. @@ -774,6 +794,19 @@ private: */ void drawRegionRects(const Region& region); + /** + * Renders the specified region as a series of rectangles. The region + * must be in screen-space coordinates. + */ + void drawRegionRects(const SkRegion& region, int color, SkXfermode::Mode mode, + bool dirty = false); + + /** + * Draws the current clip region if any. Only when DEBUG_CLIP_REGIONS + * is turned on. + */ + void debugClip(); + void debugOverdraw(bool enable, bool clear); void renderOverdraw(); diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index d947299..22c7dde 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -31,7 +31,7 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), transform = &mTransformRoot; clipRect = &mClipRectRoot; region = NULL; - clipRegion = NULL; + clipRegion = &mClipRegionRoot; } /** @@ -39,12 +39,10 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), * the previous snapshot. */ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): - flags(0), previous(s), layer(NULL), fbo(s->fbo), + flags(0), previous(s), layer(s->layer), fbo(s->fbo), invisible(s->invisible), empty(false), viewport(s->viewport), height(s->height), alpha(s->alpha) { - clipRegion = NULL; - if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); transform = &mTransformRoot; @@ -55,17 +53,13 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): if (saveFlags & SkCanvas::kClip_SaveFlag) { mClipRectRoot.set(*s->clipRect); clipRect = &mClipRectRoot; -#if STENCIL_BUFFER_SIZE - if (s->clipRegion) { + if (!s->clipRegion->isEmpty()) { mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op); - clipRegion = &mClipRegionRoot; } -#endif + clipRegion = &mClipRegionRoot; } else { clipRect = s->clipRect; -#if STENCIL_BUFFER_SIZE clipRegion = s->clipRegion; -#endif } if (s->flags & Snapshot::kFlagFboTarget) { @@ -81,41 +75,38 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): /////////////////////////////////////////////////////////////////////////////// void Snapshot::ensureClipRegion() { -#if STENCIL_BUFFER_SIZE - if (!clipRegion) { - clipRegion = &mClipRegionRoot; + if (clipRegion->isEmpty()) { clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); } -#endif } void Snapshot::copyClipRectFromRegion() { -#if STENCIL_BUFFER_SIZE if (!clipRegion->isEmpty()) { const SkIRect& bounds = clipRegion->getBounds(); clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); if (clipRegion->isRect()) { clipRegion->setEmpty(); - clipRegion = NULL; } } else { clipRect->setEmpty(); - clipRegion = NULL; } -#endif } bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) { -#if STENCIL_BUFFER_SIZE SkIRect tmp; tmp.set(left, top, right, bottom); clipRegion->op(tmp, op); copyClipRectFromRegion(); return true; -#else - return false; -#endif +} + +bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { + ensureClipRegion(); + clipRegion->op(region, op); + copyClipRectFromRegion(); + flags |= Snapshot::kFlagClipSet; + return true; } bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) { @@ -129,7 +120,7 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) { switch (op) { case SkRegion::kIntersect_Op: { - if (CC_UNLIKELY(clipRegion)) { + if (CC_UNLIKELY(!clipRegion->isEmpty())) { ensureClipRegion(); clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op); } else { @@ -142,7 +133,7 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) { break; } case SkRegion::kUnion_Op: { - if (CC_UNLIKELY(clipRegion)) { + if (CC_UNLIKELY(!clipRegion->isEmpty())) { ensureClipRegion(); clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op); } else { @@ -171,12 +162,9 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) { void Snapshot::setClip(float left, float top, float right, float bottom) { clipRect->set(left, top, right, bottom); -#if STENCIL_BUFFER_SIZE - if (clipRegion) { + if (!clipRegion->isEmpty()) { clipRegion->setEmpty(); - clipRegion = NULL; } -#endif flags |= Snapshot::kFlagClipSet; } diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 9c612ff..ffd4729 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -94,6 +94,12 @@ public: bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op); /** + * Modifies the current clip with the specified region and operation. + * The specified region is considered already transformed. + */ + bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op); + + /** * Sets the current clip. */ void setClip(float left, float top, float right, float bottom); @@ -136,7 +142,7 @@ public: sp<Snapshot> previous; /** - * Only set when the flag kFlagIsLayer is set. + * A pointer to the currently active layer. * * This snapshot does not own the layer, this pointer must not be freed. */ @@ -200,8 +206,6 @@ public: * * This is a reference to a region owned by this snapshot or another * snapshot. This pointer must not be freed. See ::mClipRegionRoot. - * - * This field is used only if STENCIL_BUFFER_SIZE is > 0. */ SkRegion* clipRegion; @@ -234,9 +238,7 @@ private: Rect mClipRectRoot; Rect mLocalClip; -#if STENCIL_BUFFER_SIZE SkRegion mClipRegionRoot; -#endif }; // class Snapshot diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp index 84df82b..4fcd51d 100644 --- a/libs/hwui/Stencil.cpp +++ b/libs/hwui/Stencil.cpp @@ -37,7 +37,7 @@ void Stencil::clear() { void Stencil::enableTest() { if (mState != kTest) { enable(); - glStencilFunc(GL_EQUAL, 0x1, 0x1); + glStencilFunc(GL_EQUAL, 0xff, 0xff); // 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 +48,7 @@ void Stencil::enableTest() { void Stencil::enableWrite() { if (mState != kWrite) { enable(); - glStencilFunc(GL_ALWAYS, 0x1, 0x1); + glStencilFunc(GL_ALWAYS, 0xff, 0xff); // 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); |