1 /**
2  * Dlang vulkan lib loader retrieving vkGetInstanceProcAddr for windows 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     auto 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 posix platforms
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     auto 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 posix platforms
45 else version( Posix ) {
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.so.1", RTLD_LAZY | RTLD_LOCAL ); }
50     auto 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.so.1! Error: %s\n", message, dlerror );
54     }
55 }
56 
57 
58 /// tries to load the platform vulkan dynamic link library
59 /// the library handle / pointer is stored privately in this module
60 /// errors are reported to a specifiable stream which is standard error by default
61 /// Params:
62 ///     log_stream = file stream to receive error messages, default stderr
63 /// Returns: true if the vulkan lib could be loaded, false otherwise
64 bool loadVulkanLib( FILE* log_stream = stderr ) {
65     vulkan_lib = loadLib;
66     if( !vulkan_lib ) {
67         logLibError( log_stream, "Could not load " );
68         return false;
69     } else {
70         return true;
71     }
72 }
73 
74 
75 /// tries to load the vkGetInstanceProcAddr function from the module private lib handle / pointer
76 /// if the lib was not loaded so far loadVulkanLib is called
77 /// errors are reported to a specifiable stream which is standard error by default
78 /// Params:
79 ///     log_stream = file stream to receive error messages, default stderr
80 /// Returns: vkGetInstanceProcAddr if it could be loaded from the lib, null otherwise
81 PFN_vkGetInstanceProcAddr loadGetInstanceProcAddr( FILE* log_stream = stderr ) {
82     if( !vulkan_lib && !loadVulkanLib( log_stream )) {
83         fprintf( log_stream, "Cannot not retrieve vkGetInstanceProcAddr as vulkan lib is not loaded!" );
84         return null;
85     }
86     auto getInstanceProcAddr = loadSym;
87     if( !getInstanceProcAddr )
88         logLibError( log_stream, "Could not retrieve vkGetInstanceProcAddr from " );
89     return getInstanceProcAddr;
90 }
91 
92 
93 /// tries to free / unload the previously loaded platform vulkan lib
94 /// errors are reported to a specifiable stream which is standard error by default
95 /// Params:
96 ///     log_stream = file stream to receive error messages, default stderr
97 /// Returns: true if the vulkan lib could be freed, false otherwise
98 bool freeVulkanLib( FILE* log_stream = stderr ) {
99     if( !vulkan_lib ) {
100         fprintf( log_stream, "Cannot free vulkan lib as it is not loaded!" );
101         return false;
102     } else if( freeLib ) {
103         logLibError( log_stream, "Could not unload " );
104         return false;
105     } else {
106         return true;
107     }
108 }
109 
110 
111 /// Combines loadVulkanLib, loadGetInstanceProcAddr and loadGlobalLevelFunctions( PFN_vkGetInstanceProcAddr )
112 /// from module erupted.functions. If this function succeeds the function vkGetInstanceProcAddr
113 /// from module erupted.functions can be used freely. Moreover the required functions to initialize a
114 /// vulkan instance a vkEnumerateInstanceExtensionProperties, vkEnumerateInstanceLayerProperties and vkCreateInstance
115 /// are available as well. To get all the other functions an vulkan instance must be created and with it
116 /// loadInstanceLevelFunctions be called from either erupted.functions or through a custom tailored module
117 /// with mixed in extensions through the erupted.platform.mixin_extensions mechanism.
118 /// Additional device based functions can then be loaded with loadDeviceLevelFunctions passing in the instance or
119 /// with creating a vulkan device beforehand and calling the same function with it.
120 ///
121 /// Note: as this function indirectly calls loadVulkanLib loading the vulkan lib, freeVulkanLib should be called
122 ///       at some point in the process to cleanly free / unload the lib
123 /// all errors during vulkan lib loading and vkGetInstanceProcAddr retrieving are reported to log_stream, default stderr
124 ///     log_stream = file stream to receive error messages, default stderr
125 /// Returns: true if the vulkan lib could be freed, false otherwise
126 bool loadGlobalLevelFunctions( FILE* log_stream = stderr ) {
127     auto getInstanceProcAddr = loadGetInstanceProcAddr( log_stream );
128     if( !getInstanceProcAddr ) return false;
129     erupted.functions.loadGlobalLevelFunctions( getInstanceProcAddr );
130     return true;
131 }
132 
133