icd: use global, instance, and device tables

This commit is contained in:
Danny Robson 2019-01-05 23:30:52 +11:00
parent deca2ded59
commit 833d82f818
7 changed files with 116 additions and 130 deletions

View File

@ -14,7 +14,8 @@ endif ()
############################################################################### ###############################################################################
file (MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icd") set (GENERATED_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/cruft/vk/")
file (MAKE_DIRECTORY "${GENERATED_PREFIX}/icd")
if (WIN32) if (WIN32)
@ -29,18 +30,18 @@ endif()
##----------------------------------------------------------------------------- ##-----------------------------------------------------------------------------
add_custom_command ( add_custom_command (
OUTPUT OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/vk.hpp" "${GENERATED_PREFIX}/vk.hpp"
"${CMAKE_CURRENT_BINARY_DIR}/icd/dispatch.cpp" "${GENERATED_PREFIX}/icd/dispatch.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/icd/vtable.hpp" "${GENERATED_PREFIX}/icd/vtable.hpp"
COMMENT COMMENT
"[spec.py] vk.hpp" "[spec.py] vk.hpp"
COMMAND COMMAND
"${PYTHON_EXECUTABLE}" "${PYTHON_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/tools/spec.py" "${CMAKE_CURRENT_SOURCE_DIR}/tools/spec.py"
"--src" "${CMAKE_CURRENT_SOURCE_DIR}/specs/xml/vk.xml" "--src" "${CMAKE_CURRENT_SOURCE_DIR}/specs/xml/vk.xml"
"--dst" "${CMAKE_CURRENT_BINARY_DIR}/vk.hpp" "--dst" "${GENERATED_PREFIX}/vk.hpp"
"--icd" "${CMAKE_CURRENT_BINARY_DIR}/icd/vtable.hpp" "--icd" "${GENERATED_PREFIX}/icd/vtable.hpp"
"--dispatch" "${CMAKE_CURRENT_BINARY_DIR}/icd/dispatch.cpp" "--dispatch" "${GENERATED_PREFIX}/icd/dispatch.cpp"
"--platform" "${VK_PLATFORM}" "--platform" "${VK_PLATFORM}"
DEPENDS DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/tools/spec.py" "${CMAKE_CURRENT_SOURCE_DIR}/tools/spec.py"
@ -62,8 +63,8 @@ endif()
##----------------------------------------------------------------------------- ##-----------------------------------------------------------------------------
add_library (cruft-vk-icd STATIC add_library (cruft-vk-icd STATIC
icd/fwd.hpp icd/fwd.hpp
${CMAKE_CURRENT_BINARY_DIR}/icd/vtable.hpp ${GENERATED_PREFIX}/icd/vtable.hpp
${CMAKE_CURRENT_BINARY_DIR}/icd/dispatch.cpp ${GENERATED_PREFIX}/icd/dispatch.cpp
icd/dispatch.hpp icd/dispatch.hpp
icd/vendor.hpp icd/vendor.hpp
icd/vendor.cpp icd/vendor.cpp
@ -82,7 +83,7 @@ target_link_libraries (cruft-vk-icd cruft-json cruft)
############################################################################### ###############################################################################
list (APPEND sources list (APPEND sources
vk.hpp "${GENERATED_PREFIX}/vk.hpp"
fwd.hpp fwd.hpp
object.cpp object.cpp
@ -144,7 +145,7 @@ target_link_libraries (cruft-vk cruft-vk-icd cruft)
target_include_directories (cruft-vk target_include_directories (cruft-vk
PUBLIC PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}" "${GENERATED_PREFIX}"
"${CMAKE_CURRENT_SOURCE_DIR}/specs/include/vulkan" "${CMAKE_CURRENT_SOURCE_DIR}/specs/include/vulkan"
) )

View File

@ -5,6 +5,7 @@
using cruft::vk::icd::vendor; using cruft::vk::icd::vendor;
///////////////////////////////////////////////////////////////////////////////
template <> template <>
cruft::vk::icd::icd_t cruft::vk::icd::icd_t
json::tree::io<cruft::vk::icd::icd_t>::deserialise (json::tree::node const &obj) json::tree::io<cruft::vk::icd::icd_t>::deserialise (json::tree::node const &obj)
@ -19,6 +20,11 @@ json::tree::io<cruft::vk::icd::icd_t>::deserialise (json::tree::node const &obj)
} }
///////////////////////////////////////////////////////////////////////////////
cruft::vk::icd::global_table const *cruft::vk::icd::g_table;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
vendor::vendor (icd_t const &_icd): vendor::vendor (icd_t const &_icd):
vendor (cruft::library (_icd.icd.library_path)) vendor (cruft::library (_icd.icd.library_path))
@ -28,7 +34,16 @@ vendor::vendor (icd_t const &_icd):
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
vendor::vendor (::cruft::library &&_library) vendor::vendor (::cruft::library &&_library)
: m_library (std::move (_library)) : m_library (std::move (_library))
, m_get_proc (m_library.symbol<decltype(m_get_proc)> ("vk_icdGetInstanceProcAddr")) , m_get_proc (
m_library.symbol<decltype(m_get_proc)> ("vk_icdGetInstanceProcAddr")
)
{ {
vtable.CreateInstance = reinterpret_cast<decltype(vtable.CreateInstance)> (m_get_proc (nullptr, "vkCreateInstance")); #define LOADFN(name) \
vtable.name = reinterpret_cast< \
decltype(vtable.name) \
> ( \
m_get_proc(nullptr, #name) \
);
MAP_INSTANCE_COMMANDS (LOADFN)
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "vk.hpp" #include <cruft/vk/vk.hpp>
#include <cruft/vk/icd/vtable.hpp>
#include <cruft/util/library.hpp> #include <cruft/util/library.hpp>
@ -28,20 +29,12 @@ namespace cruft::vk::icd {
vendor (icd_t const&); vendor (icd_t const&);
vendor (::cruft::library &&); vendor (::cruft::library &&);
struct vtable_t {
VkResult (*CreateInstance) (
VkInstanceCreateInfo const*,
VkAllocationCallbacks const*,
VkInstance*
) noexcept;
void (*GetInstanceProc) (VkInstance instance, const char* pName) noexcept;
} vtable;
private: private:
::cruft::library m_library; ::cruft::library m_library;
public: public:
global_table vtable;
using get_proc_t = void* (*)(VkInstance, char const*); using get_proc_t = void* (*)(VkInstance, char const*);
get_proc_t const m_get_proc; get_proc_t const m_get_proc;
}; };

View File

@ -1,14 +1,7 @@
#include "icd/vtable.hpp" #include <cruft/vk/icd/vtable.hpp>
#include "vendor.hpp"
using cruft::vk::icd::instance_table; using cruft::vk::icd::instance_table;
instance_table::instance_table (vendor &src)
{
#define LOADFN(name) this->name = reinterpret_cast<decltype(this->name)> (src.m_get_proc(
this->vkCreateInstance = reinterpret_cast<decltype(this->vkCreateInstance)> ( ///////////////////////////////////////////////////////////////////////////////
src.m_get_proc(nullptr, "vkCreateInstance")
);
}

View File

@ -7,8 +7,7 @@
* 2016-2017, Danny Robson <danny@nerdcruft.net> * 2016-2017, Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef CRUFT_VK_INSTANCE_HPP #pragma once
#define CRUFT_VK_INSTANCE_HPP
#include "./object.hpp" #include "./object.hpp"
@ -54,5 +53,3 @@ namespace cruft::vk {
static std::vector<VkLayerProperties> available_layers (void); static std::vector<VkLayerProperties> available_layers (void);
}; };
} }
#endif

View File

@ -9,6 +9,8 @@
#include <cruft/util/iterator.hpp> #include <cruft/util/iterator.hpp>
#include <cruft/vk/icd/vtable.hpp>
#include <cruft/vk/instance.hpp> #include <cruft/vk/instance.hpp>
#include <cruft/vk/physical_device.hpp> #include <cruft/vk/physical_device.hpp>
#include <cruft/vk/ostream.hpp> #include <cruft/vk/ostream.hpp>
@ -26,7 +28,8 @@ main (int, char**)
{ {
for (auto const &i: cruft::vk::icd::enumerate ()) { for (auto const &i: cruft::vk::icd::enumerate ()) {
cruft::vk::icd::vendor v (i); cruft::vk::icd::vendor v (i);
cruft::vk::icd::instance_table t (v);
cruft::vk::icd::g_table = &v.vtable;
//std::cout << "available_layers: [ " //std::cout << "available_layers: [ "
// << cruft::make_infix (cruft::vk::instance::available_layers ()) // << cruft::make_infix (cruft::vk::instance::available_layers ())
@ -35,7 +38,7 @@ main (int, char**)
cruft::vk::instance instance; cruft::vk::instance instance;
std::cout << "instance: " << instance << '\n'; std::cout << "instance: " << instance << '\n';
for (const auto &d: cruft::vk::physical_device::find (instance)) //for (const auto &d: cruft::vk::physical_device::find (instance))
std::cout << d << '\n'; // std::cout << d << '\n';
} }
} }

View File

@ -428,39 +428,38 @@ class Command(Type):
'params': ", ".join(p.param for p in self.params) 'params': ", ".join(p.param for p in self.params)
} }
def is_global(self, reg: Registry):
if not self.params:
return True
return not isinstance(self.params[0], Handle)
def is_instance(self, reg: Registry): def is_instance(self, reg: Registry):
assert reg assert reg
if not self.params: if not self.params:
return True return False
first_arg = self.params[0].type first_name = self.params[0].type
if first_arg == 'VkInstance': first_obj = reg.types[first_name]
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): 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 return False
if first_arg == 'VkDevice' or first_obj.has_parent('VkDevice', reg):
return False instance = first_obj.has_parent('VkInstance', reg)
return True device = first_obj.has_parent('VkPhysicalDevice', reg)
return instance and not device
def is_device(self, reg: Registry): def is_device(self, reg: Registry):
return not self.is_instance(reg) if not self.params:
return False
first_name = self.params[0].type
first_obj = reg.types[first_name]
if not isinstance(first_obj, Handle):
return False
return first_obj.has_parent('VkPhysicalDevice', reg)
############################################################################### ###############################################################################
@ -751,50 +750,47 @@ def write_header(dst: TextIO, q: List[Type], reg: Registry):
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def write_icd(dst: TextIO, q: List[Type], reg: Registry): def write_icd(dst: TextIO, q: List[Type], reg: Registry):
commands = [i for i in q if isinstance(i, Command)] 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) collections = {
'global': Command.is_global,
'instance': Command.is_instance,
'device': Command.is_device,
}
dst.write(f""" dst.write(f"""
#include "vk.hpp" #pragma once
#include <cruft/vk/vk.hpp>
#include <cruft/util/preprocessor.hpp> #include <cruft/util/preprocessor.hpp>
#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 {{ namespace cruft::vk::icd {{
class vendor; class vendor;
struct func {{ #define MAP_COMMANDS(FUNC) MAP0(FUNC,{",".join(i.name for i in commands)})
void *handle;
void const *table;
}};
struct instance_table {{ """)
instance_table (vendor &);
for name, test in collections.items():
dst.write(f"""
#define MAP_{name.upper()}_COMMANDS(FUNC) \
MAP0(FUNC,{",".join(i.name for i in commands if i.is_global(reg))})
struct {name}_table{{
""") """)
# Generate the vtable entries for instance methods # Generate the vtable entries for instance methods
dst.writelines(( dst.writelines((
f"{obj.result} (*{obj.name}) ({','.join(p.param for p in obj.params)}) = nullptr;" f"{obj.result} (*{obj.name}) ({','.join(p.param for p in obj.params)}) = nullptr;"
for obj in instance_commands for obj in commands if test(obj, reg)
))
dst.write("""};
struct device_table {
""")
# Generate the vtable and entries for device_commands
dst.writelines((
f"{obj.result} (*{obj.name}) ({','.join(p.param for p in obj.params)}) = nullptr;"
for obj in device_commands
)) ))
dst.write(""" dst.write("""
}; };
""")
dst.write("""
extern cruft::vk::icd::global_table const *g_table [[maybe_unused]];
} }
""") """)
@ -802,62 +798,50 @@ def write_icd(dst: TextIO, q: List[Type], reg: Registry):
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def write_dispatch(dst: TextIO, q: List[Type], reg: Registry): def write_dispatch(dst: TextIO, q: List[Type], reg: Registry):
dst.write(""" dst.write("""
#include "../vk.hpp" #include <cruft/vk/vk.hpp>
#include "vtable.hpp" #include <cruft/vk/icd/vtable.hpp>
#include <cruft/vk/icd/dispatch.hpp>
#include "icd/dispatch.hpp"
#include <cruft/util/debug.hpp> #include <cruft/util/debug.hpp>
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
static cruft::vk::icd::instance_table const *i_table [[maybe_unused]] = nullptr; struct indirect {
static cruft::vk::icd::device_table const *d_table [[maybe_unused]] = nullptr; void *handle;
void const *table;
void (*cruft_vk_icdGetInstanceProcAddr) ( };
VkInstance instance,
const char* pName
) = nullptr;
void cruft::vk::icd::init (vendor const &impl)
{
cruft_vk_icdGetInstanceProcAddr = impl.vtable.GetInstanceProc;
}
""") """)
for obj in (i for i in q if isinstance(i, Command)): for obj in (i for i in q if isinstance(i, Command)):
first_arg = reg.types[obj.params[0].type] first_arg = reg.types[obj.params[0].type]
if not isinstance(first_arg, Handle): if obj.is_global(reg):
dst.write(f""" dst.write(f"""
extern "C" {obj.result} {rename(obj.name)} ({", ".join(p.param for p in obj.params)}) noexcept {{ extern "C"
unimplemented (); {obj.result}
}}""") {rename(obj.name)} ({", ".join(p.param for p in obj.params)}) noexcept {{
return cruft::vk::icd::g_table->{obj.name} ({", ".join(p.name for p in obj.params)});
}}
""")
continue continue
elif obj.is_instance(reg):
if first_arg.has_parent('VkDevice', reg): table = 'instance'
table = "d_table" elif obj.is_device(reg):
elif first_arg.has_parent('VkInstance', reg): table = 'device'
table = 'i_table'
else: else:
raise Exception("Unknown param type") raise Exception("Unhandled command type")
dst.write(f""" dst.write(f"""
extern "C" {obj.result} {rename(obj.name)} ({", ".join(p.param for p in obj.params)}) noexcept {{ extern "C" {obj.result} {rename(obj.name)} ({", ".join(p.param for p in obj.params)}) noexcept {{
using first_arg_t = std::decay_t<decltype({obj.params[0].name})>; using first_arg_t = std::decay_t<decltype({obj.params[0].name})>;
if constexpr (is_instance_v<first_arg_t>) {{ auto const entry = reinterpret_cast<indirect const*> ({obj.params[0].name});
auto const entry = reinterpret_cast<cruft::vk::icd::func const*> ({obj.params[0].name}); auto const *table = reinterpret_cast<{table}_table*> (entry->table);
auto const *table = reinterpret_cast<decltype({table})> (entry->table);
return (table->{obj.name})( return (table->{obj.name})(
reinterpret_cast<decltype({obj.params[0].name})> (entry->handle) reinterpret_cast<decltype({obj.params[0].name})> (entry->handle)
{", ".join([''] + [p.name for p in obj.params[1:]])} {", ".join([''] + [p.name for p in obj.params[1:]])}
); );
}} else {{
unimplemented ();
}}
}} }}
""") """)