/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EglDisplay.h"

#include "aemu/base/containers/Lookup.h"
#include "aemu/base/files/StreamSerializing.h"
#include "EglConfig.h"
#include "EglGlobalInfo.h"
#include "EglOsApi.h"
#include <GLcommon/GLutils.h>

#include <algorithm>

EglDisplay::EglDisplay(EGLNativeDisplayType dpy,
                       EglOS::Display* idpy) :
    m_dpy(dpy),
    m_idpy(idpy)
{
    m_manager[GLES_1_1] = new ObjectNameManager(&m_globalNameSpace);
    m_manager[GLES_2_0] = new ObjectNameManager(&m_globalNameSpace);
    m_manager[GLES_3_0] = m_manager[GLES_2_0];
    m_manager[GLES_3_1] = m_manager[GLES_2_0];
};

EglDisplay::~EglDisplay() {
    android::base::AutoLock mutex(m_lock);

    m_configs.clear();

    delete m_manager[GLES_1_1];
    delete m_manager[GLES_2_0];

    delete m_idpy;
}

void EglDisplay::initialize(int renderableType) {
    android::base::AutoLock mutex(m_lock);
    m_initialized = true;
    initConfigurations(renderableType);
    m_configInitialized = true;
}

bool EglDisplay::isInitialize() {
    android::base::AutoLock mutex(m_lock);
    return m_initialized;
}

void EglDisplay::terminate(){
    android::base::AutoLock mutex(m_lock);
    m_contexts.clear();
    m_surfaces.clear();
    m_initialized = false;
}

namespace CompareEglConfigs {

// Old compare function used to initialize to something decently sorted.
struct StaticCompare {
    bool operator()(const std::unique_ptr<EglConfig>& first,
                    const std::unique_ptr<EglConfig>& second) const {
        return *first < *second;
    }
};

// In actual usage, we need to dynamically re-sort configs
// that are returned to the user.
struct DynamicCompare;
// This is because the sorting order of configs is affected
// based on dynamic properties.
//
// See https://www.khronos.org/registry/egl/sdk/docs/man/html/eglChooseConfig.xhtml
// and the section on config sorting.
//
// If the user requests an EGL config with a particular EGL_RED_SIZE,
// for example, we must sort configs based on that criteria, while if that
// was not specified, we would just skip right on to sorting by buffer size.
// Below is an implementation of EGL config sorting according
// to spec, that takes the dynamic properties into account.
static int ColorBufferTypeVal(EGLenum type) {
    switch (type) {
    case EGL_RGB_BUFFER: return 0;
    case EGL_LUMINANCE_BUFFER: return 1;
    case EGL_YUV_BUFFER_EXT: return 2;
    }
    return 3;
}

static bool nonTrivialAttribVal(EGLint val) {
    return val != 0 && val != EGL_DONT_CARE;
}

struct DynamicCompare {
public:
    DynamicCompare(const EglConfig& wantedAttribs) {

        EGLint wantedRVal = wantedAttribs.getConfAttrib(EGL_RED_SIZE);
        EGLint wantedGVal = wantedAttribs.getConfAttrib(EGL_GREEN_SIZE);
        EGLint wantedBVal = wantedAttribs.getConfAttrib(EGL_BLUE_SIZE);
        EGLint wantedLVal = wantedAttribs.getConfAttrib(EGL_LUMINANCE_SIZE);
        EGLint wantedAVal = wantedAttribs.getConfAttrib(EGL_ALPHA_SIZE);

        wantedR = wantedAttribs.isWantedAttrib(EGL_RED_SIZE) && nonTrivialAttribVal(wantedRVal);
        wantedG = wantedAttribs.isWantedAttrib(EGL_GREEN_SIZE) && nonTrivialAttribVal(wantedGVal);
        wantedB = wantedAttribs.isWantedAttrib(EGL_BLUE_SIZE) && nonTrivialAttribVal(wantedBVal);
        wantedL = wantedAttribs.isWantedAttrib(EGL_LUMINANCE_SIZE) && nonTrivialAttribVal(wantedLVal);
        wantedA = wantedAttribs.isWantedAttrib(EGL_ALPHA_SIZE) && nonTrivialAttribVal(wantedAVal);
    }

