xref: /aosp_15_r20/frameworks/native/vulkan/scripts/api_generator.py (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1#!/usr/bin/env python3
2#
3# Copyright 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Generates the api_gen.h and api_gen.cpp.
18"""
19
20import os
21import generator_common as gencom
22
23# Functions intercepted at vulkan::api level.
24_INTERCEPTED_COMMANDS = [
25    'vkCreateDevice',
26    'vkDestroyDevice',
27    'vkDestroyInstance',
28    'vkEnumerateDeviceExtensionProperties',
29    'vkEnumerateDeviceLayerProperties',
30]
31
32
33def gen_h():
34  """Generates the api_gen.h file.
35  """
36  genfile = os.path.join(os.path.dirname(__file__),
37                         '..', 'libvulkan', 'api_gen.h')
38
39  with open(genfile, 'w') as f:
40    instance_dispatch_table_entries = []
41    device_dispatch_table_entries = []
42
43    for cmd in gencom.command_list:
44      if cmd not in gencom.alias_dict:
45        if gencom.is_instance_dispatch_table_entry(cmd):
46          instance_dispatch_table_entries.append(
47              'PFN_' + cmd + ' ' + gencom.base_name(cmd) + ';')
48        elif gencom.is_device_dispatch_table_entry(cmd):
49          device_dispatch_table_entries.append(
50              'PFN_' + cmd + ' ' + gencom.base_name(cmd) + ';')
51
52    f.write(gencom.copyright_and_warning(2016))
53
54    f.write("""\
55#ifndef LIBVULKAN_API_GEN_H
56#define LIBVULKAN_API_GEN_H
57
58#include <vulkan/vulkan.h>
59
60#include <bitset>
61
62#include "driver_gen.h"
63
64/*
65 * This file is autogenerated by api_generator.py. Do not edit directly.
66 */
67namespace vulkan {
68namespace api {
69
70struct InstanceDispatchTable {
71    // clang-format off\n""")
72
73    for entry in instance_dispatch_table_entries:
74      f.write(gencom.indent(1) + entry + '\n')
75
76    f.write("""\
77    // clang-format on
78};
79
80struct DeviceDispatchTable {
81    // clang-format off\n""")
82
83    for entry in device_dispatch_table_entries:
84      f.write(gencom.indent(1) + entry + '\n')
85
86    f.write("""\
87    // clang-format on
88};
89
90bool InitDispatchTable(
91    VkInstance instance,
92    PFN_vkGetInstanceProcAddr get_proc,
93    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
94bool InitDispatchTable(
95    VkDevice dev,
96    PFN_vkGetDeviceProcAddr get_proc,
97    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
98
99}  // namespace api
100}  // namespace vulkan
101
102#endif  // LIBVULKAN_API_GEN_H\n""")
103
104    f.close()
105  gencom.run_clang_format(genfile)
106
107
108def _define_extension_stub(cmd, f):
109  """Emits a stub for an exported extension function.
110
111  Args:
112    cmd: Vulkan function name.
113    f: Output file handle.
114  """
115  if (cmd in gencom.extension_dict and gencom.is_function_exported(cmd)):
116    ext_name = gencom.extension_dict[cmd]
117    ret = gencom.return_type_dict[cmd]
118    params = gencom.param_dict[cmd]
119    first_param = params[0][0] + params[0][1]
120    tail_params = ', '.join([i[0][:-1] for i in params[1:]])
121
122    f.write('VKAPI_ATTR ' + ret + ' disabled' + gencom.base_name(cmd) +
123            '(' + first_param + ', ' + tail_params + ') {\n')
124
125    f.write(gencom.indent(1) + 'driver::Logger(' + params[0][1] +
126            ').Err(' + params[0][1] + ', \"' + ext_name +
127            ' not enabled. Exported ' + cmd + ' not executed.\");\n')
128
129    if gencom.return_type_dict[cmd] != 'void':
130      f.write(gencom.indent(1) + 'return VK_SUCCESS;\n')
131
132    f.write('}\n\n')
133
134
135def _is_intercepted(cmd):
136  """Returns true if a function is intercepted by vulkan::api.
137
138  Args:
139    cmd: Vulkan function name.
140  """
141  if gencom.is_function_supported(cmd):
142    if gencom.is_globally_dispatched(cmd) or cmd in _INTERCEPTED_COMMANDS:
143      return True
144  return False
145
146
147def _intercept_instance_proc_addr(f):
148  """Emits code for vkGetInstanceProcAddr for function interception.
149
150  Args:
151    f: Output file handle.
152  """
153  f.write("""\
154    // global functions
155    if (instance == VK_NULL_HANDLE) {\n""")
156
157  for cmd in gencom.command_list:
158    # vkGetInstanceProcAddr(nullptr, "vkGetInstanceProcAddr") is effectively
159    # globally dispatched
160    if gencom.is_globally_dispatched(cmd) or cmd == 'vkGetInstanceProcAddr':
161      f.write(gencom.indent(2) +
162              'if (strcmp(pName, \"' + cmd +
163              '\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +
164              gencom.base_name(cmd) + ');\n')
165
166  f.write("""
167        ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \\\"%s\\\") call", pName);
168        return nullptr;
169    }
170
171    static const struct Hook {
172        const char* name;
173        PFN_vkVoidFunction proc;
174    } hooks[] = {\n""")
175
176  sorted_command_list = sorted(gencom.command_list)
177  for cmd in sorted_command_list:
178    if gencom.is_function_exported(cmd):
179      if gencom.is_globally_dispatched(cmd):
180        f.write(gencom.indent(2) + '{ \"' + cmd + '\", nullptr },\n')
181      elif (_is_intercepted(cmd) or
182            cmd == 'vkGetInstanceProcAddr' or
183            gencom.is_device_dispatched(cmd)):
184        f.write(gencom.indent(2) + '{ \"' + cmd +
185                '\", reinterpret_cast<PFN_vkVoidFunction>(' +
186                gencom.base_name(cmd) + ') },\n')
187
188  f.write("""\
189    };
190    // clang-format on
191    constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
192    auto hook = std::lower_bound(
193        hooks, hooks + count, pName,
194        [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
195    if (hook < hooks + count && strcmp(hook->name, pName) == 0) {
196        if (!hook->proc) {
197            vulkan::driver::Logger(instance).Err(
198                instance, "invalid vkGetInstanceProcAddr(%p, \\\"%s\\\") call",
199                instance, pName);
200        }
201        return hook->proc;
202    }
203    // clang-format off\n\n""")
204
205
206def _intercept_device_proc_addr(f):
207  """Emits code for vkGetDeviceProcAddr for function interception.
208
209  Args:
210    f: Output file handle.
211  """
212  f.write("""\
213    if (device == VK_NULL_HANDLE) {
214        ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call");
215        return nullptr;
216    }
217
218    static const char* const known_non_device_names[] = {\n""")
219
220  sorted_command_list = sorted(gencom.command_list)
221  for cmd in sorted_command_list:
222    if gencom.is_function_supported(cmd):
223      if not gencom.is_device_dispatched(cmd):
224        f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
225
226  f.write("""\
227    };
228    // clang-format on
229    constexpr size_t count =
230        sizeof(known_non_device_names) / sizeof(known_non_device_names[0]);
231    if (!pName ||
232        std::binary_search(
233            known_non_device_names, known_non_device_names + count, pName,
234            [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
235        vulkan::driver::Logger(device).Err(
236            device, "invalid vkGetDeviceProcAddr(%p, \\\"%s\\\") call", device,
237            (pName) ? pName : "(null)");
238        return nullptr;
239    }
240    // clang-format off\n\n""")
241
242  for cmd in gencom.command_list:
243    if gencom.is_device_dispatched(cmd):
244      if _is_intercepted(cmd) or cmd == 'vkGetDeviceProcAddr':
245        f.write(gencom.indent(1) + 'if (strcmp(pName, "' + cmd +
246                '") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +
247                gencom.base_name(cmd) + ');\n')
248  f.write('\n')
249
250
251def _api_dispatch(cmd, f):
252  """Emits code to dispatch a function.
253
254  Args:
255    cmd: Vulkan function name.
256    f: Output file handle.
257  """
258  assert not _is_intercepted(cmd)
259
260  f.write(gencom.indent(1))
261  if gencom.return_type_dict[cmd] != 'void':
262    f.write('return ')
263
264  param_list = gencom.param_dict[cmd]
265  handle = param_list[0][1]
266  f.write('GetData(' + handle + ').dispatch.' + gencom.base_name(cmd) +
267          '(' + ', '.join(i[1] for i in param_list) + ');\n')
268
269
270def gen_cpp():
271  """Generates the api_gen.cpp file.
272  """
273  genfile = os.path.join(os.path.dirname(__file__),
274                         '..', 'libvulkan', 'api_gen.cpp')
275
276  with open(genfile, 'w') as f:
277    f.write(gencom.copyright_and_warning(2016))
278
279    f.write("""\
280#include <log/log.h>
281#include <string.h>
282
283#include <algorithm>
284
285// to catch mismatches between vulkan.h and this file
286#undef VK_NO_PROTOTYPES
287#include "api.h"
288
289/*
290 * This file is autogenerated by api_generator.py. Do not edit directly.
291 */
292namespace vulkan {
293namespace api {
294
295#define UNLIKELY(expr) __builtin_expect((expr), 0)
296
297#define INIT_PROC(required, obj, proc)                                 \\
298    do {                                                               \\
299        data.dispatch.proc =                                           \\
300            reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\
301        if (UNLIKELY(required && !data.dispatch.proc)) {               \\
302            ALOGE("missing " #obj " proc: vk" #proc);                  \\
303            success = false;                                           \\
304        }                                                              \\
305    } while (0)
306
307// Exported extension functions may be invoked even when their extensions
308// are disabled.  Dispatch to stubs when that happens.
309#define INIT_PROC_EXT(ext, required, obj, proc)  \\
310    do {                                         \\
311        if (extensions[driver::ProcHook::ext])   \\
312            INIT_PROC(required, obj, proc);      \\
313        else                                     \\
314            data.dispatch.proc = disabled##proc; \\
315    } while (0)
316
317namespace {
318
319// clang-format off\n\n""")
320
321    for cmd in gencom.command_list:
322      _define_extension_stub(cmd, f)
323
324    f.write("""\
325// clang-format on
326
327}  // namespace
328
329bool InitDispatchTable(
330    VkInstance instance,
331    PFN_vkGetInstanceProcAddr get_proc,
332    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
333    auto& data = GetData(instance);
334    bool success = true;
335
336    // clang-format off\n""")
337
338    for cmd in gencom.command_list:
339      if gencom.is_instance_dispatch_table_entry(cmd):
340        gencom.init_proc(cmd, f)
341
342    f.write("""\
343    // clang-format on
344
345    return success;
346}
347
348bool InitDispatchTable(
349    VkDevice dev,
350    PFN_vkGetDeviceProcAddr get_proc,
351    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
352    auto& data = GetData(dev);
353    bool success = true;
354
355    // clang-format off\n""")
356
357    for cmd in gencom.command_list:
358      if gencom.is_device_dispatch_table_entry(cmd):
359        gencom.init_proc(cmd, f)
360
361    f.write("""\
362    // clang-format on
363
364    return success;
365}
366
367// clang-format off
368
369namespace {
370
371// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr
372""")
373
374    for cmd in gencom.command_list:
375      if gencom.is_function_exported(cmd) and not _is_intercepted(cmd):
376        param_list = [''.join(i) for i in gencom.param_dict[cmd]]
377        f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' +
378                gencom.base_name(cmd) + '(' + ', '.join(param_list) + ');\n')
379
380    f.write('\n')
381    for cmd in gencom.command_list:
382      if gencom.is_function_exported(cmd) and not _is_intercepted(cmd):
383        param_list = [''.join(i) for i in gencom.param_dict[cmd]]
384        f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' +
385                gencom.base_name(cmd) + '(' + ', '.join(param_list) + ') {\n')
386        if cmd == 'vkGetInstanceProcAddr':
387          _intercept_instance_proc_addr(f)
388        elif cmd == 'vkGetDeviceProcAddr':
389          _intercept_device_proc_addr(f)
390        _api_dispatch(cmd, f)
391        f.write('}\n\n')
392
393    f.write("""
394}  // anonymous namespace
395
396// clang-format on
397
398}  // namespace api
399}  // namespace vulkan
400
401// clang-format off\n\n""")
402
403    for cmd in gencom.command_list:
404      if gencom.is_function_exported(cmd):
405        param_list = [''.join(i) for i in gencom.param_dict[cmd]]
406        f.write('__attribute__((visibility("default")))\n')
407        f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' +
408                cmd + '(' + ', '.join(param_list) + ') {\n')
409        f.write(gencom.indent(1))
410        if gencom.return_type_dict[cmd] != 'void':
411          f.write('return ')
412        param_list = gencom.param_dict[cmd]
413        f.write('vulkan::api::' + gencom.base_name(cmd) +
414                '(' + ', '.join(i[1] for i in param_list) + ');\n}\n\n')
415
416    f.write('// clang-format on\n')
417    f.close()
418  gencom.run_clang_format(genfile)
419