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