Caches.cpp revision 17035b0211a3c9d45ea46a99217a6acbe76e8fbe
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
29namespace android {
30
31#ifdef USE_OPENGL_RENDERER
32using namespace uirenderer;
33ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
34#endif
35
36namespace 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
52Caches::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
66bool 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
112void Caches::initFont() {
113    fontRenderer = GammaFontRenderer::createRenderer();
114}
115
116void 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
137void 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
147void 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
159bool 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
219void 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
251uint32_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
261void Caches::dumpMemoryUsage() {
262    String8 stringLog;
263    dumpMemoryUsage(stringLog);
264    ALOGD("%s", stringLog.string());
265}
266
267void 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
322void 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
343void Caches::deleteLayerDeferred(Layer* layer) {
344    Mutex::Autolock _l(mGarbageLock);
345    mLayerGarbage.push(layer);
346}
347
348void Caches::flush(FlushMode mode) {
349    FLUSH_LOGD("Flushing caches (mode %d)", mode);
350
351    // We must stop tasks before clearing caches
352    if (mode > kFlushMode_Layers) {
353        tasks.stop();
354    }
355
356    switch (mode) {
357        case kFlushMode_Full:
358            textureCache.clear();
359            patchCache.clear();
360            dropShadowCache.clear();
361            gradientCache.clear();
362            fontRenderer->clear();
363            fboCache.clear();
364            dither.clear();
365            // fall through
366        case kFlushMode_Moderate:
367            fontRenderer->flush();
368            textureCache.flush();
369            pathCache.clear();
370            tessellationCache.clear();
371            // fall through
372        case kFlushMode_Layers:
373            layerCache.clear();
374            renderBufferCache.clear();
375            break;
376    }
377
378    clearGarbage();
379}
380
381///////////////////////////////////////////////////////////////////////////////
382// VBO
383///////////////////////////////////////////////////////////////////////////////
384
385bool Caches::bindMeshBuffer() {
386    return bindMeshBuffer(meshBuffer);
387}
388
389bool Caches::bindMeshBuffer(const GLuint buffer) {
390    if (mCurrentBuffer != buffer) {
391        glBindBuffer(GL_ARRAY_BUFFER, buffer);
392        mCurrentBuffer = buffer;
393        return true;
394    }
395    return false;
396}
397
398bool Caches::unbindMeshBuffer() {
399    if (mCurrentBuffer) {
400        glBindBuffer(GL_ARRAY_BUFFER, 0);
401        mCurrentBuffer = 0;
402        return true;
403    }
404    return false;
405}
406
407bool Caches::bindIndicesBufferInternal(const GLuint buffer) {
408    if (mCurrentIndicesBuffer != buffer) {
409        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
410        mCurrentIndicesBuffer = buffer;
411        return true;
412    }
413    return false;
414}
415
416bool Caches::bindQuadIndicesBuffer() {
417    if (!mMeshIndices) {
418        uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6];
419        for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
420            uint16_t quad = i * 4;
421            int index = i * 6;
422            regionIndices[index    ] = quad;       // top-left
423            regionIndices[index + 1] = quad + 1;   // top-right
424            regionIndices[index + 2] = quad + 2;   // bottom-left
425            regionIndices[index + 3] = quad + 2;   // bottom-left
426            regionIndices[index + 4] = quad + 1;   // top-right
427            regionIndices[index + 5] = quad + 3;   // bottom-right
428        }
429
430        glGenBuffers(1, &mMeshIndices);
431        bool force = bindIndicesBufferInternal(mMeshIndices);
432        glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
433                regionIndices, GL_STATIC_DRAW);
434
435        delete[] regionIndices;
436        return force;
437    }
438
439    return bindIndicesBufferInternal(mMeshIndices);
440}
441
442bool Caches::bindShadowIndicesBuffer() {
443    if (!mShadowStripsIndices) {
444        uint16_t* shadowIndices = new uint16_t[MAX_SHADOW_INDEX_COUNT];
445        ShadowTessellator::generateShadowIndices(shadowIndices);
446        glGenBuffers(1, &mShadowStripsIndices);
447        bool force = bindIndicesBufferInternal(mShadowStripsIndices);
448        glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t),
449            shadowIndices, GL_STATIC_DRAW);
450
451        delete[] shadowIndices;
452        return force;
453    }
454
455    return bindIndicesBufferInternal(mShadowStripsIndices);
456}
457
458bool Caches::unbindIndicesBuffer() {
459    if (mCurrentIndicesBuffer) {
460        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
461        mCurrentIndicesBuffer = 0;
462        return true;
463    }
464    return false;
465}
466
467///////////////////////////////////////////////////////////////////////////////
468// PBO
469///////////////////////////////////////////////////////////////////////////////
470
471bool Caches::bindPixelBuffer(const GLuint buffer) {
472    if (mCurrentPixelBuffer != buffer) {
473        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
474        mCurrentPixelBuffer = buffer;
475        return true;
476    }
477    return false;
478}
479
480bool Caches::unbindPixelBuffer() {
481    if (mCurrentPixelBuffer) {
482        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
483        mCurrentPixelBuffer = 0;
484        return true;
485    }
486    return false;
487}
488
489///////////////////////////////////////////////////////////////////////////////
490// Meshes and textures
491///////////////////////////////////////////////////////////////////////////////
492
493void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
494    if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
495        GLuint slot = currentProgram->position;
496        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
497        mCurrentPositionPointer = vertices;
498        mCurrentPositionStride = stride;
499    }
500}
501
502void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
503    if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
504        GLuint slot = currentProgram->texCoords;
505        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
506        mCurrentTexCoordsPointer = vertices;
507        mCurrentTexCoordsStride = stride;
508    }
509}
510
511void Caches::resetVertexPointers() {
512    mCurrentPositionPointer = this;
513    mCurrentTexCoordsPointer = this;
514}
515
516void Caches::resetTexCoordsVertexPointer() {
517    mCurrentTexCoordsPointer = this;
518}
519
520void Caches::enableTexCoordsVertexArray() {
521    if (!mTexCoordsArrayEnabled) {
522        glEnableVertexAttribArray(Program::kBindingTexCoords);
523        mCurrentTexCoordsPointer = this;
524        mTexCoordsArrayEnabled = true;
525    }
526}
527
528void Caches::disableTexCoordsVertexArray() {
529    if (mTexCoordsArrayEnabled) {
530        glDisableVertexAttribArray(Program::kBindingTexCoords);
531        mTexCoordsArrayEnabled = false;
532    }
533}
534
535void Caches::activeTexture(GLuint textureUnit) {
536    if (mTextureUnit != textureUnit) {
537        glActiveTexture(gTextureUnits[textureUnit]);
538        mTextureUnit = textureUnit;
539    }
540}
541
542void Caches::resetActiveTexture() {
543    mTextureUnit = -1;
544}
545
546void Caches::bindTexture(GLuint texture) {
547    if (mBoundTextures[mTextureUnit] != texture) {
548        glBindTexture(GL_TEXTURE_2D, texture);
549        mBoundTextures[mTextureUnit] = texture;
550    }
551}
552
553void Caches::bindTexture(GLenum target, GLuint texture) {
554    if (mBoundTextures[mTextureUnit] != texture) {
555        glBindTexture(target, texture);
556        mBoundTextures[mTextureUnit] = texture;
557    }
558}
559
560void Caches::deleteTexture(GLuint texture) {
561    // When glDeleteTextures() is called on a currently bound texture,
562    // OpenGL ES specifies that the texture is then considered unbound
563    // Consider the following series of calls:
564    //
565    // glGenTextures -> creates texture name 2
566    // glBindTexture(2)
567    // glDeleteTextures(2) -> 2 is now unbound
568    // glGenTextures -> can return 2 again
569    //
570    // If we don't call glBindTexture(2) after the second glGenTextures
571    // call, any texture operation will be performed on the default
572    // texture (name=0)
573
574    unbindTexture(texture);
575
576    glDeleteTextures(1, &texture);
577}
578
579void Caches::resetBoundTextures() {
580    memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
581}
582
583void Caches::unbindTexture(GLuint texture) {
584    for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
585        if (mBoundTextures[i] == texture) {
586            mBoundTextures[i] = 0;
587        }
588    }
589}
590
591///////////////////////////////////////////////////////////////////////////////
592// Scissor
593///////////////////////////////////////////////////////////////////////////////
594
595bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
596    if (scissorEnabled && (x != mScissorX || y != mScissorY ||
597            width != mScissorWidth || height != mScissorHeight)) {
598
599        if (x < 0) {
600            width += x;
601            x = 0;
602        }
603        if (y < 0) {
604            height += y;
605            y = 0;
606        }
607        if (width < 0) {
608            width = 0;
609        }
610        if (height < 0) {
611            height = 0;
612        }
613        glScissor(x, y, width, height);
614
615        mScissorX = x;
616        mScissorY = y;
617        mScissorWidth = width;
618        mScissorHeight = height;
619
620        return true;
621    }
622    return false;
623}
624
625bool Caches::enableScissor() {
626    if (!scissorEnabled) {
627        glEnable(GL_SCISSOR_TEST);
628        scissorEnabled = true;
629        resetScissor();
630        return true;
631    }
632    return false;
633}
634
635bool Caches::disableScissor() {
636    if (scissorEnabled) {
637        glDisable(GL_SCISSOR_TEST);
638        scissorEnabled = false;
639        return true;
640    }
641    return false;
642}
643
644void Caches::setScissorEnabled(bool enabled) {
645    if (scissorEnabled != enabled) {
646        if (enabled) glEnable(GL_SCISSOR_TEST);
647        else glDisable(GL_SCISSOR_TEST);
648        scissorEnabled = enabled;
649    }
650}
651
652void Caches::resetScissor() {
653    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
654}
655
656///////////////////////////////////////////////////////////////////////////////
657// Tiling
658///////////////////////////////////////////////////////////////////////////////
659
660void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
661    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
662        glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
663    }
664}
665
666void Caches::endTiling() {
667    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
668        glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
669    }
670}
671
672bool Caches::hasRegisteredFunctors() {
673    return mFunctorsCount > 0;
674}
675
676void Caches::registerFunctors(uint32_t functorCount) {
677    mFunctorsCount += functorCount;
678}
679
680void Caches::unregisterFunctors(uint32_t functorCount) {
681    if (functorCount > mFunctorsCount) {
682        mFunctorsCount = 0;
683    } else {
684        mFunctorsCount -= functorCount;
685    }
686}
687
688///////////////////////////////////////////////////////////////////////////////
689// Regions
690///////////////////////////////////////////////////////////////////////////////
691
692TextureVertex* Caches::getRegionMesh() {
693    // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
694    if (!mRegionMesh) {
695        mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4];
696    }
697
698    return mRegionMesh;
699}
700
701///////////////////////////////////////////////////////////////////////////////
702// Temporary Properties
703///////////////////////////////////////////////////////////////////////////////
704
705void Caches::initTempProperties() {
706    propertyLightDiameter = -1.0f;
707    propertyLightPosY = -1.0f;
708    propertyLightPosZ = -1.0f;
709    propertyAmbientRatio = -1.0f;
710    propertyAmbientShadowStrength = -1;
711    propertySpotShadowStrength = -1;
712}
713
714void Caches::setTempProperty(const char* name, const char* value) {
715    ALOGD("setting property %s to %s", name, value);
716    if (!strcmp(name, "ambientRatio")) {
717        propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0);
718        ALOGD("ambientRatio = %.2f", propertyAmbientRatio);
719        return;
720    } else if (!strcmp(name, "lightDiameter")) {
721        propertyLightDiameter = fmin(fmax(atof(value), 0.0), 3000.0);
722        ALOGD("lightDiameter = %.2f", propertyLightDiameter);
723        return;
724    } else if (!strcmp(name, "lightPosY")) {
725        propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0);
726        ALOGD("lightPos Y = %.2f", propertyLightPosY);
727        return;
728    } else if (!strcmp(name, "lightPosZ")) {
729        propertyLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0);
730        ALOGD("lightPos Z = %.2f", propertyLightPosZ);
731        return;
732    } else if (!strcmp(name, "ambientShadowStrength")) {
733        propertyAmbientShadowStrength = atoi(value);
734        ALOGD("ambient shadow strength = 0x%x out of 0xff", propertyAmbientShadowStrength);
735        return;
736    } else if (!strcmp(name, "spotShadowStrength")) {
737        propertySpotShadowStrength = atoi(value);
738        ALOGD("spot shadow strength = 0x%x out of 0xff", propertySpotShadowStrength);
739        return;
740    }
741    ALOGD("    failed");
742}
743
744}; // namespace uirenderer
745}; // namespace android
746