1 /**
2  * Dlang vulkan lib loader retrieving vkGetInstanceProcAddr for windows, macos and posix systems
3  *
4  * Copyright: Copyright 2015-2016 The Khronos Group Inc.; Copyright 2016 Alex Parrill, Peter Particle.
5  * License:   $(https://opensource.org/licenses/MIT, MIT License).
6  * Authors: Copyright 2016 Alex Parrill, Peter Particle
7  */
8 module erupted.vulkan_lib_loader;
9 
10 import erupted.functions;
11 import core.stdc.stdio : fprintf, stderr, FILE;
12 
13 nothrow @nogc:
14 
15 
16 /// private helper functions for windows platform
17 version( Windows ) {
18 private:
19     import core.sys.windows.windows;
20     HMODULE         vulkan_lib  = null;
21     auto loadLib()  { return LoadLibrary( "vulkan-1.dll" ); }
22     bool freeLib()  { return FreeLibrary( vulkan_lib ) != 0; }
23     auto loadSym()  { return cast( PFN_vkGetInstanceProcAddr )GetProcAddress( vulkan_lib, "vkGetInstanceProcAddr" ); }
24     void logLibError( FILE* log_stream, const( char )* message ) {
25         fprintf( log_stream, "%svulkan-1.dll! Error code: 0x%x\n", message, GetLastError());
26     }
27 }
28 
29 
30 /// private helper functions for android platform
31 else version( Android ) {
32 private:
33     import core.sys.posix.dlfcn : dlerror, dlopen, dlclose, dlsym, RTLD_NOW, RTLD_LOCAL;
34     void*           vulkan_lib  = null;
35     auto loadLib()  { return dlopen( "libvulkan.so", RTLD_NOW | RTLD_LOCAL ); }
36     bool freeLib()  { return dlclose( vulkan_lib ) == 0; }
37     auto loadSym()  { return cast( PFN_vkGetInstanceProcAddr )dlsym( vulkan_lib, "vkGetInstanceProcAddr" ); }
38     void logLibError( FILE* log_stream, const( char )* message ) {
39         fprintf( log_stream, "%slibvulkan.so.1! Error: %s\n", message, dlerror );
40     }
41 }
42 
43 
44 /// private helper functions for macos platforms
45 else version( OSX ) {
46 private:
47     import core.sys.posix.dlfcn : dlerror, dlopen, dlclose, dlsym, RTLD_LAZY, RTLD_LOCAL;
48     void*           vulkan_lib  = null;
49     auto loadLib()  { return dlopen( "libvulkan.1.dylib", RTLD_LAZY | RTLD_LOCAL ); }
50     bool freeLib()  { return dlclose( vulkan_lib ) == 0; }
51     auto loadSym()  { return cast( PFN_vkGetInstanceProcAddr )dlsym( vulkan_lib, "vkGetInstanceProcAddr" ); }
52     void logLibError( FILE* log_stream, const( char )* message ) {
53         fprintf( log_stream, "%slibvulkan.1.dylib! Error: %s\n", message, dlerror );
54     }
55 }
56 
57 
58 /// private helper functions for posix platforms
59 else version( Posix ) {
60 private:
61     import core.sys.posix.dlfcn : dlerror, dlopen, dlclose, dlsym, RTLD_LAZY, RTLD_LOCAL;
62     void*           vulkan_lib  = null;
63     auto loadLib()  { return dlopen( "libvulkan.so.1", RTLD_LAZY | RTLD_LOCAL ); }
64     bool freeLib()  { return dlclose( vulkan_lib ) == 0; }
65     auto loadSym()  { return cast( PFN_vkGetInstanceProcAddr )dlsym( vulkan_lib, "vkGetInstanceProcAddr" ); }
66     void logLibError( FILE* log_stream, const( char )* message ) {
67         fprintf( log_stream, "%slibvulkan.so.1! Error: %s\n", message, dlerror );
68     }
69 }
70 
71 
72 /// tries to load the platform vulkan dynamic link library
73 /// the library handle / pointer is stored privately in this module
74 /// errors are reported to a specifiable stream which is standard error by default
75 /// Params:
76 ///     log_stream = file stream to receive error messages, default stderr
77 /// Returns: true if the vulkan lib could be loaded, false otherwise
78 bool loadVulkanLib( FILE* log_stream = stderr ) {
79     vulkan_lib = loadLib;
80     if( !vulkan_lib ) {
81         logLibError( log_stream, "Could not load " );
82         return false;
83     } else {
84         return true;
85     }
86 }
87 
88 
89 /// tries to load the vkGetInstanceProcAddr function from the module private lib handle / pointer
90 /// if the lib was not loaded so far loadVulkanLib is called
91 /// errors are reported to a specifiable stream which is standard error by default
92 /// Params:
93 ///     log_stream = file stream to receive error messages, default stderr
94 /// Returns: vkGetInstanceProcAddr if it could be loaded from the lib, null otherwise
95 PFN_vkGetInstanceProcAddr loadGetInstanceProcAddr( FILE* log_stream = stderr ) {
96     if( !vulkan_lib && !loadVulkanLib( log_stream )) {
97         fprintf( log_stream, "Cannot not retrieve vkGetInstanceProcAddr as vulkan lib is not loaded!" );
98         return null;
99     }
100     auto getInstanceProcAddr = loadSym;
101     if( !getInstanceProcAddr )
102         logLibError( log_stream, "Could not retrieve vkGetInstanceProcAddr from " );
103     return getInstanceProcAddr;
104 }
105 
106 
107 /// tries to free / unload the previously loaded platform vulkan lib
108 /// errors are reported to a specifiable stream which is standard error by default
109 /// Params:
110 ///     log_stream = file stream to receive error messages, default stderr
111 /// Returns: true if the vulkan lib could be freed, false otherwise
112 bool freeVulkanLib( FILE* log_stream = stderr ) {
113     if( !vulkan_lib ) {
114         fprintf( log_stream, "Cannot free vulkan lib as it is not loaded!" );
115         return false;
116     } else if( !freeLib() ) {
117         logLibError( log_stream, "Could not unload " );
118         return false;
119     } else {
120         return true;
121     }
122 }
123 
124 
125 /// Combines loadVulkanLib, loadGetInstanceProcAddr and loadGlobalLevelFunctions( PFN_vkGetInstanceProcAddr )
126 /// from module erupted.functions. If this function succeeds the function vkGetInstanceProcAddr
127 /// from module erupted.functions can be used freely. Moreover the required functions to initialize a
128 /// vulkan instance a vkEnumerateInstanceExtensionProperties, vkEnumerateInstanceLayerProperties and vkCreateInstance
129 /// are available as well. To get all the other functions an vulkan instance must be created and with it
130 /// loadInstanceLevelFunctions be called from either erupted.functions or through a custom tailored module
131 /// with mixed in extensions through the erupted.platform.mixin_extensions mechanism.
132 /// Additional device based functions can then be loaded with loadDeviceLevelFunctions passing in the instance or
133 /// with creating a vulkan device beforehand and calling the same function with it.
134 ///
135 /// Note: as this function indirectly calls loadVulkanLib loading the vulkan lib, freeVulkanLib should be called
136 ///       at some point in the process to cleanly free / unload the lib
137 /// all errors during vulkan lib loading and vkGetInstanceProcAddr retrieving are reported to log_stream, default stderr
138 ///     log_stream = file stream to receive error messages, default stderr
139 /// Returns: true if the vulkan lib could be freed, false otherwise
140 bool loadGlobalLevelFunctions( FILE* log_stream = stderr ) {
141     auto getInstanceProcAddr = loadGetInstanceProcAddr( log_stream );
142     if( !getInstanceProcAddr ) return false;
143     erupted.functions.loadGlobalLevelFunctions( getInstanceProcAddr );
144     return true;
145 }
146 
147