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::createPbufferFromClientBuffer(
    267     EGLClientBuffer clientBuffer, EGLenum bufferType, const EGLConfig& config, EGLint* errorCode)
    268 {
    269     EGLSurface surface = eglCreatePbufferFromClientBuffer(m_display,
    270         bufferType, clientBuffer, config, 0 /* attribList */);
    271 
    272     if (errorCode)
    273         *errorCode = eglGetError();
    274     else
    275         ASSERT_EGL_NO_ERROR();
    276 
    277     if (surface == EGL_NO_SURFACE)
    278         return EGL_NO_SURFACE;
    279 
    280     EGLint surfaceConfigId;
    281     EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId);
    282     ASSERT(success == EGL_TRUE);
    283     ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
    284 
    285     ASSERT(!m_surfaceConfigIds.contains(surface));
    286     m_surfaceConfigIds.set(surface, surfaceConfigId);
    287     return surface;
    288 }
    289 
    290 EGLSurface EGLDisplayOpenVG::surfaceForWindow(EGLNativeWindowType wId, const EGLConfig& config)
    291 {
    292     if (m_windowSurfaces.contains(wId))
    293         return m_windowSurfaces.get(wId);
    294 
    295     EGLSurface surface = eglCreateWindowSurface(m_display, config, wId, 0);
    296     ASSERT_EGL_NO_ERROR();
    297 
    298     EGLint surfaceConfigId;
    299     EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId);
    300     ASSERT(success == EGL_TRUE);
    301     ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
    302 
    303     ASSERT(!m_surfaceConfigIds.contains(surface));
    304     m_surfaceConfigIds.set(surface, surfaceConfigId);
    305     return surface;
    306 }
    307 
    308 bool EGLDisplayOpenVG::surfacesCompatible(const EGLSurface& surface, const EGLSurface& otherSurface)
    309 {
    310     if (surface == EGL_NO_SURFACE || otherSurface == EGL_NO_SURFACE)
    311         return false;
    312 
    313     // Currently, we assume that all surfaces known to this object are
    314     // context-compatible to each other (which is reasonable to assume,
    315     // otherwise eglCreateContext() would fail with EGL_BAD_MATCH for shared
    316     // context compatibility anyways.
    317     return m_surfaceConfigIds.contains(surface) && m_surfaceConfigIds.contains(otherSurface);
    318 }
    319 
    320 void EGLDisplayOpenVG::destroySurface(const EGLSurface& surface)
    321 {
    322     ASSERT(surface != EGL_NO_SURFACE);
    323 
    324     if (eglGetCurrentSurface(EGL_DRAW) == surface) {
    325         eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    326         ASSERT_EGL_NO_ERROR();
    327     }
    328 
    329     // Destroy the context associated to the surface, if we already created one.
    330     if (m_surfaceConfigIds.contains(surface)) {
    331         EGLint surfaceConfigId = m_surfaceConfigIds.take(surface); // take = get and remove
    332         bool isContextReferenced = false;
    333 
    334         if (m_compatibleConfigIds.contains(surfaceConfigId))
    335             surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId);
    336 
    337         HashMap<EGLSurface, EGLint>::iterator end = m_surfaceConfigIds.end();
    338 
    339         // ...but only if there's no other surfaces associated to that context.
    340         for (HashMap<EGLSurface, EGLint>::iterator it = m_surfaceConfigIds.begin(); it != end; ++it) {
    341             if ((*it).second == surfaceConfigId) {
    342                 isContextReferenced = true;
    343                 break;
    344             }
    345         }
    346         if (!isContextReferenced && m_contexts.contains(surfaceConfigId)) {
    347             EGLContext context = m_contexts.take(surfaceConfigId);
    348             eglDestroyContext(m_display, context);
    349             ASSERT_EGL_NO_ERROR();
    350         }
    351     }
    352 
    353     m_platformSurfaces.remove(surface);
    354 
    355     HashMap<EGLNativeWindowType, EGLSurface>::iterator end = m_windowSurfaces.end();
    356     for (HashMap<EGLNativeWindowType, EGLSurface>::iterator it = m_windowSurfaces.begin(); it != end; ++it) {
    357         if ((*it).second == surface) {
    358             m_windowSurfaces.remove(it);
    359             break;
    360         }
    361     }
    362 
    363     eglDestroySurface(m_display, surface);
    364     ASSERT_EGL_NO_ERROR();
    365 }
    366 
    367 EGLContext EGLDisplayOpenVG::contextForSurface(const EGLSurface& surface)
    368 {
    369     ASSERT(surface != EGL_NO_SURFACE);
    370 
    371     if (m_platformSurfaces.contains(surface))
    372         return m_platformSurfaces.get(surface)->eglContext();
    373 
    374     eglBindAPI(EGL_OPENVG_API);
    375     ASSERT_EGL_NO_ERROR();
    376 
    377     EGLint surfaceConfigId;
    378 
    379     if (m_surfaceConfigIds.contains(surface))
    380         surfaceConfigId = m_surfaceConfigIds.get(surface);
    381     else {
    382         // Retrieve the same EGL config for context creation that was used to
    383         // create the the EGL surface.
    384         EGLBoolean success = eglQuerySurface(m_display, surface, EGL_CONFIG_ID, &surfaceConfigId);
    385         ASSERT(success == EGL_TRUE);
    386         ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
    387 
    388         m_surfaceConfigIds.set(surface, surfaceConfigId);
    389     }
    390 
    391     if (m_compatibleConfigIds.contains(surfaceConfigId))
    392         surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId);
    393 
    394     if (m_contexts.contains(surfaceConfigId))
    395         return m_contexts.get(surfaceConfigId);
    396 
    397     if (!m_sharedPlatformSurface) // shared context has not been created yet
    398         sharedPlatformSurface(); // creates the shared surface & context
    399 
    400     EGLDisplay currentDisplay = eglGetCurrentDisplay();
    401     EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ);
    402     EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
    403     EGLContext currentContext = eglGetCurrentContext();
    404 
    405     // Before creating a new context, let's try whether an existing one
    406     // is compatible with the surface. EGL doesn't give us a different way
    407     // to check context/surface compatibility than trying it out, so let's
    408     // do just that.
    409     HashMap<EGLint, EGLContext>::iterator end = m_contexts.end();
    410 
    411     for (HashMap<EGLint, EGLContext>::iterator it = m_contexts.begin(); it != end; ++it) {
    412         eglMakeCurrent(m_display, surface, surface, (*it).second);
    413         if (eglGetError() == EGL_SUCCESS) {
    414             // Restore previous surface/context.
    415             if (currentContext != EGL_NO_CONTEXT) {
    416                 eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext);
    417                 ASSERT_EGL_NO_ERROR();
    418             }
    419             // Cool, surface is compatible to one of our existing contexts.
    420             m_compatibleConfigIds.set(surfaceConfigId, (*it).first);
    421             return (*it).second;
    422         }
    423     }
    424     // Restore previous surface/context.
    425     if (currentContext != EGL_NO_CONTEXT) {
    426         eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext);
    427         ASSERT_EGL_NO_ERROR();
    428     }
    429 
    430     EGLConfig config;
    431     EGLint numConfigs;
    432 
    433     const EGLint configAttribs[] = {
    434         EGL_CONFIG_ID, surfaceConfigId,
    435         EGL_NONE
    436     };
    437 
    438     eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
    439     ASSERT_EGL_NO_ERROR();
    440     ASSERT(numConfigs == 1);
    441 
    442     // We share all of the images and paths amongst the different contexts,
    443     // so that they can be used in all of them. Resources that are created
    444     // while m_sharedPlatformSurface->context() is current will be
    445     // accessible from all other contexts, but are not restricted to the
    446     // lifetime of those contexts.
    447     EGLContext context = eglCreateContext(m_display, config, m_sharedPlatformSurface->eglContext(), 0);
    448     ASSERT_EGL_NO_ERROR();
    449 
    450     ASSERT(!m_contexts.contains(surfaceConfigId));
    451     m_contexts.set(surfaceConfigId, context);
    452     return context;
    453 }
    454 
    455 }
    456