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