1#!/usr/bin/python3 -i 2# 3# Copyright (c) 2015-2019 The Khronos Group Inc. 4# Copyright (c) 2015-2019 Valve Corporation 5# Copyright (c) 2015-2019 LunarG, Inc. 6# Copyright (c) 2015-2019 Google Inc. 7# 8# Licensed under the Apache License, Version 2.0 (the "License"); 9# you may not use this file except in compliance with the License. 10# You may obtain a copy of the License at 11# 12# http://www.apache.org/licenses/LICENSE-2.0 13# 14# Unless required by applicable law or agreed to in writing, software 15# distributed under the License is distributed on an "AS IS" BASIS, 16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17# See the License for the specific language governing permissions and 18# limitations under the License. 19# 20# Author: Mark Young <marky@lunarg.com> 21# Author: Mark Lobodzinski <mark@lunarg.com> 22 23import os,re,sys 24import xml.etree.ElementTree as etree 25from generator import * 26from collections import namedtuple 27from common_codegen import * 28 29ADD_INST_CMDS = ['vkCreateInstance', 30 'vkEnumerateInstanceExtensionProperties', 31 'vkEnumerateInstanceLayerProperties', 32 'vkEnumerateInstanceVersion'] 33 34# 35# LayerDispatchTableGeneratorOptions - subclass of GeneratorOptions. 36class LayerDispatchTableGeneratorOptions(GeneratorOptions): 37 def __init__(self, 38 conventions = None, 39 filename = None, 40 directory = '.', 41 apiname = None, 42 profile = None, 43 versions = '.*', 44 emitversions = '.*', 45 defaultExtensions = None, 46 addExtensions = None, 47 removeExtensions = None, 48 emitExtensions = None, 49 sortProcedure = regSortFeatures, 50 prefixText = "", 51 genFuncPointers = True, 52 protectFile = True, 53 protectFeature = True, 54 apicall = '', 55 apientry = '', 56 apientryp = '', 57 indentFuncProto = True, 58 indentFuncPointer = False, 59 alignFuncParam = 0, 60 expandEnumerants = True): 61 GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile, 62 versions, emitversions, defaultExtensions, 63 addExtensions, removeExtensions, emitExtensions, sortProcedure) 64 self.prefixText = prefixText 65 self.prefixText = None 66 self.apicall = apicall 67 self.apientry = apientry 68 self.apientryp = apientryp 69 self.alignFuncParam = alignFuncParam 70 self.expandEnumerants = expandEnumerants 71 72# 73# LayerDispatchTableOutputGenerator - subclass of OutputGenerator. 74# Generates dispatch table helper header files for LVL 75class LayerDispatchTableOutputGenerator(OutputGenerator): 76 """Generate dispatch tables header based on XML element attributes""" 77 def __init__(self, 78 errFile = sys.stderr, 79 warnFile = sys.stderr, 80 diagFile = sys.stdout): 81 OutputGenerator.__init__(self, errFile, warnFile, diagFile) 82 83 # Internal state - accumulators for different inner block text 84 self.ext_instance_dispatch_list = [] # List of extension entries for instance dispatch list 85 self.ext_device_dispatch_list = [] # List of extension entries for device dispatch list 86 self.core_commands = [] # List of CommandData records for core Vulkan commands 87 self.ext_commands = [] # List of CommandData records for extension Vulkan commands 88 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'cdecl']) 89 self.CommandData = namedtuple('CommandData', ['name', 'ext_name', 'ext_type', 'protect', 'return_type', 'handle_type', 'params', 'cdecl']) 90 91 # 92 # Called once at the beginning of each run 93 def beginFile(self, genOpts): 94 OutputGenerator.beginFile(self, genOpts) 95 96 # Initialize members that require the tree 97 self.handle_types = GetHandleTypes(self.registry.tree) 98 99 # User-supplied prefix text, if any (list of strings) 100 if (genOpts.prefixText): 101 for s in genOpts.prefixText: 102 write(s, file=self.outFile) 103 104 # File Comment 105 file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n' 106 file_comment += '// See layer_dispatch_table_generator.py for modifications\n' 107 write(file_comment, file=self.outFile) 108 109 # Copyright Notice 110 copyright = '/*\n' 111 copyright += ' * Copyright (c) 2015-2019 The Khronos Group Inc.\n' 112 copyright += ' * Copyright (c) 2015-2019 Valve Corporation\n' 113 copyright += ' * Copyright (c) 2015-2019 LunarG, Inc.\n' 114 copyright += ' *\n' 115 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n' 116 copyright += ' * you may not use this file except in compliance with the License.\n' 117 copyright += ' * You may obtain a copy of the License at\n' 118 copyright += ' *\n' 119 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n' 120 copyright += ' *\n' 121 copyright += ' * Unless required by applicable law or agreed to in writing, software\n' 122 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n' 123 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' 124 copyright += ' * See the License for the specific language governing permissions and\n' 125 copyright += ' * limitations under the License.\n' 126 copyright += ' *\n' 127 copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n' 128 copyright += ' * Author: Mark Young <marky@lunarg.com>\n' 129 copyright += ' */\n' 130 131 preamble = '' 132 if self.genOpts.filename == 'vk_layer_dispatch_table.h': 133 preamble += '#pragma once\n' 134 preamble += '\n' 135 preamble += 'typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName);\n' 136 137 write(copyright, file=self.outFile) 138 write(preamble, file=self.outFile) 139 140 # 141 # Write generate and write dispatch tables to output file 142 def endFile(self): 143 file_data = '' 144 if self.genOpts.filename == 'vk_layer_dispatch_table.h': 145 file_data += self.OutputLayerInstanceDispatchTable() 146 file_data += self.OutputLayerDeviceDispatchTable() 147 148 write(file_data, file=self.outFile); 149 150 # Finish processing in superclass 151 OutputGenerator.endFile(self) 152 153 def beginFeature(self, interface, emit): 154 # Start processing in superclass 155 OutputGenerator.beginFeature(self, interface, emit) 156 self.featureExtraProtect = GetFeatureProtect(interface) 157 158 enums = interface[0].findall('enum') 159 self.currentExtension = '' 160 161 self.type = interface.get('type') 162 self.num_commands = 0 163 name = interface.get('name') 164 self.currentExtension = name 165 166 # 167 # Process commands, adding to appropriate dispatch tables 168 def genCmd(self, cmdinfo, name, alias): 169 OutputGenerator.genCmd(self, cmdinfo, name, alias) 170 171 # Get first param type 172 params = cmdinfo.elem.findall('param') 173 info = self.getTypeNameTuple(params[0]) 174 175 self.num_commands += 1 176 177 if 'android' not in name: 178 self.AddCommandToDispatchList(self.currentExtension, self.type, name, cmdinfo, info[0]) 179 180 def endFeature(self): 181 # Finish processing in superclass 182 OutputGenerator.endFeature(self) 183 184 # 185 # Retrieve the value of the len tag 186 def getLen(self, param): 187 result = None 188 len = param.attrib.get('len') 189 if len and len != 'null-terminated': 190 # For string arrays, 'len' can look like 'count,null-terminated', 191 # indicating that we have a null terminated array of strings. We 192 # strip the null-terminated from the 'len' field and only return 193 # the parameter specifying the string count 194 if 'null-terminated' in len: 195 result = len.split(',')[0] 196 else: 197 result = len 198 result = str(result).replace('::', '->') 199 return result 200 201 # 202 # Determine if this API should be ignored or added to the instance or device dispatch table 203 def AddCommandToDispatchList(self, extension_name, extension_type, name, cmdinfo, handle_type): 204 return_type = cmdinfo.elem.find('proto/type') 205 if (return_type is not None and return_type.text == 'void'): 206 return_type = None 207 208 cmd_params = [] 209 210 # Generate a list of commands for use in printing the necessary 211 # core instance terminator prototypes 212 params = cmdinfo.elem.findall('param') 213 lens = set() 214 for param in params: 215 len = self.getLen(param) 216 if len: 217 lens.add(len) 218 paramsInfo = [] 219 for param in params: 220 paramInfo = self.getTypeNameTuple(param) 221 param_type = paramInfo[0] 222 param_name = paramInfo[1] 223 param_cdecl = self.makeCParamDecl(param, 0) 224 cmd_params.append(self.CommandParam(type=param_type, name=param_name, 225 cdecl=param_cdecl)) 226 227 if handle_type in self.handle_types and handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice': 228 # The Core Vulkan code will be wrapped in a feature called VK_VERSION_#_# 229 # For example: VK_VERSION_1_0 wraps the core 1.0 Vulkan functionality 230 if 'VK_VERSION_' in extension_name: 231 self.core_commands.append( 232 self.CommandData(name=name, ext_name=extension_name, 233 ext_type='device', 234 protect=self.featureExtraProtect, 235 return_type = return_type, 236 handle_type = handle_type, 237 params = cmd_params, 238 cdecl=self.makeCDecls(cmdinfo.elem)[0])) 239 else: 240 self.ext_device_dispatch_list.append((name, self.featureExtraProtect)) 241 self.ext_commands.append( 242 self.CommandData(name=name, ext_name=extension_name, 243 ext_type=extension_type, 244 protect=self.featureExtraProtect, 245 return_type = return_type, 246 handle_type = handle_type, 247 params = cmd_params, 248 cdecl=self.makeCDecls(cmdinfo.elem)[0])) 249 else: 250 # The Core Vulkan code will be wrapped in a feature called VK_VERSION_#_# 251 # For example: VK_VERSION_1_0 wraps the core 1.0 Vulkan functionality 252 if 'VK_VERSION_' in extension_name: 253 self.core_commands.append( 254 self.CommandData(name=name, ext_name=extension_name, 255 ext_type='instance', 256 protect=self.featureExtraProtect, 257 return_type = return_type, 258 handle_type = handle_type, 259 params = cmd_params, 260 cdecl=self.makeCDecls(cmdinfo.elem)[0])) 261 262 else: 263 self.ext_instance_dispatch_list.append((name, self.featureExtraProtect)) 264 self.ext_commands.append( 265 self.CommandData(name=name, ext_name=extension_name, 266 ext_type=extension_type, 267 protect=self.featureExtraProtect, 268 return_type = return_type, 269 handle_type = handle_type, 270 params = cmd_params, 271 cdecl=self.makeCDecls(cmdinfo.elem)[0])) 272 273 # 274 # Retrieve the type and name for a parameter 275 def getTypeNameTuple(self, param): 276 type = '' 277 name = '' 278 for elem in param: 279 if elem.tag == 'type': 280 type = noneStr(elem.text) 281 elif elem.tag == 'name': 282 name = noneStr(elem.text) 283 return (type, name) 284 285 # 286 # Create a layer instance dispatch table from the appropriate list and return it as a string 287 def OutputLayerInstanceDispatchTable(self): 288 commands = [] 289 table = '' 290 cur_extension_name = '' 291 292 table += '// Instance function pointer dispatch table\n' 293 table += 'typedef struct VkLayerInstanceDispatchTable_ {\n' 294 295 # First add in an entry for GetPhysicalDeviceProcAddr. This will not 296 # ever show up in the XML or header, so we have to manually add it. 297 table += ' // Manually add in GetPhysicalDeviceProcAddr entry\n' 298 table += ' PFN_GetPhysicalDeviceProcAddr GetPhysicalDeviceProcAddr;\n' 299 300 for x in range(0, 2): 301 if x == 0: 302 commands = self.core_commands 303 else: 304 commands = self.ext_commands 305 306 for cur_cmd in commands: 307 is_inst_handle_type = cur_cmd.name in ADD_INST_CMDS or cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice' 308 if is_inst_handle_type: 309 310 if cur_cmd.ext_name != cur_extension_name: 311 if 'VK_VERSION_' in cur_cmd.ext_name: 312 table += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] 313 else: 314 table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name 315 cur_extension_name = cur_cmd.ext_name 316 317 # Remove 'vk' from proto name 318 base_name = cur_cmd.name[2:] 319 320 if cur_cmd.protect is not None: 321 table += '#ifdef %s\n' % cur_cmd.protect 322 323 table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) 324 325 if cur_cmd.protect is not None: 326 table += '#endif // %s\n' % cur_cmd.protect 327 328 table += '} VkLayerInstanceDispatchTable;\n\n' 329 return table 330 331 # 332 # Create a layer device dispatch table from the appropriate list and return it as a string 333 def OutputLayerDeviceDispatchTable(self): 334 commands = [] 335 table = '' 336 cur_extension_name = '' 337 338 table += '// Device function pointer dispatch table\n' 339 table += 'typedef struct VkLayerDispatchTable_ {\n' 340 341 for x in range(0, 2): 342 if x == 0: 343 commands = self.core_commands 344 else: 345 commands = self.ext_commands 346 347 for cur_cmd in commands: 348 is_inst_handle_type = cur_cmd.name in ADD_INST_CMDS or cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice' 349 if not is_inst_handle_type: 350 351 if cur_cmd.ext_name != cur_extension_name: 352 if 'VK_VERSION_' in cur_cmd.ext_name: 353 table += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] 354 else: 355 table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name 356 cur_extension_name = cur_cmd.ext_name 357 358 # Remove 'vk' from proto name 359 base_name = cur_cmd.name[2:] 360 361 if cur_cmd.protect is not None: 362 table += '#ifdef %s\n' % cur_cmd.protect 363 364 table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) 365 366 if cur_cmd.protect is not None: 367 table += '#endif // %s\n' % cur_cmd.protect 368 369 table += '} VkLayerDispatchTable;\n\n' 370 return table 371