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 }