xref: /aosp_15_r20/frameworks/native/vulkan/scripts/generator_common.py (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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