| Adithya Srinivasan | 751a7dc | 2019-07-02 17:17:25 -0700 | [diff] [blame] | 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 | # This script provides the functions required for generating the | 
|  | 18 | # vulkan api framework directly from the vulkan registry (vk.xml) | 
|  | 19 |  | 
|  | 20 | import os | 
|  | 21 | import generator_common as gencom | 
|  | 22 |  | 
|  | 23 | def isInstanceDispatchTableEntry(functionName): | 
|  | 24 | if functionName == 'vkEnumerateDeviceLayerProperties': # deprecated, unused internally - @dbd33bc | 
|  | 25 | return False | 
|  | 26 | if gencom.gencom.isFunctionExported(functionName) and gencom.isInstanceDispatched(functionName): | 
|  | 27 | return True | 
|  | 28 | return False | 
|  | 29 |  | 
|  | 30 | def isDeviceDispatchTableEntry(functionName): | 
|  | 31 | if gencom.gencom.isFunctionExported(functionName) and gencom.gencom.isDeviceDispatched(functionName): | 
|  | 32 | return True | 
|  | 33 | return False | 
|  | 34 |  | 
|  | 35 | def api_genh(): | 
|  | 36 |  | 
|  | 37 | header = """#ifndef LIBVULKAN_API_GEN_H | 
|  | 38 | #define LIBVULKAN_API_GEN_H | 
|  | 39 |  | 
|  | 40 | #include <vulkan/vulkan.h> | 
|  | 41 |  | 
|  | 42 | #include <bitset> | 
|  | 43 |  | 
|  | 44 | #include "driver_gen.h" | 
|  | 45 |  | 
|  | 46 | namespace vulkan { | 
|  | 47 | namespace api { | 
|  | 48 |  | 
|  | 49 | """ | 
|  | 50 |  | 
|  | 51 | tail = """ | 
|  | 52 | bool InitDispatchTable( | 
|  | 53 | VkInstance instance, | 
|  | 54 | PFN_vkGetInstanceProcAddr get_proc, | 
|  | 55 | const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions); | 
|  | 56 | bool InitDispatchTable( | 
|  | 57 | VkDevice dev, | 
|  | 58 | PFN_vkGetDeviceProcAddr get_proc, | 
|  | 59 | const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions); | 
|  | 60 |  | 
|  | 61 | }  // namespace api | 
|  | 62 | }  // namespace vulkan | 
|  | 63 |  | 
|  | 64 | #endif  // LIBVULKAN_API_GEN_H | 
|  | 65 | """ | 
| Adithya Srinivasan | 8dce9d7 | 2019-07-11 14:26:04 -0700 | [diff] [blame] | 66 | genfile = os.path.join(os.path.dirname(__file__),'..','libvulkan','api_gen.h') | 
| Adithya Srinivasan | 751a7dc | 2019-07-02 17:17:25 -0700 | [diff] [blame] | 67 | with open(genfile, 'w') as f: | 
|  | 68 | instanceDispatchTableEntries = [] | 
|  | 69 | deviceDispatchTableEntries = [] | 
|  | 70 | for commands in gencom.allCommandsList: | 
|  | 71 | if commands not in gencom.aliasDict: | 
|  | 72 | if gencom.isInstanceDispatchTableEntry(commands): | 
|  | 73 | instanceDispatchTableEntries.append('PFN_'+commands+' '+commands[2:]+';') | 
|  | 74 | elif gencom.isDeviceDispatchTableEntry(commands): | 
|  | 75 | deviceDispatchTableEntries.append('PFN_'+commands+' '+commands[2:]+';') | 
|  | 76 |  | 
|  | 77 | f.write (gencom.copyright) | 
|  | 78 | f.write (gencom.warning) | 
|  | 79 | f.write (header) | 
|  | 80 | f.write ('struct InstanceDispatchTable {\n') | 
|  | 81 | gencom.clang_off(f,1) | 
|  | 82 | for functions in instanceDispatchTableEntries: | 
|  | 83 | f.write(gencom.clang_off_spaces + functions + '\n') | 
|  | 84 | gencom.clang_on(f,1) | 
|  | 85 | f.write ('};\n\n') | 
|  | 86 |  | 
|  | 87 | f.write ('struct DeviceDispatchTable {\n') | 
|  | 88 | gencom.clang_off(f,1) | 
|  | 89 | for functions in deviceDispatchTableEntries: | 
|  | 90 | f.write(gencom.clang_off_spaces + functions + '\n') | 
|  | 91 | gencom.clang_on(f,1) | 
|  | 92 | f.write ('};\n') | 
|  | 93 |  | 
|  | 94 | f.write (tail) | 
|  | 95 | f.close() | 
| Adithya Srinivasan | 8dce9d7 | 2019-07-11 14:26:04 -0700 | [diff] [blame] | 96 | gencom.runClangFormat(genfile) | 
| Adithya Srinivasan | 751a7dc | 2019-07-02 17:17:25 -0700 | [diff] [blame] | 97 |  | 
|  | 98 | def defineInitProc(name, f): | 
|  | 99 | f.write ('#define UNLIKELY(expr) __builtin_expect((expr), 0)\n') | 
|  | 100 | f.write ('\n') | 
|  | 101 | f.write ("""#define INIT_PROC(required, obj, proc)                                 \\ | 
|  | 102 | do {                                                               \\ | 
|  | 103 | data.""" + name + """.proc =                                           \\ | 
|  | 104 | reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\ | 
|  | 105 | if (UNLIKELY(required && !data.""" + name + """.proc)) {               \\ | 
|  | 106 | ALOGE("missing " #obj " proc: vk" #proc);                  \\ | 
|  | 107 | success = false;                                           \\ | 
|  | 108 | }                                                              \\ | 
|  | 109 | } while (0)\n\n""") | 
|  | 110 |  | 
|  | 111 | def defineInitProcExt(f): | 
|  | 112 | f.write ('// Exported extension functions may be invoked even when their extensions\n') | 
|  | 113 | f.write ('// are disabled.  Dispatch to stubs when that happens.\n') | 
|  | 114 | f.write ("""#define INIT_PROC_EXT(ext, required, obj, proc)  \\ | 
|  | 115 | do {                                         \\ | 
|  | 116 | if (extensions[driver::ProcHook::ext])   \\ | 
|  | 117 | INIT_PROC(required, obj, proc);      \\ | 
|  | 118 | else                                     \\ | 
|  | 119 | data.dispatch.proc = disabled##proc; \\ | 
|  | 120 | } while (0)\n\n""") | 
|  | 121 |  | 
|  | 122 | def defineExtensionStub(functionName, f): | 
|  | 123 | if functionName in gencom.extensionsDict and gencom.isFunctionExported(functionName): | 
|  | 124 | extname = gencom.extensionsDict[functionName] | 
|  | 125 | base_name = functionName[2:] | 
|  | 126 | pList = gencom.paramDict[functionName] | 
|  | 127 | firstParam = pList[0][0] + pList[0][1] | 
|  | 128 | tailParams = [x[0][:-1] for x in pList[1:]] | 
|  | 129 | tailP = ', '.join(tailParams) | 
|  | 130 | f.write ('VKAPI_ATTR ' + gencom.returnTypeDict[functionName] + ' disabled' + base_name + '(' + firstParam + ', ' + tailP + ') {\n') | 
|  | 131 | f.write (gencom.clang_off_spaces) | 
|  | 132 | f.write ('driver::Logger(' + pList[0][1] + ').Err(' + pList[0][1] + ', \"' + extname + ' not enabled. Exported ' + functionName + ' not executed.\");\n') | 
|  | 133 | if gencom.returnTypeDict[functionName] != 'void': | 
|  | 134 | f.write(gencom.clang_off_spaces + 'return VK_SUCCESS;\n') | 
|  | 135 | f.write ('}\n\n') | 
|  | 136 |  | 
|  | 137 | def isIntercepted(functionName): | 
|  | 138 | if gencom.isFunctionSupported(functionName): | 
|  | 139 | if gencom.isGloballyDispatched(functionName): | 
|  | 140 | return True | 
|  | 141 | elif functionName == 'vkCreateDevice': | 
|  | 142 | return True | 
|  | 143 | elif functionName == 'vkEnumerateDeviceLayerProperties': | 
|  | 144 | return True | 
|  | 145 | elif functionName == 'vkEnumerateDeviceExtensionProperties': | 
|  | 146 | return True | 
|  | 147 | elif functionName == 'vkDestroyInstance': | 
|  | 148 | return True | 
|  | 149 | elif functionName == 'vkDestroyDevice': | 
|  | 150 | return True | 
|  | 151 | return False | 
|  | 152 |  | 
|  | 153 | def interceptInstanceProcAddr(functionName, f): | 
|  | 154 | indent = 1 | 
|  | 155 | f.write (gencom.clang_off_spaces*indent + '// global functions\n' + gencom.clang_off_spaces*indent+ 'if (instance == VK_NULL_HANDLE) {\n') | 
|  | 156 | indent = indent + 1 | 
|  | 157 | for cmds in gencom.allCommandsList: | 
|  | 158 | if gencom.isGloballyDispatched(cmds): | 
|  | 159 | f.write(gencom.clang_off_spaces*indent + 'if (strcmp(pName, \"' + cmds + '\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + cmds[2:] + ');\n') | 
|  | 160 |  | 
|  | 161 | f.write ('\n') | 
|  | 162 | f.write ("""        ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \\\"%s\\\") call", pName); | 
|  | 163 | return nullptr; | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | static const struct Hook { | 
|  | 167 | const char* name; | 
|  | 168 | PFN_vkVoidFunction proc; | 
|  | 169 | } hooks[] = {\n""") | 
|  | 170 | sortedCommandsList = sorted(gencom.allCommandsList) | 
|  | 171 | for cmds in sortedCommandsList: | 
|  | 172 | if gencom.isFunctionExported(cmds): | 
|  | 173 | if gencom.isGloballyDispatched(cmds): | 
|  | 174 | f.write (gencom.clang_off_spaces*2 + '{ \"' + cmds + '\", nullptr },\n') | 
|  | 175 | elif isIntercepted(cmds) or cmds == 'vkGetInstanceProcAddr' or gencom.isDeviceDispatched(cmds): | 
|  | 176 | f.write (gencom.clang_off_spaces*2 + '{ \"' + cmds + '\", reinterpret_cast<PFN_vkVoidFunction>(' + cmds[2:] + ') },\n') | 
|  | 177 | f.write (gencom.clang_off_spaces + """}; | 
|  | 178 | // clang-format on | 
|  | 179 | constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]); | 
|  | 180 | auto hook = std::lower_bound( | 
|  | 181 | hooks, hooks + count, pName, | 
|  | 182 | [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; }); | 
|  | 183 | if (hook < hooks + count && strcmp(hook->name, pName) == 0) { | 
|  | 184 | if (!hook->proc) { | 
|  | 185 | vulkan::driver::Logger(instance).Err( | 
|  | 186 | instance, "invalid vkGetInstanceProcAddr(%p, \\\"%s\\\") call", | 
|  | 187 | instance, pName); | 
|  | 188 | } | 
|  | 189 | return hook->proc; | 
|  | 190 | } | 
|  | 191 | // clang-format off\n\n""") | 
|  | 192 |  | 
|  | 193 | def interceptDeviceProcAddr(functionName, f): | 
|  | 194 | f.write (gencom.clang_off_spaces + """if (device == VK_NULL_HANDLE) { | 
|  | 195 | ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call"); | 
|  | 196 | return nullptr; | 
|  | 197 | }\n\n""") | 
|  | 198 | f.write (gencom.clang_off_spaces + 'static const char* const known_non_device_names[] = {\n') | 
|  | 199 | sortedCommandsList = sorted(gencom.allCommandsList) | 
|  | 200 | for cmds in sortedCommandsList: | 
|  | 201 | if gencom.isFunctionSupported(cmds): | 
|  | 202 | if not gencom.isDeviceDispatched(cmds): | 
|  | 203 | f.write(gencom.clang_off_spaces*2 + '\"' + cmds + '\",\n') | 
|  | 204 | f.write(gencom.clang_off_spaces + '};\n') | 
|  | 205 | f.write(gencom.clang_off_spaces + """// clang-format on | 
|  | 206 | constexpr size_t count = | 
|  | 207 | sizeof(known_non_device_names) / sizeof(known_non_device_names[0]); | 
|  | 208 | if (!pName || | 
|  | 209 | std::binary_search( | 
|  | 210 | known_non_device_names, known_non_device_names + count, pName, | 
|  | 211 | [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) { | 
|  | 212 | vulkan::driver::Logger(device).Err( | 
|  | 213 | device, "invalid vkGetDeviceProcAddr(%p, \\\"%s\\\") call", device, | 
|  | 214 | (pName) ? pName : "(null)"); | 
|  | 215 | return nullptr; | 
|  | 216 | } | 
|  | 217 | // clang-format off\n\n""") | 
|  | 218 | for cmds in gencom.allCommandsList: | 
|  | 219 | if gencom.isDeviceDispatched(cmds): | 
|  | 220 | if isIntercepted(cmds) or cmds == 'vkGetDeviceProcAddr': | 
|  | 221 | f.write (gencom.clang_off_spaces + 'if (strcmp(pName, "' + cmds + '") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + cmds[2:] + ');\n') | 
|  | 222 | f.write ('\n') | 
|  | 223 |  | 
|  | 224 | def apiDispatch(functionName, f): | 
|  | 225 | assert not isIntercepted(functionName) | 
|  | 226 |  | 
|  | 227 | f.write (gencom.clang_off_spaces) | 
|  | 228 | if gencom.returnTypeDict[functionName] != 'void': | 
|  | 229 | f.write ('return ') | 
|  | 230 |  | 
|  | 231 | paramList = gencom.paramDict[functionName] | 
|  | 232 | p0 = paramList[0][1] | 
|  | 233 | f.write('GetData(' + p0 + ').dispatch.' + functionName[2:] + '(' + ', '.join(i[1] for i in paramList) + ');\n') | 
|  | 234 |  | 
|  | 235 |  | 
|  | 236 | def api_gencpp(): | 
| Adithya Srinivasan | 8dce9d7 | 2019-07-11 14:26:04 -0700 | [diff] [blame] | 237 | genfile = os.path.join(os.path.dirname(__file__),'..','libvulkan','api_gen.cpp') | 
| Adithya Srinivasan | 751a7dc | 2019-07-02 17:17:25 -0700 | [diff] [blame] | 238 | header = """#include <log/log.h> | 
|  | 239 | #include <string.h> | 
|  | 240 |  | 
|  | 241 | #include <algorithm> | 
|  | 242 |  | 
|  | 243 | // to catch mismatches between vulkan.h and this file | 
|  | 244 | #undef VK_NO_PROTOTYPES | 
|  | 245 | #include "api.h" | 
|  | 246 |  | 
|  | 247 | namespace vulkan { | 
|  | 248 | namespace api { | 
|  | 249 |  | 
|  | 250 | """ | 
|  | 251 | with open(genfile, 'w') as f: | 
|  | 252 | f.write (gencom.copyright) | 
|  | 253 | f.write (gencom.warning) | 
|  | 254 | f.write ("""#include <log/log.h> | 
|  | 255 | #include <string.h> | 
|  | 256 |  | 
|  | 257 | #include <algorithm> | 
|  | 258 |  | 
|  | 259 | // to catch mismatches between vulkan.h and this file | 
|  | 260 | #undef VK_NO_PROTOTYPES | 
|  | 261 | #include "api.h" | 
|  | 262 |  | 
|  | 263 | namespace vulkan { | 
|  | 264 | namespace api {\n\n""") | 
|  | 265 | defineInitProc('dispatch',f) | 
|  | 266 | defineInitProcExt(f) | 
|  | 267 | f.write ('namespace {\n\n') | 
|  | 268 | gencom.clang_off(f,0) | 
|  | 269 | f.write ('\n') | 
|  | 270 | for cmds in gencom.allCommandsList: | 
|  | 271 | defineExtensionStub(cmds,f) | 
|  | 272 | gencom.clang_on(f,0) | 
|  | 273 | f.write ('\n}  // namespace\n\n') | 
|  | 274 | f.write ("""bool InitDispatchTable( | 
|  | 275 | VkInstance instance, | 
|  | 276 | PFN_vkGetInstanceProcAddr get_proc, | 
|  | 277 | const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) { | 
|  | 278 | auto& data = GetData(instance); | 
|  | 279 | bool success = true;\n\n""") | 
|  | 280 | gencom.clang_off(f,1) | 
|  | 281 | for cmds in gencom.allCommandsList: | 
|  | 282 | if gencom.isInstanceDispatchTableEntry(cmds): | 
|  | 283 | gencom.initProc(cmds, f) | 
|  | 284 | gencom.clang_on(f,1) | 
|  | 285 | f.write ('\n') | 
|  | 286 | f.write ('    return success;\n}\n\n') | 
|  | 287 | f.write ("""bool InitDispatchTable( | 
|  | 288 | VkDevice dev, | 
|  | 289 | PFN_vkGetDeviceProcAddr get_proc, | 
|  | 290 | const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) { | 
|  | 291 | auto& data = GetData(dev); | 
|  | 292 | bool success = true;\n\n""") | 
|  | 293 |  | 
|  | 294 | gencom.clang_off(f,1) | 
|  | 295 | for cmds in gencom.allCommandsList: | 
|  | 296 | if gencom.isDeviceDispatchTableEntry(cmds): | 
|  | 297 | gencom.initProc(cmds, f) | 
|  | 298 | gencom.clang_on(f,1) | 
|  | 299 | f.write ('\n') | 
|  | 300 | f.write ('    return success;\n}\n\n') | 
|  | 301 |  | 
|  | 302 | gencom.clang_off(f,0) | 
|  | 303 |  | 
|  | 304 | f.write ('\nnamespace {\n\n') | 
|  | 305 | f.write('// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr\n') | 
|  | 306 | for cmds in gencom.allCommandsList: | 
|  | 307 | if gencom.isFunctionExported(cmds) and not isIntercepted(cmds): | 
|  | 308 | paramList = [''.join(i) for i in gencom.paramDict[cmds]] | 
|  | 309 | f.write ('VKAPI_ATTR '+gencom.returnTypeDict[cmds] + ' ' + cmds[2:] + '(' + ', '.join(paramList) + ');\n') | 
|  | 310 |  | 
|  | 311 | f.write ('\n') | 
|  | 312 |  | 
|  | 313 | for cmds in gencom.allCommandsList: | 
|  | 314 | if gencom.isFunctionExported(cmds) and not isIntercepted(cmds): | 
|  | 315 | paramList = [''.join(i) for i in gencom.paramDict[cmds]] | 
|  | 316 | f.write ('VKAPI_ATTR ' + gencom.returnTypeDict[cmds] + ' ' + cmds[2:] + '(' + ', '.join(paramList) + ') {\n') | 
|  | 317 | if cmds == 'vkGetInstanceProcAddr': | 
|  | 318 | interceptInstanceProcAddr(cmds, f) | 
|  | 319 | elif cmds == 'vkGetDeviceProcAddr': | 
|  | 320 | interceptDeviceProcAddr(cmds, f) | 
|  | 321 | apiDispatch(cmds, f) | 
|  | 322 | f.write('}\n\n') | 
|  | 323 | f.write ("""\n}  // anonymous namespace | 
|  | 324 |  | 
|  | 325 | // clang-format on | 
|  | 326 |  | 
|  | 327 | }  // namespace api | 
|  | 328 | }  // namespace vulkan | 
|  | 329 |  | 
|  | 330 | // clang-format off\n\n""") | 
|  | 331 |  | 
|  | 332 | for cmds in gencom.allCommandsList: | 
|  | 333 | if gencom.isFunctionExported(cmds): | 
|  | 334 | paramList = [''.join(i) for i in gencom.paramDict[cmds]] | 
|  | 335 | f.write ('__attribute__((visibility("default")))\n') | 
|  | 336 | f.write ('VKAPI_ATTR ' + gencom.returnTypeDict[cmds] + ' ' + cmds + '(' + ', '.join(paramList) + ') {\n') | 
|  | 337 | f.write (gencom.clang_off_spaces) | 
|  | 338 | if gencom.returnTypeDict[cmds] != 'void': | 
|  | 339 | f.write ('return ') | 
|  | 340 | paramList = gencom.paramDict[cmds] | 
|  | 341 | f.write ('vulkan::api::' + cmds[2:] + '(' + ', '.join(i[1] for i in paramList) + ');\n') | 
|  | 342 | f.write ('}\n\n') | 
|  | 343 |  | 
|  | 344 | gencom.clang_on(f, 0) | 
| Adithya Srinivasan | 8dce9d7 | 2019-07-11 14:26:04 -0700 | [diff] [blame] | 345 | f.close() | 
|  | 346 | gencom.runClangFormat(genfile) |