opengl_context_egl.cpp (18946B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) 3 4 #include "opengl_context_egl.h" 5 6 #include "common/assert.h" 7 #include "common/dynamic_library.h" 8 #include "common/error.h" 9 #include "common/log.h" 10 11 #include <atomic> 12 #include <cstring> 13 #include <optional> 14 #include <vector> 15 16 Log_SetChannel(OpenGLContext); 17 18 static DynamicLibrary s_egl_library; 19 static std::atomic_uint32_t s_egl_refcount = 0; 20 21 static bool LoadEGL() 22 { 23 // We're not going to be calling this from multiple threads concurrently. 24 // So, not wrapping this in a mutex should be fine. 25 if (s_egl_refcount.fetch_add(1, std::memory_order_acq_rel) == 0) 26 { 27 DebugAssert(!s_egl_library.IsOpen()); 28 29 std::string egl_libname = DynamicLibrary::GetVersionedFilename("libEGL"); 30 INFO_LOG("Loading EGL from {}...", egl_libname); 31 32 Error error; 33 if (!s_egl_library.Open(egl_libname.c_str(), &error)) 34 { 35 // Try versioned. 36 egl_libname = DynamicLibrary::GetVersionedFilename("libEGL", 1); 37 INFO_LOG("Loading EGL from {}...", egl_libname); 38 if (!s_egl_library.Open(egl_libname.c_str(), &error)) 39 ERROR_LOG("Failed to load EGL: {}", error.GetDescription()); 40 } 41 } 42 43 return s_egl_library.IsOpen(); 44 } 45 46 static void UnloadEGL() 47 { 48 DebugAssert(s_egl_refcount.load(std::memory_order_acquire) > 0); 49 if (s_egl_refcount.fetch_sub(1, std::memory_order_acq_rel) == 1) 50 { 51 INFO_LOG("Unloading EGL."); 52 s_egl_library.Close(); 53 } 54 } 55 56 static bool LoadGLADEGL(EGLDisplay display, Error* error) 57 { 58 const int version = 59 gladLoadEGL(display, [](const char* name) { return (GLADapiproc)s_egl_library.GetSymbolAddress(name); }); 60 if (version == 0) 61 { 62 Error::SetStringView(error, "Loading GLAD EGL functions failed"); 63 return false; 64 } 65 66 DEV_LOG("GLAD EGL Version: {}.{}", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); 67 return true; 68 } 69 70 OpenGLContextEGL::OpenGLContextEGL(const WindowInfo& wi) : OpenGLContext(wi) 71 { 72 LoadEGL(); 73 } 74 75 OpenGLContextEGL::~OpenGLContextEGL() 76 { 77 DestroySurface(); 78 DestroyContext(); 79 UnloadEGL(); 80 } 81 82 std::unique_ptr<OpenGLContext> OpenGLContextEGL::Create(const WindowInfo& wi, std::span<const Version> versions_to_try, 83 Error* error) 84 { 85 std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>(wi); 86 if (!context->Initialize(versions_to_try, error)) 87 return nullptr; 88 89 return context; 90 } 91 92 bool OpenGLContextEGL::Initialize(std::span<const Version> versions_to_try, Error* error) 93 { 94 if (!LoadGLADEGL(EGL_NO_DISPLAY, error)) 95 return false; 96 97 m_display = GetPlatformDisplay(error); 98 if (m_display == EGL_NO_DISPLAY) 99 return false; 100 101 int egl_major, egl_minor; 102 if (!eglInitialize(m_display, &egl_major, &egl_minor)) 103 { 104 const int gerror = static_cast<int>(eglGetError()); 105 Error::SetStringFmt(error, "eglInitialize() failed: {} (0x{:X})", gerror, gerror); 106 return false; 107 } 108 109 DEV_LOG("eglInitialize() version: {}.{}", egl_major, egl_minor); 110 111 // Re-initialize EGL/GLAD. 112 if (!LoadGLADEGL(m_display, error)) 113 return false; 114 115 if (!GLAD_EGL_KHR_surfaceless_context) 116 WARNING_LOG("EGL implementation does not support surfaceless contexts, emulating with pbuffers"); 117 118 for (const Version& cv : versions_to_try) 119 { 120 if (CreateContextAndSurface(cv, nullptr, true)) 121 return true; 122 } 123 124 Error::SetStringView(error, "Failed to create any context versions"); 125 return false; 126 } 127 128 EGLDisplay OpenGLContextEGL::GetPlatformDisplay(Error* error) 129 { 130 EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, "EGL_MESA_platform_surfaceless"); 131 if (dpy == EGL_NO_DISPLAY) 132 dpy = GetFallbackDisplay(error); 133 134 return dpy; 135 } 136 137 EGLSurface OpenGLContextEGL::CreatePlatformSurface(EGLConfig config, void* win, Error* error) 138 { 139 EGLSurface surface = TryCreatePlatformSurface(config, win, error); 140 if (!surface) 141 surface = CreateFallbackSurface(config, win, error); 142 return surface; 143 } 144 145 EGLDisplay OpenGLContextEGL::TryGetPlatformDisplay(EGLenum platform, const char* platform_ext) 146 { 147 const char* extensions_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); 148 if (!extensions_str) 149 { 150 ERROR_LOG("No extensions supported."); 151 return EGL_NO_DISPLAY; 152 } 153 154 EGLDisplay dpy = EGL_NO_DISPLAY; 155 if (platform_ext && std::strstr(extensions_str, platform_ext)) 156 { 157 DEV_LOG("Using EGL platform {}.", platform_ext); 158 159 PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display_ext = 160 (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); 161 if (get_platform_display_ext) 162 { 163 dpy = get_platform_display_ext(platform, m_wi.display_connection, nullptr); 164 m_use_ext_platform_base = (dpy != EGL_NO_DISPLAY); 165 if (!m_use_ext_platform_base) 166 { 167 const EGLint err = eglGetError(); 168 ERROR_LOG("eglGetPlatformDisplayEXT() failed: {} (0x{:X})", err, err); 169 } 170 } 171 else 172 { 173 WARNING_LOG("eglGetPlatformDisplayEXT() was not found"); 174 } 175 } 176 else 177 { 178 WARNING_LOG("{} is not supported.", platform_ext); 179 } 180 181 return dpy; 182 } 183 184 EGLSurface OpenGLContextEGL::TryCreatePlatformSurface(EGLConfig config, void* win, Error* error) 185 { 186 EGLSurface surface = EGL_NO_SURFACE; 187 if (m_use_ext_platform_base) 188 { 189 PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC create_platform_window_surface_ext = 190 (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); 191 if (create_platform_window_surface_ext) 192 { 193 surface = create_platform_window_surface_ext(m_display, config, win, nullptr); 194 if (surface == EGL_NO_SURFACE) 195 { 196 const EGLint err = eglGetError(); 197 Error::SetStringFmt(error, "eglCreatePlatformWindowSurfaceEXT() failed: {} (0x{:X})", err, err); 198 } 199 } 200 else 201 { 202 ERROR_LOG("eglCreatePlatformWindowSurfaceEXT() not found"); 203 } 204 } 205 206 return surface; 207 } 208 209 EGLDisplay OpenGLContextEGL::GetFallbackDisplay(Error* error) 210 { 211 WARNING_LOG("Using fallback eglGetDisplay() path."); 212 213 EGLDisplay dpy = eglGetDisplay(m_wi.display_connection); 214 if (dpy == EGL_NO_DISPLAY) 215 { 216 const EGLint err = eglGetError(); 217 Error::SetStringFmt(error, "eglGetDisplay() failed: {} (0x{:X})", err, err); 218 } 219 220 return dpy; 221 } 222 223 EGLSurface OpenGLContextEGL::CreateFallbackSurface(EGLConfig config, void* win, Error* error) 224 { 225 WARNING_LOG("Using fallback eglCreateWindowSurface() path."); 226 227 EGLSurface surface = eglCreateWindowSurface(m_display, config, (EGLNativeWindowType)win, nullptr); 228 if (surface == EGL_NO_SURFACE) 229 { 230 const EGLint err = eglGetError(); 231 Error::SetStringFmt(error, "eglCreateWindowSurface() failed: {} (0x{:X})", err, err); 232 } 233 234 return surface; 235 } 236 237 void* OpenGLContextEGL::GetProcAddress(const char* name) 238 { 239 return reinterpret_cast<void*>(eglGetProcAddress(name)); 240 } 241 242 bool OpenGLContextEGL::ChangeSurface(const WindowInfo& new_wi) 243 { 244 const bool was_current = (eglGetCurrentContext() == m_context); 245 if (was_current) 246 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 247 248 if (m_surface != EGL_NO_SURFACE) 249 { 250 eglDestroySurface(m_display, m_surface); 251 m_surface = EGL_NO_SURFACE; 252 } 253 254 m_wi = new_wi; 255 if (!CreateSurface()) 256 return false; 257 258 if (was_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context)) 259 { 260 ERROR_LOG("Failed to make context current again after surface change"); 261 return false; 262 } 263 264 return true; 265 } 266 267 void OpenGLContextEGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/) 268 { 269 if (new_surface_width == 0 && new_surface_height == 0) 270 { 271 EGLint surface_width, surface_height; 272 if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) && 273 eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height)) [[likely]] 274 { 275 m_wi.surface_width = static_cast<u32>(surface_width); 276 m_wi.surface_height = static_cast<u32>(surface_height); 277 return; 278 } 279 else 280 { 281 ERROR_LOG("eglQuerySurface() failed: 0x{:X}", eglGetError()); 282 } 283 } 284 285 m_wi.surface_width = new_surface_width; 286 m_wi.surface_height = new_surface_height; 287 } 288 289 bool OpenGLContextEGL::SwapBuffers() 290 { 291 return eglSwapBuffers(m_display, m_surface); 292 } 293 294 bool OpenGLContextEGL::IsCurrent() const 295 { 296 return m_context && eglGetCurrentContext() == m_context; 297 } 298 299 bool OpenGLContextEGL::MakeCurrent() 300 { 301 if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context)) [[unlikely]] 302 { 303 ERROR_LOG("eglMakeCurrent() failed: 0x{:X}", eglGetError()); 304 return false; 305 } 306 307 return true; 308 } 309 310 bool OpenGLContextEGL::DoneCurrent() 311 { 312 return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 313 } 314 315 bool OpenGLContextEGL::SupportsNegativeSwapInterval() const 316 { 317 return m_supports_negative_swap_interval; 318 } 319 320 bool OpenGLContextEGL::SetSwapInterval(s32 interval) 321 { 322 return eglSwapInterval(m_display, interval); 323 } 324 325 std::unique_ptr<OpenGLContext> OpenGLContextEGL::CreateSharedContext(const WindowInfo& wi, Error* error) 326 { 327 std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>(wi); 328 context->m_display = m_display; 329 330 if (!context->CreateContextAndSurface(m_version, m_context, false)) 331 { 332 Error::SetStringView(error, "Failed to create context/surface"); 333 return nullptr; 334 } 335 336 return context; 337 } 338 339 bool OpenGLContextEGL::CreateSurface() 340 { 341 if (m_wi.type == WindowInfo::Type::Surfaceless) 342 { 343 if (GLAD_EGL_KHR_surfaceless_context) 344 return true; 345 else 346 return CreatePBufferSurface(); 347 } 348 349 Error error; 350 m_surface = CreatePlatformSurface(m_config, m_wi.window_handle, &error); 351 if (m_surface == EGL_NO_SURFACE) 352 { 353 ERROR_LOG("Failed to create platform surface: {}", error.GetDescription()); 354 return false; 355 } 356 357 // Some implementations may require the size to be queried at runtime. 358 EGLint surface_width, surface_height; 359 if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) && 360 eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height)) 361 { 362 m_wi.surface_width = static_cast<u32>(surface_width); 363 m_wi.surface_height = static_cast<u32>(surface_height); 364 } 365 else 366 { 367 ERROR_LOG("eglQuerySurface() failed: 0x{:X}", eglGetError()); 368 } 369 370 m_wi.surface_format = GetSurfaceTextureFormat(); 371 372 return true; 373 } 374 375 bool OpenGLContextEGL::CreatePBufferSurface() 376 { 377 const u32 width = std::max<u32>(m_wi.surface_width, 1); 378 const u32 height = std::max<u32>(m_wi.surface_height, 1); 379 380 // TODO: Format 381 EGLint attrib_list[] = { 382 EGL_WIDTH, static_cast<EGLint>(width), EGL_HEIGHT, static_cast<EGLint>(height), EGL_NONE, 383 }; 384 385 m_surface = eglCreatePbufferSurface(m_display, m_config, attrib_list); 386 if (!m_surface) [[unlikely]] 387 { 388 ERROR_LOG("eglCreatePbufferSurface() failed: {}", eglGetError()); 389 return false; 390 } 391 392 m_wi.surface_format = GetSurfaceTextureFormat(); 393 394 DEV_LOG("Created {}x{} pbuffer surface", width, height); 395 return true; 396 } 397 398 bool OpenGLContextEGL::CheckConfigSurfaceFormat(EGLConfig config, GPUTexture::Format format) 399 { 400 int red_size, green_size, blue_size, alpha_size; 401 if (!eglGetConfigAttrib(m_display, config, EGL_RED_SIZE, &red_size) || 402 !eglGetConfigAttrib(m_display, config, EGL_GREEN_SIZE, &green_size) || 403 !eglGetConfigAttrib(m_display, config, EGL_BLUE_SIZE, &blue_size) || 404 !eglGetConfigAttrib(m_display, config, EGL_ALPHA_SIZE, &alpha_size)) 405 { 406 return false; 407 } 408 409 switch (format) 410 { 411 case GPUTexture::Format::RGBA8: 412 return (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8); 413 414 case GPUTexture::Format::RGB565: 415 return (red_size == 5 && green_size == 6 && blue_size == 5); 416 417 case GPUTexture::Format::RGBA5551: 418 return (red_size == 5 && green_size == 5 && blue_size == 5 && alpha_size == 1); 419 420 case GPUTexture::Format::Unknown: 421 return true; 422 423 default: 424 return false; 425 } 426 } 427 428 GPUTexture::Format OpenGLContextEGL::GetSurfaceTextureFormat() const 429 { 430 int red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0; 431 eglGetConfigAttrib(m_display, m_config, EGL_RED_SIZE, &red_size); 432 eglGetConfigAttrib(m_display, m_config, EGL_GREEN_SIZE, &green_size); 433 eglGetConfigAttrib(m_display, m_config, EGL_BLUE_SIZE, &blue_size); 434 eglGetConfigAttrib(m_display, m_config, EGL_ALPHA_SIZE, &alpha_size); 435 436 if (red_size == 5 && green_size == 6 && blue_size == 5) 437 { 438 return GPUTexture::Format::RGB565; 439 } 440 else if (red_size == 5 && green_size == 5 && blue_size == 5 && alpha_size == 1) 441 { 442 return GPUTexture::Format::RGBA5551; 443 } 444 else if (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8) 445 { 446 return GPUTexture::Format::RGBA8; 447 } 448 else 449 { 450 ERROR_LOG("Unknown surface format: R={}, G={}, B={}, A={}", red_size, green_size, blue_size, alpha_size); 451 return GPUTexture::Format::RGBA8; 452 } 453 } 454 455 void OpenGLContextEGL::DestroyContext() 456 { 457 if (eglGetCurrentContext() == m_context) 458 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 459 460 if (m_context != EGL_NO_CONTEXT) 461 { 462 eglDestroyContext(m_display, m_context); 463 m_context = EGL_NO_CONTEXT; 464 } 465 } 466 467 void OpenGLContextEGL::DestroySurface() 468 { 469 if (eglGetCurrentSurface(EGL_DRAW) == m_surface) 470 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 471 472 if (m_surface != EGL_NO_SURFACE) 473 { 474 eglDestroySurface(m_display, m_surface); 475 m_surface = EGL_NO_SURFACE; 476 } 477 } 478 479 bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_context) 480 { 481 DEV_LOG("Trying version {}.{} ({})", version.major_version, version.minor_version, 482 version.profile == OpenGLContext::Profile::ES ? 483 "ES" : 484 (version.profile == OpenGLContext::Profile::Core ? "Core" : "None")); 485 int surface_attribs[16] = { 486 EGL_RENDERABLE_TYPE, 487 (version.profile == Profile::ES) ? 488 ((version.major_version >= 3) ? EGL_OPENGL_ES3_BIT : 489 ((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT)) : 490 EGL_OPENGL_BIT, 491 EGL_SURFACE_TYPE, 492 (m_wi.type != WindowInfo::Type::Surfaceless) ? EGL_WINDOW_BIT : 0, 493 }; 494 int nsurface_attribs = 4; 495 496 const GPUTexture::Format format = m_wi.surface_format; 497 if (format == GPUTexture::Format::Unknown) 498 { 499 WARNING_LOG("Surface format not specified, assuming RGBA8."); 500 m_wi.surface_format = GPUTexture::Format::RGBA8; 501 } 502 503 switch (m_wi.surface_format) 504 { 505 case GPUTexture::Format::RGBA8: 506 surface_attribs[nsurface_attribs++] = EGL_RED_SIZE; 507 surface_attribs[nsurface_attribs++] = 8; 508 surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE; 509 surface_attribs[nsurface_attribs++] = 8; 510 surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE; 511 surface_attribs[nsurface_attribs++] = 8; 512 surface_attribs[nsurface_attribs++] = EGL_ALPHA_SIZE; 513 surface_attribs[nsurface_attribs++] = 8; 514 break; 515 516 case GPUTexture::Format::RGB565: 517 surface_attribs[nsurface_attribs++] = EGL_RED_SIZE; 518 surface_attribs[nsurface_attribs++] = 5; 519 surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE; 520 surface_attribs[nsurface_attribs++] = 6; 521 surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE; 522 surface_attribs[nsurface_attribs++] = 5; 523 break; 524 525 case GPUTexture::Format::Unknown: 526 break; 527 528 default: 529 UnreachableCode(); 530 break; 531 } 532 533 surface_attribs[nsurface_attribs++] = EGL_NONE; 534 surface_attribs[nsurface_attribs++] = 0; 535 536 EGLint num_configs; 537 if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) || num_configs == 0) 538 { 539 ERROR_LOG("eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError())); 540 return false; 541 } 542 543 std::vector<EGLConfig> configs(static_cast<u32>(num_configs)); 544 if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs)) 545 { 546 ERROR_LOG("eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError())); 547 return false; 548 } 549 configs.resize(static_cast<u32>(num_configs)); 550 551 std::optional<EGLConfig> config; 552 for (EGLConfig check_config : configs) 553 { 554 if (CheckConfigSurfaceFormat(check_config, m_wi.surface_format)) 555 { 556 config = check_config; 557 break; 558 } 559 } 560 561 if (!config.has_value()) 562 { 563 WARNING_LOG("No EGL configs matched exactly, using first."); 564 config = configs.front(); 565 } 566 567 int attribs[8]; 568 int nattribs = 0; 569 if (version.profile != Profile::NoProfile) 570 { 571 attribs[nattribs++] = EGL_CONTEXT_MAJOR_VERSION; 572 attribs[nattribs++] = version.major_version; 573 attribs[nattribs++] = EGL_CONTEXT_MINOR_VERSION; 574 attribs[nattribs++] = version.minor_version; 575 } 576 attribs[nattribs++] = EGL_NONE; 577 attribs[nattribs++] = 0; 578 579 if (!eglBindAPI((version.profile == Profile::ES) ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) 580 { 581 ERROR_LOG("eglBindAPI({}) failed", (version.profile == Profile::ES) ? "EGL_OPENGL_ES_API" : "EGL_OPENGL_API"); 582 return false; 583 } 584 585 m_context = eglCreateContext(m_display, config.value(), share_context, attribs); 586 if (!m_context) 587 { 588 ERROR_LOG("eglCreateContext() failed: 0x{:x}", static_cast<unsigned>(eglGetError())); 589 return false; 590 } 591 592 INFO_LOG("Got version {}.{} ({})", version.major_version, version.minor_version, 593 version.profile == OpenGLContext::Profile::ES ? 594 "ES" : 595 (version.profile == OpenGLContext::Profile::Core ? "Core" : "None")); 596 597 EGLint min_swap_interval, max_swap_interval; 598 m_supports_negative_swap_interval = false; 599 if (eglGetConfigAttrib(m_display, config.value(), EGL_MIN_SWAP_INTERVAL, &min_swap_interval) && 600 eglGetConfigAttrib(m_display, config.value(), EGL_MAX_SWAP_INTERVAL, &max_swap_interval)) 601 { 602 VERBOSE_LOG("EGL_MIN_SWAP_INTERVAL = {}", min_swap_interval); 603 VERBOSE_LOG("EGL_MAX_SWAP_INTERVAL = {}", max_swap_interval); 604 m_supports_negative_swap_interval = (min_swap_interval <= -1); 605 } 606 607 INFO_LOG("Negative swap interval/tear-control is {}supported", m_supports_negative_swap_interval ? "" : "NOT "); 608 609 m_config = config.value(); 610 m_version = version; 611 return true; 612 } 613 614 bool OpenGLContextEGL::CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current) 615 { 616 if (!CreateContext(version, share_context)) 617 return false; 618 619 if (!CreateSurface()) 620 { 621 ERROR_LOG("Failed to create surface for context"); 622 eglDestroyContext(m_display, m_context); 623 m_context = EGL_NO_CONTEXT; 624 return false; 625 } 626 627 if (make_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context)) 628 { 629 ERROR_LOG("eglMakeCurrent() failed: 0x{:X}", eglGetError()); 630 if (m_surface != EGL_NO_SURFACE) 631 { 632 eglDestroySurface(m_display, m_surface); 633 m_surface = EGL_NO_SURFACE; 634 } 635 eglDestroyContext(m_display, m_context); 636 m_context = EGL_NO_CONTEXT; 637 return false; 638 } 639 640 return true; 641 }