1#!/usr/bin/python3 -i 2# 3# Copyright (c) 2015-2020 The Khronos Group Inc. 4# Copyright (c) 2015-2020 Valve Corporation 5# Copyright (c) 2015-2020 LunarG, Inc. 6# 7# Licensed under the Apache License, Version 2.0 (the "License"); 8# you may not use this file except in compliance with the License. 9# You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, software 14# distributed under the License is distributed on an "AS IS" BASIS, 15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16# See the License for the specific language governing permissions and 17# limitations under the License. 18# 19# Author: Mark Lobodzinski <mark@lunarg.com> 20 21import os,re,sys,string,json 22import xml.etree.ElementTree as etree 23from generator import * 24from collections import namedtuple 25from common_codegen import * 26 27# This is a workaround to use a Python 2.7 and 3.x compatible syntax 28from io import open 29 30class BestPracticesOutputGeneratorOptions(GeneratorOptions): 31 def __init__(self, 32 conventions = None, 33 filename = None, 34 directory = '.', 35 genpath = None, 36 apiname = None, 37 profile = None, 38 versions = '.*', 39 emitversions = '.*', 40 defaultExtensions = None, 41 addExtensions = None, 42 removeExtensions = None, 43 emitExtensions = None, 44 sortProcedure = regSortFeatures, 45 prefixText = "", 46 genFuncPointers = True, 47 protectFile = True, 48 protectFeature = True, 49 apicall = '', 50 apientry = '', 51 apientryp = '', 52 indentFuncProto = True, 53 indentFuncPointer = False, 54 alignFuncParam = 0, 55 expandEnumerants = True): 56 GeneratorOptions.__init__(self, 57 conventions = conventions, 58 filename = filename, 59 directory = directory, 60 genpath = genpath, 61 apiname = apiname, 62 profile = profile, 63 versions = versions, 64 emitversions = emitversions, 65 defaultExtensions = defaultExtensions, 66 addExtensions = addExtensions, 67 removeExtensions = removeExtensions, 68 emitExtensions = emitExtensions, 69 sortProcedure = sortProcedure) 70 self.prefixText = prefixText 71 self.genFuncPointers = genFuncPointers 72 self.protectFile = protectFile 73 self.protectFeature = protectFeature 74 self.apicall = apicall 75 self.apientry = apientry 76 self.apientryp = apientryp 77 self.indentFuncProto = indentFuncProto 78 self.indentFuncPointer = indentFuncPointer 79 self.alignFuncParam = alignFuncParam 80 self.expandEnumerants = expandEnumerants 81# 82# BestPracticesOutputGenerator(errFile, warnFile, diagFile) 83class BestPracticesOutputGenerator(OutputGenerator): 84 def __init__(self, 85 errFile = sys.stderr, 86 warnFile = sys.stderr, 87 diagFile = sys.stdout): 88 OutputGenerator.__init__(self, errFile, warnFile, diagFile) 89 # Commands which are not autogenerated but still intercepted 90 self.no_autogen_list = [ 91 'vkEnumerateInstanceVersion', 92 'vkCreateValidationCacheEXT', 93 'vkDestroyValidationCacheEXT', 94 'vkMergeValidationCachesEXT', 95 'vkGetValidationCacheDataEXT', 96 ] 97 # Commands that require an extra parameter for state sharing between validate/record steps 98 self.extra_parameter_list = [ 99 "vkCreateShaderModule", 100 "vkCreateGraphicsPipelines", 101 "vkCreateComputePipelines", 102 "vkAllocateDescriptorSets", 103 "vkCreateRayTracingPipelinesNV", 104 "vkCreateRayTracingPipelinesKHR", 105 ] 106 # Commands that have a manually written post-call-record step which needs to be called from the autogen'd fcn 107 self.manual_postcallrecord_list = [ 108 'vkAllocateDescriptorSets', 109 'vkAllocateMemory', 110 'vkQueuePresentKHR', 111 'vkQueueBindSparse', 112 'vkCreateGraphicsPipelines', 113 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR', 114 'vkGetPhysicalDeviceSurfaceCapabilities2KHR', 115 'vkGetPhysicalDeviceSurfaceCapabilities2EXT', 116 'vkGetPhysicalDeviceSurfacePresentModesKHR', 117 'vkGetPhysicalDeviceSurfaceFormatsKHR', 118 'vkGetPhysicalDeviceSurfaceFormats2KHR', 119 'vkGetPhysicalDeviceDisplayPlanePropertiesKHR', 120 'vkCreateSwapchainKHR', 121 'vkGetSwapchainImagesKHR', 122 'vkEnumeratePhysicalDevices', 123 'vkCreateDevice', 124 ] 125 126 self.extension_info = dict() 127 # 128 # Separate content for validation source and header files 129 def otwrite(self, dest, formatstring): 130 if 'best_practices.h' in self.genOpts.filename and (dest == 'hdr' or dest == 'both'): 131 write(formatstring, file=self.outFile) 132 elif 'best_practices.cpp' in self.genOpts.filename and (dest == 'cpp' or dest == 'both'): 133 write(formatstring, file=self.outFile) 134 # 135 # Called at beginning of processing as file is opened 136 def beginFile(self, genOpts): 137 OutputGenerator.beginFile(self, genOpts) 138 139 header_file = (genOpts.filename == 'best_practices.h') 140 source_file = (genOpts.filename == 'best_practices.cpp') 141 142 if not header_file and not source_file: 143 print("Error: Output Filenames have changed, update generator source.\n") 144 sys.exit(1) 145 146 # File Comment 147 file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n' 148 file_comment += '// See best_practices_generator.py for modifications\n' 149 self.otwrite('both', file_comment) 150 # Copyright Statement 151 copyright = '' 152 copyright += '\n' 153 copyright += '/***************************************************************************\n' 154 copyright += ' *\n' 155 copyright += ' * Copyright (c) 2015-2020 The Khronos Group Inc.\n' 156 copyright += ' * Copyright (c) 2015-2020 Valve Corporation\n' 157 copyright += ' * Copyright (c) 2015-2020 LunarG, Inc.\n' 158 copyright += ' *\n' 159 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n' 160 copyright += ' * you may not use this file except in compliance with the License.\n' 161 copyright += ' * You may obtain a copy of the License at\n' 162 copyright += ' *\n' 163 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n' 164 copyright += ' *\n' 165 copyright += ' * Unless required by applicable law or agreed to in writing, software\n' 166 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n' 167 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' 168 copyright += ' * See the License for the specific language governing permissions and\n' 169 copyright += ' * limitations under the License.\n' 170 copyright += ' *\n' 171 copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n' 172 copyright += ' *\n' 173 copyright += ' ****************************************************************************/\n' 174 self.otwrite('both', copyright) 175 self.newline() 176 self.otwrite('cpp', '#include "chassis.h"') 177 self.otwrite('cpp', '#include "best_practices_validation.h"') 178 # 179 # Now that the data is all collected and complete, generate and output the object validation routines 180 def endFile(self): 181 self.newline() 182 # Actually write the interface to the output file. 183 if (self.emit): 184 self.newline() 185 if self.featureExtraProtect is not None: 186 prot = '#ifdef %s' % self.featureExtraProtect 187 self.otwrite('both', '%s' % prot) 188 if (self.featureExtraProtect is not None): 189 prot = '\n#endif // %s', self.featureExtraProtect 190 self.otwrite('both', prot) 191 else: 192 self.otwrite('both', '\n') 193 ext_deprecation_data = 'const std::unordered_map<std::string, DeprecationData> deprecated_extensions = {\n' 194 for ext in sorted(self.extension_info): 195 ext_data = self.extension_info[ext] 196 ext_deprecation_data += ' {"%s", {kExt%s, "%s"}},\n' % (ext, ext_data[0], ext_data[1]) 197 ext_deprecation_data += '};\n' 198 self.otwrite('hdr', ext_deprecation_data) 199 OutputGenerator.endFile(self) 200 # 201 # Processing point at beginning of each extension definition 202 def beginFeature(self, interface, emit): 203 OutputGenerator.beginFeature(self, interface, emit) 204 self.featureExtraProtect = GetFeatureProtect(interface) 205 ext_name = interface.attrib.get('name') 206 ext_promoted = (interface.attrib.get('promotedto')) 207 ext_obsoleted = interface.attrib.get('obsoletedby') 208 ext_deprecated = interface.attrib.get('deprecatedby') 209 if ext_promoted is not None: 210 reason = 'Promoted' 211 target = ext_promoted 212 elif ext_obsoleted is not None: 213 reason = 'Obsoleted' 214 target = ext_obsoleted 215 elif ext_deprecated is not None: 216 reason = 'Deprecated' 217 target = ext_deprecated 218 else: 219 return 220 self.extension_info[ext_name] = [reason, target] 221 # 222 # Retrieve the type and name for a parameter 223 def getTypeNameTuple(self, param): 224 type = '' 225 name = '' 226 for elem in param: 227 if elem.tag == 'type': 228 type = noneStr(elem.text) 229 elif elem.tag == 'name': 230 name = noneStr(elem.text) 231 return (type, name) 232 # 233 # Capture command parameter info needed to create, destroy, and validate objects 234 def genCmd(self, cmdinfo, cmdname, alias): 235 OutputGenerator.genCmd(self, cmdinfo, cmdname, alias) 236 intercept = '' 237 if cmdname in self.no_autogen_list: 238 intercept += '// Skipping %s for autogen as it has a manually created custom function or ignored.\n' % cmdname 239 self.otwrite('cpp', intercept) 240 return 241 cdecl=self.makeCDecls(cmdinfo.elem)[0] 242 decls = self.makeCDecls(cmdinfo.elem) 243 typedef = decls[1] 244 typedef = typedef.split(')',1)[1] 245 pre_decl = decls[0][:-1] 246 pre_decl = pre_decl.split("VKAPI_CALL ")[1] 247 pre_decl = pre_decl.replace(')', ',\n VkResult result)') 248 if cmdname in self.extra_parameter_list: 249 pre_decl = pre_decl.replace(')', ',\n void* state_data)') 250 pre_decl = pre_decl.replace(')', ') {\n') 251 pre_decl = 'void BestPractices::PostCallRecord' + pre_decl[2:] 252 type = cdecl.split(' ')[1] 253 if type == 'VkResult': 254 error_codes = cmdinfo.elem.attrib.get('errorcodes') 255 success_codes = cmdinfo.elem.attrib.get('successcodes') 256 success_codes = success_codes.replace('VK_SUCCESS,','') 257 success_codes = success_codes.replace('VK_SUCCESS','') 258 if error_codes is None and success_codes == '': 259 return 260 if self.featureExtraProtect is not None: 261 self.otwrite('both', '#ifdef %s\n' % self.featureExtraProtect) 262 func_decl = pre_decl.replace(' {',';\n'); 263 func_decl = func_decl.replace('BestPractices::', '') 264 self.otwrite('hdr', func_decl) 265 intercept += pre_decl 266 params_text = '' 267 params = cmdinfo.elem.findall('param') 268 for param in params: 269 paramtype,paramname = self.getTypeNameTuple(param) 270 params_text += '%s, ' % paramname 271 params_text = params_text + 'result, ' 272 if cmdname in self.extra_parameter_list: 273 params_text += 'state_data, ' 274 params_text = params_text[:-2] + ');\n' 275 intercept += ' ValidationStateTracker::PostCallRecord'+cmdname[2:] + '(' + params_text 276 if cmdname in self.manual_postcallrecord_list: 277 intercept += ' ManualPostCallRecord'+cmdname[2:] + '(' + params_text 278 intercept += ' if (result != VK_SUCCESS) {\n' 279 if error_codes is not None: 280 intercept += ' static const std::vector<VkResult> error_codes = {%s};\n' % error_codes 281 else: 282 intercept += ' static const std::vector<VkResult> error_codes = {};\n' 283 if success_codes is not None: 284 intercept += ' static const std::vector<VkResult> success_codes = {%s};\n' % success_codes 285 else: 286 intercept += ' static const std::vector<VkResult> success_codes = {};\n' 287 intercept += ' ValidateReturnCodes("%s", result, error_codes, success_codes);\n' % cmdname 288 intercept += ' }\n' 289 intercept += '}\n' 290 self.otwrite('cpp', intercept) 291 if self.featureExtraProtect is not None: 292 self.otwrite('both', '#endif // %s\n' % self.featureExtraProtect) 293