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 driver_gen.h and driver_gen.cpp.
18"""
19
20import os
21import generator_common as gencom
22
23# Extensions intercepted at vulkan::driver level.
24_INTERCEPTED_EXTENSIONS = [
25    'VK_ANDROID_native_buffer',
26    'VK_EXT_debug_report',
27    'VK_EXT_hdr_metadata',
28    'VK_EXT_swapchain_colorspace',
29    'VK_GOOGLE_display_timing',
30    'VK_KHR_android_surface',
31    'VK_KHR_get_surface_capabilities2',
32    'VK_KHR_incremental_present',
33    'VK_KHR_shared_presentable_image',
34    'VK_KHR_surface',
35    'VK_KHR_swapchain',
36]
37
38# Extensions known to vulkan::driver level.
39_KNOWN_EXTENSIONS = _INTERCEPTED_EXTENSIONS + [
40    'VK_ANDROID_external_memory_android_hardware_buffer',
41    'VK_KHR_bind_memory2',
42    'VK_KHR_get_physical_device_properties2',
43    'VK_KHR_device_group_creation',
44    'VK_KHR_external_memory_capabilities',
45    'VK_KHR_external_semaphore_capabilities',
46    'VK_KHR_external_fence_capabilities',
47]
48
49# Functions needed at vulkan::driver level.
50_NEEDED_COMMANDS = [
51    # Create functions of dispatchable objects
52    'vkCreateDevice',
53    'vkGetDeviceQueue',
54    'vkGetDeviceQueue2',
55    'vkAllocateCommandBuffers',
56
57    # Destroy functions of dispatchable objects
58    'vkDestroyInstance',
59    'vkDestroyDevice',
60
61    # Enumeration of extensions
62    'vkEnumerateDeviceExtensionProperties',
63
64    # We cache physical devices in loader.cpp
65    'vkEnumeratePhysicalDevices',
66    'vkEnumeratePhysicalDeviceGroups',
67
68    'vkGetInstanceProcAddr',
69    'vkGetDeviceProcAddr',
70
71    'vkQueueSubmit',
72
73    # VK_KHR_swapchain->VK_ANDROID_native_buffer translation
74    'vkCreateImage',
75    'vkDestroyImage',
76
77    'vkGetPhysicalDeviceProperties',
78
79    # VK_KHR_swapchain v69 requirement
80    'vkBindImageMemory2',
81    'vkBindImageMemory2KHR',
82
83    # For promoted VK_KHR_device_group_creation
84    'vkEnumeratePhysicalDeviceGroupsKHR',
85
86    # For promoted VK_KHR_get_physical_device_properties2
87    'vkGetPhysicalDeviceFeatures2',
88    'vkGetPhysicalDeviceFeatures2KHR',
89    'vkGetPhysicalDeviceProperties2',
90    'vkGetPhysicalDeviceProperties2KHR',
91    'vkGetPhysicalDeviceFormatProperties2',
92    'vkGetPhysicalDeviceFormatProperties2KHR',
93    'vkGetPhysicalDeviceImageFormatProperties2',
94    'vkGetPhysicalDeviceImageFormatProperties2KHR',
95    'vkGetPhysicalDeviceQueueFamilyProperties2',
96    'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
97    'vkGetPhysicalDeviceMemoryProperties2',
98    'vkGetPhysicalDeviceMemoryProperties2KHR',
99    'vkGetPhysicalDeviceSparseImageFormatProperties2',
100    'vkGetPhysicalDeviceSparseImageFormatProperties2KHR',
101
102    # For promoted VK_KHR_external_memory_capabilities
103    'vkGetPhysicalDeviceExternalBufferProperties',
104    'vkGetPhysicalDeviceExternalBufferPropertiesKHR',
105
106    # For promoted VK_KHR_external_semaphore_capabilities
107    'vkGetPhysicalDeviceExternalSemaphoreProperties',
108    'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR',
109
110    # For promoted VK_KHR_external_fence_capabilities
111    'vkGetPhysicalDeviceExternalFenceProperties',
112    'vkGetPhysicalDeviceExternalFencePropertiesKHR',
113]
114
115# Functions intercepted at vulkan::driver level.
116_INTERCEPTED_COMMANDS = [
117    # Create functions of dispatchable objects
118    'vkCreateInstance',
119    'vkCreateDevice',
120    'vkEnumeratePhysicalDevices',
121    'vkEnumeratePhysicalDeviceGroups',
122    'vkGetDeviceQueue',
123    'vkGetDeviceQueue2',
124    'vkAllocateCommandBuffers',
125
126    # Destroy functions of dispatchable objects
127    'vkDestroyInstance',
128    'vkDestroyDevice',
129
130    # Enumeration of extensions
131    'vkEnumerateInstanceExtensionProperties',
132    'vkEnumerateDeviceExtensionProperties',
133
134    'vkGetInstanceProcAddr',
135    'vkGetDeviceProcAddr',
136
137    'vkQueueSubmit',
138
139    # VK_KHR_swapchain v69 requirement
140    'vkBindImageMemory2',
141    'vkBindImageMemory2KHR',
142
143    # For promoted VK_KHR_get_physical_device_properties2
144    'vkGetPhysicalDeviceFeatures2',
145    'vkGetPhysicalDeviceProperties2',
146    'vkGetPhysicalDeviceFormatProperties2',
147    'vkGetPhysicalDeviceImageFormatProperties2',
148    'vkGetPhysicalDeviceQueueFamilyProperties2',
149    'vkGetPhysicalDeviceMemoryProperties2',
150    'vkGetPhysicalDeviceSparseImageFormatProperties2',
151
152    # For promoted VK_KHR_external_memory_capabilities
153    'vkGetPhysicalDeviceExternalBufferProperties',
154
155    # For promoted VK_KHR_external_semaphore_capabilities
156    'vkGetPhysicalDeviceExternalSemaphoreProperties',
157
158    # For promoted VK_KHR_external_fence_capabilities
159    'vkGetPhysicalDeviceExternalFenceProperties',
160]
161
162
163def _is_driver_table_entry(cmd):
164  """Returns true if a function is needed by vulkan::driver.
165
166  Args:
167    cmd: Vulkan function name.
168  """
169  if gencom.is_function_supported(cmd):
170    if cmd in _NEEDED_COMMANDS:
171      return True
172    if cmd in gencom.extension_dict:
173      if (gencom.extension_dict[cmd] == 'VK_ANDROID_native_buffer' or
174          gencom.extension_dict[cmd] == 'VK_EXT_debug_report'):
175        return True
176  return False
177
178
179def _is_instance_driver_table_entry(cmd):
180  """Returns true if a instance-dispatched function is needed by vulkan::driver.
181
182  Args:
183    cmd: Vulkan function name.
184  """
185  return (_is_driver_table_entry(cmd) and
186          gencom.is_instance_dispatched(cmd))
187
188
189def _is_device_driver_table_entry(cmd):
190  """Returns true if a device-dispatched function is needed by vulkan::driver.
191
192  Args:
193    cmd: Vulkan function name.
194  """
195  return (_is_driver_table_entry(cmd) and
196          gencom.is_device_dispatched(cmd))
197
198
199def gen_h():
200  """Generates the driver_gen.h file.
201  """
202  genfile = os.path.join(os.path.dirname(__file__),
203                         '..', 'libvulkan', 'driver_gen.h')
204
205  with open(genfile, 'w') as f:
206    f.write(gencom.copyright_and_warning(2016))
207
208    f.write("""\
209#ifndef LIBVULKAN_DRIVER_GEN_H
210#define LIBVULKAN_DRIVER_GEN_H
211
212#include <vulkan/vk_android_native_buffer.h>
213#include <vulkan/vulkan.h>
214
215#include <bitset>
216#include <optional>
217#include <vector>
218
219namespace vulkan {
220namespace driver {
221
222struct ProcHook {
223    enum Type {
224        GLOBAL,
225        INSTANCE,
226        DEVICE,
227    };
228    enum Extension {\n""")
229
230    for ext in _KNOWN_EXTENSIONS:
231      f.write(gencom.indent(2) + gencom.base_ext_name(ext) + ',\n')
232
233    f.write('\n')
234    for version in gencom.version_code_list:
235      f.write(gencom.indent(2) + 'EXTENSION_CORE_' + version + ',\n')
236
237    # EXTENSION_COUNT must be the next enum after the highest API version.
238    f.write("""\
239        EXTENSION_COUNT,
240        EXTENSION_UNKNOWN,
241    };
242
243    const char* name;
244    Type type;
245    Extension extension;
246
247    PFN_vkVoidFunction proc;
248    PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks
249};
250
251struct InstanceDriverTable {
252    // clang-format off\n""")
253
254    for cmd in gencom.command_list:
255      if _is_instance_driver_table_entry(cmd):
256        f.write(gencom.indent(1) + 'PFN_' + cmd + ' ' +
257                gencom.base_name(cmd) + ';\n')
258
259    f.write("""\
260    // clang-format on
261};
262
263struct DeviceDriverTable {
264    // clang-format off\n""")
265
266    for cmd in gencom.command_list:
267      if _is_device_driver_table_entry(cmd):
268        f.write(gencom.indent(1) + 'PFN_' + cmd + ' ' +
269                gencom.base_name(cmd) + ';\n')
270
271    f.write("""\
272    // clang-format on
273};
274
275const ProcHook* GetProcHook(const char* name);
276ProcHook::Extension GetProcHookExtension(const char* name);
277
278bool InitDriverTable(VkInstance instance,
279                     PFN_vkGetInstanceProcAddr get_proc,
280                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
281bool InitDriverTable(VkDevice dev,
282                     PFN_vkGetDeviceProcAddr get_proc,
283                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
284
285std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
286uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
287                                         uint32_t end_version);
288std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
289                                                       uint32_t end_version);
290
291}  // namespace driver
292}  // namespace vulkan
293
294#endif  // LIBVULKAN_DRIVER_TABLE_H\n""")
295
296    f.close()
297  gencom.run_clang_format(genfile)
298
299
300def _is_intercepted(cmd):
301  """Returns true if a function is intercepted by vulkan::driver.
302
303  Args:
304    cmd: Vulkan function name.
305  """
306  if gencom.is_function_supported(cmd):
307    if cmd in _INTERCEPTED_COMMANDS:
308      return True
309
310    if cmd in gencom.extension_dict:
311      return gencom.extension_dict[cmd] in _INTERCEPTED_EXTENSIONS
312  return False
313
314
315def _get_proc_hook_enum(cmd):
316  """Returns the ProcHook enumeration for the corresponding core function.
317
318  Args:
319    cmd: Vulkan function name.
320  """
321  assert cmd in gencom.version_dict
322  for version in gencom.version_code_list:
323    if gencom.version_dict[cmd] == 'VK_VERSION_' + version:
324      return 'ProcHook::EXTENSION_CORE_' + version
325
326
327def _need_proc_hook_stub(cmd):
328  """Returns true if a function needs a ProcHook stub.
329
330  Args:
331    cmd: Vulkan function name.
332  """
333  if _is_intercepted(cmd) and gencom.is_device_dispatched(cmd):
334    if cmd in gencom.extension_dict:
335      if not gencom.is_extension_internal(gencom.extension_dict[cmd]):
336        return True
337    elif gencom.version_dict[cmd] != 'VK_VERSION_1_0':
338      return True
339  return False
340
341
342def _define_proc_hook_stub(cmd, f):
343  """Emits a stub for ProcHook::checked_proc.
344
345  Args:
346    cmd: Vulkan function name.
347    f: Output file handle.
348  """
349  if _need_proc_hook_stub(cmd):
350    return_type = gencom.return_type_dict[cmd]
351
352    ext_name = ''
353    ext_hook = ''
354    if cmd in gencom.extension_dict:
355      ext_name = gencom.extension_dict[cmd]
356      ext_hook = 'ProcHook::' + gencom.base_ext_name(ext_name)
357    else:
358      ext_name = gencom.version_dict[cmd]
359      ext_hook = _get_proc_hook_enum(cmd)
360
361    handle = gencom.param_dict[cmd][0][1]
362    param_types = ', '.join([''.join(i) for i in gencom.param_dict[cmd]])
363    param_names = ', '.join([''.join(i[1]) for i in gencom.param_dict[cmd]])
364
365    f.write('VKAPI_ATTR ' + return_type + ' checked' + gencom.base_name(cmd) +
366            '(' + param_types + ') {\n')
367    f.write(gencom.indent(1) + 'if (GetData(' + handle + ').hook_extensions[' +
368            ext_hook + ']) {\n')
369
370    f.write(gencom.indent(2))
371    if gencom.return_type_dict[cmd] != 'void':
372      f.write('return ')
373    f.write(gencom.base_name(cmd) + '(' + param_names + ');\n')
374
375    f.write(gencom.indent(1) + '} else {\n')
376    f.write(gencom.indent(2) + 'Logger(' + handle + ').Err(' + handle + ', \"' +
377            ext_name + ' not enabled. ' + cmd + ' not executed.\");\n')
378    if gencom.return_type_dict[cmd] != 'void':
379      f.write(gencom.indent(2) + 'return VK_SUCCESS;\n')
380    f.write(gencom.indent(1) + '}\n}\n\n')
381
382
383def _define_global_proc_hook(cmd, f):
384  """Emits definition of a global ProcHook.
385
386  Args:
387    cmd: Vulkan function name.
388    f: Output file handle.
389  """
390  assert cmd not in gencom.extension_dict
391
392  f.write(gencom.indent(1) + '{\n')
393  f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
394  f.write(gencom.indent(2) + 'ProcHook::GLOBAL,\n')
395  f.write(gencom.indent(2) + _get_proc_hook_enum(cmd) + ',\n')
396  f.write(gencom.indent(2) + 'reinterpret_cast<PFN_vkVoidFunction>(' +
397          gencom.base_name(cmd) + '),\n')
398  f.write(gencom.indent(2) + 'nullptr,\n')
399  f.write(gencom.indent(1) + '},\n')
400
401
402def _define_instance_proc_hook(cmd, f):
403  """Emits definition of a instance ProcHook.
404
405  Args:
406    cmd: Vulkan function name.
407    f: Output file handle.
408  """
409  f.write(gencom.indent(1) + '{\n')
410  f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
411  f.write(gencom.indent(2) + 'ProcHook::INSTANCE,\n')
412
413  if cmd in gencom.extension_dict:
414    ext_name = gencom.extension_dict[cmd]
415    f.write(gencom.indent(2) + 'ProcHook::' +
416            gencom.base_ext_name(ext_name) + ',\n')
417
418    if gencom.is_extension_internal(ext_name):
419      f.write("""\
420        nullptr,
421        nullptr,\n""")
422    else:
423      f.write("""\
424        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
425        nullptr,\n""")
426  else:
427    f.write(gencom.indent(2) + _get_proc_hook_enum(cmd) + ',\n')
428    f.write("""\
429        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
430        nullptr,\n""")
431
432  f.write(gencom.indent(1) + '},\n')
433
434
435def _define_device_proc_hook(cmd, f):
436  """Emits definition of a device ProcHook.
437
438  Args:
439    cmd: Vulkan function name.
440    f: Output file handle.
441  """
442  f.write(gencom.indent(1) + '{\n')
443  f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
444  f.write(gencom.indent(2) + 'ProcHook::DEVICE,\n')
445
446  if (cmd in gencom.extension_dict or
447      gencom.version_dict[cmd] != 'VK_VERSION_1_0'):
448    ext_name = ''
449    ext_hook = ''
450    if cmd in gencom.extension_dict:
451      ext_name = gencom.extension_dict[cmd]
452      ext_hook = 'ProcHook::' + gencom.base_ext_name(ext_name)
453    else:
454      ext_name = gencom.version_dict[cmd]
455      ext_hook = _get_proc_hook_enum(cmd)
456    f.write(gencom.indent(2) + ext_hook + ',\n')
457
458    if gencom.is_extension_internal(ext_name):
459      f.write("""\
460        nullptr,
461        nullptr,\n""")
462    else:
463      f.write("""\
464        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
465        reinterpret_cast<PFN_vkVoidFunction>(checked""" +
466              gencom.base_name(cmd) + '),\n')
467
468  else:
469    f.write(gencom.indent(2) + _get_proc_hook_enum(cmd) + ',\n')
470    f.write("""\
471        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
472        nullptr,\n""")
473
474  f.write(gencom.indent(1) + '},\n')
475
476
477def gen_cpp():
478  """Generates the driver_gen.cpp file.
479  """
480  genfile = os.path.join(os.path.dirname(__file__),
481                         '..', 'libvulkan', 'driver_gen.cpp')
482
483  with open(genfile, 'w') as f:
484    f.write(gencom.copyright_and_warning(2016))
485    f.write("""\
486#include <log/log.h>
487#include <string.h>
488
489#include <algorithm>
490
491#include "driver.h"
492
493namespace vulkan {
494namespace driver {
495
496namespace {
497
498// clang-format off\n\n""")
499
500    for cmd in gencom.command_list:
501      _define_proc_hook_stub(cmd, f)
502
503    f.write("""\
504// clang-format on
505
506const ProcHook g_proc_hooks[] = {
507    // clang-format off\n""")
508
509    sorted_command_list = sorted(gencom.command_list)
510    for cmd in sorted_command_list:
511      if _is_intercepted(cmd):
512        if gencom.is_globally_dispatched(cmd):
513          _define_global_proc_hook(cmd, f)
514        elif gencom.is_instance_dispatched(cmd):
515          _define_instance_proc_hook(cmd, f)
516        elif gencom.is_device_dispatched(cmd):
517          _define_device_proc_hook(cmd, f)
518
519    f.write("""\
520    // clang-format on
521};
522
523}  // namespace
524
525const ProcHook* GetProcHook(const char* name) {
526    auto begin = std::cbegin(g_proc_hooks);
527    auto end = std::cend(g_proc_hooks);
528    auto hook = std::lower_bound(
529        begin, end, name,
530        [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
531    return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
532}
533
534ProcHook::Extension GetProcHookExtension(const char* name) {
535    // clang-format off\n""")
536
537    for ext in _KNOWN_EXTENSIONS:
538      f.write(gencom.indent(1) + 'if (strcmp(name, \"' + ext +
539              '\") == 0) return ProcHook::' + gencom.base_ext_name(ext) + ';\n')
540
541    f.write("""\
542    // clang-format on
543    return ProcHook::EXTENSION_UNKNOWN;
544}
545
546#define UNLIKELY(expr) __builtin_expect((expr), 0)
547
548#define INIT_PROC(required, obj, proc)                                 \\
549    do {                                                               \\
550        data.driver.proc =                                             \\
551            reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\
552        if (UNLIKELY(required && !data.driver.proc)) {                 \\
553            ALOGE("missing " #obj " proc: vk" #proc);                  \\
554            success = false;                                           \\
555        }                                                              \\
556    } while (0)
557
558#define INIT_PROC_EXT(ext, required, obj, proc) \\
559    do {                                        \\
560        if (extensions[ProcHook::ext])          \\
561            INIT_PROC(required, obj, proc);     \\
562    } while (0)
563
564bool InitDriverTable(VkInstance instance,
565                     PFN_vkGetInstanceProcAddr get_proc,
566                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
567    auto& data = GetData(instance);
568    bool success = true;
569
570    // clang-format off\n""")
571
572    for cmd in gencom.command_list:
573      if _is_instance_driver_table_entry(cmd):
574        gencom.init_proc(cmd, f)
575
576    f.write("""\
577    // clang-format on
578
579    return success;
580}
581
582bool InitDriverTable(VkDevice dev,
583                     PFN_vkGetDeviceProcAddr get_proc,
584                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
585    auto& data = GetData(dev);
586    bool success = true;
587
588    // clang-format off\n""")
589
590    for cmd in gencom.command_list:
591      if _is_device_driver_table_entry(cmd):
592        gencom.init_proc(cmd, f)
593
594    f.write("""\
595    // clang-format on
596
597    return success;
598}
599
600const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
601    // clang-format off\n""")
602
603    for key, value in sorted(gencom.promoted_inst_ext_dict.items()):
604      f.write(gencom.indent(1) + 'std::make_pair("' + key + '", ' + value + '),\n')
605
606    f.write("""\
607    // clang-format on
608};
609
610std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
611    auto begin = std::cbegin(g_promoted_instance_extensions);
612    auto end = std::cend(g_promoted_instance_extensions);
613    auto iter =
614        std::lower_bound(begin, end, name,
615                         [](const std::pair<const char*, uint32_t>& e,
616                            const char* n) { return strcmp(e.first, n) < 0; });
617    return (iter < end && strcmp(iter->first, name) == 0)
618               ? std::optional<uint32_t>(iter->second)
619               : std::nullopt;
620}
621
622uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
623                                         uint32_t end_version) {
624    auto begin = std::cbegin(g_promoted_instance_extensions);
625    auto end = std::cend(g_promoted_instance_extensions);
626    uint32_t count = 0;
627
628    for (auto iter = begin; iter != end; iter++)
629        if (iter->second > begin_version && iter->second <= end_version)
630            count++;
631
632    return count;
633}
634
635std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
636                                                       uint32_t end_version) {
637    auto begin = std::cbegin(g_promoted_instance_extensions);
638    auto end = std::cend(g_promoted_instance_extensions);
639    std::vector<const char*> extensions;
640
641    for (auto iter = begin; iter != end; iter++)
642        if (iter->second > begin_version && iter->second <= end_version)
643            extensions.emplace_back(iter->first);
644
645    return extensions;
646}
647
648}  // namespace driver
649}  // namespace vulkan\n""")
650
651    f.close()
652  gencom.run_clang_format(genfile)
653