diff --git a/CMakeLists.txt b/CMakeLists.txt index ea40581..502e1e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,6 @@ project(cruft-vulkan CXX) ############################################################################### -find_package (Vulkan REQUIRED) find_package (PythonInterp 3 REQUIRED) find_package (glfw3 REQUIRED) @@ -15,6 +14,7 @@ endif () ############################################################################### +file (MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icd") include_directories ("${CMAKE_CURRENT_BINARY_DIR}") @@ -31,6 +31,7 @@ endif() add_custom_command ( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/vk.hpp" + "${CMAKE_CURRENT_BINARY_DIR}/icd/dispatch.cpp" COMMENT "[spec.py] vk.hpp" COMMAND @@ -38,6 +39,7 @@ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tools/spec.py" "--src" "${CMAKE_CURRENT_SOURCE_DIR}/specs/xml/vk.xml" "--dst" "${CMAKE_CURRENT_BINARY_DIR}/vk.hpp" + "--dispatch" "${CMAKE_CURRENT_BINARY_DIR}/icd/dispatch.cpp" "--platform" "${VK_PLATFORM}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tools/spec.py" @@ -47,7 +49,27 @@ DEPENDS include_directories("${CMAKE_CURRENT_SOURCE_DIR}/specs/include/vulkan") +############################################################################### +if (WIN32) + set(VK_LOADER_VENDOR win32) +elseif (LINUX) + set(VK_LOADER_VENDOR posix) +else () + message (FATAL_ERROR "unhandled vk-loader platform") +endif() + + ##----------------------------------------------------------------------------- +add_library (cruft-vk-icd STATIC + ${CMAKE_CURRENT_BINARY_DIR}/icd/dispatch.cpp + icd/vendor.hpp + icd/vendor.cpp + icd/vendor_${VK_LOADER_VENDOR} +) + +target_link_libraries (cruft-vk-icd cruft-json cruft) + +############################################################################### list (APPEND sources vk.hpp @@ -107,7 +129,7 @@ list (APPEND sources ##----------------------------------------------------------------------------- add_library (cruft-vk STATIC ${sources}) -target_link_libraries (cruft-vk cruft vulkan) +target_link_libraries (cruft-vk cruft-vk-icd cruft) ############################################################################### diff --git a/icd/vendor.cpp b/icd/vendor.cpp new file mode 100644 index 0000000..573c6a4 --- /dev/null +++ b/icd/vendor.cpp @@ -0,0 +1,34 @@ +#include "vendor.hpp" + +#include + +using cruft::vk::icd::vendor; + + +template <> +cruft::vk::icd::icd_t +json::tree::io::deserialise (json::tree::node const &obj) +{ + return { + .file_format_version = obj["'file_format_version"].as_string (), + .icd = { + .library_path = obj["ICD"]["library_path"].as_string ().native (), + .api_version = obj["ICD"]["api_version"].as_string (), + }, + }; +} + + +/////////////////////////////////////////////////////////////////////////////// +vendor::vendor (icd_t const &_icd): + vendor (cruft::library (_icd.icd.library_path)) +{ ; } + + +//----------------------------------------------------------------------------- +vendor::vendor (::cruft::library &&_library) + : m_library (std::move (_library)) + , m_get_proc (m_library.symbol ("vk_icdGetInstanceProcAddr")) +{ + vtable.CreateInstance = reinterpret_cast (m_get_proc (nullptr, "vkCreateInstance")); +} diff --git a/icd/vendor.hpp b/icd/vendor.hpp new file mode 100644 index 0000000..5b03021 --- /dev/null +++ b/icd/vendor.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "vk.hpp" + +#include + +#include +#include + + +namespace cruft::vk::icd { + struct icd_t { + std::string file_format_version; + struct { + std::experimental::filesystem::path library_path; + std::string api_version; + } icd; + }; + + + std::vector + enumerate (void); + + + class vendor { + public: + vendor (icd_t const&); + vendor (::cruft::library &&); + + struct vtable_t { + VkResult (*CreateInstance) (const VkInstanceCreateInfo*, const VkAllocationCallbacks*, VkInstance*) noexcept; + } vtable; + + private: + using get_proc_t = void* (*)(VkInstance, char const*); + + ::cruft::library m_library; + get_proc_t const m_get_proc; + }; +} \ No newline at end of file diff --git a/icd/vendor_posix.cpp b/icd/vendor_posix.cpp new file mode 100644 index 0000000..e69de29 diff --git a/icd/vendor_win32.cpp b/icd/vendor_win32.cpp new file mode 100644 index 0000000..f4305f6 --- /dev/null +++ b/icd/vendor_win32.cpp @@ -0,0 +1,51 @@ +#include "vendor.hpp" + +#include + +#include +#include +#include +#include + + +/////////////////////////////////////////////////////////////////////////////// +std::vector +cruft::vk::icd::enumerate() +{ + std::vector res; + + win32::key root (HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Class\\"); + + for (auto &&adapter: root.subkeys ()) { + for (auto &&device: adapter.subkeys ()) { + // device keys must be of the form '000X' + auto const name = device.name (); + if (name.size () != 4 || + name[0] != '0' || + name[1] != '0' || + name[2] != '0' || + !std::isdigit(name[3])) + { + continue; + } + + // 'VulkanDriverName' contains the JSON for the ICD data. Parse + // this and add to the list. If we encounter an error then + // quietly drop it. + try { + auto const path = device.data ("VulkanDriverName"); + auto const text = cruft::slurp (path); + auto const jobj = json::tree::parse (cruft::view{text}); + auto const data = from_json (*jobj); + + res.push_back (data); + } catch (win32::error const&) { + ; + } catch (std::exception const &e) { + LOG_WARNING ("error loading Vulkan ICD manifest, %!", e.what ()); + } + } + } + + return res; +} diff --git a/tools/info.cpp b/tools/info.cpp index 740af7e..229d14e 100644 --- a/tools/info.cpp +++ b/tools/info.cpp @@ -16,10 +16,19 @@ #include +#include "../icd/vendor.hpp" + + /////////////////////////////////////////////////////////////////////////////// int main (int, char**) { + auto all = cruft::vk::icd::enumerate (); + for (auto const &i: all) { + cruft::vk::icd::vendor v (i); + } + + std::cout << "available_layers: [ " << cruft::make_infix (cruft::vk::instance::available_layers ()) << " ]\n"; diff --git a/tools/spec.py b/tools/spec.py index e72b586..059ee5a 100644 --- a/tools/spec.py +++ b/tools/spec.py @@ -652,6 +652,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser(description='Transform XML API specification into C++ headers') parser.add_argument('--src', type=str, help='the path to the XML file to transform') parser.add_argument('--dst', type=str, help='the output path for the result') + parser.add_argument('--dispatch', type=str, help="the output path for function dispatch") parser.add_argument( '--platform', type=str, @@ -662,7 +663,6 @@ if __name__ == '__main__': args = parser.parse_args() src = open(args.src, 'r') - dst = open(args.dst, 'w') tree = ET.parse(src) root = tree.getroot() @@ -689,14 +689,29 @@ if __name__ == '__main__': extensions = ["VK_KHR_swapchain", "VK_EXT_debug_report", "VK_KHR_external_memory"] q = reg.serialise(args.platform) + with open(args.dst, 'w') as dst: + dst.write("#pragma once\n") + dst.write('extern "C" {\n') + for obj in q: + dst.write(obj.declare()) + dst.write('\n') + dst.write(obj.define(reg)) + dst.write('\n') + dst.write('}\n') - dst.write("#pragma once\n") - dst.write('extern "C" {\n') - for obj in q: - dst.write(obj.declare()) - dst.write('\n') - dst.write(obj.define(reg)) - dst.write('\n') - dst.write('}\n') + with open(args.dispatch, 'w') as dispatch: + dispatch.write(""" + #include "vk.hpp" + #include - #write_types(dst, types) + #pragma GCC diagnostic ignored "-Wunused-parameter" + """) + for obj in q: + if not isinstance(obj, command): + continue + + dispatch.write(f""" + extern "C" {obj.result} {rename(obj.name)} ({", ".join(obj.params)}) noexcept {{ + unimplemented (); + }} + """)