1/*
2 *
3 * Copyright (c) 2023 The Khronos Group Inc.
4 * Copyright (c) 2023 Valve Corporation
5 * Copyright (c) 2023 LunarG, Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *
20 * Author: Charles Giessen <charles@lunarg.com>
21 *
22 */
23
24#include "settings.h"
25
26#include "allocation.h"
27#include "cJSON.h"
28#include "loader.h"
29#include "loader_environment.h"
30#include "loader_windows.h"
31#include "log.h"
32#include "stack_allocation.h"
33#include "vk_loader_platform.h"
34
35loader_platform_thread_mutex global_loader_settings_lock;
36loader_settings global_loader_settings;
37
38void free_layer_configuration(const struct loader_instance* inst, loader_settings_layer_configuration* layer_configuration) {
39    loader_instance_heap_free(inst, layer_configuration->name);
40    loader_instance_heap_free(inst, layer_configuration->path);
41    memset(layer_configuration, 0, sizeof(loader_settings_layer_configuration));
42}
43
44void free_loader_settings(const struct loader_instance* inst, loader_settings* settings) {
45    if (NULL != settings->layer_configurations) {
46        for (uint32_t i = 0; i < settings->layer_configuration_count; i++) {
47            free_layer_configuration(inst, &settings->layer_configurations[i]);
48        }
49    }
50    loader_instance_heap_free(inst, settings->layer_configurations);
51    loader_instance_heap_free(inst, settings->settings_file_path);
52    memset(settings, 0, sizeof(loader_settings));
53}
54
55loader_settings_layer_control parse_control_string(char* control_string) {
56    loader_settings_layer_control layer_control = LOADER_SETTINGS_LAYER_CONTROL_DEFAULT;
57    if (strcmp(control_string, "auto") == 0)
58        layer_control = LOADER_SETTINGS_LAYER_CONTROL_DEFAULT;
59    else if (strcmp(control_string, "on") == 0)
60        layer_control = LOADER_SETTINGS_LAYER_CONTROL_ON;
61    else if (strcmp(control_string, "off") == 0)
62        layer_control = LOADER_SETTINGS_LAYER_CONTROL_OFF;
63    else if (strcmp(control_string, "unordered_layer_location") == 0)
64        layer_control = LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION;
65    return layer_control;
66}
67
68const char* loader_settings_layer_control_to_string(loader_settings_layer_control control) {
69    switch (control) {
70        case (LOADER_SETTINGS_LAYER_CONTROL_DEFAULT):
71            return "auto";
72        case (LOADER_SETTINGS_LAYER_CONTROL_ON):
73            return "on";
74        case (LOADER_SETTINGS_LAYER_CONTROL_OFF):
75            return "off";
76        case (LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION):
77            return "unordered_layer_location";
78        default:
79            return "UNKNOWN_LAYER_CONTROl";
80    }
81}
82
83uint32_t parse_log_filters_from_strings(struct loader_string_list* log_filters) {
84    uint32_t filters = 0;
85    for (uint32_t i = 0; i < log_filters->count; i++) {
86        if (strcmp(log_filters->list[i], "all") == 0)
87            filters |= VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_PERF_BIT | VULKAN_LOADER_ERROR_BIT |
88                       VULKAN_LOADER_DEBUG_BIT | VULKAN_LOADER_LAYER_BIT | VULKAN_LOADER_DRIVER_BIT | VULKAN_LOADER_VALIDATION_BIT;
89        else if (strcmp(log_filters->list[i], "info") == 0)
90            filters |= VULKAN_LOADER_INFO_BIT;
91        else if (strcmp(log_filters->list[i], "warn") == 0)
92            filters |= VULKAN_LOADER_WARN_BIT;
93        else if (strcmp(log_filters->list[i], "perf") == 0)
94            filters |= VULKAN_LOADER_PERF_BIT;
95        else if (strcmp(log_filters->list[i], "error") == 0)
96            filters |= VULKAN_LOADER_ERROR_BIT;
97        else if (strcmp(log_filters->list[i], "debug") == 0)
98            filters |= VULKAN_LOADER_DEBUG_BIT;
99        else if (strcmp(log_filters->list[i], "layer") == 0)
100            filters |= VULKAN_LOADER_LAYER_BIT;
101        else if (strcmp(log_filters->list[i], "driver") == 0)
102            filters |= VULKAN_LOADER_DRIVER_BIT;
103        else if (strcmp(log_filters->list[i], "validation") == 0)
104            filters |= VULKAN_LOADER_VALIDATION_BIT;
105    }
106    return filters;
107}
108
109bool parse_json_enable_disable_option(const struct loader_instance* inst, cJSON* object, const char* key) {
110    char* str = NULL;
111    VkResult res = loader_parse_json_string(object, key, &str);
112    if (res != VK_SUCCESS || NULL == str) {
113        return false;
114    }
115    bool enable = false;
116    if (strcmp(str, "enabled") == 0) {
117        enable = true;
118    }
119    loader_instance_heap_free(inst, str);
120    return enable;
121}
122
123VkResult parse_layer_configuration(const struct loader_instance* inst, cJSON* layer_configuration_json,
124                                   loader_settings_layer_configuration* layer_configuration) {
125    char* control_string = NULL;
126    VkResult res = loader_parse_json_string(layer_configuration_json, "control", &control_string);
127    if (res != VK_SUCCESS) {
128        goto out;
129    }
130    layer_configuration->control = parse_control_string(control_string);
131    loader_instance_heap_free(inst, control_string);
132
133    // If that is the only value - do no further parsing
134    if (layer_configuration->control == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
135        goto out;
136    }
137
138    res = loader_parse_json_string(layer_configuration_json, "name", &(layer_configuration->name));
139    if (res != VK_SUCCESS) {
140        goto out;
141    }
142
143    res = loader_parse_json_string(layer_configuration_json, "path", &(layer_configuration->path));
144    if (res != VK_SUCCESS) {
145        goto out;
146    }
147
148    cJSON* treat_as_implicit_manifest = loader_cJSON_GetObjectItem(layer_configuration_json, "treat_as_implicit_manifest");
149    if (treat_as_implicit_manifest && treat_as_implicit_manifest->type == cJSON_True) {
150        layer_configuration->treat_as_implicit_manifest = true;
151    }
152out:
153    if (VK_SUCCESS != res) {
154        free_layer_configuration(inst, layer_configuration);
155    }
156    return res;
157}
158
159VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* settings_object, loader_settings* loader_settings) {
160    VkResult res = VK_SUCCESS;
161
162    cJSON* layer_configurations = loader_cJSON_GetObjectItem(settings_object, "layers");
163    if (NULL == layer_configurations) {
164        return VK_ERROR_INITIALIZATION_FAILED;
165    }
166
167    uint32_t layer_configurations_count = loader_cJSON_GetArraySize(layer_configurations);
168    if (layer_configurations_count == 0) {
169        return VK_SUCCESS;
170    }
171
172    loader_settings->layer_configuration_count = layer_configurations_count;
173
174    loader_settings->layer_configurations = loader_instance_heap_calloc(
175        inst, sizeof(loader_settings_layer_configuration) * layer_configurations_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
176    if (NULL == loader_settings->layer_configurations) {
177        res = VK_ERROR_OUT_OF_HOST_MEMORY;
178        goto out;
179    }
180
181    for (uint32_t i = 0; i < layer_configurations_count; i++) {
182        cJSON* layer = loader_cJSON_GetArrayItem(layer_configurations, i);
183        if (NULL == layer) {
184            res = VK_ERROR_INITIALIZATION_FAILED;
185            goto out;
186        }
187        res = parse_layer_configuration(inst, layer, &(loader_settings->layer_configurations[i]));
188        if (VK_SUCCESS != res) {
189            goto out;
190        }
191    }
192out:
193    if (res != VK_SUCCESS) {
194        if (loader_settings->layer_configurations) {
195            for (uint32_t i = 0; i < loader_settings->layer_configuration_count; i++) {
196                free_layer_configuration(inst, &(loader_settings->layer_configurations[i]));
197            }
198            loader_settings->layer_configuration_count = 0;
199            loader_instance_heap_free(inst, loader_settings->layer_configurations);
200            loader_settings->layer_configurations = NULL;
201        }
202    }
203
204    return res;
205}
206
207VkResult check_if_settings_path_exists(const struct loader_instance* inst, char* base, char* suffix, char** settings_file_path) {
208    if (NULL == base || NULL == suffix) {
209        return VK_ERROR_INITIALIZATION_FAILED;
210    }
211    size_t base_len = strlen(base);
212    size_t suffix_len = strlen(suffix);
213    size_t path_len = base_len + suffix_len + 1;
214    *settings_file_path = loader_instance_heap_calloc(inst, path_len, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
215    if (NULL == *settings_file_path) {
216        return VK_ERROR_OUT_OF_HOST_MEMORY;
217    }
218    loader_strncpy(*settings_file_path, path_len, base, base_len);
219    loader_strncat(*settings_file_path, path_len, suffix, suffix_len);
220
221    if (!loader_platform_file_exists(*settings_file_path)) {
222        loader_instance_heap_free(inst, *settings_file_path);
223        *settings_file_path = NULL;
224        return VK_ERROR_INITIALIZATION_FAILED;
225    }
226    return VK_SUCCESS;
227}
228VkResult get_unix_settings_path(const struct loader_instance* inst, char** settings_file_path) {
229    VkResult res =
230        check_if_settings_path_exists(inst, loader_secure_getenv("HOME", inst),
231                                      "/.local/share/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, settings_file_path);
232    if (res == VK_SUCCESS) {
233        return res;
234    }
235    // If HOME isn't set, fallback to XDG_DATA_HOME
236    res = check_if_settings_path_exists(inst, loader_secure_getenv("XDG_DATA_HOME", inst),
237                                        "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, settings_file_path);
238    if (res == VK_SUCCESS) {
239        return res;
240    }
241    // if XDG_DATA_HOME isn't set, fallback to /etc.
242    // note that the settings_fil_path_suffix stays the same since its the same layout as for XDG_DATA_HOME
243    return check_if_settings_path_exists(inst, "/etc", "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME,
244                                         settings_file_path);
245}
246
247bool check_if_settings_are_equal(loader_settings* a, loader_settings* b) {
248    // If either pointer is null, return true
249    if (NULL == a || NULL == b) return false;
250    bool are_equal = true;
251    are_equal &= a->settings_active == b->settings_active;
252    are_equal &= a->has_unordered_layer_location == b->has_unordered_layer_location;
253    are_equal &= a->debug_level == b->debug_level;
254    are_equal &= a->layer_configuration_count == b->layer_configuration_count;
255    if (!are_equal) return false;
256    for (uint32_t i = 0; i < a->layer_configuration_count && i < b->layer_configuration_count; i++) {
257        if (a->layer_configurations[i].name && b->layer_configurations[i].name) {
258            are_equal &= 0 == strcmp(a->layer_configurations[i].name, b->layer_configurations[i].name);
259        } else {
260            are_equal = false;
261        }
262        if (a->layer_configurations[i].path && b->layer_configurations[i].path) {
263            are_equal &= 0 == strcmp(a->layer_configurations[i].path, b->layer_configurations[i].path);
264        } else {
265            are_equal = false;
266        }
267        are_equal &= a->layer_configurations[i].control == b->layer_configurations[i].control;
268    }
269    return are_equal;
270}
271
272void log_settings(const struct loader_instance* inst, loader_settings* settings) {
273    if (settings == NULL) {
274        return;
275    }
276    loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Using layer configurations found in loader settings from %s",
277               settings->settings_file_path);
278
279    loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Layer Configurations count = %d", settings->layer_configuration_count);
280    for (uint32_t i = 0; i < settings->layer_configuration_count; i++) {
281        loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---- Layer Configuration [%d] ----", i);
282        if (settings->layer_configurations[i].control != LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
283            loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Name: %s", settings->layer_configurations[i].name);
284            loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Path: %s", settings->layer_configurations[i].path);
285        }
286        loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Control: %s",
287                   loader_settings_layer_control_to_string(settings->layer_configurations[i].control));
288    }
289    loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---------------------------------");
290}
291
292// Loads the vk_loader_settings.json file
293// Returns VK_SUCCESS if it was found & was successfully parsed. Otherwise, it returns VK_ERROR_INITIALIZATION_FAILED if it
294// wasn't found or failed to parse, and returns VK_ERROR_OUT_OF_HOST_MEMORY if it was unable to allocate enough memory.
295VkResult get_loader_settings(const struct loader_instance* inst, loader_settings* loader_settings) {
296    VkResult res = VK_SUCCESS;
297    cJSON* json = NULL;
298    char* file_format_version_string = NULL;
299    char* settings_file_path = NULL;
300#if defined(WIN32)
301    res = windows_get_loader_settings_file_path(inst, &settings_file_path);
302    if (res != VK_SUCCESS) {
303        goto out;
304    }
305
306#elif COMMON_UNIX_PLATFORMS
307    res = get_unix_settings_path(inst, &settings_file_path);
308    if (res != VK_SUCCESS) {
309        goto out;
310    }
311#else
312#warning "Unsupported platform - must specify platform specific location for vk_loader_settings.json"
313#endif
314
315    res = loader_get_json(inst, settings_file_path, &json);
316    // Make sure sure the top level json value is an object
317    if (res != VK_SUCCESS || NULL == json || json->type != 6) {
318        goto out;
319    }
320
321    res = loader_parse_json_string(json, "file_format_version", &file_format_version_string);
322    if (res != VK_SUCCESS) {
323        goto out;
324    }
325    uint32_t settings_array_count = 0;
326    bool has_multi_setting_file = false;
327    cJSON* settings_array = loader_cJSON_GetObjectItem(json, "settings_array");
328    cJSON* single_settings_object = loader_cJSON_GetObjectItem(json, "settings");
329    if (NULL != settings_array) {
330        has_multi_setting_file = true;
331        settings_array_count = loader_cJSON_GetArraySize(settings_array);
332    } else if (NULL != single_settings_object) {
333        settings_array_count = 1;
334    }
335
336    // Corresponds to the settings object that has no app keys
337    int global_settings_index = -1;
338    // Corresponds to the settings object which has a matching app key
339    int index_to_use = -1;
340
341    char current_process_path[1024];
342    bool valid_exe_path = NULL != loader_platform_executable_path(current_process_path, 1024);
343
344    for (int i = 0; i < (int)settings_array_count; i++) {
345        if (has_multi_setting_file) {
346            single_settings_object = loader_cJSON_GetArrayItem(settings_array, i);
347        }
348        cJSON* app_keys = loader_cJSON_GetObjectItem(single_settings_object, "app_keys");
349        if (NULL == app_keys) {
350            if (global_settings_index == -1) {
351                global_settings_index = i;  // use the first 'global' settings that has no app keys as the global one
352            }
353            continue;
354        } else if (valid_exe_path) {
355            int app_key_count = loader_cJSON_GetArraySize(app_keys);
356            if (app_key_count == 0) {
357                continue;  // empty array
358            }
359            for (int j = 0; j < app_key_count; j++) {
360                cJSON* app_key_json = loader_cJSON_GetArrayItem(app_keys, j);
361                if (NULL == app_key_json) {
362                    continue;
363                }
364                char* app_key = loader_cJSON_Print(app_key_json);
365                if (NULL == app_key) {
366                    continue;
367                }
368
369                if (strcmp(current_process_path, app_key) == 0) {
370                    index_to_use = i;
371                }
372                loader_instance_heap_free(inst, app_key);
373                if (index_to_use == i) {
374                    break;  // break only after freeing the app key
375                }
376            }
377        }
378    }
379
380    // No app specific settings match - either use global settings or exit
381    if (index_to_use == -1) {
382        if (global_settings_index == -1) {
383            goto out;  // No global settings were found - exit
384        } else {
385            index_to_use = global_settings_index;  // Global settings are present - use it
386        }
387    }
388
389    // Now get the actual settings object to use - already have it if there is only one settings object
390    // If there are multiple settings, just need to set single_settings_object to the desired settings object
391    if (has_multi_setting_file) {
392        single_settings_object = loader_cJSON_GetArrayItem(settings_array, index_to_use);
393        if (NULL == single_settings_object) {
394            res = VK_ERROR_INITIALIZATION_FAILED;
395            goto out;
396        }
397    }
398
399    // optional
400    cJSON* stderr_filter = loader_cJSON_GetObjectItem(single_settings_object, "stderr_log");
401    if (NULL != stderr_filter) {
402        struct loader_string_list stderr_log = {0};
403        res = loader_parse_json_array_of_strings(inst, single_settings_object, "stderr_log", &stderr_log);
404        if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
405            goto out;
406        }
407        loader_settings->debug_level = parse_log_filters_from_strings(&stderr_log);
408        free_string_list(inst, &stderr_log);
409    }
410
411    // optional
412    cJSON* logs_to_use = loader_cJSON_GetObjectItem(single_settings_object, "log_locations");
413    if (NULL != logs_to_use) {
414        int log_count = loader_cJSON_GetArraySize(logs_to_use);
415        for (int i = 0; i < log_count; i++) {
416            cJSON* log_element = loader_cJSON_GetArrayItem(logs_to_use, i);
417            // bool is_valid = true;
418            if (NULL != log_element) {
419                struct loader_string_list log_destinations = {0};
420                res = loader_parse_json_array_of_strings(inst, log_element, "destinations", &log_destinations);
421                if (res != VK_SUCCESS) {
422                    // is_valid = false;
423                }
424                free_string_list(inst, &log_destinations);
425                struct loader_string_list log_filters = {0};
426                res = loader_parse_json_array_of_strings(inst, log_element, "filters", &log_filters);
427                if (res != VK_SUCCESS) {
428                    // is_valid = false;
429                }
430                free_string_list(inst, &log_filters);
431            }
432        }
433    }
434
435    res = parse_layer_configurations(inst, single_settings_object, loader_settings);
436    if (res != VK_SUCCESS) {
437        goto out;
438    }
439
440    // Determine if there exists a layer configuration indicating where to put layers not contained in the settings file
441    // LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION
442    for (uint32_t i = 0; i < loader_settings->layer_configuration_count; i++) {
443        if (loader_settings->layer_configurations[i].control == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
444            loader_settings->has_unordered_layer_location = true;
445            break;
446        }
447    }
448
449    loader_settings->settings_file_path = settings_file_path;
450    settings_file_path = NULL;
451    loader_settings->settings_active = true;
452out:
453    if (NULL != json) {
454        loader_cJSON_Delete(json);
455    }
456
457    loader_instance_heap_free(inst, settings_file_path);
458
459    loader_instance_heap_free(inst, file_format_version_string);
460    return res;
461}
462
463VkResult update_global_loader_settings(void) {
464    loader_settings settings = {0};
465    VkResult res = get_loader_settings(NULL, &settings);
466    loader_platform_thread_lock_mutex(&global_loader_settings_lock);
467
468    free_loader_settings(NULL, &global_loader_settings);
469    if (res == VK_SUCCESS) {
470        if (!check_if_settings_are_equal(&settings, &global_loader_settings)) {
471            log_settings(NULL, &settings);
472        }
473
474        memcpy(&global_loader_settings, &settings, sizeof(loader_settings));
475        if (global_loader_settings.settings_active) {
476            loader_set_global_debug_level(global_loader_settings.debug_level);
477        }
478    }
479    loader_platform_thread_unlock_mutex(&global_loader_settings_lock);
480    return res;
481}
482
483void init_global_loader_settings(void) {
484    loader_platform_thread_create_mutex(&global_loader_settings_lock);
485    // Free out the global settings in case the process was loaded & unloaded
486    free_loader_settings(NULL, &global_loader_settings);
487}
488void teardown_global_loader_settings(void) {
489    free_loader_settings(NULL, &global_loader_settings);
490    loader_platform_thread_delete_mutex(&global_loader_settings_lock);
491}
492
493bool should_skip_logging_global_messages(VkFlags msg_type) {
494    loader_platform_thread_lock_mutex(&global_loader_settings_lock);
495    bool should_skip = global_loader_settings.settings_active && 0 != (msg_type & global_loader_settings.debug_level);
496    loader_platform_thread_unlock_mutex(&global_loader_settings_lock);
497    return should_skip;
498}
499
500// Use this function to get the correct settings to use based on the context
501// If inst is NULL - use the global settings and lock the mutex
502// Else return the settings local to the instance - but do nto lock the mutex
503const loader_settings* get_current_settings_and_lock(const struct loader_instance* inst) {
504    if (inst) {
505        return &inst->settings;
506    }
507    loader_platform_thread_lock_mutex(&global_loader_settings_lock);
508    return &global_loader_settings;
509}
510// Release the global settings lock if we are using the global settings - aka if inst is NULL
511void release_current_settings_lock(const struct loader_instance* inst) {
512    if (inst == NULL) {
513        loader_platform_thread_unlock_mutex(&global_loader_settings_lock);
514    }
515}
516
517VkResult get_settings_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers,
518                             bool* should_search_for_other_layers) {
519    VkResult res = VK_SUCCESS;
520    *should_search_for_other_layers = true;  // default to true
521
522    const loader_settings* settings = get_current_settings_and_lock(inst);
523
524    if (NULL == settings || !settings->settings_active) {
525        goto out;
526    }
527
528    // Assume the list doesn't contain LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION at first
529    *should_search_for_other_layers = false;
530
531    for (uint32_t i = 0; i < settings->layer_configuration_count; i++) {
532        loader_settings_layer_configuration* layer_config = &settings->layer_configurations[i];
533
534        // If we encountered a layer that should be forced off, we add it to the settings_layers list but only
535        // with the data required to compare it with layers not in the settings file (aka name and manifest path)
536        if (layer_config->control == LOADER_SETTINGS_LAYER_CONTROL_OFF) {
537            struct loader_layer_properties props = {0};
538            props.settings_control_value = LOADER_SETTINGS_LAYER_CONTROL_OFF;
539            loader_strncpy(props.info.layerName, VK_MAX_EXTENSION_NAME_SIZE, layer_config->name, VK_MAX_EXTENSION_NAME_SIZE);
540            props.info.layerName[VK_MAX_EXTENSION_NAME_SIZE - 1] = '\0';
541            res = loader_copy_to_new_str(inst, layer_config->path, &props.manifest_file_name);
542            if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
543                goto out;
544            }
545            res = loader_append_layer_property(inst, settings_layers, &props);
546            if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
547                loader_free_layer_properties(inst, &props);
548                goto out;
549            }
550            continue;
551        }
552
553        // The special layer location that indicates where unordered layers should go only should have the
554        // settings_control_value set - everything else should be NULL
555        if (layer_config->control == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
556            struct loader_layer_properties props = {0};
557            props.settings_control_value = LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION;
558            res = loader_append_layer_property(inst, settings_layers, &props);
559            if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
560                loader_free_layer_properties(inst, &props);
561                goto out;
562            }
563            *should_search_for_other_layers = true;
564            continue;
565        }
566
567        if (layer_config->path == NULL) {
568            continue;
569        }
570
571        cJSON* json = NULL;
572        VkResult local_res = loader_get_json(inst, layer_config->path, &json);
573        if (VK_ERROR_OUT_OF_HOST_MEMORY == local_res) {
574            res = VK_ERROR_OUT_OF_HOST_MEMORY;
575            goto out;
576        } else if (VK_SUCCESS != local_res || NULL == json) {
577            continue;
578        }
579
580        local_res =
581            loader_add_layer_properties(inst, settings_layers, json, layer_config->treat_as_implicit_manifest, layer_config->path);
582        loader_cJSON_Delete(json);
583
584        // If the error is anything other than out of memory we still want to try to load the other layers
585        if (VK_ERROR_OUT_OF_HOST_MEMORY == local_res) {
586            res = VK_ERROR_OUT_OF_HOST_MEMORY;
587            goto out;
588        }
589        struct loader_layer_properties* newly_added_layer = &settings_layers->list[settings_layers->count - 1];
590        newly_added_layer->settings_control_value = layer_config->control;
591        // If the manifest file found has a name that differs from the one in the settings, remove this layer from consideration
592        bool should_remove = false;
593        if (strncmp(newly_added_layer->info.layerName, layer_config->name, VK_MAX_EXTENSION_NAME_SIZE) != 0) {
594            should_remove = true;
595            loader_remove_layer_in_list(inst, settings_layers, settings_layers->count - 1);
596        }
597        // Make sure the layer isn't already in the list
598        for (uint32_t j = 0; settings_layers->count > 0 && j < settings_layers->count - 1; j++) {
599            if (0 ==
600                strncmp(settings_layers->list[j].info.layerName, newly_added_layer->info.layerName, VK_MAX_EXTENSION_NAME_SIZE)) {
601                if (0 == (newly_added_layer->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) &&
602                    strcmp(settings_layers->list[j].lib_name, newly_added_layer->lib_name) == 0) {
603                    should_remove = true;
604                    break;
605                }
606            }
607        }
608        if (should_remove) {
609            loader_remove_layer_in_list(inst, settings_layers, settings_layers->count - 1);
610        }
611    }
612
613out:
614    release_current_settings_lock(inst);
615    return res;
616}
617
618// Check if layers has an element with the same name.
619// If layer_property is a regular layer, check if the lib_path is the same.
620// If layer_property is a meta layer, just use the layerName
621bool check_if_layer_is_in_list(struct loader_layer_list* layer_list, struct loader_layer_properties* layer_property) {
622    // If the layer is a meta layer, just check against the name
623    for (uint32_t i = 0; i < layer_list->count; i++) {
624        if (0 == strncmp(layer_list->list[i].info.layerName, layer_property->info.layerName, VK_MAX_EXTENSION_NAME_SIZE)) {
625            if (0 == (layer_property->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) &&
626                strcmp(layer_list->list[i].lib_name, layer_property->lib_name) == 0) {
627                return true;
628            }
629        }
630    }
631    return false;
632}
633
634VkResult combine_settings_layers_with_regular_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers,
635                                                     struct loader_layer_list* regular_layers,
636                                                     struct loader_layer_list* output_layers) {
637    VkResult res = VK_SUCCESS;
638    bool has_unordered_layer_location = false;
639    uint32_t unordered_layer_location_index = 0;
640    // Location to put layers that aren't known to the settings file
641    // Find it here so we dont have to pass in a loader_settings struct
642    for (uint32_t i = 0; i < settings_layers->count; i++) {
643        if (settings_layers->list[i].settings_control_value == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
644            has_unordered_layer_location = true;
645            unordered_layer_location_index = i;
646            break;
647        }
648    }
649
650    if (settings_layers->count == 0 && regular_layers->count == 0) {
651        // No layers to combine
652        goto out;
653    } else if (settings_layers->count == 0) {
654        // No settings layers - just copy regular to output_layers - memset regular layers to prevent double frees
655        *output_layers = *regular_layers;
656        memset(regular_layers, 0, sizeof(struct loader_layer_list));
657        goto out;
658    } else if (regular_layers->count == 0 || !has_unordered_layer_location) {
659        // No regular layers or has_unordered_layer_location is false - just copy settings to output_layers -
660        // memset settings layers to prevent double frees
661        *output_layers = *settings_layers;
662        memset(settings_layers, 0, sizeof(struct loader_layer_list));
663        goto out;
664    }
665
666    res = loader_init_generic_list(inst, (struct loader_generic_list*)output_layers,
667                                   (settings_layers->count + regular_layers->count) * sizeof(struct loader_layer_properties));
668    if (VK_SUCCESS != res) {
669        goto out;
670    }
671
672    // Insert the settings layers into output_layers up to unordered_layer_index
673    for (uint32_t i = 0; i < unordered_layer_location_index; i++) {
674        if (!check_if_layer_is_in_list(output_layers, &settings_layers->list[i])) {
675            res = loader_append_layer_property(inst, output_layers, &settings_layers->list[i]);
676            if (VK_SUCCESS != res) {
677                goto out;
678            }
679        }
680    }
681
682    for (uint32_t i = 0; i < regular_layers->count; i++) {
683        // Check if its already been put in the output_layers list as well as the remaining settings_layers
684        bool regular_layer_is_ordered = check_if_layer_is_in_list(output_layers, &regular_layers->list[i]) ||
685                                        check_if_layer_is_in_list(settings_layers, &regular_layers->list[i]);
686        // If it isn't found, add it
687        if (!regular_layer_is_ordered) {
688            res = loader_append_layer_property(inst, output_layers, &regular_layers->list[i]);
689            if (VK_SUCCESS != res) {
690                goto out;
691            }
692        } else {
693            // layer is already ordered and can be safely freed
694            loader_free_layer_properties(inst, &regular_layers->list[i]);
695        }
696    }
697
698    // Insert the rest of the settings layers into combined_layers from  unordered_layer_index to the end
699    // start at one after the unordered_layer_index
700    for (uint32_t i = unordered_layer_location_index + 1; i < settings_layers->count; i++) {
701        res = loader_append_layer_property(inst, output_layers, &settings_layers->list[i]);
702        if (VK_SUCCESS != res) {
703            goto out;
704        }
705    }
706
707out:
708    if (res != VK_SUCCESS) {
709        loader_delete_layer_list_and_properties(inst, output_layers);
710    }
711
712    return res;
713}
714
715VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, const struct loader_envvar_all_filters* filters,
716                                             uint32_t app_enabled_name_count, const char* const* app_enabled_names,
717                                             const struct loader_layer_list* instance_layers,
718                                             struct loader_pointer_layer_list* target_layer_list,
719                                             struct loader_pointer_layer_list* activated_layer_list) {
720    VkResult res = VK_SUCCESS;
721    char* vk_instance_layers_env = loader_getenv(ENABLED_LAYERS_ENV, inst);
722    size_t vk_instance_layers_env_len = 0;
723    char* vk_instance_layers_env_copy = NULL;
724    if (vk_instance_layers_env != NULL) {
725        vk_instance_layers_env_len = strlen(vk_instance_layers_env) + 1;
726        vk_instance_layers_env_copy = loader_stack_alloc(vk_instance_layers_env_len);
727
728        loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "env var \'%s\' defined and adding layers: %s",
729                   ENABLED_LAYERS_ENV, vk_instance_layers_env);
730    }
731    for (uint32_t i = 0; i < instance_layers->count; i++) {
732        bool enable_layer = false;
733        struct loader_layer_properties* props = &instance_layers->list[i];
734
735        // Do not enable the layer if the settings have it set as off
736        if (props->settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_OFF) {
737            continue;
738        }
739        // Force enable it based on settings
740        if (props->settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON) {
741            enable_layer = true;
742        }
743
744        // Check if disable filter needs to skip the layer
745        if ((filters->disable_filter.disable_all || filters->disable_filter.disable_all_implicit ||
746             check_name_matches_filter_environment_var(props->info.layerName, &filters->disable_filter.additional_filters)) &&
747            !check_name_matches_filter_environment_var(props->info.layerName, &filters->allow_filter)) {
748            continue;
749        }
750        // Check the enable filter
751        if (!enable_layer && check_name_matches_filter_environment_var(props->info.layerName, &filters->enable_filter)) {
752            enable_layer = true;
753        }
754
755        // First look for the old-fashion layers forced on with VK_INSTANCE_LAYERS
756        if (!enable_layer && vk_instance_layers_env && vk_instance_layers_env_copy && vk_instance_layers_env_len > 0) {
757            // Copy the env-var on each iteration, so that loader_get_next_path can correctly find the separators
758            // This solution only needs one stack allocation ahead of time rather than an allocation per layer in the env-var
759            loader_strncpy(vk_instance_layers_env_copy, vk_instance_layers_env_len, vk_instance_layers_env,
760                           vk_instance_layers_env_len);
761
762            while (vk_instance_layers_env_copy && *vk_instance_layers_env_copy) {
763                char* next = loader_get_next_path(vk_instance_layers_env_copy);
764                if (0 == strcmp(vk_instance_layers_env_copy, props->info.layerName)) {
765                    enable_layer = true;
766                    break;
767                }
768                vk_instance_layers_env_copy = next;
769            }
770        }
771
772        // Check if it should be enabled by the application
773        if (!enable_layer) {
774            for (uint32_t j = 0; j < app_enabled_name_count; j++) {
775                if (strcmp(props->info.layerName, app_enabled_names[j]) == 0) {
776                    enable_layer = true;
777                    break;
778                }
779            }
780        }
781
782        // Check if its an implicit layers and thus enabled by default
783        if (!enable_layer && (0 == (props->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER)) &&
784            loader_implicit_layer_is_enabled(inst, filters, props)) {
785            enable_layer = true;
786        }
787
788        if (enable_layer) {
789            // Check if the layer is a meta layer reuse the existing function to add the meta layer
790            if (props->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) {
791                res = loader_add_meta_layer(inst, filters, props, target_layer_list, activated_layer_list, instance_layers, NULL);
792                if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
793            } else {
794                res = loader_add_layer_properties_to_list(inst, target_layer_list, props);
795                if (res != VK_SUCCESS) {
796                    goto out;
797                }
798                res = loader_add_layer_properties_to_list(inst, activated_layer_list, props);
799                if (res != VK_SUCCESS) {
800                    goto out;
801                }
802            }
803        }
804    }
805out:
806    return res;
807}
808