/* * 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 #include 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& first, const std::unique_ptr& 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 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(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(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 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 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 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 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(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 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(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 (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 (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(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 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 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(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 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); } bool EglDisplay::nativeTextureDecompressionEnabled() const { return m_nativeTextureDecompressionEnabled; } void EglDisplay::setNativeTextureDecompressionEnabled(bool enabled) { m_nativeTextureDecompressionEnabled = enabled; }