blob: ef36f8cd0cc35a1ffd7632966b982cd8eee59583 [file] [log] [blame]
#!/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,
'vkQueueSubmit' : 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,
'vkQueueSubmit' : 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)