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 64namespace vulkan { 65namespace api { 66 67struct InstanceDispatchTable { 68 // clang-format off\n""") 69 70 for entry in instance_dispatch_table_entries: 71 f.write(gencom.indent(1) + entry + '\n') 72 73 f.write("""\ 74 // clang-format on 75}; 76 77struct DeviceDispatchTable { 78 // clang-format off\n""") 79 80 for entry in device_dispatch_table_entries: 81 f.write(gencom.indent(1) + entry + '\n') 82 83 f.write("""\ 84 // clang-format on 85}; 86 87bool InitDispatchTable( 88 VkInstance instance, 89 PFN_vkGetInstanceProcAddr get_proc, 90 const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions); 91bool InitDispatchTable( 92 VkDevice dev, 93 PFN_vkGetDeviceProcAddr get_proc, 94 const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions); 95 96} // namespace api 97} // namespace vulkan 98 99#endif // LIBVULKAN_API_GEN_H\n""") 100 101 f.close() 102 gencom.run_clang_format(genfile) 103 104 105def _define_extension_stub(cmd, f): 106 """Emits a stub for an exported extension function. 107 108 Args: 109 cmd: Vulkan function name. 110 f: Output file handle. 111 """ 112 if (cmd in gencom.extension_dict and gencom.is_function_exported(cmd)): 113 ext_name = gencom.extension_dict[cmd] 114 ret = gencom.return_type_dict[cmd] 115 params = gencom.param_dict[cmd] 116 first_param = params[0][0] + params[0][1] 117 tail_params = ', '.join([i[0][:-1] for i in params[1:]]) 118 119 f.write('VKAPI_ATTR ' + ret + ' disabled' + gencom.base_name(cmd) + 120 '(' + first_param + ', ' + tail_params + ') {\n') 121 122 f.write(gencom.indent(1) + 'driver::Logger(' + params[0][1] + 123 ').Err(' + params[0][1] + ', \"' + ext_name + 124 ' not enabled. Exported ' + cmd + ' not executed.\");\n') 125 126 if gencom.return_type_dict[cmd] != 'void': 127 f.write(gencom.indent(1) + 'return VK_SUCCESS;\n') 128 129 f.write('}\n\n') 130 131 132def _is_intercepted(cmd): 133 """Returns true if a function is intercepted by vulkan::api. 134 135 Args: 136 cmd: Vulkan function name. 137 """ 138 if gencom.is_function_supported(cmd): 139 if gencom.is_globally_dispatched(cmd) or cmd in _INTERCEPTED_COMMANDS: 140 return True 141 return False 142 143 144def _intercept_instance_proc_addr(f): 145 """Emits code for vkGetInstanceProcAddr for function interception. 146 147 Args: 148 f: Output file handle. 149 """ 150 f.write("""\ 151 // global functions 152 if (instance == VK_NULL_HANDLE) {\n""") 153 154 for cmd in gencom.command_list: 155 # vkGetInstanceProcAddr(nullptr, "vkGetInstanceProcAddr") is effectively 156 # globally dispatched 157 if gencom.is_globally_dispatched(cmd) or cmd == 'vkGetInstanceProcAddr': 158 f.write(gencom.indent(2) + 159 'if (strcmp(pName, \"' + cmd + 160 '\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + 161 gencom.base_name(cmd) + ');\n') 162 163 f.write(""" 164 ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \\\"%s\\\") call", pName); 165 return nullptr; 166 } 167 168 static const struct Hook { 169 const char* name; 170 PFN_vkVoidFunction proc; 171 } hooks[] = {\n""") 172 173 sorted_command_list = sorted(gencom.command_list) 174 for cmd in sorted_command_list: 175 if gencom.is_function_exported(cmd): 176 if gencom.is_globally_dispatched(cmd): 177 f.write(gencom.indent(2) + '{ \"' + cmd + '\", nullptr },\n') 178 elif (_is_intercepted(cmd) or 179 cmd == 'vkGetInstanceProcAddr' or 180 gencom.is_device_dispatched(cmd)): 181 f.write(gencom.indent(2) + '{ \"' + cmd + 182 '\", reinterpret_cast<PFN_vkVoidFunction>(' + 183 gencom.base_name(cmd) + ') },\n') 184 185 f.write("""\ 186 }; 187 // clang-format on 188 constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]); 189 auto hook = std::lower_bound( 190 hooks, hooks + count, pName, 191 [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; }); 192 if (hook < hooks + count && strcmp(hook->name, pName) == 0) { 193 if (!hook->proc) { 194 vulkan::driver::Logger(instance).Err( 195 instance, "invalid vkGetInstanceProcAddr(%p, \\\"%s\\\") call", 196 instance, pName); 197 } 198 return hook->proc; 199 } 200 // clang-format off\n\n""") 201 202 203def _intercept_device_proc_addr(f): 204 """Emits code for vkGetDeviceProcAddr for function interception. 205 206 Args: 207 f: Output file handle. 208 """ 209 f.write("""\ 210 if (device == VK_NULL_HANDLE) { 211 ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call"); 212 return nullptr; 213 } 214 215 static const char* const known_non_device_names[] = {\n""") 216 217 sorted_command_list = sorted(gencom.command_list) 218 for cmd in sorted_command_list: 219 if gencom.is_function_supported(cmd): 220 if not gencom.is_device_dispatched(cmd): 221 f.write(gencom.indent(2) + '\"' + cmd + '\",\n') 222 223 f.write("""\ 224 }; 225 // clang-format on 226 constexpr size_t count = 227 sizeof(known_non_device_names) / sizeof(known_non_device_names[0]); 228 if (!pName || 229 std::binary_search( 230 known_non_device_names, known_non_device_names + count, pName, 231 [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) { 232 vulkan::driver::Logger(device).Err( 233 device, "invalid vkGetDeviceProcAddr(%p, \\\"%s\\\") call", device, 234 (pName) ? pName : "(null)"); 235 return nullptr; 236 } 237 // clang-format off\n\n""") 238 239 for cmd in gencom.command_list: 240 if gencom.is_device_dispatched(cmd): 241 if _is_intercepted(cmd) or cmd == 'vkGetDeviceProcAddr': 242 f.write(gencom.indent(1) + 'if (strcmp(pName, "' + cmd + 243 '") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + 244 gencom.base_name(cmd) + ');\n') 245 f.write('\n') 246 247 248def _api_dispatch(cmd, f): 249 """Emits code to dispatch a function. 250 251 Args: 252 cmd: Vulkan function name. 253 f: Output file handle. 254 """ 255 assert not _is_intercepted(cmd) 256 257 f.write(gencom.indent(1)) 258 if gencom.return_type_dict[cmd] != 'void': 259 f.write('return ') 260 261 param_list = gencom.param_dict[cmd] 262 handle = param_list[0][1] 263 f.write('GetData(' + handle + ').dispatch.' + gencom.base_name(cmd) + 264 '(' + ', '.join(i[1] for i in param_list) + ');\n') 265 266 267def gen_cpp(): 268 """Generates the api_gen.cpp file. 269 """ 270 genfile = os.path.join(os.path.dirname(__file__), 271 '..', 'libvulkan', 'api_gen.cpp') 272 273 with open(genfile, 'w') as f: 274 f.write(gencom.copyright_and_warning(2016)) 275 276 f.write("""\ 277#include <log/log.h> 278#include <string.h> 279 280#include <algorithm> 281 282// to catch mismatches between vulkan.h and this file 283#undef VK_NO_PROTOTYPES 284#include "api.h" 285 286namespace vulkan { 287namespace api { 288 289#define UNLIKELY(expr) __builtin_expect((expr), 0) 290 291#define INIT_PROC(required, obj, proc) \\ 292 do { \\ 293 data.dispatch.proc = \\ 294 reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\ 295 if (UNLIKELY(required && !data.dispatch.proc)) { \\ 296 ALOGE("missing " #obj " proc: vk" #proc); \\ 297 success = false; \\ 298 } \\ 299 } while (0) 300 301// Exported extension functions may be invoked even when their extensions 302// are disabled. Dispatch to stubs when that happens. 303#define INIT_PROC_EXT(ext, required, obj, proc) \\ 304 do { \\ 305 if (extensions[driver::ProcHook::ext]) \\ 306 INIT_PROC(required, obj, proc); \\ 307 else \\ 308 data.dispatch.proc = disabled##proc; \\ 309 } while (0) 310 311namespace { 312 313// clang-format off\n\n""") 314 315 for cmd in gencom.command_list: 316 _define_extension_stub(cmd, f) 317 318 f.write("""\ 319// clang-format on 320 321} // namespace 322 323bool InitDispatchTable( 324 VkInstance instance, 325 PFN_vkGetInstanceProcAddr get_proc, 326 const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) { 327 auto& data = GetData(instance); 328 bool success = true; 329 330 // clang-format off\n""") 331 332 for cmd in gencom.command_list: 333 if gencom.is_instance_dispatch_table_entry(cmd): 334 gencom.init_proc(cmd, f) 335 336 f.write("""\ 337 // clang-format on 338 339 return success; 340} 341 342bool InitDispatchTable( 343 VkDevice dev, 344 PFN_vkGetDeviceProcAddr get_proc, 345 const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) { 346 auto& data = GetData(dev); 347 bool success = true; 348 349 // clang-format off\n""") 350 351 for cmd in gencom.command_list: 352 if gencom.is_device_dispatch_table_entry(cmd): 353 gencom.init_proc(cmd, f) 354 355 f.write("""\ 356 // clang-format on 357 358 return success; 359} 360 361// clang-format off 362 363namespace { 364 365// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr 366""") 367 368 for cmd in gencom.command_list: 369 if gencom.is_function_exported(cmd) and not _is_intercepted(cmd): 370 param_list = [''.join(i) for i in gencom.param_dict[cmd]] 371 f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' + 372 gencom.base_name(cmd) + '(' + ', '.join(param_list) + ');\n') 373 374 f.write('\n') 375 for cmd in gencom.command_list: 376 if gencom.is_function_exported(cmd) and not _is_intercepted(cmd): 377 param_list = [''.join(i) for i in gencom.param_dict[cmd]] 378 f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' + 379 gencom.base_name(cmd) + '(' + ', '.join(param_list) + ') {\n') 380 if cmd == 'vkGetInstanceProcAddr': 381 _intercept_instance_proc_addr(f) 382 elif cmd == 'vkGetDeviceProcAddr': 383 _intercept_device_proc_addr(f) 384 _api_dispatch(cmd, f) 385 f.write('}\n\n') 386 387 f.write(""" 388} // anonymous namespace 389 390// clang-format on 391 392} // namespace api 393} // namespace vulkan 394 395// clang-format off\n\n""") 396 397 for cmd in gencom.command_list: 398 if gencom.is_function_exported(cmd): 399 param_list = [''.join(i) for i in gencom.param_dict[cmd]] 400 f.write('__attribute__((visibility("default")))\n') 401 f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' + 402 cmd + '(' + ', '.join(param_list) + ') {\n') 403 f.write(gencom.indent(1)) 404 if gencom.return_type_dict[cmd] != 'void': 405 f.write('return ') 406 param_list = gencom.param_dict[cmd] 407 f.write('vulkan::api::' + gencom.base_name(cmd) + 408 '(' + ', '.join(i[1] for i in param_list) + ');\n}\n\n') 409 410 f.write('// clang-format on\n') 411 f.close() 412 gencom.run_clang_format(genfile) 413