blob: 5106bb46dcb6f6f14d9a96a94914814bae482208 [file] [log] [blame]
Mark Lobodzinskid1461482017-07-18 13:56:09 -06001#!/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,string
23import xml.etree.ElementTree as etree
24from generator import *
25from collections import namedtuple
26from vuid_mapping import *
27
28# ObjectTrackerGeneratorOptions - subclass of GeneratorOptions.
29#
30# Adds options used by ObjectTrackerOutputGenerator objects during
31# object_tracker layer generation.
32#
33# Additional members
34# prefixText - list of strings to prefix generated header with
35# (usually a copyright statement + calling convention macros).
36# protectFile - True if multiple inclusion protection should be
37# generated (based on the filename) around the entire header.
38# protectFeature - True if #ifndef..#endif protection should be
39# generated around a feature interface in the header file.
40# genFuncPointers - True if function pointer typedefs should be
41# generated
42# protectProto - If conditional protection should be generated
43# around prototype declarations, set to either '#ifdef'
44# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
45# to require opt-out (#ifndef protectProtoStr). Otherwise
46# set to None.
47# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
48# declarations, if protectProto is set
49# apicall - string to use for the function declaration prefix,
50# such as APICALL on Windows.
51# apientry - string to use for the calling convention macro,
52# in typedefs, such as APIENTRY.
53# apientryp - string to use for the calling convention macro
54# in function pointer typedefs, such as APIENTRYP.
55# indentFuncProto - True if prototype declarations should put each
56# parameter on a separate line
57# indentFuncPointer - True if typedefed function pointers should put each
58# parameter on a separate line
59# alignFuncParam - if nonzero and parameters are being put on a
60# separate line, align parameter names at the specified column
61class ObjectTrackerGeneratorOptions(GeneratorOptions):
62 def __init__(self,
63 filename = None,
64 directory = '.',
65 apiname = None,
66 profile = None,
67 versions = '.*',
68 emitversions = '.*',
69 defaultExtensions = None,
70 addExtensions = None,
71 removeExtensions = None,
72 sortProcedure = regSortFeatures,
73 prefixText = "",
74 genFuncPointers = True,
75 protectFile = True,
76 protectFeature = True,
77 protectProto = None,
78 protectProtoStr = None,
79 apicall = '',
80 apientry = '',
81 apientryp = '',
82 indentFuncProto = True,
83 indentFuncPointer = False,
84 alignFuncParam = 0):
85 GeneratorOptions.__init__(self, filename, directory, apiname, profile,
86 versions, emitversions, defaultExtensions,
87 addExtensions, removeExtensions, sortProcedure)
88 self.prefixText = prefixText
89 self.genFuncPointers = genFuncPointers
90 self.protectFile = protectFile
91 self.protectFeature = protectFeature
92 self.protectProto = protectProto
93 self.protectProtoStr = protectProtoStr
94 self.apicall = apicall
95 self.apientry = apientry
96 self.apientryp = apientryp
97 self.indentFuncProto = indentFuncProto
98 self.indentFuncPointer = indentFuncPointer
99 self.alignFuncParam = alignFuncParam
100
101# ObjectTrackerOutputGenerator - subclass of OutputGenerator.
102# Generates object_tracker layer object validation code
103#
104# ---- methods ----
105# ObjectTrackerOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state.
106# ---- methods overriding base class ----
107# beginFile(genOpts)
108# endFile()
109# beginFeature(interface, emit)
110# endFeature()
111# genCmd(cmdinfo)
112# genStruct()
113# genType()
114class ObjectTrackerOutputGenerator(OutputGenerator):
115 """Generate ObjectTracker code based on XML element attributes"""
116 # This is an ordered list of sections in the header file.
117 ALL_SECTIONS = ['command']
118 def __init__(self,
119 errFile = sys.stderr,
120 warnFile = sys.stderr,
121 diagFile = sys.stdout):
122 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
123 self.INDENT_SPACES = 4
124 self.intercepts = []
125 self.instance_extensions = []
126 self.device_extensions = []
127 # Commands which are not autogenerated but still intercepted
128 self.no_autogen_list = [
129 'vkDestroyInstance',
130 'vkDestroyDevice',
131 'vkUpdateDescriptorSets',
132 'vkDestroyDebugReportCallbackEXT',
133 'vkDebugReportMessageEXT',
134 'vkGetPhysicalDeviceQueueFamilyProperties',
135 'vkFreeCommandBuffers',
136 'vkDestroySwapchainKHR',
137 'vkDestroyDescriptorPool',
138 'vkDestroyCommandPool',
139 'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
140 'vkResetDescriptorPool',
141 'vkBeginCommandBuffer',
142 'vkCreateDebugReportCallbackEXT',
143 'vkEnumerateInstanceLayerProperties',
144 'vkEnumerateDeviceLayerProperties',
145 'vkEnumerateInstanceExtensionProperties',
146 'vkEnumerateDeviceExtensionProperties',
147 'vkCreateDevice',
148 'vkCreateInstance',
149 'vkEnumeratePhysicalDevices',
150 'vkAllocateCommandBuffers',
151 'vkAllocateDescriptorSets',
152 'vkFreeDescriptorSets',
Tony Barbour2fd0c2c2017-08-08 12:51:33 -0600153 'vkCmdPushDescriptorSetKHR',
Mark Lobodzinskid1461482017-07-18 13:56:09 -0600154 'vkDebugMarkerSetObjectNameEXT',
155 'vkGetPhysicalDeviceProcAddr',
156 'vkGetDeviceProcAddr',
157 'vkGetInstanceProcAddr',
158 'vkEnumerateInstanceExtensionProperties',
159 'vkEnumerateInstanceLayerProperties',
160 'vkEnumerateDeviceLayerProperties',
161 'vkGetDeviceProcAddr',
162 'vkGetInstanceProcAddr',
163 'vkEnumerateDeviceExtensionProperties',
164 'vk_layerGetPhysicalDeviceProcAddr',
165 'vkNegotiateLoaderLayerInterfaceVersion',
166 'vkCreateComputePipelines',
167 'vkGetDeviceQueue',
168 'vkGetSwapchainImagesKHR',
169 ]
170 # These VUIDS are not implicit, but are best handled in this layer. Codegen for vkDestroy calls will generate a key
171 # which is translated here into a good VU. Saves ~40 checks.
172 self.manual_vuids = dict()
173 self.manual_vuids = {
174 "fence-compatalloc": "VALIDATION_ERROR_24e008c2",
175 "fence-nullalloc": "VALIDATION_ERROR_24e008c4",
176 "event-compatalloc": "VALIDATION_ERROR_24c008f4",
177 "event-nullalloc": "VALIDATION_ERROR_24c008f6",
178 "buffer-compatalloc": "VALIDATION_ERROR_23c00736",
179 "buffer-nullalloc": "VALIDATION_ERROR_23c00738",
180 "image-compatalloc": "VALIDATION_ERROR_252007d2",
181 "image-nullalloc": "VALIDATION_ERROR_252007d4",
182 "shaderModule-compatalloc": "VALIDATION_ERROR_26a00888",
183 "shaderModule-nullalloc": "VALIDATION_ERROR_26a0088a",
184 "pipeline-compatalloc": "VALIDATION_ERROR_25c005fc",
185 "pipeline-nullalloc": "VALIDATION_ERROR_25c005fe",
186 "sampler-compatalloc": "VALIDATION_ERROR_26600876",
187 "sampler-nullalloc": "VALIDATION_ERROR_26600878",
188 "renderPass-compatalloc": "VALIDATION_ERROR_264006d4",
189 "renderPass-nullalloc": "VALIDATION_ERROR_264006d6",
190 "descriptorUpdateTemplate-compatalloc": "VALIDATION_ERROR_248002c8",
191 "descriptorUpdateTemplate-nullalloc": "VALIDATION_ERROR_248002ca",
192 "imageView-compatalloc": "VALIDATION_ERROR_25400806",
193 "imageView-nullalloc": "VALIDATION_ERROR_25400808",
194 "pipelineCache-compatalloc": "VALIDATION_ERROR_25e00606",
195 "pipelineCache-nullalloc": "VALIDATION_ERROR_25e00608",
196 "pipelineLayout-compatalloc": "VALIDATION_ERROR_26000256",
197 "pipelineLayout-nullalloc": "VALIDATION_ERROR_26000258",
198 "descriptorSetLayout-compatalloc": "VALIDATION_ERROR_24600238",
199 "descriptorSetLayout-nullalloc": "VALIDATION_ERROR_2460023a",
200 "semaphore-compatalloc": "VALIDATION_ERROR_268008e4",
201 "semaphore-nullalloc": "VALIDATION_ERROR_268008e6",
202 "queryPool-compatalloc": "VALIDATION_ERROR_26200634",
203 "queryPool-nullalloc": "VALIDATION_ERROR_26200636",
204 "bufferView-compatalloc": "VALIDATION_ERROR_23e00752",
205 "bufferView-nullalloc": "VALIDATION_ERROR_23e00754",
206 "surface-compatalloc": "VALIDATION_ERROR_26c009e6",
207 "surface-nullalloc": "VALIDATION_ERROR_26c009e8",
208 "framebuffer-compatalloc": "VALIDATION_ERROR_250006fa",
209 "framebuffer-nullalloc": "VALIDATION_ERROR_250006fc",
210 }
211
212 # Commands shadowed by interface functions and are not implemented
213 self.interface_functions = [
214 ]
215 self.headerVersion = None
216 # Internal state - accumulators for different inner block text
217 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
218 self.cmdMembers = []
219 self.cmd_feature_protect = [] # Save ifdef's for each command
220 self.cmd_info_data = [] # Save the cmdinfo data for validating the handles when processing is complete
221 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
222 self.extension_structs = [] # List of all structs or sister-structs containing handles
223 # A sister-struct may contain no handles but shares <validextensionstructs> with one that does
224 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
225 self.struct_member_dict = dict()
226 # Named tuples to store struct and command data
227 self.StructType = namedtuple('StructType', ['name', 'value'])
228 self.CmdMemberData = namedtuple('CmdMemberData', ['name', 'members'])
229 self.CmdInfoData = namedtuple('CmdInfoData', ['name', 'cmdinfo'])
230 self.CmdExtraProtect = namedtuple('CmdExtraProtect', ['name', 'extra_protect'])
231 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isconst', 'isoptional', 'iscount', 'len', 'extstructs', 'cdecl', 'islocal', 'iscreate', 'isdestroy', 'feature_protect'])
232 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
233 self.object_types = [] # List of all handle types
234 self.valid_vuids = set() # Set of all valid VUIDs
235 self.vuid_file = None
236 # Cover cases where file is built from scripts directory, Lin/Win, or Android build structure
237 vuid_filename_locations = [
238 './vk_validation_error_messages.h',
239 '../layers/vk_validation_error_messages.h',
240 '../../layers/vk_validation_error_messages.h',
241 '../../../layers/vk_validation_error_messages.h',
242 ]
243 for vuid_filename in vuid_filename_locations:
244 if os.path.isfile(vuid_filename):
Lenny Komowb79f04a2017-09-18 17:07:00 -0600245 self.vuid_file = open(vuid_filename, "r", encoding="utf8")
Mark Lobodzinskid1461482017-07-18 13:56:09 -0600246 break
247 if self.vuid_file == None:
248 print("Error: Could not find vk_validation_error_messages.h")
249 quit()
250 #
251 # Check if the parameter passed in is optional
252 def paramIsOptional(self, param):
253 # See if the handle is optional
254 isoptional = False
255 # Simple, if it's optional, return true
256 optString = param.attrib.get('optional')
257 if optString:
258 if optString == 'true':
259 isoptional = True
260 elif ',' in optString:
261 opts = []
262 for opt in optString.split(','):
263 val = opt.strip()
264 if val == 'true':
265 opts.append(True)
266 elif val == 'false':
267 opts.append(False)
268 else:
269 print('Unrecognized len attribute value',val)
270 isoptional = opts
271 return isoptional
272 #
273 # Convert decimal number to 8 digit hexadecimal lower-case representation
274 def IdToHex(self, dec_num):
275 if dec_num > 4294967295:
276 print ("ERROR: Decimal # %d can't be represented in 8 hex digits" % (dec_num))
277 sys.exit()
278 hex_num = hex(dec_num)
279 return hex_num[2:].zfill(8)
280 #
281 # Get VUID identifier from implicit VUID tag
282 def GetVuid(self, vuid_string):
283 if '->' in vuid_string:
284 return "VALIDATION_ERROR_UNDEFINED"
285 vuid_num = self.IdToHex(convertVUID(vuid_string))
286 if vuid_num in self.valid_vuids:
287 vuid = "VALIDATION_ERROR_%s" % vuid_num
288 else:
289 vuid = "VALIDATION_ERROR_UNDEFINED"
290 return vuid
291 #
292 # Increases indent by 4 spaces and tracks it globally
293 def incIndent(self, indent):
294 inc = ' ' * self.INDENT_SPACES
295 if indent:
296 return indent + inc
297 return inc
298 #
299 # Decreases indent by 4 spaces and tracks it globally
300 def decIndent(self, indent):
301 if indent and (len(indent) > self.INDENT_SPACES):
302 return indent[:-self.INDENT_SPACES]
303 return ''
304 #
305 # Override makeProtoName to drop the "vk" prefix
306 def makeProtoName(self, name, tail):
307 return self.genOpts.apientry + name[2:] + tail
308 #
309 # Check if the parameter passed in is a pointer to an array
310 def paramIsArray(self, param):
311 return param.attrib.get('len') is not None
312 #
313 # Generate the object tracker undestroyed object validation function
314 def GenReportFunc(self):
315 output_func = ''
316 output_func += 'void ReportUndestroyedObjects(VkDevice device, enum UNIQUE_VALIDATION_ERROR_CODE error_code) {\n'
317 output_func += ' DeviceReportUndestroyedObjects(device, kVulkanObjectTypeCommandBuffer, error_code);\n'
318 for handle in self.object_types:
319 if self.isHandleTypeNonDispatchable(handle):
320 output_func += ' DeviceReportUndestroyedObjects(device, %s, error_code);\n' % (self.GetVulkanObjType(handle))
321 output_func += '}\n'
322 return output_func
323 #
324 # Called at beginning of processing as file is opened
325 def beginFile(self, genOpts):
326 OutputGenerator.beginFile(self, genOpts)
327 # Open vk_validation_error_messages.h file to verify computed VUIDs
328 for line in self.vuid_file:
329 # Grab hex number from enum definition
330 vuid_list = line.split('0x')
331 # If this is a valid enumeration line, remove trailing comma and CR
332 if len(vuid_list) == 2:
333 vuid_num = vuid_list[1][:-2]
334 # Make sure this is a good hex number before adding to set
335 if len(vuid_num) == 8 and all(c in string.hexdigits for c in vuid_num):
336 self.valid_vuids.add(vuid_num)
337 # File Comment
338 file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
339 file_comment += '// See object_tracker_generator.py for modifications\n'
340 write(file_comment, file=self.outFile)
341 # Copyright Statement
342 copyright = ''
343 copyright += '\n'
344 copyright += '/***************************************************************************\n'
345 copyright += ' *\n'
346 copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n'
347 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
348 copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n'
349 copyright += ' * Copyright (c) 2015-2017 Google Inc.\n'
350 copyright += ' *\n'
351 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
352 copyright += ' * you may not use this file except in compliance with the License.\n'
353 copyright += ' * You may obtain a copy of the License at\n'
354 copyright += ' *\n'
355 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
356 copyright += ' *\n'
357 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
358 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
359 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
360 copyright += ' * See the License for the specific language governing permissions and\n'
361 copyright += ' * limitations under the License.\n'
362 copyright += ' *\n'
363 copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
364 copyright += ' *\n'
365 copyright += ' ****************************************************************************/\n'
366 write(copyright, file=self.outFile)
367 # Namespace
368 self.newline()
369 write('#include "object_tracker.h"', file = self.outFile)
370 self.newline()
371 write('namespace object_tracker {', file = self.outFile)
372 #
373 # Now that the data is all collected and complete, generate and output the object validation routines
374 def endFile(self):
375 self.struct_member_dict = dict(self.structMembers)
376 # Generate the list of APIs that might need to handle wrapped extension structs
377 # self.GenerateCommandWrapExtensionList()
378 self.WrapCommands()
379 # Build undestroyed objects reporting function
380 report_func = self.GenReportFunc()
381 self.newline()
382 write('// ObjectTracker undestroyed objects validation function', file=self.outFile)
383 write('%s' % report_func, file=self.outFile)
384 # Actually write the interface to the output file.
385 if (self.emit):
386 self.newline()
387 if (self.featureExtraProtect != None):
388 write('#ifdef', self.featureExtraProtect, file=self.outFile)
389 # Write the object_tracker code to the file
390 if (self.sections['command']):
391 if (self.genOpts.protectProto):
392 write(self.genOpts.protectProto,
393 self.genOpts.protectProtoStr, file=self.outFile)
394 write('\n'.join(self.sections['command']), end=u'', file=self.outFile)
395 if (self.featureExtraProtect != None):
396 write('\n#endif //', self.featureExtraProtect, file=self.outFile)
397 else:
398 self.newline()
399
400 # Record intercepted procedures
401 write('// Map of all APIs to be intercepted by this layer', file=self.outFile)
402 write('const std::unordered_map<std::string, void*> name_to_funcptr_map = {', file=self.outFile)
403 write('\n'.join(self.intercepts), file=self.outFile)
404 write('};\n', file=self.outFile)
405 self.newline()
406 write('} // namespace object_tracker', file=self.outFile)
407 # Finish processing in superclass
408 OutputGenerator.endFile(self)
409 #
410 # Processing point at beginning of each extension definition
411 def beginFeature(self, interface, emit):
412 # Start processing in superclass
413 OutputGenerator.beginFeature(self, interface, emit)
414 self.headerVersion = None
415
416 if self.featureName != 'VK_VERSION_1_0':
417 white_list_entry = []
418 if (self.featureExtraProtect != None):
419 white_list_entry += [ '#ifdef %s' % self.featureExtraProtect ]
420 white_list_entry += [ '"%s"' % self.featureName ]
421 if (self.featureExtraProtect != None):
422 white_list_entry += [ '#endif' ]
423 featureType = interface.get('type')
424 if featureType == 'instance':
425 self.instance_extensions += white_list_entry
426 elif featureType == 'device':
427 self.device_extensions += white_list_entry
428 #
429 # Processing point at end of each extension definition
430 def endFeature(self):
431 # Finish processing in superclass
432 OutputGenerator.endFeature(self)
433 #
434 # Process enums, structs, etc.
435 def genType(self, typeinfo, name):
436 OutputGenerator.genType(self, typeinfo, name)
437 typeElem = typeinfo.elem
438 # If the type is a struct type, traverse the imbedded <member> tags generating a structure.
439 # Otherwise, emit the tag text.
440 category = typeElem.get('category')
441 if (category == 'struct' or category == 'union'):
442 self.genStruct(typeinfo, name)
443 if category == 'handle':
444 self.object_types.append(name)
445 #
446 # Append a definition to the specified section
447 def appendSection(self, section, text):
448 # self.sections[section].append('SECTION: ' + section + '\n')
449 self.sections[section].append(text)
450 #
451 # Check if the parameter passed in is a pointer
452 def paramIsPointer(self, param):
453 ispointer = False
454 for elem in param:
455 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
456 ispointer = True
457 return ispointer
458 #
459 # Get the category of a type
460 def getTypeCategory(self, typename):
461 types = self.registry.tree.findall("types/type")
462 for elem in types:
463 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
464 return elem.attrib.get('category')
465 #
466 # Check if a parent object is dispatchable or not
467 def isHandleTypeObject(self, handletype):
468 handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
469 if handle is not None:
470 return True
471 else:
472 return False
473 #
474 # Check if a parent object is dispatchable or not
475 def isHandleTypeNonDispatchable(self, handletype):
476 handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
477 if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
478 return True
479 else:
480 return False
481 #
482 # Retrieve the type and name for a parameter
483 def getTypeNameTuple(self, param):
484 type = ''
485 name = ''
486 for elem in param:
487 if elem.tag == 'type':
488 type = noneStr(elem.text)
489 elif elem.tag == 'name':
490 name = noneStr(elem.text)
491 return (type, name)
492 #
493 # Retrieve the value of the len tag
494 def getLen(self, param):
495 result = None
496 len = param.attrib.get('len')
497 if len and len != 'null-terminated':
498 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we
499 # have a null terminated array of strings. We strip the null-terminated from the
500 # 'len' field and only return the parameter specifying the string count
501 if 'null-terminated' in len:
502 result = len.split(',')[0]
503 else:
504 result = len
505 # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
506 result = str(result).replace('::', '->')
507 return result
508 #
509 # Generate a VkStructureType based on a structure typename
510 def genVkStructureType(self, typename):
511 # Add underscore between lowercase then uppercase
512 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
513 # Change to uppercase
514 value = value.upper()
515 # Add STRUCTURE_TYPE_
516 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
517 #
518 # Struct parameter check generation.
519 # This is a special case of the <type> tag where the contents are interpreted as a set of
520 # <member> tags instead of freeform C type declarations. The <member> tags are just like
521 # <param> tags - they are a declaration of a struct or union member. Only simple member
522 # declarations are supported (no nested structs etc.)
523 def genStruct(self, typeinfo, typeName):
524 OutputGenerator.genStruct(self, typeinfo, typeName)
525 members = typeinfo.elem.findall('.//member')
526 # Iterate over members once to get length parameters for arrays
527 lens = set()
528 for member in members:
529 len = self.getLen(member)
530 if len:
531 lens.add(len)
532 # Generate member info
533 membersInfo = []
534 for member in members:
535 # Get the member's type and name
536 info = self.getTypeNameTuple(member)
537 type = info[0]
538 name = info[1]
539 cdecl = self.makeCParamDecl(member, 0)
540 # Process VkStructureType
541 if type == 'VkStructureType':
542 # Extract the required struct type value from the comments
543 # embedded in the original text defining the 'typeinfo' element
544 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
545 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
546 if result:
547 value = result.group(0)
548 else:
549 value = self.genVkStructureType(typeName)
550 # Store the required type value
551 self.structTypes[typeName] = self.StructType(name=name, value=value)
552 # Store pointer/array/string info
553 extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None
554 membersInfo.append(self.CommandParam(type=type,
555 name=name,
556 ispointer=self.paramIsPointer(member),
557 isconst=True if 'const' in cdecl else False,
558 isoptional=self.paramIsOptional(member),
559 iscount=True if name in lens else False,
560 len=self.getLen(member),
561 extstructs=extstructs,
562 cdecl=cdecl,
563 islocal=False,
564 iscreate=False,
565 isdestroy=False,
566 feature_protect=self.featureExtraProtect))
567 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
568 #
569 # Insert a lock_guard line
570 def lock_guard(self, indent):
571 return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent
572 #
573 # Determine if a struct has an object as a member or an embedded member
574 def struct_contains_object(self, struct_item):
575 struct_member_dict = dict(self.structMembers)
576 struct_members = struct_member_dict[struct_item]
577
578 for member in struct_members:
579 if self.isHandleTypeObject(member.type):
580 return True
581 elif member.type in struct_member_dict:
582 if self.struct_contains_object(member.type) == True:
583 return True
584 return False
585 #
586 # Return list of struct members which contain, or whose sub-structures contain an obj in a given list of parameters or members
587 def getParmeterStructsWithObjects(self, item_list):
588 struct_list = set()
589 for item in item_list:
590 paramtype = item.find('type')
591 typecategory = self.getTypeCategory(paramtype.text)
592 if typecategory == 'struct':
593 if self.struct_contains_object(paramtype.text) == True:
594 struct_list.add(item)
595 return struct_list
596 #
597 # Return list of objects from a given list of parameters or members
598 def getObjectsInParameterList(self, item_list, create_func):
599 object_list = set()
600 if create_func == True:
601 member_list = item_list[0:-1]
602 else:
603 member_list = item_list
604 for item in member_list:
605 if self.isHandleTypeObject(paramtype.text):
606 object_list.add(item)
607 return object_list
608 #
609 # Construct list of extension structs containing handles, or extension structs that share a <validextensionstructs>
610 # tag WITH an extension struct containing handles.
611 def GenerateCommandWrapExtensionList(self):
612 for struct in self.structMembers:
613 if (len(struct.members) > 1) and struct.members[1].extstructs is not None:
614 found = False;
615 for item in struct.members[1].extstructs.split(','):
616 if item != '' and self.struct_contains_object(item) == True:
617 found = True
618 if found == True:
619 for item in struct.members[1].extstructs.split(','):
620 if item != '' and item not in self.extension_structs:
621 self.extension_structs.append(item)
622 #
623 # Returns True if a struct may have a pNext chain containing an object
624 def StructWithExtensions(self, struct_type):
625 if struct_type in self.struct_member_dict:
626 param_info = self.struct_member_dict[struct_type]
627 if (len(param_info) > 1) and param_info[1].extstructs is not None:
628 for item in param_info[1].extstructs.split(','):
629 if item in self.extension_structs:
630 return True
631 return False
632 #
633 # Generate VulkanObjectType from object type
634 def GetVulkanObjType(self, type):
635 return 'kVulkanObjectType%s' % type[2:]
636 #
637 # Return correct dispatch table type -- instance or device
638 def GetDispType(self, type):
639 return 'instance' if type in ['VkInstance', 'VkPhysicalDevice'] else 'device'
640 #
641 # Generate source for creating a Vulkan object
642 def generate_create_object_code(self, indent, proto, params, cmd_info):
643 create_obj_code = ''
644 handle_type = params[-1].find('type')
645 if self.isHandleTypeObject(handle_type.text):
646 # Check for special case where multiple handles are returned
647 object_array = False
648 if cmd_info[-1].len is not None:
649 object_array = True;
650 handle_name = params[-1].find('name')
651 create_obj_code += '%sif (VK_SUCCESS == result) {\n' % (indent)
652 indent = self.incIndent(indent)
653 create_obj_code += '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % (indent)
654 object_dest = '*%s' % handle_name.text
655 if object_array == True:
656 create_obj_code += '%sfor (uint32_t index = 0; index < %s; index++) {\n' % (indent, cmd_info[-1].len)
657 indent = self.incIndent(indent)
658 object_dest = '%s[index]' % cmd_info[-1].name
659 create_obj_code += '%sCreateObject(%s, %s, %s, pAllocator);\n' % (indent, params[0].find('name').text, object_dest, self.GetVulkanObjType(cmd_info[-1].type))
660 if object_array == True:
661 indent = self.decIndent(indent)
662 create_obj_code += '%s}\n' % indent
663 indent = self.decIndent(indent)
664 create_obj_code += '%s}\n' % (indent)
665 return create_obj_code
666 #
667 # Generate source for destroying a non-dispatchable object
668 def generate_destroy_object_code(self, indent, proto, cmd_info):
669 destroy_obj_code = ''
670 object_array = False
671 if True in [destroy_txt in proto.text for destroy_txt in ['Destroy', 'Free']]:
672 # Check for special case where multiple handles are returned
673 if cmd_info[-1].len is not None:
674 object_array = True;
675 param = -1
676 else:
677 param = -2
678 compatalloc_vuid_string = '%s-compatalloc' % cmd_info[param].name
679 nullalloc_vuid_string = '%s-nullalloc' % cmd_info[param].name
680 compatalloc_vuid = self.manual_vuids.get(compatalloc_vuid_string, "VALIDATION_ERROR_UNDEFINED")
681 nullalloc_vuid = self.manual_vuids.get(nullalloc_vuid_string, "VALIDATION_ERROR_UNDEFINED")
682 if self.isHandleTypeObject(cmd_info[param].type) == True:
683 if object_array == True:
684 # This API is freeing an array of handles -- add loop control
685 destroy_obj_code += 'HEY, NEED TO DESTROY AN ARRAY\n'
686 else:
687 # Call Destroy a single time
688 destroy_obj_code += '%sif (skip) return;\n' % indent
689 destroy_obj_code += '%s{\n' % indent
690 destroy_obj_code += '%s std::lock_guard<std::mutex> lock(global_lock);\n' % indent
691 destroy_obj_code += '%s DestroyObject(%s, %s, %s, pAllocator, %s, %s);\n' % (indent, cmd_info[0].name, cmd_info[param].name, self.GetVulkanObjType(cmd_info[param].type), compatalloc_vuid, nullalloc_vuid)
692 destroy_obj_code += '%s}\n' % indent
693 return object_array, destroy_obj_code
694 #
695 # Output validation for a single object (obj_count is NULL) or a counted list of objects
696 def outputObjects(self, obj_type, obj_name, obj_count, prefix, index, indent, destroy_func, destroy_array, disp_name, parent_name, null_allowed, top_level):
697 decl_code = ''
698 pre_call_code = ''
699 post_call_code = ''
700 param_vuid_string = 'VUID-%s-%s-parameter' % (parent_name, obj_name)
701 parent_vuid_string = 'VUID-%s-%s-parent' % (parent_name, obj_name)
702 param_vuid = self.GetVuid(param_vuid_string)
703 parent_vuid = self.GetVuid(parent_vuid_string)
704 # If no parent VUID for this member, look for a commonparent VUID
705 if parent_vuid == 'VALIDATION_ERROR_UNDEFINED':
706 commonparent_vuid_string = 'VUID-%s-commonparent' % parent_name
707 parent_vuid = self.GetVuid(commonparent_vuid_string)
708 if obj_count is not None:
709 pre_call_code += '%s if (%s%s) {\n' % (indent, prefix, obj_name)
710 indent = self.incIndent(indent)
711 pre_call_code += '%s for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, obj_count, index)
712 indent = self.incIndent(indent)
713 pre_call_code += '%s skip |= ValidateObject(%s, %s%s[%s], %s, %s, %s, %s);\n' % (indent, disp_name, prefix, obj_name, index, self.GetVulkanObjType(obj_type), null_allowed, param_vuid, parent_vuid)
714 indent = self.decIndent(indent)
715 pre_call_code += '%s }\n' % indent
716 indent = self.decIndent(indent)
717 pre_call_code += '%s }\n' % indent
718 else:
719 pre_call_code += '%s skip |= ValidateObject(%s, %s%s, %s, %s, %s, %s);\n' % (indent, disp_name, prefix, obj_name, self.GetVulkanObjType(obj_type), null_allowed, param_vuid, parent_vuid)
720 return decl_code, pre_call_code, post_call_code
721 #
722 # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct
723 # create_func means that this is API creates or allocates objects
724 # destroy_func indicates that this API destroys or frees objects
725 # destroy_array means that the destroy_func operated on an array of objects
726 def validate_objects(self, members, indent, prefix, array_index, create_func, destroy_func, destroy_array, disp_name, parent_name, first_level_param):
727 decls = ''
728 pre_code = ''
729 post_code = ''
730 index = 'index%s' % str(array_index)
731 array_index += 1
732 # Process any objects in this structure and recurse for any sub-structs in this struct
733 for member in members:
734 # Handle objects
735 if member.iscreate and first_level_param and member == members[-1]:
736 continue
737 if self.isHandleTypeObject(member.type) == True:
738 count_name = member.len
739 if (count_name is not None):
740 count_name = '%s%s' % (prefix, member.len)
741 null_allowed = member.isoptional
742 (tmp_decl, tmp_pre, tmp_post) = self.outputObjects(member.type, member.name, count_name, prefix, index, indent, destroy_func, destroy_array, disp_name, parent_name, str(null_allowed).lower(), first_level_param)
743 decls += tmp_decl
744 pre_code += tmp_pre
745 post_code += tmp_post
746 # Handle Structs that contain objects at some level
747 elif member.type in self.struct_member_dict:
748 # Structs at first level will have an object
749 if self.struct_contains_object(member.type) == True:
750 struct_info = self.struct_member_dict[member.type]
751 # Struct Array
752 if member.len is not None:
753 # Update struct prefix
754 new_prefix = '%s%s' % (prefix, member.name)
755 pre_code += '%s if (%s%s) {\n' % (indent, prefix, member.name)
756 indent = self.incIndent(indent)
757 pre_code += '%s for (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, member.len, index)
758 indent = self.incIndent(indent)
759 local_prefix = '%s[%s].' % (new_prefix, index)
760 # Process sub-structs in this struct
761 (tmp_decl, tmp_pre, tmp_post) = self.validate_objects(struct_info, indent, local_prefix, array_index, create_func, destroy_func, destroy_array, disp_name, member.type, False)
762 decls += tmp_decl
763 pre_code += tmp_pre
764 post_code += tmp_post
765 indent = self.decIndent(indent)
766 pre_code += '%s }\n' % indent
767 indent = self.decIndent(indent)
768 pre_code += '%s }\n' % indent
769 # Single Struct
770 else:
771 # Update struct prefix
772 new_prefix = '%s%s->' % (prefix, member.name)
773 # Declare safe_VarType for struct
774 pre_code += '%s if (%s%s) {\n' % (indent, prefix, member.name)
775 indent = self.incIndent(indent)
776 # Process sub-structs in this struct
777 (tmp_decl, tmp_pre, tmp_post) = self.validate_objects(struct_info, indent, new_prefix, array_index, create_func, destroy_func, destroy_array, disp_name, member.type, False)
778 decls += tmp_decl
779 pre_code += tmp_pre
780 post_code += tmp_post
781 indent = self.decIndent(indent)
782 pre_code += '%s }\n' % indent
783 return decls, pre_code, post_code
784 #
785 # For a particular API, generate the object handling code
786 def generate_wrapping_code(self, cmd):
787 indent = ' '
788 proto = cmd.find('proto/name')
789 params = cmd.findall('param')
790 if proto.text is not None:
791 cmd_member_dict = dict(self.cmdMembers)
792 cmd_info = cmd_member_dict[proto.text]
793 disp_name = cmd_info[0].name
794 # Handle object create operations
795 if cmd_info[0].iscreate:
796 create_obj_code = self.generate_create_object_code(indent, proto, params, cmd_info)
797 else:
798 create_obj_code = ''
799 # Handle object destroy operations
800 if cmd_info[0].isdestroy:
801 (destroy_array, destroy_object_code) = self.generate_destroy_object_code(indent, proto, cmd_info)
802 else:
803 destroy_array = False
804 destroy_object_code = ''
805 paramdecl = ''
806 param_pre_code = ''
807 param_post_code = ''
808 create_func = True if create_obj_code else False
809 destroy_func = True if destroy_object_code else False
810 (paramdecl, param_pre_code, param_post_code) = self.validate_objects(cmd_info, indent, '', 0, create_func, destroy_func, destroy_array, disp_name, proto.text, True)
811 param_post_code += create_obj_code
812 if destroy_object_code:
813 if destroy_array == True:
814 param_post_code += destroy_object_code
815 else:
816 param_pre_code += destroy_object_code
817 if param_pre_code:
818 if (not destroy_func) or (destroy_array):
819 param_pre_code = '%s{\n%s%s%s%s}\n' % (' ', indent, self.lock_guard(indent), param_pre_code, indent)
820 return paramdecl, param_pre_code, param_post_code
821 #
822 # Capture command parameter info needed to create, destroy, and validate objects
823 def genCmd(self, cmdinfo, cmdname):
824
825 # Add struct-member type information to command parameter information
826 OutputGenerator.genCmd(self, cmdinfo, cmdname)
827 members = cmdinfo.elem.findall('.//param')
828 # Iterate over members once to get length parameters for arrays
829 lens = set()
830 for member in members:
831 len = self.getLen(member)
832 if len:
833 lens.add(len)
834 struct_member_dict = dict(self.structMembers)
835 # Generate member info
836 membersInfo = []
837 constains_extension_structs = False
838 for member in members:
839 # Get type and name of member
840 info = self.getTypeNameTuple(member)
841 type = info[0]
842 name = info[1]
843 cdecl = self.makeCParamDecl(member, 0)
844 # Check for parameter name in lens set
845 iscount = True if name in lens else False
846 len = self.getLen(member)
847 isconst = True if 'const' in cdecl else False
848 ispointer = self.paramIsPointer(member)
849 # Mark param as local if it is an array of objects
850 islocal = False;
851 if self.isHandleTypeObject(type) == True:
852 if (len is not None) and (isconst == True):
853 islocal = True
854 # Or if it's a struct that contains an object
855 elif type in struct_member_dict:
856 if self.struct_contains_object(type) == True:
857 islocal = True
858 isdestroy = True if True in [destroy_txt in cmdname for destroy_txt in ['Destroy', 'Free']] else False
859 iscreate = True if True in [create_txt in cmdname for create_txt in ['Create', 'Allocate', 'Enumerate', 'RegisterDeviceEvent', 'RegisterDisplayEvent']] or ('vkGet' in cmdname and member == members[-1] and ispointer == True) else False
860 extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None
861 membersInfo.append(self.CommandParam(type=type,
862 name=name,
863 ispointer=ispointer,
864 isconst=isconst,
865 isoptional=self.paramIsOptional(member),
866 iscount=iscount,
867 len=len,
868 extstructs=extstructs,
869 cdecl=cdecl,
870 islocal=islocal,
871 iscreate=iscreate,
872 isdestroy=isdestroy,
873 feature_protect=self.featureExtraProtect))
874 self.cmdMembers.append(self.CmdMemberData(name=cmdname, members=membersInfo))
875 self.cmd_info_data.append(self.CmdInfoData(name=cmdname, cmdinfo=cmdinfo))
876 self.cmd_feature_protect.append(self.CmdExtraProtect(name=cmdname, extra_protect=self.featureExtraProtect))
877 #
878 # Create code Create, Destroy, and validate Vulkan objects
879 def WrapCommands(self):
880 cmd_member_dict = dict(self.cmdMembers)
881 cmd_info_dict = dict(self.cmd_info_data)
882 cmd_protect_dict = dict(self.cmd_feature_protect)
883 for api_call in self.cmdMembers:
884 cmdname = api_call.name
885 cmdinfo = cmd_info_dict[api_call.name]
886 if cmdname in self.interface_functions:
887 continue
888 if cmdname in self.no_autogen_list:
889 decls = self.makeCDecls(cmdinfo.elem)
890 self.appendSection('command', '')
891 self.appendSection('command', '// Declare only')
892 self.appendSection('command', decls[0])
893 self.intercepts += [ ' {"%s", (void *)%s},' % (cmdname,cmdname[2:]) ]
894 continue
895 # Generate object handling code
896 (api_decls, api_pre, api_post) = self.generate_wrapping_code(cmdinfo.elem)
897 # If API doesn't contain any object handles, don't fool with it
898 if not api_decls and not api_pre and not api_post:
899 continue
900 feature_extra_protect = cmd_protect_dict[api_call.name]
901 if (feature_extra_protect != None):
902 self.appendSection('command', '')
903 self.appendSection('command', '#ifdef '+ feature_extra_protect)
904 self.intercepts += [ '#ifdef %s' % feature_extra_protect ]
905 # Add intercept to procmap
906 self.intercepts += [ ' {"%s", (void*)%s},' % (cmdname,cmdname[2:]) ]
907 decls = self.makeCDecls(cmdinfo.elem)
908 self.appendSection('command', '')
909 self.appendSection('command', decls[0][:-1])
910 self.appendSection('command', '{')
911 self.appendSection('command', ' bool skip = false;')
912 # Handle return values, if any
913 resulttype = cmdinfo.elem.find('proto/type')
914 if (resulttype != None and resulttype.text == 'void'):
915 resulttype = None
916 if (resulttype != None):
917 assignresult = resulttype.text + ' result = '
918 else:
919 assignresult = ''
920 # Pre-pend declarations and pre-api-call codegen
921 if api_decls:
922 self.appendSection('command', "\n".join(str(api_decls).rstrip().split("\n")))
923 if api_pre:
924 self.appendSection('command', "\n".join(str(api_pre).rstrip().split("\n")))
925 # Generate the API call itself
926 # Gather the parameter items
927 params = cmdinfo.elem.findall('param/name')
928 # Pull out the text for each of the parameters, separate them by commas in a list
929 paramstext = ', '.join([str(param.text) for param in params])
930 # Use correct dispatch table
931 disp_type = cmdinfo.elem.find('param/type').text
932 disp_name = cmdinfo.elem.find('param/name').text
933 dispatch_table = 'get_dispatch_table(ot_%s_table_map, %s)->' % (self.GetDispType(disp_type), disp_name)
934 API = cmdinfo.elem.attrib.get('name').replace('vk', dispatch_table, 1)
935 # Put all this together for the final down-chain call
936 if assignresult != '':
937 self.appendSection('command', ' if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;')
938 else:
939 self.appendSection('command', ' if (skip) return;')
940 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
941 # And add the post-API-call codegen
942 self.appendSection('command', "\n".join(str(api_post).rstrip().split("\n")))
943 # Handle the return result variable, if any
944 if (resulttype != None):
945 self.appendSection('command', ' return result;')
946 self.appendSection('command', '}')
947 if (feature_extra_protect != None):
948 self.appendSection('command', '#endif // '+ feature_extra_protect)
949 self.intercepts += [ '#endif' ]