|  | #!/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) |