    bool operator()(EglConfig* a, EglConfig* b) const {
        EGLint aConformant = a->getConfAttrib(EGL_CONFORMANT);
        EGLint bConformant = b->getConfAttrib(EGL_CONFORMANT);

        if (aConformant != bConformant) {
            return aConformant != 0;
        }

        EGLint aCaveat = a->getConfAttrib(EGL_CONFIG_CAVEAT);
        EGLint bCaveat = b->getConfAttrib(EGL_CONFIG_CAVEAT);
        if (aCaveat != bCaveat) {
            return aCaveat < bCaveat;
        }

        EGLint aCbType = a->getConfAttrib(EGL_COLOR_BUFFER_TYPE);
        EGLint bCbType = b->getConfAttrib(EGL_COLOR_BUFFER_TYPE);
        if (aCbType != bCbType) {
            return ColorBufferTypeVal(aCbType) <
                   ColorBufferTypeVal(bCbType);
        }

        EGLint aCbSize = 0;
        EGLint bCbSize = 0;

        if (wantedR) {
            aCbSize += a->getConfAttrib(EGL_RED_SIZE);
            bCbSize += b->getConfAttrib(EGL_RED_SIZE);
        }
        if (wantedG) {
            aCbSize += a->getConfAttrib(EGL_GREEN_SIZE);
            bCbSize += b->getConfAttrib(EGL_GREEN_SIZE);
        }
        if (wantedB) {
            aCbSize += a->getConfAttrib(EGL_BLUE_SIZE);
            bCbSize += b->getConfAttrib(EGL_BLUE_SIZE);
        }
        if (wantedL) {
            aCbSize += a->getConfAttrib(EGL_LUMINANCE_SIZE);
            bCbSize += b->getConfAttrib(EGL_LUMINANCE_SIZE);
        }
        if (wantedA) {
            aCbSize += a->getConfAttrib(EGL_ALPHA_SIZE);
            bCbSize += b->getConfAttrib(EGL_ALPHA_SIZE);
        }

        if (aCbSize != bCbSize) {
            return aCbSize > bCbSize;
        }

        EGLint aBufferSize = a->getConfAttrib(EGL_BUFFER_SIZE);
        EGLint bBufferSize = b->getConfAttrib(EGL_BUFFER_SIZE);
        if (aBufferSize != bBufferSize) {
            return aBufferSize < bBufferSize;
        }

        EGLint aSampleBuffersNum = a->getConfAttrib(EGL_SAMPLE_BUFFERS);
        EGLint bSampleBuffersNum = b->getConfAttrib(EGL_SAMPLE_BUFFERS);
        if (aSampleBuffersNum != bSampleBuffersNum) {
            return aSampleBuffersNum < bSampleBuffersNum;
        }

        EGLint aSPP = a->getConfAttrib(EGL_SAMPLES);
        EGLint bSPP = b->getConfAttrib(EGL_SAMPLES);
        if (aSPP != bSPP) {
            return aSPP < bSPP;
        }

        EGLint aDepthSize = a->getConfAttrib(EGL_DEPTH_SIZE);
        EGLint bDepthSize = b->getConfAttrib(EGL_DEPTH_SIZE);
        if (aDepthSize != bDepthSize) {
            return aDepthSize < bDepthSize;
        }

        EGLint aStencilSize = a->getConfAttrib(EGL_STENCIL_SIZE);
        EGLint bStencilSize = b->getConfAttrib(EGL_STENCIL_SIZE);
        if (aStencilSize != bStencilSize) {
            return aStencilSize < bStencilSize;
        }

        return a->getConfAttrib(EGL_CONFIG_ID) < b->getConfAttrib(EGL_CONFIG_ID);
    }

    bool wantedR;
    bool wantedG;
    bool wantedB;
    bool wantedL;
    bool wantedA;
};

}

