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  for commands in root.iter('commands'):
355    for command in commands:
356      if command.tag == 'command':
357        parameter_list = []
358        protoset = False
359        cmd_name = ''
360        cmd_type = ''
361        if command.get('alias') is not None:
362          alias = command.get('alias')
363          cmd_name = command.get('name')
364          alias_dict[cmd_name] = alias
365          command_list.append(cmd_name)
366          param_dict[cmd_name] = param_dict[alias].copy()
367          return_type_dict[cmd_name] = return_type_dict[alias]
368        for params in command:
369          if params.tag == 'param':
370            param_type = ''
371            if params.text is not None and params.text.strip():
372              param_type = params.text.strip() + ' '
373            type_val = params.find('type')
374            param_type = param_type + type_val.text
375            if type_val.tail is not None:
376              param_type += type_val.tail.strip() + ' '
377            pname = params.find('name')
378            param_name = pname.text
379            if pname.tail is not None and pname.tail.strip():
380              parameter_list.append(
381                  (param_type, param_name, pname.tail.strip()))
382            else:
383              parameter_list.append((param_type, param_name))
384          if params.tag == 'proto':
385            for c in params:
386              if c.tag == 'type':
387                cmd_type = c.text
388              if c.tag == 'name':
389                cmd_name = c.text
390                protoset = True
391                command_list.append(cmd_name)
392                return_type_dict[cmd_name] = cmd_type
393        if protoset:
394          param_dict[cmd_name] = parameter_list.copy()
395
396  for exts in root.iter('extensions'):
397    for extension in exts:
398      apiversion = 'VK_VERSION_1_0'
399      if extension.tag == 'extension':
400        extname = extension.get('name')
401        if (extension.get('type') == 'instance' and
402            extension.get('promotedto') is not None):
403          promoted_inst_ext_dict[extname] = \
404              version_2_api_version(extension.get('promotedto'))
405        for req in extension:
406          if req.get('feature') is not None:
407            apiversion = req.get('feature')
408          for commands in req:
409            if commands.tag == 'command':
410              cmd_name = commands.get('name')
411              if cmd_name not in extension_dict:
412                extension_dict[cmd_name] = extname
413                version_dict[cmd_name] = apiversion
414
415  for feature in root.iter('feature'):
416    apiversion = feature.get('name')
417    for req in feature:
418      for command in req:
419        if command.tag == 'command':
420          cmd_name = command.get('name')
421          if cmd_name in command_list:
422            version_dict[cmd_name] = apiversion
423
424  version_code_set = set()
425  for version in version_dict.values():
426    version_code_set.add(version_code(version))
427  for code in sorted(version_code_set):
428    version_code_list.append(code)
429