comparison fuhtark_test/Vulkan-Headers-1.4.334/registry/base_generator.py @ 1501:f40d9d814c08 default tip

did: correct vulkan-api generator
author sam <sam@basx.dev>
date Wed, 26 Nov 2025 23:34:29 +0700
parents
children
comparison
equal deleted inserted replaced
1500:91c8c3b7cbf0 1501:f40d9d814c08
1 #!/usr/bin/env python3 -i
2 #
3 # Copyright 2023-2025 The Khronos Group Inc.
4 #
5 # SPDX-License-Identifier: Apache-2.0
6
7 import pickle
8 import os
9 import tempfile
10 import copy
11 from vulkan_object import (VulkanObject,
12 Extension, Version, Legacy, Handle, Param, CommandScope, Command,
13 EnumField, Enum, Flag, Bitmask, ExternSync, Flags, Member, Struct,
14 Constant, FormatComponent, FormatPlane, Format, FeatureRequirement,
15 SyncSupport, SyncEquivalent, SyncStage, SyncAccess, SyncPipelineStage, SyncPipeline,
16 SpirvEnables, Spirv,
17 VideoCodec, VideoFormat, VideoProfiles, VideoProfileMember, VideoRequiredCapabilities,
18 VideoStd, VideoStdHeader)
19
20 # These live in the Vulkan-Docs repo, but are pulled in via the
21 # Vulkan-Headers/registry folder
22 from generator import OutputGenerator, GeneratorOptions, write
23 from vkconventions import VulkanConventions
24 from reg import Registry
25 from xml.etree import ElementTree
26
27 # An API style convention object
28 vulkanConventions = VulkanConventions()
29
30 # Helpers to keep things cleaner
31 def splitIfGet(elem, name):
32 return elem.get(name).split(',') if elem.get(name) is not None and elem.get(name) != '' else []
33
34 def textIfFind(elem, name):
35 return elem.find(name).text if elem.find(name) is not None else None
36
37 def intIfGet(elem, name):
38 return None if elem.get(name) is None else int(elem.get(name), 0)
39
40 def boolGet(elem, name) -> bool:
41 return elem.get(name) is not None and elem.get(name) == "true"
42
43 def externSyncGet(elem):
44 value = elem.get('externsync')
45 if value is None:
46 return (ExternSync.NONE, None)
47 if value == 'true':
48 return (ExternSync.ALWAYS, None)
49 if value == 'maybe':
50 return (ExternSync.MAYBE, None)
51
52 # There are no cases where multiple members of the param are marked as
53 # externsync. Supporting that with maybe: requires more than
54 # ExternSync.SUBTYPE_MAYBE (which is only one bit of information), which is
55 # not currently done as there are no users.
56 #
57 # If this assert is hit, please consider simplifying the design such that
58 # externsync can move to the struct itself and so external synchronization
59 # requirements do not depend on the context.
60 assert ',' not in value
61
62 if value.startswith('maybe:'):
63 return (ExternSync.SUBTYPE_MAYBE, value.removeprefix('maybe:'))
64 return (ExternSync.SUBTYPE, value)
65
66 # Shared object used by Sync elements that do not have ones
67 maxSyncSupport = SyncSupport(None, None, True)
68 maxSyncEquivalent = SyncEquivalent(None, None, True)
69
70 # Helpers to set GeneratorOptions options globally
71 def SetOutputFileName(fileName: str) -> None:
72 global globalFileName
73 globalFileName = fileName
74
75 def SetOutputDirectory(directory: str) -> None:
76 global globalDirectory
77 globalDirectory = directory
78
79 def SetTargetApiName(apiname: str) -> None:
80 global globalApiName
81 globalApiName = apiname
82
83 def SetMergedApiNames(names: str) -> None:
84 global mergedApiNames
85 mergedApiNames = names
86
87 cachingEnabled = False
88 def EnableCaching() -> None:
89 global cachingEnabled
90 cachingEnabled = True
91
92 # This class is a container for any source code, data, or other behavior that is necessary to
93 # customize the generator script for a specific target API variant (e.g. Vulkan SC). As such,
94 # all of these API-specific interfaces and their use in the generator script are part of the
95 # contract between this repository and its downstream users. Changing or removing any of these
96 # interfaces or their use in the generator script will have downstream effects and thus
97 # should be avoided unless absolutely necessary.
98 class APISpecific:
99 # Version object factory method
100 @staticmethod
101 def createApiVersion(targetApiName: str, name: str, featureRequirement) -> Version:
102 match targetApiName:
103
104 # Vulkan SC specific API version creation
105 case 'vulkansc':
106 nameApi = name.replace('VK_', 'VK_API_')
107 nameApi = nameApi.replace('VKSC_', 'VKSC_API_')
108 nameString = f'"{name}"'
109 return Version(name, nameString, nameApi, featureRequirement)
110
111 # Vulkan specific API version creation
112 case 'vulkan':
113 nameApi = name.replace('VK_', 'VK_API_')
114 nameString = f'"{name}"'
115 return Version(name, nameString, nameApi, featureRequirement)
116
117 # TODO - Currently genType in reg.py does not provide a good way to get this string to apply the C-macro
118 # We do our best to emulate the answer here the way the spec/headers will with goal to have a proper fix before these assumptions break
119 @staticmethod
120 def createHeaderVersion(targetApiName: str, vk: VulkanObject) -> str:
121 match targetApiName:
122 case 'vulkan':
123 major_version = 1
124 minor_version = 4
125 case 'vulkansc':
126 major_version = 1
127 minor_version = 0
128 return f'{major_version}.{minor_version}.{vk.headerVersion}'
129
130
131 # This Generator Option is used across all generators.
132 # After years of use, it has shown that most the options are unified across each generator (file)
133 # as it is easier to modify things per-file that need the difference
134 class BaseGeneratorOptions(GeneratorOptions):
135 def __init__(self,
136 customFileName = None,
137 customDirectory = None,
138 customApiName = None,
139 videoXmlPath = None):
140 GeneratorOptions.__init__(self,
141 conventions = vulkanConventions,
142 filename = customFileName if customFileName else globalFileName,
143 directory = customDirectory if customDirectory else globalDirectory,
144 apiname = customApiName if customApiName else globalApiName,
145 mergeApiNames = mergedApiNames,
146 defaultExtensions = customApiName if customApiName else globalApiName,
147 emitExtensions = '.*',
148 emitSpirv = '.*',
149 emitFormats = '.*')
150 # These are used by the generator.py script
151 self.apicall = 'VKAPI_ATTR '
152 self.apientry = 'VKAPI_CALL '
153 self.apientryp = 'VKAPI_PTR *'
154 self.alignFuncParam = 48
155
156 # This is used to provide the video.xml to the private video XML generator
157 self.videoXmlPath = videoXmlPath
158
159 #
160 # This object handles all the parsing from reg.py generator scripts in the Vulkan-Headers
161 # It will grab all the data and form it into a single object the rest of the generators will use
162 class BaseGenerator(OutputGenerator):
163 def __init__(self):
164 OutputGenerator.__init__(self, None, None, None)
165 self.vk = VulkanObject()
166 self.targetApiName = globalApiName
167
168 # reg.py has a `self.featureName` but this is nicer because
169 # it will be either the Version or Extension object
170 self.currentExtension = None
171 self.currentVersion = None
172
173 # We need to flag extensions that we ignore because they are disabled or not
174 # supported in the target API(s)
175 self.unsupportedExtension = False
176
177 # Will map alias to promoted name
178 # ex. ['VK_FILTER_CUBIC_IMG' : 'VK_FILTER_CUBIC_EXT']
179 # When generating any code, there is no reason so use the old name
180 self.enumAliasMap = dict()
181 self.enumFieldAliasMap = dict()
182 self.bitmaskAliasMap = dict()
183 self.flagAliasMap = dict()
184 self.flagsAliasMap = dict()
185 self.structAliasMap = dict()
186 self.handleAliasMap = dict()
187
188 # We track all enum constants and flag bits so that we can apply their aliases in the end
189 self.enumFieldMap: dict[str, EnumField] = dict()
190 self.flagMap: dict[str, Flag] = dict()
191
192 # De-aliases a definition name based on the specified alias map.
193 # There are aliases of aliases.
194 # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR aliases
195 # VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR which itself aliases
196 # But it is also common for EXT types promoted to KHR then to core.
197 # We should not make assumptions about the nesting level of aliases, instead we resolve any
198 # level of alias aliasing.
199 def dealias(self, name: str, aliasMap: dict):
200 while name in aliasMap:
201 name = aliasMap[name]
202 return name
203
204 def write(self, data):
205 # Prevents having to check before writing
206 if data is not None and data != "":
207 write(data, file=self.outFile)
208
209 def beginFile(self, genOpts):
210 OutputGenerator.beginFile(self, genOpts)
211 self.filename = genOpts.filename
212
213 # No gen*() command to get these, so do it manually
214 for platform in self.registry.tree.findall('platforms/platform'):
215 self.vk.platforms[platform.get('name')] = platform.get('protect')
216
217 for tags in self.registry.tree.findall('tags'):
218 for tag in tags.findall('tag'):
219 self.vk.vendorTags.append(tag.get('name'))
220
221 # If the video.xml path is provided then we need to load and parse it using
222 # the private video std generator
223 if genOpts.videoXmlPath is not None:
224 videoStdGenerator = _VideoStdGenerator()
225 videoRegistry = Registry(videoStdGenerator, genOpts)
226 videoRegistry.loadElementTree(ElementTree.parse(genOpts.videoXmlPath))
227 videoRegistry.apiGen()
228 self.vk.videoStd = videoStdGenerator.vk.videoStd
229
230 # This function should be overloaded
231 def generate(self):
232 print("WARNING: This should not be called from the child class")
233 return
234
235 # This function is dense, it does all the magic to set the right extensions dependencies!
236 #
237 # The issue is if 2 extension expose a command, genCmd() will only
238 # show one of the extension, at endFile() we can finally go through
239 # and update which things depend on which extensions
240 #
241 # self.featureDictionary is built for use in the reg.py framework
242 # Details found in Vulkan-Docs/scripts/scriptgenerator.py
243 def applyExtensionDependency(self):
244 for extension in self.vk.extensions.values():
245 # dict.key() can be None, so need to double loop
246 dict = self.featureDictionary[extension.name]['command']
247
248 # "required" == None
249 # or
250 # an additional feature dependency, which is a boolean expression of
251 # one or more extension and/or core version names
252 for required in dict:
253 for commandName in dict[required]:
254 # Skip commands removed in the target API
255 # This check is needed because parts of the base generator code bypass the
256 # dependency resolution logic in the registry tooling and thus the generator
257 # may attempt to generate code for commands which are not supported in the
258 # target API variant, thus this check needs to happen even if any specific
259 # target API variant may not specifically need it
260 if not commandName in self.vk.commands:
261 continue
262
263 command = self.vk.commands[commandName]
264 # Make sure list is unique
265 command.extensions.extend([extension.name] if extension.name not in command.extensions else [])
266 extension.commands.extend([command] if command not in extension.commands else [])
267
268 # While genGroup() will call twice with aliased value, it does not provide all the information we need
269 dict = self.featureDictionary[extension.name]['enumconstant']
270 for required in dict:
271 # group can be a Enum or Bitmask
272 for group in dict[required]:
273 if group in self.vk.handles:
274 handle = self.vk.handles[group]
275 # Make sure list is unique
276 handle.extensions.extend([extension.name] if extension.name not in handle.extensions else [])
277 extension.handles[group].extend([handle] if handle not in extension.handles[group] else [])
278 if group in self.vk.enums:
279 if group not in extension.enumFields:
280 extension.enumFields[group] = [] # Dict needs init
281 enum = self.vk.enums[group]
282 # Need to convert all alias so they match what is in EnumField
283 enumList = list(map(lambda x: x if x not in self.enumFieldAliasMap else self.dealias(x, self.enumFieldAliasMap), dict[required][group]))
284
285 for enumField in [x for x in enum.fields if x.name in enumList]:
286 # Make sure list is unique
287 enum.fieldExtensions.extend([extension.name] if extension.name not in enum.fieldExtensions else [])
288 enumField.extensions.extend([extension.name] if extension.name not in enumField.extensions else [])
289 extension.enumFields[group].extend([enumField] if enumField not in extension.enumFields[group] else [])
290 if group in self.vk.bitmasks:
291 if group not in extension.flagBits:
292 extension.flagBits[group] = [] # Dict needs init
293 bitmask = self.vk.bitmasks[group]
294 # Need to convert all alias so they match what is in Flags
295 flagList = list(map(lambda x: x if x not in self.flagAliasMap else self.dealias(x, self.flagAliasMap), dict[required][group]))
296
297 for flags in [x for x in bitmask.flags if x.name in flagList]:
298 # Make sure list is unique
299 bitmask.flagExtensions.extend([extension.name] if extension.name not in bitmask.flagExtensions else [])
300 flags.extensions.extend([extension.name] if extension.name not in flags.extensions else [])
301 extension.flagBits[group].extend([flags] if flags not in extension.flagBits[group] else [])
302 if group in self.vk.flags:
303 flags = self.vk.flags[group]
304 # Make sure list is unique
305 flags.extensions.extend([extension.name] if extension.name not in flags.extensions else [])
306 extension.flags.extend([flags] if flags not in extension.flags[group] else [])
307
308 # Need to do 'enum'/'bitmask' after 'enumconstant' has applied everything so we can add implicit extensions
309 #
310 # Sometimes two extensions enable an Enum, but the newer extension version has extra flags allowed
311 # This information seems to be implicit, so need to update it here
312 # Go through each Flag and append the Enum extension to it
313 #
314 # ex. VkAccelerationStructureTypeKHR where GENERIC_KHR is not allowed with just VK_NV_ray_tracing
315 # This only works because the values are aliased as well, making the KHR a superset enum
316 for extension in self.vk.extensions.values():
317 dict = self.featureDictionary[extension.name]['enum']
318 for required in dict:
319 for group in dict[required]:
320 for enumName in dict[required][group]:
321 isAlias = enumName in self.enumAliasMap
322 enumName = self.dealias(enumName, self.enumAliasMap)
323 if enumName in self.vk.enums:
324 enum = self.vk.enums[enumName]
325 enum.extensions.extend([extension.name] if extension.name not in enum.extensions else [])
326 extension.enums.extend([enum] if enum not in extension.enums else [])
327 # Update fields with implicit base extension
328 if isAlias:
329 continue
330 enum.fieldExtensions.extend([extension.name] if extension.name not in enum.fieldExtensions else [])
331 for enumField in [x for x in enum.fields if (not x.extensions or (x.extensions and all(e in enum.extensions for e in x.extensions)))]:
332 enumField.extensions.extend([extension.name] if extension.name not in enumField.extensions else [])
333 if enumName not in extension.enumFields:
334 extension.enumFields[enumName] = [] # Dict needs init
335 extension.enumFields[enumName].extend([enumField] if enumField not in extension.enumFields[enumName] else [])
336
337 dict = self.featureDictionary[extension.name]['bitmask']
338 for required in dict:
339 for group in dict[required]:
340 for bitmaskName in dict[required][group]:
341 bitmaskName = bitmaskName.replace('Flags', 'FlagBits') # Works since Flags is not repeated in name
342 isAlias = bitmaskName in self.bitmaskAliasMap
343 bitmaskName = self.dealias(bitmaskName, self.bitmaskAliasMap)
344 if bitmaskName in self.vk.bitmasks:
345 bitmask = self.vk.bitmasks[bitmaskName]
346 bitmask.extensions.extend([extension.name] if extension.name not in bitmask.extensions else [])
347 extension.bitmasks.extend([bitmask] if bitmask not in extension.bitmasks else [])
348 # Update flags with implicit base extension
349 if isAlias:
350 continue
351 bitmask.flagExtensions.extend([extension.name] if extension.name not in bitmask.flagExtensions else [])
352 for flag in [x for x in bitmask.flags if (not x.extensions or (x.extensions and all(e in bitmask.extensions for e in x.extensions)))]:
353 flag.extensions.extend([extension.name] if extension.name not in flag.extensions else [])
354 if bitmaskName not in extension.flagBits:
355 extension.flagBits[bitmaskName] = [] # Dict needs init
356 extension.flagBits[bitmaskName].extend([flag] if flag not in extension.flagBits[bitmaskName] else [])
357
358 # Some structs (ex VkAttachmentSampleCountInfoAMD) can have multiple alias pointing to same extension
359 for extension in self.vk.extensions.values():
360 dict = self.featureDictionary[extension.name]['struct']
361 for required in dict:
362 for group in dict[required]:
363 for structName in dict[required][group]:
364 isAlias = structName in self.structAliasMap
365 structName = self.dealias(structName, self.structAliasMap)
366 if structName in self.vk.structs:
367 struct = self.vk.structs[structName]
368 struct.extensions.extend([extension.name] if extension.name not in struct.extensions else [])
369 extension.structs.extend([struct] if struct not in extension.structs else [])
370
371 # While we update struct alias inside other structs, the command itself might have the struct as a first level param.
372 # We use this time to update params to have the promoted name
373 # Example - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9322
374 # TODO: It is unclear why only structs need dealiasing here, but not other types, so this probably needs revisiting
375 for command in self.vk.commands.values():
376 for member in command.params:
377 if member.type in self.structAliasMap:
378 member.type = self.dealias(member.type, self.structAliasMap)
379 # Replace string with Version class now we have all version created
380 if command.legacy and command.legacy.version:
381 if command.legacy.version not in self.vk.versions:
382 # occurs if something like VK_VERSION_1_0, in which case we will always warn for deprecation
383 command.legacy.version = None
384 else:
385 command.legacy.version = self.vk.versions[command.legacy.version]
386
387 # Could build up a reverse lookup map, but since these are not too large of list, just do here
388 # (Need to be done after we have found all the aliases)
389 for key, value in self.structAliasMap.items():
390 self.vk.structs[self.dealias(value, self.structAliasMap)].aliases.append(key)
391 for key, value in self.enumFieldAliasMap.items():
392 self.enumFieldMap[self.dealias(value, self.enumFieldAliasMap)].aliases.append(key)
393 for key, value in self.enumAliasMap.items():
394 self.vk.enums[self.dealias(value, self.enumAliasMap)].aliases.append(key)
395 for key, value in self.flagAliasMap.items():
396 self.flagMap[self.dealias(value, self.flagAliasMap)].aliases.append(key)
397 for key, value in self.bitmaskAliasMap.items():
398 self.vk.bitmasks[self.dealias(value, self.bitmaskAliasMap)].aliases.append(key)
399 for key, value in self.flagsAliasMap.items():
400 self.vk.flags[self.dealias(value, self.flagsAliasMap)].aliases.append(key)
401 for key, value in self.handleAliasMap.items():
402 self.vk.handles[self.dealias(value, self.handleAliasMap)].aliases.append(key)
403
404 def addConstants(self, constantNames: list[str]):
405 for constantName in constantNames:
406 enumInfo = self.registry.enumdict[constantName]
407 typeName = enumInfo.type
408 valueStr = enumInfo.elem.get('value')
409 # These values are represented in c-style
410 isHex = valueStr.upper().startswith('0X')
411 intBase = 16 if isHex else 10
412 if valueStr.upper().endswith('F') and not isHex:
413 value = float(valueStr[:-1])
414 elif valueStr.upper().endswith('U)'):
415 inner_number = int(valueStr.removeprefix("(~").removesuffix(")")[:-1], intBase)
416 value = (~inner_number) & ((1 << 32) - 1)
417 elif valueStr.upper().endswith('ULL)'):
418 inner_number = int(valueStr.removeprefix("(~").removesuffix(")")[:-3], intBase)
419 value = (~0) & ((1 << 64) - 1)
420 else:
421 value = int(valueStr, intBase)
422 self.vk.constants[constantName] = Constant(constantName, typeName, value, valueStr)
423
424 def addVideoCodecs(self):
425 for xmlVideoCodec in self.registry.tree.findall('videocodecs/videocodec'):
426 name = xmlVideoCodec.get('name')
427 extend = xmlVideoCodec.get('extend')
428 value = xmlVideoCodec.get('value')
429
430 profiles: dict[str, VideoProfiles] = {}
431 capabilities: dict[str, str] = {}
432 formats: dict[str, VideoFormat] = {}
433
434 if extend is not None:
435 # Inherit base profiles, capabilities, and formats
436 profiles = copy.deepcopy(self.vk.videoCodecs[extend].profiles)
437 capabilities = copy.deepcopy(self.vk.videoCodecs[extend].capabilities)
438 formats = copy.deepcopy(self.vk.videoCodecs[extend].formats)
439
440 for xmlVideoProfiles in xmlVideoCodec.findall('videoprofiles'):
441 videoProfileStructName = xmlVideoProfiles.get('struct')
442 videoProfileStructMembers : dict[str, VideoProfileMember] = {}
443
444 for xmlVideoProfileMember in xmlVideoProfiles.findall('videoprofilemember'):
445 memberName = xmlVideoProfileMember.get('name')
446 memberValues: dict[str, str] = {}
447
448 for xmlVideoProfile in xmlVideoProfileMember.findall('videoprofile'):
449 memberValues[xmlVideoProfile.get('value')] = xmlVideoProfile.get('name')
450
451 videoProfileStructMembers[memberName] = VideoProfileMember(memberName, memberValues)
452
453 profiles[videoProfileStructName] = VideoProfiles(videoProfileStructName, videoProfileStructMembers)
454
455 for xmlVideoCapabilities in xmlVideoCodec.findall('videocapabilities'):
456 capabilities[xmlVideoCapabilities.get('struct')] = xmlVideoCapabilities.get('struct')
457
458 for xmlVideoFormat in xmlVideoCodec.findall('videoformat'):
459 videoFormatName = xmlVideoFormat.get('name')
460 videoFormatExtend = xmlVideoFormat.get('extend')
461
462 videoFormatRequiredCaps: list[VideoRequiredCapabilities] = []
463 videoFormatProps: dict[str, str] = {}
464
465 if videoFormatName is not None:
466 # This is a new video format category
467 videoFormatUsage = xmlVideoFormat.get('usage')
468 videoFormat = VideoFormat(videoFormatName, videoFormatUsage, videoFormatRequiredCaps, videoFormatProps)
469 formats[videoFormatName] = videoFormat
470 else:
471 # This is an extension to an already defined video format category
472 videoFormat = formats[videoFormatExtend]
473 videoFormatRequiredCaps = videoFormat.requiredCaps
474 videoFormatProps = videoFormat.properties
475
476 for xmlVideoFormatRequiredCap in xmlVideoFormat.findall('videorequirecapabilities'):
477 requiredCap = VideoRequiredCapabilities(xmlVideoFormatRequiredCap.get('struct'),
478 xmlVideoFormatRequiredCap.get('member'),
479 xmlVideoFormatRequiredCap.get('value'))
480 videoFormatRequiredCaps.append(requiredCap)
481
482 for xmlVideoFormatProperties in xmlVideoFormat.findall('videoformatproperties'):
483 videoFormatProps[xmlVideoFormatProperties.get('struct')] = xmlVideoFormatProperties.get('struct')
484
485 self.vk.videoCodecs[name] = VideoCodec(name, value, profiles, capabilities, formats)
486
487 def endFile(self):
488 # This is the point were reg.py has ran, everything is collected
489 # We do some post processing now
490 self.applyExtensionDependency()
491
492 self.addConstants([k for k,v in self.registry.enumvaluedict.items() if v == 'API Constants'])
493 self.addVideoCodecs()
494
495 self.vk.headerVersionComplete = APISpecific.createHeaderVersion(self.targetApiName, self.vk)
496
497 # Use structs and commands to find which things are returnedOnly
498 for struct in [x for x in self.vk.structs.values() if not x.returnedOnly]:
499 for enum in [self.vk.enums[x.type] for x in struct.members if x.type in self.vk.enums]:
500 enum.returnedOnly = False
501 for bitmask in [self.vk.bitmasks[x.type] for x in struct.members if x.type in self.vk.bitmasks]:
502 bitmask.returnedOnly = False
503 for flags in [self.vk.flags[x.type] for x in struct.members if x.type in self.vk.flags]:
504 flags.returnedOnly = False
505 if flags.bitmaskName is not None:
506 self.vk.bitmasks[flags.bitmaskName].returnedOnly = False
507 for command in self.vk.commands.values():
508 for enum in [self.vk.enums[x.type] for x in command.params if x.type in self.vk.enums]:
509 enum.returnedOnly = False
510 for bitmask in [self.vk.bitmasks[x.type] for x in command.params if x.type in self.vk.bitmasks]:
511 bitmask.returnedOnly = False
512 for flags in [self.vk.flags[x.type] for x in command.params if x.type in self.vk.flags]:
513 flags.returnedOnly = False
514 if flags.bitmaskName is not None:
515 self.vk.bitmasks[flags.bitmaskName].returnedOnly = False
516
517 # Turn handle parents into pointers to classes
518 for handle in [x for x in self.vk.handles.values() if x.parent is not None]:
519 handle.parent = self.vk.handles[handle.parent]
520 # search up parent chain to see if instance or device
521 for handle in [x for x in self.vk.handles.values()]:
522 next_parent = handle.parent
523 while (not handle.instance and not handle.device):
524 handle.instance = next_parent.name == 'VkInstance'
525 handle.device = next_parent.name == 'VkDevice'
526 next_parent = next_parent.parent
527
528 maxSyncSupport.stages = self.vk.bitmasks['VkPipelineStageFlagBits2'].flags
529 maxSyncEquivalent.accesses = self.vk.bitmasks['VkAccessFlagBits2'].flags
530 maxSyncEquivalent.stages = self.vk.bitmasks['VkPipelineStageFlagBits2'].flags
531
532 # All inherited generators should run from here
533 self.generate()
534
535 if cachingEnabled:
536 cachePath = os.path.join(tempfile.gettempdir(), f'vkobject_{os.getpid()}')
537 if not os.path.isfile(cachePath):
538 cacheFile = open(cachePath, 'wb')
539 pickle.dump(self.vk, cacheFile)
540 cacheFile.close()
541
542 # This should not have to do anything but call into OutputGenerator
543 OutputGenerator.endFile(self)
544
545 #
546 # Bypass the entire processing and load in the VkObject data
547 # Still need to handle the beingFile/endFile for reg.py
548 def generateFromCache(self, cacheVkObjectData, genOpts):
549 OutputGenerator.beginFile(self, genOpts)
550 self.filename = genOpts.filename
551 self.vk = cacheVkObjectData
552 self.generate()
553 OutputGenerator.endFile(self)
554
555 #
556 # Processing point at beginning of each extension definition
557 def beginFeature(self, interface, emit):
558 OutputGenerator.beginFeature(self, interface, emit)
559 platform = interface.get('platform')
560 self.featureExtraProtec = self.vk.platforms[platform] if platform in self.vk.platforms else None
561 protect = self.vk.platforms[platform] if platform in self.vk.platforms else None
562 name = interface.get('name')
563
564 # TODO - This is just mimicking featurerequirementsgenerator.py and works because the logic is simple enough (for now)
565 featureRequirement = []
566 requires = interface.findall('./require')
567 for require in requires:
568 requireDepends = require.get('depends')
569 for feature in require.findall('./feature'):
570 featureStruct = feature.get('struct')
571 featureName = feature.get('name')
572 featureRequirement.append(FeatureRequirement(featureStruct, featureName, requireDepends))
573
574 if interface.tag == 'extension':
575 # Generator scripts built on BaseGenerator do not handle the `supported` attribute of extensions
576 # therefore historically the `generate_source.py` in individual ecosystem components hacked the
577 # registry by removing non-applicable or disabled extensions from the loaded XML already before
578 # reg.py parsed it. That broke the general behavior of reg.py for certain use cases so we now
579 # filter extensions here instead (after parsing) in order to no longer need the filtering hack
580 # in downstream `generate_source.py` scripts.
581 enabledApiList = [ globalApiName ] + ([] if mergedApiNames is None else mergedApiNames.split(','))
582 if (sup := interface.get('supported')) is not None and all(api not in sup.split(',') for api in enabledApiList):
583 self.unsupportedExtension = True
584 return
585
586 instance = interface.get('type') == 'instance'
587 device = not instance
588 depends = interface.get('depends')
589 vendorTag = name.split('_')[1]
590 platform = interface.get('platform')
591 provisional = boolGet(interface, 'provisional')
592 promotedto = interface.get('promotedto')
593 deprecatedby = interface.get('deprecatedby')
594 obsoletedby = interface.get('obsoletedby')
595 specialuse = splitIfGet(interface, 'specialuse')
596 ratifiedApis = splitIfGet(interface, 'ratified')
597 ratified = True if len(ratifiedApis) > 0 and self.genOpts.apiname in ratifiedApis else False
598
599 # Not sure if better way to get this info
600 specVersion = self.featureDictionary[name]['enumconstant'][None][None][0]
601 nameString = self.featureDictionary[name]['enumconstant'][None][None][1]
602
603 self.currentExtension = Extension(name, nameString, specVersion, instance, device, depends, vendorTag,
604 platform, protect, provisional, promotedto, deprecatedby,
605 obsoletedby, specialuse, featureRequirement, ratified)
606 self.vk.extensions[name] = self.currentExtension
607 else: # version
608 number = interface.get('number')
609 if number != '1.0':
610 self.currentVersion = APISpecific.createApiVersion(self.targetApiName, name, featureRequirement)
611 self.vk.versions[name] = self.currentVersion
612
613 def endFeature(self):
614 OutputGenerator.endFeature(self)
615 self.currentExtension = None
616 self.currentVersion = None
617 self.unsupportedExtension = False
618
619 #
620 # All <command> from XML
621 def genCmd(self, cmdinfo, name, alias):
622 OutputGenerator.genCmd(self, cmdinfo, name, alias)
623
624 # Do not include APIs from unsupported extensions
625 if self.unsupportedExtension:
626 return
627
628 params = []
629 for param in cmdinfo.elem.findall('param'):
630 paramName = param.find('name').text
631 paramType = textIfFind(param, 'type')
632 paramAlias = param.get('alias')
633
634 cdecl = self.makeCParamDecl(param, 0)
635 paramFullType = ' '.join(cdecl.split()[:-1])
636 pointer = '*' in cdecl or paramType.startswith('PFN_')
637 paramConst = 'const' in cdecl
638 fixedSizeArray = [x[:-1] for x in cdecl.split('[') if x.endswith(']')]
639
640 paramNoautovalidity = boolGet(param, 'noautovalidity')
641
642 nullTerminated = False
643 length = param.get('altlen') if param.get('altlen') is not None else param.get('len')
644 if length:
645 # we will either find it like "null-terminated" or "enabledExtensionCount,null-terminated"
646 # This finds both
647 nullTerminated = 'null-terminated' in length
648 length = length.replace(',null-terminated', '') if 'null-terminated' in length else length
649 length = None if length == 'null-terminated' else length
650
651 if fixedSizeArray and not length:
652 length = ','.join(fixedSizeArray)
653
654 # See Member::optional code for details of this
655 optionalValues = splitIfGet(param, 'optional')
656 optional = len(optionalValues) > 0 and optionalValues[0].lower() == "true"
657 optionalPointer = len(optionalValues) > 1 and optionalValues[1].lower() == "true"
658
659 # externsync will be 'true', 'maybe', '<expression>' or 'maybe:<expression>'
660 (externSync, externSyncPointer) = externSyncGet(param)
661
662 params.append(Param(paramName, paramAlias, paramType, paramFullType, paramNoautovalidity,
663 paramConst, length, nullTerminated, pointer, fixedSizeArray,
664 optional, optionalPointer,
665 externSync, externSyncPointer, cdecl))
666
667 attrib = cmdinfo.elem.attrib
668 alias = attrib.get('alias')
669 tasks = splitIfGet(attrib, 'tasks')
670
671 queues = splitIfGet(attrib, 'queues')
672 allowNoQueues = boolGet(attrib, 'allownoqueues')
673 successcodes = splitIfGet(attrib, 'successcodes')
674 errorcodes = splitIfGet(attrib, 'errorcodes')
675 cmdbufferlevel = attrib.get('cmdbufferlevel')
676 primary = cmdbufferlevel is not None and 'primary' in cmdbufferlevel
677 secondary = cmdbufferlevel is not None and 'secondary' in cmdbufferlevel
678
679 renderpass = attrib.get('renderpass')
680 renderpass = CommandScope.NONE if renderpass is None else getattr(CommandScope, renderpass.upper())
681 videocoding = attrib.get('videocoding')
682 videocoding = CommandScope.NONE if videocoding is None else getattr(CommandScope, videocoding.upper())
683
684 protoElem = cmdinfo.elem.find('proto')
685 returnType = textIfFind(protoElem, 'type')
686
687 decls = self.makeCDecls(cmdinfo.elem)
688 cPrototype = decls[0]
689 cFunctionPointer = decls[1]
690
691 legacy = None
692 if cmdinfo.deprecatedlink:
693 legacy = Legacy(cmdinfo.deprecatedlink,
694 cmdinfo.deprecatedbyversion, # is just the string, will update to class later
695 cmdinfo.deprecatedbyextensions)
696
697 protect = self.currentExtension.protect if self.currentExtension is not None else None
698
699 # These coammds have no way from the XML to detect they would be an instance command
700 specialInstanceCommand = ['vkCreateInstance', 'vkEnumerateInstanceExtensionProperties','vkEnumerateInstanceLayerProperties', 'vkEnumerateInstanceVersion']
701 instance = len(params) > 0 and (params[0].type == 'VkInstance' or params[0].type == 'VkPhysicalDevice' or name in specialInstanceCommand)
702 device = not instance
703
704 implicitElem = cmdinfo.elem.find('implicitexternsyncparams')
705 implicitExternSyncParams = [x.text for x in implicitElem.findall('param')] if implicitElem is not None else []
706
707 self.vk.commands[name] = Command(name, alias, protect, [], self.currentVersion,
708 returnType, params, instance, device,
709 tasks, queues, allowNoQueues, successcodes, errorcodes,
710 primary, secondary, renderpass, videocoding,
711 implicitExternSyncParams, legacy, cPrototype, cFunctionPointer)
712
713 #
714 # List the enum for the commands
715 # TODO - Seems empty groups like `VkDeviceDeviceMemoryReportCreateInfoEXT` do not show up in here
716 def genGroup(self, groupinfo, groupName, alias):
717 # Do not include APIs from unsupported extensions
718 if self.unsupportedExtension:
719 return
720
721 # There can be case where the Enum/Bitmask is in a protect, but the individual
722 # fields also have their own protect
723 groupProtect = self.currentExtension.protect if hasattr(self.currentExtension, 'protect') and self.currentExtension.protect is not None else None
724 enumElem = groupinfo.elem
725 bitwidth = 32 if enumElem.get('bitwidth') is None else int(enumElem.get('bitwidth'))
726 fields = []
727 if enumElem.get('type') == "enum":
728 if alias is not None:
729 self.enumAliasMap[groupName] = alias
730 return
731
732 for elem in enumElem.findall('enum'):
733 fieldName = elem.get('name')
734
735 # Do not include non-required enum constants
736 # reg.py emits the enum constants of the entire type, even constants that are part of unsupported
737 # extensions or those that are removed by <remove> elements in a given API. reg.py correctly tracks
738 # down these and also alias dependencies and marks the enum constants that are actually required
739 # with the 'required' attribute. Therefore we also have to verify that here to make sure we only
740 # include enum constants that are actually required in the target API(s).
741 if elem.get('required') is None:
742 continue
743
744 if elem.get('alias') is not None:
745 self.enumFieldAliasMap[fieldName] = elem.get('alias')
746 continue
747
748 negative = elem.get('dir') is not None
749 protect = elem.get('protect')
750 (valueInt, valueStr) = self.enumToValue(elem, True, bitwidth)
751
752 # Some values have multiple extensions (ex VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR)
753 # genGroup() lists them twice
754 if next((x for x in fields if x.name == fieldName), None) is None:
755 self.enumFieldMap[fieldName] = EnumField(fieldName, [], protect, negative, valueInt, valueStr, [])
756 fields.append(self.enumFieldMap[fieldName])
757
758 self.vk.enums[groupName] = Enum(groupName, [], groupProtect, bitwidth, True, fields, [], [])
759
760 else: # "bitmask"
761 if alias is not None:
762 self.bitmaskAliasMap[groupName] = alias
763 return
764
765 for elem in enumElem.findall('enum'):
766 flagName = elem.get('name')
767
768 # Do not include non-required enum constants
769 # reg.py emits the enum constants of the entire type, even constants that are part of unsupported
770 # extensions or those that are removed by <remove> elements in a given API. reg.py correctly tracks
771 # down these and also alias dependencies and marks the enum constants that are actually required
772 # with the 'required' attribute. Therefore we also have to verify that here to make sure we only
773 # include enum constants that are actually required in the target API(s).
774 if elem.get('required') is None:
775 continue
776
777 if elem.get('alias') is not None:
778 self.flagAliasMap[flagName] = elem.get('alias')
779 continue
780
781 protect = elem.get('protect')
782
783 (valueInt, valueStr) = self.enumToValue(elem, True, bitwidth)
784 flagZero = valueInt == 0
785 flagMultiBit = False
786 # if flag uses 'value' instead of 'bitpos', will be zero or a mask
787 if elem.get('bitpos') is None and elem.get('value'):
788 flagMultiBit = valueInt != 0
789
790 # Some values have multiple extensions (ex VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT)
791 # genGroup() lists them twice
792 if next((x for x in fields if x.name == flagName), None) is None:
793 self.flagMap[flagName] = Flag(flagName, [], protect, valueInt, valueStr, flagMultiBit, flagZero, [])
794 fields.append(self.flagMap[flagName])
795
796 flagName = groupName.replace('FlagBits', 'Flags')
797 self.vk.bitmasks[groupName] = Bitmask(groupName, [], flagName, groupProtect, bitwidth, True, fields, [], [])
798
799 def genType(self, typeInfo, typeName, alias):
800 OutputGenerator.genType(self, typeInfo, typeName, alias)
801
802 # Do not include APIs from unsupported extensions
803 if self.unsupportedExtension:
804 return
805
806 typeElem = typeInfo.elem
807 protect = self.currentExtension.protect if hasattr(self.currentExtension, 'protect') and self.currentExtension.protect is not None else None
808 extension = [self.currentExtension.name] if self.currentExtension is not None else []
809 category = typeElem.get('category')
810 if (category == 'struct' or category == 'union'):
811 if alias is not None:
812 self.structAliasMap[typeName] = alias
813 return
814
815 union = category == 'union'
816
817 returnedOnly = boolGet(typeElem, 'returnedonly')
818 allowDuplicate = boolGet(typeElem, 'allowduplicate')
819
820 extends = splitIfGet(typeElem, 'structextends')
821 extendedBy = self.registry.validextensionstructs[typeName] if len(self.registry.validextensionstructs[typeName]) > 0 else []
822
823 membersElem = typeInfo.elem.findall('.//member')
824 members = []
825 sType = None
826
827 for member in membersElem:
828 for comment in member.findall('comment'):
829 member.remove(comment)
830
831 name = textIfFind(member, 'name')
832 type = textIfFind(member, 'type')
833 sType = member.get('values') if member.get('values') is not None else sType
834 noautovalidity = boolGet(member, 'noautovalidity')
835 limittype = member.get('limittype')
836
837 (externSync, externSyncPointer) = externSyncGet(member)
838 # No cases currently where a subtype of a struct is marked as externally synchronized.
839 assert externSyncPointer is None
840
841 nullTerminated = False
842 length = member.get('altlen') if member.get('altlen') is not None else member.get('len')
843 if length:
844 # we will either find it like "null-terminated" or "enabledExtensionCount,null-terminated"
845 # This finds both
846 nullTerminated = 'null-terminated' in length
847 length = length.replace(',null-terminated', '') if 'null-terminated' in length else length
848 length = None if length == 'null-terminated' else length
849
850 cdecl = self.makeCParamDecl(member, 0)
851 fullType = ' '.join(cdecl[:cdecl.rfind(name)].split())
852 pointer = '*' in cdecl or type.startswith('PFN_')
853 const = 'const' in cdecl
854 # Some structs like VkTransformMatrixKHR have a 2D array
855 fixedSizeArray = [x[:-1] for x in cdecl.split('[') if x.endswith(']')]
856
857 if fixedSizeArray and not length:
858 length = ','.join(fixedSizeArray)
859
860 # Handle C bit field members
861 bitFieldWidth = int(cdecl.split(':')[1]) if ':' in cdecl else None
862
863 selector = member.get('selector') if not union else None
864 selection = member.get('selection') if union else None
865 selections = []
866 if selection:
867 selections = [s for s in selection.split(',')]
868
869 # if a pointer, this can be a something like:
870 # optional="true,false" for ppGeometries
871 # optional="false,true" for pPhysicalDeviceCount
872 # the first is if the variable itself is optional
873 # the second is the value of the pointer is optional;
874 optionalValues = splitIfGet(member, 'optional')
875 optional = len(optionalValues) > 0 and optionalValues[0].lower() == "true"
876 optionalPointer = len(optionalValues) > 1 and optionalValues[1].lower() == "true"
877
878 members.append(Member(name, type, fullType, noautovalidity, limittype,
879 const, length, nullTerminated, pointer, fixedSizeArray,
880 optional, optionalPointer,
881 externSync, cdecl, bitFieldWidth, selector, selections))
882
883 self.vk.structs[typeName] = Struct(typeName, [], extension, self.currentVersion, protect, members,
884 union, returnedOnly, sType, allowDuplicate, extends, extendedBy)
885
886 elif category == 'handle':
887 if alias is not None:
888 self.handleAliasMap[typeName] = alias
889 return
890 type = typeElem.get('objtypeenum')
891
892 # will resolve these later, the VulkanObjectType does not list things in dependent order
893 parent = typeElem.get('parent')
894 instance = typeName == 'VkInstance'
895 device = typeName == 'VkDevice'
896
897 dispatchable = typeElem.find('type').text == 'VK_DEFINE_HANDLE'
898
899 self.vk.handles[typeName] = Handle(typeName, [], type, protect, parent, instance, device, dispatchable, extension)
900
901 elif category == 'define':
902 if typeName == 'VK_HEADER_VERSION':
903 self.vk.headerVersion = typeElem.find('name').tail.strip()
904
905 elif category == 'bitmask':
906 if alias is not None:
907 self.flagsAliasMap[typeName] = alias
908 return
909
910 # Bitmask types, i.e. flags
911 baseFlagsType = typeElem.find('type').text
912 bitWidth = 64 if baseFlagsType == 'VkFlags64' else 32
913
914 # Bitmask enum type is either in the 'requires' or 'bitvalues' attribute
915 # (for some reason there are two conventions)
916 bitmaskName = typeElem.get('bitvalues')
917 if bitmaskName is None:
918 bitmaskName = typeElem.get('requires')
919
920 self.vk.flags[typeName] = Flags(typeName, [], bitmaskName, protect, baseFlagsType, bitWidth, True, extension)
921
922 else:
923 # not all categories are used
924 # 'group'/'enum' are routed to genGroup instead
925 # 'basetype'/'include' are only for headers
926 # 'funcpointer` ignore until needed
927 return
928
929 def genSpirv(self, spirvinfo, spirvName, alias):
930 OutputGenerator.genSpirv(self, spirvinfo, spirvName, alias)
931 spirvElem = spirvinfo.elem
932 name = spirvElem.get('name')
933 extension = True if spirvElem.tag == 'spirvextension' else False
934 capability = not extension
935
936 enables = []
937 for elem in spirvElem:
938 version = elem.attrib.get('version')
939 extensionEnable = elem.attrib.get('extension')
940 struct = elem.attrib.get('struct')
941 feature = elem.attrib.get('feature')
942 requires = elem.attrib.get('requires')
943 propertyEnable = elem.attrib.get('property')
944 member = elem.attrib.get('member')
945 value = elem.attrib.get('value')
946 enables.append(SpirvEnables(version, extensionEnable, struct, feature,
947 requires, propertyEnable, member, value))
948
949 self.vk.spirv.append(Spirv(name, extension, capability, enables))
950
951 def genFormat(self, format, formatinfo, alias):
952 OutputGenerator.genFormat(self, format, formatinfo, alias)
953 formatElem = format.elem
954 name = formatElem.get('name')
955
956 components = []
957 for component in formatElem.iterfind('component'):
958 type = component.get('name')
959 bits = component.get('bits')
960 numericFormat = component.get('numericFormat')
961 planeIndex = intIfGet(component, 'planeIndex')
962 components.append(FormatComponent(type, bits, numericFormat, planeIndex))
963
964 planes = []
965 for plane in formatElem.iterfind('plane'):
966 index = int(plane.get('index'))
967 widthDivisor = int(plane.get('widthDivisor'))
968 heightDivisor = int(plane.get('heightDivisor'))
969 compatible = plane.get('compatible')
970 planes.append(FormatPlane(index, widthDivisor, heightDivisor, compatible))
971
972 className = formatElem.get('class')
973 blockSize = int(formatElem.get('blockSize'))
974 texelsPerBlock = int(formatElem.get('texelsPerBlock'))
975 blockExtent = splitIfGet(formatElem, 'blockExtent')
976 packed = intIfGet(formatElem, 'packed')
977 chroma = formatElem.get('chroma')
978 compressed = formatElem.get('compressed')
979 spirvImageFormat = formatElem.find('spirvimageformat')
980 if spirvImageFormat is not None:
981 spirvImageFormat = spirvImageFormat.get('name')
982
983 self.vk.formats[name] = Format(name, className, blockSize, texelsPerBlock,
984 blockExtent, packed, chroma, compressed,
985 components, planes, spirvImageFormat)
986
987 def genSyncStage(self, sync):
988 OutputGenerator.genSyncStage(self, sync)
989 syncElem = sync.elem
990
991 support = maxSyncSupport
992 supportElem = syncElem.find('syncsupport')
993 if supportElem is not None:
994 queues = splitIfGet(supportElem, 'queues')
995 stageNames = splitIfGet(supportElem, 'stage')
996 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None
997 support = SyncSupport(queues, stages, False)
998
999 equivalent = maxSyncEquivalent
1000 equivalentElem = syncElem.find('syncequivalent')
1001 if equivalentElem is not None:
1002 stageNames = splitIfGet(equivalentElem, 'stage')
1003 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None
1004 accessNames = splitIfGet(equivalentElem, 'access')
1005 accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if len(accessNames) > 0 else None
1006 equivalent = SyncEquivalent(stages, accesses, False)
1007
1008 flagName = syncElem.get('name')
1009 flag = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name == flagName]
1010 # This check is needed because not all API variants have VK_KHR_synchronization2
1011 if flag:
1012 self.vk.syncStage.append(SyncStage(flag[0], support, equivalent))
1013
1014 def genSyncAccess(self, sync):
1015 OutputGenerator.genSyncAccess(self, sync)
1016 syncElem = sync.elem
1017
1018 support = maxSyncSupport
1019 supportElem = syncElem.find('syncsupport')
1020 if supportElem is not None:
1021 queues = splitIfGet(supportElem, 'queues')
1022 stageNames = splitIfGet(supportElem, 'stage')
1023 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None
1024 support = SyncSupport(queues, stages, False)
1025
1026 equivalent = maxSyncEquivalent
1027 equivalentElem = syncElem.find('syncequivalent')
1028 if equivalentElem is not None:
1029 stageNames = splitIfGet(equivalentElem, 'stage')
1030 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None
1031 accessNames = splitIfGet(equivalentElem, 'access')
1032 accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if len(accessNames) > 0 else None
1033 equivalent = SyncEquivalent(stages, accesses, False)
1034
1035 flagName = syncElem.get('name')
1036 flag = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name == flagName]
1037 # This check is needed because not all API variants have VK_KHR_synchronization2
1038 if flag:
1039 self.vk.syncAccess.append(SyncAccess(flag[0], support, equivalent))
1040
1041 def genSyncPipeline(self, sync):
1042 OutputGenerator.genSyncPipeline(self, sync)
1043 syncElem = sync.elem
1044 name = syncElem.get('name')
1045 depends = splitIfGet(syncElem, 'depends')
1046 stages = []
1047 for stageElem in syncElem.findall('syncpipelinestage'):
1048 order = stageElem.get('order')
1049 before = stageElem.get('before')
1050 after = stageElem.get('after')
1051 value = stageElem.text
1052 stages.append(SyncPipelineStage(order, before, after, value))
1053
1054 self.vk.syncPipeline.append(SyncPipeline(name, depends, stages))
1055
1056 #
1057 # This object handles all the parsing from the video.xml (i.e. Video Std header definitions)
1058 # It will fill in video standard definitions into the VulkanObject
1059 class _VideoStdGenerator(BaseGenerator):
1060 def __init__(self):
1061 BaseGenerator.__init__(self)
1062 self.vk.videoStd = VideoStd()
1063
1064 # Track the current Video Std header we are processing
1065 self.currentVideoStdHeader = None
1066
1067 def write(self, data):
1068 # We do not write anything here
1069 return
1070
1071 def beginFile(self, genOpts):
1072 # We intentionally skip default BaseGenerator behavior
1073 OutputGenerator.beginFile(self, genOpts)
1074
1075 def endFile(self):
1076 # Move parsed definitions to the Video Std definitions
1077 self.vk.videoStd.enums = self.vk.enums
1078 self.vk.videoStd.structs = self.vk.structs
1079 self.vk.videoStd.constants = self.vk.constants
1080
1081 # We intentionally skip default BaseGenerator behavior
1082 OutputGenerator.endFile(self)
1083
1084 def beginFeature(self, interface, emit):
1085 # We intentionally skip default BaseGenerator behavior
1086 OutputGenerator.beginFeature(self, interface, emit)
1087
1088 # Only "extension" is possible in the video.xml, identifying the Video Std header
1089 assert interface.tag == 'extension'
1090 name = interface.get('name')
1091 version: (str | None) = None
1092 depends: list[str] = []
1093
1094 # Handle Video Std header version constant
1095 for enum in interface.findall('require/enum[@value]'):
1096 enumName = enum.get('name')
1097 if enumName.endswith('_SPEC_VERSION'):
1098 version = enum.get('value')
1099
1100 # Handle dependencies on other Video Std headers
1101 for type in interface.findall('require/type[@name]'):
1102 typeName = type.get('name')
1103 if typeName.startswith('vk_video/'):
1104 depends.append(typeName[len('vk_video/'):-len('.h')])
1105
1106 headerFile = f'vk_video/{name}.h'
1107
1108 self.vk.videoStd.headers[name] = VideoStdHeader(name, version, headerFile, depends)
1109
1110 self.currentVideoStdHeader = self.vk.videoStd.headers[name]
1111
1112 # Handle constants here as that seems the most straightforward
1113 constantNames = []
1114 for enum in interface.findall('require/enum[@type]'):
1115 constantNames.append(enum.get('name'))
1116 self.addConstants(constantNames)
1117 for constantName in constantNames:
1118 self.vk.constants[constantName].videoStdHeader = self.currentVideoStdHeader.name
1119
1120 def endFeature(self):
1121 self.currentVideoStdHeader = None
1122
1123 # We intentionally skip default BaseGenerator behavior
1124 OutputGenerator.endFeature(self)
1125
1126 def genCmd(self, cmdinfo, name, alias):
1127 # video.xml should not contain any commands
1128 assert False
1129
1130 def genGroup(self, groupinfo, groupName, alias):
1131 BaseGenerator.genGroup(self, groupinfo, groupName, alias)
1132
1133 # We are supposed to be inside a video std header
1134 assert self.currentVideoStdHeader is not None
1135
1136 # Mark the enum with the Video Std header it comes from
1137 if groupinfo.elem.get('type') == 'enum':
1138 assert alias is None
1139 self.vk.enums[groupName].videoStdHeader = self.currentVideoStdHeader.name
1140
1141 def genType(self, typeInfo, typeName, alias):
1142 BaseGenerator.genType(self, typeInfo, typeName, alias)
1143
1144 # We are supposed to be inside a video std header
1145 assert self.currentVideoStdHeader is not None
1146
1147 # Mark the struct with the Video Std header it comes from
1148 if typeInfo.elem.get('category') == 'struct':
1149 assert alias is None
1150 self.vk.structs[typeName].videoStdHeader = self.currentVideoStdHeader.name
1151
1152 def genSpirv(self, spirvinfo, spirvName, alias):
1153 # video.xml should not contain any SPIR-V info
1154 assert False
1155
1156 def genFormat(self, format, formatinfo, alias):
1157 # video.xml should not contain any format info
1158 assert False
1159
1160 def genSyncStage(self, sync):
1161 # video.xml should not contain any sync stage info
1162 assert False
1163
1164 def genSyncAccess(self, sync):
1165 # video.xml should not contain any sync access info
1166 assert False
1167
1168 def genSyncPipeline(self, sync):
1169 # video.xml should not contain any sync pipeline info
1170 assert False