diff --git a/CMakeLists.txt b/CMakeLists.txt index 62c99c86c..7ef3b9b50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ if (UNIX) option(LOADER_ENABLE_THREAD_SANITIZER "Linux & macOS only: Advanced thread checking" OFF) endif() -if(APPLE) +if(TRUE) option(BUILD_STATIC_LOADER "Build a loader that can be statically linked" OFF) endif() @@ -236,9 +236,10 @@ if (NOT (WIN32 OR APPLE)) endif() endif() +find_package(Python3 REQUIRED) + option(LOADER_CODEGEN "Enable vulkan loader code generation") if(LOADER_CODEGEN) - find_package(Python3 REQUIRED) add_custom_target(loader_codegen COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/scripts/generate_source.py "${VULKAN_HEADERS_INSTALL_DIR}/${CMAKE_INSTALL_DATADIR}/vulkan/registry" @@ -246,6 +247,23 @@ if(LOADER_CODEGEN) ) endif() +set(JINJA2_FOUND 0) + +execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import jinja2" + RESULT_VARIABLE EXIT_CODE + OUTPUT_QUIET + ) +if (NOT EXIT_CODE) + set(JINJA2_FOUND 1) +endif() + +if (JINJA2_FOUND) + execute_process(COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/generate_static_icds_header.py) +else() + message(FATAL_ERROR "Python 3 with jinja2 is required to generate the static ICDs header file.") +endif() + if(UNIX) target_compile_definitions(loader_common_options INTERFACE FALLBACK_CONFIG_DIRS="${FALLBACK_CONFIG_DIRS}" FALLBACK_DATA_DIRS="${FALLBACK_DATA_DIRS}") diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 18a2871b4..ec26e9ea3 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -354,7 +354,7 @@ if(WIN32) add_dependencies(vulkan loader_asm_gen_files) else() - if(APPLE AND BUILD_STATIC_LOADER) + if(BUILD_STATIC_LOADER) add_library(vulkan STATIC ${NORMAL_LOADER_SRCS} ${OPT_LOADER_SRCS}) target_compile_definitions(vulkan PRIVATE BUILD_STATIC_LOADER) else() diff --git a/loader/cJSON.c b/loader/cJSON.c index 24c8f2b96..15c6738f3 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -951,6 +951,22 @@ cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string) { return c; } +VkResult loader_get_json_from_str(const struct loader_instance *inst, const char *filename, const char *json_str, cJSON **json) { + VkResult res = VK_SUCCESS; + bool out_of_memory = false; + *json = cJSON_Parse(inst ? &inst->alloc_callbacks : NULL, json_str, &out_of_memory); + if (out_of_memory) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json_from_str: Out of Memory error occured while parsing JSON file %s.", filename); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } else if (*json == NULL) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json_from_str: Invalid JSON file %s.", filename); + goto out; + } +out: + return res; +} + VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) { FILE *file = NULL; char *json_buf = NULL; diff --git a/loader/cJSON.h b/loader/cJSON.h index 4878288f5..6fb080264 100644 --- a/loader/cJSON.h +++ b/loader/cJSON.h @@ -89,6 +89,12 @@ cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string); struct loader_instance; struct loader_string_list; +// Parse an already read JSON file to a buffer. +// +// @return - A pointer to a cJSON object representing the JSON parse tree. +// This returned buffer should be freed by caller. +VkResult loader_get_json_from_str(const struct loader_instance *inst, const char *filename, const char *json_str, cJSON **json); + // Read a JSON file into a buffer. // // @return - A pointer to a cJSON object representing the JSON parse tree. diff --git a/loader/loader.c b/loader/loader.c index aee5be418..78ecf2eef 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -72,6 +72,8 @@ // Generated file containing all the extension data #include "vk_loader_extensions.c" +#include "static_icds.h" + struct loader_struct loader = {0}; struct activated_layer_info { @@ -1381,7 +1383,7 @@ void loader_scanned_icd_clear(const struct loader_instance *inst, struct loader_ if (0 != icd_tramp_list->capacity && icd_tramp_list->scanned_list) { for (uint32_t i = 0; i < icd_tramp_list->count; i++) { if (icd_tramp_list->scanned_list[i].handle) { - loader_platform_close_library(icd_tramp_list->scanned_list[i].handle); + //loader_platform_close_library(icd_tramp_list->scanned_list[i].handle); icd_tramp_list->scanned_list[i].handle = NULL; } loader_instance_heap_free(inst, icd_tramp_list->scanned_list[i].lib_name); @@ -1647,11 +1649,19 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade // TODO implement smarter opening/closing of libraries. For now this // function leaves libraries open and the scanned_icd_clear closes them +#if 0 #if defined(__Fuchsia__) handle = loader_platform_open_driver(filename); #else handle = loader_platform_open_library(filename); #endif +#endif + for (uint32_t i = 0; i < static_icds.count; i++) { + if (!strcmp(static_icds.icd_list[i].libname, filename)) { + handle = (loader_platform_dl_handle) &static_icds.icd_list[i]; + break; + } + } if (NULL == handle) { loader_handle_load_library_error(inst, filename, lib_status); if (lib_status && *lib_status == LOADER_LAYER_LIB_ERROR_OUT_OF_MEMORY) { @@ -1663,13 +1673,13 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade } // Try to load the driver's exported vk_icdNegotiateLoaderICDInterfaceVersion - fp_negotiate_icd_version = loader_platform_get_proc_address(handle, "vk_icdNegotiateLoaderICDInterfaceVersion"); + fp_negotiate_icd_version = ((struct loader_static_icd *) handle)->api.fp_negotiate_icd_version; // If it isn't exported, we are dealing with either a v0, v1, or a v7 and up driver if (NULL == fp_negotiate_icd_version) { // Try to load the driver's exported vk_icdGetInstanceProcAddr - if this is a v7 or up driver, we can use it to get // the driver's vk_icdNegotiateLoaderICDInterfaceVersion function - fp_get_proc_addr = loader_platform_get_proc_address(handle, "vk_icdGetInstanceProcAddr"); + fp_get_proc_addr = ((struct loader_static_icd *) handle)->api.fp_get_proc_addr; // If we successfully loaded vk_icdGetInstanceProcAddr, try to get vk_icdNegotiateLoaderICDInterfaceVersion if (fp_get_proc_addr) { @@ -1690,7 +1700,7 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade // If we didn't already query vk_icdGetInstanceProcAddr, try now if (NULL == fp_get_proc_addr) { - fp_get_proc_addr = loader_platform_get_proc_address(handle, "vk_icdGetInstanceProcAddr"); + fp_get_proc_addr = ((struct loader_static_icd *) handle)->api.fp_get_proc_addr; } // If vk_icdGetInstanceProcAddr is NULL, this ICD is using version 0 and so we should respond accordingly. @@ -1763,7 +1773,7 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade (PFN_vk_icdGetPhysicalDeviceProcAddr)fp_get_proc_addr(NULL, "vk_icdGetPhysicalDeviceProcAddr"); } if (NULL == fp_get_phys_dev_proc_addr && interface_vers >= 3) { - fp_get_phys_dev_proc_addr = loader_platform_get_proc_address(handle, "vk_icdGetPhysicalDeviceProcAddr"); + fp_get_phys_dev_proc_addr = ((struct loader_static_icd *) handle)->api.fp_get_phys_dev_proc_addr; } #if defined(VK_USE_PLATFORM_WIN32_KHR) // Query "vk_icdEnumerateAdapterPhysicalDevices" with vk_icdGetInstanceProcAddr if the library reports interface version @@ -3383,8 +3393,8 @@ struct ICDManifestInfo { // Takes a json file, opens, reads, and parses an ICD Manifest out of it. // Should only return VK_SUCCESS, VK_ERROR_INCOMPATIBLE_DRIVER, or VK_ERROR_OUT_OF_HOST_MEMORY -VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *file_str, struct ICDManifestInfo *icd, - bool *skipped_portability_drivers) { +VkResult loader_parse_icd_manifest(const struct loader_instance *inst, const char *file_str, const char *json_str, + struct ICDManifestInfo *icd, bool *skipped_portability_drivers) { VkResult res = VK_SUCCESS; cJSON *json = NULL; char *file_vers_str = NULL; @@ -3395,7 +3405,7 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - res = loader_get_json(inst, file_str, &json); + res = loader_get_json_from_str(inst, file_str, json_str, &json); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { goto out; } @@ -3554,7 +3564,7 @@ out: VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list, const VkInstanceCreateInfo *pCreateInfo, bool *skipped_portability_drivers) { VkResult res = VK_SUCCESS; - struct loader_string_list manifest_files = {0}; +// struct loader_string_list manifest_files = {0}; struct loader_envvar_filter select_filter = {0}; struct loader_envvar_filter disable_filter = {0}; struct ICDManifestInfo *icd_details = NULL; @@ -3585,24 +3595,25 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t if (VK_SUCCESS != res) { goto out; } - +#if 0 // Get a list of manifest files for ICDs res = loader_get_data_files(inst, LOADER_DATA_FILE_MANIFEST_DRIVER, NULL, &manifest_files); if (VK_SUCCESS != res) { goto out; } +#endif - icd_details = loader_stack_alloc(sizeof(struct ICDManifestInfo) * manifest_files.count); + icd_details = loader_stack_alloc(sizeof(struct ICDManifestInfo) * static_icds.count); if (NULL == icd_details) { res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } - memset(icd_details, 0, sizeof(struct ICDManifestInfo) * manifest_files.count); + memset(icd_details, 0, sizeof(struct ICDManifestInfo) * static_icds.count); - for (uint32_t i = 0; i < manifest_files.count; i++) { + for (uint32_t i = 0; i < static_icds.count; i++) { VkResult icd_res = VK_SUCCESS; - icd_res = loader_parse_icd_manifest(inst, manifest_files.list[i], &icd_details[i], skipped_portability_drivers); + icd_res = loader_parse_icd_manifest(inst, static_icds.icd_list[i].jsonname, static_icds.icd_list[i].json, &icd_details[i], skipped_portability_drivers); if (VK_ERROR_OUT_OF_HOST_MEMORY == icd_res) { res = icd_res; goto out; @@ -3611,12 +3622,11 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t } if (select_filter.count > 0 || disable_filter.count > 0) { - // Get only the filename for comparing to the filters - char *just_filename_str = strrchr(manifest_files.list[i], DIRECTORY_SYMBOL); + char *just_filename_str = strrchr(static_icds.icd_list[i].jsonname, DIRECTORY_SYMBOL); // No directory symbol, just the filename if (NULL == just_filename_str) { - just_filename_str = manifest_files.list[i]; + just_filename_str = (char*) static_icds.icd_list[i].jsonname; } else { just_filename_str++; } @@ -3674,11 +3684,11 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t out: if (NULL != icd_details) { // Successfully got the icd_details structure, which means we need to free the paths contained within - for (uint32_t i = 0; i < manifest_files.count; i++) { + for (uint32_t i = 0; i < static_icds.count; i++) { loader_instance_heap_free(inst, icd_details[i].full_library_path); } } - free_string_list(inst, &manifest_files); +// free_string_list(inst, &manifest_files); return res; } diff --git a/scripts/generate_static_icds_header.py b/scripts/generate_static_icds_header.py new file mode 100644 index 000000000..f62a472b6 --- /dev/null +++ b/scripts/generate_static_icds_header.py @@ -0,0 +1,24 @@ +from jinja2 import Template +from pathlib import Path +from itertools import groupby +from operator import itemgetter + +import glob +import re + +def get_icds(files): + for file in files: + name = re.sub(r"_icd.i686.json$", "", file.name) + yield { + "libname": f"/usr/local/lib/libvulkan_{name}.so", + "jsonname": file.name, + "json": file.read_bytes(), + "prefix": name + } + +files = Path("/usr/local/share/vulkan/icd.d").glob("*_icd.i686.json") + +path = Path(__file__).parent +template = Template((path / "static_icds_h.template").read_text(), + trim_blocks=True, lstrip_blocks=True) +(path.parent / "loader" / "static_icds.h").write_text(template.render(icds=list(get_icds(files)))) diff --git a/scripts/static_icds_h.template b/scripts/static_icds_h.template new file mode 100644 index 000000000..9078018ea --- /dev/null +++ b/scripts/static_icds_h.template @@ -0,0 +1,50 @@ +#ifndef __STATIC_ICDS_H__ +#define __STATIC_ICDS_H__ + +{% for icd in icds %} +extern VKAPI_ATTR VkResult VKAPI_CALL {{ icd.prefix }}_vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pVersion); +extern VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL {{ icd.prefix }}_vk_icdGetInstanceProcAddr(VkInstance instance, const char* pName); +extern VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL {{ icd.prefix }}_vk_icdGetPhysicalDeviceProcAddr(VkInstance isntance, const char* pName); +{% endfor %} + +struct loader_static_icd_api { + const PFN_vk_icdNegotiateLoaderICDInterfaceVersion fp_negotiate_icd_version; + const PFN_vk_icdGetInstanceProcAddr fp_get_proc_addr; + const PFN_vk_icdGetPhysicalDeviceProcAddr fp_get_phys_dev_proc_addr; +}; + +struct loader_static_icd { + const char *libname; + const char *jsonname; + const char *json; + const struct loader_static_icd_api api; +}; + +struct loader_static_icds { + const struct loader_static_icd *icd_list; + const uint32_t count; +}; + +static const struct loader_static_icd static_icd_list[] = { + {% for icd in icds %} + { + "{{ icd.libname }}", + "{{ icd.jsonname }}", + {% for byte in icd.json %} + {% if loop.index % 8 == 1 %} + "{% endif %} +{{ "\\x{:02x}".format(byte) }}{% if loop.index % 8 == 0 or loop.last %}"{% if not loop.last +%} +{% endif %}{% endif %} + {% endfor +%}, + { + {{ icd.prefix }}_vk_icdNegotiateLoaderICDInterfaceVersion, + {{ icd.prefix }}_vk_icdGetInstanceProcAddr, + {{ icd.prefix }}_vk_icdGetPhysicalDeviceProcAddr + } + }{% if not loop.last %},{% endif +%} + {% endfor %} +}; + +static const struct loader_static_icds static_icds = { static_icd_list, {{ icds|length }} }; + +#endif // __STATIC_ICDS_H__