EglConfig* EglDisplay::addSimplePixelFormat(int red_size,
                                      int green_size,
                                      int blue_size,
                                      int alpha_size,
                                      int sample_per_pixel) {
    std::sort(m_configs.begin(), m_configs.end(), CompareEglConfigs::StaticCompare());

    EGLConfig match;

    EglConfig dummy(red_size,
                    green_size,
                    blue_size,
                    alpha_size,  // RGB_565
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    16, // Depth
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    sample_per_pixel,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    EGL_DONT_CARE,
                    NULL);

    if(!doChooseConfigs(dummy, &match, 1))
    {
        return nullptr;
    }

    EglConfig* config = (EglConfig*)match;

    int bSize;
    config->getConfAttrib(EGL_BUFFER_SIZE,&bSize);

    if(bSize == 16)
    {
        return config;
    }

    std::unique_ptr<EglConfig> newConfig(
         new EglConfig(*config,
                       red_size, green_size, blue_size,
                       alpha_size));

    if (m_uniqueConfigs.insert(*newConfig).second) {
        config = newConfig.release();
        m_configs.emplace_back(config);
    }
    return config;
}

// BUG: 246999412
// We might want to deprecate this list.
static const EGLint kCommonCfgs[][5] = {
    {8, 8, 8, 0, EGL_DONT_CARE},
    {8, 8, 8, 8, EGL_DONT_CARE},
    // 565 fails with ANGLE on Mac
    // {5, 6, 5, 0, EGL_DONT_CARE},
    // The following are multi-sample configs. They have issues with CTS test:
    // (API26) run cts -m CtsOpenGLTestCases -t
    // android.opengl.cts.EglConfigTest#testEglConfigs
    // We disable them until we figure out how to fix that test properly
    // BUG: 69421199
    // {5, 6, 5, 0, 2},
    // {8, 8, 8, 0, 2},
    // {8, 8, 8, 8, 2},
    // {5, 6, 5, 0, 4},
    // {8, 8, 8, 0, 4},
    // {8, 8, 8, 8, 4},
};

static constexpr int kReservedIdNum = sizeof(kCommonCfgs) / 5 / sizeof(EGLint);

void EglDisplay::addReservedConfigs() {
    for (int i = 0; i < kReservedIdNum; i++) {
        EglConfig* cfg = nullptr;
        cfg = addSimplePixelFormat(kCommonCfgs[i][0],
                kCommonCfgs[i][1],
                kCommonCfgs[i][2],
                kCommonCfgs[i][3],
                kCommonCfgs[i][4]);
        // ID starts with 1
        if (cfg) {
            cfg->setId(i + 1);
        }
    }
}

void EglDisplay::initConfigurations(int renderableType) {
    if (m_configInitialized) {
        return;
    }
    m_idpy->queryConfigs(renderableType, addConfig, this);

    for (size_t i = 0; i < m_configs.size(); i++) {
        // ID starts with 1
        m_configs[i]->setId(static_cast<EGLint>(i + 1 + kReservedIdNum));
    }
    addReservedConfigs();
    // It is ok if config id is not continual.
    std::sort(m_configs.begin(), m_configs.end(), CompareEglConfigs::StaticCompare());

#if EMUGL_DEBUG
    for (ConfigsList::const_iterator it = m_configs.begin();
         it != m_configs.end();
         ++it) {
        EglConfig* config = it->get();
        EGLint red, green, blue, alpha, depth, stencil, renderable, surface;
        config->getConfAttrib(EGL_RED_SIZE, &red);
        config->getConfAttrib(EGL_GREEN_SIZE, &green);
        config->getConfAttrib(EGL_BLUE_SIZE, &blue);
        config->getConfAttrib(EGL_ALPHA_SIZE, &alpha);
        config->getConfAttrib(EGL_DEPTH_SIZE, &depth);
        config->getConfAttrib(EGL_STENCIL_SIZE, &stencil);
        config->getConfAttrib(EGL_RENDERABLE_TYPE, &renderable);
        config->getConfAttrib(EGL_SURFACE_TYPE, &surface);
    }
#endif  // EMUGL_DEBUG
}

EglConfig* EglDisplay::getConfig(EGLConfig conf) const {
    android::base::AutoLock mutex(m_lock);

    for(ConfigsList::const_iterator it = m_configs.begin();
        it != m_configs.end();
        ++it) {
        if(static_cast<EGLConfig>(it->get()) == conf) {
            return it->get();
        }
    }
    return NULL;
}

