DisplayListCanvas.h revision 2af4635e4a9e448a65ff541252f8f94bc6ac48e0
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
18#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
19
20#include <SkChunkAlloc.h>
21#include <SkFlattenable.h>
22#include <SkMatrix.h>
23#include <SkCamera.h>
24#include <SkPaint.h>
25#include <SkPath.h>
26#include <SkRefCnt.h>
27#include <SkTDArray.h>
28#include <SkTSearch.h>
29
30#include <cutils/compiler.h>
31
32#include "DisplayListLogBuffer.h"
33#include "OpenGLRenderer.h"
34#include "utils/LinearAllocator.h"
35
36namespace android {
37namespace uirenderer {
38
39///////////////////////////////////////////////////////////////////////////////
40// Defines
41///////////////////////////////////////////////////////////////////////////////
42
43#define MIN_WRITER_SIZE 4096
44#define OP_MAY_BE_SKIPPED_MASK 0xff000000
45
46// Debug
47#if DEBUG_DISPLAY_LIST
48    #define DISPLAY_LIST_LOGD(...) ALOGD(__VA_ARGS__)
49#else
50    #define DISPLAY_LIST_LOGD(...)
51#endif
52
53#define TRANSLATION 0x0001
54#define ROTATION    0x0002
55#define ROTATION_3D 0x0004
56#define SCALE       0x0008
57#define PIVOT       0x0010
58
59///////////////////////////////////////////////////////////////////////////////
60// Display list
61///////////////////////////////////////////////////////////////////////////////
62
63class DisplayListRenderer;
64class DisplayListOp;
65class DrawOp;
66class StateOp;
67
68/**
69 * Refcounted structure that holds data used in display list stream
70 */
71class DisplayListData: public LightRefBase<DisplayListData> {
72public:
73    LinearAllocator allocator;
74    Vector<DisplayListOp*> displayListOps;
75};
76
77/**
78 * Replays recorded drawing commands.
79 */
80class DisplayList {
81public:
82    DisplayList(const DisplayListRenderer& recorder);
83    ANDROID_API ~DisplayList();
84
85    // See flags defined in DisplayList.java
86    enum ReplayFlag {
87        kReplayFlag_ClipChildren = 0x1
88    };
89
90    void setViewProperties(OpenGLRenderer& renderer, uint32_t level);
91    void outputViewProperties(uint32_t level);
92
93    ANDROID_API size_t getSize();
94    ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList);
95    ANDROID_API static void outputLogBuffer(int fd);
96
97    void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
98
99    status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0);
100
101    void output(uint32_t level = 0);
102
103    ANDROID_API void reset();
104
105    void setRenderable(bool renderable) {
106        mIsRenderable = renderable;
107    }
108
109    bool isRenderable() const {
110        return mIsRenderable;
111    }
112
113    void setName(const char* name) {
114        if (name) {
115            mName.setTo(name);
116        }
117    }
118
119    void setClipChildren(bool clipChildren) {
120        mClipChildren = clipChildren;
121    }
122
123    void setStaticMatrix(SkMatrix* matrix) {
124        delete mStaticMatrix;
125        mStaticMatrix = new SkMatrix(*matrix);
126    }
127
128    void setAnimationMatrix(SkMatrix* matrix) {
129        delete mAnimationMatrix;
130        if (matrix) {
131            mAnimationMatrix = new SkMatrix(*matrix);
132        } else {
133            mAnimationMatrix = NULL;
134        }
135    }
136
137    void setAlpha(float alpha) {
138        alpha = fminf(1.0f, fmaxf(0.0f, alpha));
139        if (alpha != mAlpha) {
140            mAlpha = alpha;
141            mMultipliedAlpha = (int) (255 * alpha);
142        }
143    }
144
145    void setHasOverlappingRendering(bool hasOverlappingRendering) {
146        mHasOverlappingRendering = hasOverlappingRendering;
147    }
148
149    void setTranslationX(float translationX) {
150        if (translationX != mTranslationX) {
151            mTranslationX = translationX;
152            mMatrixDirty = true;
153            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
154                mMatrixFlags &= ~TRANSLATION;
155            } else {
156                mMatrixFlags |= TRANSLATION;
157            }
158        }
159    }
160
161    void setTranslationY(float translationY) {
162        if (translationY != mTranslationY) {
163            mTranslationY = translationY;
164            mMatrixDirty = true;
165            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
166                mMatrixFlags &= ~TRANSLATION;
167            } else {
168                mMatrixFlags |= TRANSLATION;
169            }
170        }
171    }
172
173    void setRotation(float rotation) {
174        if (rotation != mRotation) {
175            mRotation = rotation;
176            mMatrixDirty = true;
177            if (mRotation == 0.0f) {
178                mMatrixFlags &= ~ROTATION;
179            } else {
180                mMatrixFlags |= ROTATION;
181            }
182        }
183    }
184
185    void setRotationX(float rotationX) {
186        if (rotationX != mRotationX) {
187            mRotationX = rotationX;
188            mMatrixDirty = true;
189            if (mRotationX == 0.0f && mRotationY == 0.0f) {
190                mMatrixFlags &= ~ROTATION_3D;
191            } else {
192                mMatrixFlags |= ROTATION_3D;
193            }
194        }
195    }
196
197    void setRotationY(float rotationY) {
198        if (rotationY != mRotationY) {
199            mRotationY = rotationY;
200            mMatrixDirty = true;
201            if (mRotationX == 0.0f && mRotationY == 0.0f) {
202                mMatrixFlags &= ~ROTATION_3D;
203            } else {
204                mMatrixFlags |= ROTATION_3D;
205            }
206        }
207    }
208
209    void setScaleX(float scaleX) {
210        if (scaleX != mScaleX) {
211            mScaleX = scaleX;
212            mMatrixDirty = true;
213            if (mScaleX == 1.0f && mScaleY == 1.0f) {
214                mMatrixFlags &= ~SCALE;
215            } else {
216                mMatrixFlags |= SCALE;
217            }
218        }
219    }
220
221    void setScaleY(float scaleY) {
222        if (scaleY != mScaleY) {
223            mScaleY = scaleY;
224            mMatrixDirty = true;
225            if (mScaleX == 1.0f && mScaleY == 1.0f) {
226                mMatrixFlags &= ~SCALE;
227            } else {
228                mMatrixFlags |= SCALE;
229            }
230        }
231    }
232
233    void setPivotX(float pivotX) {
234        mPivotX = pivotX;
235        mMatrixDirty = true;
236        if (mPivotX == 0.0f && mPivotY == 0.0f) {
237            mMatrixFlags &= ~PIVOT;
238        } else {
239            mMatrixFlags |= PIVOT;
240        }
241        mPivotExplicitlySet = true;
242    }
243
244    void setPivotY(float pivotY) {
245        mPivotY = pivotY;
246        mMatrixDirty = true;
247        if (mPivotX == 0.0f && mPivotY == 0.0f) {
248            mMatrixFlags &= ~PIVOT;
249        } else {
250            mMatrixFlags |= PIVOT;
251        }
252        mPivotExplicitlySet = true;
253    }
254
255    void setCameraDistance(float distance) {
256        if (distance != mCameraDistance) {
257            mCameraDistance = distance;
258            mMatrixDirty = true;
259            if (!mTransformCamera) {
260                mTransformCamera = new Sk3DView();
261                mTransformMatrix3D = new SkMatrix();
262            }
263            mTransformCamera->setCameraLocation(0, 0, distance);
264        }
265    }
266
267    void setLeft(int left) {
268        if (left != mLeft) {
269            mLeft = left;
270            mWidth = mRight - mLeft;
271            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
272                mMatrixDirty = true;
273            }
274        }
275    }
276
277    void setTop(int top) {
278        if (top != mTop) {
279            mTop = top;
280            mHeight = mBottom - mTop;
281            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
282                mMatrixDirty = true;
283            }
284        }
285    }
286
287    void setRight(int right) {
288        if (right != mRight) {
289            mRight = right;
290            mWidth = mRight - mLeft;
291            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
292                mMatrixDirty = true;
293            }
294        }
295    }
296
297    void setBottom(int bottom) {
298        if (bottom != mBottom) {
299            mBottom = bottom;
300            mHeight = mBottom - mTop;
301            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
302                mMatrixDirty = true;
303            }
304        }
305    }
306
307    void setLeftTop(int left, int top) {
308        if (left != mLeft || top != mTop) {
309            mLeft = left;
310            mTop = top;
311            mWidth = mRight - mLeft;
312            mHeight = mBottom - mTop;
313            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
314                mMatrixDirty = true;
315            }
316        }
317    }
318
319    void setLeftTopRightBottom(int left, int top, int right, int bottom) {
320        if (left != mLeft || top != mTop || right != mRight || bottom != mBottom) {
321            mLeft = left;
322            mTop = top;
323            mRight = right;
324            mBottom = bottom;
325            mWidth = mRight - mLeft;
326            mHeight = mBottom - mTop;
327            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
328                mMatrixDirty = true;
329            }
330        }
331    }
332
333    void offsetLeftRight(int offset) {
334        if (offset != 0) {
335            mLeft += offset;
336            mRight += offset;
337            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
338                mMatrixDirty = true;
339            }
340        }
341    }
342
343    void offsetTopBottom(int offset) {
344        if (offset != 0) {
345            mTop += offset;
346            mBottom += offset;
347            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
348                mMatrixDirty = true;
349            }
350        }
351    }
352
353    void setCaching(bool caching) {
354        mCaching = caching;
355    }
356
357    int getWidth() {
358        return mWidth;
359    }
360
361    int getHeight() {
362        return mHeight;
363    }
364
365private:
366    void init();
367
368    void clearResources();
369
370    void updateMatrix();
371
372    class TextContainer {
373    public:
374        size_t length() const {
375            return mByteLength;
376        }
377
378        const char* text() const {
379            return (const char*) mText;
380        }
381
382        size_t mByteLength;
383        const char* mText;
384    };
385
386    Vector<SkBitmap*> mBitmapResources;
387    Vector<SkBitmap*> mOwnedBitmapResources;
388    Vector<SkiaColorFilter*> mFilterResources;
389
390    Vector<SkPaint*> mPaints;
391    Vector<SkPath*> mPaths;
392    SortedVector<SkPath*> mSourcePaths;
393    Vector<SkRegion*> mRegions;
394    Vector<SkMatrix*> mMatrices;
395    Vector<SkiaShader*> mShaders;
396    Vector<Layer*> mLayers;
397
398    sp<DisplayListData> mDisplayListData;
399
400    size_t mSize;
401
402    bool mIsRenderable;
403    uint32_t mFunctorCount;
404
405    String8 mName;
406
407    // View properties
408    bool mClipChildren;
409    float mAlpha;
410    int mMultipliedAlpha;
411    bool mHasOverlappingRendering;
412    float mTranslationX, mTranslationY;
413    float mRotation, mRotationX, mRotationY;
414    float mScaleX, mScaleY;
415    float mPivotX, mPivotY;
416    float mCameraDistance;
417    int mLeft, mTop, mRight, mBottom;
418    int mWidth, mHeight;
419    int mPrevWidth, mPrevHeight;
420    bool mPivotExplicitlySet;
421    bool mMatrixDirty;
422    bool mMatrixIsIdentity;
423    uint32_t mMatrixFlags;
424    SkMatrix* mTransformMatrix;
425    Sk3DView* mTransformCamera;
426    SkMatrix* mTransformMatrix3D;
427    SkMatrix* mStaticMatrix;
428    SkMatrix* mAnimationMatrix;
429    bool mCaching;
430};
431
432///////////////////////////////////////////////////////////////////////////////
433// Renderer
434///////////////////////////////////////////////////////////////////////////////
435
436/**
437 * Records drawing commands in a display list for latter playback.
438 */
439class DisplayListRenderer: public OpenGLRenderer {
440public:
441    ANDROID_API DisplayListRenderer();
442    virtual ~DisplayListRenderer();
443
444    ANDROID_API DisplayList* getDisplayList(DisplayList* displayList);
445
446    virtual bool isDeferred();
447
448    virtual void setViewport(int width, int height);
449    virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
450    virtual void finish();
451
452    virtual status_t callDrawGLFunction(Functor *functor, Rect& dirty);
453
454    virtual void interrupt();
455    virtual void resume();
456
457    virtual int save(int flags);
458    virtual void restore();
459    virtual void restoreToCount(int saveCount);
460
461    virtual int saveLayer(float left, float top, float right, float bottom,
462            SkPaint* p, int flags);
463    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
464                int alpha, int flags);
465
466    virtual void translate(float dx, float dy);
467    virtual void rotate(float degrees);
468    virtual void scale(float sx, float sy);
469    virtual void skew(float sx, float sy);
470
471    virtual void setMatrix(SkMatrix* matrix);
472    virtual void concatMatrix(SkMatrix* matrix);
473
474    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
475    virtual bool clipPath(SkPath* path, SkRegion::Op op);
476    virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
477
478    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
479            uint32_t level = 0);
480    virtual status_t drawLayer(Layer* layer, float x, float y, SkPaint* paint);
481    virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
482    virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
483    virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
484            float srcRight, float srcBottom, float dstLeft, float dstTop,
485            float dstRight, float dstBottom, SkPaint* paint);
486    virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
487    virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
488            float* vertices, int* colors, SkPaint* paint);
489    virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
490            const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
491            float left, float top, float right, float bottom, SkPaint* paint);
492    virtual status_t drawColor(int color, SkXfermode::Mode mode);
493    virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
494    virtual status_t drawRoundRect(float left, float top, float right, float bottom,
495            float rx, float ry, SkPaint* paint);
496    virtual status_t drawCircle(float x, float y, float radius, SkPaint* paint);
497    virtual status_t drawOval(float left, float top, float right, float bottom, SkPaint* paint);
498    virtual status_t drawArc(float left, float top, float right, float bottom,
499            float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
500    virtual status_t drawPath(SkPath* path, SkPaint* paint);
501    virtual status_t drawLines(float* points, int count, SkPaint* paint);
502    virtual status_t drawPoints(float* points, int count, SkPaint* paint);
503    virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
504            float hOffset, float vOffset, SkPaint* paint);
505    virtual status_t drawPosText(const char* text, int bytesCount, int count,
506            const float* positions, SkPaint* paint);
507    virtual status_t drawText(const char* text, int bytesCount, int count,
508            float x, float y, const float* positions, SkPaint* paint, float length);
509    virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
510
511    virtual void resetShader();
512    virtual void setupShader(SkiaShader* shader);
513
514    virtual void resetColorFilter();
515    virtual void setupColorFilter(SkiaColorFilter* filter);
516
517    virtual void resetShadow();
518    virtual void setupShadow(float radius, float dx, float dy, int color);
519
520    virtual void resetPaintFilter();
521    virtual void setupPaintFilter(int clearBits, int setBits);
522
523    ANDROID_API void reset();
524
525    sp<DisplayListData> getDisplayListData() const {
526        return mDisplayListData;
527    }
528
529    const Vector<SkBitmap*>& getBitmapResources() const {
530        return mBitmapResources;
531    }
532
533    const Vector<SkBitmap*>& getOwnedBitmapResources() const {
534        return mOwnedBitmapResources;
535    }
536
537    const Vector<SkiaColorFilter*>& getFilterResources() const {
538        return mFilterResources;
539    }
540
541    const Vector<SkiaShader*>& getShaders() const {
542        return mShaders;
543    }
544
545    const Vector<SkPaint*>& getPaints() const {
546        return mPaints;
547    }
548
549    const Vector<SkPath*>& getPaths() const {
550        return mPaths;
551    }
552
553    const SortedVector<SkPath*>& getSourcePaths() const {
554        return mSourcePaths;
555    }
556
557    const Vector<SkRegion*>& getRegions() const {
558        return mRegions;
559    }
560
561    const Vector<Layer*>& getLayers() const {
562        return mLayers;
563    }
564
565    const Vector<SkMatrix*>& getMatrices() const {
566        return mMatrices;
567    }
568
569    uint32_t getFunctorCount() const {
570        return mFunctorCount;
571    }
572
573private:
574    void insertRestoreToCount();
575    void insertTranslate();
576
577    LinearAllocator& alloc() { return mDisplayListData->allocator; }
578    void addStateOp(StateOp* op);
579    bool addDrawOp(DrawOp* op); // returns true if op not rejected
580    void addOpInternal(DisplayListOp* op) {
581        insertRestoreToCount();
582        insertTranslate();
583        mDisplayListData->displayListOps.add(op);
584    }
585
586    template<class T>
587    inline T* refBuffer(const T* srcBuffer, int32_t count) {
588        if (srcBuffer == NULL) return NULL;
589        T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
590        memcpy(dstBuffer, srcBuffer, count * sizeof(T));
591        return dstBuffer;
592    }
593
594    inline char* refText(const char* text, size_t byteLength) {
595        return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
596    }
597
598    inline SkPath* refPath(SkPath* path) {
599        if (!path) return NULL;
600
601        SkPath* pathCopy = mPathMap.valueFor(path);
602        if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) {
603            pathCopy = new SkPath(*path);
604            pathCopy->setSourcePath(path);
605            // replaceValueFor() performs an add if the entry doesn't exist
606            mPathMap.replaceValueFor(path, pathCopy);
607            mPaths.add(pathCopy);
608        }
609        if (mSourcePaths.indexOf(path) < 0) {
610            mCaches.resourceCache.incrementRefcount(path);
611            mSourcePaths.add(path);
612        }
613        return pathCopy;
614    }
615
616    inline SkPaint* refPaint(SkPaint* paint) {
617        if (!paint) {
618            return paint;
619        }
620
621        SkPaint* paintCopy = mPaintMap.valueFor(paint);
622        if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) {
623            paintCopy = new SkPaint(*paint);
624            // replaceValueFor() performs an add if the entry doesn't exist
625            mPaintMap.replaceValueFor(paint, paintCopy);
626            mPaints.add(paintCopy);
627        }
628
629        return paintCopy;
630    }
631
632    inline SkRegion* refRegion(SkRegion* region) {
633        if (!region) {
634            return region;
635        }
636
637        SkRegion* regionCopy = mRegionMap.valueFor(region);
638        // TODO: Add generation ID to SkRegion
639        if (regionCopy == NULL) {
640            regionCopy = new SkRegion(*region);
641            // replaceValueFor() performs an add if the entry doesn't exist
642            mRegionMap.replaceValueFor(region, regionCopy);
643            mRegions.add(regionCopy);
644        }
645
646        return regionCopy;
647    }
648
649    inline SkMatrix* refMatrix(SkMatrix* matrix) {
650        // Copying the matrix is cheap and prevents against the user changing the original
651        // matrix before the operation that uses it
652        SkMatrix* copy = new SkMatrix(*matrix);
653        mMatrices.add(copy);
654        return copy;
655    }
656
657    inline SkBitmap* refBitmap(SkBitmap* bitmap) {
658        // Note that this assumes the bitmap is immutable. There are cases this won't handle
659        // correctly, such as creating the bitmap from scratch, drawing with it, changing its
660        // contents, and drawing again. The only fix would be to always copy it the first time,
661        // which doesn't seem worth the extra cycles for this unlikely case.
662        mBitmapResources.add(bitmap);
663        mCaches.resourceCache.incrementRefcount(bitmap);
664        return bitmap;
665    }
666
667    inline SkBitmap* refBitmapData(SkBitmap* bitmap) {
668        mOwnedBitmapResources.add(bitmap);
669        mCaches.resourceCache.incrementRefcount(bitmap);
670        return bitmap;
671    }
672
673    inline SkiaShader* refShader(SkiaShader* shader) {
674        if (!shader) return NULL;
675
676        SkiaShader* shaderCopy = mShaderMap.valueFor(shader);
677        // TODO: We also need to handle generation ID changes in compose shaders
678        if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) {
679            shaderCopy = shader->copy();
680            // replaceValueFor() performs an add if the entry doesn't exist
681            mShaderMap.replaceValueFor(shader, shaderCopy);
682            mShaders.add(shaderCopy);
683            mCaches.resourceCache.incrementRefcount(shaderCopy);
684        }
685        return shaderCopy;
686    }
687
688    inline SkiaColorFilter* refColorFilter(SkiaColorFilter* colorFilter) {
689        mFilterResources.add(colorFilter);
690        mCaches.resourceCache.incrementRefcount(colorFilter);
691        return colorFilter;
692    }
693
694    Vector<SkBitmap*> mBitmapResources;
695    Vector<SkBitmap*> mOwnedBitmapResources;
696    Vector<SkiaColorFilter*> mFilterResources;
697
698    Vector<SkPaint*> mPaints;
699    DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap;
700
701    Vector<SkPath*> mPaths;
702    DefaultKeyedVector<SkPath*, SkPath*> mPathMap;
703
704    SortedVector<SkPath*> mSourcePaths;
705
706    Vector<SkRegion*> mRegions;
707    DefaultKeyedVector<SkRegion*, SkRegion*> mRegionMap;
708
709    Vector<SkiaShader*> mShaders;
710    DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
711
712    Vector<SkMatrix*> mMatrices;
713
714    Vector<Layer*> mLayers;
715
716    int mRestoreSaveCount;
717
718    Caches& mCaches;
719    sp<DisplayListData> mDisplayListData;
720
721    float mTranslateX;
722    float mTranslateY;
723    bool mHasTranslate;
724    bool mHasDrawOps;
725
726    uint32_t mFunctorCount;
727
728    friend class DisplayList;
729
730}; // class DisplayListRenderer
731
732}; // namespace uirenderer
733}; // namespace android
734
735#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
736