Caches.cpp revision 4a473c7d9406a2d6f6792f0f48d933424740ec5c
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#define LOG_TAG "OpenGLRenderer"
18
19#include <utils/Log.h>
20#include <utils/String8.h>
21
22#include "Caches.h"
23#include "DisplayListRenderer.h"
24#include "Properties.h"
25#include "LayerRenderer.h"
26
27namespace android {
28
29#ifdef USE_OPENGL_RENDERER
30using namespace uirenderer;
31ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
32#endif
33
34namespace uirenderer {
35
36///////////////////////////////////////////////////////////////////////////////
37// Macros
38///////////////////////////////////////////////////////////////////////////////
39
40#if DEBUG_CACHE_FLUSH
41    #define FLUSH_LOGD(...) ALOGD(__VA_ARGS__)
42#else
43    #define FLUSH_LOGD(...)
44#endif
45
46///////////////////////////////////////////////////////////////////////////////
47// Constructors/destructor
48///////////////////////////////////////////////////////////////////////////////
49
50Caches::Caches(): Singleton<Caches>(),
51        mExtensions(Extensions::getInstance()), mInitialized(false) {
52    init();
53    initFont();
54    initConstraints();
55    initProperties();
56    initStaticProperties();
57    initExtensions();
58
59    mDebugLevel = readDebugLevel();
60    ALOGD("Enabling debug mode %d", mDebugLevel);
61}
62
63bool Caches::init() {
64    if (mInitialized) return false;
65
66    glGenBuffers(1, &meshBuffer);
67    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
68    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
69
70    mCurrentBuffer = meshBuffer;
71    mCurrentIndicesBuffer = 0;
72    mCurrentPositionPointer = this;
73    mCurrentPositionStride = 0;
74    mCurrentTexCoordsPointer = this;
75    mCurrentPixelBuffer = 0;
76
77    mTexCoordsArrayEnabled = false;
78
79    glDisable(GL_SCISSOR_TEST);
80    scissorEnabled = false;
81    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
82
83    glActiveTexture(gTextureUnits[0]);
84    mTextureUnit = 0;
85
86    mRegionMesh = NULL;
87    mMeshIndices = 0;
88
89    blend = false;
90    lastSrcMode = GL_ZERO;
91    lastDstMode = GL_ZERO;
92    currentProgram = NULL;
93
94    mFunctorsCount = 0;
95
96    debugLayersUpdates = false;
97    debugOverdraw = false;
98    debugStencilClip = kStencilHide;
99
100    patchCache.init(*this);
101
102    mInitialized = true;
103
104    resetBoundTextures();
105
106    return true;
107}
108
109void Caches::initFont() {
110    fontRenderer = GammaFontRenderer::createRenderer();
111}
112
113void Caches::initExtensions() {
114    if (mExtensions.hasDebugMarker()) {
115        eventMark = glInsertEventMarkerEXT;
116
117        startMark = glPushGroupMarkerEXT;
118        endMark = glPopGroupMarkerEXT;
119    } else {
120        eventMark = eventMarkNull;
121        startMark = startMarkNull;
122        endMark = endMarkNull;
123    }
124
125    if (mExtensions.hasDebugLabel() && (drawDeferDisabled || drawReorderDisabled)) {
126        setLabel = glLabelObjectEXT;
127        getLabel = glGetObjectLabelEXT;
128    } else {
129        setLabel = setLabelNull;
130        getLabel = getLabelNull;
131    }
132}
133
134void Caches::initConstraints() {
135    GLint maxTextureUnits;
136    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
137    if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
138        ALOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
139    }
140
141    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
142}
143
144void Caches::initStaticProperties() {
145    gpuPixelBuffersEnabled = false;
146
147    // OpenGL ES 3.0+ specific features
148    if (mExtensions.hasPixelBufferObjects()) {
149        char property[PROPERTY_VALUE_MAX];
150        if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "true") > 0) {
151            gpuPixelBuffersEnabled = !strcmp(property, "true");
152        }
153    }
154}
155
156bool Caches::initProperties() {
157    bool prevDebugLayersUpdates = debugLayersUpdates;
158    bool prevDebugOverdraw = debugOverdraw;
159    StencilClipDebug prevDebugStencilClip = debugStencilClip;
160
161    char property[PROPERTY_VALUE_MAX];
162    if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, NULL) > 0) {
163        INIT_LOGD("  Layers updates debug enabled: %s", property);
164        debugLayersUpdates = !strcmp(property, "true");
165    } else {
166        debugLayersUpdates = false;
167    }
168
169    debugOverdraw = false;
170    if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
171        INIT_LOGD("  Overdraw debug enabled: %s", property);
172        if (!strcmp(property, "show")) {
173            debugOverdraw = true;
174            mOverdrawDebugColorSet = kColorSet_Default;
175        } else if (!strcmp(property, "show_deuteranomaly")) {
176            debugOverdraw = true;
177            mOverdrawDebugColorSet = kColorSet_Deuteranomaly;
178        }
179    }
180
181    // See Properties.h for valid values
182    if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, NULL) > 0) {
183        INIT_LOGD("  Stencil clip debug enabled: %s", property);
184        if (!strcmp(property, "hide")) {
185            debugStencilClip = kStencilHide;
186        } else if (!strcmp(property, "highlight")) {
187            debugStencilClip = kStencilShowHighlight;
188        } else if (!strcmp(property, "region")) {
189            debugStencilClip = kStencilShowRegion;
190        }
191    } else {
192        debugStencilClip = kStencilHide;
193    }
194
195    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
196        drawDeferDisabled = !strcasecmp(property, "true");
197        INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
198    } else {
199        INIT_LOGD("  Draw defer enabled");
200    }
201
202    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
203        drawReorderDisabled = !strcasecmp(property, "true");
204        INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
205    } else {
206        INIT_LOGD("  Draw reorder enabled");
207    }
208
209    return (prevDebugLayersUpdates != debugLayersUpdates) ||
210            (prevDebugOverdraw != debugOverdraw) ||
211            (prevDebugStencilClip != debugStencilClip);
212}
213
214void Caches::terminate() {
215    if (!mInitialized) return;
216
217    glDeleteBuffers(1, &meshBuffer);
218    mCurrentBuffer = 0;
219
220    glDeleteBuffers(1, &mMeshIndices);
221    delete[] mRegionMesh;
222    mMeshIndices = 0;
223    mRegionMesh = NULL;
224
225    fboCache.clear();
226
227    programCache.clear();
228    currentProgram = NULL;
229
230    assetAtlas.terminate();
231
232    patchCache.clear();
233
234    clearGarbage();
235
236    mInitialized = false;
237}
238
239///////////////////////////////////////////////////////////////////////////////
240// Debug
241///////////////////////////////////////////////////////////////////////////////
242
243uint32_t Caches::getOverdrawColor(uint32_t amount) const {
244    static uint32_t sOverdrawColors[2][4] = {
245            { 0x2f0000ff, 0x2f00ff00, 0x3fff0000, 0x7fff0000 },
246            { 0x2f0000ff, 0x4fffff00, 0x5fff8ad8, 0x7fff0000 }
247    };
248    if (amount < 1) amount = 1;
249    if (amount > 4) amount = 4;
250    return sOverdrawColors[mOverdrawDebugColorSet][amount - 1];
251}
252
253void Caches::dumpMemoryUsage() {
254    String8 stringLog;
255    dumpMemoryUsage(stringLog);
256    ALOGD("%s", stringLog.string());
257}
258
259void Caches::dumpMemoryUsage(String8 &log) {
260    log.appendFormat("Current memory usage / total memory usage (bytes):\n");
261    log.appendFormat("  TextureCache         %8d / %8d\n",
262            textureCache.getSize(), textureCache.getMaxSize());
263    log.appendFormat("  LayerCache           %8d / %8d\n",
264            layerCache.getSize(), layerCache.getMaxSize());
265    log.appendFormat("  RenderBufferCache    %8d / %8d\n",
266            renderBufferCache.getSize(), renderBufferCache.getMaxSize());
267    log.appendFormat("  GradientCache        %8d / %8d\n",
268            gradientCache.getSize(), gradientCache.getMaxSize());
269    log.appendFormat("  PathCache            %8d / %8d\n",
270            pathCache.getSize(), pathCache.getMaxSize());
271    log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
272            dropShadowCache.getMaxSize());
273    log.appendFormat("  PatchCache           %8d / %8d\n",
274            patchCache.getSize(), patchCache.getMaxSize());
275    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
276        const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
277        const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
278        log.appendFormat("  FontRenderer %d A8    %8d / %8d\n", i, sizeA8, sizeA8);
279        log.appendFormat("  FontRenderer %d RGBA  %8d / %8d\n", i, sizeRGBA, sizeRGBA);
280        log.appendFormat("  FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
281                sizeA8 + sizeRGBA);
282    }
283    log.appendFormat("Other:\n");
284    log.appendFormat("  FboCache             %8d / %8d\n",
285            fboCache.getSize(), fboCache.getMaxSize());
286
287    uint32_t total = 0;
288    total += textureCache.getSize();
289    total += layerCache.getSize();
290    total += renderBufferCache.getSize();
291    total += gradientCache.getSize();
292    total += pathCache.getSize();
293    total += dropShadowCache.getSize();
294    total += patchCache.getSize();
295    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
296        total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
297        total += fontRenderer->getFontRendererSize(i, GL_RGBA);
298    }
299
300    log.appendFormat("Total memory usage:\n");
301    log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
302}
303
304///////////////////////////////////////////////////////////////////////////////
305// Memory management
306///////////////////////////////////////////////////////////////////////////////
307
308void Caches::clearGarbage() {
309    textureCache.clearGarbage();
310    pathCache.clearGarbage();
311    patchCache.clearGarbage();
312
313    Vector<DisplayList*> displayLists;
314    Vector<Layer*> layers;
315
316    { // scope for the lock
317        Mutex::Autolock _l(mGarbageLock);
318        displayLists = mDisplayListGarbage;
319        layers = mLayerGarbage;
320        mDisplayListGarbage.clear();
321        mLayerGarbage.clear();
322    }
323
324    size_t count = displayLists.size();
325    for (size_t i = 0; i < count; i++) {
326        DisplayList* displayList = displayLists.itemAt(i);
327        delete displayList;
328    }
329
330    count = layers.size();
331    for (size_t i = 0; i < count; i++) {
332        Layer* layer = layers.itemAt(i);
333        delete layer;
334    }
335    layers.clear();
336}
337
338void Caches::deleteLayerDeferred(Layer* layer) {
339    Mutex::Autolock _l(mGarbageLock);
340    mLayerGarbage.push(layer);
341}
342
343void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
344    Mutex::Autolock _l(mGarbageLock);
345    mDisplayListGarbage.push(displayList);
346}
347
348void Caches::flush(FlushMode mode) {
349    FLUSH_LOGD("Flushing caches (mode %d)", mode);
350
351    // We must stop tasks before clearing caches
352    if (mode > kFlushMode_Layers) {
353        tasks.stop();
354    }
355
356    switch (mode) {
357        case kFlushMode_Full:
358            textureCache.clear();
359            patchCache.clear();
360            dropShadowCache.clear();
361            gradientCache.clear();
362            fontRenderer->clear();
363            fboCache.clear();
364            dither.clear();
365            // fall through
366        case kFlushMode_Moderate:
367            fontRenderer->flush();
368            textureCache.flush();
369            pathCache.clear();
370            // fall through
371        case kFlushMode_Layers:
372            layerCache.clear();
373            renderBufferCache.clear();
374            break;
375    }
376
377    clearGarbage();
378}
379
380///////////////////////////////////////////////////////////////////////////////
381// VBO
382///////////////////////////////////////////////////////////////////////////////
383
384bool Caches::bindMeshBuffer() {
385    return bindMeshBuffer(meshBuffer);
386}
387
388bool Caches::bindMeshBuffer(const GLuint buffer) {
389    if (mCurrentBuffer != buffer) {
390        glBindBuffer(GL_ARRAY_BUFFER, buffer);
391        mCurrentBuffer = buffer;
392        return true;
393    }
394    return false;
395}
396
397bool Caches::unbindMeshBuffer() {
398    if (mCurrentBuffer) {
399        glBindBuffer(GL_ARRAY_BUFFER, 0);
400        mCurrentBuffer = 0;
401        return true;
402    }
403    return false;
404}
405
406bool Caches::bindIndicesBuffer(const GLuint buffer) {
407    if (mCurrentIndicesBuffer != buffer) {
408        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
409        mCurrentIndicesBuffer = buffer;
410        return true;
411    }
412    return false;
413}
414
415bool Caches::bindIndicesBuffer() {
416    if (!mMeshIndices) {
417        uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6];
418        for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
419            uint16_t quad = i * 4;
420            int index = i * 6;
421            regionIndices[index    ] = quad;       // top-left
422            regionIndices[index + 1] = quad + 1;   // top-right
423            regionIndices[index + 2] = quad + 2;   // bottom-left
424            regionIndices[index + 3] = quad + 2;   // bottom-left
425            regionIndices[index + 4] = quad + 1;   // top-right
426            regionIndices[index + 5] = quad + 3;   // bottom-right
427        }
428
429        glGenBuffers(1, &mMeshIndices);
430        bool force = bindIndicesBuffer(mMeshIndices);
431        glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
432                regionIndices, GL_STATIC_DRAW);
433
434        delete[] regionIndices;
435        return force;
436    }
437
438    return bindIndicesBuffer(mMeshIndices);
439}
440
441bool Caches::unbindIndicesBuffer() {
442    if (mCurrentIndicesBuffer) {
443        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
444        mCurrentIndicesBuffer = 0;
445        return true;
446    }
447    return false;
448}
449
450///////////////////////////////////////////////////////////////////////////////
451// PBO
452///////////////////////////////////////////////////////////////////////////////
453
454bool Caches::bindPixelBuffer(const GLuint buffer) {
455    if (mCurrentPixelBuffer != buffer) {
456        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
457        mCurrentPixelBuffer = buffer;
458        return true;
459    }
460    return false;
461}
462
463bool Caches::unbindPixelBuffer() {
464    if (mCurrentPixelBuffer) {
465        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
466        mCurrentPixelBuffer = 0;
467        return true;
468    }
469    return false;
470}
471
472///////////////////////////////////////////////////////////////////////////////
473// Meshes and textures
474///////////////////////////////////////////////////////////////////////////////
475
476void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
477    if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
478        GLuint slot = currentProgram->position;
479        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
480        mCurrentPositionPointer = vertices;
481        mCurrentPositionStride = stride;
482    }
483}
484
485void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
486    if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
487        GLuint slot = currentProgram->texCoords;
488        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
489        mCurrentTexCoordsPointer = vertices;
490        mCurrentTexCoordsStride = stride;
491    }
492}
493
494void Caches::resetVertexPointers() {
495    mCurrentPositionPointer = this;
496    mCurrentTexCoordsPointer = this;
497}
498
499void Caches::resetTexCoordsVertexPointer() {
500    mCurrentTexCoordsPointer = this;
501}
502
503void Caches::enableTexCoordsVertexArray() {
504    if (!mTexCoordsArrayEnabled) {
505        glEnableVertexAttribArray(Program::kBindingTexCoords);
506        mCurrentTexCoordsPointer = this;
507        mTexCoordsArrayEnabled = true;
508    }
509}
510
511void Caches::disableTexCoordsVertexArray() {
512    if (mTexCoordsArrayEnabled) {
513        glDisableVertexAttribArray(Program::kBindingTexCoords);
514        mTexCoordsArrayEnabled = false;
515    }
516}
517
518void Caches::activeTexture(GLuint textureUnit) {
519    if (mTextureUnit != textureUnit) {
520        glActiveTexture(gTextureUnits[textureUnit]);
521        mTextureUnit = textureUnit;
522    }
523}
524
525void Caches::resetActiveTexture() {
526    mTextureUnit = -1;
527}
528
529void Caches::bindTexture(GLuint texture) {
530    if (mBoundTextures[mTextureUnit] != texture) {
531        glBindTexture(GL_TEXTURE_2D, texture);
532        mBoundTextures[mTextureUnit] = texture;
533    }
534}
535
536void Caches::bindTexture(GLenum target, GLuint texture) {
537    if (mBoundTextures[mTextureUnit] != texture) {
538        glBindTexture(target, texture);
539        mBoundTextures[mTextureUnit] = texture;
540    }
541}
542
543void Caches::deleteTexture(GLuint texture) {
544    // When glDeleteTextures() is called on a currently bound texture,
545    // OpenGL ES specifies that the texture is then considered unbound
546    // Consider the following series of calls:
547    //
548    // glGenTextures -> creates texture name 2
549    // glBindTexture(2)
550    // glDeleteTextures(2) -> 2 is now unbound
551    // glGenTextures -> can return 2 again
552    //
553    // If we don't call glBindTexture(2) after the second glGenTextures
554    // call, any texture operation will be performed on the default
555    // texture (name=0)
556
557    unbindTexture(texture);
558
559    glDeleteTextures(1, &texture);
560}
561
562void Caches::resetBoundTextures() {
563    memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
564}
565
566void Caches::unbindTexture(GLuint texture) {
567    for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
568        if (mBoundTextures[i] == texture) {
569            mBoundTextures[i] = 0;
570        }
571    }
572}
573
574///////////////////////////////////////////////////////////////////////////////
575// Scissor
576///////////////////////////////////////////////////////////////////////////////
577
578bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
579    if (scissorEnabled && (x != mScissorX || y != mScissorY ||
580            width != mScissorWidth || height != mScissorHeight)) {
581
582        if (x < 0) {
583            width += x;
584            x = 0;
585        }
586        if (y < 0) {
587            height += y;
588            y = 0;
589        }
590        if (width < 0) {
591            width = 0;
592        }
593        if (height < 0) {
594            height = 0;
595        }
596        glScissor(x, y, width, height);
597
598        mScissorX = x;
599        mScissorY = y;
600        mScissorWidth = width;
601        mScissorHeight = height;
602
603        return true;
604    }
605    return false;
606}
607
608bool Caches::enableScissor() {
609    if (!scissorEnabled) {
610        glEnable(GL_SCISSOR_TEST);
611        scissorEnabled = true;
612        resetScissor();
613        return true;
614    }
615    return false;
616}
617
618bool Caches::disableScissor() {
619    if (scissorEnabled) {
620        glDisable(GL_SCISSOR_TEST);
621        scissorEnabled = false;
622        return true;
623    }
624    return false;
625}
626
627void Caches::setScissorEnabled(bool enabled) {
628    if (scissorEnabled != enabled) {
629        if (enabled) glEnable(GL_SCISSOR_TEST);
630        else glDisable(GL_SCISSOR_TEST);
631        scissorEnabled = enabled;
632    }
633}
634
635void Caches::resetScissor() {
636    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
637}
638
639///////////////////////////////////////////////////////////////////////////////
640// Tiling
641///////////////////////////////////////////////////////////////////////////////
642
643void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
644    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
645        glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
646    }
647}
648
649void Caches::endTiling() {
650    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
651        glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
652    }
653}
654
655bool Caches::hasRegisteredFunctors() {
656    return mFunctorsCount > 0;
657}
658
659void Caches::registerFunctors(uint32_t functorCount) {
660    mFunctorsCount += functorCount;
661}
662
663void Caches::unregisterFunctors(uint32_t functorCount) {
664    if (functorCount > mFunctorsCount) {
665        mFunctorsCount = 0;
666    } else {
667        mFunctorsCount -= functorCount;
668    }
669}
670
671///////////////////////////////////////////////////////////////////////////////
672// Regions
673///////////////////////////////////////////////////////////////////////////////
674
675TextureVertex* Caches::getRegionMesh() {
676    // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
677    if (!mRegionMesh) {
678        mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4];
679    }
680
681    return mRegionMesh;
682}
683
684}; // namespace uirenderer
685}; // namespace android
686