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