diff --git a/CMakeLists.txt b/CMakeLists.txt index cb4e65f..5aae9aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,11 +63,13 @@ endif() ##----------------------------------------------------------------------------- add_library (cruft-vk-icd STATIC + icd/fwd.hpp ${CMAKE_CURRENT_BINARY_DIR}/icd/vtable.hpp ${CMAKE_CURRENT_BINARY_DIR}/icd/dispatch.cpp icd/vendor.hpp icd/vendor.cpp icd/vendor_${VK_LOADER_VENDOR} + icd/vtable.cpp ) target_link_libraries (cruft-vk-icd cruft-json cruft) diff --git a/icd/fwd.hpp b/icd/fwd.hpp new file mode 100644 index 0000000..e69de29 diff --git a/icd/vendor.hpp b/icd/vendor.hpp index 85bc202..21a857f 100644 --- a/icd/vendor.hpp +++ b/icd/vendor.hpp @@ -1,45 +1,46 @@ -#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) ( - VkInstanceCreateInfo const*, - VkAllocationCallbacks const*, - 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 +#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) ( + VkInstanceCreateInfo const*, + VkAllocationCallbacks const*, + VkInstance* + ) noexcept; + } vtable; + + private: + ::cruft::library m_library; + + public: + using get_proc_t = void* (*)(VkInstance, char const*); + get_proc_t const m_get_proc; + }; +} diff --git a/icd/vtable.cpp b/icd/vtable.cpp new file mode 100644 index 0000000..054bc77 --- /dev/null +++ b/icd/vtable.cpp @@ -0,0 +1,14 @@ +#include "icd/vtable.hpp" + +#include "vendor.hpp" + +using cruft::vk::icd::instance_table; + +instance_table::instance_table (vendor &src) +{ +#define LOADFN(name) this->name = reinterpret_castname)> (src.m_get_proc( + + this->vkCreateInstance = reinterpret_castvkCreateInstance)> ( + src.m_get_proc(nullptr, "vkCreateInstance") + ); +} diff --git a/tools/info.cpp b/tools/info.cpp index 9c011fa..04992e2 100644 --- a/tools/info.cpp +++ b/tools/info.cpp @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright: - * 2017, Danny Robson + * 2017-2018, Danny Robson */ #include @@ -17,6 +17,7 @@ #include "../icd/vendor.hpp" +#include "icd/vtable.hpp" /////////////////////////////////////////////////////////////////////////////// @@ -25,6 +26,7 @@ main (int, char**) { for (auto const &i: cruft::vk::icd::enumerate ()) { cruft::vk::icd::vendor v (i); + cruft::vk::icd::instance_table t (v); //std::cout << "available_layers: [ " // << cruft::make_infix (cruft::vk::instance::available_layers ()) diff --git a/tools/spec.py b/tools/spec.py index e5c2e7a..dce02ab 100644 --- a/tools/spec.py +++ b/tools/spec.py @@ -229,6 +229,7 @@ class handle(type): super().__init__(name, depends=[type]) self.type = type + self.parent = node.attrib.get('parent', None) def declare(self): return "struct %(name)s_t; using %(name)s = %(name)s_t*;" % { @@ -236,6 +237,20 @@ class handle(type): "type": self.type } + def has_parent(self, name:str, reg:registry) -> bool: + """ + Recursively check if this type is derived from a given parent type. + """ + assert name + assert reg + + if not self.parent: + return False + if self.parent == name: + return True + + return reg.types[self.parent].has_parent(name, reg) + class enum(type): def __init__(self,node): @@ -395,10 +410,14 @@ class command(type): **kwargs ) - self.type = "" + self.type = node.find('type').text + + self.param = "" for i in node.iter(): - self.type += i.text or "" - self.type += i.tail or "" + self.param += i.text or "" + self.param += i.tail or "" + # normalise whitespace + self.param = " ".join(self.param.split()) def __init__(self, node): assert node.tag == "command" @@ -416,9 +435,43 @@ class command(type): return "%(result)s %(name)s (%(params)s) noexcept;" % { 'name': rename(self.name), 'result': self.result, - 'params': ", ".join(p.type for p in self.params) + 'params': ", ".join(p.param for p in self.params) } + def is_instance(self, reg:registry): + assert reg + + if not self.params: + return True + + first_arg = self.params[0].type + if first_arg == 'VkInstance': + return True + + first_obj = reg.types[first_arg] + + # If the first type isn't a handle of any description then it should + # be an instance function. + if not isinstance(first_obj, handle): + return True + + # Both VkInstance and VkPhysicalDevice are listed as possible instance + # parameters. + # + # Test that the handle is derived from VkInstance, and not derived from + # VkDevice. The second test is required because VkDevice is indirectly + # derived from VkInstance. This approach buys us a little more + # generality. + + if not first_obj.has_parent('VkInstance', reg): + return False + if first_arg == 'VkDevice' or first_obj.has_parent('VkDevice', reg): + return False + return True + + def is_device(self, reg:registry): + return not self.is_instance(reg) + class require(object): def __init__(self, root): @@ -586,7 +639,7 @@ def parse_types(reg:registry, root): ] if category in supported_categories: - obj = globals()[category] (t) + obj = globals()[category](t) reg.types[name] = obj else: raise 'unhandled type' @@ -729,17 +782,43 @@ if __name__ == '__main__': dst.write('}\n') with open(args.icd, 'w') as icd: - icd.write(""" + commands = [i for i in q if isinstance(i, command)] + instance_commands = [i for i in commands if i.is_instance(reg)] + device_commands = [i for i in commands if i.is_device(reg)] + + assert len(instance_commands) + len(device_commands) == len(commands) + + icd.write(f""" #include "vk.hpp" - namespace cruft::vk::icd { - struct vtable { + #include + + #define MAP_COMMANDS(FUNC) MAP0(FUNC,{",".join(i.name for i in commands)}) + #define MAP_INSTANCE_COMMANDS(FUNC) MAP0(FUNC,{",".join(i.name for i in instance_commands)}) + #define MAP_DEVICE_COMMANDS(FUNC) MAP0(FUNC,{",".join(i.name for i in device_commands)}) + + namespace cruft::vk::icd {{ + struct vendor; + + struct func {{ + void *handle; + void const *table; + }}; + + struct instance_table {{ + instance_table (vendor &); """) - for obj in q: - if not isinstance(obj, command): - continue - icd.write(f"{obj.result} (*{obj.name}) ({','.join(p.type for p in obj.params)});\n") + for obj in instance_commands: + icd.write(f"{obj.result} (*{obj.name}) ({','.join(p.param for p in obj.params)}) = nullptr;\n") + + icd.write("""}; + + struct device_table { + """) + + for obj in device_commands: + icd.write(f"{obj.result} (*{obj.name}) ({','.join(p.param for p in obj.params)}) = nullptr;\n") icd.write(""" }; @@ -756,16 +835,25 @@ if __name__ == '__main__': #pragma GCC diagnostic ignored "-Wunused-parameter" - static cruft::vk::icd::vtable *s_vtable = nullptr; + static cruft::vk::icd::instance_table const *i_table = nullptr; + static cruft::vk::icd::device_table const *d_table = nullptr; """) - - for obj in q: - if not isinstance(obj, command): - continue + for obj in commands: + if obj.is_instance(reg): + table = "i_table" + elif obj.is_device(reg): + table = "d_table" + else: + raise Exception("unhandled command type") dispatch.write(f""" - extern "C" {obj.result} {rename(obj.name)} ({", ".join(p.type for p in obj.params)}) noexcept {{ - return (*s_vtable->{obj.name})({", ".join(p.name for p in obj.params)}); - }} + extern "C" {obj.result} {rename(obj.name)} ({", ".join(p.param for p in obj.params)}) noexcept {{ + auto const entry = reinterpret_cast ({obj.params[0].name}); + auto const *table = reinterpret_cast (entry->table); + + return (table->{obj.name})( + reinterpret_cast (entry->handle), + {", ".join(p.name for p in obj.params[1:])} + ); """)