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