icd: resolve enough to run the info tool

This commit is contained in:
Danny Robson 2019-03-02 22:47:03 +11:00
parent 6e813ecc5e
commit ce02f21614
8 changed files with 323 additions and 127 deletions

View File

@ -49,10 +49,18 @@ namespace cruft::vk {
>; >;
if constexpr (returns_vkresult) { if constexpr (returns_vkresult) {
try_code (func (maybe_native (args)...)); try_code (
std::invoke (
std::forward<FuncT> (func),
maybe_native (std::forward<Args> (args))...
)
);
return; return;
} else { } else {
return func (maybe_native (args)...); return std::invoke (
std::forward<FuncT> (func),
maybe_native (std::forward<Args> (args))...
);
} }
} }
@ -117,7 +125,9 @@ namespace cruft::vk {
} }
//--------------------------------------------------------------------- ///--------------------------------------------------------------------
/// Safely calls a function that returns an array of Handle objects,
/// and returns a vector of wrapped objects.
template < template <
typename ReturnT, typename ReturnT,
template <typename,typename...> class ContainerT = std::vector, template <typename,typename...> class ContainerT = std::vector,

View File

@ -2,9 +2,18 @@
#include <cruft/json/tree.hpp> #include <cruft/json/tree.hpp>
#include <cruft/util/std.hpp>
#include <cruft/util/log.hpp>
using cruft::vk::icd::vendor; using cruft::vk::icd::vendor;
#define MAP_ICD_COMMANDS(FUNC,...) MAP0(FUNC,\
vk_icdNegotiateLoaderICDInterfaceVersion,\
vk_icdGetInstanceProcAddr,\
vk_icdGetPhysicalDeviceProcAddr)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
template <> template <>
cruft::vk::icd::icd_t cruft::vk::icd::icd_t
@ -20,11 +29,6 @@ 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))
@ -33,17 +37,45 @@ 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")
)
{ {
#define LOADFN(name) \ // Negotiate needs to be called before anything else. But we load all the
vtable.name = reinterpret_cast< \ // ICD calls at once for simplicity.
decltype(vtable.name) \ #define GET(NAME) vtable.NAME = m_library.symbol<decltype(vtable.NAME)> (#NAME);
> ( \ MAP_ICD_COMMANDS (GET)
m_get_proc(nullptr, #name) \ #undef GET
);
MAP_INSTANCE_COMMANDS (LOADFN) version = 2;
switch (auto err = vtable.vk_icdNegotiateLoaderICDInterfaceVersion (&version); err) {
case VK_ERROR_INCOMPATIBLE_DRIVER:
static constexpr char incompatible_message[] = "Incompatible Vulkan ICD interface";
LOG_ERROR ("%! %!", incompatible_message, version);
throw std::runtime_error (incompatible_message);
default:
static constexpr char unknown_message[] = "Unknown Vulkan ICD interface response";
LOG_ERROR (
"%! %!",
unknown_message,
static_cast<std::underlying_type_t<decltype(err)>>(err)
);
throw std::runtime_error (unknown_message);
case VK_SUCCESS:
LOG_INFO ("vk::icd version %!", version);
break;
}
// Only load the instance table after we've queried all the icd functions.
itable = {
#define LOADFN(NAME) \
.NAME = reinterpret_cast< \
decltype(itable.NAME) \
> ( \
vtable.vk_icdGetInstanceProcAddr (nullptr, #NAME) \
),
MAP_INSTANCE_COMMANDS (LOADFN)
};
} }

View File

@ -4,6 +4,7 @@
#include <cruft/vk/icd/vtable.hpp> #include <cruft/vk/icd/vtable.hpp>
#include <cruft/util/library.hpp> #include <cruft/util/library.hpp>
#include <cruft/util/std.hpp>
#include <string> #include <string>
#include <filesystem> #include <filesystem>
@ -24,6 +25,12 @@ namespace cruft::vk::icd {
enumerate (void); enumerate (void);
struct vendor_table {
VkResult (*vk_icdNegotiateLoaderICDInterfaceVersion)(u32*) = nullptr;
void* (*vk_icdGetInstanceProcAddr) (VkInstance, char const*) = nullptr;
void* (*vk_icdGetPhysicalDeviceProcAddr) (VkInstance, char const*) = nullptr;
};
class vendor { class vendor {
public: public:
vendor (icd_t const&); vendor (icd_t const&);
@ -33,9 +40,8 @@ namespace cruft::vk::icd {
::cruft::library m_library; ::cruft::library m_library;
public: public:
global_table vtable; vendor_table vtable;
instance_table itable;
using get_proc_t = void* (*)(VkInstance, char const*); u32 version = 0;
get_proc_t const m_get_proc;
}; };
} }

View File

@ -41,6 +41,9 @@ cruft::vk::icd::enumerate (void)
for (size_t i = 0; i < words.we_wordc; ++i) { for (size_t i = 0; i < words.we_wordc; ++i) {
try { try {
for (auto const &path: fs::directory_iterator (words.we_wordv[i])) { for (auto const &path: fs::directory_iterator (words.we_wordv[i])) {
if (path.is_directory ())
continue;
found.push_back (from_json<icd_t> (*json::tree::parse (path))); found.push_back (from_json<icd_t> (*json::tree::parse (path)));
} }
} catch (std::exception const &e) { } catch (std::exception const &e) {

View File

@ -4,4 +4,5 @@ using cruft::vk::icd::instance_table;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
cruft::vk::icd::vendor_table const *cruft::vk::icd::v_table = nullptr;
cruft::vk::icd::instance_table const *cruft::vk::icd::i_table = nullptr;

View File

@ -90,16 +90,18 @@ namespace cruft::vk {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/// a vulkan object that is obtained by listings from a parent object. /// A vulkan object that is obtained by listings from a parent object.
template <typename SelfT, typename ParentT> template <typename SelfT, typename ParentT>
struct enumerated : public object<SelfT> { struct enumerated : public object<SelfT> {
using object<SelfT>::object; using object<SelfT>::object;
/// Returns a vector of available objects given a parent object.
static std::vector<SelfT> static std::vector<SelfT>
find (const ParentT &parent) find (const ParentT &parent)
{ {
return error::try_handles<SelfT> ( return error::try_handles<SelfT> (
enum_traits<native_t<SelfT>>::enumerate, parent.native () enum_traits<native_t<SelfT>>::enumerate,
parent.native ()
); );
} }
}; };

View File

@ -30,7 +30,8 @@ main (int, char**)
std::cout << "[ "; std::cout << "[ ";
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::g_table = &v.vtable; cruft::vk::icd::i_table = &v.itable;
cruft::vk::icd::v_table = &v.vtable;
cruft::vk::instance instance; cruft::vk::instance instance;

View File

@ -3,6 +3,7 @@
import sys import sys
import logging import logging
from typing import List, Dict, Set, TextIO from typing import List, Dict, Set, TextIO
import pprint
import xml.etree.ElementTree import xml.etree.ElementTree
@ -392,32 +393,27 @@ class Constant(Type):
class Command(Type): class Command(Type):
class Param(Type): class Param(Type):
name: str name: str
"""An appropriate title for this parameter""" """An string that can be used to refer to the parameter variable"""
type: str type: str
"""The name of this parameter's dependant type""" """The name of this parameter's dependant type"""
param: str param: str
""" """
The components of this type for a C definition (ie, includes The components of this type for a C definition (ie, includes
pointer, const, and other decorations pointer, const, other decorations, _and_ the variable name (which must
be the same as self.name for a useful system).
""" """
def __init__(self, node, **kwargs): def __init__(self, name:str, type:str, param:str, depends:List[str]):
assert node.tag == 'param'
super().__init__( super().__init__(
name=node.find('name').text, name=name,
depends=[node.find('type').text], depends=depends,
**kwargs
) )
self.type = node.find('type').text self.type = type
self.param = param
self.param = "" def __repr__(self) -> str:
for i in node.iter(): return f"{{ name: '{self.name}', type: '{self.type}', param: '{self.param}' }}"
self.param += i.text or ""
self.param += i.tail or ""
# normalise whitespace
self.param = " ".join(self.param.split())
def is_pointer(self): def is_pointer(self):
return '*' in self.param return '*' in self.param
@ -429,6 +425,9 @@ class Command(Type):
self.params = params self.params = params
self.depends += depends or [] self.depends += depends or []
def __repr__(self) -> str:
return f"{{ name: '{self.name}', result: '{self.result}', param: {self.params} }}"
def declare(self): def declare(self):
return 'extern "C" %(result)s %(name)s (%(params)s) noexcept;' % { return 'extern "C" %(result)s %(name)s (%(params)s) noexcept;' % {
'name': rename(self.name), 'name': rename(self.name),
@ -436,14 +435,6 @@ 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
first_name = self.params[0].type
first_obj = reg.types[first_name]
return not isinstance(first_obj, Handle)
def is_instance(self, reg: Registry): def is_instance(self, reg: Registry):
assert reg assert reg
@ -454,11 +445,11 @@ class Command(Type):
first_obj = reg.types[first_name] first_obj = reg.types[first_name]
if not isinstance(first_obj, Handle): if not isinstance(first_obj, Handle):
return False return True
instance = first_obj.has_parent('VkInstance', reg) instance = first_obj.name == 'VkInstance'
device = first_obj.has_parent('VkPhysicalDevice', reg) physical = first_obj.name == 'VkPhysicalDevice'
return instance and not device return instance or physical
def is_device(self, reg: Registry): def is_device(self, reg: Registry):
if not self.params: if not self.params:
@ -469,24 +460,18 @@ class Command(Type):
if not isinstance(first_obj, Handle): if not isinstance(first_obj, Handle):
return False return False
return first_obj.has_parent('VkPhysicalDevice', reg) for i in ['VkDevice', 'VkQueue', 'VkCommandBuffer']:
if first_obj.has_parent(i, reg):
return True
return False
############################################################################### ###############################################################################
class Require(object): class Require(object):
def __init__(self, root): def __init__(self, values, depends: List[str]):
self.values = [] self.values = values or []
self.depends = [] self.depends = depends or []
for node in root:
if node.tag == 'enum':
self.values.append(node)
elif node.tag in ['command', 'type']:
self.depends.append(node.attrib['name'])
elif node.tag in ['comment']:
pass
else:
raise RuntimeError("Unknown requires node")
def apply(self, reg: Registry, extnumber=None): def apply(self, reg: Registry, extnumber=None):
required = [] required = []
@ -559,7 +544,7 @@ class Extension(Type):
for node in root: for node in root:
if node.tag == 'require': if node.tag == 'require':
self.requires.append(Require(node)) self.requires.append(parse_require(node))
else: else:
raise RuntimeError("Unknown extension node") raise RuntimeError("Unknown extension node")
@ -660,6 +645,28 @@ def parse_enums(reg: Registry, root):
else: else:
owner[valuename] = Constant(node) owner[valuename] = Constant(node)
#------------------------------------------------------------------------------
def parse_param(root) -> Command.Param:
assert root.tag == 'param'
param = ""
for i in root.iter():
param += i.text or ""
param += i.tail or ""
# normalise whitespace
param = " ".join(param.split())
name = root.find('name').text
type = root.find('type').text
depends = [root.find('type').text]
return Command.Param(
name=name,
type=type,
param=param,
depends=depends,
)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def parse_commands(reg: Registry, root): def parse_commands(reg: Registry, root):
@ -677,7 +684,7 @@ def parse_commands(reg: Registry, root):
name = proto.find('name').text name = proto.find('name').text
result = proto.find('type').text result = proto.find('type').text
params = [Command.Param(p) for p in node.findall('./param')] params = [parse_param(p) for p in node.findall('./param')]
depends = [result] depends = [result]
for p in params: for p in params:
depends += p.depends depends += p.depends
@ -686,6 +693,25 @@ def parse_commands(reg: Registry, root):
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def parse_require(root) -> Require:
assert root.tag == 'require'
values = []
depends = []
for node in root:
if node.tag == 'enum':
values.append(node)
elif node.tag in ['command', 'type']:
depends.append(node.attrib['name'])
elif node.tag in ['comment']:
pass
else:
raise RuntimeError("Unknown requires node")
return Require(values=values, depends=depends)
##-----------------------------------------------------------------------------
def parse_feature(reg: Registry, root): def parse_feature(reg: Registry, root):
assert root.tag == 'feature' assert root.tag == 'feature'
@ -695,7 +721,7 @@ def parse_feature(reg: Registry, root):
requires = [] requires = []
for node in root: for node in root:
if 'require' == node.tag: if 'require' == node.tag:
requires.append(Require(node)) requires.append(parse_require(node))
else: else:
raise RuntimeError("Unhandled feature node") raise RuntimeError("Unhandled feature node")
@ -769,7 +795,6 @@ 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)]
collections = { collections = {
'global': Command.is_global,
'instance': Command.is_instance, 'instance': Command.is_instance,
'device': Command.is_device, 'device': Command.is_device,
} }
@ -789,9 +814,13 @@ def write_icd(dst: TextIO, q: List[Type], reg: Registry):
""") """)
for name, test in collections.items(): for name, test in collections.items():
curr = [i for i in commands if test(i, reg)]
next = [i for i in commands if not test(i, reg)]
commands = next
dst.write(f""" dst.write(f"""
#define MAP_{name.upper()}_COMMANDS(FUNC) \ #define MAP_{name.upper()}_COMMANDS(FUNC) \
MAP0(FUNC,{",".join(i.name for i in commands if i.is_global(reg))}) MAP0(FUNC,{",".join([i.name for i in curr])})
struct {name}_table{{ struct {name}_table{{
""") """)
@ -799,7 +828,7 @@ def write_icd(dst: TextIO, q: List[Type], reg: Registry):
# 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 commands if test(obj, reg) for obj in curr
)) ))
dst.write(""" dst.write("""
@ -807,7 +836,9 @@ def write_icd(dst: TextIO, q: List[Type], reg: Registry):
""") """)
dst.write(""" dst.write("""
extern cruft::vk::icd::global_table const *g_table [[maybe_unused]]; struct vendor_table;
extern cruft::vk::icd::vendor_table const *v_table;
extern cruft::vk::icd::instance_table const *i_table;
} }
""") """)
@ -823,30 +854,88 @@ def write_dispatch(dst: TextIO, q: List[Type], reg: Registry):
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename HandleT, typename TableT>
struct indirect { struct indirect {
void *handle; HandleT handle;
void const *table; TableT table;
}; };
""") """)
implementations = {
'vkCreateInstance': """
auto res = std::make_unique<indirect<VkInstance,cruft::vk::icd::instance_table>> ();
auto err = cruft::vk::icd::i_table->vkCreateInstance (pCreateInfo, pAllocator, &res->handle);
if (err != VK_SUCCESS)
return err;
#define GET(NAME) res->table.NAME = reinterpret_cast<decltype(&NAME)> (cruft::vk::icd::v_table->vk_icdGetInstanceProcAddr (res->handle, #NAME));
MAP_INSTANCE_COMMANDS(GET)
#undef GET
#define GET(NAME) if (!res->table.NAME) res->table.NAME = reinterpret_cast<decltype(&NAME)> (res->table.vkGetInstanceProcAddr (res->handle, #NAME));
MAP_INSTANCE_COMMANDS(GET)
#undef GET
*pInstance = (VkInstance)res.release ();
return err;
""",
'vkCreateDevice': """
(void)physicalDevice;
(void)pCreateInfo;
(void)pAllocator;
(void)pDevice;
unimplemented ();
""",
'vkEnumeratePhysicalDevices': """
auto const entry = reinterpret_cast<
indirect<VkInstance,cruft::vk::icd::instance_table> const*
> (instance);
if (!pPhysicalDeviceCount || !pPhysicalDevices) {
return (entry->table.vkEnumeratePhysicalDevices)(
entry->handle, pPhysicalDeviceCount, pPhysicalDevices
);
}
std::vector<VkPhysicalDevice> res (*pPhysicalDeviceCount);
auto err = (entry->table.vkEnumeratePhysicalDevices)(
entry->handle, pPhysicalDeviceCount, res.data ()
);
res.resize (*pPhysicalDeviceCount);
if (err != VK_SUCCESS)
return err;
for (uint32_t i = 0; i < *pPhysicalDeviceCount; ++i) {
auto wrapped = std::make_unique<indirect<VkPhysicalDevice,cruft::vk::icd::instance_table>> ();
wrapped->handle = res[i];
#define GET(NAME) wrapped->table.NAME = wrapped->table.NAME ?: reinterpret_cast<decltype(&NAME)> (vkGetInstanceProcAddr (instance, #NAME));
MAP_INSTANCE_COMMANDS(GET)
#undef GET
pPhysicalDevices[i] = (VkPhysicalDevice)wrapped.release ();
}
return err;
"""
}
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 obj.is_global(reg): instance_forward = f"""
dst.write(f""" return cruft::vk::icd::i_table->{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 {{ if obj.is_instance(reg):
return cruft::vk::icd::g_table->{obj.name} ({", ".join(p.name for p in obj.params)});
}}
""")
continue
elif obj.is_instance(reg):
table = 'instance' table = 'instance'
elif obj.is_device(reg): elif obj.is_device(reg):
table = 'device' table = 'device'
else: else:
raise Exception("Unhandled command type") raise Exception(f"Unhandled command type for {obj}")
if obj.params: if obj.params:
last_param = obj.params[-1] last_param = obj.params[-1]
@ -855,15 +944,20 @@ def write_dispatch(dst: TextIO, q: List[Type], reg: Registry):
else: else:
is_creating = False is_creating = False
if obj.is_instance(reg) and obj.params[0].type not in ['VkInstance', 'VkPhysicalDevice']:
forwarding = f"return ::cruft::vk::icd::i_table->{obj.name} ({', '.join (i.name for i in obj.params)});"
else:
forwarding = f"""
auto const entry = reinterpret_cast<indirect<{obj.params[0].type},cruft::vk::icd::{table}_table> const*> ({obj.params[0].name});
return (entry->table.{obj.name})(
{", ".join(['entry->handle'] + [p.name for p in obj.params[1:]])}
);
"""
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 {{
auto const entry = reinterpret_cast<indirect const*> ({obj.params[0].name}); {implementations.get(obj.name, forwarding)}
auto const *table = reinterpret_cast<cruft::vk::icd::{table}_table const*> (entry->table);
return (table->{obj.name})(
reinterpret_cast<decltype({obj.params[0].name})> (entry->handle)
{", ".join([''] + [p.name for p in obj.params[1:]])}
);
}} }}
""") """)
@ -912,6 +1006,53 @@ def main():
reg.types['VK_DEFINE_HANDLE'] = AliasType("VK_DEFINE_HANDLE", "void*") reg.types['VK_DEFINE_HANDLE'] = AliasType("VK_DEFINE_HANDLE", "void*")
reg.types['VK_NULL_HANDLE'] = AliasValue("VK_NULL_HANDLE", "nullptr") reg.types['VK_NULL_HANDLE'] = AliasValue("VK_NULL_HANDLE", "nullptr")
#reg.types['vk_icdGetInstanceProcAddr'] = Command(
# name='vk_icdGetInstanceProcAddr',
# result='void*',
# params=[
# Command.Param(
# name='instance',
# type='VkInstance',
# param='VkInstance instance',
# depends=['VkInstance']
# ),
# Command.Param(
# name='pName',
# type='void*',
# param='char const *pName',
# depends=[]
# ),
# ]
#)
#reg.types['vk_icdGetPhysicalDeviceProcAddr'] = Command(
# name='vk_icdGetPhysicalDeviceProcAddr',
# result='void*',
# params=[
# Command.Param(
# name='instance',
# type='VkInstance',
# param='VkInstance instance',
# depends=['VkInstance']
# ),
# Command.Param(
# name='pName',
# type='void*',
# param='char const *pName',
# depends=[]
# ),
# ]
#)
#icd = Feature(
# "__nerdcruft_icd",
# requires=[
# Require(None, ["vk_icdGetInstanceProcAddr"]),
# Require(None, ["vk_icdGetPhysicalDeviceProcAddr"])
# ]
#)
#reg.features['__nerdcruft_icd'] = icd
#reg.types['__nerdcruft_icd'] = reg.features['__nerdcruft_icd']
# Request serialisation of all features # Request serialisation of all features
#features = [Feature(n) for n in root.findall('./feature')] #features = [Feature(n) for n in root.findall('./feature')]
#features = dict((f.name,f) for f in features) #features = dict((f.name,f) for f in features)