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

View File

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

View File

@ -1,14 +1,7 @@
#include "icd/vtable.hpp"
#include "vendor.hpp"
#include <cruft/vk/icd/vtable.hpp>
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>
*/
#ifndef CRUFT_VK_INSTANCE_HPP
#define CRUFT_VK_INSTANCE_HPP
#pragma once
#include "./object.hpp"
@ -54,5 +53,3 @@ namespace cruft::vk {
static std::vector<VkLayerProperties> available_layers (void);
};
}
#endif

View File

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

View File

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