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