-
Notifications
You must be signed in to change notification settings - Fork 4
/
create_vulkan_wrapper.py
326 lines (267 loc) · 10.6 KB
/
create_vulkan_wrapper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import re
import urllib.request as req
src = req.urlopen("https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/master/include/vulkan/vulkan_core.h").read().decode('utf-8')
src_win32 = req.urlopen("https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/master/include/vulkan/vulkan_win32.h").read().decode('utf-8')
src_xcb = req.urlopen("https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/master/include/vulkan/vulkan_xcb.h").read().decode('utf-8')
src += "\n\n" + src_win32 + "\n\n" + src_xcb
BASE = r"""
#
# Vulkan wrapper generated from "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/master/include/vulkan/vulkan_core.h"
#
from ctypes import c_int8, c_uint16, c_int32, c_uint32, c_uint64, c_size_t, c_float, c_char, c_char_p, c_void_p, POINTER, Structure, Union, cast
from platform import system
# Helper functions
repr_fn = lambda self: str(dict(self._fields_))
def MAKE_VERSION(major, minor, patch):
return (major<<22) | (minor<<12) | patch
def define_struct(name, *args):
return type(name, (Structure,), {'_fields_': args, '__repr__': repr_fn})
def define_union(name, *args):
return type(name, (Union,), {'_fields_': args, '__repr__': repr_fn})
def load_functions(vk_object, functions_list, loader):
functions = []
for name, prototype in functions_list:
py_name = name.decode()[2::]
fn_ptr = loader(vk_object, name)
fn_ptr = cast(fn_ptr, c_void_p)
if fn_ptr:
fn = prototype(fn_ptr.value)
functions.append((py_name, fn))
elif __debug__ == True:
print('Function {} could not be loaded. (__debug__ == True)'.format(py_name))
return functions
API_VERSION_1_0 = MAKE_VERSION(1,0,0)
# System initialization
system_name = system()
if system_name == 'Windows':
from ctypes import WINFUNCTYPE, windll
FUNCTYPE = WINFUNCTYPE
vk = windll.LoadLibrary('vulkan-1')
elif system_name == 'Linux':
from ctypes import CFUNCTYPE, cdll
FUNCTYPE = CFUNCTYPE
vk = cdll.LoadLibrary('libvulkan.so.1')
# System types
HINSTANCE = c_size_t
HWND = c_size_t
HANDLE = c_size_t
DWORD = c_uint32
BOOL = c_uint32
LPCWSTR = POINTER(c_uint16)
xcb_connection_t = c_size_t
xcb_window_t = c_uint32
xcb_visualid_t = c_uint32
MirConnection = c_size_t
MirSurface = c_size_t
wl_display = c_void_p
wl_surface = c_void_p
Display = c_size_t
Window = c_uint32
VisualID = c_uint32
ANativeWindow = c_size_t
RROutput = c_uint32
SECURITY_ATTRIBUTES = define_struct('SECURITY_ATTRIBUTES',
('nLength', c_uint32),
('lpSecurityDescriptor', c_void_p),
('bInheritHandle', c_uint32),
)
# Base types
Flags = c_uint32
Bool32 = c_uint32
DeviceSize = c_uint64
SampleMask = c_uint32
# Base constants
LOD_CLAMP_NONE = 1000.0
REMAINING_MIP_LEVELS = c_uint32(-1)
REMAINING_ARRAY_LAYERS = c_uint32(-1)
WHOLE_SIZE = c_uint64(-1)
ATTACHMENT_UNUSED = c_uint32(-1)
TRUE = 1
FALSE = 0
QUEUE_FAMILY_IGNORED = c_uint32(-1)
SUBPASS_EXTERNAL = c_uint32(-1)
MAX_PHYSICAL_DEVICE_NAME_SIZE = 256
UUID_SIZE = 16
MAX_MEMORY_TYPES = 32
MAX_MEMORY_HEAPS = 16
MAX_EXTENSION_NAME_SIZE = 256
MAX_DESCRIPTION_SIZE = 256
MAX_DEVICE_GROUP_SIZE_KHX = 32
MAX_DEVICE_GROUP_SIZE = 32
LUID_SIZE_KHX = 8
LUID_SIZE_KHR = 8
LUID_SIZE = 8
MAX_DRIVER_NAME_SIZE_KHR = 256
MAX_DRIVER_INFO_SIZE_KHR = 256
"""[1:]
def no_vk(t):
t = t.replace('Vk', '')
t = t.replace('PFN_vk', 'Fn')
t = t.replace('VK_', '')
return t
def translate_type(t):
table = {
"float": 'c_float',
"uint32_t": 'c_uint32',
"uint64_t": 'c_uint64',
"size_t": 'c_size_t',
"float": 'c_float',
'int32_t': 'c_int32',
'int': 'c_int32',
'uint8_t': 'c_int8',
"uint16_t": 'c_uint16',
"char": "c_char",
"void": "None",
"void*": "c_void_p",
"const void*": 'c_void_p',
"const char*": 'c_char_p',
"const char* const*": 'POINTER(c_char_p)',
"struct wl_display*": "POINTER(wl_display)",
"struct wl_surface*": "POINTER(wl_surface)",
"const ObjectTableEntryNVX* const*": "POINTER(POINTER(ObjectTableEntryNVX))",
'v': ''
}
if t in table.keys():
return table[t]
if t.endswith("*"):
if t.startswith("const"):
ttype = t[6:len(t)-1]
ttype = table[ttype] if ttype in table else ttype
return "POINTER({})".format(ttype)
else:
ttype = t[:len(t)-1]
ttype = table[ttype] if ttype in table else ttype
return "POINTER({})".format(ttype)
return t
def parse_array(n, t):
name, length = n.split('[')
length = no_vk(length[0:len(length)-1])
type_ = "{} * {}".format(do_type(t), length)
return name, type_
def to_snake_case(name):
"Transform the name of something to snake_case"
def lower(m):
g0 = m.group(0)
g1, g2 = m.groups()
if g1:
return g0[0] + '_'+g1.lower() + g0[2]
elif g2:
return g0[0] + '_' + g0[1:3]
name = re.sub('[^A-Z]([A-Z])[^A-Z]|[^A-Z]([A-Z])', lower, name)
return name
def fix_arg(arg):
name = to_snake_case(arg)
# Remove useless pointer identifier in field name
for p in ('s_', 'p_', 'pp_', 'pfn_'):
if name.startswith(p):
name = name[len(p)::]
return name
def do_type(t):
return translate_type(no_vk(t))
def parse_handles_def(f):
f.write("# Handles types\n")
handles = re.findall("VK_DEFINE_HANDLE\(Vk(\w+)\)", src, re.S)
for h in handles:
f.write("{} = c_size_t\n".format(h))
handles_non_dispatchable = re.findall("VK_DEFINE_NON_DISPATCHABLE_HANDLE\(Vk(\w+)\)", src, re.S)
for h in handles_non_dispatchable:
f.write("{} = c_uint64\n".format(h))
def parse_flags_def(f):
f.write("# Flags types\n")
data = re.findall("typedef VkFlags Vk(\w+?);", src)
for name in data:
f.write("{} = Flags\n".format(name))
def parse_enums(f):
f.write("# Enums\n")
data = re.findall("typedef enum Vk(\w+) {(.+?)} \w+;", src, re.S)
for name, fields in data:
f.write("{} = c_uint32\n".format(name))
for name, value in re.findall("VK_(\w+?) = (.*?)(?:,|})", fields, re.S):
f.write("{} = {}\n".format(name, no_vk(value)))
f.write("\n")
def parse_allocation_callback(f):
# Allocation callback must be defined before the structs, but there are no good way to differenciate them
# from the function pointers. Hence why they are hardcoded here
f.write("""
# Allocation callback
FnAllocationFunction = FUNCTYPE(c_void_p, c_void_p, c_size_t, c_size_t, SystemAllocationScope)
FnReallocationFunction = FUNCTYPE(c_void_p, c_void_p, c_size_t, c_size_t, SystemAllocationScope)
FnFreeFunction = FUNCTYPE(None, c_void_p, c_void_p)
FnInternalAllocationNotification = FUNCTYPE(None, c_void_p, c_size_t, InternalAllocationType, SystemAllocationScope)
FnInternalFreeNotification = FUNCTYPE(None, c_void_p, c_size_t, InternalAllocationType, SystemAllocationScope)
FnDebugReportCallbackEXT = FUNCTYPE(Bool32, DebugReportFlagsEXT, DebugReportObjectTypeEXT, c_uint64, c_size_t, c_uint32, c_char_p, c_char_p, c_void_p)
"""[1::])
def parse_structs(f):
data = re.findall("typedef (struct|union) Vk(\w+?) {(.+?)} \w+?;", src, re.S)
for _type, name, fields in data:
# Structures that are not parsed correctly and not used anywhere else
if name in ("BaseOutStructure", "BaseInStructure"):
continue
# A callback definition MUST be written just before this struct
if name == "DebugUtilsMessengerCreateInfoEXT":
f.write("FnDebugUtilsMessengerCallbackEXT = FUNCTYPE(Bool32, DebugUtilsMessageSeverityFlagBitsEXT, DebugUtilsMessageTypeFlagsEXT, POINTER(DebugUtilsMessengerCallbackDataEXT), c_void_p)\n\n")
fields = re.findall("\s+(.+?)\s+([_a-zA-Z0-9[\]]+);", fields)
f.write("{0} = define_{1}('{0}', \n".format(name, _type))
for type_, fname in fields:
if '[' in fname:
fname, type_ = parse_array(fname, type_)
f.write(" ('{}', {}),\n".format(fix_arg(fname), do_type(type_)))
f.write(")\n\n")
# Some struct name that are not redefined automatically
if name in ("MemoryRequirements2",):
f.write("MemoryRequirements2KHR = MemoryRequirements2\n\n")
def parse_functions(f):
data = re.findall("typedef (\w+\*?) \(\w+ \*(\w+)\)\((.+?)\);", src, re.S)
for rt, name, fields in data:
no_vk_name = no_vk(name)
if no_vk_name not in ('FnAllocationFunction', 'FnReallocationFunction', 'FnFreeFunction', 'FnInternalAllocationNotification', 'FnInternalFreeNotification', 'FnDebugReportCallbackEXT'):
data_fields = ', '.join([do_type(t) for t in re.findall("(?:\s*|)(.+?)\s*\w+(?:,|$)", fields)])
f.write("{} = FUNCTYPE({}, {})\n".format(no_vk_name, do_type(rt), data_fields))
def group_functions(f):
data = re.findall("typedef (\w+\*?) \(\w+ \*(\w+)\)\((.+?)\);", src, re.S)
group_map = {"Instance":[], "Device":[], "Loader":[]}
for rt, vkname, fields in data:
fields_types_name = [do_type(t) for t in re.findall("(?:\s*|)(.+?)\s*\w+(?:,|$)", fields)]
table_name = fields_types_name[0]
name = no_vk(vkname)
if table_name in ('Device', 'Queue', 'CommandBuffer') and name != 'FnGetDeviceProcAddr':
group_map["Device"].append(name)
elif table_name in ('Instance', 'PhysicalDevice') or name == 'FnGetDeviceProcAddr':
group_map["Instance"].append(name)
elif table_name in ('c_void_p', '', 'DebugReportFlagsEXT') or name == 'FnGetInstanceProcAddr':
# Skip the allocation function and the dll entry point
pass
else:
#print(table_name, name)
group_map["Loader"].append(name)
for group_name, group_lines in group_map.items():
f.write("{}Functions = (\n".format(group_name))
for name in group_lines:
f.write(' (b"{}", {}),\n'.format(name.replace('Fn', 'vk'), name))
f.write(")\n\n")
def write_base_loader(f):
f.write('''
# Loading proc
GetInstanceProcAddr = FnGetInstanceProcAddr((b"vkGetInstanceProcAddr", vk))
# Load the loader functions in the module namespace
loc = locals()
for name, fnptr in load_functions(Instance(0), LoaderFunctions, GetInstanceProcAddr):
loc[name] = fnptr
del loc'''[1::])
with open("vk.py", 'w') as f:
f.write(BASE)
parse_handles_def(f)
f.write("\n\n")
parse_flags_def(f)
f.write("\n\n")
parse_enums(f)
f.write("\n\n")
parse_allocation_callback(f)
f.write("\n\n")
parse_structs(f)
f.write("\n\n")
parse_functions(f)
f.write("\n\n")
group_functions(f)
f.write("\n\n")
write_base_loader(f)