SurfacePtr EglDisplay::getSurface(EGLSurface surface) const {
    android::base::AutoLock mutex(m_lock);
    /* surface is "key" in map<unsigned int, SurfacePtr>. */
    unsigned int hndl = SafeUIntFromPointer(surface);
    SurfacesHndlMap::const_iterator it = m_surfaces.find(hndl);
    return it != m_surfaces.end() ?
                                  (*it).second :
                                   SurfacePtr();
}

ContextPtr EglDisplay::getContext(EGLContext ctx) const {
    android::base::AutoLock mutex(m_lock);
    /* ctx is "key" in map<unsigned int, ContextPtr>. */
    unsigned int hndl = SafeUIntFromPointer(ctx);
    ContextsHndlMap::const_iterator it = m_contexts.find(hndl);
    return it != m_contexts.end() ?
                                  (*it).second :
                                   ContextPtr();
}

void* EglDisplay::getLowLevelContext(EGLContext ctx) const {
    auto lctx = getContext(ctx);
    if (lctx) {
        return lctx->nativeType()->lowLevelContext();
    }
    return nullptr;
}

bool EglDisplay::removeSurface(EGLSurface s) {
    android::base::AutoLock mutex(m_lock);
    /* s is "key" in map<unsigned int, SurfacePtr>. */
    unsigned int hndl = SafeUIntFromPointer(s);
    SurfacesHndlMap::iterator it = m_surfaces.find(hndl);
    if(it != m_surfaces.end()) {
        m_surfaces.erase(it);
        return true;
    }
    return false;
}

bool EglDisplay::removeContext(EGLContext ctx) {
    android::base::AutoLock mutex(m_lock);
    /* ctx is "key" in map<unsigned int, ContextPtr>. */
    unsigned int hndl = SafeUIntFromPointer(ctx);
    ContextsHndlMap::iterator it = m_contexts.find(hndl);
    if(it != m_contexts.end()) {
        m_contexts.erase(it);
        return true;
    }
    return false;
}

bool EglDisplay::removeContext(ContextPtr ctx) {
    android::base::AutoLock mutex(m_lock);

    ContextsHndlMap::iterator it;
    for(it = m_contexts.begin(); it != m_contexts.end();++it) {
        if((*it).second.get() == ctx.get()){
            break;
        }
    }
    if(it != m_contexts.end()) {
        m_contexts.erase(it);
        return true;
    }
    return false;
}

EglConfig* EglDisplay::getConfig(EGLint id) const {
    android::base::AutoLock mutex(m_lock);

    for(ConfigsList::const_iterator it = m_configs.begin();
        it != m_configs.end();
        ++it) {
        if((*it)->id() == id) {
            return it->get();
        }
    }
    return NULL;
}

EglConfig* EglDisplay::getDefaultConfig() const {
    return getConfig(2); // rgba8888
}

int EglDisplay::getConfigs(EGLConfig* configs,int config_size) const {
    android::base::AutoLock mutex(m_lock);
    int i = 0;
    for(ConfigsList::const_iterator it = m_configs.begin();
        it != m_configs.end() && i < config_size;
        i++, ++it) {
        configs[i] = static_cast<EGLConfig>(it->get());
    }
    return i;
}

int EglDisplay::chooseConfigs(const EglConfig& dummy,
                              EGLConfig* configs,
                              int config_size) const {
    android::base::AutoLock mutex(m_lock);
    return doChooseConfigs(dummy, configs, config_size);
}

int EglDisplay::doChooseConfigs(const EglConfig& dummy,
                                EGLConfig* configs,
                                int config_size) const {
    int added = 0;

    std::vector<EglConfig*> validConfigs;

    CHOOSE_CONFIG_DLOG("returning configs. ids: {");
    for(ConfigsList::const_iterator it = m_configs.begin();
        it != m_configs.end() && (added < config_size || !configs);
        ++it) {
        if( (*it)->chosen(dummy)){
            if(configs) {
                CHOOSE_CONFIG_DLOG("valid config: id=0x%x", it->get()->id());
                validConfigs.push_back(it->get());
            }
            added++;
       }
    }

    CHOOSE_CONFIG_DLOG("sorting valid configs...");

    std::sort(validConfigs.begin(),
              validConfigs.end(),
              CompareEglConfigs::DynamicCompare(dummy));

    for (int i = 0; configs && i < added; i++) {
        configs[i] = static_cast<EGLConfig>(validConfigs[i]);
    }

    CHOOSE_CONFIG_DLOG("returning configs. ids end }");
    return added;
}

