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"""Provide the utilities for framework generation. 18""" 19 20import os 21import subprocess 22import xml.etree.ElementTree as element_tree 23 24# Extensions unsupported on Android. 25_BLOCKED_EXTENSIONS = [ 26 'VK_EXT_acquire_xlib_display', 27 'VK_EXT_direct_mode_display', 28 'VK_EXT_directfb_surface', 29 'VK_EXT_display_control', 30 'VK_EXT_display_surface_counter', 31 'VK_EXT_full_screen_exclusive', 32 'VK_EXT_headless_surface', 33 'VK_EXT_metal_surface', 34 'VK_FUCHSIA_imagepipe_surface', 35 'VK_GGP_stream_descriptor_surface', 36 'VK_HUAWEI_subpass_shading', 37 'VK_KHR_display', 38 'VK_KHR_display_swapchain', 39 'VK_KHR_external_fence_win32', 40 'VK_KHR_external_memory_win32', 41 'VK_KHR_external_semaphore_win32', 42 'VK_KHR_mir_surface', 43 'VK_KHR_wayland_surface', 44 'VK_KHR_win32_keyed_mutex', 45 'VK_KHR_win32_surface', 46 'VK_KHR_xcb_surface', 47 'VK_KHR_xlib_surface', 48 'VK_MVK_ios_surface', 49 'VK_MVK_macos_surface', 50 'VK_NN_vi_surface', 51 'VK_NV_acquire_winrt_display', 52 'VK_NV_cooperative_matrix', 53 'VK_NV_coverage_reduction_mode', 54 'VK_NV_external_memory_win32', 55 'VK_NV_win32_keyed_mutex', 56 'VK_NVX_image_view_handle', 57 'VK_QNX_screen_surface', 58] 59 60# Extensions having functions exported by the loader. 61_EXPORTED_EXTENSIONS = [ 62 'VK_ANDROID_external_memory_android_hardware_buffer', 63 'VK_KHR_android_surface', 64 'VK_KHR_surface', 65 'VK_KHR_swapchain', 66] 67 68# Functions optional on Android even if extension is advertised. 69_OPTIONAL_COMMANDS = [ 70 'vkGetSwapchainGrallocUsageANDROID', 71 'vkGetSwapchainGrallocUsage2ANDROID', 72 'vkGetSwapchainGrallocUsage3ANDROID', 73 'vkGetSwapchainGrallocUsage4ANDROID', 74] 75 76# Dict for mapping dispatch table to a type. 77_DISPATCH_TYPE_DICT = { 78 'VkInstance ': 'Instance', 79 'VkPhysicalDevice ': 'Instance', 80 'VkDevice ': 'Device', 81 'VkQueue ': 'Device', 82 'VkCommandBuffer ': 'Device' 83} 84 85# Dict for mapping a function to its alias. 86alias_dict = {} 87 88# List of all the Vulkan functions. 89command_list = [] 90 91# Dict for mapping a function to an extension. 92extension_dict = {} 93 94# Dict for mapping a function to all its parameters. 95param_dict = {} 96 97# Dict for mapping a function to its return type. 98return_type_dict = {} 99 100# List of the sorted Vulkan version codes. e.g. '1_0', '1_1'. 101version_code_list = [] 102 103# Dict for mapping a function to the core Vulkan API version. 104version_dict = {} 105 106# Dict for mapping a promoted instance extension to the core Vulkan API version. 107promoted_inst_ext_dict = {} 108 109 110def indent(num): 111 """Returns the requested indents. 112 113 Args: 114 num: Number of the 4-space indents. 115 """ 116 return ' ' * num 117 118 119def copyright_and_warning(year): 120 """Returns the standard copyright and warning codes. 121 122 Args: 123 year: An integer year for the copyright. 124 """ 125 return """\ 126/* 127 * Copyright """ + str(year) + """ The Android Open Source Project 128 * 129 * Licensed under the Apache License, Version 2.0 (the "License"); 130 * you may not use this file except in compliance with the License. 131 * You may obtain a copy of the License at 132 * 133 * http://www.apache.org/licenses/LICENSE-2.0 134 * 135 * Unless required by applicable law or agreed to in writing, software 136 * distributed under the License is distributed on an "AS IS" BASIS, 137 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138 * See the License for the specific language governing permissions and 139 * limitations under the License. 140 */ 141 142// WARNING: This file is generated. See ../README.md for instructions. 143 144""" 145 146 147def run_clang_format(args): 148 """Run clang format on the file. 149 150 Args: 151 args: The file to be formatted. 152 """ 153 clang_call = ['clang-format', '--style', 'file', '-i', args] 154 subprocess.check_call(clang_call) 155 156 157def is_extension_internal(ext): 158 """Returns true if an extension is internal to the loader and drivers. 159 160 The loader should not enumerate this extension. 161 162 Args: 163 ext: Vulkan extension name. 164 """ 165 return ext == 'VK_ANDROID_native_buffer' 166 167 168def base_name(cmd): 169 """Returns a function name without the 'vk' prefix. 170 171 Args: 172 cmd: Vulkan function name. 173 """ 174 return cmd[2:] 175 176 177def base_ext_name(ext): 178 """Returns an extension name without the 'VK_' prefix. 179 180 Args: 181 ext: Vulkan extension name. 182 """ 183 return ext[3:] 184 185 186def version_code(version): 187 """Returns the version code from a version string. 188 189 Args: 190 version: Vulkan version string. 191 """ 192 return version[11:] 193 194 195def version_2_api_version(version): 196 """Returns the api version from a version string. 197 198 Args: 199 version: Vulkan version string. 200 """ 201 return 'VK_API' + version[2:] 202 203 204def is_function_supported(cmd): 205 """Returns true if a function is core or from a supportable extension. 206 207 Args: 208 cmd: Vulkan function name. 209 """ 210 if cmd not in extension_dict: 211 return True 212 else: 213 if extension_dict[cmd] not in _BLOCKED_EXTENSIONS: 214 return True 215 return False 216 217 218def get_dispatch_table_type(cmd): 219 """Returns the dispatch table type for a function. 220 221 Args: 222 cmd: Vulkan function name. 223 """ 224 if cmd not in param_dict: 225 return None 226 227 if param_dict[cmd]: 228 return _DISPATCH_TYPE_DICT.get(param_dict[cmd][0][0], 'Global') 229 return 'Global' 230 231 232def is_globally_dispatched(cmd): 233 """Returns true if the function is global, which is not dispatched. 234 235 Only global functions and functions handled in the loader top without calling 236 into lower layers are not dispatched. 237 238 Args: 239 cmd: Vulkan function name. 240 """ 241 return is_function_supported(cmd) and get_dispatch_table_type(cmd) == 'Global' 242 243 244def is_instance_dispatched(cmd): 245 """Returns true for functions that can have instance-specific dispatch. 246 247 Args: 248 cmd: Vulkan function name. 249 """ 250 return (is_function_supported(cmd) and 251 get_dispatch_table_type(cmd) == 'Instance') 252 253 254def is_device_dispatched(cmd): 255 """Returns true for functions that can have device-specific dispatch. 256 257 Args: 258 cmd: Vulkan function name. 259 """ 260 return is_function_supported(cmd) and get_dispatch_table_type(cmd) == 'Device' 261 262 263def is_extension_exported(ext): 264 """Returns true if an extension has functions exported by the loader. 265 266 E.g. applications can directly link to an extension function. 267 268 Args: 269 ext: Vulkan extension name. 270 """ 271 return ext in _EXPORTED_EXTENSIONS 272 273 274def is_function_exported(cmd): 275 """Returns true if a function is exported from the Android Vulkan library. 276 277 Functions in the core API and in loader extensions are exported. 278 279 Args: 280 cmd: Vulkan function name. 281 """ 282 if is_function_supported(cmd): 283 if cmd in extension_dict: 284 return is_extension_exported(extension_dict[cmd]) 285 return True 286 return False 287 288 289def is_instance_dispatch_table_entry(cmd): 290 """Returns true if a function is exported and instance-dispatched. 291 292 Args: 293 cmd: Vulkan function name. 294 """ 295 if cmd == 'vkEnumerateDeviceLayerProperties': 296 # deprecated, unused internally - @dbd33bc 297 return False 298 return is_function_exported(cmd) and is_instance_dispatched(cmd) 299 300 301def is_device_dispatch_table_entry(cmd): 302 """Returns true if a function is exported and device-dispatched. 303 304 Args: 305 cmd: Vulkan function name. 306 """ 307 return is_function_exported(cmd) and is_device_dispatched(cmd) 308 309 310def init_proc(name, f): 311 """Emits code to invoke INIT_PROC or INIT_PROC_EXT. 312 313 Args: 314 name: Vulkan function name. 315 f: Output file handle. 316 """ 317 f.write(indent(1)) 318 if name in extension_dict: 319 f.write('INIT_PROC_EXT(' + base_ext_name(extension_dict[name]) + ', ') 320 else: 321 f.write('INIT_PROC(') 322 323 if name in _OPTIONAL_COMMANDS: 324 f.write('false, ') 325 elif version_dict[name] == 'VK_VERSION_1_0': 326 f.write('true, ') 327 else: 328 f.write('false, ') 329 330 if is_instance_dispatched(name): 331 f.write('instance, ') 332 else: 333 f.write('dev, ') 334 335 f.write(base_name(name) + ');\n') 336 337 338def parse_vulkan_registry(): 339 """Parses Vulkan registry into the below global variables. 340 341 alias_dict 342 command_list 343 extension_dict 344 param_dict 345 return_type_dict 346 version_code_list 347 version_dict 348 promoted_inst_ext_dict 349 """ 350 registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 351 'external', 'vulkan-headers', 'registry', 'vk.xml') 352 tree = element_tree.parse(registry) 353 root = tree.getroot() 354 355 for exts in root.iter('extensions'): 356 for extension in exts.iter('extension'): 357 if 'vulkan' not in extension.get('supported').split(','): 358 # ANDROID_native_buffer is a weird special case -- it's declared in vk.xml as 359 # disabled but we _do_ want to generate plumbing for it in the Android loader. 360 if extension.get('name') != 'VK_ANDROID_native_buffer': 361 print('skip extension disabled or not for vulkan: ' + extension.get('name')) 362 continue 363 364 apiversion = 'VK_VERSION_1_0' 365 if extension.tag == 'extension': 366 extname = extension.get('name') 367 if (extension.get('type') == 'instance' and 368 extension.get('promotedto') is not None): 369 promoted_inst_ext_dict[extname] = \ 370 version_2_api_version(extension.get('promotedto')) 371 for req in extension.iter('require'): 372 if req.get('feature') is not None: 373 apiversion = req.get('feature') 374 for commands in req: 375 if commands.tag == 'command': 376 cmd_name = commands.get('name') 377 if cmd_name not in extension_dict: 378 extension_dict[cmd_name] = extname 379 version_dict[cmd_name] = apiversion 380 381 for feature in root.iter('feature'): 382 # hack, 'feature' element has multiple meanings.. should be more precise with path match 383 if feature.get('api') is None: 384 continue 385 if 'vulkan' not in feature.get('api').split(','): 386 continue 387 388 apiversion = feature.get('name') 389 for req in feature.iter('require'): 390 for command in req: 391 if command.tag == 'command': 392 cmd_name = command.get('name') 393 version_dict[cmd_name] = apiversion 394 395 for commands in root.iter('commands'): 396 for command in commands: 397 if command.tag == 'command': 398 if command.get('api') == 'vulkansc': 399 continue 400 401 parameter_list = [] 402 protoset = False 403 cmd_name = '' 404 cmd_type = '' 405 if command.get('alias') is not None: 406 alias = command.get('alias') 407 cmd_name = command.get('name') 408 # At this stage all valid commands have been added to the version 409 # dict so we can use it to filter valid commands 410 if cmd_name in version_dict: 411 alias_dict[cmd_name] = alias 412 command_list.append(cmd_name) 413 param_dict[cmd_name] = param_dict[alias].copy() 414 return_type_dict[cmd_name] = return_type_dict[alias] 415 for params in command: 416 if params.tag == 'param': 417 if params.get('api') == 'vulkansc': 418 # skip SC-only param variant 419 continue 420 param_type = '' 421 if params.text is not None and params.text.strip(): 422 param_type = params.text.strip() + ' ' 423 type_val = params.find('type') 424 param_type = param_type + type_val.text 425 if type_val.tail is not None: 426 param_type += type_val.tail.strip() + ' ' 427 pname = params.find('name') 428 param_name = pname.text 429 if pname.tail is not None and pname.tail.strip(): 430 parameter_list.append( 431 (param_type, param_name, pname.tail.strip())) 432 else: 433 parameter_list.append((param_type, param_name)) 434 if params.tag == 'proto': 435 for c in params: 436 if c.tag == 'type': 437 cmd_type = c.text 438 if c.tag == 'name': 439 cmd_name = c.text 440 if cmd_name in version_dict: 441 protoset = True 442 command_list.append(cmd_name) 443 return_type_dict[cmd_name] = cmd_type 444 if protoset: 445 param_dict[cmd_name] = parameter_list.copy() 446 447 448 version_code_set = set() 449 for version in version_dict.values(): 450 version_code_set.add(version_code(version)) 451 for code in sorted(version_code_set): 452 version_code_list.append(code) 453