OpenGLRenderer.cpp revision db663fe83f976107fd8fd9307d871b37d9e47370
1ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist/*
2ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * Copyright (C) 2010 The Android Open Source Project
3ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
4ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * Licensed under the Apache License, Version 2.0 (the "License");
5ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * you may not use this file except in compliance with the License.
6ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * You may obtain a copy of the License at
7ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
8ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *      http://www.apache.org/licenses/LICENSE-2.0
9ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
10ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * Unless required by applicable law or agreed to in writing, software
11ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * distributed under the License is distributed on an "AS IS" BASIS,
12ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * See the License for the specific language governing permissions and
14ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * limitations under the License.
15ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist */
16ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
17ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "OpenGLRenderer.h"
18ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
19ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "DeferredDisplayList.h"
20ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "GammaFontRenderer.h"
21ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "Glop.h"
22ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "GlopBuilder.h"
23ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "Patch.h"
24ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "PathTessellator.h"
25ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "Properties.h"
26ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "RenderNode.h"
27ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "renderstate/MeshState.h"
28ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "renderstate/RenderState.h"
29ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "ShadowTessellator.h"
30ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "SkiaShader.h"
31ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "Vector.h"
32ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "VertexBuffer.h"
33ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "utils/GLUtils.h"
34ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "utils/PaintUtils.h"
35ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include "utils/TraceUtils.h"
36ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
37ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <stdlib.h>
38ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <stdint.h>
39ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <sys/types.h>
40ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
41ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <SkCanvas.h>
42ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <SkColor.h>
43ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <SkShader.h>
44ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <SkTypeface.h>
45ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
46ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <utils/Log.h>
47ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist#include <utils/StopWatch.h>
48
49#include <private/hwui/DrawGlInfo.h>
50
51#include <ui/Rect.h>
52
53#if DEBUG_DETAILED_EVENTS
54    #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
55#else
56    #define EVENT_LOGD(...)
57#endif
58
59namespace android {
60namespace uirenderer {
61
62///////////////////////////////////////////////////////////////////////////////
63// Constructors/destructor
64///////////////////////////////////////////////////////////////////////////////
65
66OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
67        : mState(*this)
68        , mCaches(Caches::getInstance())
69        , mRenderState(renderState)
70        , mFrameStarted(false)
71        , mScissorOptimizationDisabled(false)
72        , mSuppressTiling(false)
73        , mFirstFrameAfterResize(true)
74        , mDirty(false)
75        , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
76        , mLightRadius(FLT_MIN)
77        , mAmbientShadowAlpha(0)
78        , mSpotShadowAlpha(0) {
79    // *set* draw modifiers to be 0
80    memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
81    mDrawModifiers.mOverrideLayerAlpha = 1.0f;
82}
83
84OpenGLRenderer::~OpenGLRenderer() {
85    // The context has already been destroyed at this point, do not call
86    // GL APIs. All GL state should be kept in Caches.h
87}
88
89void OpenGLRenderer::initProperties() {
90    char property[PROPERTY_VALUE_MAX];
91    if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) {
92        mScissorOptimizationDisabled = !strcasecmp(property, "true");
93        INIT_LOGD("  Scissor optimization %s",
94                mScissorOptimizationDisabled ? "disabled" : "enabled");
95    } else {
96        INIT_LOGD("  Scissor optimization enabled");
97    }
98}
99
100void OpenGLRenderer::initLight(const Vector3& lightCenter, float lightRadius,
101        uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
102    mLightCenter = lightCenter;
103    mLightRadius = lightRadius;
104    mAmbientShadowAlpha = ambientShadowAlpha;
105    mSpotShadowAlpha = spotShadowAlpha;
106}
107
108///////////////////////////////////////////////////////////////////////////////
109// Setup
110///////////////////////////////////////////////////////////////////////////////
111
112void OpenGLRenderer::onViewportInitialized() {
113    glDisable(GL_DITHER);
114    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
115    mFirstFrameAfterResize = true;
116}
117
118void OpenGLRenderer::setupFrameState(float left, float top,
119        float right, float bottom, bool opaque) {
120    mCaches.clearGarbage();
121    mState.initializeSaveStack(left, top, right, bottom, mLightCenter);
122    mOpaque = opaque;
123    mTilingClip.set(left, top, right, bottom);
124}
125
126void OpenGLRenderer::startFrame() {
127    if (mFrameStarted) return;
128    mFrameStarted = true;
129
130    mState.setDirtyClip(true);
131
132    discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
133
134    mRenderState.setViewport(mState.getWidth(), mState.getHeight());
135
136    // Functors break the tiling extension in pretty spectacular ways
137    // This ensures we don't use tiling when a functor is going to be
138    // invoked during the frame
139    mSuppressTiling = mCaches.hasRegisteredFunctors()
140            || mFirstFrameAfterResize;
141    mFirstFrameAfterResize = false;
142
143    startTilingCurrentClip(true);
144
145    debugOverdraw(true, true);
146
147    clear(mTilingClip.left, mTilingClip.top,
148            mTilingClip.right, mTilingClip.bottom, mOpaque);
149}
150
151void OpenGLRenderer::prepareDirty(float left, float top,
152        float right, float bottom, bool opaque) {
153
154    setupFrameState(left, top, right, bottom, opaque);
155
156    // Layer renderers will start the frame immediately
157    // The framebuffer renderer will first defer the display list
158    // for each layer and wait until the first drawing command
159    // to start the frame
160    if (currentSnapshot()->fbo == 0) {
161        mRenderState.blend().syncEnabled();
162        updateLayers();
163    } else {
164        startFrame();
165    }
166}
167
168void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
169    // If we know that we are going to redraw the entire framebuffer,
170    // perform a discard to let the driver know we don't need to preserve
171    // the back buffer for this frame.
172    if (mCaches.extensions().hasDiscardFramebuffer() &&
173            left <= 0.0f && top <= 0.0f && right >= mState.getWidth() && bottom >= mState.getHeight()) {
174        const bool isFbo = getTargetFbo() == 0;
175        const GLenum attachments[] = {
176                isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
177                isFbo ? (const GLenum) GL_STENCIL_EXT : (const GLenum) GL_STENCIL_ATTACHMENT };
178        glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
179    }
180}
181
182void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
183    if (!opaque) {
184        mRenderState.scissor().setEnabled(true);
185        mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top);
186        glClear(GL_COLOR_BUFFER_BIT);
187        mDirty = true;
188        return;
189    }
190
191    mRenderState.scissor().reset();
192}
193
194void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
195    if (!mSuppressTiling) {
196        const Snapshot* snapshot = currentSnapshot();
197
198        const Rect* clip = &mTilingClip;
199        if (snapshot->flags & Snapshot::kFlagFboTarget) {
200            clip = &(snapshot->layer->clipRect);
201        }
202
203        startTiling(*clip, getViewportHeight(), opaque, expand);
204    }
205}
206
207void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) {
208    if (!mSuppressTiling) {
209        if(expand) {
210            // Expand the startTiling region by 1
211            int leftNotZero = (clip.left > 0) ? 1 : 0;
212            int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0;
213
214            mCaches.startTiling(
215                clip.left - leftNotZero,
216                windowHeight - clip.bottom - topNotZero,
217                clip.right - clip.left + leftNotZero + 1,
218                clip.bottom - clip.top + topNotZero + 1,
219                opaque);
220        } else {
221            mCaches.startTiling(clip.left, windowHeight - clip.bottom,
222                clip.right - clip.left, clip.bottom - clip.top, opaque);
223        }
224    }
225}
226
227void OpenGLRenderer::endTiling() {
228    if (!mSuppressTiling) mCaches.endTiling();
229}
230
231bool OpenGLRenderer::finish() {
232    renderOverdraw();
233    endTiling();
234    mTempPaths.clear();
235
236    // When finish() is invoked on FBO 0 we've reached the end
237    // of the current frame
238    if (getTargetFbo() == 0) {
239        mCaches.pathCache.trim();
240        mCaches.tessellationCache.trim();
241    }
242
243    if (!suppressErrorChecks()) {
244#if DEBUG_OPENGL
245        GLUtils::dumpGLErrors();
246#endif
247
248#if DEBUG_MEMORY_USAGE
249        mCaches.dumpMemoryUsage();
250#else
251        if (mCaches.getDebugLevel() & kDebugMemory) {
252            mCaches.dumpMemoryUsage();
253        }
254#endif
255    }
256
257    mFrameStarted = false;
258
259    return reportAndClearDirty();
260}
261
262void OpenGLRenderer::resumeAfterLayer() {
263    mRenderState.setViewport(getViewportWidth(), getViewportHeight());
264    mRenderState.bindFramebuffer(currentSnapshot()->fbo);
265    debugOverdraw(true, false);
266
267    mRenderState.scissor().reset();
268    dirtyClip();
269}
270
271void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
272    if (mState.currentlyIgnored()) return;
273
274    Rect clip(mState.currentClipRect());
275    clip.snapToPixelBoundaries();
276
277    // Since we don't know what the functor will draw, let's dirty
278    // the entire clip region
279    if (hasLayer()) {
280        dirtyLayerUnchecked(clip, getRegion());
281    }
282
283    DrawGlInfo info;
284    info.clipLeft = clip.left;
285    info.clipTop = clip.top;
286    info.clipRight = clip.right;
287    info.clipBottom = clip.bottom;
288    info.isLayer = hasLayer();
289    info.width = getViewportWidth();
290    info.height = getViewportHeight();
291    currentTransform()->copyTo(&info.transform[0]);
292
293    bool prevDirtyClip = mState.getDirtyClip();
294    // setup GL state for functor
295    if (mState.getDirtyClip()) {
296        setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
297    }
298    if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) {
299        setScissorFromClip();
300    }
301
302    mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info);
303    // Scissor may have been modified, reset dirty clip
304    dirtyClip();
305
306    mDirty = true;
307}
308
309///////////////////////////////////////////////////////////////////////////////
310// Debug
311///////////////////////////////////////////////////////////////////////////////
312
313void OpenGLRenderer::eventMarkDEBUG(const char* fmt, ...) const {
314#if DEBUG_DETAILED_EVENTS
315    const int BUFFER_SIZE = 256;
316    va_list ap;
317    char buf[BUFFER_SIZE];
318
319    va_start(ap, fmt);
320    vsnprintf(buf, BUFFER_SIZE, fmt, ap);
321    va_end(ap);
322
323    eventMark(buf);
324#endif
325}
326
327
328void OpenGLRenderer::eventMark(const char* name) const {
329    mCaches.eventMark(0, name);
330}
331
332void OpenGLRenderer::startMark(const char* name) const {
333    mCaches.startMark(0, name);
334}
335
336void OpenGLRenderer::endMark() const {
337    mCaches.endMark();
338}
339
340void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
341    mRenderState.debugOverdraw(enable, clear);
342}
343
344void OpenGLRenderer::renderOverdraw() {
345    if (mCaches.debugOverdraw && getTargetFbo() == 0) {
346        const Rect* clip = &mTilingClip;
347
348        mRenderState.scissor().setEnabled(true);
349        mRenderState.scissor().set(clip->left,
350                mState.firstSnapshot()->getViewportHeight() - clip->bottom,
351                clip->right - clip->left,
352                clip->bottom - clip->top);
353
354        // 1x overdraw
355        mRenderState.stencil().enableDebugTest(2);
356        drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
357
358        // 2x overdraw
359        mRenderState.stencil().enableDebugTest(3);
360        drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
361
362        // 3x overdraw
363        mRenderState.stencil().enableDebugTest(4);
364        drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
365
366        // 4x overdraw and higher
367        mRenderState.stencil().enableDebugTest(4, true);
368        drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
369
370        mRenderState.stencil().disable();
371    }
372}
373
374///////////////////////////////////////////////////////////////////////////////
375// Layers
376///////////////////////////////////////////////////////////////////////////////
377
378bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
379    if (layer->deferredUpdateScheduled && layer->renderer
380            && layer->renderNode.get() && layer->renderNode->isRenderable()) {
381
382        if (inFrame) {
383            endTiling();
384            debugOverdraw(false, false);
385        }
386
387        if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
388            layer->render(*this);
389        } else {
390            layer->defer(*this);
391        }
392
393        if (inFrame) {
394            resumeAfterLayer();
395            startTilingCurrentClip();
396        }
397
398        layer->debugDrawUpdate = mCaches.debugLayersUpdates;
399        layer->hasDrawnSinceUpdate = false;
400
401        return true;
402    }
403
404    return false;
405}
406
407void OpenGLRenderer::updateLayers() {
408    // If draw deferring is enabled this method will simply defer
409    // the display list of each individual layer. The layers remain
410    // in the layer updates list which will be cleared by flushLayers().
411    int count = mLayerUpdates.size();
412    if (count > 0) {
413        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
414            startMark("Layer Updates");
415        } else {
416            startMark("Defer Layer Updates");
417        }
418
419        // Note: it is very important to update the layers in order
420        for (int i = 0; i < count; i++) {
421            Layer* layer = mLayerUpdates.itemAt(i).get();
422            updateLayer(layer, false);
423        }
424
425        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
426            mLayerUpdates.clear();
427            mRenderState.bindFramebuffer(getTargetFbo());
428        }
429        endMark();
430    }
431}
432
433void OpenGLRenderer::flushLayers() {
434    int count = mLayerUpdates.size();
435    if (count > 0) {
436        startMark("Apply Layer Updates");
437
438        // Note: it is very important to update the layers in order
439        for (int i = 0; i < count; i++) {
440            mLayerUpdates.itemAt(i)->flush();
441        }
442
443        mLayerUpdates.clear();
444        mRenderState.bindFramebuffer(getTargetFbo());
445
446        endMark();
447    }
448}
449
450void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
451    if (layer) {
452        // Make sure we don't introduce duplicates.
453        // SortedVector would do this automatically but we need to respect
454        // the insertion order. The linear search is not an issue since
455        // this list is usually very short (typically one item, at most a few)
456        for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
457            if (mLayerUpdates.itemAt(i) == layer) {
458                return;
459            }
460        }
461        mLayerUpdates.push_back(layer);
462    }
463}
464
465void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
466    if (layer) {
467        for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
468            if (mLayerUpdates.itemAt(i) == layer) {
469                mLayerUpdates.removeAt(i);
470                break;
471            }
472        }
473    }
474}
475
476void OpenGLRenderer::flushLayerUpdates() {
477    ATRACE_NAME("Update HW Layers");
478    mRenderState.blend().syncEnabled();
479    updateLayers();
480    flushLayers();
481    // Wait for all the layer updates to be executed
482    glFinish();
483}
484
485void OpenGLRenderer::markLayersAsBuildLayers() {
486    for (size_t i = 0; i < mLayerUpdates.size(); i++) {
487        mLayerUpdates[i]->wasBuildLayered = true;
488    }
489}
490
491///////////////////////////////////////////////////////////////////////////////
492// State management
493///////////////////////////////////////////////////////////////////////////////
494
495void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
496    bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer;
497    bool restoreClip = removed.flags & Snapshot::kFlagClipSet;
498    bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer;
499
500    if (restoreViewport) {
501        mRenderState.setViewport(getViewportWidth(), getViewportHeight());
502    }
503
504    if (restoreClip) {
505        dirtyClip();
506    }
507
508    if (restoreLayer) {
509        endMark(); // Savelayer
510        ATRACE_END(); // SaveLayer
511        startMark("ComposeLayer");
512        composeLayer(removed, restored);
513        endMark();
514    }
515}
516
517///////////////////////////////////////////////////////////////////////////////
518// Layers
519///////////////////////////////////////////////////////////////////////////////
520
521int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
522        const SkPaint* paint, int flags, const SkPath* convexMask) {
523    // force matrix/clip isolation for layer
524    flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
525
526    const int count = mState.saveSnapshot(flags);
527
528    if (!mState.currentlyIgnored()) {
529        createLayer(left, top, right, bottom, paint, flags, convexMask);
530    }
531
532    return count;
533}
534
535void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
536    const Rect untransformedBounds(bounds);
537
538    currentTransform()->mapRect(bounds);
539
540    // Layers only make sense if they are in the framebuffer's bounds
541    if (bounds.intersect(mState.currentClipRect())) {
542        // We cannot work with sub-pixels in this case
543        bounds.snapToPixelBoundaries();
544
545        // When the layer is not an FBO, we may use glCopyTexImage so we
546        // need to make sure the layer does not extend outside the bounds
547        // of the framebuffer
548        const Snapshot& previous = *(currentSnapshot()->previous);
549        Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
550        if (!bounds.intersect(previousViewport)) {
551            bounds.setEmpty();
552        } else if (fboLayer) {
553            clip.set(bounds);
554            mat4 inverse;
555            inverse.loadInverse(*currentTransform());
556            inverse.mapRect(clip);
557            clip.snapToPixelBoundaries();
558            if (clip.intersect(untransformedBounds)) {
559                clip.translate(-untransformedBounds.left, -untransformedBounds.top);
560                bounds.set(untransformedBounds);
561            } else {
562                clip.setEmpty();
563            }
564        }
565    } else {
566        bounds.setEmpty();
567    }
568}
569
570void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
571        bool fboLayer, int alpha) {
572    if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
573            bounds.getHeight() > mCaches.maxTextureSize ||
574            (fboLayer && clip.isEmpty())) {
575        writableSnapshot()->empty = fboLayer;
576    } else {
577        writableSnapshot()->invisible = writableSnapshot()->invisible || (alpha <= 0 && fboLayer);
578    }
579}
580
581int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
582        const SkPaint* paint, int flags) {
583    const int count = mState.saveSnapshot(flags);
584
585    if (!mState.currentlyIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
586        // initialize the snapshot as though it almost represents an FBO layer so deferred draw
587        // operations will be able to store and restore the current clip and transform info, and
588        // quick rejection will be correct (for display lists)
589
590        Rect bounds(left, top, right, bottom);
591        Rect clip;
592        calculateLayerBoundsAndClip(bounds, clip, true);
593        updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
594
595        if (!mState.currentlyIgnored()) {
596            writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
597            writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
598            writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
599            writableSnapshot()->roundRectClipState = nullptr;
600        }
601    }
602
603    return count;
604}
605
606/**
607 * Layers are viewed by Skia are slightly different than layers in image editing
608 * programs (for instance.) When a layer is created, previously created layers
609 * and the frame buffer still receive every drawing command. For instance, if a
610 * layer is created and a shape intersecting the bounds of the layers and the
611 * framebuffer is draw, the shape will be drawn on both (unless the layer was
612 * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
613 *
614 * A way to implement layers is to create an FBO for each layer, backed by an RGBA
615 * texture. Unfortunately, this is inefficient as it requires every primitive to
616 * be drawn n + 1 times, where n is the number of active layers. In practice this
617 * means, for every primitive:
618 *   - Switch active frame buffer
619 *   - Change viewport, clip and projection matrix
620 *   - Issue the drawing
621 *
622 * Switching rendering target n + 1 times per drawn primitive is extremely costly.
623 * To avoid this, layers are implemented in a different way here, at least in the
624 * general case. FBOs are used, as an optimization, when the "clip to layer" flag
625 * is set. When this flag is set we can redirect all drawing operations into a
626 * single FBO.
627 *
628 * This implementation relies on the frame buffer being at least RGBA 8888. When
629 * a layer is created, only a texture is created, not an FBO. The content of the
630 * frame buffer contained within the layer's bounds is copied into this texture
631 * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
632 * buffer and drawing continues as normal. This technique therefore treats the
633 * frame buffer as a scratch buffer for the layers.
634 *
635 * To compose the layers back onto the frame buffer, each layer texture
636 * (containing the original frame buffer data) is drawn as a simple quad over
637 * the frame buffer. The trick is that the quad is set as the composition
638 * destination in the blending equation, and the frame buffer becomes the source
639 * of the composition.
640 *
641 * Drawing layers with an alpha value requires an extra step before composition.
642 * An empty quad is drawn over the layer's region in the frame buffer. This quad
643 * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
644 * quad is used to multiply the colors in the frame buffer. This is achieved by
645 * changing the GL blend functions for the GL_FUNC_ADD blend equation to
646 * GL_ZERO, GL_SRC_ALPHA.
647 *
648 * Because glCopyTexImage2D() can be slow, an alternative implementation might
649 * be use to draw a single clipped layer. The implementation described above
650 * is correct in every case.
651 *
652 * (1) The frame buffer is actually not cleared right away. To allow the GPU
653 *     to potentially optimize series of calls to glCopyTexImage2D, the frame
654 *     buffer is left untouched until the first drawing operation. Only when
655 *     something actually gets drawn are the layers regions cleared.
656 */
657bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
658        const SkPaint* paint, int flags, const SkPath* convexMask) {
659    LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
660    LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
661
662    const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
663
664    // Window coordinates of the layer
665    Rect clip;
666    Rect bounds(left, top, right, bottom);
667    calculateLayerBoundsAndClip(bounds, clip, fboLayer);
668    updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
669
670    // Bail out if we won't draw in this snapshot
671    if (mState.currentlyIgnored()) {
672        return false;
673    }
674
675    mCaches.textureState().activateTexture(0);
676    Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
677    if (!layer) {
678        return false;
679    }
680
681    layer->setPaint(paint);
682    layer->layer.set(bounds);
683    layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
684            bounds.getWidth() / float(layer->getWidth()), 0.0f);
685
686    layer->setBlend(true);
687    layer->setDirty(false);
688    layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache
689
690    // Save the layer in the snapshot
691    writableSnapshot()->flags |= Snapshot::kFlagIsLayer;
692    writableSnapshot()->layer = layer;
693
694    ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u",
695            fboLayer ? "" : "unclipped ",
696            layer->getWidth(), layer->getHeight());
697    startMark("SaveLayer");
698    if (fboLayer) {
699        return createFboLayer(layer, bounds, clip);
700    } else {
701        // Copy the framebuffer into the layer
702        layer->bindTexture();
703        if (!bounds.isEmpty()) {
704            if (layer->isEmpty()) {
705                // Workaround for some GL drivers. When reading pixels lying outside
706                // of the window we should get undefined values for those pixels.
707                // Unfortunately some drivers will turn the entire target texture black
708                // when reading outside of the window.
709                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->getWidth(), layer->getHeight(),
710                        0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
711                layer->setEmpty(false);
712            }
713
714            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
715                    bounds.left, getViewportHeight() - bounds.bottom,
716                    bounds.getWidth(), bounds.getHeight());
717
718            // Enqueue the buffer coordinates to clear the corresponding region later
719            mLayers.push_back(Rect(bounds));
720        }
721    }
722
723    return true;
724}
725
726bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
727    layer->clipRect.set(clip);
728    layer->setFbo(mCaches.fboCache.get());
729
730    writableSnapshot()->region = &writableSnapshot()->layer->region;
731    writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
732    writableSnapshot()->fbo = layer->getFbo();
733    writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
734    writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
735    writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
736    writableSnapshot()->roundRectClipState = nullptr;
737
738    endTiling();
739    debugOverdraw(false, false);
740    // Bind texture to FBO
741    mRenderState.bindFramebuffer(layer->getFbo());
742    layer->bindTexture();
743
744    // Initialize the texture if needed
745    if (layer->isEmpty()) {
746        layer->allocateTexture();
747        layer->setEmpty(false);
748    }
749
750    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
751            layer->getTextureId(), 0);
752
753    // Expand the startTiling region by 1
754    startTilingCurrentClip(true, true);
755
756    // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
757    mRenderState.scissor().setEnabled(true);
758    mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
759            clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
760    glClear(GL_COLOR_BUFFER_BIT);
761
762    dirtyClip();
763
764    // Change the ortho projection
765    mRenderState.setViewport(bounds.getWidth(), bounds.getHeight());
766    return true;
767}
768
769/**
770 * Read the documentation of createLayer() before doing anything in this method.
771 */
772void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) {
773    if (!removed.layer) {
774        ALOGE("Attempting to compose a layer that does not exist");
775        return;
776    }
777
778    Layer* layer = removed.layer;
779    const Rect& rect = layer->layer;
780    const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer;
781
782    bool clipRequired = false;
783    mState.calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
784            &clipRequired, nullptr, false); // safely ignore return, should never be rejected
785    mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
786
787    if (fboLayer) {
788        endTiling();
789
790        // Detach the texture from the FBO
791        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
792
793        layer->removeFbo(false);
794
795        // Unbind current FBO and restore previous one
796        mRenderState.bindFramebuffer(restored.fbo);
797        debugOverdraw(true, false);
798
799        startTilingCurrentClip();
800    }
801
802    if (!fboLayer && layer->getAlpha() < 255) {
803        SkPaint layerPaint;
804        layerPaint.setAlpha(layer->getAlpha());
805        layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
806        layerPaint.setColorFilter(layer->getColorFilter());
807
808        drawColorRect(rect.left, rect.top, rect.right, rect.bottom, &layerPaint, true);
809        // Required below, composeLayerRect() will divide by 255
810        layer->setAlpha(255);
811    }
812
813    mRenderState.meshState().unbindMeshBuffer();
814
815    mCaches.textureState().activateTexture(0);
816
817    // When the layer is stored in an FBO, we can save a bit of fillrate by
818    // drawing only the dirty region
819    if (fboLayer) {
820        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform);
821        composeLayerRegion(layer, rect);
822    } else if (!rect.isEmpty()) {
823        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
824
825        save(0);
826        // the layer contains screen buffer content that shouldn't be alpha modulated
827        // (and any necessary alpha modulation was handled drawing into the layer)
828        writableSnapshot()->alpha = 1.0f;
829        composeLayerRect(layer, rect, true);
830        restore();
831    }
832
833    dirtyClip();
834
835    // Failing to add the layer to the cache should happen only if the layer is too large
836    layer->setConvexMask(nullptr);
837    if (!mCaches.layerCache.put(layer)) {
838        LAYER_LOGD("Deleting layer");
839        layer->decStrong(nullptr);
840    }
841}
842
843void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
844    bool snap = !layer->getForceFilter()
845            && layer->getWidth() == (uint32_t) rect.getWidth()
846            && layer->getHeight() == (uint32_t) rect.getHeight();
847    Glop glop;
848    GlopBuilder(mRenderState, mCaches, &glop)
849            .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
850            .setFillTextureLayer(*layer, getLayerAlpha(layer))
851            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
852            .setModelViewMapUnitToRectOptionalSnap(snap, rect)
853            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
854            .build();
855    renderGlop(glop);
856}
857
858void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
859    if (layer->isTextureLayer()) {
860        EVENT_LOGD("composeTextureLayerRect");
861        drawTextureLayer(layer, rect);
862    } else {
863        EVENT_LOGD("composeHardwareLayerRect");
864
865        Blend::ModeOrderSwap modeUsage = swap ?
866                Blend::ModeOrderSwap::Swap : Blend::ModeOrderSwap::NoSwap;
867        const Matrix4& transform = swap ? Matrix4::identity() : *currentTransform();
868        bool snap = !swap
869                && layer->getWidth() == static_cast<uint32_t>(rect.getWidth())
870                && layer->getHeight() == static_cast<uint32_t>(rect.getHeight());
871        Glop glop;
872        GlopBuilder(mRenderState, mCaches, &glop)
873                .setMeshTexturedUvQuad(nullptr, layer->texCoords)
874                .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), modeUsage)
875                .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
876                .setModelViewMapUnitToRectOptionalSnap(snap, rect)
877                .setRoundRectClipState(currentSnapshot()->roundRectClipState)
878                .build();
879        renderGlop(glop);
880    }
881}
882
883/**
884 * Issues the command X, and if we're composing a save layer to the fbo or drawing a newly updated
885 * hardware layer with overdraw debug on, draws again to the stencil only, so that these draw
886 * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used
887 * by saveLayer's restore
888 */
889#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) {                             \
890        DRAW_COMMAND;                                                            \
891        if (CC_UNLIKELY(mCaches.debugOverdraw && getTargetFbo() == 0 && COND)) { \
892            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);                 \
893            DRAW_COMMAND;                                                        \
894            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);                     \
895        }                                                                        \
896    }
897
898#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
899
900// This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
901// use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
902class LayerShader : public SkShader {
903public:
904    LayerShader(Layer* layer, const SkMatrix* localMatrix)
905    : INHERITED(localMatrix)
906    , mLayer(layer) {
907    }
908
909    virtual bool asACustomShader(void** data) const override {
910        if (data) {
911            *data = static_cast<void*>(mLayer);
912        }
913        return true;
914    }
915
916    virtual bool isOpaque() const override {
917        return !mLayer->isBlend();
918    }
919
920protected:
921    virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
922        LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
923    }
924
925    virtual void flatten(SkWriteBuffer&) const override {
926        LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
927    }
928
929    virtual Factory getFactory() const override {
930        LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
931        return nullptr;
932    }
933private:
934    // Unowned.
935    Layer* mLayer;
936    typedef SkShader INHERITED;
937};
938
939void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
940    if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
941
942    if (layer->getConvexMask()) {
943        save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
944
945        // clip to the area of the layer the mask can be larger
946        clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
947
948        SkPaint paint;
949        paint.setAntiAlias(true);
950        paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
951
952        // create LayerShader to map SaveLayer content into subsequent draw
953        SkMatrix shaderMatrix;
954        shaderMatrix.setTranslate(rect.left, rect.bottom);
955        shaderMatrix.preScale(1, -1);
956        LayerShader layerShader(layer, &shaderMatrix);
957        paint.setShader(&layerShader);
958
959        // Since the drawing primitive is defined in local drawing space,
960        // we don't need to modify the draw matrix
961        const SkPath* maskPath = layer->getConvexMask();
962        DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
963
964        paint.setShader(nullptr);
965        restore();
966
967        return;
968    }
969
970    if (layer->region.isRect()) {
971        layer->setRegionAsRect();
972
973        DRAW_DOUBLE_STENCIL(composeLayerRect(layer, layer->regionRect));
974
975        layer->region.clear();
976        return;
977    }
978
979    EVENT_LOGD("composeLayerRegion");
980    // standard Region based draw
981    size_t count;
982    const android::Rect* rects;
983    Region safeRegion;
984    if (CC_LIKELY(hasRectToRectTransform())) {
985        rects = layer->region.getArray(&count);
986    } else {
987        safeRegion = Region::createTJunctionFreeRegion(layer->region);
988        rects = safeRegion.getArray(&count);
989    }
990
991    const float texX = 1.0f / float(layer->getWidth());
992    const float texY = 1.0f / float(layer->getHeight());
993    const float height = rect.getHeight();
994
995    TextureVertex quadVertices[count * 4];
996    TextureVertex* mesh = &quadVertices[0];
997    for (size_t i = 0; i < count; i++) {
998        const android::Rect* r = &rects[i];
999
1000        const float u1 = r->left * texX;
1001        const float v1 = (height - r->top) * texY;
1002        const float u2 = r->right * texX;
1003        const float v2 = (height - r->bottom) * texY;
1004
1005        // TODO: Reject quads outside of the clip
1006        TextureVertex::set(mesh++, r->left, r->top, u1, v1);
1007        TextureVertex::set(mesh++, r->right, r->top, u2, v1);
1008        TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
1009        TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
1010    }
1011    Rect modelRect = Rect(rect.getWidth(), rect.getHeight());
1012    Glop glop;
1013    GlopBuilder(mRenderState, mCaches, &glop)
1014            .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
1015            .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
1016            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
1017            .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
1018            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1019            .build();
1020    DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
1021
1022#if DEBUG_LAYERS_AS_REGIONS
1023    drawRegionRectsDebug(layer->region);
1024#endif
1025
1026    layer->region.clear();
1027}
1028
1029#if DEBUG_LAYERS_AS_REGIONS
1030void OpenGLRenderer::drawRegionRectsDebug(const Region& region) {
1031    size_t count;
1032    const android::Rect* rects = region.getArray(&count);
1033
1034    uint32_t colors[] = {
1035            0x7fff0000, 0x7f00ff00,
1036            0x7f0000ff, 0x7fff00ff,
1037    };
1038
1039    int offset = 0;
1040    int32_t top = rects[0].top;
1041
1042    for (size_t i = 0; i < count; i++) {
1043        if (top != rects[i].top) {
1044            offset ^= 0x2;
1045            top = rects[i].top;
1046        }
1047
1048        SkPaint paint;
1049        paint.setColor(colors[offset + (i & 0x1)]);
1050        Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
1051        drawColorRect(r.left, r.top, r.right, r.bottom, paint);
1052    }
1053}
1054#endif
1055
1056void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty) {
1057    Vector<float> rects;
1058
1059    SkRegion::Iterator it(region);
1060    while (!it.done()) {
1061        const SkIRect& r = it.rect();
1062        rects.push(r.fLeft);
1063        rects.push(r.fTop);
1064        rects.push(r.fRight);
1065        rects.push(r.fBottom);
1066        it.next();
1067    }
1068
1069    drawColorRects(rects.array(), rects.size(), &paint, true, dirty, false);
1070}
1071
1072void OpenGLRenderer::dirtyLayer(const float left, const float top,
1073        const float right, const float bottom, const Matrix4& transform) {
1074    if (hasLayer()) {
1075        Rect bounds(left, top, right, bottom);
1076        transform.mapRect(bounds);
1077        dirtyLayerUnchecked(bounds, getRegion());
1078    }
1079}
1080
1081void OpenGLRenderer::dirtyLayer(const float left, const float top,
1082        const float right, const float bottom) {
1083    if (hasLayer()) {
1084        Rect bounds(left, top, right, bottom);
1085        dirtyLayerUnchecked(bounds, getRegion());
1086    }
1087}
1088
1089void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
1090    if (bounds.intersect(mState.currentClipRect())) {
1091        bounds.snapToPixelBoundaries();
1092        android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
1093        if (!dirty.isEmpty()) {
1094            region->orSelf(dirty);
1095        }
1096    }
1097}
1098
1099void OpenGLRenderer::clearLayerRegions() {
1100    const size_t quadCount = mLayers.size();
1101    if (quadCount == 0) return;
1102
1103    if (!mState.currentlyIgnored()) {
1104        EVENT_LOGD("clearLayerRegions");
1105        // Doing several glScissor/glClear here can negatively impact
1106        // GPUs with a tiler architecture, instead we draw quads with
1107        // the Clear blending mode
1108
1109        // The list contains bounds that have already been clipped
1110        // against their initial clip rect, and the current clip
1111        // is likely different so we need to disable clipping here
1112        bool scissorChanged = mRenderState.scissor().setEnabled(false);
1113
1114        Vertex mesh[quadCount * 4];
1115        Vertex* vertex = mesh;
1116
1117        for (uint32_t i = 0; i < quadCount; i++) {
1118            const Rect& bounds = mLayers[i];
1119
1120            Vertex::set(vertex++, bounds.left, bounds.top);
1121            Vertex::set(vertex++, bounds.right, bounds.top);
1122            Vertex::set(vertex++, bounds.left, bounds.bottom);
1123            Vertex::set(vertex++, bounds.right, bounds.bottom);
1124        }
1125        // We must clear the list of dirty rects before we
1126        // call clearLayerRegions() in renderGlop to prevent
1127        // stencil setup from doing the same thing again
1128        mLayers.clear();
1129
1130        Glop glop;
1131        GlopBuilder(mRenderState, mCaches, &glop)
1132                .setMeshIndexedQuads(&mesh[0], quadCount)
1133                .setFillClear()
1134                .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
1135                .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
1136                .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1137                .build();
1138        renderGlop(glop, false);
1139
1140        if (scissorChanged) mRenderState.scissor().setEnabled(true);
1141    } else {
1142        mLayers.clear();
1143    }
1144}
1145
1146///////////////////////////////////////////////////////////////////////////////
1147// State Deferral
1148///////////////////////////////////////////////////////////////////////////////
1149
1150bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
1151    const Rect& currentClip = mState.currentClipRect();
1152    const mat4* currentMatrix = currentTransform();
1153
1154    if (stateDeferFlags & kStateDeferFlag_Draw) {
1155        // state has bounds initialized in local coordinates
1156        if (!state.mBounds.isEmpty()) {
1157            currentMatrix->mapRect(state.mBounds);
1158            Rect clippedBounds(state.mBounds);
1159            // NOTE: if we ever want to use this clipping info to drive whether the scissor
1160            // is used, it should more closely duplicate the quickReject logic (in how it uses
1161            // snapToPixelBoundaries)
1162
1163            if (!clippedBounds.intersect(currentClip)) {
1164                // quick rejected
1165                return true;
1166            }
1167
1168            state.mClipSideFlags = kClipSide_None;
1169            if (!currentClip.contains(state.mBounds)) {
1170                int& flags = state.mClipSideFlags;
1171                // op partially clipped, so record which sides are clipped for clip-aware merging
1172                if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
1173                if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
1174                if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
1175                if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
1176            }
1177            state.mBounds.set(clippedBounds);
1178        } else {
1179            // Empty bounds implies size unknown. Label op as conservatively clipped to disable
1180            // overdraw avoidance (since we don't know what it overlaps)
1181            state.mClipSideFlags = kClipSide_ConservativeFull;
1182            state.mBounds.set(currentClip);
1183        }
1184    }
1185
1186    state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
1187    if (state.mClipValid) {
1188        state.mClip.set(currentClip);
1189    }
1190
1191    // Transform, drawModifiers, and alpha always deferred, since they are used by state operations
1192    // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
1193    state.mMatrix.load(*currentMatrix);
1194    state.mDrawModifiers = mDrawModifiers;
1195    state.mAlpha = currentSnapshot()->alpha;
1196
1197    // always store/restore, since it's just a pointer
1198    state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
1199    return false;
1200}
1201
1202void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
1203    setMatrix(state.mMatrix);
1204    writableSnapshot()->alpha = state.mAlpha;
1205    mDrawModifiers = state.mDrawModifiers;
1206    writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
1207
1208    if (state.mClipValid && !skipClipRestore) {
1209        writableSnapshot()->setClip(state.mClip.left, state.mClip.top,
1210                state.mClip.right, state.mClip.bottom);
1211        dirtyClip();
1212    }
1213}
1214
1215/**
1216 * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
1217 * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
1218 * least one op is clipped), or disabled entirely (because no merged op is clipped)
1219 *
1220 * This method should be called when restoreDisplayState() won't be restoring the clip
1221 */
1222void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
1223    if (clipRect != nullptr) {
1224        writableSnapshot()->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
1225    } else {
1226        writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight());
1227    }
1228    dirtyClip();
1229    bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled;
1230    mRenderState.scissor().setEnabled(enableScissor);
1231}
1232
1233///////////////////////////////////////////////////////////////////////////////
1234// Clipping
1235///////////////////////////////////////////////////////////////////////////////
1236
1237void OpenGLRenderer::setScissorFromClip() {
1238    Rect clip(mState.currentClipRect());
1239    clip.snapToPixelBoundaries();
1240
1241    if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
1242            clip.getWidth(), clip.getHeight())) {
1243        mState.setDirtyClip(false);
1244    }
1245}
1246
1247void OpenGLRenderer::ensureStencilBuffer() {
1248    // Thanks to the mismatch between EGL and OpenGL ES FBO we
1249    // cannot attach a stencil buffer to fbo0 dynamically. Let's
1250    // just hope we have one when hasLayer() returns false.
1251    if (hasLayer()) {
1252        attachStencilBufferToLayer(currentSnapshot()->layer);
1253    }
1254}
1255
1256void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
1257    // The layer's FBO is already bound when we reach this stage
1258    if (!layer->getStencilRenderBuffer()) {
1259        // GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
1260        // is attached after we initiated tiling. We must turn it off,
1261        // attach the new render buffer then turn tiling back on
1262        endTiling();
1263
1264        RenderBuffer* buffer = mCaches.renderBufferCache.get(
1265                Stencil::getSmallestStencilFormat(),
1266                layer->getWidth(), layer->getHeight());
1267        layer->setStencilRenderBuffer(buffer);
1268
1269        startTiling(layer->clipRect, layer->layer.getHeight());
1270    }
1271}
1272
1273static void handlePoint(std::vector<Vertex>& rectangleVertices, const Matrix4& transform,
1274        float x, float y) {
1275    Vertex v;
1276    v.x = x;
1277    v.y = y;
1278    transform.mapPoint(v.x, v.y);
1279    rectangleVertices.push_back(v);
1280}
1281
1282static void handlePointNoTransform(std::vector<Vertex>& rectangleVertices, float x, float y) {
1283    Vertex v;
1284    v.x = x;
1285    v.y = y;
1286    rectangleVertices.push_back(v);
1287}
1288
1289void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) {
1290    int quadCount = rectangleList.getTransformedRectanglesCount();
1291    std::vector<Vertex> rectangleVertices(quadCount * 4);
1292    Rect scissorBox = rectangleList.calculateBounds();
1293    scissorBox.snapToPixelBoundaries();
1294    for (int i = 0; i < quadCount; ++i) {
1295        const TransformedRectangle& tr(rectangleList.getTransformedRectangle(i));
1296        const Matrix4& transform = tr.getTransform();
1297        Rect bounds = tr.getBounds();
1298        if (transform.rectToRect()) {
1299            transform.mapRect(bounds);
1300            if (!bounds.intersect(scissorBox)) {
1301                bounds.setEmpty();
1302            } else {
1303                handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
1304                handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
1305                handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
1306                handlePointNoTransform(rectangleVertices, bounds.right, bounds.bottom);
1307            }
1308        } else {
1309            handlePoint(rectangleVertices, transform, bounds.left, bounds.top);
1310            handlePoint(rectangleVertices, transform, bounds.right, bounds.top);
1311            handlePoint(rectangleVertices, transform, bounds.left, bounds.bottom);
1312            handlePoint(rectangleVertices, transform, bounds.right, bounds.bottom);
1313        }
1314    }
1315
1316    mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom,
1317            scissorBox.getWidth(), scissorBox.getHeight());
1318
1319    Glop glop;
1320    Vertex* vertices = &rectangleVertices[0];
1321    GlopBuilder(mRenderState, mCaches, &glop)
1322            .setMeshIndexedQuads(vertices, rectangleVertices.size() / 4)
1323            .setFillBlack()
1324            .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
1325            .setModelViewOffsetRect(0, 0, scissorBox)
1326            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1327            .build();
1328    renderGlop(glop);
1329}
1330
1331void OpenGLRenderer::setStencilFromClip() {
1332    if (!mCaches.debugOverdraw) {
1333        if (!currentSnapshot()->clipIsSimple()) {
1334            int incrementThreshold;
1335            EVENT_LOGD("setStencilFromClip - enabling");
1336
1337            // NOTE: The order here is important, we must set dirtyClip to false
1338            //       before any draw call to avoid calling back into this method
1339            mState.setDirtyClip(false);
1340
1341            ensureStencilBuffer();
1342
1343            const ClipArea& clipArea = currentSnapshot()->getClipArea();
1344
1345            bool isRectangleList = clipArea.isRectangleList();
1346            if (isRectangleList) {
1347                incrementThreshold = clipArea.getRectangleList().getTransformedRectanglesCount();
1348            } else {
1349                incrementThreshold = 0;
1350            }
1351
1352            mRenderState.stencil().enableWrite(incrementThreshold);
1353
1354            // Clean and update the stencil, but first make sure we restrict drawing
1355            // to the region's bounds
1356            bool resetScissor = mRenderState.scissor().setEnabled(true);
1357            if (resetScissor) {
1358                // The scissor was not set so we now need to update it
1359                setScissorFromClip();
1360            }
1361
1362            mRenderState.stencil().clear();
1363
1364            // stash and disable the outline clip state, since stencil doesn't account for outline
1365            bool storedSkipOutlineClip = mSkipOutlineClip;
1366            mSkipOutlineClip = true;
1367
1368            SkPaint paint;
1369            paint.setColor(SK_ColorBLACK);
1370            paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1371
1372            if (isRectangleList) {
1373                drawRectangleList(clipArea.getRectangleList());
1374            } else {
1375                // NOTE: We could use the region contour path to generate a smaller mesh
1376                //       Since we are using the stencil we could use the red book path
1377                //       drawing technique. It might increase bandwidth usage though.
1378
1379                // The last parameter is important: we are not drawing in the color buffer
1380                // so we don't want to dirty the current layer, if any
1381                drawRegionRects(clipArea.getClipRegion(), paint, false);
1382            }
1383            if (resetScissor) mRenderState.scissor().setEnabled(false);
1384            mSkipOutlineClip = storedSkipOutlineClip;
1385
1386            mRenderState.stencil().enableTest(incrementThreshold);
1387
1388            // Draw the region used to generate the stencil if the appropriate debug
1389            // mode is enabled
1390            // TODO: Implement for rectangle list clip areas
1391            if (mCaches.debugStencilClip == Caches::kStencilShowRegion &&
1392                    !clipArea.isRectangleList()) {
1393                paint.setColor(0x7f0000ff);
1394                paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
1395                drawRegionRects(currentSnapshot()->getClipRegion(), paint);
1396            }
1397        } else {
1398            EVENT_LOGD("setStencilFromClip - disabling");
1399            mRenderState.stencil().disable();
1400        }
1401    }
1402}
1403
1404/**
1405 * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out.
1406 *
1407 * @param paint if not null, the bounds will be expanded to account for stroke depending on paint
1408 *         style, and tessellated AA ramp
1409 */
1410bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom,
1411        const SkPaint* paint) {
1412    bool snapOut = paint && paint->isAntiAlias();
1413
1414    if (paint && paint->getStyle() != SkPaint::kFill_Style) {
1415        float outset = paint->getStrokeWidth() * 0.5f;
1416        left -= outset;
1417        top -= outset;
1418        right += outset;
1419        bottom += outset;
1420    }
1421
1422    bool clipRequired = false;
1423    bool roundRectClipRequired = false;
1424    if (mState.calculateQuickRejectForScissor(left, top, right, bottom,
1425            &clipRequired, &roundRectClipRequired, snapOut)) {
1426        return true;
1427    }
1428
1429    // not quick rejected, so enable the scissor if clipRequired
1430    mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
1431    mSkipOutlineClip = !roundRectClipRequired;
1432    return false;
1433}
1434
1435void OpenGLRenderer::debugClip() {
1436#if DEBUG_CLIP_REGIONS
1437    if (!currentSnapshot()->clipRegion->isEmpty()) {
1438        SkPaint paint;
1439        paint.setColor(0x7f00ff00);
1440        drawRegionRects(*(currentSnapshot()->clipRegion, paint);
1441
1442    }
1443#endif
1444}
1445
1446void OpenGLRenderer::renderGlop(const Glop& glop, bool clearLayer) {
1447    // TODO: It would be best if we could do this before quickRejectSetupScissor()
1448    //       changes the scissor test state
1449    if (clearLayer) clearLayerRegions();
1450
1451    if (mState.getDirtyClip()) {
1452        if (mRenderState.scissor().isEnabled()) {
1453            setScissorFromClip();
1454        }
1455
1456        setStencilFromClip();
1457    }
1458    mRenderState.render(glop);
1459    if (!mRenderState.stencil().isWriteEnabled()) {
1460        // TODO: specify more clearly when a draw should dirty the layer.
1461        // is writing to the stencil the only time we should ignore this?
1462        dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
1463        mDirty = true;
1464    }
1465}
1466
1467///////////////////////////////////////////////////////////////////////////////
1468// Drawing
1469///////////////////////////////////////////////////////////////////////////////
1470
1471void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
1472    // All the usual checks and setup operations (quickReject, setupDraw, etc.)
1473    // will be performed by the display list itself
1474    if (renderNode && renderNode->isRenderable()) {
1475        // compute 3d ordering
1476        renderNode->computeOrdering();
1477        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
1478            startFrame();
1479            ReplayStateStruct replayStruct(*this, dirty, replayFlags);
1480            renderNode->replay(replayStruct, 0);
1481            return;
1482        }
1483
1484        // Don't avoid overdraw when visualizing, since that makes it harder to
1485        // debug where it's coming from, and when the problem occurs.
1486        bool avoidOverdraw = !mCaches.debugOverdraw;
1487        DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
1488        DeferStateStruct deferStruct(deferredList, *this, replayFlags);
1489        renderNode->defer(deferStruct, 0);
1490
1491        flushLayers();
1492        startFrame();
1493
1494        deferredList.flush(*this, dirty);
1495    } else {
1496        // Even if there is no drawing command(Ex: invisible),
1497        // it still needs startFrame to clear buffer and start tiling.
1498        startFrame();
1499    }
1500}
1501
1502/**
1503 * Important note: this method is intended to draw batches of bitmaps and
1504 * will not set the scissor enable or dirty the current layer, if any.
1505 * The caller is responsible for properly dirtying the current layer.
1506 */
1507void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
1508        int bitmapCount, TextureVertex* vertices, bool pureTranslate,
1509        const Rect& bounds, const SkPaint* paint) {
1510    Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
1511    if (!texture) return;
1512
1513    const AutoTexture autoCleanup(texture);
1514
1515    // TODO: remove layer dirty in multi-draw callers
1516    // TODO: snap doesn't need to touch transform, only texture filter.
1517    bool snap = pureTranslate;
1518    const float x = floorf(bounds.left + 0.5f);
1519    const float y = floorf(bounds.top + 0.5f);
1520    int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
1521            ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
1522    Glop glop;
1523    GlopBuilder(mRenderState, mCaches, &glop)
1524            .setMeshTexturedMesh(vertices, bitmapCount * 6)
1525            .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1526            .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
1527            .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(0, 0, bounds.getWidth(), bounds.getHeight()))
1528            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1529            .build();
1530    renderGlop(glop);
1531}
1532
1533void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
1534    if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
1535        return;
1536    }
1537
1538    mCaches.textureState().activateTexture(0);
1539    Texture* texture = getTexture(bitmap);
1540    if (!texture) return;
1541    const AutoTexture autoCleanup(texture);
1542
1543    int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
1544            ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
1545    Glop glop;
1546    GlopBuilder(mRenderState, mCaches, &glop)
1547            .setMeshTexturedUnitQuad(texture->uvMapper)
1548            .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1549            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
1550            .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
1551            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1552            .build();
1553    renderGlop(glop);
1554}
1555
1556void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
1557        const float* vertices, const int* colors, const SkPaint* paint) {
1558    if (!vertices || mState.currentlyIgnored()) {
1559        return;
1560    }
1561
1562    float left = FLT_MAX;
1563    float top = FLT_MAX;
1564    float right = FLT_MIN;
1565    float bottom = FLT_MIN;
1566
1567    const uint32_t elementCount = meshWidth * meshHeight * 6;
1568
1569    std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
1570    ColorTextureVertex* vertex = &mesh[0];
1571
1572    std::unique_ptr<int[]> tempColors;
1573    if (!colors) {
1574        uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1);
1575        tempColors.reset(new int[colorsCount]);
1576        memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
1577        colors = tempColors.get();
1578    }
1579
1580    Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
1581    const UvMapper& mapper(getMapper(texture));
1582
1583    for (int32_t y = 0; y < meshHeight; y++) {
1584        for (int32_t x = 0; x < meshWidth; x++) {
1585            uint32_t i = (y * (meshWidth + 1) + x) * 2;
1586
1587            float u1 = float(x) / meshWidth;
1588            float u2 = float(x + 1) / meshWidth;
1589            float v1 = float(y) / meshHeight;
1590            float v2 = float(y + 1) / meshHeight;
1591
1592            mapper.map(u1, v1, u2, v2);
1593
1594            int ax = i + (meshWidth + 1) * 2;
1595            int ay = ax + 1;
1596            int bx = i;
1597            int by = bx + 1;
1598            int cx = i + 2;
1599            int cy = cx + 1;
1600            int dx = i + (meshWidth + 1) * 2 + 2;
1601            int dy = dx + 1;
1602
1603            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
1604            ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
1605            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
1606
1607            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
1608            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
1609            ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
1610
1611            left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx])));
1612            top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy])));
1613            right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx])));
1614            bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy])));
1615        }
1616    }
1617
1618    if (quickRejectSetupScissor(left, top, right, bottom)) {
1619        return;
1620    }
1621
1622    if (!texture) {
1623        texture = mCaches.textureCache.get(bitmap);
1624        if (!texture) {
1625            return;
1626        }
1627    }
1628    const AutoTexture autoCleanup(texture);
1629
1630    /*
1631     * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
1632     * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
1633     */
1634    Glop glop;
1635    GlopBuilder(mRenderState, mCaches, &glop)
1636            .setMeshColoredTexturedMesh(mesh.get(), elementCount)
1637            .setFillTexturePaint(*texture, static_cast<int>(TextureFillFlags::kNone), paint, currentSnapshot()->alpha)
1638            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
1639            .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
1640            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1641            .build();
1642    renderGlop(glop);
1643}
1644
1645void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, const SkPaint* paint) {
1646    if (quickRejectSetupScissor(dst)) {
1647        return;
1648    }
1649
1650    Texture* texture = getTexture(bitmap);
1651    if (!texture) return;
1652    const AutoTexture autoCleanup(texture);
1653
1654    Rect uv(fmax(0.0f, src.left / texture->width),
1655            fmax(0.0f, src.top / texture->height),
1656            fmin(1.0f, src.right / texture->width),
1657            fmin(1.0f, src.bottom / texture->height));
1658
1659    int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
1660            ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
1661    Glop glop;
1662    GlopBuilder(mRenderState, mCaches, &glop)
1663            .setMeshTexturedUvQuad(texture->uvMapper, uv)
1664            .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1665            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
1666            .setModelViewMapUnitToRectSnap(dst)
1667            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1668            .build();
1669    renderGlop(glop);
1670}
1671
1672void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
1673        AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
1674        const SkPaint* paint) {
1675    if (!mesh || !mesh->verticesCount || quickRejectSetupScissor(left, top, right, bottom)) {
1676        return;
1677    }
1678
1679    Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
1680    if (!texture) return;
1681
1682    // 9 patches are built for stretching - always filter
1683    int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter);
1684    if (bitmap->colorType() == kAlpha_8_SkColorType) {
1685        textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture;
1686    }
1687    Glop glop;
1688    GlopBuilder(mRenderState, mCaches, &glop)
1689            .setMeshPatchQuads(*mesh)
1690            .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1691            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
1692            .setModelViewOffsetRectSnap(left, top, Rect(0, 0, right - left, bottom - top)) // TODO: get minimal bounds from patch
1693            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1694            .build();
1695    renderGlop(glop);
1696}
1697
1698/**
1699 * Important note: this method is intended to draw batches of 9-patch objects and
1700 * will not set the scissor enable or dirty the current layer, if any.
1701 * The caller is responsible for properly dirtying the current layer.
1702 */
1703void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
1704        TextureVertex* vertices, uint32_t elementCount, const SkPaint* paint) {
1705    mCaches.textureState().activateTexture(0);
1706    Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
1707    if (!texture) return;
1708    const AutoTexture autoCleanup(texture);
1709
1710    // TODO: get correct bounds from caller
1711    // 9 patches are built for stretching - always filter
1712    int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter);
1713    if (bitmap->colorType() == kAlpha_8_SkColorType) {
1714        textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture;
1715    }
1716    Glop glop;
1717    GlopBuilder(mRenderState, mCaches, &glop)
1718            .setMeshTexturedIndexedQuads(vertices, elementCount)
1719            .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
1720            .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
1721            .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
1722            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1723            .build();
1724    renderGlop(glop);
1725}
1726
1727void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
1728        const VertexBuffer& vertexBuffer, const SkPaint* paint, int displayFlags) {
1729    // not missing call to quickReject/dirtyLayer, always done at a higher level
1730    if (!vertexBuffer.getVertexCount()) {
1731        // no vertices to draw
1732        return;
1733    }
1734
1735    bool fudgeOffset = displayFlags & kVertexBuffer_Offset;
1736    bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp;
1737    Glop glop;
1738    GlopBuilder(mRenderState, mCaches, &glop)
1739            .setMeshVertexBuffer(vertexBuffer, shadowInterp)
1740            .setFillPaint(*paint, currentSnapshot()->alpha)
1741            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), fudgeOffset)
1742            .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
1743            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
1744            .build();
1745    renderGlop(glop);
1746}
1747
1748/**
1749 * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
1750 * that of AA lines in the drawLines() function.  We expand the convex path by a half pixel in
1751 * screen space in all directions. However, instead of using a fragment shader to compute the
1752 * translucency of the color from its position, we simply use a varying parameter to define how far
1753 * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
1754 *
1755 * Doesn't yet support joins, caps, or path effects.
1756 */
1757void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
1758    VertexBuffer vertexBuffer;
1759    // TODO: try clipping large paths to viewport
1760    PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
1761    drawVertexBuffer(vertexBuffer, paint);
1762}
1763
1764/**
1765 * We create tristrips for the lines much like shape stroke tessellation, using a per-vertex alpha
1766 * and additional geometry for defining an alpha slope perimeter.
1767 *
1768 * Using GL_LINES can be difficult because the rasterization rules for those lines produces some
1769 * unexpected results, and may vary between hardware devices. Previously we used a varying-base
1770 * in-shader alpha region, but found it to be taxing on some GPUs.
1771 *
1772 * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
1773 * memory transfer by removing need for degenerate vertices.
1774 */
1775void OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
1776    if (mState.currentlyIgnored() || count < 4) return;
1777
1778    count &= ~0x3; // round down to nearest four
1779
1780    VertexBuffer buffer;
1781    PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer);
1782    const Rect& bounds = buffer.getBounds();
1783
1784    if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
1785        return;
1786    }
1787
1788    int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
1789    drawVertexBuffer(buffer, paint, displayFlags);
1790}
1791
1792void OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
1793    if (mState.currentlyIgnored() || count < 2) return;
1794
1795    count &= ~0x1; // round down to nearest two
1796
1797    VertexBuffer buffer;
1798    PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer);
1799
1800    const Rect& bounds = buffer.getBounds();
1801    if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
1802        return;
1803    }
1804
1805    int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
1806    drawVertexBuffer(buffer, paint, displayFlags);
1807
1808    mDirty = true;
1809}
1810
1811void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
1812    // No need to check against the clip, we fill the clip region
1813    if (mState.currentlyIgnored()) return;
1814
1815    Rect clip(mState.currentClipRect());
1816    clip.snapToPixelBoundaries();
1817
1818    SkPaint paint;
1819    paint.setColor(color);
1820    paint.setXfermodeMode(mode);
1821
1822    drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true);
1823
1824    mDirty = true;
1825}
1826
1827void OpenGLRenderer::drawShape(float left, float top, PathTexture* texture,
1828        const SkPaint* paint) {
1829    if (!texture) return;
1830    const AutoTexture autoCleanup(texture);
1831
1832    const float x = left + texture->left - texture->offset;
1833    const float y = top + texture->top - texture->offset;
1834
1835    drawPathTexture(texture, x, y, paint);
1836
1837    mDirty = true;
1838}
1839
1840void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
1841        float rx, float ry, const SkPaint* p) {
1842    if (mState.currentlyIgnored()
1843            || quickRejectSetupScissor(left, top, right, bottom, p)
1844            || PaintUtils::paintWillNotDraw(*p)) {
1845        return;
1846    }
1847
1848    if (p->getPathEffect() != nullptr) {
1849        mCaches.textureState().activateTexture(0);
1850        PathTexture* texture = mCaches.pathCache.getRoundRect(
1851                right - left, bottom - top, rx, ry, p);
1852        drawShape(left, top, texture, p);
1853    } else {
1854        const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
1855                *currentTransform(), *p, right - left, bottom - top, rx, ry);
1856        drawVertexBuffer(left, top, *vertexBuffer, p);
1857    }
1858}
1859
1860void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
1861    if (mState.currentlyIgnored()
1862            || quickRejectSetupScissor(x - radius, y - radius, x + radius, y + radius, p)
1863            || PaintUtils::paintWillNotDraw(*p)) {
1864        return;
1865    }
1866    if (p->getPathEffect() != nullptr) {
1867        mCaches.textureState().activateTexture(0);
1868        PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
1869        drawShape(x - radius, y - radius, texture, p);
1870    } else {
1871        SkPath path;
1872        if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
1873            path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
1874        } else {
1875            path.addCircle(x, y, radius);
1876        }
1877        drawConvexPath(path, p);
1878    }
1879}
1880
1881void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
1882        const SkPaint* p) {
1883    if (mState.currentlyIgnored()
1884            || quickRejectSetupScissor(left, top, right, bottom, p)
1885            || PaintUtils::paintWillNotDraw(*p)) {
1886        return;
1887    }
1888
1889    if (p->getPathEffect() != nullptr) {
1890        mCaches.textureState().activateTexture(0);
1891        PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
1892        drawShape(left, top, texture, p);
1893    } else {
1894        SkPath path;
1895        SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
1896        if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
1897            rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
1898        }
1899        path.addOval(rect);
1900        drawConvexPath(path, p);
1901    }
1902}
1903
1904void OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
1905        float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) {
1906    if (mState.currentlyIgnored()
1907            || quickRejectSetupScissor(left, top, right, bottom, p)
1908            || PaintUtils::paintWillNotDraw(*p)) {
1909        return;
1910    }
1911
1912    // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
1913    if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) {
1914        mCaches.textureState().activateTexture(0);
1915        PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
1916                startAngle, sweepAngle, useCenter, p);
1917        drawShape(left, top, texture, p);
1918        return;
1919    }
1920    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
1921    if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
1922        rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
1923    }
1924
1925    SkPath path;
1926    if (useCenter) {
1927        path.moveTo(rect.centerX(), rect.centerY());
1928    }
1929    path.arcTo(rect, startAngle, sweepAngle, !useCenter);
1930    if (useCenter) {
1931        path.close();
1932    }
1933    drawConvexPath(path, p);
1934}
1935
1936// See SkPaintDefaults.h
1937#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
1938
1939void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
1940        const SkPaint* p) {
1941    if (mState.currentlyIgnored()
1942            || quickRejectSetupScissor(left, top, right, bottom, p)
1943            || PaintUtils::paintWillNotDraw(*p)) {
1944        return;
1945    }
1946
1947    if (p->getStyle() != SkPaint::kFill_Style) {
1948        // only fill style is supported by drawConvexPath, since others have to handle joins
1949        if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
1950                p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
1951            mCaches.textureState().activateTexture(0);
1952            PathTexture* texture =
1953                    mCaches.pathCache.getRect(right - left, bottom - top, p);
1954            drawShape(left, top, texture, p);
1955        } else {
1956            SkPath path;
1957            SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
1958            if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
1959                rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
1960            }
1961            path.addRect(rect);
1962            drawConvexPath(path, p);
1963        }
1964    } else {
1965        if (p->isAntiAlias() && !currentTransform()->isSimple()) {
1966            SkPath path;
1967            path.addRect(left, top, right, bottom);
1968            drawConvexPath(path, p);
1969        } else {
1970            drawColorRect(left, top, right, bottom, p);
1971
1972            mDirty = true;
1973        }
1974    }
1975}
1976
1977void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
1978        int bytesCount, int count, const float* positions,
1979        FontRenderer& fontRenderer, int alpha, float x, float y) {
1980    mCaches.textureState().activateTexture(0);
1981
1982    TextShadow textShadow;
1983    if (!getTextShadow(paint, &textShadow)) {
1984        LOG_ALWAYS_FATAL("failed to query shadow attributes");
1985    }
1986
1987    // NOTE: The drop shadow will not perform gamma correction
1988    //       if shader-based correction is enabled
1989    mCaches.dropShadowCache.setFontRenderer(fontRenderer);
1990    ShadowTexture* texture = mCaches.dropShadowCache.get(
1991            paint, text, bytesCount, count, textShadow.radius, positions);
1992    // If the drop shadow exceeds the max texture size or couldn't be
1993    // allocated, skip drawing
1994    if (!texture) return;
1995    const AutoTexture autoCleanup(texture);
1996
1997    const float sx = x - texture->left + textShadow.dx;
1998    const float sy = y - texture->top + textShadow.dy;
1999
2000    Glop glop;
2001    GlopBuilder(mRenderState, mCaches, &glop)
2002            .setMeshTexturedUnitQuad(nullptr)
2003            .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
2004            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
2005            .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
2006            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2007            .build();
2008    renderGlop(glop);
2009}
2010
2011bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
2012    float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
2013    return MathUtils::isZero(alpha)
2014            && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
2015}
2016
2017void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
2018        const float* positions, const SkPaint* paint) {
2019    if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
2020        return;
2021    }
2022
2023    // NOTE: Skia does not support perspective transform on drawPosText yet
2024    if (!currentTransform()->isSimple()) {
2025        return;
2026    }
2027
2028    mRenderState.scissor().setEnabled(true);
2029
2030    float x = 0.0f;
2031    float y = 0.0f;
2032    const bool pureTranslate = currentTransform()->isPureTranslate();
2033    if (pureTranslate) {
2034        x = floorf(x + currentTransform()->getTranslateX() + 0.5f);
2035        y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
2036    }
2037
2038    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2039    fontRenderer.setFont(paint, SkMatrix::I());
2040
2041    int alpha;
2042    SkXfermode::Mode mode;
2043    getAlphaAndMode(paint, &alpha, &mode);
2044
2045    if (CC_UNLIKELY(hasTextShadow(paint))) {
2046        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
2047                alpha, 0.0f, 0.0f);
2048    }
2049
2050    // Pick the appropriate texture filtering
2051    bool linearFilter = currentTransform()->changesBounds();
2052    if (pureTranslate && !linearFilter) {
2053        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
2054    }
2055    fontRenderer.setTextureFiltering(linearFilter);
2056
2057    const Rect& clip(pureTranslate ? writableSnapshot()->getClipRect() : writableSnapshot()->getLocalClip());
2058    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2059
2060    TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
2061    if (fontRenderer.renderPosText(paint, &clip, text, 0, bytesCount, count, x, y,
2062            positions, hasLayer() ? &bounds : nullptr, &functor)) {
2063        dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
2064        mDirty = true;
2065    }
2066
2067}
2068
2069bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const {
2070    if (CC_LIKELY(transform.isPureTranslate())) {
2071        outMatrix->setIdentity();
2072        return false;
2073    } else if (CC_UNLIKELY(transform.isPerspective())) {
2074        outMatrix->setIdentity();
2075        return true;
2076    }
2077
2078    /**
2079     * Input is a non-perspective, scaling transform. Generate a scale-only transform,
2080     * with values rounded to the nearest int.
2081     */
2082    float sx, sy;
2083    transform.decomposeScale(sx, sy);
2084    outMatrix->setScale(
2085            roundf(fmaxf(1.0f, sx)),
2086            roundf(fmaxf(1.0f, sy)));
2087    return true;
2088}
2089
2090int OpenGLRenderer::getSaveCount() const {
2091    return mState.getSaveCount();
2092}
2093
2094int OpenGLRenderer::save(int flags) {
2095    return mState.save(flags);
2096}
2097
2098void OpenGLRenderer::restore() {
2099    mState.restore();
2100}
2101
2102void OpenGLRenderer::restoreToCount(int saveCount) {
2103    mState.restoreToCount(saveCount);
2104}
2105
2106void OpenGLRenderer::translate(float dx, float dy, float dz) {
2107    mState.translate(dx, dy, dz);
2108}
2109
2110void OpenGLRenderer::rotate(float degrees) {
2111    mState.rotate(degrees);
2112}
2113
2114void OpenGLRenderer::scale(float sx, float sy) {
2115    mState.scale(sx, sy);
2116}
2117
2118void OpenGLRenderer::skew(float sx, float sy) {
2119    mState.skew(sx, sy);
2120}
2121
2122void OpenGLRenderer::setMatrix(const Matrix4& matrix) {
2123    mState.setMatrix(matrix);
2124}
2125
2126void OpenGLRenderer::concatMatrix(const Matrix4& matrix) {
2127    mState.concatMatrix(matrix);
2128}
2129
2130bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
2131    return mState.clipRect(left, top, right, bottom, op);
2132}
2133
2134bool OpenGLRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
2135    return mState.clipPath(path, op);
2136}
2137
2138bool OpenGLRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
2139    return mState.clipRegion(region, op);
2140}
2141
2142void OpenGLRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
2143    mState.setClippingOutline(allocator, outline);
2144}
2145
2146void OpenGLRenderer::setClippingRoundRect(LinearAllocator& allocator,
2147        const Rect& rect, float radius, bool highPriority) {
2148    mState.setClippingRoundRect(allocator, rect, radius, highPriority);
2149}
2150
2151void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
2152        const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
2153        DrawOpMode drawOpMode) {
2154
2155    if (drawOpMode == DrawOpMode::kImmediate) {
2156        // The checks for corner-case ignorable text and quick rejection is only done for immediate
2157        // drawing as ops from DeferredDisplayList are already filtered for these
2158        if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
2159                quickRejectSetupScissor(bounds)) {
2160            return;
2161        }
2162    }
2163
2164    const float oldX = x;
2165    const float oldY = y;
2166
2167    const mat4& transform = *currentTransform();
2168    const bool pureTranslate = transform.isPureTranslate();
2169
2170    if (CC_LIKELY(pureTranslate)) {
2171        x = floorf(x + transform.getTranslateX() + 0.5f);
2172        y = floorf(y + transform.getTranslateY() + 0.5f);
2173    }
2174
2175    int alpha;
2176    SkXfermode::Mode mode;
2177    getAlphaAndMode(paint, &alpha, &mode);
2178
2179    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2180
2181    if (CC_UNLIKELY(hasTextShadow(paint))) {
2182        fontRenderer.setFont(paint, SkMatrix::I());
2183        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
2184                alpha, oldX, oldY);
2185    }
2186
2187    const bool hasActiveLayer = hasLayer();
2188
2189    // We only pass a partial transform to the font renderer. That partial
2190    // matrix defines how glyphs are rasterized. Typically we want glyphs
2191    // to be rasterized at their final size on screen, which means the partial
2192    // matrix needs to take the scale factor into account.
2193    // When a partial matrix is used to transform glyphs during rasterization,
2194    // the mesh is generated with the inverse transform (in the case of scale,
2195    // the mesh is generated at 1.0 / scale for instance.) This allows us to
2196    // apply the full transform matrix at draw time in the vertex shader.
2197    // Applying the full matrix in the shader is the easiest way to handle
2198    // rotation and perspective and allows us to always generated quads in the
2199    // font renderer which greatly simplifies the code, clipping in particular.
2200    SkMatrix fontTransform;
2201    bool linearFilter = findBestFontTransform(transform, &fontTransform)
2202            || fabs(y - (int) y) > 0.0f
2203            || fabs(x - (int) x) > 0.0f;
2204    fontRenderer.setFont(paint, fontTransform);
2205    fontRenderer.setTextureFiltering(linearFilter);
2206
2207    // TODO: Implement better clipping for scaled/rotated text
2208    const Rect* clip = !pureTranslate ? nullptr : &mState.currentClipRect();
2209    Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2210
2211    bool status;
2212    TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
2213
2214    // don't call issuedrawcommand, do it at end of batch
2215    bool forceFinish = (drawOpMode != DrawOpMode::kDefer);
2216    if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
2217        SkPaint paintCopy(*paint);
2218        paintCopy.setTextAlign(SkPaint::kLeft_Align);
2219        status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
2220                positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
2221    } else {
2222        status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
2223                positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
2224    }
2225
2226    if ((status || drawOpMode != DrawOpMode::kImmediate) && hasActiveLayer) {
2227        if (!pureTranslate) {
2228            transform.mapRect(layerBounds);
2229        }
2230        dirtyLayerUnchecked(layerBounds, getRegion());
2231    }
2232
2233    drawTextDecorations(totalAdvance, oldX, oldY, paint);
2234
2235    mDirty = true;
2236}
2237
2238void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
2239        const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
2240    if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
2241        return;
2242    }
2243
2244    // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
2245    mRenderState.scissor().setEnabled(true);
2246
2247    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2248    fontRenderer.setFont(paint, SkMatrix::I());
2249    fontRenderer.setTextureFiltering(true);
2250
2251    int alpha;
2252    SkXfermode::Mode mode;
2253    getAlphaAndMode(paint, &alpha, &mode);
2254    TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
2255
2256    const Rect* clip = &writableSnapshot()->getLocalClip();
2257    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2258
2259    if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
2260            hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
2261        dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
2262        mDirty = true;
2263    }
2264}
2265
2266void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
2267    if (mState.currentlyIgnored()) return;
2268
2269    mCaches.textureState().activateTexture(0);
2270
2271    PathTexture* texture = mCaches.pathCache.get(path, paint);
2272    if (!texture) return;
2273    const AutoTexture autoCleanup(texture);
2274
2275    const float x = texture->left - texture->offset;
2276    const float y = texture->top - texture->offset;
2277
2278    drawPathTexture(texture, x, y, paint);
2279    mDirty = true;
2280}
2281
2282void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
2283    if (!layer) {
2284        return;
2285    }
2286
2287    mat4* transform = nullptr;
2288    if (layer->isTextureLayer()) {
2289        transform = &layer->getTransform();
2290        if (!transform->isIdentity()) {
2291            save(SkCanvas::kMatrix_SaveFlag);
2292            concatMatrix(*transform);
2293        }
2294    }
2295
2296    bool clipRequired = false;
2297    const bool rejected = mState.calculateQuickRejectForScissor(
2298            x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
2299            &clipRequired, nullptr, false);
2300
2301    if (rejected) {
2302        if (transform && !transform->isIdentity()) {
2303            restore();
2304        }
2305        return;
2306    }
2307
2308    EVENT_LOGD("drawLayer," RECT_STRING ", clipRequired %d", x, y,
2309            x + layer->layer.getWidth(), y + layer->layer.getHeight(), clipRequired);
2310
2311    updateLayer(layer, true);
2312
2313    mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
2314    mCaches.textureState().activateTexture(0);
2315
2316    if (CC_LIKELY(!layer->region.isEmpty())) {
2317        if (layer->region.isRect()) {
2318            DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
2319                    composeLayerRect(layer, layer->regionRect));
2320        } else if (layer->mesh) {
2321            Glop glop;
2322            GlopBuilder(mRenderState, mCaches, &glop)
2323                    .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
2324                    .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
2325                    .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
2326                    .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
2327                    .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2328                    .build();
2329            DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
2330#if DEBUG_LAYERS_AS_REGIONS
2331            drawRegionRectsDebug(layer->region);
2332#endif
2333        }
2334
2335        if (layer->debugDrawUpdate) {
2336            layer->debugDrawUpdate = false;
2337
2338            SkPaint paint;
2339            paint.setColor(0x7f00ff00);
2340            drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), &paint);
2341        }
2342    }
2343    layer->hasDrawnSinceUpdate = true;
2344
2345    if (transform && !transform->isIdentity()) {
2346        restore();
2347    }
2348
2349    mDirty = true;
2350}
2351
2352///////////////////////////////////////////////////////////////////////////////
2353// Draw filters
2354///////////////////////////////////////////////////////////////////////////////
2355void OpenGLRenderer::setDrawFilter(SkDrawFilter* filter) {
2356    // We should never get here since we apply the draw filter when stashing
2357    // the paints in the DisplayList.
2358    LOG_ALWAYS_FATAL("OpenGLRenderer does not directly support DrawFilters");
2359}
2360
2361///////////////////////////////////////////////////////////////////////////////
2362// Drawing implementation
2363///////////////////////////////////////////////////////////////////////////////
2364
2365Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) {
2366    Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
2367    if (!texture) {
2368        return mCaches.textureCache.get(bitmap);
2369    }
2370    return texture;
2371}
2372
2373void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y,
2374        const SkPaint* paint) {
2375    if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) {
2376        return;
2377    }
2378
2379    Glop glop;
2380    GlopBuilder(mRenderState, mCaches, &glop)
2381            .setMeshTexturedUnitQuad(nullptr)
2382            .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
2383            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
2384            .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height))
2385            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2386            .build();
2387    renderGlop(glop);
2388}
2389
2390// Same values used by Skia
2391#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
2392#define kStdUnderline_Offset    (1.0f / 9.0f)
2393#define kStdUnderline_Thickness (1.0f / 18.0f)
2394
2395void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y,
2396        const SkPaint* paint) {
2397    // Handle underline and strike-through
2398    uint32_t flags = paint->getFlags();
2399    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
2400        SkPaint paintCopy(*paint);
2401
2402        if (CC_LIKELY(underlineWidth > 0.0f)) {
2403            const float textSize = paintCopy.getTextSize();
2404            const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
2405
2406            const float left = x;
2407            float top = 0.0f;
2408
2409            int linesCount = 0;
2410            if (flags & SkPaint::kUnderlineText_Flag) linesCount++;
2411            if (flags & SkPaint::kStrikeThruText_Flag) linesCount++;
2412
2413            const int pointsCount = 4 * linesCount;
2414            float points[pointsCount];
2415            int currentPoint = 0;
2416
2417            if (flags & SkPaint::kUnderlineText_Flag) {
2418                top = y + textSize * kStdUnderline_Offset;
2419                points[currentPoint++] = left;
2420                points[currentPoint++] = top;
2421                points[currentPoint++] = left + underlineWidth;
2422                points[currentPoint++] = top;
2423            }
2424
2425            if (flags & SkPaint::kStrikeThruText_Flag) {
2426                top = y + textSize * kStdStrikeThru_Offset;
2427                points[currentPoint++] = left;
2428                points[currentPoint++] = top;
2429                points[currentPoint++] = left + underlineWidth;
2430                points[currentPoint++] = top;
2431            }
2432
2433            paintCopy.setStrokeWidth(strokeWidth);
2434
2435            drawLines(&points[0], pointsCount, &paintCopy);
2436        }
2437    }
2438}
2439
2440void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
2441    if (mState.currentlyIgnored()) {
2442        return;
2443    }
2444
2445    drawColorRects(rects, count, paint, false, true, true);
2446}
2447
2448void OpenGLRenderer::drawShadow(float casterAlpha,
2449        const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
2450    if (mState.currentlyIgnored()) return;
2451
2452    // TODO: use quickRejectWithScissor. For now, always force enable scissor.
2453    mRenderState.scissor().setEnabled(true);
2454
2455    SkPaint paint;
2456    paint.setAntiAlias(true); // want to use AlphaVertex
2457
2458    // The caller has made sure casterAlpha > 0.
2459    float ambientShadowAlpha = mAmbientShadowAlpha;
2460    if (CC_UNLIKELY(mCaches.propertyAmbientShadowStrength >= 0)) {
2461        ambientShadowAlpha = mCaches.propertyAmbientShadowStrength;
2462    }
2463    if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
2464        paint.setARGB(casterAlpha * ambientShadowAlpha, 0, 0, 0);
2465        drawVertexBuffer(*ambientShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
2466    }
2467
2468    float spotShadowAlpha = mSpotShadowAlpha;
2469    if (CC_UNLIKELY(mCaches.propertySpotShadowStrength >= 0)) {
2470        spotShadowAlpha = mCaches.propertySpotShadowStrength;
2471    }
2472    if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
2473        paint.setARGB(casterAlpha * spotShadowAlpha, 0, 0, 0);
2474        drawVertexBuffer(*spotShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
2475    }
2476
2477    mDirty=true;
2478}
2479
2480void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint,
2481        bool ignoreTransform, bool dirty, bool clip) {
2482    if (count == 0) {
2483        return;
2484    }
2485
2486    float left = FLT_MAX;
2487    float top = FLT_MAX;
2488    float right = FLT_MIN;
2489    float bottom = FLT_MIN;
2490
2491    Vertex mesh[count];
2492    Vertex* vertex = mesh;
2493
2494    for (int index = 0; index < count; index += 4) {
2495        float l = rects[index + 0];
2496        float t = rects[index + 1];
2497        float r = rects[index + 2];
2498        float b = rects[index + 3];
2499
2500        Vertex::set(vertex++, l, t);
2501        Vertex::set(vertex++, r, t);
2502        Vertex::set(vertex++, l, b);
2503        Vertex::set(vertex++, r, b);
2504
2505        left = fminf(left, l);
2506        top = fminf(top, t);
2507        right = fmaxf(right, r);
2508        bottom = fmaxf(bottom, b);
2509    }
2510
2511    if (clip && quickRejectSetupScissor(left, top, right, bottom)) {
2512        return;
2513    }
2514
2515    const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
2516    Glop glop;
2517    GlopBuilder(mRenderState, mCaches, &glop)
2518            .setMeshIndexedQuads(&mesh[0], count / 4)
2519            .setFillPaint(*paint, currentSnapshot()->alpha)
2520            .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
2521            .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
2522            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2523            .build();
2524    renderGlop(glop);
2525}
2526
2527void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
2528        const SkPaint* paint, bool ignoreTransform) {
2529    const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
2530    Glop glop;
2531    GlopBuilder(mRenderState, mCaches, &glop)
2532            .setMeshUnitQuad()
2533            .setFillPaint(*paint, currentSnapshot()->alpha)
2534            .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
2535            .setModelViewMapUnitToRect(Rect(left, top, right, bottom))
2536            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
2537            .build();
2538    renderGlop(glop);
2539}
2540
2541void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
2542        SkXfermode::Mode* mode) const {
2543    getAlphaAndModeDirect(paint, alpha,  mode);
2544    if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
2545        // if drawing a layer, ignore the paint's alpha
2546        *alpha = mDrawModifiers.mOverrideLayerAlpha * 255;
2547    }
2548    *alpha *= currentSnapshot()->alpha;
2549}
2550
2551float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
2552    float alpha;
2553    if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
2554        alpha = mDrawModifiers.mOverrideLayerAlpha;
2555    } else {
2556        alpha = layer->getAlpha() / 255.0f;
2557    }
2558    return alpha * currentSnapshot()->alpha;
2559}
2560
2561}; // namespace uirenderer
2562}; // namespace android
2563