Caches.cpp revision b746371de7f21ae36a14953d9b253df06838efb1
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    if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
170        INIT_LOGD("  Overdraw debug enabled: %s", property);
171        debugOverdraw = !strcmp(property, "show");
172    } else {
173        debugOverdraw = false;
174    }
175
176    // See Properties.h for valid values
177    if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, NULL) > 0) {
178        INIT_LOGD("  Stencil clip debug enabled: %s", property);
179        if (!strcmp(property, "hide")) {
180            debugStencilClip = kStencilHide;
181        } else if (!strcmp(property, "highlight")) {
182            debugStencilClip = kStencilShowHighlight;
183        } else if (!strcmp(property, "region")) {
184            debugStencilClip = kStencilShowRegion;
185        }
186    } else {
187        debugStencilClip = kStencilHide;
188    }
189
190    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
191        drawDeferDisabled = !strcasecmp(property, "true");
192        INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
193    } else {
194        INIT_LOGD("  Draw defer enabled");
195    }
196
197    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
198        drawReorderDisabled = !strcasecmp(property, "true");
199        INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
200    } else {
201        INIT_LOGD("  Draw reorder enabled");
202    }
203
204    return (prevDebugLayersUpdates != debugLayersUpdates) ||
205            (prevDebugOverdraw != debugOverdraw) ||
206            (prevDebugStencilClip != debugStencilClip);
207}
208
209void Caches::terminate() {
210    if (!mInitialized) return;
211
212    glDeleteBuffers(1, &meshBuffer);
213    mCurrentBuffer = 0;
214
215    glDeleteBuffers(1, &mMeshIndices);
216    delete[] mRegionMesh;
217    mMeshIndices = 0;
218    mRegionMesh = NULL;
219
220    fboCache.clear();
221
222    programCache.clear();
223    currentProgram = NULL;
224
225    assetAtlas.terminate();
226
227    patchCache.clear();
228
229    mInitialized = false;
230}
231
232///////////////////////////////////////////////////////////////////////////////
233// Debug
234///////////////////////////////////////////////////////////////////////////////
235
236void Caches::dumpMemoryUsage() {
237    String8 stringLog;
238    dumpMemoryUsage(stringLog);
239    ALOGD("%s", stringLog.string());
240}
241
242void Caches::dumpMemoryUsage(String8 &log) {
243    log.appendFormat("Current memory usage / total memory usage (bytes):\n");
244    log.appendFormat("  TextureCache         %8d / %8d\n",
245            textureCache.getSize(), textureCache.getMaxSize());
246    log.appendFormat("  LayerCache           %8d / %8d\n",
247            layerCache.getSize(), layerCache.getMaxSize());
248    log.appendFormat("  RenderBufferCache    %8d / %8d\n",
249            renderBufferCache.getSize(), renderBufferCache.getMaxSize());
250    log.appendFormat("  GradientCache        %8d / %8d\n",
251            gradientCache.getSize(), gradientCache.getMaxSize());
252    log.appendFormat("  PathCache            %8d / %8d\n",
253            pathCache.getSize(), pathCache.getMaxSize());
254    log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
255            dropShadowCache.getMaxSize());
256    log.appendFormat("  PatchCache           %8d / %8d\n",
257            patchCache.getSize(), patchCache.getMaxSize());
258    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
259        const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
260        const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
261        log.appendFormat("  FontRenderer %d A8    %8d / %8d\n", i, sizeA8, sizeA8);
262        log.appendFormat("  FontRenderer %d RGBA  %8d / %8d\n", i, sizeRGBA, sizeRGBA);
263        log.appendFormat("  FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
264                sizeA8 + sizeRGBA);
265    }
266    log.appendFormat("Other:\n");
267    log.appendFormat("  FboCache             %8d / %8d\n",
268            fboCache.getSize(), fboCache.getMaxSize());
269
270    uint32_t total = 0;
271    total += textureCache.getSize();
272    total += layerCache.getSize();
273    total += renderBufferCache.getSize();
274    total += gradientCache.getSize();
275    total += pathCache.getSize();
276    total += dropShadowCache.getSize();
277    total += patchCache.getSize();
278    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
279        total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
280        total += fontRenderer->getFontRendererSize(i, GL_RGBA);
281    }
282
283    log.appendFormat("Total memory usage:\n");
284    log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
285}
286
287///////////////////////////////////////////////////////////////////////////////
288// Memory management
289///////////////////////////////////////////////////////////////////////////////
290
291void Caches::clearGarbage() {
292    textureCache.clearGarbage();
293    pathCache.clearGarbage();
294    patchCache.clearGarbage();
295
296    Vector<DisplayList*> displayLists;
297    Vector<Layer*> layers;
298
299    { // scope for the lock
300        Mutex::Autolock _l(mGarbageLock);
301        displayLists = mDisplayListGarbage;
302        layers = mLayerGarbage;
303        mDisplayListGarbage.clear();
304        mLayerGarbage.clear();
305    }
306
307    size_t count = displayLists.size();
308    for (size_t i = 0; i < count; i++) {
309        DisplayList* displayList = displayLists.itemAt(i);
310        delete displayList;
311    }
312
313    count = layers.size();
314    for (size_t i = 0; i < count; i++) {
315        Layer* layer = layers.itemAt(i);
316        delete layer;
317    }
318    layers.clear();
319}
320
321void Caches::deleteLayerDeferred(Layer* layer) {
322    Mutex::Autolock _l(mGarbageLock);
323    mLayerGarbage.push(layer);
324}
325
326void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
327    Mutex::Autolock _l(mGarbageLock);
328    mDisplayListGarbage.push(displayList);
329}
330
331void Caches::flush(FlushMode mode) {
332    FLUSH_LOGD("Flushing caches (mode %d)", mode);
333
334    switch (mode) {
335        case kFlushMode_Full:
336            textureCache.clear();
337            patchCache.clear();
338            dropShadowCache.clear();
339            gradientCache.clear();
340            fontRenderer->clear();
341            fboCache.clear();
342            dither.clear();
343            // fall through
344        case kFlushMode_Moderate:
345            fontRenderer->flush();
346            textureCache.flush();
347            pathCache.clear();
348            tasks.stop();
349            // fall through
350        case kFlushMode_Layers:
351            layerCache.clear();
352            renderBufferCache.clear();
353            break;
354    }
355
356    clearGarbage();
357}
358
359///////////////////////////////////////////////////////////////////////////////
360// VBO
361///////////////////////////////////////////////////////////////////////////////
362
363bool Caches::bindMeshBuffer() {
364    return bindMeshBuffer(meshBuffer);
365}
366
367bool Caches::bindMeshBuffer(const GLuint buffer) {
368    if (mCurrentBuffer != buffer) {
369        glBindBuffer(GL_ARRAY_BUFFER, buffer);
370        mCurrentBuffer = buffer;
371        return true;
372    }
373    return false;
374}
375
376bool Caches::unbindMeshBuffer() {
377    if (mCurrentBuffer) {
378        glBindBuffer(GL_ARRAY_BUFFER, 0);
379        mCurrentBuffer = 0;
380        return true;
381    }
382    return false;
383}
384
385bool Caches::bindIndicesBuffer(const GLuint buffer) {
386    if (mCurrentIndicesBuffer != buffer) {
387        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
388        mCurrentIndicesBuffer = buffer;
389        return true;
390    }
391    return false;
392}
393
394bool Caches::bindIndicesBuffer() {
395    if (!mMeshIndices) {
396        uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6];
397        for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
398            uint16_t quad = i * 4;
399            int index = i * 6;
400            regionIndices[index    ] = quad;       // top-left
401            regionIndices[index + 1] = quad + 1;   // top-right
402            regionIndices[index + 2] = quad + 2;   // bottom-left
403            regionIndices[index + 3] = quad + 2;   // bottom-left
404            regionIndices[index + 4] = quad + 1;   // top-right
405            regionIndices[index + 5] = quad + 3;   // bottom-right
406        }
407
408        glGenBuffers(1, &mMeshIndices);
409        bool force = bindIndicesBuffer(mMeshIndices);
410        glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
411                regionIndices, GL_STATIC_DRAW);
412
413        delete[] regionIndices;
414        return force;
415    }
416
417    return bindIndicesBuffer(mMeshIndices);
418}
419
420bool Caches::unbindIndicesBuffer() {
421    if (mCurrentIndicesBuffer) {
422        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
423        mCurrentIndicesBuffer = 0;
424        return true;
425    }
426    return false;
427}
428
429///////////////////////////////////////////////////////////////////////////////
430// PBO
431///////////////////////////////////////////////////////////////////////////////
432
433bool Caches::bindPixelBuffer(const GLuint buffer) {
434    if (mCurrentPixelBuffer != buffer) {
435        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
436        mCurrentPixelBuffer = buffer;
437        return true;
438    }
439    return false;
440}
441
442bool Caches::unbindPixelBuffer() {
443    if (mCurrentPixelBuffer) {
444        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
445        mCurrentPixelBuffer = 0;
446        return true;
447    }
448    return false;
449}
450
451///////////////////////////////////////////////////////////////////////////////
452// Meshes and textures
453///////////////////////////////////////////////////////////////////////////////
454
455void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
456    if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
457        GLuint slot = currentProgram->position;
458        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
459        mCurrentPositionPointer = vertices;
460        mCurrentPositionStride = stride;
461    }
462}
463
464void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
465    if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
466        GLuint slot = currentProgram->texCoords;
467        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
468        mCurrentTexCoordsPointer = vertices;
469        mCurrentTexCoordsStride = stride;
470    }
471}
472
473void Caches::resetVertexPointers() {
474    mCurrentPositionPointer = this;
475    mCurrentTexCoordsPointer = this;
476}
477
478void Caches::resetTexCoordsVertexPointer() {
479    mCurrentTexCoordsPointer = this;
480}
481
482void Caches::enableTexCoordsVertexArray() {
483    if (!mTexCoordsArrayEnabled) {
484        glEnableVertexAttribArray(Program::kBindingTexCoords);
485        mCurrentTexCoordsPointer = this;
486        mTexCoordsArrayEnabled = true;
487    }
488}
489
490void Caches::disableTexCoordsVertexArray() {
491    if (mTexCoordsArrayEnabled) {
492        glDisableVertexAttribArray(Program::kBindingTexCoords);
493        mTexCoordsArrayEnabled = false;
494    }
495}
496
497void Caches::activeTexture(GLuint textureUnit) {
498    if (mTextureUnit != textureUnit) {
499        glActiveTexture(gTextureUnits[textureUnit]);
500        mTextureUnit = textureUnit;
501    }
502}
503
504void Caches::resetActiveTexture() {
505    mTextureUnit = -1;
506}
507
508void Caches::bindTexture(GLuint texture) {
509    if (mBoundTextures[mTextureUnit] != texture) {
510        glBindTexture(GL_TEXTURE_2D, texture);
511        mBoundTextures[mTextureUnit] = texture;
512    }
513}
514
515void Caches::bindTexture(GLenum target, GLuint texture) {
516    if (mBoundTextures[mTextureUnit] != texture) {
517        glBindTexture(target, texture);
518        mBoundTextures[mTextureUnit] = texture;
519    }
520}
521
522void Caches::deleteTexture(GLuint texture) {
523    // When glDeleteTextures() is called on a currently bound texture,
524    // OpenGL ES specifies that the texture is then considered unbound
525    // Consider the following series of calls:
526    //
527    // glGenTextures -> creates texture name 2
528    // glBindTexture(2)
529    // glDeleteTextures(2) -> 2 is now unbound
530    // glGenTextures -> can return 2 again
531    //
532    // If we don't call glBindTexture(2) after the second glGenTextures
533    // call, any texture operation will be performed on the default
534    // texture (name=0)
535
536    for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
537        if (mBoundTextures[i] == texture) {
538            mBoundTextures[i] = 0;
539        }
540    }
541    glDeleteTextures(1, &texture);
542}
543
544void Caches::resetBoundTextures() {
545    memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
546}
547
548///////////////////////////////////////////////////////////////////////////////
549// Scissor
550///////////////////////////////////////////////////////////////////////////////
551
552bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
553    if (scissorEnabled && (x != mScissorX || y != mScissorY ||
554            width != mScissorWidth || height != mScissorHeight)) {
555
556        if (x < 0) {
557            width += x;
558            x = 0;
559        }
560        if (y < 0) {
561            height += y;
562            y = 0;
563        }
564        if (width < 0) {
565            width = 0;
566        }
567        if (height < 0) {
568            height = 0;
569        }
570        glScissor(x, y, width, height);
571
572        mScissorX = x;
573        mScissorY = y;
574        mScissorWidth = width;
575        mScissorHeight = height;
576
577        return true;
578    }
579    return false;
580}
581
582bool Caches::enableScissor() {
583    if (!scissorEnabled) {
584        glEnable(GL_SCISSOR_TEST);
585        scissorEnabled = true;
586        resetScissor();
587        return true;
588    }
589    return false;
590}
591
592bool Caches::disableScissor() {
593    if (scissorEnabled) {
594        glDisable(GL_SCISSOR_TEST);
595        scissorEnabled = false;
596        return true;
597    }
598    return false;
599}
600
601void Caches::setScissorEnabled(bool enabled) {
602    if (scissorEnabled != enabled) {
603        if (enabled) glEnable(GL_SCISSOR_TEST);
604        else glDisable(GL_SCISSOR_TEST);
605        scissorEnabled = enabled;
606    }
607}
608
609void Caches::resetScissor() {
610    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
611}
612
613///////////////////////////////////////////////////////////////////////////////
614// Tiling
615///////////////////////////////////////////////////////////////////////////////
616
617void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
618    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
619        glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
620    }
621}
622
623void Caches::endTiling() {
624    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
625        glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
626    }
627}
628
629bool Caches::hasRegisteredFunctors() {
630    return mFunctorsCount > 0;
631}
632
633void Caches::registerFunctors(uint32_t functorCount) {
634    mFunctorsCount += functorCount;
635}
636
637void Caches::unregisterFunctors(uint32_t functorCount) {
638    if (functorCount > mFunctorsCount) {
639        mFunctorsCount = 0;
640    } else {
641        mFunctorsCount -= functorCount;
642    }
643}
644
645///////////////////////////////////////////////////////////////////////////////
646// Regions
647///////////////////////////////////////////////////////////////////////////////
648
649TextureVertex* Caches::getRegionMesh() {
650    // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
651    if (!mRegionMesh) {
652        mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4];
653    }
654
655    return mRegionMesh;
656}
657
658}; // namespace uirenderer
659}; // namespace android
660