Home | History | Annotate | Download | only in openvg
      1 /*
      2  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  */
     19 
     20 #include "config.h"
     21 #include "EGLDisplayOpenVG.h"
     22 
     23 #include "EGLUtils.h"
     24 #include "IntSize.h"
     25 #include "SurfaceOpenVG.h"
     26 
     27 #include <wtf/Assertions.h>
     28 #include <wtf/StdLibExtras.h>
     29 
     30 namespace WebCore {
     31 
     32 // Need to typedef this, otherwise DEFINE_STATIC_LOCAL() doesn't swallow it.
     33 typedef HashMap<EGLDisplay, EGLDisplayOpenVG*> EGLDisplayManagerMap;
     34 
     35 // File-static variables.
     36 static EGLDisplayManagerMap& displayManagers()
     37 {
     38     DEFINE_STATIC_LOCAL(EGLDisplayManagerMap, managers, ());
     39     return managers;
     40 }
     41 
     42 static EGLDisplayOpenVG* s_current = 0;
     43 
     44 // Static class members.
     45 
     46 SurfaceOpenVG* EGLDisplayOpenVG::currentSurface()
     47 {
     48     EGLDisplayManagerMap& managers = displayManagers();
     49     EGLDisplay currentDisplay = eglGetCurrentDisplay();
     50 
     51     if (currentDisplay == EGL_NO_DISPLAY || !managers.contains(currentDisplay))
     52         return 0;
     53 
     54     EGLDisplayOpenVG* displayManager = managers.get(currentDisplay);
     55     EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
     56 
     57     if (currentSurface == EGL_NO_SURFACE || !displayManager->m_platformSurfaces.contains(currentSurface))
     58         return 0;
     59 
     60     return displayManager->m_platformSurfaces.get(currentSurface);
     61 }
     62 
     63 void EGLDisplayOpenVG::registerPlatformSurface(SurfaceOpenVG* platformSurface)
     64 {
     65     EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay());
     66     displayManager->m_platformSurfaces.set(platformSurface->eglSurface(), platformSurface);
     67 }
     68 
     69 void EGLDisplayOpenVG::unregisterPlatformSurface(SurfaceOpenVG* platformSurface)
     70 {
     71     EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay());
     72     displayManager->m_platformSurfaces.remove(platformSurface->eglSurface());
     73 }
     74 
     75 void EGLDisplayOpenVG::setCurrentDisplay(const EGLDisplay& display)
     76 {
     77     s_current = EGLDisplayOpenVG::forDisplay(display);
     78 }
     79 
     80 EGLDisplayOpenVG* EGLDisplayOpenVG::current()
     81 {
     82     if (!s_current) {
     83         EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     84         eglInitialize(display, 0, 0);
     85         ASSERT_EGL_NO_ERROR();
     86 
     87         s_current = EGLDisplayOpenVG::forDisplay(display);
     88     }
     89     return s_current;
     90 }
     91 
     92 EGLDisplayOpenVG* EGLDisplayOpenVG::forDisplay(const EGLDisplay& display)
     93 {
     94     EGLDisplayManagerMap& managers = displayManagers();
     95 
     96     if (!managers.contains(display))
     97         managers.set(display, new EGLDisplayOpenVG(display));
     98 
     99     return managers.get(display);
    100 }
    101 
    102 
    103 // Object/instance members.
    104 
    105 EGLDisplayOpenVG::EGLDisplayOpenVG(const EGLDisplay& display)
    106     : m_display(display)
    107     , m_sharedPlatformSurface(0)
    108     , m_pbufferConfigId(0)
    109     , m_windowConfigId(0)
    110 {
    111     eglBindAPI(EGL_OPENVG_API);
    112     ASSERT_EGL_NO_ERROR();
    113 }
    114 
    115 EGLDisplayOpenVG::~EGLDisplayOpenVG()
    116 {
    117     eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    118     ASSERT_EGL_NO_ERROR();
    119 
    120     delete m_sharedPlatformSurface;
    121 
    122     HashMap<EGLSurface, EGLint>::const_iterator end = m_surfaceConfigIds.end();
    123     for (HashMap<EGLSurface, EGLint>::const_iterator it = m_surfaceConfigIds.begin(); it != end; ++it)
    124         destroySurface((*it).first);
    125 
    126     eglTerminate(m_display);
    127     ASSERT_EGL_NO_ERROR();
    128 }
    129 
    130 void EGLDisplayOpenVG::setDefaultPbufferConfig(const EGLConfig& config)
    131 {
    132     EGLint configId;
    133     EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId);
    134     ASSERT(success == EGL_TRUE);
    135     ASSERT(configId != EGL_BAD_ATTRIBUTE);
    136 
    137     m_pbufferConfigId = configId;
    138 }
    139 
    140 EGLConfig EGLDisplayOpenVG::defaultPbufferConfig()
    141 {
    142     EGLConfig config;
    143     EGLint numConfigs;
    144 
    145     // Hopefully the client will have set the pbuffer config of its choice
    146     // by now - if not, use a 32-bit generic one as default.
    147     if (!m_pbufferConfigId) {
    148         static const EGLint configAttribs[] = {
    149             EGL_RED_SIZE, 8,
    150             EGL_GREEN_SIZE, 8,
    151             EGL_BLUE_SIZE, 8,
    152             EGL_ALPHA_SIZE, 8,
    153             EGL_ALPHA_MASK_SIZE, 1,
    154             EGL_LUMINANCE_SIZE, EGL_DONT_CARE,
    155             EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
    156             EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
    157             EGL_NONE
    158         };
    159         eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
    160     } else {
    161         const EGLint configAttribs[] = {
    162             EGL_CONFIG_ID, m_pbufferConfigId,
    163             EGL_NONE
    164         };
    165         eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
    166     }
    167 
    168     ASSERT_EGL_NO_ERROR();
    169     ASSERT(numConfigs == 1);
    170     return config;
    171 }
    172 
    173 void EGLDisplayOpenVG::setDefaultWindowConfig(const EGLConfig& config)
    174 {
    175     EGLint configId;
    176     EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId);
    177     ASSERT(success == EGL_TRUE);
    178     ASSERT(configId != EGL_BAD_ATTRIBUTE);
    179 
    180     m_windowConfigId = configId;
    181 }
    182 
    183 EGLConfig EGLDisplayOpenVG::defaultWindowConfig()
    184 {
    185     EGLConfig config;
    186     EGLint numConfigs;
    187 
    188     // Hopefully the client will have set the window config of its choice
    189     // by now - if not, use a 32-bit generic one as default.
    190     if (!m_windowConfigId) {
    191         static const EGLint configAttribs[] = {
    192             EGL_RED_SIZE, 8,
    193             EGL_GREEN_SIZE, 8,
    194             EGL_BLUE_SIZE, 8,
    195             EGL_ALPHA_SIZE, 8,
    196             EGL_ALPHA_MASK_SIZE, 1,
    197             EGL_LUMINANCE_SIZE, EGL_DONT_CARE,
    198             EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
    199             EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
    200             EGL_NONE
    201         };
    202         eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
    203     } else {
    204         const EGLint configAttribs[] = {
    205             EGL_CONFIG_ID, m_windowConfigId,
    206             EGL_NONE
    207         };
    208         eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
    209     }
    210 
    211     ASSERT_EGL_NO_ERROR();
    212     ASSERT(numConfigs == 1);
    213     return config;
    214 }
    215 
    216 SurfaceOpenVG* EGLDisplayOpenVG::sharedPlatformSurface()
    217 {
    218     if (!m_sharedPlatformSurface) {
    219         // The shared surface doesn't need to be drawn on, it just exists so
    220         // that we can always make the shared context current (which in turn is
    221         // the owner of long-living resources such as images, paths and fonts).
    222         // We'll just make the shared surface as small as possible: 1x1 pixel.
    223         EGLConfig config = defaultPbufferConfig();
    224         EGLSurface surface = createPbufferSurface(IntSize(1, 1), config);
    225 
    226         EGLContext context = eglCreateContext(m_display, config, EGL_NO_CONTEXT, 0);
    227         ASSERT_EGL_NO_ERROR();
    228         m_contexts.set(m_surfaceConfigIds.get(surface), context);
    229 
    230         m_sharedPlatformSurface = new SurfaceOpenVG;
    231         m_sharedPlatformSurface->m_eglDisplay = m_display;
    232         m_sharedPlatformSurface->m_eglSurface = surface;
    233         m_sharedPlatformSurface->m_eglContext = context;
    234         m_platformSurfaces.set(surface, m_sharedPlatformSurface); // a.k.a. registerPlatformSurface()
    235     }
    236     return m_sharedPlatformSurface;
    237 }
    238 
    239 EGLSurface EGLDisplayOpenVG::createPbufferSurface(const IntSize& size, const EGLConfig& config, EGLint* errorCode)
    240 {
    241     const EGLint attribList[] = {
    242         EGL_WIDTH, size.width(),
    243         EGL_HEIGHT, size.height(),
    244         EGL_NONE
    245     };
    246     EGLSurface surface = eglCreatePbufferSurface(m_display, config, attribList);
    247 
    248     if (errorCode)
    249         *errorCode = eglGetError();
    250     else
    251         ASSERT_EGL_NO_ERROR();
    252 
    253     if (surface == EGL_NO_SURFACE)
    254         return EGL_NO_SURFACE;
    255 
    256     EGLint surfaceConfigId;
    257     EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId);
    258     ASSERT(success == EGL_TRUE);
    259     ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
    260 
    261     ASSERT(!m_surfaceConfigIds.contains(surface));
    262     m_surfaceConfigIds.set(surface, surfaceConfigId);
    263     return surface;
    264 }
    265 
    266 EGLSurface EGLDisplayOpenVG::surfaceForWindow(EGLNativeWindowType wId, const EGLConfig& config)
    267 {
    268     if (m_windowSurfaces.contains(wId))
    269         return m_windowSurfaces.get(wId);
    270 
    271     EGLSurface surface = eglCreateWindowSurface(m_display, config, wId, 0);
    272     ASSERT_EGL_NO_ERROR();
    273 
    274     EGLint surfaceConfigId;
    275     EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId);
    276     ASSERT(success == EGL_TRUE);
    277     ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
    278 
    279     ASSERT(!m_surfaceConfigIds.contains(surface));
    280     m_surfaceConfigIds.set(surface, surfaceConfigId);
    281     return surface;
    282 }
    283 
    284 bool EGLDisplayOpenVG::surfacesCompatible(const EGLSurface& surface, const EGLSurface& otherSurface)
    285 {
    286     if (surface == EGL_NO_SURFACE || otherSurface == EGL_NO_SURFACE)
    287         return false;
    288 
    289     // Currently, we assume that all surfaces known to this object are
    290     // context-compatible to each other (which is reasonable to assume,
    291     // otherwise eglCreateContext() would fail with EGL_BAD_MATCH for shared
    292     // context compatibility anyways.
    293     return m_surfaceConfigIds.contains(surface) && m_surfaceConfigIds.contains(otherSurface);
    294 }
    295 
    296 void EGLDisplayOpenVG::destroySurface(const EGLSurface& surface)
    297 {
    298     ASSERT(surface != EGL_NO_SURFACE);
    299 
    300     if (eglGetCurrentSurface(EGL_DRAW) == surface) {
    301         eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    302         ASSERT_EGL_NO_ERROR();
    303     }
    304 
    305     // Destroy the context associated to the surface, if we already created one.
    306     if (m_surfaceConfigIds.contains(surface)) {
    307         EGLint surfaceConfigId = m_surfaceConfigIds.take(surface); // take = get and remove
    308         bool isContextReferenced = false;
    309 
    310         if (m_compatibleConfigIds.contains(surfaceConfigId))
    311             surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId);
    312 
    313         HashMap<EGLSurface, EGLint>::iterator end = m_surfaceConfigIds.end();
    314 
    315         // ...but only if there's no other surfaces associated to that context.
    316         for (HashMap<EGLSurface, EGLint>::iterator it = m_surfaceConfigIds.begin(); it != end; ++it) {
    317             if ((*it).second == surfaceConfigId) {
    318                 isContextReferenced = true;
    319                 break;
    320             }
    321         }
    322         if (!isContextReferenced && m_contexts.contains(surfaceConfigId)) {
    323             EGLContext context = m_contexts.take(surfaceConfigId);
    324             eglDestroyContext(m_display, context);
    325             ASSERT_EGL_NO_ERROR();
    326         }
    327     }
    328 
    329     m_platformSurfaces.remove(surface);
    330 
    331     HashMap<EGLNativeWindowType, EGLSurface>::iterator end = m_windowSurfaces.end();
    332     for (HashMap<EGLNativeWindowType, EGLSurface>::iterator it = m_windowSurfaces.begin(); it != end; ++it) {
    333         if ((*it).second == surface) {
    334             m_windowSurfaces.remove(it);
    335             break;
    336         }
    337     }
    338 
    339     eglDestroySurface(m_display, surface);
    340     ASSERT_EGL_NO_ERROR();
    341 }
    342 
    343 EGLContext EGLDisplayOpenVG::contextForSurface(const EGLSurface& surface)
    344 {
    345     ASSERT(surface != EGL_NO_SURFACE);
    346 
    347     if (m_platformSurfaces.contains(surface))
    348         return m_platformSurfaces.get(surface)->eglContext();
    349 
    350     eglBindAPI(EGL_OPENVG_API);
    351     ASSERT_EGL_NO_ERROR();
    352 
    353     EGLint surfaceConfigId;
    354 
    355     if (m_surfaceConfigIds.contains(surface))
    356         surfaceConfigId = m_surfaceConfigIds.get(surface);
    357     else {
    358         // Retrieve the same EGL config for context creation that was used to
    359         // create the the EGL surface.
    360         EGLBoolean success = eglQuerySurface(m_display, surface, EGL_CONFIG_ID, &surfaceConfigId);
    361         ASSERT(success == EGL_TRUE);
    362         ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
    363 
    364         m_surfaceConfigIds.set(surface, surfaceConfigId);
    365     }
    366 
    367     if (m_compatibleConfigIds.contains(surfaceConfigId))
    368         surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId);
    369 
    370     if (m_contexts.contains(surfaceConfigId))
    371         return m_contexts.get(surfaceConfigId);
    372 
    373     if (!m_sharedPlatformSurface) // shared context has not been created yet
    374         sharedPlatformSurface(); // creates the shared surface & context
    375 
    376     EGLDisplay currentDisplay = eglGetCurrentDisplay();
    377     EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ);
    378     EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
    379     EGLContext currentContext = eglGetCurrentContext();
    380 
    381     // Before creating a new context, let's try whether an existing one
    382     // is compatible with the surface. EGL doesn't give us a different way
    383     // to check context/surface compatibility than trying it out, so let's
    384     // do just that.
    385     HashMap<EGLint, EGLContext>::iterator end = m_contexts.end();
    386 
    387     for (HashMap<EGLint, EGLContext>::iterator it = m_contexts.begin(); it != end; ++it) {
    388         eglMakeCurrent(m_display, surface, surface, (*it).second);
    389         if (eglGetError() == EGL_SUCCESS) {
    390             // Restore previous surface/context.
    391             if (currentContext != EGL_NO_CONTEXT) {
    392                 eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext);
    393                 ASSERT_EGL_NO_ERROR();
    394             }
    395             // Cool, surface is compatible to one of our existing contexts.
    396             m_compatibleConfigIds.set(surfaceConfigId, (*it).first);
    397             return (*it).second;
    398         }
    399     }
    400     // Restore previous surface/context.
    401     if (currentContext != EGL_NO_CONTEXT) {
    402         eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext);
    403         ASSERT_EGL_NO_ERROR();
    404     }
    405 
    406     EGLConfig config;
    407     EGLint numConfigs;
    408 
    409     const EGLint configAttribs[] = {
    410         EGL_CONFIG_ID, surfaceConfigId,
    411         EGL_NONE
    412     };
    413 
    414     eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
    415     ASSERT_EGL_NO_ERROR();
    416     ASSERT(numConfigs == 1);
    417 
    418     // We share all of the images and paths amongst the different contexts,
    419     // so that they can be used in all of them. Resources that are created
    420     // while m_sharedPlatformSurface->context() is current will be
    421     // accessible from all other contexts, but are not restricted to the
    422     // lifetime of those contexts.
    423     EGLContext context = eglCreateContext(m_display, config, m_sharedPlatformSurface->eglContext(), 0);
    424     ASSERT_EGL_NO_ERROR();
    425 
    426     ASSERT(!m_contexts.contains(surfaceConfigId));
    427     m_contexts.set(surfaceConfigId, context);
    428     return context;
    429 }
    430 
    431 }
    432