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