| /* |
| * 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, |
| 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->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); |
| } |