1 2 // Copyright 2018 - 2021 Michael D. Parker 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 module bindbc.opengl.context; 8 9 import bindbc.loader; 10 import bindbc.opengl.config; 11 12 private { 13 enum uint glVersion = 0x1F02; 14 enum uint glExtensions = 0x1F03; 15 enum uint glNumExtensions = 0x821D; 16 extern(System) @nogc nothrow { 17 alias GetString = const(char)* function(uint); 18 alias GetStringi = const(char)* function(uint,uint); 19 alias GetIntegerv = void function(uint, int*); 20 } 21 __gshared { 22 GetString getString; 23 GetStringi getStringi; 24 GetIntegerv getIntegerv; 25 } 26 27 extern(System) @nogc nothrow { 28 alias GetCurrentContext = void* function(); 29 alias GetProcAddress = void* function(const(char)*); 30 } 31 __gshared { 32 GetCurrentContext getCurrentContext; 33 GetProcAddress getProcAddress; 34 } 35 36 version(Windows) { 37 enum getCurrentContextName = "wglGetCurrentContext"; 38 enum getProcAddressName = "wglGetProcAddress"; 39 } 40 else version(OSX) { 41 enum getCurrentContextName = "CGLGetCurrentContext"; 42 } 43 else version(Posix) { 44 enum getCurrentContextName = "glXGetCurrentContext"; 45 enum getProcAddressName = "glXGetProcAddress"; 46 } 47 else static assert(0, "Platform Problem!!!"); 48 } 49 50 @nogc nothrow: 51 52 package GLSupport getContextVersion(SharedLib lib) 53 { 54 // Lazy load the appropriate symbols for fetching the current context 55 // and getting OpenGL symbols from it. 56 if(getCurrentContext == null) { 57 lib.bindSymbol(cast(void**)&getCurrentContext, getCurrentContextName); 58 if(getCurrentContext == null) return GLSupport.badLibrary; 59 60 version(OSX) { /* Nothing to do */ } 61 else { 62 lib.bindSymbol(cast(void**)&getProcAddress, getProcAddressName); 63 if(getProcAddress == null) return GLSupport.badLibrary; 64 } 65 } 66 67 // Check if a context is current 68 if(getCurrentContext() == null) return GLSupport.noContext; 69 70 // Lazy load glGetString to check the context version 71 if(getString == null) { 72 lib.bindSymbol(cast(void**)&getString, "glGetString"); 73 if(getString == null) return GLSupport.badLibrary; 74 } 75 76 /* glGetString(GL_VERSION) is guaranteed to return a constant string 77 of the format "[major].[minor].[build] xxxx", where xxxx is vendor-specific 78 information. Here, I'm pulling two characters out of the string, the major 79 and minor version numbers. */ 80 auto verstr = getString(glVersion); 81 char major = *verstr; 82 char minor = *(verstr + 2); 83 84 GLSupport support = GLSupport.noLibrary; 85 86 switch(major) { 87 case '4': 88 if(minor == '5') support = GLSupport.gl45; 89 else if(minor == '4') support = GLSupport.gl44; 90 else if(minor == '3') support = GLSupport.gl43; 91 else if(minor == '2') support = GLSupport.gl42; 92 else if(minor == '1') support = GLSupport.gl41; 93 else if(minor == '0') support = GLSupport.gl40; 94 95 /* No default condition here, since it's possible for new 96 minor versions of the 4.x series to be released before 97 support is added. That case is handled outside 98 of the switch. When no more 4.x versions are released, this 99 should be changed to return GL40 by default. */ 100 break; 101 102 case '3': 103 if(minor == '3') support = GLSupport.gl33; 104 else if(minor == '2') support = GLSupport.gl32; 105 else if(minor == '1') support = GLSupport.gl31; 106 else support = GLSupport.gl30; 107 break; 108 109 case '2': 110 if(minor == '1') support = GLSupport.gl21; 111 else support = GLSupport.gl20; 112 break; 113 114 case '1': 115 if(minor == '5') support = GLSupport.gl15; 116 else if(minor == '4') support = GLSupport.gl14; 117 else if(minor == '3') support = GLSupport.gl13; 118 else if(minor == '2') support = GLSupport.gl12; 119 else support = GLSupport.gl11; 120 break; 121 122 default: 123 /* glGetString(GL_VERSION) is guaranteed to return a result 124 of a specific format, so if this point is reached it is 125 going to be because a major version higher than what BindBC 126 supports was encountered. That case is handled outside the 127 switch. */ 128 break; 129 } 130 131 // If support hasn't yet been set, it means the context has a higher 132 // version than the binding knows about. Set to the compile-time version. 133 if(support == GLSupport.noLibrary) support = glSupport; 134 135 // For contexts >= 3.0, make sure glGetStringi & glGetIntegerv are avaliable. 136 if(support >= GLSupport.gl30) { 137 lib.bindGLSymbol(cast(void**)&getStringi, "glGetStringi"); 138 139 // Use bindSymbol here, since it's a base function 140 lib.bindSymbol(cast(void**)&getIntegerv, "glGetIntegerv"); 141 142 if(getStringi == null || getIntegerv == null) 143 return GLSupport.badLibrary; 144 } 145 146 return support; 147 } 148 149 private uint numErrors; 150 151 package(bindbc.opengl): 152 153 uint errorCountGL() { return numErrors; } 154 155 bool resetErrorCountGL() 156 { 157 if(numErrors != 0) { 158 numErrors = 0; 159 return false; 160 } 161 else return true; 162 } 163 164 void bindGLSymbol(SharedLib lib, void** ptr, const(char)* symName) 165 { 166 // Use dlopen on Mac 167 version(OSX) { 168 auto startErrorCount = errorCount(); 169 lib.bindSymbol(ptr, symName); 170 numErrors += errorCount() - startErrorCount; 171 } 172 else { 173 *ptr = getProcAddress(symName); 174 if(*ptr == null) ++numErrors; 175 } 176 } 177 178 bool hasExtension(GLSupport contextVersion, const(char)* extName) 179 { 180 import core.stdc.string : strcmp, strstr, strlen; 181 182 // With a modern context, use the modern approach 183 if(contextVersion >= GLSupport.gl30) { 184 int count; 185 getIntegerv(glNumExtensions, &count); 186 187 const(char)* ext; 188 for(int i=0; i<count; ++i) { 189 ext = getStringi(glExtensions, i); 190 if(ext && strcmp(ext, extName) == 0) return true; 191 } 192 } 193 // Otherwise, use the classic approach 194 else { 195 auto extstr = getString(glExtensions); 196 if(!extstr) return false; 197 198 auto len = strlen(extName); 199 auto ext = strstr(extstr, extName); 200 while(ext) { 201 /* It's possible that the extension name is actually a substring of 202 another extension. If not, then the character following the name in 203 the extension string should be a space (or possibly the null character). 204 */ 205 if(ext[len] == ' ' || ext[len] == '\0') return true; 206 ext = strstr(ext + len, extName); 207 } 208 } 209 210 return false; 211 }