EGLSurface EglDisplay::addSurface(SurfacePtr s ) {
    android::base::AutoLock mutex(m_lock);
   unsigned int hndl = s.get()->getHndl();
   EGLSurface ret =reinterpret_cast<EGLSurface> (hndl);

   if(m_surfaces.find(hndl) != m_surfaces.end()) {
       return ret;
   }

   m_surfaces[hndl] = s;
   return ret;
}

EGLContext EglDisplay::addContext(ContextPtr ctx ) {
    android::base::AutoLock mutex(m_lock);

   unsigned int hndl = ctx.get()->getHndl();
   EGLContext ret    = reinterpret_cast<EGLContext> (hndl);

   if(m_contexts.find(hndl) != m_contexts.end()) {
       return ret;
   }
   m_contexts[hndl] = ctx;
   return ret;
}


EGLImageKHR EglDisplay::addImageKHR(ImagePtr img) {
    android::base::AutoLock mutex(m_lock);
    do {
        ++m_nextEglImageId;
    } while(m_nextEglImageId == 0
            || android::base::contains(m_eglImages, m_nextEglImageId));
    img->imageId = m_nextEglImageId;
    m_eglImages[m_nextEglImageId] = img;
    return reinterpret_cast<EGLImageKHR>(m_nextEglImageId);
}

static void touchEglImage(EglImage* eglImage,
        SaveableTexture::restorer_t restorer) {
    if (eglImage->needRestore) {
        if (eglImage->saveableTexture.get()) {
            restorer(eglImage->saveableTexture.get());
            eglImage->saveableTexture->fillEglImage(eglImage);
        }
        eglImage->needRestore = false;
    }
}

ImagePtr EglDisplay::getImage(EGLImageKHR img,
        SaveableTexture::restorer_t restorer) const {
    android::base::AutoLock mutex(m_lock);
    /* img is "key" in map<unsigned int, ImagePtr>. */
    unsigned int hndl = SafeUIntFromPointer(img);
    ImagesHndlMap::const_iterator i( m_eglImages.find(hndl) );
    if (i == m_eglImages.end()) {
        return ImagePtr();
    }
    touchEglImage(i->second.get(), restorer);
    return i->second;
}

bool EglDisplay:: destroyImageKHR(EGLImageKHR img) {
    android::base::AutoLock mutex(m_lock);
    /* img is "key" in map<unsigned int, ImagePtr>. */
    unsigned int hndl = SafeUIntFromPointer(img);
    ImagesHndlMap::iterator i( m_eglImages.find(hndl) );
    if (i != m_eglImages.end())
    {
        m_eglImages.erase(i);
        return true;
    }
    return false;
}

EglOS::Context* EglDisplay::getGlobalSharedContext() const {
    android::base::AutoLock mutex(m_lock);
#ifndef _WIN32
    // find an existing OpenGL context to share with, if exist
    EglOS::Context* ret =
        (EglOS::Context*)m_manager[GLES_1_1]->getGlobalContext();
    if (!ret)
        ret = (EglOS::Context*)m_manager[GLES_2_0]->getGlobalContext();
    return ret;
#else
    if (!m_globalSharedContext) {
        //
        // On windows we create a dummy context to serve as the
        // "global context" which all contexts share with.
        // This is because on windows it is not possible to share
        // with a context which is already current. This dummy context
        // will never be current to any thread so it is safe to share with.
        // Create that context using the first config
        if (m_configs.empty()) {
            // Should not happen! config list should be initialized at this point
            return NULL;
        }
        EglConfig *cfg = m_configs.front().get();
        m_globalSharedContext = m_idpy->createContext(
                isCoreProfile() ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR : 0,
                cfg->nativeFormat(), NULL);
    }

    return m_globalSharedContext.get();
#endif
}

