1 /*
2  *  BSD 3-Clause License
3  *  
4  *  Copyright (c) 2016, Mango-Engine Team
5  *  All rights reserved.
6  *  
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are met:
9  *  
10  *  * Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *  
13  *  * Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *  
17  *  * Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from
19  *    this software without specific prior written permission.
20  *  
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 module mango_engine.graphics.opengl.gl_backend;
33 
34 import mango_engine.mango;
35 import mango_engine.logging;
36 import mango_engine.graphics.backend;
37 
38 import derelict.opengl3.gl3;
39 import derelict.glfw3.glfw3;
40 import derelict.freeimage.freeimage;
41 import derelict.util.exception;
42 
43 import std.conv : to;
44 
45 /// The Major OpenGL version used by mango-engine.
46 immutable uint MANGO_GL_VERSION_MAJOR = 3;
47 /// The Minor OpenGL version used by mango-engine.
48 immutable uint MANGO_GL_VERSION_MINOR = 3;
49 /// The Whole OpenGL version used by mango-engine.
50 immutable GLVersion MANGO_GL_VERSION = GLVersion.GL33;
51 /// The OpenGL version used by mango-engine (string)
52 immutable string MANGO_GL_VERSION_STRING = "GL 3.3";
53 
54 private alias checkSupport = gl_check;
55 
56 package shared bool failedContext = false;
57 
58 package shared Logger backendLogger;
59 
60 void gl_check() @safe { // Check if we were compiled with OpenGL support.
61     if(!mango_hasGLSupport()) {
62         throw new Exception("Mango-Engine was not compiled with OpenGL backend support!");
63     }
64 }
65 
66 extern(C) private void glfwErrorCallback(int error, const char* description) @system {
67     import std.stdio : writeln;
68     import blocksound.util : toDString;
69 
70     if(error == 65543) {
71         failedContext = true;
72     }
73 
74     (cast(Logger) backendLogger).logError("GLFW ERROR " ~ to!string(error) ~ ", " ~ toDString(description));
75 }
76 
77 /++
78     The OpenGL Backend implementation for mango_engine.
79 
80     Loads the following libraries:
81         OpenGL
82         GLFW 3
83         FreeImage
84     On Windows libraries will be loaded
85     from the "lib" directory (should be placed in current directory),
86     with the exception of OpenGL (see below).
87     
88     loadLibraries() accepts the following keys, values:
89         "gl_useProvided" =  ONLY ON WINDOWS:
90                                 Attempts to load opengl32.dll from the
91                                 DLL library folder "lib" (by default it is loaded from the system). 
92                                 Set to "true" if you want to load the OpenGL DLL from here.
93                                 Useful for using a software renderer such as LLVMpipe.
94 +/
95 class GLBackend : Backend {
96 
97     this(Logger logger) @trusted {
98         super(logger);
99         // TODO: init logger based on logger class provided
100         backendLogger = cast(shared) new ConsoleLogger("GLBackend");
101     }
102 
103     /// Loads the core methods of OpenGL (1.1+)
104     static void loadCoreMethods() @system {
105         DerelictGL3.reload();
106     }
107 
108     override {
109         void loadLibraries(in string[string] args = null) @system {
110             checkSupport();
111 
112             if("gl_useProvided" in args && to!bool(args["gl_useProvided"]) == true) {
113                 loadGL(true);
114             } else loadGL(false);
115 
116             loadGLFW();
117             loadFI();
118         }
119 
120         void doInit() @system {
121             glfwSetErrorCallback(cast(GLFWerrorfun) &glfwErrorCallback);
122 
123             if(!glfwInit()) {
124                 // GLFW failed to initalize
125                 throw new LibraryLoadException("GLFW", "glfwInit() Failed!");
126             }
127         }
128 
129         void doDestroy() @system {
130             glfwTerminate();
131         }
132     }
133 
134     ShouldThrow derelictShouldThrow(string symbolName) {
135         // For now we will ignore missing symbols, TODO: FIX!
136         logger.logWarn("Missing symbol: " ~ symbolName ~ " !");
137         return ShouldThrow.No;
138     }
139 
140     private void loadGL(bool useProvided) @system { // Load code for OpenGL
141         logger.logDebug("Loading OpenGL...");
142         version(mango_GLBackend) {
143             version(Windows) {
144                 //------------------------------- Windows Load Code
145                 try {
146                     if(useProvided) {
147                         DerelictGL3.load("lib\\opengl32.dll"); // Use provided DLL
148                         logger.logDebug("Loaded Provided opengl32.dll");
149                     } else {
150                         DerelictGL3.load();
151                     }
152 
153                 } catch(Exception e) {
154                     throw new LibraryLoadException("OpenGL", e.toString());
155                 }
156                 //------------------------------- End Windows Load Code
157             } else { // All other OS
158                 try {
159                     DerelictGL3.load();
160                 } catch(Exception e) {
161                     throw new LibraryLoadException("OpenGL", e.toString());
162                 }
163             }
164         }
165     }
166 
167     private void loadGLFW() @system { // Load code for GLFW
168         logger.logDebug("Loadng GLFW...");
169         version(Windows) {
170             //------------------------------- Windows Load Code
171             try {
172                 DerelictGLFW3.load("lib\\glfw3.dll");
173             } catch(Exception e) {
174                 throw new LibraryLoadException("GLFW", e.toString());
175             }
176             //------------------------------- End Windows Load Code
177         } else { // All other OS
178             DerelictGLFW3.missingSymbolCallback = &this.derelictShouldThrow;
179             try {
180                 DerelictGLFW3.load();
181             } catch(Exception e) {
182                 throw new LibraryLoadException("GLFW", e.toString());
183             }
184         }
185     }
186 
187     private void loadFI() @system { // Load code for FreeImage
188         logger.logDebug("Loading FreeImage...");
189         version(Windows) {
190             //------------------------------- Windows Load Code
191             try {
192                 DerelictFI.load("lib\\FreeImage.dll");
193             } catch(Exception e) {
194                 throw new LibraryLoadException("FreeImage", e.toString());
195             }
196             //------------------------------- End Windows Load Code
197         } else { // All other OS
198             DerelictFI.missingSymbolCallback = &derelictShouldThrow;
199             try {
200                 DerelictFI.load();
201             } catch(Exception e) {
202                 throw new LibraryLoadException("FreeImage", e.toString());
203             }
204         }
205     }
206 }