1 /** 2 Copyright: Copyright (c) 2017-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module voxelman.graphics.gl; 8 9 public import derelict.opengl; 10 import voxelman.log; 11 import std.conv: to; 12 import std..string : format; 13 import std.typecons : tuple; 14 15 mixin glFreeFuncs!(GLVersion.gl31); 16 17 void loadOpenGL() 18 { 19 DerelictGL3.load(); 20 } 21 22 void reloadOpenGL() 23 { 24 import std..string : fromStringz; 25 26 // Load maximum avaliable OpenGL version 27 auto loaded = DerelictGL3.reload(); // calls loadExtensions below 28 29 infof("OpenGL %s", glGetString(GL_VERSION).fromStringz); 30 infof("Vendor %s", glGetString(GL_VENDOR).fromStringz); 31 infof("Renderer %s", glGetString(GL_RENDERER).fromStringz); 32 33 } 34 35 36 //enum EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD = 0x9160; 37 //__gshared bool AMD_pinned_memory; 38 39 // GL_ARB_debug_output 40 //import derelict.opengl.extensions.arb_d; 41 //mixin(arbDebugOutput); 42 //__gshared bool GL_ARB_debug_output_supported; 43 44 // GL_KHR_debug 45 import derelict.opengl.extensions.khr; 46 mixin(khrDebug); 47 __gshared bool GL_KHR_debug_supported; 48 49 50 // Is called by DerelictGL3.reload, because of mixin glFreeFuncs 51 // `static if(is(typeof(loadExtensions()))) loadExtensions();` in derelict.opengl.impl 52 void loadExtensions() 53 { 54 //AMD_pinned_memory = DerelictGL3.isExtensionSupported("GL_AMD_pinned_memory"); 55 //GL_ARB_debug_output_supported = DerelictGL3.isExtensionSupported("GL_ARB_debug_output"); 56 //infof("GL_ARB_debug_output %s", GL_ARB_debug_output_supported); 57 GL_KHR_debug_supported = DerelictGL3.isExtensionSupported("GL_KHR_debug"); 58 infof("GL_KHR_debug %s", GL_KHR_debug_supported); 59 } 60 61 void setupGLDebugLogging() 62 { 63 int flags; 64 glGetIntegerv(GL_CONTEXT_FLAGS, &flags); 65 if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) 66 { 67 // initialize debug output 68 infof("Debug context inited"); 69 glEnable(GL_DEBUG_OUTPUT); 70 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 71 glDebugMessageCallback(&glDebugLog, null); 72 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, null, GL_TRUE); 73 74 // test 75 glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, 76 GL_DEBUG_SEVERITY_MEDIUM, -1, "Testing error callback"); 77 } 78 else 79 { 80 infof("Debug context not inited"); 81 } 82 } 83 84 extern(System) void glDebugLog( 85 GLenum source, 86 GLenum type, 87 GLuint id, 88 GLenum severity, 89 GLsizei length, 90 const GLchar* message, 91 const void* userParam) nothrow 92 { 93 string sourceStr; 94 switch (source) 95 { 96 case GL_DEBUG_SOURCE_API: sourceStr = "API"; break; 97 case GL_DEBUG_SOURCE_WINDOW_SYSTEM: sourceStr = "Window System"; break; 98 case GL_DEBUG_SOURCE_SHADER_COMPILER: sourceStr = "Shader Compiler"; break; 99 case GL_DEBUG_SOURCE_THIRD_PARTY: sourceStr = "Third Party"; break; 100 case GL_DEBUG_SOURCE_APPLICATION: sourceStr = "Application"; break; 101 case GL_DEBUG_SOURCE_OTHER: sourceStr = "Other"; break; 102 default: break; 103 } 104 105 string typeStr; 106 switch (type) 107 { 108 case GL_DEBUG_TYPE_ERROR: typeStr = "Error"; break; 109 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: typeStr = "Deprecated Behaviour"; break; 110 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: typeStr = "Undefined Behaviour"; break; 111 case GL_DEBUG_TYPE_PORTABILITY: typeStr = "Portability"; break; 112 case GL_DEBUG_TYPE_PERFORMANCE: typeStr = "Performance"; break; 113 case GL_DEBUG_TYPE_MARKER: typeStr = "Marker"; break; 114 case GL_DEBUG_TYPE_PUSH_GROUP: typeStr = "Push Group"; break; 115 case GL_DEBUG_TYPE_POP_GROUP: typeStr = "Pop Group"; break; 116 case GL_DEBUG_TYPE_OTHER: typeStr = "Other"; break; 117 default: break; 118 } 119 120 string severityStr; 121 switch (severity) 122 { 123 case GL_DEBUG_SEVERITY_HIGH: severityStr = "high"; break; 124 case GL_DEBUG_SEVERITY_MEDIUM: severityStr = "medium"; break; 125 case GL_DEBUG_SEVERITY_LOW: severityStr = "low"; break; 126 case GL_DEBUG_SEVERITY_NOTIFICATION: severityStr = "notification"; break; 127 default: break; 128 } 129 130 try { 131 infof("[GL] source: %s, type: %s, id: %s, severity: %s, msg: \"%s\"", 132 sourceStr, typeStr, id, severityStr, message[0..length]); 133 } 134 catch(Exception){} 135 } 136 137 138 /// Error checking template; should work in debug build. 139 /// Usage: checkgl!glFunction(funcParams); 140 template checkgl(alias func) 141 { 142 import std.traits : Parameters; 143 debug auto checkgl(string file = __FILE__, int line = __LINE__)(Parameters!func args) 144 { 145 scope(success) checkGlError(file, line, func.stringof, args); 146 return func(args); 147 } else 148 alias checkgl = func; 149 } 150 151 void checkGlError(string file = __FILE__, size_t line = __LINE__) 152 { 153 uint error = glGetError(); 154 if (error != GL_NO_ERROR) 155 { 156 auto msg = format("OpenGL error \"%s\" [%s]", glErrorStringTable[error], error); 157 throw new OpenglException(msg, file, line); 158 } 159 } 160 161 void checkGlError(Args...)(string file, size_t line, string funcName, Args args) 162 { 163 uint error = glGetError(); 164 if (error != GL_NO_ERROR) 165 { 166 auto msg = format("%s(%(%s%|, %)): \"%s\" [%s]", funcName, tuple(args), glErrorStringTable[error], error); 167 throw new OpenglException(msg, file, line); 168 } 169 } 170 171 class OpenglException : Exception 172 { 173 this(string msg, string file = __FILE__, size_t line = __LINE__) 174 { 175 super(msg, file, line); 176 } 177 } 178 179 static const string[uint] glErrorStringTable; 180 181 static this() 182 { 183 glErrorStringTable = [ 184 //GL_NO_ERROR : "no error", 185 GL_INVALID_ENUM : "invalid enum", 186 GL_INVALID_VALUE : "invalid value", 187 GL_INVALID_OPERATION : "invalid operation", 188 GL_INVALID_FRAMEBUFFER_OPERATION : "invalid framebuffer operation", 189 GL_OUT_OF_MEMORY : "out of memory", 190 //GL_STACK_UNDERFLOW : "stack underflow", 191 //GL_STACK_OVERFLOW : "stack overflow", 192 ]; 193 } 194 195 int openglMajorVersion(GLVersion openglVersion) { 196 final switch(openglVersion) with (GLVersion) { 197 case gl11, gl12, gl13, gl14, gl15: return 1; 198 case gl20, gl21: return 2; 199 case gl30, gl31, gl32, gl33: return 3; 200 case gl40, gl41, gl42, gl43, gl44, gl45: return 4; 201 case none: return 0; 202 } 203 } 204 205 int openglMinorVersion(GLVersion openglVersion) { 206 final switch(openglVersion) with (GLVersion) { 207 case gl20, gl30, gl40: return 0; 208 case gl11, gl21, gl31, gl41: return 1; 209 case gl12, gl32, gl42: return 2; 210 case gl13, gl33, gl43: return 3; 211 case gl14, gl44: return 4; 212 case gl15, gl45: return 5; 213 case none: return 0; 214 } 215 } 216 217 GLVersion toGLVersion(int major, int minor) 218 { 219 switch(major) { 220 case 4: 221 if(minor == 5) return GLVersion.gl45; 222 else if(minor == 4) return GLVersion.gl44; 223 else if(minor == 3) return GLVersion.gl43; 224 else if(minor == 2) return GLVersion.gl42; 225 else if(minor == 1) return GLVersion.gl41; 226 else if(minor == 0) return GLVersion.gl40; 227 228 /* No default condition here, since its possible for new 229 minor versions of the 4.x series to be released before 230 support is added to Derelict. That case is handled outside 231 of the switch. When no more 4.x versions are released, this 232 should be changed to return GL40 by default. */ 233 break; 234 235 case 3: 236 if(minor == 3) return GLVersion.gl33; 237 else if(minor == 2) return GLVersion.gl32; 238 else if(minor == 1) return GLVersion.gl31; 239 else return GLVersion.gl30; 240 241 case 2: 242 if(minor == 1) return GLVersion.gl21; 243 else return GLVersion.gl20; 244 245 case 1: 246 if(minor == 5) return GLVersion.gl15; 247 else if(minor == 4) return GLVersion.gl14; 248 else if(minor == 3) return GLVersion.gl13; 249 else if(minor == 2) return GLVersion.gl12; 250 else return GLVersion.gl11; 251 252 default: 253 /* glGetString(GL_VERSION) is guaranteed to return a result 254 of a specific format, so if this point is reached it is 255 going to be because a major version higher than what Derelict 256 supports was encountered. That case is handled outside the 257 switch. */ 258 break; 259 } 260 261 /* It's highly likely at this point that the version is higher than 262 what Derelict supports, so return the highest supported version. */ 263 return GLVersion.highestSupported; 264 }