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