Home | History | Annotate | Download | only in hwui
      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 
     27 namespace android {
     28 
     29 #ifdef USE_OPENGL_RENDERER
     30 using namespace uirenderer;
     31 ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
     32 #endif
     33 
     34 namespace 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 
     50 Caches::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 
     63 bool 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 
    109 void Caches::initFont() {
    110     fontRenderer = GammaFontRenderer::createRenderer();
    111 }
    112 
    113 void 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 
    134 void 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 
    144 void 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 
    156 bool 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 
    214 void 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 
    243 uint32_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 
    253 void Caches::dumpMemoryUsage() {
    254     String8 stringLog;
    255     dumpMemoryUsage(stringLog);
    256     ALOGD("%s", stringLog.string());
    257 }
    258 
    259 void 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 
    308 void 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 
    338 void Caches::deleteLayerDeferred(Layer* layer) {
    339     Mutex::Autolock _l(mGarbageLock);
    340     mLayerGarbage.push(layer);
    341 }
    342 
    343 void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
    344     Mutex::Autolock _l(mGarbageLock);
    345     mDisplayListGarbage.push(displayList);
    346 }
    347 
    348 void 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 
    384 bool Caches::bindMeshBuffer() {
    385     return bindMeshBuffer(meshBuffer);
    386 }
    387 
    388 bool 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 
    397 bool Caches::unbindMeshBuffer() {
    398     if (mCurrentBuffer) {
    399         glBindBuffer(GL_ARRAY_BUFFER, 0);
    400         mCurrentBuffer = 0;
    401         return true;
    402     }
    403     return false;
    404 }
    405 
    406 bool 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 
    415 bool 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 
    441 bool 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 
    454 bool 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 
    463 bool 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 
    476 void 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 
    485 void 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 
    494 void Caches::resetVertexPointers() {
    495     mCurrentPositionPointer = this;
    496     mCurrentTexCoordsPointer = this;
    497 }
    498 
    499 void Caches::resetTexCoordsVertexPointer() {
    500     mCurrentTexCoordsPointer = this;
    501 }
    502 
    503 void Caches::enableTexCoordsVertexArray() {
    504     if (!mTexCoordsArrayEnabled) {
    505         glEnableVertexAttribArray(Program::kBindingTexCoords);
    506         mCurrentTexCoordsPointer = this;
    507         mTexCoordsArrayEnabled = true;
    508     }
    509 }
    510 
    511 void Caches::disableTexCoordsVertexArray() {
    512     if (mTexCoordsArrayEnabled) {
    513         glDisableVertexAttribArray(Program::kBindingTexCoords);
    514         mTexCoordsArrayEnabled = false;
    515     }
    516 }
    517 
    518 void Caches::activeTexture(GLuint textureUnit) {
    519     if (mTextureUnit != textureUnit) {
    520         glActiveTexture(gTextureUnits[textureUnit]);
    521         mTextureUnit = textureUnit;
    522     }
    523 }
    524 
    525 void Caches::resetActiveTexture() {
    526     mTextureUnit = -1;
    527 }
    528 
    529 void Caches::bindTexture(GLuint texture) {
    530     if (mBoundTextures[mTextureUnit] != texture) {
    531         glBindTexture(GL_TEXTURE_2D, texture);
    532         mBoundTextures[mTextureUnit] = texture;
    533     }
    534 }
    535 
    536 void Caches::bindTexture(GLenum target, GLuint texture) {
    537     if (mBoundTextures[mTextureUnit] != texture) {
    538         glBindTexture(target, texture);
    539         mBoundTextures[mTextureUnit] = texture;
    540     }
    541 }
    542 
    543 void 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     for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
    558         if (mBoundTextures[i] == texture) {
    559             mBoundTextures[i] = 0;
    560         }
    561     }
    562     glDeleteTextures(1, &texture);
    563 }
    564 
    565 void Caches::resetBoundTextures() {
    566     memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
    567 }
    568 
    569 ///////////////////////////////////////////////////////////////////////////////
    570 // Scissor
    571 ///////////////////////////////////////////////////////////////////////////////
    572 
    573 bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
    574     if (scissorEnabled && (x != mScissorX || y != mScissorY ||
    575             width != mScissorWidth || height != mScissorHeight)) {
    576 
    577         if (x < 0) {
    578             width += x;
    579             x = 0;
    580         }
    581         if (y < 0) {
    582             height += y;
    583             y = 0;
    584         }
    585         if (width < 0) {
    586             width = 0;
    587         }
    588         if (height < 0) {
    589             height = 0;
    590         }
    591         glScissor(x, y, width, height);
    592 
    593         mScissorX = x;
    594         mScissorY = y;
    595         mScissorWidth = width;
    596         mScissorHeight = height;
    597 
    598         return true;
    599     }
    600     return false;
    601 }
    602 
    603 bool Caches::enableScissor() {
    604     if (!scissorEnabled) {
    605         glEnable(GL_SCISSOR_TEST);
    606         scissorEnabled = true;
    607         resetScissor();
    608         return true;
    609     }
    610     return false;
    611 }
    612 
    613 bool Caches::disableScissor() {
    614     if (scissorEnabled) {
    615         glDisable(GL_SCISSOR_TEST);
    616         scissorEnabled = false;
    617         return true;
    618     }
    619     return false;
    620 }
    621 
    622 void Caches::setScissorEnabled(bool enabled) {
    623     if (scissorEnabled != enabled) {
    624         if (enabled) glEnable(GL_SCISSOR_TEST);
    625         else glDisable(GL_SCISSOR_TEST);
    626         scissorEnabled = enabled;
    627     }
    628 }
    629 
    630 void Caches::resetScissor() {
    631     mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
    632 }
    633 
    634 ///////////////////////////////////////////////////////////////////////////////
    635 // Tiling
    636 ///////////////////////////////////////////////////////////////////////////////
    637 
    638 void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
    639     if (mExtensions.hasTiledRendering() && !debugOverdraw) {
    640         glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
    641     }
    642 }
    643 
    644 void Caches::endTiling() {
    645     if (mExtensions.hasTiledRendering() && !debugOverdraw) {
    646         glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
    647     }
    648 }
    649 
    650 bool Caches::hasRegisteredFunctors() {
    651     return mFunctorsCount > 0;
    652 }
    653 
    654 void Caches::registerFunctors(uint32_t functorCount) {
    655     mFunctorsCount += functorCount;
    656 }
    657 
    658 void Caches::unregisterFunctors(uint32_t functorCount) {
    659     if (functorCount > mFunctorsCount) {
    660         mFunctorsCount = 0;
    661     } else {
    662         mFunctorsCount -= functorCount;
    663     }
    664 }
    665 
    666 ///////////////////////////////////////////////////////////////////////////////
    667 // Regions
    668 ///////////////////////////////////////////////////////////////////////////////
    669 
    670 TextureVertex* Caches::getRegionMesh() {
    671     // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
    672     if (!mRegionMesh) {
    673         mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4];
    674     }
    675 
    676     return mRegionMesh;
    677 }
    678 
    679 }; // namespace uirenderer
    680 }; // namespace android
    681