1 /*
2 *
3 * Copyright (c) 2015-2022 The Khronos Group Inc.
4 * Copyright (c) 2015-2022 Valve Corporation
5 * Copyright (c) 2015-2022 LunarG, Inc.
6 * Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
7 * Copyright (c) 2023-2023 RasterGrid Kft.
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 * Author: Ian Elliot <ian@lunarg.com>
22 * Author: Jon Ashburn <jon@lunarg.com>
23 * Author: Lenny Komow <lenny@lunarg.com>
24 * Author: Charles Giessen <charles@lunarg.com>
25 *
26 */
27 #pragma once
28
29 #if defined(__FreeBSD__) || defined(__OpenBSD__)
30 #include <sys/types.h>
31 #include <sys/select.h>
32 #endif
33
34 #include <assert.h>
35 #include <float.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <string.h>
40
41 #if defined(__Fuchsia__)
42 #include "dlopen_fuchsia.h"
43 #endif // defined(__Fuchsia__)
44
45 // Set of platforms with a common set of functionality which is queried throughout the program
46 #if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__QNX__) || defined(__FreeBSD__) || \
47 defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__)
48 #define COMMON_UNIX_PLATFORMS 1
49 #else
50 #define COMMON_UNIX_PLATFORMS 0
51 #endif
52
53 #if COMMON_UNIX_PLATFORMS
54 #include <unistd.h>
55 // Note: The following file is for dynamic loading:
56 #include <dlfcn.h>
57 #include <pthread.h>
58 #include <stdlib.h>
59 #include <libgen.h>
60
61 #elif defined(_WIN32)
62 // WinBase.h defines CreateSemaphore and synchapi.h defines CreateEvent
63 // undefine them to avoid conflicts with VkLayerDispatchTable struct members.
64 #if defined(CreateSemaphore)
65 #undef CreateSemaphore
66 #endif
67 #if defined(CreateEvent)
68 #undef CreateEvent
69 #endif
70 #include <stdio.h>
71 #include <io.h>
72 #include <shlwapi.h>
73 #include <direct.h>
74
75 #include "stack_allocation.h"
76 #endif // defined(_WIN32)
77
78 #if defined(APPLE_STATIC_LOADER) && !defined(__APPLE__)
79 #error "APPLE_STATIC_LOADER can only be defined on Apple platforms!"
80 #endif
81
82 #if defined(APPLE_STATIC_LOADER)
83 #define LOADER_EXPORT
84 #elif defined(__GNUC__) && __GNUC__ >= 4
85 #define LOADER_EXPORT __attribute__((visibility("default")))
86 #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
87 #define LOADER_EXPORT __attribute__((visibility("default")))
88 #else
89 #define LOADER_EXPORT
90 #endif
91
92 // For testing purposes, we want to expose some functions not normally callable on the library
93 #if defined(SHOULD_EXPORT_TEST_FUNCTIONS)
94 #if defined(_WIN32)
95 #define TEST_FUNCTION_EXPORT __declspec(dllexport)
96 #else
97 #define TEST_FUNCTION_EXPORT LOADER_EXPORT
98 #endif
99 #else
100 #define TEST_FUNCTION_EXPORT
101 #endif
102
103 #define MAX_STRING_SIZE 1024
104
105 // This is defined in vk_layer.h, but if there's problems we need to create the define
106 // here.
107 #if !defined(MAX_NUM_UNKNOWN_EXTS)
108 #define MAX_NUM_UNKNOWN_EXTS 250
109 #endif
110
111 // Environment Variable information
112 #define VK_ICD_FILENAMES_ENV_VAR "VK_ICD_FILENAMES" // Deprecated in v1.3.207 loader
113 #define VK_DRIVER_FILES_ENV_VAR "VK_DRIVER_FILES"
114 #define VK_EXPLICIT_LAYER_PATH_ENV_VAR "VK_LAYER_PATH"
115 // Support added in v1.3.207 loader
116 #define VK_ADDITIONAL_DRIVER_FILES_ENV_VAR "VK_ADD_DRIVER_FILES"
117 #define VK_ADDITIONAL_EXPLICIT_LAYER_PATH_ENV_VAR "VK_ADD_LAYER_PATH"
118 // Support added in v1.3.234 loader
119 #define VK_LAYERS_ENABLE_ENV_VAR "VK_LOADER_LAYERS_ENABLE"
120 #define VK_LAYERS_DISABLE_ENV_VAR "VK_LOADER_LAYERS_DISABLE"
121 #define VK_LAYERS_ALLOW_ENV_VAR "VK_LOADER_LAYERS_ALLOW"
122 #define VK_DRIVERS_SELECT_ENV_VAR "VK_LOADER_DRIVERS_SELECT"
123 #define VK_DRIVERS_DISABLE_ENV_VAR "VK_LOADER_DRIVERS_DISABLE"
124 #define VK_LOADER_DISABLE_ALL_LAYERS_VAR_1 "~all~"
125 #define VK_LOADER_DISABLE_ALL_LAYERS_VAR_2 "*"
126 #define VK_LOADER_DISABLE_ALL_LAYERS_VAR_3 "**"
127 #define VK_LOADER_DISABLE_IMPLICIT_LAYERS_VAR "~implicit~"
128 #define VK_LOADER_DISABLE_EXPLICIT_LAYERS_VAR "~explicit~"
129 // Support added in v1.3.295 loader
130 #define VK_IMPLICIT_LAYER_PATH_ENV_VAR "VK_IMPLICIT_LAYER_PATH"
131 #define VK_ADDITIONAL_IMPLICIT_LAYER_PATH_ENV_VAR "VK_ADD_IMPLICIT_LAYER_PATH"
132
133 // Override layer information
134 #define VK_OVERRIDE_LAYER_NAME "VK_LAYER_LUNARG_override"
135
136 // Loader Settings filename
137 #define VK_LOADER_SETTINGS_FILENAME "vk_loader_settings.json"
138
139 #define LAYERS_PATH_ENV "VK_LAYER_PATH"
140 #define ENABLED_LAYERS_ENV "VK_INSTANCE_LAYERS"
141
142 #if COMMON_UNIX_PLATFORMS
143 /* Linux-specific common code: */
144
145 // VK Library Filenames, Paths, etc.:
146 #define PATH_SEPARATOR ':'
147 #define DIRECTORY_SYMBOL '/'
148
149 #define VULKAN_DIR "vulkan/"
150 #define VULKAN_ICDCONF_DIR "icd.d"
151 #define VULKAN_ICD_DIR "icd"
152 #define VULKAN_SETTINGSCONF_DIR "settings.d"
153 #define VULKAN_ELAYERCONF_DIR "explicit_layer.d"
154 #define VULKAN_ILAYERCONF_DIR "implicit_layer.d"
155 #define VULKAN_LAYER_DIR "layer"
156
157 #define VK_DRIVERS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_ICDCONF_DIR
158 #define VK_SETTINGS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_SETTINGSCONF_DIR
159 #define VK_ELAYERS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_ELAYERCONF_DIR
160 #define VK_ILAYERS_INFO_RELATIVE_DIR VULKAN_DIR VULKAN_ILAYERCONF_DIR
161
162 #define VK_DRIVERS_INFO_REGISTRY_LOC ""
163 #define VK_ELAYERS_INFO_REGISTRY_LOC ""
164 #define VK_ILAYERS_INFO_REGISTRY_LOC ""
165 #define VK_SETTINGS_INFO_REGISTRY_LOC ""
166
167 #if defined(__QNX__)
168 #ifndef SYSCONFDIR
169 #define SYSCONFDIR "/etc"
170 #endif
171 #endif
172
173 // C99:
174 #define PRINTF_SIZE_T_SPECIFIER "%zu"
175
176 // Dynamic Loading of libraries:
177 typedef void *loader_platform_dl_handle;
178
179 // Threads:
180 typedef pthread_t loader_platform_thread;
181
182 // Thread IDs:
183 typedef pthread_t loader_platform_thread_id;
184
185 // Thread mutex:
186 typedef pthread_mutex_t loader_platform_thread_mutex;
187
188 typedef pthread_cond_t loader_platform_thread_cond;
189
190 #elif defined(_WIN32)
191 /* Windows-specific common code: */
192 // VK Library Filenames, Paths, etc.:
193 #define PATH_SEPARATOR ';'
194 #define DIRECTORY_SYMBOL '\\'
195 #define DEFAULT_VK_REGISTRY_HIVE HKEY_LOCAL_MACHINE
196 #define DEFAULT_VK_REGISTRY_HIVE_STR "HKEY_LOCAL_MACHINE"
197 #define SECONDARY_VK_REGISTRY_HIVE HKEY_CURRENT_USER
198 #define SECONDARY_VK_REGISTRY_HIVE_STR "HKEY_CURRENT_USER"
199
200 #define VK_DRIVERS_INFO_RELATIVE_DIR ""
201 #define VK_SETTINGS_INFO_RELATIVE_DIR ""
202 #define VK_ELAYERS_INFO_RELATIVE_DIR ""
203 #define VK_ILAYERS_INFO_RELATIVE_DIR ""
204
205 #define VK_VARIANT_REG_STR ""
206 #define VK_VARIANT_REG_STR_W L""
207
208 #define VK_DRIVERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\Drivers"
209 #define VK_ELAYERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\ExplicitLayers"
210 #define VK_ILAYERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\ImplicitLayers"
211 #define VK_SETTINGS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\LoaderSettings"
212
213 #define PRINTF_SIZE_T_SPECIFIER "%Iu"
214
215 // Dynamic Loading:
216 typedef HMODULE loader_platform_dl_handle;
217
218 // Threads:
219 typedef HANDLE loader_platform_thread;
220
221 // Thread IDs:
222 typedef DWORD loader_platform_thread_id;
223
224 // Thread mutex:
225 typedef CRITICAL_SECTION loader_platform_thread_mutex;
226
227 typedef CONDITION_VARIABLE loader_platform_thread_cond;
228
229 #else
230
231 #warning The "vk_loader_platform.h" file must be modified for this OS.
232
233 // NOTE: In order to support another OS, an #elif needs to be added (above the
234 // "#else // defined(_WIN32)") for that OS, and OS-specific versions of the
235 // contents of this file must be created, or extend one of the existing OS specific
236 // sections with the necessary changes.
237
238 #endif
239
240 // controls whether loader_platform_close_library() closes the libraries or not - controlled by an environment variables
241 extern bool loader_disable_dynamic_library_unloading;
242
243 // Returns true if the DIRECTORY_SYMBOL is contained within path
loader_platform_is_path(const char * path)244 static inline bool loader_platform_is_path(const char *path) { return strchr(path, DIRECTORY_SYMBOL) != NULL; }
245
246 // The loader has various initialization tasks which it must do before user code can run. This includes initializing synchronization
247 // objects, determining the log level, writing the version of the loader to the log, and loading dll's (on windows). On linux, the
248 // solution is simply running the initialization code in __attribute__((constructor)), which MacOS uses when the loader is
249 // dynamically linked. When statically linking on MacOS, the setup code instead uses pthread_once to run the logic a single time
250 // regardless of which API function the application calls first. On Windows, the equivalent way to run code at dll load time is
251 // DllMain which has many limitations placed upon it. Instead, the Windows code follows MacOS and does initialization in the first
252 // API call made, using InitOnceExecuteOnce, except for initialization primitives which must be done in DllMain. This is because
253 // there is no way to clean up the resources allocated by anything allocated by once init.
254
255 #if defined(APPLE_STATIC_LOADER)
loader_platform_thread_once_fn(pthread_once_t * ctl,void (* func)(void))256 static inline void loader_platform_thread_once_fn(pthread_once_t *ctl, void (*func)(void)) {
257 assert(func != NULL);
258 assert(ctl != NULL);
259 pthread_once(ctl, func);
260 }
261 #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) pthread_once_t var = PTHREAD_ONCE_INIT;
262 #define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var) extern pthread_once_t var;
263 #define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_once_fn(ctl, func);
264 #elif defined(WIN32)
loader_platform_thread_win32_once_fn(INIT_ONCE * ctl,PINIT_ONCE_FN func)265 static inline void loader_platform_thread_win32_once_fn(INIT_ONCE *ctl, PINIT_ONCE_FN func) {
266 InitOnceExecuteOnce(ctl, func, NULL, NULL);
267 }
268 #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) INIT_ONCE var = INIT_ONCE_STATIC_INIT;
269 #define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var) extern INIT_ONCE var;
270 #define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_win32_once_fn(ctl, func);
271 #else
272 #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var)
273 #define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var)
274 #define LOADER_PLATFORM_THREAD_ONCE(ctl, func)
275
276 #endif
277
278 #if COMMON_UNIX_PLATFORMS
279
280 // File IO
loader_platform_file_exists(const char * path)281 static inline bool loader_platform_file_exists(const char *path) {
282 if (access(path, F_OK))
283 return false;
284 else
285 return true;
286 }
287
288 // Returns true if the given string appears to be a relative or absolute
289 // path, as opposed to a bare filename.
loader_platform_is_path_absolute(const char * path)290 static inline bool loader_platform_is_path_absolute(const char *path) {
291 if (path[0] == '/')
292 return true;
293 else
294 return false;
295 }
296
loader_platform_dirname(char * path)297 static inline char *loader_platform_dirname(char *path) { return dirname(path); }
298
299 // loader_platform_executable_path finds application path + name.
300 // Path cannot be longer than 1024, returns NULL if it is greater than that.
301 #if defined(__linux__) || defined(__GNU__)
loader_platform_executable_path(char * buffer,size_t size)302 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
303 ssize_t count = readlink("/proc/self/exe", buffer, size);
304 if (count == -1) return NULL;
305 if (count == 0) return NULL;
306 buffer[count] = '\0';
307 return buffer;
308 }
309 #elif defined(__APPLE__)
310 #include <TargetConditionals.h>
311 // TARGET_OS_IPHONE isn't just iOS it's also iOS/tvOS/watchOS. See TargetConditionals.h documentation.
312 #if TARGET_OS_IPHONE
loader_platform_executable_path(char * buffer,size_t size)313 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
314 (void)size;
315 buffer[0] = '\0';
316 return buffer;
317 }
318 #endif
319 #if TARGET_OS_OSX
320 #include <libproc.h>
loader_platform_executable_path(char * buffer,size_t size)321 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
322 // proc_pidpath takes a uint32_t for the buffer size
323 if (size > UINT32_MAX) {
324 return NULL;
325 }
326 pid_t pid = getpid();
327 int ret = proc_pidpath(pid, buffer, (uint32_t)size);
328 if (ret <= 0) {
329 return NULL;
330 }
331 buffer[ret] = '\0';
332 return buffer;
333 }
334 #endif
335 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
336 #include <sys/sysctl.h>
loader_platform_executable_path(char * buffer,size_t size)337 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
338 int mib[] = {
339 CTL_KERN,
340 #if defined(__NetBSD__)
341 KERN_PROC_ARGS,
342 -1,
343 KERN_PROC_PATHNAME,
344 #else
345 KERN_PROC,
346 KERN_PROC_PATHNAME,
347 -1,
348 #endif
349 };
350 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buffer, &size, NULL, 0) < 0) {
351 return NULL;
352 }
353
354 return buffer;
355 }
356 #elif defined(__Fuchsia__) || defined(__OpenBSD__)
loader_platform_executable_path(char * buffer,size_t size)357 static inline char *loader_platform_executable_path(char *buffer, size_t size) { return NULL; }
358 #elif defined(__QNX__)
359
360 #ifndef SYSCONFDIR
361 #define SYSCONFDIR "/etc"
362 #endif
363
364 #include <fcntl.h>
365 #include <sys/stat.h>
366
loader_platform_executable_path(char * buffer,size_t size)367 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
368 int fd = open("/proc/self/exefile", O_RDONLY);
369 size_t rdsize;
370
371 if (fd == -1) {
372 return NULL;
373 }
374
375 rdsize = read(fd, buffer, size);
376 if (rdsize == size) {
377 return NULL;
378 }
379 buffer[rdsize] = 0x00;
380 close(fd);
381
382 return buffer;
383 }
384 #endif // defined (__QNX__)
385
386 // Compatibility with compilers that don't support __has_feature
387 #if !defined(__has_feature)
388 #define __has_feature(x) 0
389 #endif
390
391 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
392 #define LOADER_ADDRESS_SANITIZER_ACTIVE // TODO: Add proper build flag for ASAN support
393 #endif
394
395 // When loading the library, we use RTLD_LAZY so that not all symbols have to be
396 // resolved at this time (which improves performance). Note that if not all symbols
397 // can be resolved, this could cause crashes later. Use the LD_BIND_NOW environment
398 // variable to force all symbols to be resolved here.
399 #define LOADER_DLOPEN_MODE (RTLD_LAZY | RTLD_LOCAL)
400
401 #if defined(__Fuchsia__)
loader_platform_open_driver(const char * libPath)402 static inline loader_platform_dl_handle loader_platform_open_driver(const char *libPath) {
403 return dlopen_fuchsia(libPath, LOADER_DLOPEN_MODE, true);
404 }
loader_platform_open_library(const char * libPath)405 static inline loader_platform_dl_handle loader_platform_open_library(const char *libPath) {
406 return dlopen_fuchsia(libPath, LOADER_DLOPEN_MODE, false);
407 }
408 #elif defined(VK_USE_PLATFORM_OHOS)
loader_platform_open_library(const char * libPath)409 static inline loader_platform_dl_handle loader_platform_open_library(const char *libPath) {
410 void *handle = NULL;
411 Dl_namespace ns_ps;
412 if (!dlns_get("passthrough", &ns_ps)) {
413 handle = dlopen_ns(&ns_ps, libPath, LOADER_DLOPEN_MODE);
414 }
415 if (!handle) {
416 handle = dlopen(libPath, LOADER_DLOPEN_MODE);
417 }
418 return handle;
419 }
420 #else
loader_platform_open_library(const char * libPath)421 static inline loader_platform_dl_handle loader_platform_open_library(const char *libPath) {
422 return dlopen(libPath, LOADER_DLOPEN_MODE);
423 }
424 #endif
425
loader_platform_open_library_error(const char * libPath)426 static inline const char *loader_platform_open_library_error(const char *libPath) {
427 (void)libPath;
428 #if defined(__Fuchsia__)
429 return dlerror_fuchsia();
430 #else
431 return dlerror();
432 #endif
433 }
loader_platform_close_library(loader_platform_dl_handle library)434 static inline void loader_platform_close_library(loader_platform_dl_handle library) {
435 if (!loader_disable_dynamic_library_unloading) {
436 dlclose(library);
437 } else {
438 (void)library;
439 }
440 }
loader_platform_get_proc_address(loader_platform_dl_handle library,const char * name)441 static inline void *loader_platform_get_proc_address(loader_platform_dl_handle library, const char *name) {
442 assert(library);
443 assert(name);
444 return dlsym(library, name);
445 }
loader_platform_get_proc_address_error(const char * name)446 static inline const char *loader_platform_get_proc_address_error(const char *name) {
447 (void)name;
448 return dlerror();
449 }
450
451 // Thread mutex:
loader_platform_thread_create_mutex(loader_platform_thread_mutex * pMutex)452 static inline void loader_platform_thread_create_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_init(pMutex, NULL); }
loader_platform_thread_lock_mutex(loader_platform_thread_mutex * pMutex)453 static inline void loader_platform_thread_lock_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_lock(pMutex); }
loader_platform_thread_unlock_mutex(loader_platform_thread_mutex * pMutex)454 static inline void loader_platform_thread_unlock_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_unlock(pMutex); }
loader_platform_thread_delete_mutex(loader_platform_thread_mutex * pMutex)455 static inline void loader_platform_thread_delete_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_destroy(pMutex); }
456
thread_safe_strtok(char * str,const char * delim,char ** saveptr)457 static inline void *thread_safe_strtok(char *str, const char *delim, char **saveptr) { return strtok_r(str, delim, saveptr); }
458
loader_fopen(const char * fileName,const char * mode)459 static inline FILE *loader_fopen(const char *fileName, const char *mode) { return fopen(fileName, mode); }
loader_strncat(char * dest,size_t dest_sz,const char * src,size_t count)460 static inline char *loader_strncat(char *dest, size_t dest_sz, const char *src, size_t count) {
461 (void)dest_sz;
462 return strncat(dest, src, count);
463 }
loader_strncpy(char * dest,size_t dest_sz,const char * src,size_t count)464 static inline char *loader_strncpy(char *dest, size_t dest_sz, const char *src, size_t count) {
465 (void)dest_sz;
466 return strncpy(dest, src, count);
467 }
468
469 #elif defined(_WIN32)
470
471 // Get the key for the plug n play driver registry
472 // The string returned by this function should NOT be freed
LoaderPnpDriverRegistry()473 static inline const char *LoaderPnpDriverRegistry() {
474 BOOL is_wow;
475 IsWow64Process(GetCurrentProcess(), &is_wow);
476 return is_wow ? "Vulkan" VK_VARIANT_REG_STR "DriverNameWow" : "Vulkan" VK_VARIANT_REG_STR "DriverName";
477 }
LoaderPnpDriverRegistryWide()478 static inline const wchar_t *LoaderPnpDriverRegistryWide() {
479 BOOL is_wow;
480 IsWow64Process(GetCurrentProcess(), &is_wow);
481 return is_wow ? L"Vulkan" VK_VARIANT_REG_STR_W L"DriverNameWow" : L"Vulkan" VK_VARIANT_REG_STR_W L"DriverName";
482 }
483
484 // Get the key for the plug 'n play explicit layer registry
485 // The string returned by this function should NOT be freed
LoaderPnpELayerRegistry()486 static inline const char *LoaderPnpELayerRegistry() {
487 BOOL is_wow;
488 IsWow64Process(GetCurrentProcess(), &is_wow);
489 return is_wow ? "Vulkan" VK_VARIANT_REG_STR "ExplicitLayersWow" : "Vulkan" VK_VARIANT_REG_STR "ExplicitLayers";
490 }
LoaderPnpELayerRegistryWide()491 static inline const wchar_t *LoaderPnpELayerRegistryWide() {
492 BOOL is_wow;
493 IsWow64Process(GetCurrentProcess(), &is_wow);
494 return is_wow ? L"Vulkan" VK_VARIANT_REG_STR_W L"ExplicitLayersWow" : L"Vulkan" VK_VARIANT_REG_STR_W L"ExplicitLayers";
495 }
496
497 // Get the key for the plug 'n play implicit layer registry
498 // The string returned by this function should NOT be freed
LoaderPnpILayerRegistry()499 static inline const char *LoaderPnpILayerRegistry() {
500 BOOL is_wow;
501 IsWow64Process(GetCurrentProcess(), &is_wow);
502 return is_wow ? "Vulkan" VK_VARIANT_REG_STR "ImplicitLayersWow" : "Vulkan" VK_VARIANT_REG_STR "ImplicitLayers";
503 }
LoaderPnpILayerRegistryWide()504 static inline const wchar_t *LoaderPnpILayerRegistryWide() {
505 BOOL is_wow;
506 IsWow64Process(GetCurrentProcess(), &is_wow);
507 return is_wow ? L"Vulkan" VK_VARIANT_REG_STR_W L"ImplicitLayersWow" : L"Vulkan" VK_VARIANT_REG_STR_W L"ImplicitLayers";
508 }
509
510 // File IO
loader_platform_file_exists(const char * path)511 static inline bool loader_platform_file_exists(const char *path) {
512 int path_utf16_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
513 if (path_utf16_size <= 0) {
514 return false;
515 }
516 wchar_t *path_utf16 = (wchar_t *)loader_stack_alloc(path_utf16_size * sizeof(wchar_t));
517 if (MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, path_utf16_size) != path_utf16_size) {
518 return false;
519 }
520 if (_waccess(path_utf16, 0) == -1)
521 return false;
522 else
523 return true;
524 }
525
526 // Returns true if the given string appears to be a relative or absolute
527 // path, as opposed to a bare filename.
loader_platform_is_path_absolute(const char * path)528 static inline bool loader_platform_is_path_absolute(const char *path) {
529 if (!path || !*path) {
530 return false;
531 }
532 if (*path == DIRECTORY_SYMBOL || path[1] == ':') {
533 return true;
534 }
535 return false;
536 }
537
538 // WIN32 runtime doesn't have dirname().
loader_platform_dirname(char * path)539 static inline char *loader_platform_dirname(char *path) {
540 char *current, *next;
541
542 // TODO/TBD: Do we need to deal with the Windows's ":" character?
543
544 for (current = path; *current != '\0'; current = next) {
545 next = strchr(current, DIRECTORY_SYMBOL);
546 if (next == NULL) {
547 if (current != path) *(current - 1) = '\0';
548 return path;
549 } else {
550 // Point one character past the DIRECTORY_SYMBOL:
551 next++;
552 }
553 }
554 return path;
555 }
556
loader_platform_executable_path(char * buffer,size_t size)557 static inline char *loader_platform_executable_path(char *buffer, size_t size) {
558 wchar_t *buffer_utf16 = (wchar_t *)loader_stack_alloc(size * sizeof(wchar_t));
559 DWORD ret = GetModuleFileNameW(NULL, buffer_utf16, (DWORD)size);
560 if (ret == 0) {
561 return NULL;
562 }
563 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
564 return NULL;
565 }
566 int buffer_utf8_size = WideCharToMultiByte(CP_UTF8, 0, buffer_utf16, -1, NULL, 0, NULL, NULL);
567 if (buffer_utf8_size <= 0 || (size_t)buffer_utf8_size > size) {
568 return NULL;
569 }
570 if (WideCharToMultiByte(CP_UTF8, 0, buffer_utf16, -1, buffer, buffer_utf8_size, NULL, NULL) != buffer_utf8_size) {
571 return NULL;
572 }
573 return buffer;
574 }
575
576 // Dynamic Loading:
loader_platform_open_library(const char * lib_path)577 static inline loader_platform_dl_handle loader_platform_open_library(const char *lib_path) {
578 int lib_path_utf16_size = MultiByteToWideChar(CP_UTF8, 0, lib_path, -1, NULL, 0);
579 if (lib_path_utf16_size <= 0) {
580 return NULL;
581 }
582 wchar_t *lib_path_utf16 = (wchar_t *)loader_stack_alloc(lib_path_utf16_size * sizeof(wchar_t));
583 if (MultiByteToWideChar(CP_UTF8, 0, lib_path, -1, lib_path_utf16, lib_path_utf16_size) != lib_path_utf16_size) {
584 return NULL;
585 }
586 // Try loading the library the original way first.
587 loader_platform_dl_handle lib_handle = LoadLibraryW(lib_path_utf16);
588 if (lib_handle == NULL && GetLastError() == ERROR_MOD_NOT_FOUND) {
589 // If that failed, then try loading it with broader search folders.
590 lib_handle = LoadLibraryExW(lib_path_utf16, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
591 }
592 return lib_handle;
593 }
loader_platform_open_library_error(const char * libPath)594 static inline const char *loader_platform_open_library_error(const char *libPath) {
595 static char errorMsg[512];
596 (void)snprintf(errorMsg, 511, "Failed to open dynamic library \"%s\" with error %lu", libPath, GetLastError());
597 return errorMsg;
598 }
loader_platform_close_library(loader_platform_dl_handle library)599 static inline void loader_platform_close_library(loader_platform_dl_handle library) {
600 if (!loader_disable_dynamic_library_unloading) {
601 FreeLibrary(library);
602 } else {
603 (void)library;
604 }
605 }
loader_platform_get_proc_address(loader_platform_dl_handle library,const char * name)606 static inline void *loader_platform_get_proc_address(loader_platform_dl_handle library, const char *name) {
607 assert(library);
608 assert(name);
609 return (void *)GetProcAddress(library, name);
610 }
loader_platform_get_proc_address_error(const char * name)611 static inline const char *loader_platform_get_proc_address_error(const char *name) {
612 static char errorMsg[120];
613 (void)snprintf(errorMsg, 119, "Failed to find function \"%s\" in dynamic library", name);
614 return errorMsg;
615 }
616
617 // Thread mutex:
loader_platform_thread_create_mutex(loader_platform_thread_mutex * pMutex)618 static inline void loader_platform_thread_create_mutex(loader_platform_thread_mutex *pMutex) { InitializeCriticalSection(pMutex); }
loader_platform_thread_lock_mutex(loader_platform_thread_mutex * pMutex)619 static inline void loader_platform_thread_lock_mutex(loader_platform_thread_mutex *pMutex) { EnterCriticalSection(pMutex); }
loader_platform_thread_unlock_mutex(loader_platform_thread_mutex * pMutex)620 static inline void loader_platform_thread_unlock_mutex(loader_platform_thread_mutex *pMutex) { LeaveCriticalSection(pMutex); }
loader_platform_thread_delete_mutex(loader_platform_thread_mutex * pMutex)621 static inline void loader_platform_thread_delete_mutex(loader_platform_thread_mutex *pMutex) { DeleteCriticalSection(pMutex); }
622
thread_safe_strtok(char * str,const char * delimiters,char ** context)623 static inline void *thread_safe_strtok(char *str, const char *delimiters, char **context) {
624 return strtok_s(str, delimiters, context);
625 }
626
loader_fopen(const char * fileName,const char * mode)627 static inline FILE *loader_fopen(const char *fileName, const char *mode) {
628 FILE *file = NULL;
629 errno_t err = fopen_s(&file, fileName, mode);
630 if (err != 0) return NULL;
631 return file;
632 }
633
loader_strncat(char * dest,size_t dest_sz,const char * src,size_t count)634 static inline char *loader_strncat(char *dest, size_t dest_sz, const char *src, size_t count) {
635 errno_t err = strncat_s(dest, dest_sz, src, count);
636 if (err != 0) return NULL;
637 return dest;
638 }
639
loader_strncpy(char * dest,size_t dest_sz,const char * src,size_t count)640 static inline char *loader_strncpy(char *dest, size_t dest_sz, const char *src, size_t count) {
641 errno_t err = strncpy_s(dest, dest_sz, src, count);
642 if (err != 0) return NULL;
643 return dest;
644 }
645
646 #else // defined(_WIN32)
647
648 #warning The "vk_loader_platform.h" file must be modified for this OS.
649
650 // NOTE: In order to support another OS, an #elif needs to be added (above the
651 // "#else // defined(_WIN32)") for that OS, and OS-specific versions of the
652 // contents of this file must be created.
653
654 // NOTE: Other OS-specific changes are also needed for this OS. Search for
655 // files with "WIN32" in it, as a quick way to find files that must be changed.
656
657 #endif // defined(_WIN32)
658