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 Lobodzinski <mark@lunarg.com> 21 22import os,re,sys 23import xml.etree.ElementTree as etree 24from generator import * 25from collections import namedtuple 26from common_codegen import * 27 28# 29# DispatchTableHelperOutputGeneratorOptions - subclass of GeneratorOptions. 30class DispatchTableHelperOutputGeneratorOptions(GeneratorOptions): 31 def __init__(self, 32 filename = None, 33 directory = '.', 34 apiname = None, 35 profile = None, 36 versions = '.*', 37 emitversions = '.*', 38 defaultExtensions = None, 39 addExtensions = None, 40 removeExtensions = None, 41 emitExtensions = None, 42 sortProcedure = regSortFeatures, 43 prefixText = "", 44 genFuncPointers = True, 45 apicall = '', 46 apientry = '', 47 apientryp = '', 48 alignFuncParam = 0, 49 expandEnumerants = True): 50 GeneratorOptions.__init__(self, filename, directory, apiname, profile, 51 versions, emitversions, defaultExtensions, 52 addExtensions, removeExtensions, emitExtensions, sortProcedure) 53 self.prefixText = prefixText 54 self.genFuncPointers = genFuncPointers 55 self.prefixText = None 56 self.apicall = apicall 57 self.apientry = apientry 58 self.apientryp = apientryp 59 self.alignFuncParam = alignFuncParam 60# 61# DispatchTableHelperOutputGenerator - subclass of OutputGenerator. 62# Generates dispatch table helper header files for LVL 63class DispatchTableHelperOutputGenerator(OutputGenerator): 64 """Generate dispatch table helper header based on XML element attributes""" 65 def __init__(self, 66 errFile = sys.stderr, 67 warnFile = sys.stderr, 68 diagFile = sys.stdout): 69 OutputGenerator.__init__(self, errFile, warnFile, diagFile) 70 # Internal state - accumulators for different inner block text 71 self.instance_dispatch_list = [] # List of entries for instance dispatch list 72 self.device_dispatch_list = [] # List of entries for device dispatch list 73 self.dev_ext_stub_list = [] # List of stub functions for device extension functions 74 self.device_extension_list = [] # List of device extension functions 75 self.device_stub_list = [] # List of device functions with stubs (promoted or extensions) 76 self.extension_type = '' 77 # 78 # Called once at the beginning of each run 79 def beginFile(self, genOpts): 80 OutputGenerator.beginFile(self, genOpts) 81 write("#pragma once", file=self.outFile) 82 # User-supplied prefix text, if any (list of strings) 83 if (genOpts.prefixText): 84 for s in genOpts.prefixText: 85 write(s, file=self.outFile) 86 # File Comment 87 file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n' 88 file_comment += '// See dispatch_helper_generator.py for modifications\n' 89 write(file_comment, file=self.outFile) 90 # Copyright Notice 91 copyright = '/*\n' 92 copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n' 93 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n' 94 copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n' 95 copyright += ' *\n' 96 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n' 97 copyright += ' * you may not use this file except in compliance with the License.\n' 98 copyright += ' * You may obtain a copy of the License at\n' 99 copyright += ' *\n' 100 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n' 101 copyright += ' *\n' 102 copyright += ' * Unless required by applicable law or agreed to in writing, software\n' 103 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n' 104 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' 105 copyright += ' * See the License for the specific language governing permissions and\n' 106 copyright += ' * limitations under the License.\n' 107 copyright += ' *\n' 108 copyright += ' * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>\n' 109 copyright += ' * Author: Jon Ashburn <jon@lunarg.com>\n' 110 copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n' 111 copyright += ' */\n' 112 113 preamble = '' 114 preamble += '#include <vulkan/vulkan.h>\n' 115 preamble += '#include <vulkan/vk_layer.h>\n' 116 preamble += '#include <cstring>\n' 117 preamble += '#include <string>\n' 118 preamble += '#include <unordered_set>\n' 119 preamble += '#include <unordered_map>\n' 120 preamble += '#include "vk_layer_dispatch_table.h"\n' 121 122 write(copyright, file=self.outFile) 123 write(preamble, file=self.outFile) 124 # 125 # Write generate and write dispatch tables to output file 126 def endFile(self): 127 ext_enabled_fcn = '' 128 device_table = '' 129 instance_table = '' 130 131 ext_enabled_fcn += self.OutputExtEnabledFunction() 132 device_table += self.OutputDispatchTableHelper('device') 133 instance_table += self.OutputDispatchTableHelper('instance') 134 135 for stub in self.dev_ext_stub_list: 136 write(stub, file=self.outFile) 137 write("\n\n", file=self.outFile) 138 write(ext_enabled_fcn, file=self.outFile) 139 write("\n", file=self.outFile) 140 write(device_table, file=self.outFile); 141 write("\n", file=self.outFile) 142 write(instance_table, file=self.outFile); 143 144 # Finish processing in superclass 145 OutputGenerator.endFile(self) 146 # 147 # Processing at beginning of each feature or extension 148 def beginFeature(self, interface, emit): 149 OutputGenerator.beginFeature(self, interface, emit) 150 self.featureExtraProtect = GetFeatureProtect(interface) 151 self.extension_type = interface.get('type') 152 153 # 154 # Process commands, adding to appropriate dispatch tables 155 def genCmd(self, cmdinfo, name, alias): 156 OutputGenerator.genCmd(self, cmdinfo, name, alias) 157 158 avoid_entries = ['vkCreateInstance', 159 'vkCreateDevice'] 160 # Get first param type 161 params = cmdinfo.elem.findall('param') 162 info = self.getTypeNameTuple(params[0]) 163 164 if name not in avoid_entries: 165 self.AddCommandToDispatchList(name, info[0], self.featureExtraProtect, cmdinfo) 166 167 # 168 # Determine if this API should be ignored or added to the instance or device dispatch table 169 def AddCommandToDispatchList(self, name, handle_type, protect, cmdinfo): 170 handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']") 171 if handle is None: 172 return 173 if handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice' and name != 'vkGetInstanceProcAddr': 174 self.device_dispatch_list.append((name, self.featureExtraProtect)) 175 extension = "VK_VERSION" not in self.featureName 176 promoted = not extension and "VK_VERSION_1_0" != self.featureName 177 if promoted or extension: 178 self.device_stub_list.append([name, self.featureName]) 179 if extension: 180 self.device_extension_list.append([name, self.featureName]) 181 # Build up stub function 182 return_type = '' 183 decl = self.makeCDecls(cmdinfo.elem)[1] 184 if decl.startswith('typedef VkResult'): 185 return_type = 'return VK_SUCCESS;' 186 elif decl.startswith('typedef VkDeviceAddress'): 187 return_type = 'return 0;' 188 elif decl.startswith('typedef uint32_t'): 189 return_type = 'return 0;' 190 pre_decl, decl = decl.split('*PFN_vk') 191 pre_decl = pre_decl.replace('typedef ', '') 192 pre_decl = pre_decl.split(' (')[0] 193 decl = decl.replace(')(', '(') 194 decl = 'static VKAPI_ATTR ' + pre_decl + ' VKAPI_CALL Stub' + decl 195 func_body = ' { ' + return_type + ' };' 196 decl = decl.replace (';', func_body) 197 if self.featureExtraProtect is not None: 198 self.dev_ext_stub_list.append('#ifdef %s' % self.featureExtraProtect) 199 self.dev_ext_stub_list.append(decl) 200 if self.featureExtraProtect is not None: 201 self.dev_ext_stub_list.append('#endif // %s' % self.featureExtraProtect) 202 else: 203 self.instance_dispatch_list.append((name, self.featureExtraProtect)) 204 return 205 # 206 # Retrieve the type and name for a parameter 207 def getTypeNameTuple(self, param): 208 type = '' 209 name = '' 210 for elem in param: 211 if elem.tag == 'type': 212 type = noneStr(elem.text) 213 elif elem.tag == 'name': 214 name = noneStr(elem.text) 215 return (type, name) 216 # 217 # Output a function that'll determine if an extension is in the enabled list 218 def OutputExtEnabledFunction(self): 219 ##extension_functions = dict(self.device_dispatch_list) 220 ext_fcn = '' 221 # First, write out our static data structure -- map of all APIs that are part of extensions to their extension. 222 ext_fcn += 'const std::unordered_map<std::string, std::string> api_extension_map {\n' 223 for extn in self.device_extension_list: 224 ext_fcn += ' {"%s", "%s"},\n' % (extn[0], extn[1]) 225 ext_fcn += '};\n\n' 226 ext_fcn += '// Using the above code-generated map of APINames-to-parent extension names, this function will:\n' 227 ext_fcn += '// o Determine if the API has an associated extension\n' 228 ext_fcn += '// o If it does, determine if that extension name is present in the passed-in set of enabled_ext_names \n' 229 ext_fcn += '// If the APIname has no parent extension, OR its parent extension name is IN the set, return TRUE, else FALSE\n' 230 ext_fcn += 'static inline bool ApiParentExtensionEnabled(const std::string api_name, const std::unordered_set<std::string> &enabled_ext_names) {\n' 231 ext_fcn += ' auto has_ext = api_extension_map.find(api_name);\n' 232 ext_fcn += ' // Is this API part of an extension?\n' 233 ext_fcn += ' if (has_ext != api_extension_map.end()) {\n' 234 ext_fcn += ' // Was the extension for this API enabled in the CreateDevice call?\n' 235 ext_fcn += ' if (enabled_ext_names.find(has_ext->second) == enabled_ext_names.end()) {\n' 236 ext_fcn += ' return false;\n' 237 ext_fcn += ' }\n' 238 ext_fcn += ' }\n' 239 ext_fcn += ' return true;\n' 240 ext_fcn += '}\n' 241 return ext_fcn 242 # 243 # Create a dispatch table from the appropriate list and return it as a string 244 def OutputDispatchTableHelper(self, table_type): 245 entries = [] 246 table = '' 247 if table_type == 'device': 248 entries = self.device_dispatch_list 249 table += 'static inline void layer_init_device_dispatch_table(VkDevice device, VkLayerDispatchTable *table, PFN_vkGetDeviceProcAddr gpa) {\n' 250 table += ' memset(table, 0, sizeof(*table));\n' 251 table += ' // Device function pointers\n' 252 else: 253 entries = self.instance_dispatch_list 254 table += 'static inline void layer_init_instance_dispatch_table(VkInstance instance, VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa) {\n' 255 table += ' memset(table, 0, sizeof(*table));\n' 256 table += ' // Instance function pointers\n' 257 258 stubbed_functions = dict(self.device_stub_list) 259 for item in entries: 260 # Remove 'vk' from proto name 261 base_name = item[0][2:] 262 263 if item[1] is not None: 264 table += '#ifdef %s\n' % item[1] 265 266 # If we're looking for the proc we are passing in, just point the table to it. This fixes the issue where 267 # a layer overrides the function name for the loader. 268 if ('device' in table_type and base_name == 'GetDeviceProcAddr'): 269 table += ' table->GetDeviceProcAddr = gpa;\n' 270 elif ('device' not in table_type and base_name == 'GetInstanceProcAddr'): 271 table += ' table->GetInstanceProcAddr = gpa;\n' 272 else: 273 table += ' table->%s = (PFN_%s) gpa(%s, "%s");\n' % (base_name, item[0], table_type, item[0]) 274 if 'device' in table_type and item[0] in stubbed_functions: 275 stub_check = ' if (table->%s == nullptr) { table->%s = (PFN_%s)Stub%s; }\n' % (base_name, base_name, item[0], base_name) 276 table += stub_check 277 if item[1] is not None: 278 table += '#endif // %s\n' % item[1] 279 280 table += '}' 281 return table 282