13f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian/*
23f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * Copyright 2013 The Android Open Source Project
33f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian *
43f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * Licensed under the Apache License, Version 2.0 (the "License");
53f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * you may not use this file except in compliance with the License.
63f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * You may obtain a copy of the License at
73f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian *
83f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian *      http://www.apache.org/licenses/LICENSE-2.0
93f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian *
103f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * Unless required by applicable law or agreed to in writing, software
113f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * distributed under the License is distributed on an "AS IS" BASIS,
123f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * See the License for the specific language governing permissions and
143f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * limitations under the License.
153f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian */
163f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
173f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
183f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian#define SF_RENDER_ENGINE_PROGRAMCACHE_H
193f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
203f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian#include <GLES2/gl2.h>
213f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
223f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian#include <utils/KeyedVector.h>
23b027f805c9a18893556353f44008683e20ebe049Chia-I Wu#include <utils/Singleton.h>
243f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian#include <utils/TypeHelpers.h>
253f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
263f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian#include "Description.h"
273f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
283f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopiannamespace android {
293f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
303f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopianclass Description;
312542cb07773302b01b1c22ac7819bf05616c7767Peiyong Linclass Formatter;
323f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopianclass Program;
333f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopianclass String8;
343f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
353f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian/*
363f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * This class generates GLSL programs suitable to handle a given
373f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * Description. It's responsible for figuring out what to
383f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * generate from a Description.
393f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian * It also maintains a cache of these Programs.
403f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian */
413f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopianclass ProgramCache : public Singleton<ProgramCache> {
423f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopianpublic:
433f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    /*
443f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian     * Key is used to retrieve a Program in the cache.
453f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian     * A Key is generated from a Description.
463f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian     */
473f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    class Key {
483f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        friend class ProgramCache;
493f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        typedef uint32_t key_t;
503f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        key_t mKey;
51b027f805c9a18893556353f44008683e20ebe049Chia-I Wu
523f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    public:
533f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        enum {
547e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            BLEND_SHIFT = 0,
557e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            BLEND_MASK = 1 << BLEND_SHIFT,
567e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            BLEND_PREMULT = 1 << BLEND_SHIFT,
577e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            BLEND_NORMAL = 0 << BLEND_SHIFT,
587e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu
597e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            OPACITY_SHIFT = 1,
607e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            OPACITY_MASK = 1 << OPACITY_SHIFT,
617e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
627e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
637e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu
647e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            ALPHA_SHIFT = 2,
657e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            ALPHA_MASK = 1 << ALPHA_SHIFT,
667e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
677e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
687e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu
697e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            TEXTURE_SHIFT = 3,
707e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            TEXTURE_MASK = 3 << TEXTURE_SHIFT,
717e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            TEXTURE_OFF = 0 << TEXTURE_SHIFT,
727e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            TEXTURE_EXT = 1 << TEXTURE_SHIFT,
737e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            TEXTURE_2D = 2 << TEXTURE_SHIFT,
747e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu
75a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            INPUT_TRANSFORM_MATRIX_SHIFT = 5,
76a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
77a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
78a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
797e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu
80a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            OUTPUT_TRANSFORM_MATRIX_SHIFT = 6,
81a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
82a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
83a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
84a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin
85a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            INPUT_TF_SHIFT = 7,
867e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
877e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
887e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
89131d3760a0437fac08ff4cc384640b7ca1802d17Chia-I Wu            INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
90a3fb7d6b631c9ecab70ff68443cbdee872ed649fPeiyong Lin            INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
917e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu
92a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            OUTPUT_TF_SHIFT = 9,
937e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
947e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
957e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu            OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
96131d3760a0437fac08ff4cc384640b7ca1802d17Chia-I Wu            OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
97a3fb7d6b631c9ecab70ff68443cbdee872ed649fPeiyong Lin            OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
98131d3760a0437fac08ff4cc384640b7ca1802d17Chia-I Wu
99a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            Y410_BT2020_SHIFT = 11,
100131d3760a0437fac08ff4cc384640b7ca1802d17Chia-I Wu            Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
101131d3760a0437fac08ff4cc384640b7ca1802d17Chia-I Wu            Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
102131d3760a0437fac08ff4cc384640b7ca1802d17Chia-I Wu            Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
1033f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        };
1043f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
105b027f805c9a18893556353f44008683e20ebe049Chia-I Wu        inline Key() : mKey(0) {}
106b027f805c9a18893556353f44008683e20ebe049Chia-I Wu        inline Key(const Key& rhs) : mKey(rhs.mKey) {}
1073f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1083f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        inline Key& set(key_t mask, key_t value) {
1093f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian            mKey = (mKey & ~mask) | value;
1103f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian            return *this;
1113f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        }
1123f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
113b027f805c9a18893556353f44008683e20ebe049Chia-I Wu        inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
114b027f805c9a18893556353f44008683e20ebe049Chia-I Wu        inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
115b027f805c9a18893556353f44008683e20ebe049Chia-I Wu        inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
116b027f805c9a18893556353f44008683e20ebe049Chia-I Wu        inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
117b027f805c9a18893556353f44008683e20ebe049Chia-I Wu        inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
118a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin        inline bool hasInputTransformMatrix() const {
119a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
120a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin        }
121a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin        inline bool hasOutputTransformMatrix() const {
122a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
123a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin        }
124a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin        inline bool hasTransformMatrix() const {
125a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin            return hasInputTransformMatrix() || hasOutputTransformMatrix();
126a296b0c232483ff2d80a814a1a39d02397ba559ePeiyong Lin        }
1277e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu        inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
1287e65bc00e46fbae2820fa52c997d72ab69ecd916Chia-I Wu        inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
12953f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin
13053f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        // When HDR and non-HDR contents are mixed, or different types of HDR contents are
13153f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        // mixed, we will do a tone mapping process to tone map the input content to output
13253f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        // content. Currently, the following conversions handled, they are:
13353f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        // * SDR -> HLG
13453f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        // * SDR -> PQ
13553f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        // * HLG -> PQ
13653f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        inline bool needsToneMapping() const {
13753f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            int inputTF = getInputTF();
13853f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            int outputTF = getOutputTF();
13953f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin
14053f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            // Return false when converting from SDR to SDR.
14153f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) {
14253f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin                return false;
14353f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            }
14453f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) {
14553f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin                return false;
14653f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            }
14753f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin
14853f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            inputTF >>= Key::INPUT_TF_SHIFT;
14953f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            outputTF >>= Key::OUTPUT_TF_SHIFT;
15053f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin            return inputTF != outputTF;
15153f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin        }
152131d3760a0437fac08ff4cc384640b7ca1802d17Chia-I Wu        inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
1533f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1543f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        // this is the definition of a friend function -- not a method of class Needs
1553f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {
15688d37ddac876c75dd5d2e39d33787dbcbf833641Romain Guy            return (lhs.mKey < rhs.mKey) ? 1 : 0;
1573f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian        }
1583f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    };
1593f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1603f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    ProgramCache();
1613f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    ~ProgramCache();
1623f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
16393e14df5b3e0a593c033715b64c8243c578ceb83Chia-I Wu    // Generate shaders to populate the cache
16493e14df5b3e0a593c033715b64c8243c578ceb83Chia-I Wu    void primeCache(bool hasWideColor);
16593e14df5b3e0a593c033715b64c8243c578ceb83Chia-I Wu
1663f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // useProgram lookup a suitable program in the cache or generates one
1673f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // if none can be found.
1683f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    void useProgram(const Description& description);
1693f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1703f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopianprivate:
1713f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // compute a cache Key from a Description
1723f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    static Key computeKey(const Description& description);
1732542cb07773302b01b1c22ac7819bf05616c7767Peiyong Lin    // Generate EOTF based from Key.
1742542cb07773302b01b1c22ac7819bf05616c7767Peiyong Lin    static void generateEOTF(Formatter& fs, const Key& needs);
17553f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin    // Generate necessary tone mapping methods for OOTF.
17653f320ea3541f43e11768eea122021ce6271cb17Peiyong Lin    static void generateToneMappingProcess(Formatter& fs, const Key& needs);
1772542cb07773302b01b1c22ac7819bf05616c7767Peiyong Lin    // Generate OOTF based from Key.
1782542cb07773302b01b1c22ac7819bf05616c7767Peiyong Lin    static void generateOOTF(Formatter& fs, const Key& needs);
1792542cb07773302b01b1c22ac7819bf05616c7767Peiyong Lin    // Generate OETF based from Key.
1802542cb07773302b01b1c22ac7819bf05616c7767Peiyong Lin    static void generateOETF(Formatter& fs, const Key& needs);
1813f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // generates a program from the Key
1823f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    static Program* generateProgram(const Key& needs);
1833f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // generates the vertex shader from the Key
1843f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    static String8 generateVertexShader(const Key& needs);
1853f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // generates the fragment shader from the Key
1863f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    static String8 generateFragmentShader(const Key& needs);
1873f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1883f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // Key/Value map used for caching Programs. Currently the cache
1893f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    // is never shrunk.
1903f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian    DefaultKeyedVector<Key, Program*> mCache;
1913f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian};
1923f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1933f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias AgopianANDROID_BASIC_TYPES_TRAITS(ProgramCache::Key)
1943f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1953f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian} /* namespace android */
1963f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian
1973f84483382be2d528918cc1a6fbc6a7d68e0b181Mathias Agopian#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
198