context.cc (23751B)
1 //======================================================================== 2 // GLFW 3.3 - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2002-2006 Marcus Geelnard 5 // Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org> 6 // 7 // This software is provided 'as-is', without any express or implied 8 // warranty. In no event will the authors be held liable for any damages 9 // arising from the use of this software. 10 // 11 // Permission is granted to anyone to use this software for any purpose, 12 // including commercial applications, and to alter it and redistribute it 13 // freely, subject to the following restrictions: 14 // 15 // 1. The origin of this software must not be misrepresented; you must not 16 // claim that you wrote the original software. If you use this software 17 // in a product, an acknowledgment in the product documentation would 18 // be appreciated but is not required. 19 // 20 // 2. Altered source versions must be plainly marked as such, and must not 21 // be misrepresented as being the original software. 22 // 23 // 3. This notice may not be removed or altered from any source 24 // distribution. 25 // 26 //======================================================================== 27 28 #include "internal.h" 29 30 #include <assert.h> 31 #include <stdio.h> 32 #include <string.h> 33 #include <limits.h> 34 #include <stdio.h> 35 36 37 ////////////////////////////////////////////////////////////////////////// 38 ////// GLFW internal API ////// 39 ////////////////////////////////////////////////////////////////////////// 40 41 GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) 42 { 43 if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && 44 ctxconfig->source != GLFW_EGL_CONTEXT_API && 45 ctxconfig->source != GLFW_OSMESA_CONTEXT_API) 46 { 47 _glfwInputError(GLFW_INVALID_ENUM, 48 "Invalid context creation API 0x%08X", 49 ctxconfig->source); 50 return GLFW_FALSE; 51 } 52 53 if (ctxconfig->client != GLFW_NO_API && 54 ctxconfig->client != GLFW_OPENGL_API && 55 ctxconfig->client != GLFW_OPENGL_ES_API) 56 { 57 _glfwInputError(GLFW_INVALID_ENUM, 58 "Invalid client API 0x%08X", 59 ctxconfig->client); 60 return GLFW_FALSE; 61 } 62 63 if (ctxconfig->client == GLFW_OPENGL_API) 64 { 65 if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || 66 (ctxconfig->major == 1 && ctxconfig->minor > 5) || 67 (ctxconfig->major == 2 && ctxconfig->minor > 1) || 68 (ctxconfig->major == 3 && ctxconfig->minor > 3)) 69 { 70 // OpenGL 1.0 is the smallest valid version 71 // OpenGL 1.x series ended with version 1.5 72 // OpenGL 2.x series ended with version 2.1 73 // OpenGL 3.x series ended with version 3.3 74 // For now, let everything else through 75 76 _glfwInputError(GLFW_INVALID_VALUE, 77 "Invalid OpenGL version %i.%i", 78 ctxconfig->major, ctxconfig->minor); 79 return GLFW_FALSE; 80 } 81 82 if (ctxconfig->profile) 83 { 84 if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE && 85 ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) 86 { 87 _glfwInputError(GLFW_INVALID_ENUM, 88 "Invalid OpenGL profile 0x%08X", 89 ctxconfig->profile); 90 return GLFW_FALSE; 91 } 92 93 if (ctxconfig->major <= 2 || 94 (ctxconfig->major == 3 && ctxconfig->minor < 2)) 95 { 96 // Desktop OpenGL context profiles are only defined for version 3.2 97 // and above 98 99 _glfwInputError(GLFW_INVALID_VALUE, 100 "Context profiles are only defined for OpenGL version 3.2 and above"); 101 return GLFW_FALSE; 102 } 103 } 104 105 if (ctxconfig->forward && ctxconfig->major <= 2) 106 { 107 // Forward-compatible contexts are only defined for OpenGL version 3.0 and above 108 _glfwInputError(GLFW_INVALID_VALUE, 109 "Forward-compatibility is only defined for OpenGL version 3.0 and above"); 110 return GLFW_FALSE; 111 } 112 } 113 else if (ctxconfig->client == GLFW_OPENGL_ES_API) 114 { 115 if (ctxconfig->major < 1 || ctxconfig->minor < 0 || 116 (ctxconfig->major == 1 && ctxconfig->minor > 1) || 117 (ctxconfig->major == 2 && ctxconfig->minor > 0)) 118 { 119 // OpenGL ES 1.0 is the smallest valid version 120 // OpenGL ES 1.x series ended with version 1.1 121 // OpenGL ES 2.x series ended with version 2.0 122 // For now, let everything else through 123 124 _glfwInputError(GLFW_INVALID_VALUE, 125 "Invalid OpenGL ES version %i.%i", 126 ctxconfig->major, ctxconfig->minor); 127 return GLFW_FALSE; 128 } 129 } 130 131 if (ctxconfig->robustness) 132 { 133 if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION && 134 ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) 135 { 136 _glfwInputError(GLFW_INVALID_ENUM, 137 "Invalid context robustness mode 0x%08X", 138 ctxconfig->robustness); 139 return GLFW_FALSE; 140 } 141 } 142 143 if (ctxconfig->release) 144 { 145 if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE && 146 ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) 147 { 148 _glfwInputError(GLFW_INVALID_ENUM, 149 "Invalid context release behavior 0x%08X", 150 ctxconfig->release); 151 return GLFW_FALSE; 152 } 153 } 154 155 return GLFW_TRUE; 156 } 157 158 const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, 159 const _GLFWfbconfig* alternatives, 160 unsigned int count) 161 { 162 unsigned int i; 163 unsigned int missing, leastMissing = UINT_MAX; 164 unsigned int colorDiff, leastColorDiff = UINT_MAX; 165 unsigned int extraDiff, leastExtraDiff = UINT_MAX; 166 const _GLFWfbconfig* current; 167 const _GLFWfbconfig* closest = NULL; 168 169 for (i = 0; i < count; i++) 170 { 171 current = alternatives + i; 172 173 if (desired->stereo > 0 && current->stereo == 0) 174 { 175 // Stereo is a hard constraint 176 continue; 177 } 178 179 if (desired->doublebuffer != current->doublebuffer) 180 { 181 // Double buffering is a hard constraint 182 continue; 183 } 184 185 // Count number of missing buffers 186 { 187 missing = 0; 188 189 if (desired->alphaBits > 0 && current->alphaBits == 0) 190 missing++; 191 192 if (desired->depthBits > 0 && current->depthBits == 0) 193 missing++; 194 195 if (desired->stencilBits > 0 && current->stencilBits == 0) 196 missing++; 197 198 if (desired->auxBuffers > 0 && 199 current->auxBuffers < desired->auxBuffers) 200 { 201 missing += desired->auxBuffers - current->auxBuffers; 202 } 203 204 if (desired->samples > 0 && current->samples == 0) 205 { 206 // Technically, several multisampling buffers could be 207 // involved, but that's a lower level implementation detail and 208 // not important to us here, so we count them as one 209 missing++; 210 } 211 } 212 213 // These polynomials make many small channel size differences matter 214 // less than one large channel size difference 215 216 // Calculate color channel size difference value 217 { 218 colorDiff = 0; 219 220 if (desired->redBits != GLFW_DONT_CARE) 221 { 222 colorDiff += (desired->redBits - current->redBits) * 223 (desired->redBits - current->redBits); 224 } 225 226 if (desired->greenBits != GLFW_DONT_CARE) 227 { 228 colorDiff += (desired->greenBits - current->greenBits) * 229 (desired->greenBits - current->greenBits); 230 } 231 232 if (desired->blueBits != GLFW_DONT_CARE) 233 { 234 colorDiff += (desired->blueBits - current->blueBits) * 235 (desired->blueBits - current->blueBits); 236 } 237 } 238 239 // Calculate non-color channel size difference value 240 { 241 extraDiff = 0; 242 243 if (desired->alphaBits != GLFW_DONT_CARE) 244 { 245 extraDiff += (desired->alphaBits - current->alphaBits) * 246 (desired->alphaBits - current->alphaBits); 247 } 248 249 if (desired->depthBits != GLFW_DONT_CARE) 250 { 251 extraDiff += (desired->depthBits - current->depthBits) * 252 (desired->depthBits - current->depthBits); 253 } 254 255 if (desired->stencilBits != GLFW_DONT_CARE) 256 { 257 extraDiff += (desired->stencilBits - current->stencilBits) * 258 (desired->stencilBits - current->stencilBits); 259 } 260 261 if (desired->accumRedBits != GLFW_DONT_CARE) 262 { 263 extraDiff += (desired->accumRedBits - current->accumRedBits) * 264 (desired->accumRedBits - current->accumRedBits); 265 } 266 267 if (desired->accumGreenBits != GLFW_DONT_CARE) 268 { 269 extraDiff += (desired->accumGreenBits - current->accumGreenBits) * 270 (desired->accumGreenBits - current->accumGreenBits); 271 } 272 273 if (desired->accumBlueBits != GLFW_DONT_CARE) 274 { 275 extraDiff += (desired->accumBlueBits - current->accumBlueBits) * 276 (desired->accumBlueBits - current->accumBlueBits); 277 } 278 279 if (desired->accumAlphaBits != GLFW_DONT_CARE) 280 { 281 extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) * 282 (desired->accumAlphaBits - current->accumAlphaBits); 283 } 284 285 if (desired->samples != GLFW_DONT_CARE) 286 { 287 extraDiff += (desired->samples - current->samples) * 288 (desired->samples - current->samples); 289 } 290 291 if (desired->sRGB && !current->sRGB) 292 extraDiff++; 293 } 294 295 // Figure out if the current one is better than the best one found so far 296 // Least number of missing buffers is the most important heuristic, 297 // then color buffer size match and lastly size match for other buffers 298 299 if (missing < leastMissing) 300 closest = current; 301 else if (missing == leastMissing) 302 { 303 if ((colorDiff < leastColorDiff) || 304 (colorDiff == leastColorDiff && extraDiff < leastExtraDiff)) 305 { 306 closest = current; 307 } 308 } 309 310 if (current == closest) 311 { 312 leastMissing = missing; 313 leastColorDiff = colorDiff; 314 leastExtraDiff = extraDiff; 315 } 316 } 317 318 return closest; 319 } 320 321 GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) 322 { 323 int i; 324 _GLFWwindow* window; 325 const char* version; 326 const char* prefixes[] = 327 { 328 "OpenGL ES-CM ", 329 "OpenGL ES-CL ", 330 "OpenGL ES ", 331 NULL 332 }; 333 334 window = _glfwPlatformGetTls(&_glfw.contextSlot); 335 336 window->context.source = ctxconfig->source; 337 window->context.client = GLFW_OPENGL_API; 338 339 window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) 340 window->context.getProcAddress("glGetIntegerv"); 341 window->context.GetString = (PFNGLGETSTRINGPROC) 342 window->context.getProcAddress("glGetString"); 343 if (!window->context.GetIntegerv || !window->context.GetString) 344 { 345 _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); 346 return GLFW_FALSE; 347 } 348 349 version = (const char*) window->context.GetString(GL_VERSION); 350 if (!version) 351 { 352 if (ctxconfig->client == GLFW_OPENGL_API) 353 { 354 _glfwInputError(GLFW_PLATFORM_ERROR, 355 "OpenGL version string retrieval is broken"); 356 } 357 else 358 { 359 _glfwInputError(GLFW_PLATFORM_ERROR, 360 "OpenGL ES version string retrieval is broken"); 361 } 362 363 return GLFW_FALSE; 364 } 365 366 for (i = 0; prefixes[i]; i++) 367 { 368 const size_t length = strlen(prefixes[i]); 369 370 if (strncmp(version, prefixes[i], length) == 0) 371 { 372 version += length; 373 window->context.client = GLFW_OPENGL_ES_API; 374 break; 375 } 376 } 377 378 if (!sscanf(version, "%d.%d.%d", 379 &window->context.major, 380 &window->context.minor, 381 &window->context.revision)) 382 { 383 if (window->context.client == GLFW_OPENGL_API) 384 { 385 _glfwInputError(GLFW_PLATFORM_ERROR, 386 "No version found in OpenGL version string"); 387 } 388 else 389 { 390 _glfwInputError(GLFW_PLATFORM_ERROR, 391 "No version found in OpenGL ES version string"); 392 } 393 394 return GLFW_FALSE; 395 } 396 397 if (window->context.major < ctxconfig->major || 398 (window->context.major == ctxconfig->major && 399 window->context.minor < ctxconfig->minor)) 400 { 401 // The desired OpenGL version is greater than the actual version 402 // This only happens if the machine lacks {GLX|WGL}_ARB_create_context 403 // /and/ the user has requested an OpenGL version greater than 1.0 404 405 // For API consistency, we emulate the behavior of the 406 // {GLX|WGL}_ARB_create_context extension and fail here 407 408 if (window->context.client == GLFW_OPENGL_API) 409 { 410 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 411 "Requested OpenGL version %i.%i, got version %i.%i", 412 ctxconfig->major, ctxconfig->minor, 413 window->context.major, window->context.minor); 414 } 415 else 416 { 417 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 418 "Requested OpenGL ES version %i.%i, got version %i.%i", 419 ctxconfig->major, ctxconfig->minor, 420 window->context.major, window->context.minor); 421 } 422 423 return GLFW_FALSE; 424 } 425 426 if (window->context.major >= 3) 427 { 428 // OpenGL 3.0+ uses a different function for extension string retrieval 429 // We cache it here instead of in glfwExtensionSupported mostly to alert 430 // users as early as possible that their build may be broken 431 432 window->context.GetStringi = (PFNGLGETSTRINGIPROC) 433 window->context.getProcAddress("glGetStringi"); 434 if (!window->context.GetStringi) 435 { 436 _glfwInputError(GLFW_PLATFORM_ERROR, 437 "Entry point retrieval is broken"); 438 return GLFW_FALSE; 439 } 440 } 441 442 if (window->context.client == GLFW_OPENGL_API) 443 { 444 // Read back context flags (OpenGL 3.0 and above) 445 if (window->context.major >= 3) 446 { 447 GLint flags; 448 window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags); 449 450 if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) 451 window->context.forward = GLFW_TRUE; 452 453 if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) 454 window->context.debug = GLFW_TRUE; 455 else if (glfwExtensionSupported("GL_ARB_debug_output") && 456 ctxconfig->debug) 457 { 458 // HACK: This is a workaround for older drivers (pre KHR_debug) 459 // not setting the debug bit in the context flags for 460 // debug contexts 461 window->context.debug = GLFW_TRUE; 462 } 463 464 if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR) 465 window->context.noerror = GLFW_TRUE; 466 } 467 468 // Read back OpenGL context profile (OpenGL 3.2 and above) 469 if (window->context.major >= 4 || 470 (window->context.major == 3 && window->context.minor >= 2)) 471 { 472 GLint mask; 473 window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); 474 475 if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) 476 window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; 477 else if (mask & GL_CONTEXT_CORE_PROFILE_BIT) 478 window->context.profile = GLFW_OPENGL_CORE_PROFILE; 479 else if (glfwExtensionSupported("GL_ARB_compatibility")) 480 { 481 // HACK: This is a workaround for the compatibility profile bit 482 // not being set in the context flags if an OpenGL 3.2+ 483 // context was created without having requested a specific 484 // version 485 window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; 486 } 487 } 488 489 // Read back robustness strategy 490 if (glfwExtensionSupported("GL_ARB_robustness")) 491 { 492 // NOTE: We avoid using the context flags for detection, as they are 493 // only present from 3.0 while the extension applies from 1.1 494 495 GLint strategy; 496 window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, 497 &strategy); 498 499 if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) 500 window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; 501 else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) 502 window->context.robustness = GLFW_NO_RESET_NOTIFICATION; 503 } 504 } 505 else 506 { 507 // Read back robustness strategy 508 if (glfwExtensionSupported("GL_EXT_robustness")) 509 { 510 // NOTE: The values of these constants match those of the OpenGL ARB 511 // one, so we can reuse them here 512 513 GLint strategy; 514 window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, 515 &strategy); 516 517 if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) 518 window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; 519 else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) 520 window->context.robustness = GLFW_NO_RESET_NOTIFICATION; 521 } 522 } 523 524 if (glfwExtensionSupported("GL_KHR_context_flush_control")) 525 { 526 GLint behavior; 527 window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior); 528 529 if (behavior == GL_NONE) 530 window->context.release = GLFW_RELEASE_BEHAVIOR_NONE; 531 else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) 532 window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; 533 } 534 535 // Clearing the front buffer to black to avoid garbage pixels left over from 536 // previous uses of our bit of VRAM 537 { 538 PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) 539 window->context.getProcAddress("glClear"); 540 glClear(GL_COLOR_BUFFER_BIT); 541 window->context.swapBuffers(window); 542 } 543 544 return GLFW_TRUE; 545 } 546 547 GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) 548 { 549 const char* start = extensions; 550 551 for (;;) 552 { 553 const char* where; 554 const char* terminator; 555 556 where = strstr(start, string); 557 if (!where) 558 return GLFW_FALSE; 559 560 terminator = where + strlen(string); 561 if (where == start || *(where - 1) == ' ') 562 { 563 if (*terminator == ' ' || *terminator == '\0') 564 break; 565 } 566 567 start = terminator; 568 } 569 570 return GLFW_TRUE; 571 } 572 573 574 ////////////////////////////////////////////////////////////////////////// 575 ////// GLFW public API ////// 576 ////////////////////////////////////////////////////////////////////////// 577 578 GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) 579 { 580 _GLFWwindow* window = (_GLFWwindow*) handle; 581 _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); 582 583 _GLFW_REQUIRE_INIT(); 584 585 if (window && window->context.client == GLFW_NO_API) 586 { 587 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); 588 return; 589 } 590 591 if (previous) 592 { 593 if (!window || window->context.source != previous->context.source) 594 previous->context.makeCurrent(NULL); 595 } 596 597 if (window) 598 window->context.makeCurrent(window); 599 } 600 601 GLFWAPI GLFWwindow* glfwGetCurrentContext(void) 602 { 603 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 604 return _glfwPlatformGetTls(&_glfw.contextSlot); 605 } 606 607 GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) 608 { 609 _GLFWwindow* window = (_GLFWwindow*) handle; 610 assert(window != NULL); 611 612 _GLFW_REQUIRE_INIT(); 613 614 if (window->context.client == GLFW_NO_API) 615 { 616 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); 617 return; 618 } 619 620 window->context.swapBuffers(window); 621 } 622 623 GLFWAPI void glfwSwapInterval(int interval) 624 { 625 _GLFWwindow* window; 626 627 _GLFW_REQUIRE_INIT(); 628 629 window = _glfwPlatformGetTls(&_glfw.contextSlot); 630 if (!window) 631 { 632 _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); 633 return; 634 } 635 636 window->context.swapInterval(interval); 637 } 638 639 GLFWAPI int glfwExtensionSupported(const char* extension) 640 { 641 _GLFWwindow* window; 642 assert(extension != NULL); 643 644 _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); 645 646 window = _glfwPlatformGetTls(&_glfw.contextSlot); 647 if (!window) 648 { 649 _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); 650 return GLFW_FALSE; 651 } 652 653 if (*extension == '\0') 654 { 655 _glfwInputError(GLFW_INVALID_VALUE, "Extension name is empty string"); 656 return GLFW_FALSE; 657 } 658 659 if (window->context.major >= 3) 660 { 661 int i; 662 GLint count; 663 664 // Check if extension is in the modern OpenGL extensions string list 665 666 window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count); 667 668 for (i = 0; i < count; i++) 669 { 670 const char* en = (const char*) 671 window->context.GetStringi(GL_EXTENSIONS, i); 672 if (!en) 673 { 674 _glfwInputError(GLFW_PLATFORM_ERROR, 675 "Extension string retrieval is broken"); 676 return GLFW_FALSE; 677 } 678 679 if (strcmp(en, extension) == 0) 680 return GLFW_TRUE; 681 } 682 } 683 else 684 { 685 // Check if extension is in the old style OpenGL extensions string 686 687 const char* extensions = (const char*) 688 window->context.GetString(GL_EXTENSIONS); 689 if (!extensions) 690 { 691 _glfwInputError(GLFW_PLATFORM_ERROR, 692 "Extension string retrieval is broken"); 693 return GLFW_FALSE; 694 } 695 696 if (_glfwStringInExtensionString(extension, extensions)) 697 return GLFW_TRUE; 698 } 699 700 // Check if extension is in the platform-specific string 701 return window->context.extensionSupported(extension); 702 } 703 704 GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) 705 { 706 _GLFWwindow* window; 707 assert(procname != NULL); 708 709 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 710 711 window = _glfwPlatformGetTls(&_glfw.contextSlot); 712 if (!window) 713 { 714 _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); 715 return NULL; 716 } 717 718 return window->context.getProcAddress(procname); 719 } 720