Caches.cpp revision 3b748a44c6bd2ea05fe16839caf73dbe50bd7ae9
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>(), mExtensions(Extensions::getInstance()), mInitialized(false) {
51    init();
52    initFont();
53    initConstraints();
54    initProperties();
55    initExtensions();
56
57    mDebugLevel = readDebugLevel();
58    ALOGD("Enabling debug mode %d", mDebugLevel);
59}
60
61bool Caches::init() {
62    if (mInitialized) return false;
63
64    glGenBuffers(1, &meshBuffer);
65    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
66    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
67
68    mCurrentBuffer = meshBuffer;
69    mCurrentIndicesBuffer = 0;
70    mCurrentPositionPointer = this;
71    mCurrentPositionStride = 0;
72    mCurrentTexCoordsPointer = this;
73    mCurrentPixelBuffer = 0;
74
75    mTexCoordsArrayEnabled = false;
76
77    glDisable(GL_SCISSOR_TEST);
78    scissorEnabled = false;
79    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
80
81    glActiveTexture(gTextureUnits[0]);
82    mTextureUnit = 0;
83
84    mRegionMesh = NULL;
85    mMeshIndices = 0;
86
87    blend = false;
88    lastSrcMode = GL_ZERO;
89    lastDstMode = GL_ZERO;
90    currentProgram = NULL;
91
92    mFunctorsCount = 0;
93
94    debugLayersUpdates = false;
95    debugOverdraw = false;
96    debugStencilClip = kStencilHide;
97
98    patchCache.init(*this);
99
100    mInitialized = true;
101
102    return true;
103}
104
105void Caches::initFont() {
106    fontRenderer = GammaFontRenderer::createRenderer();
107}
108
109void Caches::initExtensions() {
110    if (mExtensions.hasDebugMarker()) {
111        eventMark = glInsertEventMarkerEXT;
112
113        startMark = glPushGroupMarkerEXT;
114        endMark = glPopGroupMarkerEXT;
115    } else {
116        eventMark = eventMarkNull;
117        startMark = startMarkNull;
118        endMark = endMarkNull;
119    }
120
121    if (mExtensions.hasDebugLabel() && (drawDeferDisabled || drawReorderDisabled)) {
122        setLabel = glLabelObjectEXT;
123        getLabel = glGetObjectLabelEXT;
124    } else {
125        setLabel = setLabelNull;
126        getLabel = getLabelNull;
127    }
128}
129
130void Caches::initConstraints() {
131    GLint maxTextureUnits;
132    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
133    if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
134        ALOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
135    }
136
137    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
138}
139
140bool Caches::initProperties() {
141    bool prevDebugLayersUpdates = debugLayersUpdates;
142    bool prevDebugOverdraw = debugOverdraw;
143    StencilClipDebug prevDebugStencilClip = debugStencilClip;
144
145    char property[PROPERTY_VALUE_MAX];
146    if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, NULL) > 0) {
147        INIT_LOGD("  Layers updates debug enabled: %s", property);
148        debugLayersUpdates = !strcmp(property, "true");
149    } else {
150        debugLayersUpdates = false;
151    }
152
153    if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
154        INIT_LOGD("  Overdraw debug enabled: %s", property);
155        debugOverdraw = !strcmp(property, "true");
156    } else {
157        debugOverdraw = false;
158    }
159
160    // See Properties.h for valid values
161    if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, NULL) > 0) {
162        INIT_LOGD("  Stencil clip debug enabled: %s", property);
163        if (!strcmp(property, "hide")) {
164            debugStencilClip = kStencilHide;
165        } else if (!strcmp(property, "highlight")) {
166            debugStencilClip = kStencilShowHighlight;
167        } else if (!strcmp(property, "region")) {
168            debugStencilClip = kStencilShowRegion;
169        }
170    } else {
171        debugStencilClip = kStencilHide;
172    }
173
174    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
175        drawDeferDisabled = !strcasecmp(property, "true");
176        INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
177    } else {
178        INIT_LOGD("  Draw defer enabled");
179    }
180
181    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
182        drawReorderDisabled = !strcasecmp(property, "true");
183        INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
184    } else {
185        INIT_LOGD("  Draw reorder enabled");
186    }
187
188    return (prevDebugLayersUpdates != debugLayersUpdates) ||
189            (prevDebugOverdraw != debugOverdraw) ||
190            (prevDebugStencilClip != debugStencilClip);
191}
192
193void Caches::terminate() {
194    if (!mInitialized) return;
195
196    glDeleteBuffers(1, &meshBuffer);
197    mCurrentBuffer = 0;
198
199    glDeleteBuffers(1, &mMeshIndices);
200    delete[] mRegionMesh;
201    mMeshIndices = 0;
202    mRegionMesh = NULL;
203
204    fboCache.clear();
205
206    programCache.clear();
207    currentProgram = NULL;
208
209    assetAtlas.terminate();
210
211    patchCache.clear();
212
213    mInitialized = false;
214}
215
216///////////////////////////////////////////////////////////////////////////////
217// Debug
218///////////////////////////////////////////////////////////////////////////////
219
220void Caches::dumpMemoryUsage() {
221    String8 stringLog;
222    dumpMemoryUsage(stringLog);
223    ALOGD("%s", stringLog.string());
224}
225
226void Caches::dumpMemoryUsage(String8 &log) {
227    log.appendFormat("Current memory usage / total memory usage (bytes):\n");
228    log.appendFormat("  TextureCache         %8d / %8d\n",
229            textureCache.getSize(), textureCache.getMaxSize());
230    log.appendFormat("  LayerCache           %8d / %8d\n",
231            layerCache.getSize(), layerCache.getMaxSize());
232    log.appendFormat("  RenderBufferCache    %8d / %8d\n",
233            renderBufferCache.getSize(), renderBufferCache.getMaxSize());
234    log.appendFormat("  GradientCache        %8d / %8d\n",
235            gradientCache.getSize(), gradientCache.getMaxSize());
236    log.appendFormat("  PathCache            %8d / %8d\n",
237            pathCache.getSize(), pathCache.getMaxSize());
238    log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
239            dropShadowCache.getMaxSize());
240    log.appendFormat("  PatchCache           %8d / %8d\n",
241            patchCache.getSize(), patchCache.getMaxSize());
242    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
243        const uint32_t size = fontRenderer->getFontRendererSize(i);
244        log.appendFormat("  FontRenderer %d       %8d / %8d\n", i, size, size);
245    }
246    log.appendFormat("Other:\n");
247    log.appendFormat("  FboCache             %8d / %8d\n",
248            fboCache.getSize(), fboCache.getMaxSize());
249
250    uint32_t total = 0;
251    total += textureCache.getSize();
252    total += layerCache.getSize();
253    total += renderBufferCache.getSize();
254    total += gradientCache.getSize();
255    total += pathCache.getSize();
256    total += dropShadowCache.getSize();
257    total += patchCache.getSize();
258    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
259        total += fontRenderer->getFontRendererSize(i);
260    }
261
262    log.appendFormat("Total memory usage:\n");
263    log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
264}
265
266///////////////////////////////////////////////////////////////////////////////
267// Memory management
268///////////////////////////////////////////////////////////////////////////////
269
270void Caches::clearGarbage() {
271    textureCache.clearGarbage();
272    pathCache.clearGarbage();
273
274    Vector<DisplayList*> displayLists;
275    Vector<Layer*> layers;
276
277    { // scope for the lock
278        Mutex::Autolock _l(mGarbageLock);
279        displayLists = mDisplayListGarbage;
280        layers = mLayerGarbage;
281        mDisplayListGarbage.clear();
282        mLayerGarbage.clear();
283    }
284
285    size_t count = displayLists.size();
286    for (size_t i = 0; i < count; i++) {
287        DisplayList* displayList = displayLists.itemAt(i);
288        delete displayList;
289    }
290
291    count = layers.size();
292    for (size_t i = 0; i < count; i++) {
293        Layer* layer = layers.itemAt(i);
294        delete layer;
295    }
296    layers.clear();
297}
298
299void Caches::deleteLayerDeferred(Layer* layer) {
300    Mutex::Autolock _l(mGarbageLock);
301    mLayerGarbage.push(layer);
302}
303
304void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
305    Mutex::Autolock _l(mGarbageLock);
306    mDisplayListGarbage.push(displayList);
307}
308
309void Caches::flush(FlushMode mode) {
310    FLUSH_LOGD("Flushing caches (mode %d)", mode);
311
312    switch (mode) {
313        case kFlushMode_Full:
314            textureCache.clear();
315            patchCache.clear();
316            dropShadowCache.clear();
317            gradientCache.clear();
318            fontRenderer->clear();
319            dither.clear();
320            // fall through
321        case kFlushMode_Moderate:
322            fontRenderer->flush();
323            textureCache.flush();
324            pathCache.clear();
325            tasks.stop();
326            // fall through
327        case kFlushMode_Layers:
328            layerCache.clear();
329            renderBufferCache.clear();
330            break;
331    }
332
333    clearGarbage();
334}
335
336///////////////////////////////////////////////////////////////////////////////
337// VBO
338///////////////////////////////////////////////////////////////////////////////
339
340bool Caches::bindMeshBuffer() {
341    return bindMeshBuffer(meshBuffer);
342}
343
344bool Caches::bindMeshBuffer(const GLuint buffer) {
345    if (mCurrentBuffer != buffer) {
346        glBindBuffer(GL_ARRAY_BUFFER, buffer);
347        mCurrentBuffer = buffer;
348        return true;
349    }
350    return false;
351}
352
353bool Caches::unbindMeshBuffer() {
354    if (mCurrentBuffer) {
355        glBindBuffer(GL_ARRAY_BUFFER, 0);
356        mCurrentBuffer = 0;
357        return true;
358    }
359    return false;
360}
361
362bool Caches::bindIndicesBuffer(const GLuint buffer) {
363    if (mCurrentIndicesBuffer != buffer) {
364        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
365        mCurrentIndicesBuffer = buffer;
366        return true;
367    }
368    return false;
369}
370
371bool Caches::bindIndicesBuffer() {
372    if (!mMeshIndices) {
373        uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6];
374        for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) {
375            uint16_t quad = i * 4;
376            int index = i * 6;
377            regionIndices[index    ] = quad;       // top-left
378            regionIndices[index + 1] = quad + 1;   // top-right
379            regionIndices[index + 2] = quad + 2;   // bottom-left
380            regionIndices[index + 3] = quad + 2;   // bottom-left
381            regionIndices[index + 4] = quad + 1;   // top-right
382            regionIndices[index + 5] = quad + 3;   // bottom-right
383        }
384
385        glGenBuffers(1, &mMeshIndices);
386        bool force = bindIndicesBuffer(mMeshIndices);
387        glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t),
388                regionIndices, GL_STATIC_DRAW);
389
390        delete[] regionIndices;
391        return force;
392    }
393
394    return bindIndicesBuffer(mMeshIndices);
395}
396
397bool Caches::unbindIndicesBuffer() {
398    if (mCurrentIndicesBuffer) {
399        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
400        mCurrentIndicesBuffer = 0;
401        return true;
402    }
403    return false;
404}
405
406///////////////////////////////////////////////////////////////////////////////
407// PBO
408///////////////////////////////////////////////////////////////////////////////
409
410bool Caches::bindPixelBuffer(const GLuint buffer) {
411    if (mCurrentPixelBuffer != buffer) {
412        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
413        mCurrentPixelBuffer = buffer;
414        return true;
415    }
416    return false;
417}
418
419bool Caches::unbindPixelBuffer() {
420    if (mCurrentPixelBuffer) {
421        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
422        mCurrentPixelBuffer = 0;
423        return true;
424    }
425    return false;
426}
427
428///////////////////////////////////////////////////////////////////////////////
429// Meshes and textures
430///////////////////////////////////////////////////////////////////////////////
431
432void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
433    if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
434        GLuint slot = currentProgram->position;
435        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
436        mCurrentPositionPointer = vertices;
437        mCurrentPositionStride = stride;
438    }
439}
440
441void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
442    if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
443        GLuint slot = currentProgram->texCoords;
444        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
445        mCurrentTexCoordsPointer = vertices;
446        mCurrentTexCoordsStride = stride;
447    }
448}
449
450void Caches::resetVertexPointers() {
451    mCurrentPositionPointer = this;
452    mCurrentTexCoordsPointer = this;
453}
454
455void Caches::resetTexCoordsVertexPointer() {
456    mCurrentTexCoordsPointer = this;
457}
458
459void Caches::enableTexCoordsVertexArray() {
460    if (!mTexCoordsArrayEnabled) {
461        glEnableVertexAttribArray(Program::kBindingTexCoords);
462        mCurrentTexCoordsPointer = this;
463        mTexCoordsArrayEnabled = true;
464    }
465}
466
467void Caches::disableTexCoordsVertexArray() {
468    if (mTexCoordsArrayEnabled) {
469        glDisableVertexAttribArray(Program::kBindingTexCoords);
470        mTexCoordsArrayEnabled = false;
471    }
472}
473
474void Caches::activeTexture(GLuint textureUnit) {
475    if (mTextureUnit != textureUnit) {
476        glActiveTexture(gTextureUnits[textureUnit]);
477        mTextureUnit = textureUnit;
478    }
479}
480
481///////////////////////////////////////////////////////////////////////////////
482// Scissor
483///////////////////////////////////////////////////////////////////////////////
484
485bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
486    if (scissorEnabled && (x != mScissorX || y != mScissorY ||
487            width != mScissorWidth || height != mScissorHeight)) {
488
489        if (x < 0) {
490            width += x;
491            x = 0;
492        }
493        if (y < 0) {
494            height += y;
495            y = 0;
496        }
497        if (width < 0) {
498            width = 0;
499        }
500        if (height < 0) {
501            height = 0;
502        }
503        glScissor(x, y, width, height);
504
505        mScissorX = x;
506        mScissorY = y;
507        mScissorWidth = width;
508        mScissorHeight = height;
509
510        return true;
511    }
512    return false;
513}
514
515bool Caches::enableScissor() {
516    if (!scissorEnabled) {
517        glEnable(GL_SCISSOR_TEST);
518        scissorEnabled = true;
519        resetScissor();
520        return true;
521    }
522    return false;
523}
524
525bool Caches::disableScissor() {
526    if (scissorEnabled) {
527        glDisable(GL_SCISSOR_TEST);
528        scissorEnabled = false;
529        return true;
530    }
531    return false;
532}
533
534void Caches::setScissorEnabled(bool enabled) {
535    if (scissorEnabled != enabled) {
536        if (enabled) glEnable(GL_SCISSOR_TEST);
537        else glDisable(GL_SCISSOR_TEST);
538        scissorEnabled = enabled;
539    }
540}
541
542void Caches::resetScissor() {
543    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
544}
545
546///////////////////////////////////////////////////////////////////////////////
547// Tiling
548///////////////////////////////////////////////////////////////////////////////
549
550void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
551    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
552        glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
553    }
554}
555
556void Caches::endTiling() {
557    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
558        glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
559    }
560}
561
562bool Caches::hasRegisteredFunctors() {
563    return mFunctorsCount > 0;
564}
565
566void Caches::registerFunctors(uint32_t functorCount) {
567    mFunctorsCount += functorCount;
568}
569
570void Caches::unregisterFunctors(uint32_t functorCount) {
571    if (functorCount > mFunctorsCount) {
572        mFunctorsCount = 0;
573    } else {
574        mFunctorsCount -= functorCount;
575    }
576}
577
578///////////////////////////////////////////////////////////////////////////////
579// Regions
580///////////////////////////////////////////////////////////////////////////////
581
582TextureVertex* Caches::getRegionMesh() {
583    // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
584    if (!mRegionMesh) {
585        mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4];
586    }
587
588    return mRegionMesh;
589}
590
591}; // namespace uirenderer
592}; // namespace android
593