| #!/usr/bin/env python3 | 
 | # | 
 | # Copyright 2019 The Android Open Source Project | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 | # | 
 | # This script provides the functions for generating the | 
 | # vulkan driver framework directly from the vulkan registry (vk.xml). | 
 |  | 
 | import generator_common as gencom | 
 | import os | 
 |  | 
 | interceptedExtensions = [ | 
 |   'VK_ANDROID_native_buffer', | 
 |   'VK_EXT_debug_report', | 
 |   'VK_EXT_hdr_metadata', | 
 |   'VK_EXT_swapchain_colorspace', | 
 |   'VK_GOOGLE_display_timing', | 
 |   'VK_KHR_android_surface', | 
 |   'VK_KHR_incremental_present', | 
 |   'VK_KHR_shared_presentable_image', | 
 |   'VK_KHR_surface', | 
 |   'VK_KHR_swapchain', | 
 |   'VK_KHR_get_surface_capabilities2' | 
 | ] | 
 |  | 
 | knownExtensions = interceptedExtensions + [ | 
 |   'VK_KHR_get_physical_device_properties2', | 
 |   'VK_ANDROID_external_memory_android_hardware_buffer', | 
 |   'VK_KHR_bind_memory2' | 
 | ] | 
 |  | 
 | def defineProcHookType(f): | 
 |   f.write ("""struct ProcHook { | 
 |     enum Type { | 
 |         GLOBAL, | 
 |         INSTANCE, | 
 |         DEVICE, | 
 |     }; | 
 |     enum Extension {\n""") | 
 |   for exts in knownExtensions: | 
 |     f.write (gencom.clang_off_spaces*2 + exts[3:] + ',\n') | 
 |   f.write ('\n') | 
 |   f.write (gencom.clang_off_spaces*2 + """EXTENSION_CORE,  // valid bit | 
 |         EXTENSION_COUNT, | 
 |         EXTENSION_UNKNOWN, | 
 |     }; | 
 |  | 
 |     const char* name; | 
 |     Type type; | 
 |     Extension extension; | 
 |  | 
 |     PFN_vkVoidFunction proc; | 
 |     PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks | 
 | };\n\n""") | 
 |  | 
 | def isExtensionIntercepted(extensionName): | 
 |   if extensionName in interceptedExtensions: | 
 |     return True | 
 |   return False | 
 |  | 
 | def isDriverTableEntry(functionName): | 
 |   switchCase = { | 
 |     # Create functions of dispatchable objects | 
 |     'vkCreateDevice' : True, | 
 |     'vkGetDeviceQueue' : True, | 
 |     'vkGetDeviceQueue2' : True, | 
 |     'vkAllocateCommandBuffers' : True, | 
 |  | 
 |     # Destroy functions of dispatchable objects | 
 |     'vkDestroyInstance' : True, | 
 |     'vkDestroyDevice' : True, | 
 |  | 
 |     # Enumeration of extensions | 
 |     'vkEnumerateDeviceExtensionProperties' : True, | 
 |  | 
 |     # We cache physical devices in loader.cpp | 
 |     'vkEnumeratePhysicalDevices' : True, | 
 |     'vkEnumeratePhysicalDeviceGroups' : True, | 
 |  | 
 |     'vkGetInstanceProcAddr' : True, | 
 |     'vkGetDeviceProcAddr' : True, | 
 |  | 
 |     # VK_KHR_swapchain->VK_ANDROID_native_buffer translation | 
 |     'vkCreateImage' : True, | 
 |     'vkDestroyImage' : True, | 
 |  | 
 |     'vkGetPhysicalDeviceProperties' : True, | 
 |     'vkGetPhysicalDeviceProperties2' : True, | 
 |     'vkGetPhysicalDeviceProperties2KHR' : True, | 
 |  | 
 |     # VK_KHR_swapchain v69 requirement | 
 |     'vkBindImageMemory2' : True, | 
 |     'vkBindImageMemory2KHR' : True | 
 |   } | 
 |   if gencom.isFunctionSupported(functionName): | 
 |     if functionName in switchCase: | 
 |       return True | 
 |     if functionName in gencom.extensionsDict: | 
 |       if gencom.extensionsDict[functionName] == 'VK_ANDROID_native_buffer' or gencom.extensionsDict[functionName] == 'VK_EXT_debug_report': | 
 |         return True | 
 |   return False | 
 |  | 
 | def isInstanceDriverTableEntry(functionName): | 
 |   if isDriverTableEntry(functionName) and gencom.isInstanceDispatched(functionName): | 
 |     return True | 
 |   return False | 
 |  | 
 | def isDeviceDriverTableEntry(functionName): | 
 |   if isDriverTableEntry(functionName) and gencom.isDeviceDispatched(functionName): | 
 |     return True | 
 |   return False | 
 |  | 
 | def driver_genh(): | 
 |   header = """#ifndef LIBVULKAN_DRIVER_GEN_H | 
 | #define LIBVULKAN_DRIVER_GEN_H | 
 |  | 
 | #include <vulkan/vk_android_native_buffer.h> | 
 | #include <vulkan/vulkan.h> | 
 |  | 
 | #include <bitset> | 
 |  | 
 | namespace vulkan { | 
 | namespace driver {\n\n""" | 
 |   genfile = os.path.join(os.path.dirname(__file__),'..','libvulkan','driver_gen.h') | 
 |   with open(genfile, 'w') as f: | 
 |     f.write (gencom.copyright) | 
 |     f.write (gencom.warning) | 
 |     f.write (header) | 
 |     defineProcHookType(f) | 
 |     f.write ('struct InstanceDriverTable {\n') | 
 |     gencom.clang_off(f, 1) | 
 |     for cmds in gencom.allCommandsList: | 
 |       if isInstanceDriverTableEntry(cmds): | 
 |         f.write (gencom.clang_off_spaces + 'PFN_' + cmds + ' ' + cmds[2:] + ';\n') | 
 |     gencom.clang_on(f, 1) | 
 |     f.write ('};\n\n') | 
 |     f.write ('struct DeviceDriverTable {\n') | 
 |     gencom.clang_off(f,1) | 
 |     for cmds in gencom.allCommandsList: | 
 |       if isDeviceDriverTableEntry(cmds): | 
 |         f.write (gencom.clang_off_spaces + 'PFN_' + cmds + ' ' + cmds[2:] + ';\n') | 
 |     gencom.clang_on(f,1) | 
 |     f.write ('};\n\n') | 
 |     f.write ("""const ProcHook* GetProcHook(const char* name); | 
 | ProcHook::Extension GetProcHookExtension(const char* name); | 
 |  | 
 | bool InitDriverTable(VkInstance instance, | 
 |                      PFN_vkGetInstanceProcAddr get_proc, | 
 |                      const std::bitset<ProcHook::EXTENSION_COUNT>& extensions); | 
 | bool InitDriverTable(VkDevice dev, | 
 |                      PFN_vkGetDeviceProcAddr get_proc, | 
 |                      const std::bitset<ProcHook::EXTENSION_COUNT>& extensions); | 
 |  | 
 | }  // namespace driver | 
 | }  // namespace vulkan | 
 |  | 
 | #endif  // LIBVULKAN_DRIVER_TABLE_H\n""") | 
 |     f.close() | 
 |   gencom.runClangFormat(genfile) | 
 |  | 
 | def isIntercepted(functionName): | 
 |   switchCase = { | 
 |     # Create functions of dispatchable objects | 
 |     'vkCreateInstance' : True, | 
 |     'vkCreateDevice' : True, | 
 |     'vkEnumeratePhysicalDevices' : True, | 
 |     'vkEnumeratePhysicalDeviceGroups' : True, | 
 |     'vkGetDeviceQueue' : True, | 
 |     'vkGetDeviceQueue2' : True, | 
 |     'vkAllocateCommandBuffers' : True, | 
 |  | 
 |     # Destroy functions of dispatchable objects | 
 |     'vkDestroyInstance' : True, | 
 |     'vkDestroyDevice' : True, | 
 |  | 
 |     # Enumeration of extensions | 
 |     'vkEnumerateInstanceExtensionProperties' : True, | 
 |     'vkEnumerateDeviceExtensionProperties' : True, | 
 |  | 
 |     'vkGetInstanceProcAddr' : True, | 
 |     'vkGetDeviceProcAddr' : True, | 
 |  | 
 |     # VK_KHR_swapchain v69 requirement | 
 |     'vkBindImageMemory2' : True, | 
 |     'vkBindImageMemory2KHR' : True | 
 |   } | 
 |   if gencom.isFunctionSupported(functionName): | 
 |     if functionName in switchCase: | 
 |       return switchCase[functionName] | 
 |  | 
 |     if functionName in gencom.extensionsDict: | 
 |       return isExtensionIntercepted(gencom.extensionsDict[functionName]) | 
 |   return False | 
 |  | 
 | def needProcHookStub(functionName): | 
 |   if isIntercepted(functionName) and gencom.isDeviceDispatched(functionName): | 
 |     if functionName in gencom.extensionsDict: | 
 |       if not gencom.isExtensionInternal(gencom.extensionsDict[functionName]): | 
 |         return True | 
 |   return False | 
 |  | 
 | def defineInitProc(name, f): | 
 |   f.write ('#define UNLIKELY(expr) __builtin_expect((expr), 0)\n') | 
 |   f.write ('\n') | 
 |   f.write ("""#define INIT_PROC(required, obj, proc)                                 \\ | 
 |     do {                                                               \\ | 
 |         data.""" + name + """.proc =                                             \\ | 
 |             reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\ | 
 |         if (UNLIKELY(required && !data.""" + name + """.proc)) {                 \\ | 
 |             ALOGE("missing " #obj " proc: vk" #proc);                  \\ | 
 |             success = false;                                           \\ | 
 |         }                                                              \\ | 
 |     } while (0)\n\n""") | 
 |  | 
 | def defineInitProcExt(f): | 
 |   f.write ("""#define INIT_PROC_EXT(ext, required, obj, proc) \\ | 
 |     do {                                        \\ | 
 |         if (extensions[ProcHook::ext])          \\ | 
 |             INIT_PROC(required, obj, proc);     \\ | 
 |     } while (0)\n\n""") | 
 |  | 
 | def defineProcHookStub(functionName, f): | 
 |   if needProcHookStub(functionName): | 
 |     ext_name = gencom.extensionsDict[functionName] | 
 |     base_name = functionName[2:] | 
 |     paramList = [''.join(i) for i in gencom.paramDict[functionName]] | 
 |     p0 = gencom.paramDict[functionName][0][1] | 
 |     f.write ('VKAPI_ATTR ' + gencom.returnTypeDict[functionName] + ' checked' + base_name + '(' + ', '.join(paramList) + ') {\n') | 
 |     ext_hook = 'ProcHook::' + ext_name[3:] | 
 |  | 
 |     f.write (gencom.clang_off_spaces + 'if (GetData(' + p0 + ').hook_extensions[' + ext_hook + ']) {\n') | 
 |     f.write (gencom.clang_off_spaces *2) | 
 |     if gencom.returnTypeDict[functionName] != 'void': | 
 |       f.write ('return ') | 
 |     paramNames = [''.join(i[1]) for i in gencom.paramDict[functionName]] | 
 |     f.write (base_name + '(' + ', '.join(paramNames) + ');\n') | 
 |     f.write (gencom.clang_off_spaces + '} else {\n') | 
 |     f.write (gencom.clang_off_spaces*2 + 'Logger(' + p0 + ').Err(' + p0 + ', \"' + ext_name + ' not enabled. ' + functionName + ' not executed.\");\n') | 
 |     if gencom.returnTypeDict[functionName] != 'void': | 
 |       f.write (gencom.clang_off_spaces*2 + 'return VK_SUCCESS;\n') | 
 |     f.write (gencom.clang_off_spaces + '}\n') | 
 |     f.write ('}\n\n') | 
 |  | 
 | def defineGlobalProcHook(functionName, f): | 
 |   base_name = functionName[2:] | 
 |   assert (functionName not in gencom.extensionsDict) | 
 |   f.write (gencom.clang_off_spaces + '{\n' + gencom.clang_off_spaces*2 + '\"' + functionName + '\",\n' + gencom.clang_off_spaces*2) | 
 |   f.write ("""ProcHook::GLOBAL, | 
 |         ProcHook::EXTENSION_CORE, | 
 |         reinterpret_cast<PFN_vkVoidFunction>(""" + base_name  + """), | 
 |         nullptr, | 
 |     },\n""") | 
 |  | 
 | def defineInstanceProcHook(functionName, f): | 
 |   base_name = functionName[2:] | 
 |   f.write (gencom.clang_off_spaces + '{\n') | 
 |   f.write (gencom.clang_off_spaces*2 + '\"' + functionName + '\",\n') | 
 |   f.write (gencom.clang_off_spaces*2 + 'ProcHook::INSTANCE,\n') | 
 |  | 
 |   if functionName in gencom.extensionsDict: | 
 |     ext_name = gencom.extensionsDict[functionName] | 
 |     f.write (gencom.clang_off_spaces*2 + 'ProcHook::' + ext_name[3:] + ',\n') | 
 |     if gencom.isExtensionInternal(ext_name): | 
 |       f.write (gencom.clang_off_spaces*2 + 'nullptr,\n' + gencom.clang_off_spaces*2 + 'nullptr,\n') | 
 |     else: | 
 |       f.write (gencom.clang_off_spaces*2 + 'reinterpret_cast<PFN_vkVoidFunction>(' + base_name + '),\n' + gencom.clang_off_spaces*2 + 'nullptr,\n') | 
 |  | 
 |   else: | 
 |     f.write (gencom.clang_off_spaces*2 + """ProcHook::EXTENSION_CORE, | 
 |         reinterpret_cast<PFN_vkVoidFunction>(""" + base_name + """), | 
 |         nullptr,\n""") | 
 |  | 
 |   f.write (gencom.clang_off_spaces + '},\n') | 
 |  | 
 | def defineDeviceProcHook(functionName, f): | 
 |   base_name = functionName[2:] | 
 |   f.write (gencom.clang_off_spaces + '{\n') | 
 |   f.write (gencom.clang_off_spaces*2 + '\"' + functionName + '\",\n') | 
 |   f.write (gencom.clang_off_spaces*2 + 'ProcHook::DEVICE,\n') | 
 |  | 
 |   if functionName in gencom.extensionsDict: | 
 |     ext_name = gencom.extensionsDict[functionName] | 
 |     f.write (gencom.clang_off_spaces*2 + 'ProcHook::' + ext_name[3:] + ',\n') | 
 |     if gencom.isExtensionInternal(ext_name): | 
 |       f.write (gencom.clang_off_spaces*2 + 'nullptr,\n' + gencom.clang_off_spaces*2 + 'nullptr,\n') | 
 |     else: | 
 |       f.write (gencom.clang_off_spaces*2 + 'reinterpret_cast<PFN_vkVoidFunction>(' + base_name + '),\n' + gencom.clang_off_spaces*2 + 'reinterpret_cast<PFN_vkVoidFunction>(checked' + base_name + '),\n') | 
 |  | 
 |   else: | 
 |     f.write (gencom.clang_off_spaces*2 + """ProcHook::EXTENSION_CORE, | 
 |         reinterpret_cast<PFN_vkVoidFunction>(""" + base_name + """), | 
 |         nullptr,\n""") | 
 |  | 
 |   f.write (gencom.clang_off_spaces + '},\n') | 
 |  | 
 | def driver_gencpp(): | 
 |   header = """#include <log/log.h> | 
 | #include <string.h> | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | #include "driver.h" | 
 |  | 
 | namespace vulkan { | 
 | namespace driver { | 
 |  | 
 | namespace { | 
 |  | 
 | // clang-format off\n\n""" | 
 |  | 
 |   genfile = os.path.join(os.path.dirname(__file__),'..','libvulkan','driver_gen.cpp') | 
 |  | 
 |   with open(genfile, 'w') as f: | 
 |     f.write (gencom.copyright) | 
 |     f.write (gencom.warning) | 
 |     f.write (header) | 
 |  | 
 |     for cmds in gencom.allCommandsList: | 
 |       defineProcHookStub(cmds, f) | 
 |     gencom.clang_on(f, 0) | 
 |     f.write ('\n') | 
 |  | 
 |     f.write ('const ProcHook g_proc_hooks[] = {\n') | 
 |     gencom.clang_off(f, 1) | 
 |     sortedCommandsList = sorted(gencom.allCommandsList) | 
 |     for cmds in sortedCommandsList: | 
 |       if isIntercepted(cmds): | 
 |         if gencom.isGloballyDispatched(cmds): | 
 |           defineGlobalProcHook(cmds, f) | 
 |         elif gencom.isInstanceDispatched(cmds): | 
 |           defineInstanceProcHook(cmds, f) | 
 |         elif gencom.isDeviceDispatched(cmds): | 
 |           defineDeviceProcHook(cmds, f) | 
 |     gencom.clang_on(f, 1) | 
 |     f.write ('};\n\n}  // namespace\n\n') | 
 |  | 
 |     f.write ("""const ProcHook* GetProcHook(const char* name) { | 
 |     const auto& begin = g_proc_hooks; | 
 |     const auto& end = | 
 |         g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]); | 
 |     const auto hook = std::lower_bound( | 
 |         begin, end, name, | 
 |         [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; }); | 
 |     return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr; | 
 | }\n\n""") | 
 |  | 
 |     f.write ('ProcHook::Extension GetProcHookExtension(const char* name) {\n') | 
 |     gencom.clang_off(f, 1) | 
 |     for exts in knownExtensions: | 
 |       f.write (gencom.clang_off_spaces + 'if (strcmp(name, \"' + exts + '\") == 0) return ProcHook::' + exts[3:] + ';\n') | 
 |     gencom.clang_on(f, 1) | 
 |     f.write (gencom.clang_off_spaces + 'return ProcHook::EXTENSION_UNKNOWN;\n') | 
 |     f.write ('}\n\n') | 
 |  | 
 |     defineInitProc('driver', f) | 
 |     defineInitProcExt(f) | 
 |  | 
 |     f.write ("""bool InitDriverTable(VkInstance instance, | 
 |                      PFN_vkGetInstanceProcAddr get_proc, | 
 |                      const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) { | 
 |     auto& data = GetData(instance); | 
 |     bool success = true;\n\n""") | 
 |     gencom.clang_off(f, 1) | 
 |     for cmds in gencom.allCommandsList: | 
 |       if isInstanceDriverTableEntry(cmds): | 
 |         gencom.initProc(cmds, f) | 
 |     gencom.clang_on(f, 1) | 
 |     f.write ('\n' + gencom.clang_off_spaces + 'return success;\n') | 
 |     f.write ('}\n\n') | 
 |  | 
 |     f.write ("""bool InitDriverTable(VkDevice dev, | 
 |                      PFN_vkGetDeviceProcAddr get_proc, | 
 |                      const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) { | 
 |     auto& data = GetData(dev); | 
 |     bool success = true;\n\n""") | 
 |     gencom.clang_off(f, 1) | 
 |     for cmds in gencom.allCommandsList: | 
 |       if isDeviceDriverTableEntry(cmds): | 
 |         gencom.initProc(cmds, f) | 
 |     gencom.clang_on(f, 1) | 
 |     f.write ('\n' + gencom.clang_off_spaces + 'return success;\n') | 
 |     f.write ('}\n\n}  // namespace driver\n}  // namespace vulkan\n\n') | 
 |     gencom.clang_on(f, 0) | 
 |     f.close() | 
 |   gencom.runClangFormat(genfile) |