/* * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "EGLDisplayOpenVG.h" #include "EGLUtils.h" #include "IntSize.h" #include "SurfaceOpenVG.h" #include #include namespace WebCore { // Need to typedef this, otherwise DEFINE_STATIC_LOCAL() doesn't swallow it. typedef HashMap EGLDisplayManagerMap; // File-static variables. static EGLDisplayManagerMap& displayManagers() { DEFINE_STATIC_LOCAL(EGLDisplayManagerMap, managers, ()); return managers; } static EGLDisplayOpenVG* s_current = 0; // Static class members. SurfaceOpenVG* EGLDisplayOpenVG::currentSurface() { EGLDisplayManagerMap& managers = displayManagers(); EGLDisplay currentDisplay = eglGetCurrentDisplay(); if (currentDisplay == EGL_NO_DISPLAY || !managers.contains(currentDisplay)) return 0; EGLDisplayOpenVG* displayManager = managers.get(currentDisplay); EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW); if (currentSurface == EGL_NO_SURFACE || !displayManager->m_platformSurfaces.contains(currentSurface)) return 0; return displayManager->m_platformSurfaces.get(currentSurface); } void EGLDisplayOpenVG::registerPlatformSurface(SurfaceOpenVG* platformSurface) { EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay()); displayManager->m_platformSurfaces.set(platformSurface->eglSurface(), platformSurface); } void EGLDisplayOpenVG::unregisterPlatformSurface(SurfaceOpenVG* platformSurface) { EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay()); displayManager->m_platformSurfaces.remove(platformSurface->eglSurface()); } void EGLDisplayOpenVG::setCurrentDisplay(const EGLDisplay& display) { s_current = EGLDisplayOpenVG::forDisplay(display); } EGLDisplayOpenVG* EGLDisplayOpenVG::current() { if (!s_current) { EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); ASSERT_EGL_NO_ERROR(); s_current = EGLDisplayOpenVG::forDisplay(display); } return s_current; } EGLDisplayOpenVG* EGLDisplayOpenVG::forDisplay(const EGLDisplay& display) { EGLDisplayManagerMap& managers = displayManagers(); if (!managers.contains(display)) managers.set(display, new EGLDisplayOpenVG(display)); return managers.get(display); } // Object/instance members. EGLDisplayOpenVG::EGLDisplayOpenVG(const EGLDisplay& display) : m_display(display) , m_sharedPlatformSurface(0) , m_pbufferConfigId(0) , m_windowConfigId(0) { eglBindAPI(EGL_OPENVG_API); ASSERT_EGL_NO_ERROR(); } EGLDisplayOpenVG::~EGLDisplayOpenVG() { eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); ASSERT_EGL_NO_ERROR(); delete m_sharedPlatformSurface; HashMap::const_iterator end = m_surfaceConfigIds.end(); for (HashMap::const_iterator it = m_surfaceConfigIds.begin(); it != end; ++it) destroySurface((*it).first); eglTerminate(m_display); ASSERT_EGL_NO_ERROR(); } void EGLDisplayOpenVG::setDefaultPbufferConfig(const EGLConfig& config) { EGLint configId; EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId); ASSERT(success == EGL_TRUE); ASSERT(configId != EGL_BAD_ATTRIBUTE); m_pbufferConfigId = configId; } EGLConfig EGLDisplayOpenVG::defaultPbufferConfig() { EGLConfig config; EGLint numConfigs; // Hopefully the client will have set the pbuffer config of its choice // by now - if not, use a 32-bit generic one as default. if (!m_pbufferConfigId) { static const EGLint configAttribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_ALPHA_MASK_SIZE, 1, EGL_LUMINANCE_SIZE, EGL_DONT_CARE, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE }; eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); } else { const EGLint configAttribs[] = { EGL_CONFIG_ID, m_pbufferConfigId, EGL_NONE }; eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); } ASSERT_EGL_NO_ERROR(); ASSERT(numConfigs == 1); return config; } void EGLDisplayOpenVG::setDefaultWindowConfig(const EGLConfig& config) { EGLint configId; EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId); ASSERT(success == EGL_TRUE); ASSERT(configId != EGL_BAD_ATTRIBUTE); m_windowConfigId = configId; } EGLConfig EGLDisplayOpenVG::defaultWindowConfig() { EGLConfig config; EGLint numConfigs; // Hopefully the client will have set the window config of its choice // by now - if not, use a 32-bit generic one as default. if (!m_windowConfigId) { static const EGLint configAttribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_ALPHA_MASK_SIZE, 1, EGL_LUMINANCE_SIZE, EGL_DONT_CARE, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE }; eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); } else { const EGLint configAttribs[] = { EGL_CONFIG_ID, m_windowConfigId, EGL_NONE }; eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); } ASSERT_EGL_NO_ERROR(); ASSERT(numConfigs == 1); return config; } SurfaceOpenVG* EGLDisplayOpenVG::sharedPlatformSurface() { if (!m_sharedPlatformSurface) { // The shared surface doesn't need to be drawn on, it just exists so // that we can always make the shared context current (which in turn is // the owner of long-living resources such as images, paths and fonts). // We'll just make the shared surface as small as possible: 1x1 pixel. EGLConfig config = defaultPbufferConfig(); EGLSurface surface = createPbufferSurface(IntSize(1, 1), config); EGLContext context = eglCreateContext(m_display, config, EGL_NO_CONTEXT, 0); ASSERT_EGL_NO_ERROR(); m_contexts.set(m_surfaceConfigIds.get(surface), context); m_sharedPlatformSurface = new SurfaceOpenVG; m_sharedPlatformSurface->m_eglDisplay = m_display; m_sharedPlatformSurface->m_eglSurface = surface; m_sharedPlatformSurface->m_eglContext = context; m_platformSurfaces.set(surface, m_sharedPlatformSurface); // a.k.a. registerPlatformSurface() } return m_sharedPlatformSurface; } EGLSurface EGLDisplayOpenVG::createPbufferSurface(const IntSize& size, const EGLConfig& config, EGLint* errorCode) { const EGLint attribList[] = { EGL_WIDTH, size.width(), EGL_HEIGHT, size.height(), EGL_NONE }; EGLSurface surface = eglCreatePbufferSurface(m_display, config, attribList); if (errorCode) *errorCode = eglGetError(); else ASSERT_EGL_NO_ERROR(); if (surface == EGL_NO_SURFACE) return EGL_NO_SURFACE; EGLint surfaceConfigId; EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId); ASSERT(success == EGL_TRUE); ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE); ASSERT(!m_surfaceConfigIds.contains(surface)); m_surfaceConfigIds.set(surface, surfaceConfigId); return surface; } EGLSurface EGLDisplayOpenVG::createPbufferFromClientBuffer( EGLClientBuffer clientBuffer, EGLenum bufferType, const EGLConfig& config, EGLint* errorCode) { EGLSurface surface = eglCreatePbufferFromClientBuffer(m_display, bufferType, clientBuffer, config, 0 /* attribList */); if (errorCode) *errorCode = eglGetError(); else ASSERT_EGL_NO_ERROR(); if (surface == EGL_NO_SURFACE) return EGL_NO_SURFACE; EGLint surfaceConfigId; EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId); ASSERT(success == EGL_TRUE); ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE); ASSERT(!m_surfaceConfigIds.contains(surface)); m_surfaceConfigIds.set(surface, surfaceConfigId); return surface; } EGLSurface EGLDisplayOpenVG::surfaceForWindow(EGLNativeWindowType wId, const EGLConfig& config) { if (m_windowSurfaces.contains(wId)) return m_windowSurfaces.get(wId); EGLSurface surface = eglCreateWindowSurface(m_display, config, wId, 0); ASSERT_EGL_NO_ERROR(); EGLint surfaceConfigId; EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId); ASSERT(success == EGL_TRUE); ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE); ASSERT(!m_surfaceConfigIds.contains(surface)); m_surfaceConfigIds.set(surface, surfaceConfigId); return surface; } bool EGLDisplayOpenVG::surfacesCompatible(const EGLSurface& surface, const EGLSurface& otherSurface) { if (surface == EGL_NO_SURFACE || otherSurface == EGL_NO_SURFACE) return false; // Currently, we assume that all surfaces known to this object are // context-compatible to each other (which is reasonable to assume, // otherwise eglCreateContext() would fail with EGL_BAD_MATCH for shared // context compatibility anyways. return m_surfaceConfigIds.contains(surface) && m_surfaceConfigIds.contains(otherSurface); } void EGLDisplayOpenVG::destroySurface(const EGLSurface& surface) { ASSERT(surface != EGL_NO_SURFACE); if (eglGetCurrentSurface(EGL_DRAW) == surface) { eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); ASSERT_EGL_NO_ERROR(); } // Destroy the context associated to the surface, if we already created one. if (m_surfaceConfigIds.contains(surface)) { EGLint surfaceConfigId = m_surfaceConfigIds.take(surface); // take = get and remove bool isContextReferenced = false; if (m_compatibleConfigIds.contains(surfaceConfigId)) surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId); HashMap::iterator end = m_surfaceConfigIds.end(); // ...but only if there's no other surfaces associated to that context. for (HashMap::iterator it = m_surfaceConfigIds.begin(); it != end; ++it) { if ((*it).second == surfaceConfigId) { isContextReferenced = true; break; } } if (!isContextReferenced && m_contexts.contains(surfaceConfigId)) { EGLContext context = m_contexts.take(surfaceConfigId); eglDestroyContext(m_display, context); ASSERT_EGL_NO_ERROR(); } } m_platformSurfaces.remove(surface); HashMap::iterator end = m_windowSurfaces.end(); for (HashMap::iterator it = m_windowSurfaces.begin(); it != end; ++it) { if ((*it).second == surface) { m_windowSurfaces.remove(it); break; } } eglDestroySurface(m_display, surface); ASSERT_EGL_NO_ERROR(); } EGLContext EGLDisplayOpenVG::contextForSurface(const EGLSurface& surface) { ASSERT(surface != EGL_NO_SURFACE); if (m_platformSurfaces.contains(surface)) return m_platformSurfaces.get(surface)->eglContext(); eglBindAPI(EGL_OPENVG_API); ASSERT_EGL_NO_ERROR(); EGLint surfaceConfigId; if (m_surfaceConfigIds.contains(surface)) surfaceConfigId = m_surfaceConfigIds.get(surface); else { // Retrieve the same EGL config for context creation that was used to // create the the EGL surface. EGLBoolean success = eglQuerySurface(m_display, surface, EGL_CONFIG_ID, &surfaceConfigId); ASSERT(success == EGL_TRUE); ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE); m_surfaceConfigIds.set(surface, surfaceConfigId); } if (m_compatibleConfigIds.contains(surfaceConfigId)) surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId); if (m_contexts.contains(surfaceConfigId)) return m_contexts.get(surfaceConfigId); if (!m_sharedPlatformSurface) // shared context has not been created yet sharedPlatformSurface(); // creates the shared surface & context EGLDisplay currentDisplay = eglGetCurrentDisplay(); EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ); EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW); EGLContext currentContext = eglGetCurrentContext(); // Before creating a new context, let's try whether an existing one // is compatible with the surface. EGL doesn't give us a different way // to check context/surface compatibility than trying it out, so let's // do just that. HashMap::iterator end = m_contexts.end(); for (HashMap::iterator it = m_contexts.begin(); it != end; ++it) { eglMakeCurrent(m_display, surface, surface, (*it).second); if (eglGetError() == EGL_SUCCESS) { // Restore previous surface/context. if (currentContext != EGL_NO_CONTEXT) { eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext); ASSERT_EGL_NO_ERROR(); } // Cool, surface is compatible to one of our existing contexts. m_compatibleConfigIds.set(surfaceConfigId, (*it).first); return (*it).second; } } // Restore previous surface/context. if (currentContext != EGL_NO_CONTEXT) { eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext); ASSERT_EGL_NO_ERROR(); } EGLConfig config; EGLint numConfigs; const EGLint configAttribs[] = { EGL_CONFIG_ID, surfaceConfigId, EGL_NONE }; eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); ASSERT_EGL_NO_ERROR(); ASSERT(numConfigs == 1); // We share all of the images and paths amongst the different contexts, // so that they can be used in all of them. Resources that are created // while m_sharedPlatformSurface->context() is current will be // accessible from all other contexts, but are not restricted to the // lifetime of those contexts. EGLContext context = eglCreateContext(m_display, config, m_sharedPlatformSurface->eglContext(), 0); ASSERT_EGL_NO_ERROR(); ASSERT(!m_contexts.contains(surfaceConfigId)); m_contexts.set(surfaceConfigId, context); return context; } }