| /* |
| * 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 <wtf/Assertions.h> |
| #include <wtf/StdLibExtras.h> |
| |
| namespace WebCore { |
| |
| // Need to typedef this, otherwise DEFINE_STATIC_LOCAL() doesn't swallow it. |
| typedef HashMap<EGLDisplay, EGLDisplayOpenVG*> 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<EGLSurface, EGLint>::const_iterator end = m_surfaceConfigIds.end(); |
| for (HashMap<EGLSurface, EGLint>::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<EGLSurface, EGLint>::iterator end = m_surfaceConfigIds.end(); |
| |
| // ...but only if there's no other surfaces associated to that context. |
| for (HashMap<EGLSurface, EGLint>::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<EGLNativeWindowType, EGLSurface>::iterator end = m_windowSurfaces.end(); |
| for (HashMap<EGLNativeWindowType, EGLSurface>::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<EGLint, EGLContext>::iterator end = m_contexts.end(); |
| |
| for (HashMap<EGLint, EGLContext>::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; |
| } |
| |
| } |