// static
void EglDisplay::addConfig(void* opaque, const EglOS::ConfigInfo* info) {
    EglDisplay* display = static_cast<EglDisplay*>(opaque);

    // Greater than 24 bits of color,
    // or having no depth/stencil causes some
    // unexpected behavior in real usage, such
    // as frame corruption and wrong drawing order.
    // Just don't use those configs.
    if (info->red_size > 8 ||
        info->green_size > 8 ||
        info->blue_size > 8 ||
        info->depth_size < 24 ||
        info->stencil_size < 8 ||
        info->samples_per_pixel > 2) {
        return;
    }

    std::unique_ptr<EglConfig> config(new EglConfig(
         info->red_size,
         info->green_size,
         info->blue_size,
         info->alpha_size,
         info->alpha_mask_size,
         info->caveat,
         info->depth_size,
         info->frame_buffer_level,
         info->max_pbuffer_width,
         info->max_pbuffer_height,
         info->max_pbuffer_size,
         info->native_renderable,
         info->renderable_type,
         info->native_visual_id,
         info->native_visual_type,
         info->samples_per_pixel,
         info->stencil_size,
         info->surface_type,
         info->transparent_type,
         info->trans_red_val,
         info->trans_green_val,
         info->trans_blue_val,
         info->recordable_android,
         info->frmt));

    if (display->m_uniqueConfigs.insert(*config).second) {
        display->m_configs.emplace_back(config.release());
    }
}

void EglDisplay::onSaveAllImages(android::base::Stream* stream,
                                 const android::snapshot::ITextureSaverPtr& textureSaver,
                                 SaveableTexture::saver_t saver,
                                 SaveableTexture::restorer_t restorer) {
    // we could consider calling presave for all ShareGroups from here
    // but it would introduce overheads because not all share groups need to be
    // saved
    android::base::AutoLock mutex(m_lock);
    for (auto& image : m_eglImages) {
        // In case we loaded textures from a previous snapshot and have not
        // yet restore them to GPU, we do the restoration here.
        // TODO: skip restoration and write saveableTexture directly to the
        // new snapshot for better performance
        touchEglImage(image.second.get(), restorer);
        getGlobalNameSpace()->preSaveAddEglImage(image.second.get());
    }
    m_globalNameSpace.onSave(stream, textureSaver, saver);
    saveCollection(stream, m_eglImages, [](
            android::base::Stream* stream,
            const ImagesHndlMap::value_type& img) {
        stream->putBe32(img.first);
        stream->putBe32(img.second->globalTexObj->getGlobalName());
        // We do not need to save other fields in EglImage. We can load them
        // from SaveableTexture.
    });
}

void EglDisplay::onLoadAllImages(android::base::Stream* stream,
                                 const android::snapshot::ITextureLoaderPtr& textureLoader,
                                 SaveableTexture::creator_t creator) {
    if (!m_eglImages.empty()) {
        // Could be triggered by this bug:
        // b/36654917
        fprintf(stderr, "Warning: unreleased EGL image handles\n");
    }
    m_eglImages.clear();
    android::base::AutoLock mutex(m_lock);
    m_globalNameSpace.setIfaces(
        EglGlobalInfo::getInstance()->getEglIface(),
        EglGlobalInfo::getInstance()->getIface(GLES_2_0));
    m_globalNameSpace.onLoad(stream, textureLoader, creator);

    loadCollection(stream, &m_eglImages, [this](
        android::base::Stream* stream) {
        unsigned int hndl = stream->getBe32();
        unsigned int globalName = stream->getBe32();
        ImagePtr eglImg(new EglImage);
        eglImg->imageId = hndl;
        eglImg->saveableTexture =
                m_globalNameSpace.getSaveableTextureFromLoad(globalName);
        eglImg->needRestore = true;
        return std::make_pair(hndl, std::move(eglImg));
    });
}

void EglDisplay::postLoadAllImages(android::base::Stream* stream) {
    m_globalNameSpace.postLoad(stream);
}

bool EglDisplay::nativeTextureDecompressionEnabled() const {
    return m_nativeTextureDecompressionEnabled;
}

void EglDisplay::setNativeTextureDecompressionEnabled(bool enabled) {
    m_nativeTextureDecompressionEnabled = enabled;
}

bool EglDisplay::programBinaryLinkStatusEnabled() const {
    return m_programBinaryLinkStatusEnabled;
}

void EglDisplay::setProgramBinaryLinkStatusEnabled(bool enabled) {
    m_programBinaryLinkStatusEnabled = enabled;
}
