/libs/hwui/OpenGLRenderer.cpp
C++ | 1400 lines | 957 code | 233 blank | 210 comment | 160 complexity | 447763f97167f7b018135ce01fd768e1 MD5 | raw file
- /*
- * Copyright (C) 2010 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.
- */
- #define LOG_TAG "OpenGLRenderer"
- #include <stdlib.h>
- #include <stdint.h>
- #include <sys/types.h>
- #include <SkCanvas.h>
- #include <SkColor.h>
- #include <SkShader.h>
- #include <SkTypeface.h>
- #include <utils/Log.h>
- #include <utils/StopWatch.h>
- #include <private/hwui/DrawGlInfo.h>
- #include <ui/Rect.h>
- #include "OpenGLRenderer.h"
- #include "DeferredDisplayList.h"
- #include "DisplayListRenderer.h"
- #include "Fence.h"
- #include "RenderState.h"
- #include "PathTessellator.h"
- #include "Properties.h"
- #include "ShadowTessellator.h"
- #include "SkiaShader.h"
- #include "utils/GLUtils.h"
- #include "utils/TraceUtils.h"
- #include "Vector.h"
- #include "VertexBuffer.h"
- #if DEBUG_DETAILED_EVENTS
- #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
- #else
- #define EVENT_LOGD(...)
- #endif
- namespace android {
- namespace uirenderer {
- static GLenum getFilter(const SkPaint* paint) {
- if (!paint || paint->getFilterLevel() != SkPaint::kNone_FilterLevel) {
- return GL_LINEAR;
- }
- return GL_NEAREST;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Globals
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Structure mapping Skia xfermodes to OpenGL blending factors.
- */
- struct Blender {
- SkXfermode::Mode mode;
- GLenum src;
- GLenum dst;
- }; // struct Blender
- // In this array, the index of each Blender equals the value of the first
- // entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
- static const Blender gBlends[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
- { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
- };
- // This array contains the swapped version of each SkXfermode. For instance
- // this array's SrcOver blending mode is actually DstOver. You can refer to
- // createLayer() for more information on the purpose of this array.
- static const Blender gBlendsSwap[] = {
- { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
- { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Functions
- ///////////////////////////////////////////////////////////////////////////////
- template<typename T>
- static inline T min(T a, T b) {
- return a < b ? a : b;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Constructors/destructor
- ///////////////////////////////////////////////////////////////////////////////
- OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
- : mFrameStarted(false)
- , mCaches(Caches::getInstance())
- , mExtensions(Extensions::getInstance())
- , mRenderState(renderState)
- , mScissorOptimizationDisabled(false)
- , mSuppressTiling(false)
- , mFirstFrameAfterResize(true)
- , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
- , mLightRadius(FLT_MIN)
- , mAmbientShadowAlpha(0)
- , mSpotShadowAlpha(0) {
- // *set* draw modifiers to be 0
- memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
- mDrawModifiers.mOverrideLayerAlpha = 1.0f;
- memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
- }
- OpenGLRenderer::~OpenGLRenderer() {
- // The context has already been destroyed at this point, do not call
- // GL APIs. All GL state should be kept in Caches.h
- }
- void OpenGLRenderer::initProperties() {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) {
- mScissorOptimizationDisabled = !strcasecmp(property, "true");
- INIT_LOGD(" Scissor optimization %s",
- mScissorOptimizationDisabled ? "disabled" : "enabled");
- } else {
- INIT_LOGD(" Scissor optimization enabled");
- }
- }
- void OpenGLRenderer::initLight(const Vector3& lightCenter, float lightRadius,
- uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
- mLightCenter = lightCenter;
- mLightRadius = lightRadius;
- mAmbientShadowAlpha = ambientShadowAlpha;
- mSpotShadowAlpha = spotShadowAlpha;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Setup
- ///////////////////////////////////////////////////////////////////////////////
- void OpenGLRenderer::onViewportInitialized() {
- glDisable(GL_DITHER);
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- glEnableVertexAttribArray(Program::kBindingPosition);
- mFirstFrameAfterResize = true;
- }
- void OpenGLRenderer::setupFrameState(float left, float top,
- float right, float bottom, bool opaque) {
- mCaches.clearGarbage();
- initializeSaveStack(left, top, right, bottom, mLightCenter);
- mOpaque = opaque;
- mTilingClip.set(left, top, right, bottom);
- }
- status_t OpenGLRenderer::startFrame() {
- if (mFrameStarted) return DrawGlInfo::kStatusDone;
- mFrameStarted = true;
- mDirtyClip = true;
- discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
- mRenderState.setViewport(getWidth(), getHeight());
- // Functors break the tiling extension in pretty spectacular ways
- // This ensures we don't use tiling when a functor is going to be
- // invoked during the frame
- mSuppressTiling = mCaches.hasRegisteredFunctors()
- || mFirstFrameAfterResize;
- mFirstFrameAfterResize = false;
- startTilingCurrentClip(true);
- debugOverdraw(true, true);
- return clear(mTilingClip.left, mTilingClip.top,
- mTilingClip.right, mTilingClip.bottom, mOpaque);
- }
- status_t OpenGLRenderer::prepareDirty(float left, float top,
- float right, float bottom, bool opaque) {
- setupFrameState(left, top, right, bottom, opaque);
- // Layer renderers will start the frame immediately
- // The framebuffer renderer will first defer the display list
- // for each layer and wait until the first drawing command
- // to start the frame
- if (currentSnapshot()->fbo == 0) {
- syncState();
- updateLayers();
- } else {
- return startFrame();
- }
- return DrawGlInfo::kStatusDone;
- }
- void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
- // 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 (mExtensions.hasDiscardFramebuffer() &&
- left <= 0.0f && top <= 0.0f && right >= getWidth() && bottom >= getHeight()) {
- const bool isFbo = getTargetFbo() == 0;
- const GLenum attachments[] = {
- isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
- isFbo ? (const GLenum) GL_STENCIL_EXT : (const GLenum) GL_STENCIL_ATTACHMENT };
- glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
- }
- }
- status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
- #ifdef QCOM_HARDWARE
- mCaches.enableScissor();
- mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top);
- glClear(GL_COLOR_BUFFER_BIT);
- if (opaque) {
- mCaches.resetScissor();
- return DrawGlInfo::kStatusDone;
- }
- #else
- if (!opaque) {
- mCaches.enableScissor();
- mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top);
- glClear(GL_COLOR_BUFFER_BIT);
- return DrawGlInfo::kStatusDrew;
- }
- #endif
- return DrawGlInfo::kStatusDrew;
- }
- void OpenGLRenderer::syncState() {
- if (mCaches.blend) {
- glEnable(GL_BLEND);
- } else {
- glDisable(GL_BLEND);
- }
- }
- void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
- if (!mSuppressTiling) {
- const Snapshot* snapshot = currentSnapshot();
- const Rect* clip = &mTilingClip;
- if (snapshot->flags & Snapshot::kFlagFboTarget) {
- clip = &(snapshot->layer->clipRect);
- }
- startTiling(*clip, getViewportHeight(), opaque, expand);
- }
- }
- void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) {
- if (!mSuppressTiling) {
- if(expand) {
- // Expand the startTiling region by 1
- int leftNotZero = (clip.left > 0) ? 1 : 0;
- int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0;
- mCaches.startTiling(
- clip.left - leftNotZero,
- windowHeight - clip.bottom - topNotZero,
- clip.right - clip.left + leftNotZero + 1,
- clip.bottom - clip.top + topNotZero + 1,
- opaque);
- } else {
- mCaches.startTiling(clip.left, windowHeight - clip.bottom,
- clip.right - clip.left, clip.bottom - clip.top, opaque);
- }
- }
- }
- void OpenGLRenderer::endTiling() {
- if (!mSuppressTiling) mCaches.endTiling();
- }
- void OpenGLRenderer::finish() {
- renderOverdraw();
- endTiling();
- for (size_t i = 0; i < mTempPaths.size(); i++) {
- delete mTempPaths[i];
- }
- mTempPaths.clear();
- // When finish() is invoked on FBO 0 we've reached the end
- // of the current frame
- if (getTargetFbo() == 0) {
- mCaches.pathCache.trim();
- mCaches.tessellationCache.trim();
- }
- if (!suppressErrorChecks()) {
- #if DEBUG_OPENGL
- GLUtils::dumpGLErrors();
- #endif
- #if DEBUG_MEMORY_USAGE
- mCaches.dumpMemoryUsage();
- #else
- if (mCaches.getDebugLevel() & kDebugMemory) {
- mCaches.dumpMemoryUsage();
- }
- #endif
- }
- mFrameStarted = false;
- }
- void OpenGLRenderer::resumeAfterLayer() {
- mRenderState.setViewport(getViewportWidth(), getViewportHeight());
- mRenderState.bindFramebuffer(currentSnapshot()->fbo);
- debugOverdraw(true, false);
- mCaches.resetScissor();
- dirtyClip();
- }
- status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
- if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
- Rect clip(*currentClipRect());
- clip.snapToPixelBoundaries();
- // Since we don't know what the functor will draw, let's dirty
- // the entire clip region
- if (hasLayer()) {
- dirtyLayerUnchecked(clip, getRegion());
- }
- DrawGlInfo info;
- info.clipLeft = clip.left;
- info.clipTop = clip.top;
- info.clipRight = clip.right;
- info.clipBottom = clip.bottom;
- info.isLayer = hasLayer();
- info.width = getViewportWidth();
- info.height = getViewportHeight();
- currentTransform()->copyTo(&info.transform[0]);
- bool prevDirtyClip = mDirtyClip;
- // setup GL state for functor
- if (mDirtyClip) {
- setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
- }
- if (mCaches.enableScissor() || prevDirtyClip) {
- setScissorFromClip();
- }
- mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info);
- // Scissor may have been modified, reset dirty clip
- dirtyClip();
- return DrawGlInfo::kStatusDrew;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Debug
- ///////////////////////////////////////////////////////////////////////////////
- void OpenGLRenderer::eventMarkDEBUG(const char* fmt, ...) const {
- #if DEBUG_DETAILED_EVENTS
- const int BUFFER_SIZE = 256;
- va_list ap;
- char buf[BUFFER_SIZE];
- va_start(ap, fmt);
- vsnprintf(buf, BUFFER_SIZE, fmt, ap);
- va_end(ap);
- eventMark(buf);
- #endif
- }
- void OpenGLRenderer::eventMark(const char* name) const {
- mCaches.eventMark(0, name);
- }
- void OpenGLRenderer::startMark(const char* name) const {
- mCaches.startMark(0, name);
- }
- void OpenGLRenderer::endMark() const {
- mCaches.endMark();
- }
- void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
- mRenderState.debugOverdraw(enable, clear);
- }
- void OpenGLRenderer::renderOverdraw() {
- if (mCaches.debugOverdraw && getTargetFbo() == 0) {
- const Rect* clip = &mTilingClip;
- mCaches.enableScissor();
- mCaches.setScissor(clip->left, firstSnapshot()->getViewportHeight() - clip->bottom,
- clip->right - clip->left, clip->bottom - clip->top);
- // 1x overdraw
- mCaches.stencil.enableDebugTest(2);
- drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
- // 2x overdraw
- mCaches.stencil.enableDebugTest(3);
- drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
- // 3x overdraw
- mCaches.stencil.enableDebugTest(4);
- drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
- // 4x overdraw and higher
- mCaches.stencil.enableDebugTest(4, true);
- drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
- mCaches.stencil.disable();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Layers
- ///////////////////////////////////////////////////////////////////////////////
- bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
- if (layer->deferredUpdateScheduled && layer->renderer
- && layer->renderNode.get() && layer->renderNode->isRenderable()) {
- Rect& dirty = layer->dirtyRect;
- if (inFrame) {
- endTiling();
- debugOverdraw(false, false);
- }
- if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
- layer->render(*this);
- } else {
- layer->defer(*this);
- }
- if (inFrame) {
- resumeAfterLayer();
- startTilingCurrentClip();
- }
- layer->debugDrawUpdate = mCaches.debugLayersUpdates;
- layer->hasDrawnSinceUpdate = false;
- return true;
- }
- return false;
- }
- void OpenGLRenderer::updateLayers() {
- // If draw deferring is enabled this method will simply defer
- // the display list of each individual layer. The layers remain
- // in the layer updates list which will be cleared by flushLayers().
- int count = mLayerUpdates.size();
- if (count > 0) {
- if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
- startMark("Layer Updates");
- } else {
- startMark("Defer Layer Updates");
- }
- // Note: it is very important to update the layers in order
- for (int i = 0; i < count; i++) {
- Layer* layer = mLayerUpdates.itemAt(i).get();
- updateLayer(layer, false);
- }
- if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
- mLayerUpdates.clear();
- mRenderState.bindFramebuffer(getTargetFbo());
- }
- endMark();
- }
- }
- void OpenGLRenderer::flushLayers() {
- int count = mLayerUpdates.size();
- if (count > 0) {
- startMark("Apply Layer Updates");
- // Note: it is very important to update the layers in order
- for (int i = 0; i < count; i++) {
- mLayerUpdates.itemAt(i)->flush();
- }
- mLayerUpdates.clear();
- mRenderState.bindFramebuffer(getTargetFbo());
- endMark();
- }
- }
- void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
- if (layer) {
- // Make sure we don't introduce duplicates.
- // SortedVector would do this automatically but we need to respect
- // the insertion order. The linear search is not an issue since
- // this list is usually very short (typically one item, at most a few)
- for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
- if (mLayerUpdates.itemAt(i) == layer) {
- return;
- }
- }
- mLayerUpdates.push_back(layer);
- }
- }
- void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
- if (layer) {
- for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
- if (mLayerUpdates.itemAt(i) == layer) {
- mLayerUpdates.removeAt(i);
- break;
- }
- }
- }
- }
- void OpenGLRenderer::flushLayerUpdates() {
- ATRACE_NAME("Update HW Layers");
- syncState();
- updateLayers();
- flushLayers();
- // Wait for all the layer updates to be executed
- AutoFence fence;
- }
- void OpenGLRenderer::markLayersAsBuildLayers() {
- for (size_t i = 0; i < mLayerUpdates.size(); i++) {
- mLayerUpdates[i]->wasBuildLayered = true;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // State management
- ///////////////////////////////////////////////////////////////////////////////
- void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
- bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer;
- bool restoreClip = removed.flags & Snapshot::kFlagClipSet;
- bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer;
- if (restoreViewport) {
- mRenderState.setViewport(getViewportWidth(), getViewportHeight());
- }
- if (restoreClip) {
- dirtyClip();
- }
- if (restoreLayer) {
- endMark(); // Savelayer
- ATRACE_END(); // SaveLayer
- startMark("ComposeLayer");
- composeLayer(removed, restored);
- endMark();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Layers
- ///////////////////////////////////////////////////////////////////////////////
- int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags, const SkPath* convexMask) {
- // force matrix/clip isolation for layer
- flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
- const int count = saveSnapshot(flags);
- if (!currentSnapshot()->isIgnored()) {
- createLayer(left, top, right, bottom, paint, flags, convexMask);
- }
- return count;
- }
- void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
- const Rect untransformedBounds(bounds);
- currentTransform()->mapRect(bounds);
- // Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(*currentClipRect())) {
- // We cannot work with sub-pixels in this case
- bounds.snapToPixelBoundaries();
- // When the layer is not an FBO, we may use glCopyTexImage so we
- // need to make sure the layer does not extend outside the bounds
- // of the framebuffer
- const Snapshot& previous = *(currentSnapshot()->previous);
- Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
- if (!bounds.intersect(previousViewport)) {
- bounds.setEmpty();
- } else if (fboLayer) {
- clip.set(bounds);
- mat4 inverse;
- inverse.loadInverse(*currentTransform());
- inverse.mapRect(clip);
- clip.snapToPixelBoundaries();
- if (clip.intersect(untransformedBounds)) {
- clip.translate(-untransformedBounds.left, -untransformedBounds.top);
- bounds.set(untransformedBounds);
- } else {
- clip.setEmpty();
- }
- }
- } else {
- bounds.setEmpty();
- }
- }
- void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
- bool fboLayer, int alpha) {
- if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
- bounds.getHeight() > mCaches.maxTextureSize ||
- (fboLayer && clip.isEmpty())) {
- mSnapshot->empty = fboLayer;
- } else {
- mSnapshot->invisible = mSnapshot->invisible || (alpha <= 0 && fboLayer);
- }
- }
- int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags) {
- const int count = saveSnapshot(flags);
- if (!currentSnapshot()->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
- // initialize the snapshot as though it almost represents an FBO layer so deferred draw
- // operations will be able to store and restore the current clip and transform info, and
- // quick rejection will be correct (for display lists)
- Rect bounds(left, top, right, bottom);
- Rect clip;
- calculateLayerBoundsAndClip(bounds, clip, true);
- updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
- if (!currentSnapshot()->isIgnored()) {
- mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
- mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
- mSnapshot->roundRectClipState = NULL;
- }
- }
- return count;
- }
- /**
- * Layers are viewed by Skia are slightly different than layers in image editing
- * programs (for instance.) When a layer is created, previously created layers
- * and the frame buffer still receive every drawing command. For instance, if a
- * layer is created and a shape intersecting the bounds of the layers and the
- * framebuffer is draw, the shape will be drawn on both (unless the layer was
- * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
- *
- * A way to implement layers is to create an FBO for each layer, backed by an RGBA
- * texture. Unfortunately, this is inefficient as it requires every primitive to
- * be drawn n + 1 times, where n is the number of active layers. In practice this
- * means, for every primitive:
- * - Switch active frame buffer
- * - Change viewport, clip and projection matrix
- * - Issue the drawing
- *
- * Switching rendering target n + 1 times per drawn primitive is extremely costly.
- * To avoid this, layers are implemented in a different way here, at least in the
- * general case. FBOs are used, as an optimization, when the "clip to layer" flag
- * is set. When this flag is set we can redirect all drawing operations into a
- * single FBO.
- *
- * This implementation relies on the frame buffer being at least RGBA 8888. When
- * a layer is created, only a texture is created, not an FBO. The content of the
- * frame buffer contained within the layer's bounds is copied into this texture
- * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
- * buffer and drawing continues as normal. This technique therefore treats the
- * frame buffer as a scratch buffer for the layers.
- *
- * To compose the layers back onto the frame buffer, each layer texture
- * (containing the original frame buffer data) is drawn as a simple quad over
- * the frame buffer. The trick is that the quad is set as the composition
- * destination in the blending equation, and the frame buffer becomes the source
- * of the composition.
- *
- * Drawing layers with an alpha value requires an extra step before composition.
- * An empty quad is drawn over the layer's region in the frame buffer. This quad
- * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
- * quad is used to multiply the colors in the frame buffer. This is achieved by
- * changing the GL blend functions for the GL_FUNC_ADD blend equation to
- * GL_ZERO, GL_SRC_ALPHA.
- *
- * Because glCopyTexImage2D() can be slow, an alternative implementation might
- * be use to draw a single clipped layer. The implementation described above
- * is correct in every case.
- *
- * (1) The frame buffer is actually not cleared right away. To allow the GPU
- * to potentially optimize series of calls to glCopyTexImage2D, the frame
- * buffer is left untouched until the first drawing operation. Only when
- * something actually gets drawn are the layers regions cleared.
- */
- bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags, const SkPath* convexMask) {
- LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
- LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
- const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
- // Window coordinates of the layer
- Rect clip;
- Rect bounds(left, top, right, bottom);
- calculateLayerBoundsAndClip(bounds, clip, fboLayer);
- updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
- // Bail out if we won't draw in this snapshot
- if (currentSnapshot()->isIgnored()) {
- return false;
- }
- mCaches.activeTexture(0);
- Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
- if (!layer) {
- return false;
- }
- layer->setPaint(paint);
- layer->layer.set(bounds);
- layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
- bounds.getWidth() / float(layer->getWidth()), 0.0f);
- layer->setBlend(true);
- layer->setDirty(false);
- layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache
- // Save the layer in the snapshot
- mSnapshot->flags |= Snapshot::kFlagIsLayer;
- mSnapshot->layer = layer;
- ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u",
- fboLayer ? "" : "unclipped ",
- layer->getWidth(), layer->getHeight());
- startMark("SaveLayer");
- if (fboLayer) {
- return createFboLayer(layer, bounds, clip);
- } else {
- // Copy the framebuffer into the layer
- layer->bindTexture();
- if (!bounds.isEmpty()) {
- if (layer->isEmpty()) {
- // Workaround for some GL drivers. When reading pixels lying outside
- // of the window we should get undefined values for those pixels.
- // Unfortunately some drivers will turn the entire target texture black
- // when reading outside of the window.
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->getWidth(), layer->getHeight(),
- 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- layer->setEmpty(false);
- }
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
- bounds.left, getViewportHeight() - bounds.bottom,
- bounds.getWidth(), bounds.getHeight());
- // Enqueue the buffer coordinates to clear the corresponding region later
- mLayers.push(new Rect(bounds));
- }
- }
- return true;
- }
- bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
- layer->clipRect.set(clip);
- layer->setFbo(mCaches.fboCache.get());
- mSnapshot->region = &mSnapshot->layer->region;
- mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
- mSnapshot->fbo = layer->getFbo();
- mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
- mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
- mSnapshot->roundRectClipState = NULL;
- endTiling();
- debugOverdraw(false, false);
- // Bind texture to FBO
- mRenderState.bindFramebuffer(layer->getFbo());
- layer->bindTexture();
- // Initialize the texture if needed
- if (layer->isEmpty()) {
- layer->allocateTexture();
- layer->setEmpty(false);
- }
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- layer->getTexture(), 0);
- // Expand the startTiling region by 1
- startTilingCurrentClip(true, true);
- // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
- mCaches.enableScissor();
- mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
- clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- dirtyClip();
- // Change the ortho projection
- mRenderState.setViewport(bounds.getWidth(), bounds.getHeight());
- return true;
- }
- /**
- * Read the documentation of createLayer() before doing anything in this method.
- */
- void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) {
- if (!removed.layer) {
- ALOGE("Attempting to compose a layer that does not exist");
- return;
- }
- Layer* layer = removed.layer;
- const Rect& rect = layer->layer;
- const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer;
- bool clipRequired = false;
- calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
- &clipRequired, NULL, false); // safely ignore return, should never be rejected
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
- if (fboLayer) {
- endTiling();
- // 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
- mRenderState.bindFramebuffer(restored.fbo);
- debugOverdraw(true, false);
- startTilingCurrentClip();
- }
- if (!fboLayer && layer->getAlpha() < 255) {
- SkPaint layerPaint;
- layerPaint.setAlpha(layer->getAlpha());
- layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
- layerPaint.setColorFilter(layer->getColorFilter());
- drawColorRect(rect.left, rect.top, rect.right, rect.bottom, &layerPaint, true);
- // Required below, composeLayerRect() will divide by 255
- layer->setAlpha(255);
- }
- mCaches.unbindMeshBuffer();
- mCaches.activeTexture(0);
- // When the layer is stored in an FBO, we can save a bit of fillrate by
- // drawing only the dirty region
- if (fboLayer) {
- dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform);
- composeLayerRegion(layer, rect);
- } else if (!rect.isEmpty()) {
- dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
- save(0);
- // the layer contains screen buffer content that shouldn't be alpha modulated
- // (and any necessary alpha modulation was handled drawing into the layer)
- mSnapshot->alpha = 1.0f;
- composeLayerRect(layer, rect, true);
- restore();
- }
- dirtyClip();
- // Failing to add the layer to the cache should happen only if the layer is too large
- layer->setConvexMask(NULL);
- if (!mCaches.layerCache.put(layer)) {
- LAYER_LOGD("Deleting layer");
- layer->decStrong(0);
- }
- }
- void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
- float alpha = getLayerAlpha(layer);
- setupDraw();
- if (layer->getRenderTarget() == GL_TEXTURE_2D) {
- setupDrawWithTexture();
- } else {
- setupDrawWithExternalTexture();
- }
- setupDrawTextureTransform();
- setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter(layer->getColorFilter());
- setupDrawBlending(layer);
- setupDrawProgram();
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(layer->getColorFilter());
- if (layer->getRenderTarget() == GL_TEXTURE_2D) {
- setupDrawTexture(layer->getTexture());
- } else {
- setupDrawExternalTexture(layer->getTexture());
- }
- if (currentTransform()->isPureTranslate() &&
- !layer->getForceFilter() &&
- layer->getWidth() == (uint32_t) rect.getWidth() &&
- layer->getHeight() == (uint32_t) rect.getHeight()) {
- const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
- const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
- layer->setFilter(GL_NEAREST);
- setupDrawModelView(kModelViewMode_TranslateAndScale, false,
- x, y, x + rect.getWidth(), y + rect.getHeight(), true);
- } else {
- layer->setFilter(GL_LINEAR);
- setupDrawModelView(kModelViewMode_TranslateAndScale, false,
- rect.left, rect.top, rect.right, rect.bottom);
- }
- setupDrawTextureTransformUniforms(layer->getTexTransform());
- setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
- }
- void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
- if (layer->isTextureLayer()) {
- EVENT_LOGD("composeTextureLayerRect");
- resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
- drawTextureLayer(layer, rect);
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
- } else {
- EVENT_LOGD("composeHardwareLayerRect");
- const Rect& texCoords = layer->texCoords;
- resetDrawTextureTexCoords(texCoords.left, texCoords.top,
- texCoords.right, texCoords.bottom);
- float x = rect.left;
- float y = rect.top;
- bool simpleTransform = currentTransform()->isPureTranslate() &&
- layer->getWidth() == (uint32_t) rect.getWidth() &&
- layer->getHeight() == (uint32_t) rect.getHeight();
- if (simpleTransform) {
- // When we're swapping, the layer is already in screen coordinates
- if (!swap) {
- x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
- y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
- }
- layer->setFilter(GL_NEAREST, true);
- } else {
- layer->setFilter(GL_LINEAR, true);
- }
- SkPaint layerPaint;
- layerPaint.setAlpha(getLayerAlpha(layer) * 255);
- layerPaint.setXfermodeMode(layer->getMode());
- layerPaint.setColorFilter(layer->getColorFilter());
- bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f;
- drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
- layer->getTexture(), &layerPaint, blend,
- &mMeshVertices[0].x, &mMeshVertices[0].u,
- GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
- }
- }
- /**
- * Issues the command X, and if we're composing a save layer to the fbo or drawing a newly updated
- * hardware layer with overdraw debug on, draws again to the stencil only, so that these draw
- * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used
- * by saveLayer's restore
- */
- #define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \
- DRAW_COMMAND; \
- if (CC_UNLIKELY(mCaches.debugOverdraw && getTargetFbo() == 0 && COND)) { \
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \
- DRAW_COMMAND; \
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \
- } \
- }
- #define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
- // This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
- // use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
- class LayerShader : public SkShader {
- public:
- LayerShader(Layer* layer, const SkMatrix* localMatrix)
- : INHERITED(localMatrix)
- , mLayer(layer) {
- }
- virtual bool asACustomShader(void** data) const {
- if (data) {
- *data = static_cast<void*>(mLayer);
- }
- return true;
- }
- virtual bool isOpaque() const {
- return !mLayer->isBlend();
- }
- protected:
- virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
- LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
- }
- virtual void flatten(SkWriteBuffer&) const {
- LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
- }
- virtual Factory getFactory() const {
- LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
- return NULL;
- }
- private:
- // Unowned.
- Layer* mLayer;
- typedef SkShader INHERITED;
- };
- void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
- if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
- if (layer->getConvexMask()) {
- save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
- // clip to the area of the layer the mask can be larger
- clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
- // create LayerShader to map SaveLayer content into subsequent draw
- SkMatrix shaderMatrix;
- shaderMatrix.setTranslate(rect.left, rect.bottom);
- shaderMatrix.preScale(1, -1);
- LayerShader layerShader(layer, &shaderMatrix);
- paint.setShader(&layerShader);
- // Since the drawing primitive is defined in local drawing space,
- // we don't need to modify the draw matrix
- const SkPath* maskPath = layer->getConvexMask();
- DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
- paint.setShader(NULL);
- restore();
- return;
- }
- if (layer->region.isRect()) {
- layer->setRegionAsRect();
- DRAW_DOUBLE_STENCIL(composeLayerRect(layer, layer->regionRect));
- layer->region.clear();
- return;
- }
- EVENT_LOGD("composeLayerRegion");
- // standard Region based draw
- size_t count;
- const android::Rect* rects;
- Region safeRegion;
- if (CC_LIKELY(hasRectToRectTransform())) {
- rects = layer->region.getArray(&count);
- } else {
- safeRegion = Region::createTJunctionFreeRegion(layer->region);
- rects = safeRegion.getArray(&count);
- }
- const float alpha = getLayerAlpha(layer);
- const float texX = 1.0f / float(layer->getWidth());
- 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();
- uint32_t numQuads = 0;
- setupDrawWithTexture();
- setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter(layer->getColorFilter());
- setupDrawBlending(layer);
- setupDrawProgram();
- setupDrawDirtyRegionsDisabled();
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(layer->getColorFilter());
- setupDrawTexture(layer->getTexture());
- if (currentTransform()->isPureTranslate()) {
- const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
- const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
- layer->setFilter(GL_NEAREST);
- setupDrawModelView(kModelViewMode_Translate, false,
- x, y, x + rect.getWidth(), y + rect.getHeight(), true);
- } else {
- layer->setFilter(GL_LINEAR);
- setupDrawModelView(kModelViewMode_Translate, false,
- rect.left, rect.top, rect.right, rect.bottom);
- }
- setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
- for (size_t i = 0; i < count; i++) {
- const android::Rect* r = &rects[i];
- const float u1 = r->left * texX;
- const float v1 = (height - r->top) * texY;
- const float u2 = r->right * texX;
- const float v2 = (height - r->bottom) * texY;
- // TODO: Reject quads outside of the clip
- TextureVertex::set(mesh++, r->left, r->top, u1, v1);
- TextureVertex::set(mesh++, r->right, r->top, u2, v1);
- TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
- TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
- numQuads++;
- if (numQuads >= gMaxNumberOfQuads) {
- DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
- GL_UNSIGNED_SHORT, NULL));
- numQuads = 0;
- mesh = mCaches.getRegionMesh();
- }
- }
- if (numQuads > 0) {
- DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
- GL_UNSIGNED_SHORT, NULL));
- }
- #if DEBUG_LAYERS_AS_REGIONS
- drawRegionRectsDebug(layer->region);
- #endif
- layer->region.clear();
- }
- #if DEBUG_LAYERS_AS_REGIONS
- void OpenGLRenderer::drawRegionRectsDebug(const Region& region) {
- size_t count;
- const android::Rect* rects = region.getArray(&count);
- uint32_t colors[] = {
- 0x7fff0000, 0x7f00ff00,
- 0x7f0000ff, 0x7fff00ff,
- };
- int offset = 0;
- int32_t top = rects[0].top;
- for (size_t i = 0; i < count; i++) {
- if (top != rects[i].top) {
- offset ^= 0x2;
- top = rects[i].top;
- }
- SkPaint paint;
- paint.setColor(colors[offset + (i & 0x1)]);
- Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
- drawColorRect(r.left, r.top, r.right, r.bottom, paint);
- }
- }
- #endif
- void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty) {
- 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);
- it.next();
- }
- drawColorRects(rects.array(), rects.size(), &paint, true, dirty, false);
- }
- void OpenGLRenderer::dirtyLayer(const float left, const float top,
- const float right, const float bottom, const mat4 transform) {
- if (hasLayer()) {
- Rect bounds(left, top, right, bottom);
- transform.mapRect(bounds);
- dirtyLayerUnchecked(bounds, getRegion());
- }
- }
- void OpenGLRenderer::dirtyLayer(const float left, const float top,
- const float right, const float bottom) {
- if (hasLayer()) {
- Rect bounds(left, top, right, bottom);
- dirtyLayerUnchecked(bounds, getRegion());
- }
- }
- void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- if (bounds.intersect(*currentClipRect())) {
- bounds.snapToPixelBoundaries();
- android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
- if (!dirty.isEmpty()) {
- region->orSelf(dirty);
- }
- }
- }
- void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) {
- GLsizei elementsCount = quadsCount * 6;
- while (elementsCount > 0) {
- GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
- setupDrawIndexedVertices(&mesh[0].x);
- glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL);
- elementsCount -= drawCount;
- // Though there are 4 vertices in a quad, we use 6 indices per
- // quad to draw with GL_TRIANGLES
- mesh += (drawCount / 6) * 4;
- }
- }
- void OpenGLRenderer::clearLayerRegions() {
- const size_t count = mLayers.size();
- if (count == 0) return;
- if (!currentSnapshot()->isIgnored()) {
- EVENT_LOGD("clearLayerRegions");
- // Doing several glScissor/glClear here can negatively impact
- // GPUs with a tiler architecture, instead we draw quads with
- // the Clear blending mode
- // The list contains bounds that have already been clipped
- // against their initial clip rect, and the current clip
- // is likely different so we need to disable clipping here
- bool scissorChanged = mCaches.disableScissor();
- Vertex mesh[count * 4];
- Vertex* vertex = mesh;
- for (uint32_t i = 0; i < count; i++) {
- Rect* bounds = mLayers.itemAt(i);
- Vertex::set(vertex++, bounds->left, bounds->top);
- Vertex::set(vertex++, bounds->right, bounds->top);
- Vertex::set(vertex++, bounds->left, bounds->bottom);
- Vertex::set(vertex++, bounds->right, bounds->bottom);
- delete bounds;
- }
- // We must clear the list of dirty rects before we
- // call setupDraw() to prevent stencil setup to do
- // the same thing again
- mLayers.clear();
- SkPaint clearPaint;
- clearPaint.setXfermodeMode(SkXfermode::kClear_Mode);
- setupDraw(false);
- setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f);
- setupDrawBlending(&clearPaint, true);
- setupDrawProgram();
- setupDrawPureColorUniforms();
- setupDrawModelView(kModelViewMode_Translate, false,
- 0.0f, 0.0f, 0.0f, 0.0f, true);
- issueIndexedQuadDraw(&mesh[0], count);
- if (scissorChanged) mCaches.enableScissor();
- } else {
- for (uint32_t i = 0; i < count; i++) {
- delete mLayers.itemAt(i);
- }
- mLayers.clear();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // State Deferral
- ///////////////////////////////////////////////////////////////////////////////
- bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
- const Rect* currentClip = currentClipRect();
- const mat4* currentMatrix = currentTransform();
- if (stateDeferFlags & kStateDeferFlag_Draw) {
- // state has bounds initialized in local coordinates
- if (!state.mBounds.isEmpty()) {
- currentMatrix->mapRect(state.mBounds);
- Rect clippedBounds(state.mBounds);
- // NOTE: if we ever want to use this clipping info to drive whether the scissor
- // is used, it should more closely duplicate the quickReject logic (in how it uses
- // snapToPixelBoundaries)
- if(!clippedBounds.intersect(*currentClip)) {
- // quick rejected
- return true;
- }
- state.mClipSideFlags = kClipSide_None;
- if (!currentClip->contains(state.mBounds)) {
- int& flags = state.mClipSideFlags;
- // op partially clipped, so record which sides are clipped for clip-aware merging
- if (currentClip->left > state.mBounds.left) flags |= kClipSide_Left;
- if (currentClip->top > state.mBounds.top) flags |= kClipSide_Top;
- if (currentClip->right < state.mBounds.right) flags |= kClipSide_Right;
- if (currentClip->bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
- }
- state.mBounds.set(clippedBounds);
- } else {
- // Empty bounds implies size unknown. Label op as conservatively clipped to disable
- // overdraw avoidance (since we don't know what it overlaps)
- state.mClipSideFlags = kClipSide_ConservativeFull;
- state.mBounds.set(*currentClip);
- }
- }
- state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
- if (state.mClipValid) {
- state.mClip.set(*currentClip);
- }
- // Transform, drawModifiers, and alpha always deferred, since they are used by state operations
- // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
- state.mMatrix.load(*currentMatrix);
- state.mDrawModifiers = mDrawModifiers;
- state.mAlpha = currentSnapshot()->alpha;
- // always store/restore, since it's just a pointer
- state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
- return false;
- }
- void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
- setMatrix(state.mMatrix);
- mSnapshot->alpha = state.mAlpha;
- mDrawModifiers = state.mDrawModifiers;
- mSnapshot->roundRectClipState = state.mRoundRectClipState;
- if (state.mClipV