1e5c31af7Sopenharmony_ci#!/usr/bin/python3 -i 2e5c31af7Sopenharmony_ci# 3e5c31af7Sopenharmony_ci# Copyright 2013-2024 The Khronos Group Inc. 4e5c31af7Sopenharmony_ci# 5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 6e5c31af7Sopenharmony_ci 7e5c31af7Sopenharmony_ciimport re 8e5c31af7Sopenharmony_cifrom collections import OrderedDict, namedtuple 9e5c31af7Sopenharmony_cifrom functools import reduce 10e5c31af7Sopenharmony_cifrom pathlib import Path 11e5c31af7Sopenharmony_ci 12e5c31af7Sopenharmony_cifrom spec_tools.conventions import ProseListFormats as plf 13e5c31af7Sopenharmony_cifrom generator import OutputGenerator, write 14e5c31af7Sopenharmony_cifrom spec_tools.attributes import ExternSyncEntry, LengthEntry 15e5c31af7Sopenharmony_cifrom spec_tools.util import (findNamedElem, findNamedObject, findTypedElem, 16e5c31af7Sopenharmony_ci getElemName, getElemType) 17e5c31af7Sopenharmony_cifrom spec_tools.validity import ValidityCollection, ValidityEntry 18e5c31af7Sopenharmony_ci 19e5c31af7Sopenharmony_ci 20e5c31af7Sopenharmony_ciclass UnhandledCaseError(RuntimeError): 21e5c31af7Sopenharmony_ci def __init__(self, msg=None): 22e5c31af7Sopenharmony_ci if msg: 23e5c31af7Sopenharmony_ci super().__init__('Got a case in the validity generator that we have not explicitly handled: ' + msg) 24e5c31af7Sopenharmony_ci else: 25e5c31af7Sopenharmony_ci super().__init__('Got a case in the validity generator that we have not explicitly handled.') 26e5c31af7Sopenharmony_ci 27e5c31af7Sopenharmony_ci 28e5c31af7Sopenharmony_cidef _genericIterateIntersection(a, b): 29e5c31af7Sopenharmony_ci """Iterate through all elements in a that are also in b. 30e5c31af7Sopenharmony_ci 31e5c31af7Sopenharmony_ci Somewhat like a set's intersection(), 32e5c31af7Sopenharmony_ci but not type-specific so it can work with OrderedDicts, etc. 33e5c31af7Sopenharmony_ci It also returns a generator instead of a set, 34e5c31af7Sopenharmony_ci so you can pick a preferred container type, 35e5c31af7Sopenharmony_ci if any. 36e5c31af7Sopenharmony_ci """ 37e5c31af7Sopenharmony_ci return (x for x in a if x in b) 38e5c31af7Sopenharmony_ci 39e5c31af7Sopenharmony_ci 40e5c31af7Sopenharmony_cidef _make_ordered_dict(gen): 41e5c31af7Sopenharmony_ci """Make an ordered dict (with None as the values) from a generator.""" 42e5c31af7Sopenharmony_ci return OrderedDict(((x, None) for x in gen)) 43e5c31af7Sopenharmony_ci 44e5c31af7Sopenharmony_ci 45e5c31af7Sopenharmony_cidef _orderedDictIntersection(a, b): 46e5c31af7Sopenharmony_ci return _make_ordered_dict(_genericIterateIntersection(a, b)) 47e5c31af7Sopenharmony_ci 48e5c31af7Sopenharmony_ci 49e5c31af7Sopenharmony_cidef _genericIsDisjoint(a, b): 50e5c31af7Sopenharmony_ci """Return true if nothing in a is also in b. 51e5c31af7Sopenharmony_ci 52e5c31af7Sopenharmony_ci Like a set's is_disjoint(), 53e5c31af7Sopenharmony_ci but not type-specific so it can work with OrderedDicts, etc. 54e5c31af7Sopenharmony_ci """ 55e5c31af7Sopenharmony_ci for _ in _genericIterateIntersection(a, b): 56e5c31af7Sopenharmony_ci return False 57e5c31af7Sopenharmony_ci # if we never enter the loop... 58e5c31af7Sopenharmony_ci return True 59e5c31af7Sopenharmony_ci 60e5c31af7Sopenharmony_ci 61e5c31af7Sopenharmony_ciclass ValidityOutputGenerator(OutputGenerator): 62e5c31af7Sopenharmony_ci """ValidityOutputGenerator - subclass of OutputGenerator. 63e5c31af7Sopenharmony_ci 64e5c31af7Sopenharmony_ci Generates AsciiDoc includes of valid usage information, for reference 65e5c31af7Sopenharmony_ci pages and the specification. Similar to DocOutputGenerator. 66e5c31af7Sopenharmony_ci 67e5c31af7Sopenharmony_ci ---- methods ---- 68e5c31af7Sopenharmony_ci ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for 69e5c31af7Sopenharmony_ci OutputGenerator. Defines additional internal state. 70e5c31af7Sopenharmony_ci ---- methods overriding base class ---- 71e5c31af7Sopenharmony_ci beginFile(genOpts) 72e5c31af7Sopenharmony_ci endFile() 73e5c31af7Sopenharmony_ci beginFeature(interface, emit) 74e5c31af7Sopenharmony_ci endFeature() 75e5c31af7Sopenharmony_ci genCmd(cmdinfo) 76e5c31af7Sopenharmony_ci """ 77e5c31af7Sopenharmony_ci 78e5c31af7Sopenharmony_ci def __init__(self, *args, **kwargs): 79e5c31af7Sopenharmony_ci super().__init__(*args, **kwargs) 80e5c31af7Sopenharmony_ci 81e5c31af7Sopenharmony_ci self.currentExtension = '' 82e5c31af7Sopenharmony_ci 83e5c31af7Sopenharmony_ci # Tracks whether we are tracing operations 84e5c31af7Sopenharmony_ci self.trace = False 85e5c31af7Sopenharmony_ci 86e5c31af7Sopenharmony_ci @property 87e5c31af7Sopenharmony_ci def null(self): 88e5c31af7Sopenharmony_ci """Preferred spelling of NULL. 89e5c31af7Sopenharmony_ci 90e5c31af7Sopenharmony_ci Delegates to the object implementing ConventionsBase. 91e5c31af7Sopenharmony_ci """ 92e5c31af7Sopenharmony_ci return self.conventions.null 93e5c31af7Sopenharmony_ci 94e5c31af7Sopenharmony_ci @property 95e5c31af7Sopenharmony_ci def structtype_member_name(self): 96e5c31af7Sopenharmony_ci """Return name of the structure type member. 97e5c31af7Sopenharmony_ci 98e5c31af7Sopenharmony_ci Delegates to the object implementing ConventionsBase. 99e5c31af7Sopenharmony_ci """ 100e5c31af7Sopenharmony_ci return self.conventions.structtype_member_name 101e5c31af7Sopenharmony_ci 102e5c31af7Sopenharmony_ci @property 103e5c31af7Sopenharmony_ci def nextpointer_member_name(self): 104e5c31af7Sopenharmony_ci """Return name of the structure pointer chain member. 105e5c31af7Sopenharmony_ci 106e5c31af7Sopenharmony_ci Delegates to the object implementing ConventionsBase. 107e5c31af7Sopenharmony_ci """ 108e5c31af7Sopenharmony_ci return self.conventions.nextpointer_member_name 109e5c31af7Sopenharmony_ci 110e5c31af7Sopenharmony_ci def makeProseList(self, elements, fmt=plf.AND, 111e5c31af7Sopenharmony_ci comma_for_two_elts=False, *args, **kwargs): 112e5c31af7Sopenharmony_ci """Make a (comma-separated) list for use in prose. 113e5c31af7Sopenharmony_ci 114e5c31af7Sopenharmony_ci Adds a connective (by default, 'and') 115e5c31af7Sopenharmony_ci before the last element if there are more than 1. 116e5c31af7Sopenharmony_ci 117e5c31af7Sopenharmony_ci Optionally adds a quantifier (like 'any') before a list of 2 or more, 118e5c31af7Sopenharmony_ci if specified by fmt. 119e5c31af7Sopenharmony_ci 120e5c31af7Sopenharmony_ci Delegates to the object implementing ConventionsBase. 121e5c31af7Sopenharmony_ci """ 122e5c31af7Sopenharmony_ci if not elements: 123e5c31af7Sopenharmony_ci raise ValueError( 124e5c31af7Sopenharmony_ci 'Cannot pass an empty list if you are trying to make a prose list.') 125e5c31af7Sopenharmony_ci return self.conventions.makeProseList(elements, 126e5c31af7Sopenharmony_ci fmt, 127e5c31af7Sopenharmony_ci with_verb=False, 128e5c31af7Sopenharmony_ci comma_for_two_elts=comma_for_two_elts, 129e5c31af7Sopenharmony_ci *args, **kwargs) 130e5c31af7Sopenharmony_ci 131e5c31af7Sopenharmony_ci def makeProseListIs(self, elements, fmt=plf.AND, 132e5c31af7Sopenharmony_ci comma_for_two_elts=False, *args, **kwargs): 133e5c31af7Sopenharmony_ci """Make a (comma-separated) list for use in prose, followed by either 'is' or 'are' as appropriate. 134e5c31af7Sopenharmony_ci 135e5c31af7Sopenharmony_ci Adds a connective (by default, 'and') 136e5c31af7Sopenharmony_ci before the last element if there are more than 1. 137e5c31af7Sopenharmony_ci 138e5c31af7Sopenharmony_ci Optionally adds a quantifier (like 'any') before a list of 2 or more, 139e5c31af7Sopenharmony_ci if specified by fmt. 140e5c31af7Sopenharmony_ci 141e5c31af7Sopenharmony_ci Delegates to the object implementing ConventionsBase. 142e5c31af7Sopenharmony_ci """ 143e5c31af7Sopenharmony_ci if not elements: 144e5c31af7Sopenharmony_ci raise ValueError( 145e5c31af7Sopenharmony_ci 'Cannot pass an empty list if you are trying to make a prose list.') 146e5c31af7Sopenharmony_ci return self.conventions.makeProseList(elements, 147e5c31af7Sopenharmony_ci fmt, 148e5c31af7Sopenharmony_ci with_verb=True, 149e5c31af7Sopenharmony_ci comma_for_two_elts=comma_for_two_elts, 150e5c31af7Sopenharmony_ci *args, **kwargs) 151e5c31af7Sopenharmony_ci 152e5c31af7Sopenharmony_ci def makeValidityCollection(self, entity_name): 153e5c31af7Sopenharmony_ci """Create a ValidityCollection object, passing along our Conventions.""" 154e5c31af7Sopenharmony_ci return ValidityCollection(entity_name, self.conventions) 155e5c31af7Sopenharmony_ci 156e5c31af7Sopenharmony_ci def beginFile(self, genOpts): 157e5c31af7Sopenharmony_ci if not genOpts.conventions: 158e5c31af7Sopenharmony_ci raise RuntimeError( 159e5c31af7Sopenharmony_ci 'Must specify conventions object to generator options') 160e5c31af7Sopenharmony_ci self.conventions = genOpts.conventions 161e5c31af7Sopenharmony_ci # Vulkan says 'must: be a valid pointer' a lot, OpenXR just says 162e5c31af7Sopenharmony_ci # 'must: be a pointer'. 163e5c31af7Sopenharmony_ci self.valid_pointer_text = ' '.join( 164e5c31af7Sopenharmony_ci (x for x in (self.conventions.valid_pointer_prefix, 'pointer') if x)) 165e5c31af7Sopenharmony_ci OutputGenerator.beginFile(self, genOpts) 166e5c31af7Sopenharmony_ci 167e5c31af7Sopenharmony_ci def endFile(self): 168e5c31af7Sopenharmony_ci OutputGenerator.endFile(self) 169e5c31af7Sopenharmony_ci 170e5c31af7Sopenharmony_ci def beginFeature(self, interface, emit): 171e5c31af7Sopenharmony_ci # Start processing in superclass 172e5c31af7Sopenharmony_ci OutputGenerator.beginFeature(self, interface, emit) 173e5c31af7Sopenharmony_ci self.currentExtension = interface.get('name') 174e5c31af7Sopenharmony_ci 175e5c31af7Sopenharmony_ci def endFeature(self): 176e5c31af7Sopenharmony_ci # Finish processing in superclass 177e5c31af7Sopenharmony_ci OutputGenerator.endFeature(self) 178e5c31af7Sopenharmony_ci 179e5c31af7Sopenharmony_ci @property 180e5c31af7Sopenharmony_ci def struct_macro(self): 181e5c31af7Sopenharmony_ci """Get the appropriate format macro for a structure.""" 182e5c31af7Sopenharmony_ci # delegate to conventions 183e5c31af7Sopenharmony_ci return self.conventions.struct_macro 184e5c31af7Sopenharmony_ci 185e5c31af7Sopenharmony_ci def makeStructName(self, name): 186e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for a structure to a structure type name.""" 187e5c31af7Sopenharmony_ci # delegate to conventions 188e5c31af7Sopenharmony_ci return self.conventions.makeStructName(name) 189e5c31af7Sopenharmony_ci 190e5c31af7Sopenharmony_ci def makeParameterName(self, name): 191e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for a parameter/member to a parameter name.""" 192e5c31af7Sopenharmony_ci return 'pname:' + name 193e5c31af7Sopenharmony_ci 194e5c31af7Sopenharmony_ci def makeBaseTypeName(self, name): 195e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for a 'base type' to a type name.""" 196e5c31af7Sopenharmony_ci return 'basetype:' + name 197e5c31af7Sopenharmony_ci 198e5c31af7Sopenharmony_ci def makeEnumerationName(self, name): 199e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for an enumeration type to a enum type name.""" 200e5c31af7Sopenharmony_ci return 'elink:' + name 201e5c31af7Sopenharmony_ci 202e5c31af7Sopenharmony_ci def makeFlagsName(self, name): 203e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for a flags type to a flags type name.""" 204e5c31af7Sopenharmony_ci return 'tlink:' + name 205e5c31af7Sopenharmony_ci 206e5c31af7Sopenharmony_ci def makeFuncPointerName(self, name): 207e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for a function pointer type to a type name.""" 208e5c31af7Sopenharmony_ci return 'tlink:' + name 209e5c31af7Sopenharmony_ci 210e5c31af7Sopenharmony_ci def makeExternalTypeName(self, name): 211e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for an external type like uint32_t to a type name.""" 212e5c31af7Sopenharmony_ci # delegate to conventions 213e5c31af7Sopenharmony_ci return self.conventions.makeExternalTypeName(name) 214e5c31af7Sopenharmony_ci 215e5c31af7Sopenharmony_ci def makeEnumerantName(self, name): 216e5c31af7Sopenharmony_ci """Prepend the appropriate format macro for an enumerate (value) to a enum value name.""" 217e5c31af7Sopenharmony_ci return 'ename:' + name 218e5c31af7Sopenharmony_ci 219e5c31af7Sopenharmony_ci def writeInclude(self, directory, basename, validity: ValidityCollection, 220e5c31af7Sopenharmony_ci threadsafety, commandpropertiesentry=None, 221e5c31af7Sopenharmony_ci successcodes=None, errorcodes=None): 222e5c31af7Sopenharmony_ci """Generate an include file. 223e5c31af7Sopenharmony_ci 224e5c31af7Sopenharmony_ci directory - subdirectory to put file in (absolute or relative pathname) 225e5c31af7Sopenharmony_ci basename - base name of the file 226e5c31af7Sopenharmony_ci validity - ValidityCollection to write. 227e5c31af7Sopenharmony_ci threadsafety - List (may be empty) of thread safety statements to write. 228e5c31af7Sopenharmony_ci successcodes - Optional success codes to document. 229e5c31af7Sopenharmony_ci errorcodes - Optional error codes to document. 230e5c31af7Sopenharmony_ci """ 231e5c31af7Sopenharmony_ci # Create subdirectory, if needed 232e5c31af7Sopenharmony_ci directory = Path(directory) 233e5c31af7Sopenharmony_ci if not directory.is_absolute(): 234e5c31af7Sopenharmony_ci directory = Path(self.genOpts.directory) / directory 235e5c31af7Sopenharmony_ci self.makeDir(str(directory)) 236e5c31af7Sopenharmony_ci 237e5c31af7Sopenharmony_ci # Create validity file 238e5c31af7Sopenharmony_ci filename = str(directory / f'{basename}{self.file_suffix}') 239e5c31af7Sopenharmony_ci self.logMsg('diag', '# Generating include file:', filename) 240e5c31af7Sopenharmony_ci 241e5c31af7Sopenharmony_ci with open(filename, 'w', encoding='utf-8') as fp: 242e5c31af7Sopenharmony_ci write(self.conventions.warning_comment, file=fp) 243e5c31af7Sopenharmony_ci 244e5c31af7Sopenharmony_ci # Valid Usage 245e5c31af7Sopenharmony_ci if validity: 246e5c31af7Sopenharmony_ci write('.Valid Usage (Implicit)', file=fp) 247e5c31af7Sopenharmony_ci write('****', file=fp) 248e5c31af7Sopenharmony_ci write(validity, file=fp, end='') 249e5c31af7Sopenharmony_ci write('****', file=fp) 250e5c31af7Sopenharmony_ci write('', file=fp) 251e5c31af7Sopenharmony_ci 252e5c31af7Sopenharmony_ci # Host Synchronization 253e5c31af7Sopenharmony_ci if threadsafety: 254e5c31af7Sopenharmony_ci # The heading of this block differs between projects, so an Asciidoc attribute is used. 255e5c31af7Sopenharmony_ci write('.{externsynctitle}', file=fp) 256e5c31af7Sopenharmony_ci write('****', file=fp) 257e5c31af7Sopenharmony_ci write(threadsafety, file=fp, end='') 258e5c31af7Sopenharmony_ci write('****', file=fp) 259e5c31af7Sopenharmony_ci write('', file=fp) 260e5c31af7Sopenharmony_ci 261e5c31af7Sopenharmony_ci # Command Properties - contained within a block, to avoid table numbering 262e5c31af7Sopenharmony_ci if commandpropertiesentry: 263e5c31af7Sopenharmony_ci write('.Command Properties', file=fp) 264e5c31af7Sopenharmony_ci write('****', file=fp) 265e5c31af7Sopenharmony_ci write('[options="header", width="100%"]', file=fp) 266e5c31af7Sopenharmony_ci write('|====', file=fp) 267e5c31af7Sopenharmony_ci write(self.makeCommandPropertiesTableHeader(), file=fp) 268e5c31af7Sopenharmony_ci write(commandpropertiesentry, file=fp) 269e5c31af7Sopenharmony_ci write('|====', file=fp) 270e5c31af7Sopenharmony_ci write('****', file=fp) 271e5c31af7Sopenharmony_ci write('', file=fp) 272e5c31af7Sopenharmony_ci 273e5c31af7Sopenharmony_ci # Success Codes - contained within a block, to avoid table numbering 274e5c31af7Sopenharmony_ci if successcodes or errorcodes: 275e5c31af7Sopenharmony_ci write('.Return Codes', file=fp) 276e5c31af7Sopenharmony_ci write('****', file=fp) 277e5c31af7Sopenharmony_ci if successcodes: 278e5c31af7Sopenharmony_ci write('ifndef::doctype-manpage[]', file=fp) 279e5c31af7Sopenharmony_ci write('<<fundamentals-successcodes,Success>>::', file=fp) 280e5c31af7Sopenharmony_ci write('endif::doctype-manpage[]', file=fp) 281e5c31af7Sopenharmony_ci write('ifdef::doctype-manpage[]', file=fp) 282e5c31af7Sopenharmony_ci write('On success, this command returns::', file=fp) 283e5c31af7Sopenharmony_ci write('endif::doctype-manpage[]', file=fp) 284e5c31af7Sopenharmony_ci write(successcodes, file=fp) 285e5c31af7Sopenharmony_ci if errorcodes: 286e5c31af7Sopenharmony_ci write('ifndef::doctype-manpage[]', file=fp) 287e5c31af7Sopenharmony_ci write('<<fundamentals-errorcodes,Failure>>::', file=fp) 288e5c31af7Sopenharmony_ci write('endif::doctype-manpage[]', file=fp) 289e5c31af7Sopenharmony_ci write('ifdef::doctype-manpage[]', file=fp) 290e5c31af7Sopenharmony_ci write('On failure, this command returns::', file=fp) 291e5c31af7Sopenharmony_ci write('endif::doctype-manpage[]', file=fp) 292e5c31af7Sopenharmony_ci write(errorcodes, file=fp) 293e5c31af7Sopenharmony_ci write('****', file=fp) 294e5c31af7Sopenharmony_ci write('', file=fp) 295e5c31af7Sopenharmony_ci 296e5c31af7Sopenharmony_ci def paramIsStaticArray(self, param): 297e5c31af7Sopenharmony_ci """Check if the parameter passed in is a static array.""" 298e5c31af7Sopenharmony_ci tail = param.find('name').tail 299e5c31af7Sopenharmony_ci return tail and tail[0] == '[' 300e5c31af7Sopenharmony_ci 301e5c31af7Sopenharmony_ci def paramIsConst(self, param): 302e5c31af7Sopenharmony_ci """Check if the parameter passed in has a type that mentions const.""" 303e5c31af7Sopenharmony_ci return param.text is not None and 'const' in param.text 304e5c31af7Sopenharmony_ci 305e5c31af7Sopenharmony_ci def staticArrayLength(self, param): 306e5c31af7Sopenharmony_ci """Get the length of a parameter that has been identified as a static array.""" 307e5c31af7Sopenharmony_ci paramenumsize = param.find('enum') 308e5c31af7Sopenharmony_ci if paramenumsize is not None: 309e5c31af7Sopenharmony_ci return paramenumsize.text 310e5c31af7Sopenharmony_ci # TODO switch to below when cosmetic changes OK 311e5c31af7Sopenharmony_ci # return self.makeEnumerantName(paramenumsize.text) 312e5c31af7Sopenharmony_ci 313e5c31af7Sopenharmony_ci return param.find('name').tail[1:-1] 314e5c31af7Sopenharmony_ci 315e5c31af7Sopenharmony_ci def getHandleDispatchableAncestors(self, typename): 316e5c31af7Sopenharmony_ci """Get the ancestors of a handle object.""" 317e5c31af7Sopenharmony_ci ancestors = [] 318e5c31af7Sopenharmony_ci current = typename 319e5c31af7Sopenharmony_ci while True: 320e5c31af7Sopenharmony_ci current = self.getHandleParent(current) 321e5c31af7Sopenharmony_ci if current is None: 322e5c31af7Sopenharmony_ci return ancestors 323e5c31af7Sopenharmony_ci if self.isHandleTypeDispatchable(current): 324e5c31af7Sopenharmony_ci ancestors.append(current) 325e5c31af7Sopenharmony_ci 326e5c31af7Sopenharmony_ci def isHandleTypeDispatchable(self, handlename): 327e5c31af7Sopenharmony_ci """Check if a parent object is dispatchable or not.""" 328e5c31af7Sopenharmony_ci handle = self.registry.tree.find( 329e5c31af7Sopenharmony_ci "types/type/[name='" + handlename + "'][@category='handle']") 330e5c31af7Sopenharmony_ci if handle is not None and getElemType(handle) == 'VK_DEFINE_HANDLE': 331e5c31af7Sopenharmony_ci return True 332e5c31af7Sopenharmony_ci else: 333e5c31af7Sopenharmony_ci return False 334e5c31af7Sopenharmony_ci 335e5c31af7Sopenharmony_ci def isHandleOptional(self, param, params): 336e5c31af7Sopenharmony_ci # Simple, if it is optional, return true 337e5c31af7Sopenharmony_ci if param.get('optional') is not None: 338e5c31af7Sopenharmony_ci return True 339e5c31af7Sopenharmony_ci 340e5c31af7Sopenharmony_ci # If no validity is being generated, it usually means that validity is complex and not absolute, so say yes. 341e5c31af7Sopenharmony_ci if param.get('noautovalidity') is not None: 342e5c31af7Sopenharmony_ci return True 343e5c31af7Sopenharmony_ci 344e5c31af7Sopenharmony_ci # If the parameter is an array and we have not already returned, find out if any of the len parameters are optional 345e5c31af7Sopenharmony_ci if self.paramIsArray(param): 346e5c31af7Sopenharmony_ci for length in LengthEntry.parse_len_from_param(param): 347e5c31af7Sopenharmony_ci if not length.other_param_name: 348e5c31af7Sopenharmony_ci # do not care about constants or "null-terminated" 349e5c31af7Sopenharmony_ci continue 350e5c31af7Sopenharmony_ci 351e5c31af7Sopenharmony_ci other_param = findNamedElem(params, length.other_param_name) 352e5c31af7Sopenharmony_ci if other_param is None: 353e5c31af7Sopenharmony_ci self.logMsg('warn', length.other_param_name, 354e5c31af7Sopenharmony_ci 'is listed as a length for parameter', param, 'but no such parameter exists') 355e5c31af7Sopenharmony_ci if other_param and other_param.get('optional'): 356e5c31af7Sopenharmony_ci return True 357e5c31af7Sopenharmony_ci 358e5c31af7Sopenharmony_ci return False 359e5c31af7Sopenharmony_ci 360e5c31af7Sopenharmony_ci def makeOptionalPre(self, param): 361e5c31af7Sopenharmony_ci # Do not generate this stub for bitflags 362e5c31af7Sopenharmony_ci param_name = getElemName(param) 363e5c31af7Sopenharmony_ci paramtype = getElemType(param) 364e5c31af7Sopenharmony_ci type_category = self.getTypeCategory(paramtype) 365e5c31af7Sopenharmony_ci is_optional = param.get('optional').split(',')[0] == 'true' 366e5c31af7Sopenharmony_ci if type_category != 'bitmask' and is_optional: 367e5c31af7Sopenharmony_ci if self.paramIsArray(param) or self.paramIsPointer(param): 368e5c31af7Sopenharmony_ci optional_val = self.null 369e5c31af7Sopenharmony_ci elif type_category == 'handle': 370e5c31af7Sopenharmony_ci if self.isHandleTypeDispatchable(paramtype): 371e5c31af7Sopenharmony_ci optional_val = self.null 372e5c31af7Sopenharmony_ci else: 373e5c31af7Sopenharmony_ci optional_val = 'dlink:' + self.conventions.api_prefix + 'NULL_HANDLE' 374e5c31af7Sopenharmony_ci else: 375e5c31af7Sopenharmony_ci optional_val = self.conventions.zero 376e5c31af7Sopenharmony_ci return 'If {} is not {}, '.format( 377e5c31af7Sopenharmony_ci self.makeParameterName(param_name), 378e5c31af7Sopenharmony_ci optional_val) 379e5c31af7Sopenharmony_ci 380e5c31af7Sopenharmony_ci return "" 381e5c31af7Sopenharmony_ci 382e5c31af7Sopenharmony_ci def makeParamValidityPre(self, param, params, selector): 383e5c31af7Sopenharmony_ci """Make the start of an entry for a parameter's validity, including a chunk of text if it is an array.""" 384e5c31af7Sopenharmony_ci param_name = getElemName(param) 385e5c31af7Sopenharmony_ci paramtype = getElemType(param) 386e5c31af7Sopenharmony_ci 387e5c31af7Sopenharmony_ci # General pre-amble. Check optionality and add stuff. 388e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=(param_name, 'parameter')) 389e5c31af7Sopenharmony_ci is_optional = param.get('optional') is not None and param.get('optional').split(',')[0] == 'true' 390e5c31af7Sopenharmony_ci 391e5c31af7Sopenharmony_ci # This is for a union member, and the valid member is chosen by an enum selection 392e5c31af7Sopenharmony_ci if selector: 393e5c31af7Sopenharmony_ci selection = param.get('selection') 394e5c31af7Sopenharmony_ci 395e5c31af7Sopenharmony_ci entry += 'If {} is {}, '.format( 396e5c31af7Sopenharmony_ci self.makeParameterName(selector), 397e5c31af7Sopenharmony_ci self.makeEnumerantName(selection)) 398e5c31af7Sopenharmony_ci 399e5c31af7Sopenharmony_ci if is_optional: 400e5c31af7Sopenharmony_ci entry += "and " 401e5c31af7Sopenharmony_ci optionalpre = self.makeOptionalPre(param) 402e5c31af7Sopenharmony_ci entry += optionalpre[0].lower() + optionalpre[1:] 403e5c31af7Sopenharmony_ci 404e5c31af7Sopenharmony_ci return entry 405e5c31af7Sopenharmony_ci 406e5c31af7Sopenharmony_ci if self.paramIsStaticArray(param): 407e5c31af7Sopenharmony_ci if paramtype != 'char': 408e5c31af7Sopenharmony_ci entry += 'Each element of ' 409e5c31af7Sopenharmony_ci return entry 410e5c31af7Sopenharmony_ci 411e5c31af7Sopenharmony_ci if self.paramIsArray(param) and param.get('len') != LengthEntry.NULL_TERMINATED_STRING: 412e5c31af7Sopenharmony_ci # Find all the parameters that are called out as optional, 413e5c31af7Sopenharmony_ci # so we can document that they might be zero, and the array may be ignored 414e5c31af7Sopenharmony_ci optionallengths = [] 415e5c31af7Sopenharmony_ci for length in LengthEntry.parse_len_from_param(param): 416e5c31af7Sopenharmony_ci if not length.other_param_name: 417e5c31af7Sopenharmony_ci # Only care about length entries that are parameter names 418e5c31af7Sopenharmony_ci continue 419e5c31af7Sopenharmony_ci 420e5c31af7Sopenharmony_ci other_param = findNamedElem(params, length.other_param_name) 421e5c31af7Sopenharmony_ci other_param_optional = (other_param is not None) and ( 422e5c31af7Sopenharmony_ci other_param.get('optional') is not None) 423e5c31af7Sopenharmony_ci 424e5c31af7Sopenharmony_ci if other_param is None or not other_param_optional: 425e5c31af7Sopenharmony_ci # Do not care about not-found params or non-optional params 426e5c31af7Sopenharmony_ci continue 427e5c31af7Sopenharmony_ci 428e5c31af7Sopenharmony_ci if self.paramIsPointer(other_param): 429e5c31af7Sopenharmony_ci optionallengths.append( 430e5c31af7Sopenharmony_ci 'the value referenced by ' + self.makeParameterName(length.other_param_name)) 431e5c31af7Sopenharmony_ci else: 432e5c31af7Sopenharmony_ci optionallengths.append( 433e5c31af7Sopenharmony_ci self.makeParameterName(length.other_param_name)) 434e5c31af7Sopenharmony_ci 435e5c31af7Sopenharmony_ci # Document that these arrays may be ignored if any of the length values are 0 436e5c31af7Sopenharmony_ci if optionallengths or is_optional: 437e5c31af7Sopenharmony_ci entry += 'If ' 438e5c31af7Sopenharmony_ci if optionallengths: 439e5c31af7Sopenharmony_ci entry += self.makeProseListIs(optionallengths, fmt=plf.OR) 440e5c31af7Sopenharmony_ci entry += ' not %s, ' % self.conventions.zero 441e5c31af7Sopenharmony_ci # TODO enabling this in OpenXR, as used in Vulkan, causes nonsensical things like 442e5c31af7Sopenharmony_ci # "If pname:propertyCapacityInput is not `0`, and pname:properties is not `NULL`, pname:properties must: be a pointer to an array of pname:propertyCapacityInput slink:XrApiLayerProperties structures" 443e5c31af7Sopenharmony_ci if optionallengths and is_optional: 444e5c31af7Sopenharmony_ci entry += 'and ' 445e5c31af7Sopenharmony_ci if is_optional: 446e5c31af7Sopenharmony_ci entry += self.makeParameterName(param_name) 447e5c31af7Sopenharmony_ci # TODO switch when cosmetic changes OK 448e5c31af7Sopenharmony_ci # entry += ' is not {}, '.format(self.null) 449e5c31af7Sopenharmony_ci entry += ' is not `NULL`, ' 450e5c31af7Sopenharmony_ci return entry 451e5c31af7Sopenharmony_ci 452e5c31af7Sopenharmony_ci if param.get('optional'): 453e5c31af7Sopenharmony_ci entry += self.makeOptionalPre(param) 454e5c31af7Sopenharmony_ci return entry 455e5c31af7Sopenharmony_ci 456e5c31af7Sopenharmony_ci # If none of the early returns happened, we at least return an empty 457e5c31af7Sopenharmony_ci # entry with an anchor. 458e5c31af7Sopenharmony_ci return entry 459e5c31af7Sopenharmony_ci 460e5c31af7Sopenharmony_ci def createValidationLineForParameterImpl(self, blockname, param, params, typetext, selector, parentname): 461e5c31af7Sopenharmony_ci """Make the generic validity portion used for all parameters. 462e5c31af7Sopenharmony_ci 463e5c31af7Sopenharmony_ci May return None if nothing to validate. 464e5c31af7Sopenharmony_ci """ 465e5c31af7Sopenharmony_ci if param.get('noautovalidity') is not None: 466e5c31af7Sopenharmony_ci return None 467e5c31af7Sopenharmony_ci 468e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(blockname) 469e5c31af7Sopenharmony_ci param_name = getElemName(param) 470e5c31af7Sopenharmony_ci paramtype = getElemType(param) 471e5c31af7Sopenharmony_ci 472e5c31af7Sopenharmony_ci entry = self.makeParamValidityPre(param, params, selector) 473e5c31af7Sopenharmony_ci 474e5c31af7Sopenharmony_ci # pAllocator is not supported in VulkanSC and must always be NULL 475e5c31af7Sopenharmony_ci if self.conventions.xml_api_name == "vulkansc" and param_name == 'pAllocator' and paramtype == 'VkAllocationCallbacks': 476e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=(param_name, 'null')) 477e5c31af7Sopenharmony_ci entry += 'pname:pAllocator must: be `NULL`' 478e5c31af7Sopenharmony_ci return entry 479e5c31af7Sopenharmony_ci 480e5c31af7Sopenharmony_ci # This is for a child member of a union 481e5c31af7Sopenharmony_ci if selector: 482e5c31af7Sopenharmony_ci entry += 'the {} member of {} must: be '.format(self.makeParameterName(param_name), self.makeParameterName(parentname)) 483e5c31af7Sopenharmony_ci else: 484e5c31af7Sopenharmony_ci entry += '{} must: be '.format(self.makeParameterName(param_name)) 485e5c31af7Sopenharmony_ci 486e5c31af7Sopenharmony_ci if self.paramIsStaticArray(param) and paramtype == 'char': 487e5c31af7Sopenharmony_ci # TODO this is a minor hack to determine if this is a command parameter or a struct member 488e5c31af7Sopenharmony_ci if self.paramIsConst(param) or blockname.startswith(self.conventions.type_prefix): 489e5c31af7Sopenharmony_ci entry += 'a null-terminated UTF-8 string whose length is less than or equal to ' 490e5c31af7Sopenharmony_ci entry += self.staticArrayLength(param) 491e5c31af7Sopenharmony_ci else: 492e5c31af7Sopenharmony_ci # This is a command's output parameter 493e5c31af7Sopenharmony_ci entry += 'a character array of length %s ' % self.staticArrayLength(param) 494e5c31af7Sopenharmony_ci validity += entry 495e5c31af7Sopenharmony_ci return validity 496e5c31af7Sopenharmony_ci 497e5c31af7Sopenharmony_ci elif self.paramIsArray(param): 498e5c31af7Sopenharmony_ci # Arrays. These are hard to get right, apparently 499e5c31af7Sopenharmony_ci 500e5c31af7Sopenharmony_ci lengths = LengthEntry.parse_len_from_param(param) 501e5c31af7Sopenharmony_ci 502e5c31af7Sopenharmony_ci for i, length in enumerate(LengthEntry.parse_len_from_param(param)): 503e5c31af7Sopenharmony_ci if i == 0: 504e5c31af7Sopenharmony_ci # If the first index, make it singular. 505e5c31af7Sopenharmony_ci entry += 'a ' 506e5c31af7Sopenharmony_ci array_text = 'an array' 507e5c31af7Sopenharmony_ci pointer_text = self.valid_pointer_text 508e5c31af7Sopenharmony_ci else: 509e5c31af7Sopenharmony_ci array_text = 'arrays' 510e5c31af7Sopenharmony_ci pointer_text = self.valid_pointer_text + 's' 511e5c31af7Sopenharmony_ci 512e5c31af7Sopenharmony_ci if length.null_terminated: 513e5c31af7Sopenharmony_ci # This should always be the last thing. 514e5c31af7Sopenharmony_ci # If it ever is not for some bizarre reason, then this 515e5c31af7Sopenharmony_ci # will need some massaging. 516e5c31af7Sopenharmony_ci entry += 'null-terminated ' 517e5c31af7Sopenharmony_ci elif length.number == 1: 518e5c31af7Sopenharmony_ci entry += pointer_text 519e5c31af7Sopenharmony_ci entry += ' to ' 520e5c31af7Sopenharmony_ci else: 521e5c31af7Sopenharmony_ci entry += pointer_text 522e5c31af7Sopenharmony_ci entry += ' to ' 523e5c31af7Sopenharmony_ci entry += array_text 524e5c31af7Sopenharmony_ci entry += ' of ' 525e5c31af7Sopenharmony_ci # Handle equations, which are currently denoted with latex 526e5c31af7Sopenharmony_ci if length.math: 527e5c31af7Sopenharmony_ci # Handle equations, which are currently denoted with latex 528e5c31af7Sopenharmony_ci entry += str(length) 529e5c31af7Sopenharmony_ci else: 530e5c31af7Sopenharmony_ci entry += self.makeParameterName(str(length)) 531e5c31af7Sopenharmony_ci entry += ' ' 532e5c31af7Sopenharmony_ci 533e5c31af7Sopenharmony_ci # Void pointers do not actually point at anything - remove the word "to" 534e5c31af7Sopenharmony_ci if paramtype == 'void': 535e5c31af7Sopenharmony_ci if lengths[-1].number == 1: 536e5c31af7Sopenharmony_ci if len(lengths) > 1: 537e5c31af7Sopenharmony_ci # Take care of the extra s added by the post array chunk function. #HACK# 538e5c31af7Sopenharmony_ci entry.drop_end(5) 539e5c31af7Sopenharmony_ci else: 540e5c31af7Sopenharmony_ci entry.drop_end(4) 541e5c31af7Sopenharmony_ci 542e5c31af7Sopenharmony_ci # This has not been hit, so this has not been tested recently. 543e5c31af7Sopenharmony_ci raise UnhandledCaseError( 544e5c31af7Sopenharmony_ci "Got void pointer param/member with last length 1") 545e5c31af7Sopenharmony_ci else: 546e5c31af7Sopenharmony_ci # An array of void values is a byte array. 547e5c31af7Sopenharmony_ci entry += 'byte' 548e5c31af7Sopenharmony_ci 549e5c31af7Sopenharmony_ci elif paramtype == 'char': 550e5c31af7Sopenharmony_ci # A null terminated array of chars is a string 551e5c31af7Sopenharmony_ci if lengths[-1].null_terminated: 552e5c31af7Sopenharmony_ci entry += 'UTF-8 string' 553e5c31af7Sopenharmony_ci else: 554e5c31af7Sopenharmony_ci # Else it is just a bunch of chars 555e5c31af7Sopenharmony_ci entry += 'char value' 556e5c31af7Sopenharmony_ci 557e5c31af7Sopenharmony_ci elif self.paramIsConst(param): 558e5c31af7Sopenharmony_ci # If a value is "const" that means it will not get modified, 559e5c31af7Sopenharmony_ci # so it must be valid going into the function. 560e5c31af7Sopenharmony_ci if 'const' in param.text: 561e5c31af7Sopenharmony_ci 562e5c31af7Sopenharmony_ci if not self.isStructAlwaysValid(paramtype): 563e5c31af7Sopenharmony_ci entry += 'valid ' 564e5c31af7Sopenharmony_ci 565e5c31af7Sopenharmony_ci # Check if the array elements are optional 566e5c31af7Sopenharmony_ci array_element_optional = param.get('optional') is not None \ 567e5c31af7Sopenharmony_ci and len(param.get('optional').split(',')) == len(LengthEntry.parse_len_from_param(param)) + 1 \ 568e5c31af7Sopenharmony_ci and param.get('optional').split(',')[-1] == 'true' 569e5c31af7Sopenharmony_ci if array_element_optional and self.getTypeCategory(paramtype) != 'bitmask': # bitmask is handled later 570e5c31af7Sopenharmony_ci entry += 'or dlink:' + self.conventions.api_prefix + 'NULL_HANDLE ' 571e5c31af7Sopenharmony_ci 572e5c31af7Sopenharmony_ci entry += typetext 573e5c31af7Sopenharmony_ci 574e5c31af7Sopenharmony_ci # pluralize 575e5c31af7Sopenharmony_ci if len(lengths) > 1 or (lengths[0] != 1 and not lengths[0].null_terminated): 576e5c31af7Sopenharmony_ci entry += 's' 577e5c31af7Sopenharmony_ci 578e5c31af7Sopenharmony_ci return self.handleRequiredBitmask(blockname, param, paramtype, entry, 'true' if array_element_optional else None) 579e5c31af7Sopenharmony_ci 580e5c31af7Sopenharmony_ci if self.paramIsPointer(param): 581e5c31af7Sopenharmony_ci # Handle pointers - which are really special case arrays (i.e. 582e5c31af7Sopenharmony_ci # they do not have a length) 583e5c31af7Sopenharmony_ci # TODO should do something here if someone ever uses some intricate comma-separated `optional` 584e5c31af7Sopenharmony_ci pointercount = param.find('type').tail.count('*') 585e5c31af7Sopenharmony_ci 586e5c31af7Sopenharmony_ci # Treat void* as an int 587e5c31af7Sopenharmony_ci if paramtype == 'void': 588e5c31af7Sopenharmony_ci optional = param.get('optional') 589e5c31af7Sopenharmony_ci # If there is only void*, it is just optional int - we do not need any language. 590e5c31af7Sopenharmony_ci if pointercount == 1 and optional is not None: 591e5c31af7Sopenharmony_ci return None # early return 592e5c31af7Sopenharmony_ci # Treat the inner-most void* as an int 593e5c31af7Sopenharmony_ci pointercount -= 1 594e5c31af7Sopenharmony_ci 595e5c31af7Sopenharmony_ci # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that. 596e5c31af7Sopenharmony_ci entry += 'a ' 597e5c31af7Sopenharmony_ci entry += (self.valid_pointer_text + ' to a ') * pointercount 598e5c31af7Sopenharmony_ci 599e5c31af7Sopenharmony_ci # Handle void* and pointers to it 600e5c31af7Sopenharmony_ci if paramtype == 'void': 601e5c31af7Sopenharmony_ci if optional is None or optional.split(',')[pointercount]: 602e5c31af7Sopenharmony_ci # The last void* is just optional int (e.g. to be filled by the impl.) 603e5c31af7Sopenharmony_ci typetext = 'pointer value' 604e5c31af7Sopenharmony_ci 605e5c31af7Sopenharmony_ci # If a value is "const" that means it will not get modified, so 606e5c31af7Sopenharmony_ci # it must be valid going into the function. 607e5c31af7Sopenharmony_ci elif self.paramIsConst(param) and paramtype != 'void': 608e5c31af7Sopenharmony_ci entry += 'valid ' 609e5c31af7Sopenharmony_ci 610e5c31af7Sopenharmony_ci entry += typetext 611e5c31af7Sopenharmony_ci return self.handleRequiredBitmask(blockname, param, paramtype, entry, param.get('optional')) 612e5c31af7Sopenharmony_ci 613e5c31af7Sopenharmony_ci # Add additional line for non-optional bitmasks 614e5c31af7Sopenharmony_ci if self.getTypeCategory(paramtype) == 'bitmask': 615e5c31af7Sopenharmony_ci # TODO does not really handle if someone tries something like optional="true,false" 616e5c31af7Sopenharmony_ci # TODO OpenXR has 0 or a valid combination of flags, for optional things. 617e5c31af7Sopenharmony_ci # Vulkan does not... 618e5c31af7Sopenharmony_ci # isMandatory = param.get('optional') is None 619e5c31af7Sopenharmony_ci # if not isMandatory: 620e5c31af7Sopenharmony_ci # entry += self.conventions.zero 621e5c31af7Sopenharmony_ci # entry += ' or ' 622e5c31af7Sopenharmony_ci # Non-pointer, non-optional things must be valid 623e5c31af7Sopenharmony_ci entry += 'a valid {}'.format(typetext) 624e5c31af7Sopenharmony_ci 625e5c31af7Sopenharmony_ci return self.handleRequiredBitmask(blockname, param, paramtype, entry, param.get('optional')) 626e5c31af7Sopenharmony_ci 627e5c31af7Sopenharmony_ci # Non-pointer, non-optional things must be valid 628e5c31af7Sopenharmony_ci entry += 'a valid {}'.format(typetext) 629e5c31af7Sopenharmony_ci return entry 630e5c31af7Sopenharmony_ci 631e5c31af7Sopenharmony_ci def handleRequiredBitmask(self, blockname, param, paramtype, entry, optional): 632e5c31af7Sopenharmony_ci # TODO does not really handle if someone tries something like optional="true,false" 633e5c31af7Sopenharmony_ci if self.getTypeCategory(paramtype) != 'bitmask' or optional == 'true': 634e5c31af7Sopenharmony_ci return entry 635e5c31af7Sopenharmony_ci if self.paramIsPointer(param) and not self.paramIsArray(param): 636e5c31af7Sopenharmony_ci # This is presumably an output parameter 637e5c31af7Sopenharmony_ci return entry 638e5c31af7Sopenharmony_ci 639e5c31af7Sopenharmony_ci param_name = getElemName(param) 640e5c31af7Sopenharmony_ci # If mandatory, then we need two entries instead of just one. 641e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(blockname) 642e5c31af7Sopenharmony_ci validity += entry 643e5c31af7Sopenharmony_ci 644e5c31af7Sopenharmony_ci entry2 = ValidityEntry(anchor=(param_name, 'requiredbitmask')) 645e5c31af7Sopenharmony_ci if self.paramIsArray(param): 646e5c31af7Sopenharmony_ci entry2 += 'Each element of ' 647e5c31af7Sopenharmony_ci entry2 += '{} must: not be {}'.format( 648e5c31af7Sopenharmony_ci self.makeParameterName(param_name), self.conventions.zero) 649e5c31af7Sopenharmony_ci validity += entry2 650e5c31af7Sopenharmony_ci return validity 651e5c31af7Sopenharmony_ci 652e5c31af7Sopenharmony_ci def createValidationLineForParameter(self, blockname, param, params, typecategory, selector, parentname): 653e5c31af7Sopenharmony_ci """Make an entire validation entry for a given parameter.""" 654e5c31af7Sopenharmony_ci param_name = getElemName(param) 655e5c31af7Sopenharmony_ci paramtype = getElemType(param) 656e5c31af7Sopenharmony_ci 657e5c31af7Sopenharmony_ci is_array = self.paramIsArray(param) 658e5c31af7Sopenharmony_ci is_pointer = self.paramIsPointer(param) 659e5c31af7Sopenharmony_ci needs_recursive_validity = (is_array 660e5c31af7Sopenharmony_ci or is_pointer 661e5c31af7Sopenharmony_ci or not self.isStructAlwaysValid(paramtype)) 662e5c31af7Sopenharmony_ci typetext = None 663e5c31af7Sopenharmony_ci if paramtype in ('void', 'char'): 664e5c31af7Sopenharmony_ci # Chars and void are special cases - we call the impl function, 665e5c31af7Sopenharmony_ci # but do not use the typetext. 666e5c31af7Sopenharmony_ci # A null-terminated char array is a string, else it is chars. 667e5c31af7Sopenharmony_ci # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular 668e5c31af7Sopenharmony_ci typetext = '' 669e5c31af7Sopenharmony_ci 670e5c31af7Sopenharmony_ci elif typecategory == 'bitmask': 671e5c31af7Sopenharmony_ci bitsname = paramtype.replace('Flags', 'FlagBits') 672e5c31af7Sopenharmony_ci bitselem = self.registry.tree.find("enums[@name='" + bitsname + "']") 673e5c31af7Sopenharmony_ci 674e5c31af7Sopenharmony_ci # If bitsname is an alias, then use the alias to get bitselem. 675e5c31af7Sopenharmony_ci typeElem = self.registry.lookupElementInfo(bitsname, self.registry.typedict) 676e5c31af7Sopenharmony_ci if typeElem is not None: 677e5c31af7Sopenharmony_ci alias = self.registry.getAlias(typeElem.elem, self.registry.typedict) 678e5c31af7Sopenharmony_ci if alias is not None: 679e5c31af7Sopenharmony_ci bitselem = self.registry.tree.find("enums[@name='" + alias + "']") 680e5c31af7Sopenharmony_ci 681e5c31af7Sopenharmony_ci if bitselem is None or len(bitselem.findall('enum[@required="true"]')) == 0: 682e5c31af7Sopenharmony_ci # Empty bit mask: presumably just a placeholder (or only in 683e5c31af7Sopenharmony_ci # an extension not enabled for this build) 684e5c31af7Sopenharmony_ci entry = ValidityEntry( 685e5c31af7Sopenharmony_ci anchor=(param_name, 'zerobitmask')) 686e5c31af7Sopenharmony_ci entry += self.makeParameterName(param_name) 687e5c31af7Sopenharmony_ci entry += ' must: be ' 688e5c31af7Sopenharmony_ci entry += self.conventions.zero 689e5c31af7Sopenharmony_ci # Early return 690e5c31af7Sopenharmony_ci return entry 691e5c31af7Sopenharmony_ci 692e5c31af7Sopenharmony_ci is_const = self.paramIsConst(param) 693e5c31af7Sopenharmony_ci 694e5c31af7Sopenharmony_ci if is_array: 695e5c31af7Sopenharmony_ci if is_const: 696e5c31af7Sopenharmony_ci # input an array of bitmask values 697e5c31af7Sopenharmony_ci template = 'combinations of {bitsname} value' 698e5c31af7Sopenharmony_ci else: 699e5c31af7Sopenharmony_ci template = '{paramtype} value' 700e5c31af7Sopenharmony_ci elif is_pointer: 701e5c31af7Sopenharmony_ci if is_const: 702e5c31af7Sopenharmony_ci template = 'combination of {bitsname} values' 703e5c31af7Sopenharmony_ci else: 704e5c31af7Sopenharmony_ci template = '{paramtype} value' 705e5c31af7Sopenharmony_ci else: 706e5c31af7Sopenharmony_ci template = 'combination of {bitsname} values' 707e5c31af7Sopenharmony_ci 708e5c31af7Sopenharmony_ci # The above few cases all use makeEnumerationName, just with different context. 709e5c31af7Sopenharmony_ci typetext = template.format( 710e5c31af7Sopenharmony_ci bitsname=self.makeEnumerationName(bitsname), 711e5c31af7Sopenharmony_ci paramtype=self.makeFlagsName(paramtype)) 712e5c31af7Sopenharmony_ci 713e5c31af7Sopenharmony_ci elif typecategory == 'handle': 714e5c31af7Sopenharmony_ci typetext = '{} handle'.format(self.makeStructName(paramtype)) 715e5c31af7Sopenharmony_ci 716e5c31af7Sopenharmony_ci elif typecategory == 'enum': 717e5c31af7Sopenharmony_ci typetext = '{} value'.format(self.makeEnumerationName(paramtype)) 718e5c31af7Sopenharmony_ci 719e5c31af7Sopenharmony_ci elif typecategory == 'funcpointer': 720e5c31af7Sopenharmony_ci typetext = '{} value'.format(self.makeFuncPointerName(paramtype)) 721e5c31af7Sopenharmony_ci 722e5c31af7Sopenharmony_ci elif typecategory == 'struct': 723e5c31af7Sopenharmony_ci if needs_recursive_validity: 724e5c31af7Sopenharmony_ci typetext = '{} structure'.format( 725e5c31af7Sopenharmony_ci self.makeStructName(paramtype)) 726e5c31af7Sopenharmony_ci 727e5c31af7Sopenharmony_ci elif typecategory == 'union': 728e5c31af7Sopenharmony_ci if needs_recursive_validity: 729e5c31af7Sopenharmony_ci typetext = '{} union'.format(self.makeStructName(paramtype)) 730e5c31af7Sopenharmony_ci 731e5c31af7Sopenharmony_ci elif self.paramIsArray(param) or self.paramIsPointer(param): 732e5c31af7Sopenharmony_ci # TODO sync cosmetic changes from OpenXR? 733e5c31af7Sopenharmony_ci if typecategory is None: 734e5c31af7Sopenharmony_ci typetext = f'code:{paramtype} value' 735e5c31af7Sopenharmony_ci else: 736e5c31af7Sopenharmony_ci typetext = '{} value'.format(self.makeBaseTypeName(paramtype)) 737e5c31af7Sopenharmony_ci 738e5c31af7Sopenharmony_ci elif typecategory is None: 739e5c31af7Sopenharmony_ci if not self.isStructAlwaysValid(paramtype): 740e5c31af7Sopenharmony_ci typetext = '{} value'.format( 741e5c31af7Sopenharmony_ci self.makeExternalTypeName(paramtype)) 742e5c31af7Sopenharmony_ci 743e5c31af7Sopenharmony_ci # "a valid uint32_t value" does not make much sense. 744e5c31af7Sopenharmony_ci pass 745e5c31af7Sopenharmony_ci 746e5c31af7Sopenharmony_ci # If any of the above conditions matched and set typetext, 747e5c31af7Sopenharmony_ci # we call using it. 748e5c31af7Sopenharmony_ci if typetext is not None: 749e5c31af7Sopenharmony_ci return self.createValidationLineForParameterImpl( 750e5c31af7Sopenharmony_ci blockname, param, params, typetext, selector, parentname) 751e5c31af7Sopenharmony_ci return None 752e5c31af7Sopenharmony_ci 753e5c31af7Sopenharmony_ci def makeHandleValidityParent(self, param, params): 754e5c31af7Sopenharmony_ci """Make a validity entry for a handle's parent object. 755e5c31af7Sopenharmony_ci 756e5c31af7Sopenharmony_ci Creates 'parent' VUID. 757e5c31af7Sopenharmony_ci """ 758e5c31af7Sopenharmony_ci param_name = getElemName(param) 759e5c31af7Sopenharmony_ci paramtype = getElemType(param) 760e5c31af7Sopenharmony_ci 761e5c31af7Sopenharmony_ci # Iterate up the handle parent hierarchy for the first parameter of 762e5c31af7Sopenharmony_ci # a parent type. 763e5c31af7Sopenharmony_ci # This enables cases where a more distant ancestor is present, such 764e5c31af7Sopenharmony_ci # as VkDevice and VkCommandBuffer (but no direct parent 765e5c31af7Sopenharmony_ci # VkCommandPool). 766e5c31af7Sopenharmony_ci 767e5c31af7Sopenharmony_ci while True: 768e5c31af7Sopenharmony_ci # If we run out of ancestors, give up 769e5c31af7Sopenharmony_ci handleparent = self.getHandleParent(paramtype) 770e5c31af7Sopenharmony_ci if handleparent is None: 771e5c31af7Sopenharmony_ci if self.trace: 772e5c31af7Sopenharmony_ci print(f'makeHandleValidityParent:{param_name} has no handle parent, skipping') 773e5c31af7Sopenharmony_ci return None 774e5c31af7Sopenharmony_ci 775e5c31af7Sopenharmony_ci # Look for a parameter of the ancestor type 776e5c31af7Sopenharmony_ci otherparam = findTypedElem(params, handleparent) 777e5c31af7Sopenharmony_ci if otherparam is not None: 778e5c31af7Sopenharmony_ci break 779e5c31af7Sopenharmony_ci 780e5c31af7Sopenharmony_ci # Continue up the hierarchy 781e5c31af7Sopenharmony_ci paramtype = handleparent 782e5c31af7Sopenharmony_ci 783e5c31af7Sopenharmony_ci parent_name = getElemName(otherparam) 784e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=(param_name, 'parent')) 785e5c31af7Sopenharmony_ci 786e5c31af7Sopenharmony_ci is_optional = self.isHandleOptional(param, params) 787e5c31af7Sopenharmony_ci 788e5c31af7Sopenharmony_ci if self.paramIsArray(param): 789e5c31af7Sopenharmony_ci template = 'Each element of {}' 790e5c31af7Sopenharmony_ci if is_optional: 791e5c31af7Sopenharmony_ci template += ' that is a valid handle' 792e5c31af7Sopenharmony_ci elif is_optional: 793e5c31af7Sopenharmony_ci template = 'If {} is a valid handle, it' 794e5c31af7Sopenharmony_ci else: 795e5c31af7Sopenharmony_ci # not optional, not an array. Just say the parameter name. 796e5c31af7Sopenharmony_ci template = '{}' 797e5c31af7Sopenharmony_ci 798e5c31af7Sopenharmony_ci entry += template.format(self.makeParameterName(param_name)) 799e5c31af7Sopenharmony_ci 800e5c31af7Sopenharmony_ci entry += ' must: have been created, allocated, or retrieved from {}'.format( 801e5c31af7Sopenharmony_ci self.makeParameterName(parent_name)) 802e5c31af7Sopenharmony_ci 803e5c31af7Sopenharmony_ci return entry 804e5c31af7Sopenharmony_ci 805e5c31af7Sopenharmony_ci def makeAsciiDocHandlesCommonAncestor(self, blockname, handles, params): 806e5c31af7Sopenharmony_ci """Make an asciidoc validity entry for a common ancestors between handles. 807e5c31af7Sopenharmony_ci 808e5c31af7Sopenharmony_ci Only handles parent validity for signatures taking multiple handles 809e5c31af7Sopenharmony_ci any ancestors also being supplied to this function. 810e5c31af7Sopenharmony_ci (e.g. "Each of x, y, and z must: come from the same slink:ParentHandle") 811e5c31af7Sopenharmony_ci See self.makeAsciiDocHandleParent() for instances where the parent 812e5c31af7Sopenharmony_ci handle is named and also passed. 813e5c31af7Sopenharmony_ci 814e5c31af7Sopenharmony_ci Creates 'commonparent' VUID. 815e5c31af7Sopenharmony_ci """ 816e5c31af7Sopenharmony_ci # TODO Replace with refactored code from OpenXR 817e5c31af7Sopenharmony_ci entry = None 818e5c31af7Sopenharmony_ci 819e5c31af7Sopenharmony_ci if len(handles) > 1: 820e5c31af7Sopenharmony_ci ancestormap = {} 821e5c31af7Sopenharmony_ci anyoptional = False 822e5c31af7Sopenharmony_ci # Find all the ancestors 823e5c31af7Sopenharmony_ci for param in handles: 824e5c31af7Sopenharmony_ci paramtype = getElemType(param) 825e5c31af7Sopenharmony_ci 826e5c31af7Sopenharmony_ci if not self.paramIsPointer(param) or (param.text and 'const' in param.text): 827e5c31af7Sopenharmony_ci ancestors = self.getHandleDispatchableAncestors(paramtype) 828e5c31af7Sopenharmony_ci 829e5c31af7Sopenharmony_ci ancestormap[param] = ancestors 830e5c31af7Sopenharmony_ci 831e5c31af7Sopenharmony_ci anyoptional |= self.isHandleOptional(param, params) 832e5c31af7Sopenharmony_ci 833e5c31af7Sopenharmony_ci # Remove redundant ancestor lists 834e5c31af7Sopenharmony_ci for param in handles: 835e5c31af7Sopenharmony_ci paramtype = getElemType(param) 836e5c31af7Sopenharmony_ci 837e5c31af7Sopenharmony_ci removals = [] 838e5c31af7Sopenharmony_ci for ancestors in ancestormap.items(): 839e5c31af7Sopenharmony_ci if paramtype in ancestors[1]: 840e5c31af7Sopenharmony_ci removals.append(ancestors[0]) 841e5c31af7Sopenharmony_ci 842e5c31af7Sopenharmony_ci if removals != []: 843e5c31af7Sopenharmony_ci for removal in removals: 844e5c31af7Sopenharmony_ci del(ancestormap[removal]) 845e5c31af7Sopenharmony_ci 846e5c31af7Sopenharmony_ci # Intersect 847e5c31af7Sopenharmony_ci 848e5c31af7Sopenharmony_ci if len(ancestormap.values()) > 1: 849e5c31af7Sopenharmony_ci current = list(ancestormap.values())[0] 850e5c31af7Sopenharmony_ci for ancestors in list(ancestormap.values())[1:]: 851e5c31af7Sopenharmony_ci current = [val for val in current if val in ancestors] 852e5c31af7Sopenharmony_ci 853e5c31af7Sopenharmony_ci if len(current) > 0: 854e5c31af7Sopenharmony_ci commonancestor = current[0] 855e5c31af7Sopenharmony_ci 856e5c31af7Sopenharmony_ci if len(ancestormap.keys()) > 1: 857e5c31af7Sopenharmony_ci 858e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=('commonparent',)) 859e5c31af7Sopenharmony_ci 860e5c31af7Sopenharmony_ci parametertexts = [] 861e5c31af7Sopenharmony_ci for param in ancestormap.keys(): 862e5c31af7Sopenharmony_ci param_name = getElemName(param) 863e5c31af7Sopenharmony_ci parametertext = self.makeParameterName(param_name) 864e5c31af7Sopenharmony_ci if self.paramIsArray(param): 865e5c31af7Sopenharmony_ci parametertext = 'the elements of ' + parametertext 866e5c31af7Sopenharmony_ci parametertexts.append(parametertext) 867e5c31af7Sopenharmony_ci 868e5c31af7Sopenharmony_ci parametertexts.sort() 869e5c31af7Sopenharmony_ci 870e5c31af7Sopenharmony_ci if len(parametertexts) > 2: 871e5c31af7Sopenharmony_ci entry += 'Each of ' 872e5c31af7Sopenharmony_ci else: 873e5c31af7Sopenharmony_ci entry += 'Both of ' 874e5c31af7Sopenharmony_ci 875e5c31af7Sopenharmony_ci entry += self.makeProseList(parametertexts, 876e5c31af7Sopenharmony_ci comma_for_two_elts=True) 877e5c31af7Sopenharmony_ci if anyoptional is True: 878e5c31af7Sopenharmony_ci entry += ' that are valid handles of non-ignored parameters' 879e5c31af7Sopenharmony_ci entry += ' must: have been created, allocated, or retrieved from the same ' 880e5c31af7Sopenharmony_ci entry += self.makeStructName(commonancestor) 881e5c31af7Sopenharmony_ci 882e5c31af7Sopenharmony_ci return entry 883e5c31af7Sopenharmony_ci 884e5c31af7Sopenharmony_ci def makeStructureTypeFromName(self, structname): 885e5c31af7Sopenharmony_ci """Create text for a structure type name, like ename:VK_STRUCTURE_TYPE_CREATE_INSTANCE_INFO""" 886e5c31af7Sopenharmony_ci return self.makeEnumerantName(self.conventions.generate_structure_type_from_name(structname)) 887e5c31af7Sopenharmony_ci 888e5c31af7Sopenharmony_ci def makeStructureTypeValidity(self, structname): 889e5c31af7Sopenharmony_ci """Generate an validity line for the type value of a struct. 890e5c31af7Sopenharmony_ci 891e5c31af7Sopenharmony_ci Creates VUID named like the member name. 892e5c31af7Sopenharmony_ci """ 893e5c31af7Sopenharmony_ci info = self.registry.typedict.get(structname) 894e5c31af7Sopenharmony_ci assert(info is not None) 895e5c31af7Sopenharmony_ci 896e5c31af7Sopenharmony_ci # If this fails (meaning we have something other than a struct in here), 897e5c31af7Sopenharmony_ci # then the caller is wrong: 898e5c31af7Sopenharmony_ci # probably passing the wrong value for structname. 899e5c31af7Sopenharmony_ci members = info.getMembers() 900e5c31af7Sopenharmony_ci assert(members) 901e5c31af7Sopenharmony_ci 902e5c31af7Sopenharmony_ci # If this fails, see caller: this should only get called for a struct type with a type value. 903e5c31af7Sopenharmony_ci param = findNamedElem(members, self.structtype_member_name) 904e5c31af7Sopenharmony_ci # OpenXR gets some structs without a type field in here, so cannot assert 905e5c31af7Sopenharmony_ci assert(param is not None) 906e5c31af7Sopenharmony_ci # if param is None: 907e5c31af7Sopenharmony_ci # return None 908e5c31af7Sopenharmony_ci 909e5c31af7Sopenharmony_ci entry = ValidityEntry( 910e5c31af7Sopenharmony_ci anchor=(self.structtype_member_name, self.structtype_member_name)) 911e5c31af7Sopenharmony_ci entry += self.makeParameterName(self.structtype_member_name) 912e5c31af7Sopenharmony_ci entry += ' must: be ' 913e5c31af7Sopenharmony_ci 914e5c31af7Sopenharmony_ci values = param.get('values', '').split(',') 915e5c31af7Sopenharmony_ci if values: 916e5c31af7Sopenharmony_ci # Extract each enumerant value. They could be validated in the 917e5c31af7Sopenharmony_ci # same fashion as validextensionstructs in 918e5c31af7Sopenharmony_ci # makeStructureExtensionPointer, although that is not relevant in 919e5c31af7Sopenharmony_ci # the current extension struct model. 920e5c31af7Sopenharmony_ci entry += self.makeProseList((self.makeEnumerantName(v) 921e5c31af7Sopenharmony_ci for v in values), 'or') 922e5c31af7Sopenharmony_ci return entry 923e5c31af7Sopenharmony_ci 924e5c31af7Sopenharmony_ci if 'Base' in structname: 925e5c31af7Sopenharmony_ci # This type does not even have any values for its type, and it 926e5c31af7Sopenharmony_ci # seems like it might be a base struct that we would expect to 927e5c31af7Sopenharmony_ci # lack its own type, so omit the entire statement 928e5c31af7Sopenharmony_ci return None 929e5c31af7Sopenharmony_ci 930e5c31af7Sopenharmony_ci self.logMsg('warn', 'No values were marked-up for the structure type member of', 931e5c31af7Sopenharmony_ci structname, 'so making one up!') 932e5c31af7Sopenharmony_ci entry += self.makeStructureTypeFromName(structname) 933e5c31af7Sopenharmony_ci 934e5c31af7Sopenharmony_ci return entry 935e5c31af7Sopenharmony_ci 936e5c31af7Sopenharmony_ci def makeStructureExtensionPointer(self, blockname, param): 937e5c31af7Sopenharmony_ci """Generate an validity line for the pointer chain member value of a struct.""" 938e5c31af7Sopenharmony_ci param_name = getElemName(param) 939e5c31af7Sopenharmony_ci 940e5c31af7Sopenharmony_ci if param.get('validextensionstructs') is not None: 941e5c31af7Sopenharmony_ci self.logMsg('warn', blockname, 942e5c31af7Sopenharmony_ci 'validextensionstructs is deprecated/removed', '\n') 943e5c31af7Sopenharmony_ci 944e5c31af7Sopenharmony_ci entry = ValidityEntry( 945e5c31af7Sopenharmony_ci anchor=(param_name, self.nextpointer_member_name)) 946e5c31af7Sopenharmony_ci validextensionstructs = self.registry.validextensionstructs.get( 947e5c31af7Sopenharmony_ci blockname) 948e5c31af7Sopenharmony_ci extensionstructs = [] 949e5c31af7Sopenharmony_ci duplicatestructs = [] 950e5c31af7Sopenharmony_ci 951e5c31af7Sopenharmony_ci if validextensionstructs is not None: 952e5c31af7Sopenharmony_ci # Check each structure name and skip it if not required by the 953e5c31af7Sopenharmony_ci # generator. This allows tagging extension structs in the XML 954e5c31af7Sopenharmony_ci # that are only included in validity when needed for the spec 955e5c31af7Sopenharmony_ci # being targeted. 956e5c31af7Sopenharmony_ci # Track the required structures, and of the required structures, 957e5c31af7Sopenharmony_ci # those that allow duplicates in the pNext chain. 958e5c31af7Sopenharmony_ci for struct in validextensionstructs: 959e5c31af7Sopenharmony_ci # Unpleasantly breaks encapsulation. Should be a method in the registry class 960e5c31af7Sopenharmony_ci t = self.registry.lookupElementInfo( 961e5c31af7Sopenharmony_ci struct, self.registry.typedict) 962e5c31af7Sopenharmony_ci if t is None: 963e5c31af7Sopenharmony_ci self.logMsg('warn', 'makeStructureExtensionPointer: struct', struct, 964e5c31af7Sopenharmony_ci 'is in a validextensionstructs= attribute but is not in the registry') 965e5c31af7Sopenharmony_ci elif t.required: 966e5c31af7Sopenharmony_ci extensionstructs.append('slink:' + struct) 967e5c31af7Sopenharmony_ci if t.elem.get('allowduplicate') == 'true': 968e5c31af7Sopenharmony_ci duplicatestructs.append('slink:' + struct) 969e5c31af7Sopenharmony_ci else: 970e5c31af7Sopenharmony_ci self.logMsg( 971e5c31af7Sopenharmony_ci 'diag', 'makeStructureExtensionPointer: struct', struct, 'IS NOT required') 972e5c31af7Sopenharmony_ci 973e5c31af7Sopenharmony_ci if not extensionstructs: 974e5c31af7Sopenharmony_ci entry += '{} must: be {}'.format( 975e5c31af7Sopenharmony_ci self.makeParameterName(param_name), self.null) 976e5c31af7Sopenharmony_ci return entry 977e5c31af7Sopenharmony_ci 978e5c31af7Sopenharmony_ci if len(extensionstructs) == 1: 979e5c31af7Sopenharmony_ci entry += '{} must: be {} or a pointer to a valid instance of {}'.format(self.makeParameterName(param_name), self.null, 980e5c31af7Sopenharmony_ci extensionstructs[0]) 981e5c31af7Sopenharmony_ci else: 982e5c31af7Sopenharmony_ci # More than one extension struct. 983e5c31af7Sopenharmony_ci entry += 'Each {} member of any structure (including this one) in the pname:{} chain '.format( 984e5c31af7Sopenharmony_ci self.makeParameterName(param_name), self.nextpointer_member_name) 985e5c31af7Sopenharmony_ci entry += 'must: be either {} or a pointer to a valid instance of '.format( 986e5c31af7Sopenharmony_ci self.null) 987e5c31af7Sopenharmony_ci 988e5c31af7Sopenharmony_ci entry += self.makeProseList(extensionstructs, fmt=plf.OR) 989e5c31af7Sopenharmony_ci 990e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(blockname) 991e5c31af7Sopenharmony_ci validity += entry 992e5c31af7Sopenharmony_ci 993e5c31af7Sopenharmony_ci # Generate VU statement requiring unique structures in the pNext 994e5c31af7Sopenharmony_ci # chain. 995e5c31af7Sopenharmony_ci # NOTE: OpenXR always allows non-unique type values. Instances other 996e5c31af7Sopenharmony_ci # than the first are just ignored 997e5c31af7Sopenharmony_ci 998e5c31af7Sopenharmony_ci vu = ('The pname:' + 999e5c31af7Sopenharmony_ci self.structtype_member_name + 1000e5c31af7Sopenharmony_ci ' value of each struct in the pname:' + 1001e5c31af7Sopenharmony_ci self.nextpointer_member_name + 1002e5c31af7Sopenharmony_ci ' chain must: be unique') 1003e5c31af7Sopenharmony_ci anchor = (self.conventions.member_used_for_unique_vuid, 'unique') 1004e5c31af7Sopenharmony_ci 1005e5c31af7Sopenharmony_ci # If duplicates of some structures are allowed, they are called out 1006e5c31af7Sopenharmony_ci # explicitly. 1007e5c31af7Sopenharmony_ci num = len(duplicatestructs) 1008e5c31af7Sopenharmony_ci if num > 0: 1009e5c31af7Sopenharmony_ci vu = (vu + 1010e5c31af7Sopenharmony_ci ', with the exception of structures of type ' + 1011e5c31af7Sopenharmony_ci self.makeProseList(duplicatestructs, fmt=plf.OR)) 1012e5c31af7Sopenharmony_ci 1013e5c31af7Sopenharmony_ci validity.addValidityEntry(vu, anchor = anchor ) 1014e5c31af7Sopenharmony_ci 1015e5c31af7Sopenharmony_ci return validity 1016e5c31af7Sopenharmony_ci 1017e5c31af7Sopenharmony_ci def addSharedStructMemberValidity(self, struct, blockname, param, validity): 1018e5c31af7Sopenharmony_ci """Generate language to independently validate a parameter, for those validated even in output. 1019e5c31af7Sopenharmony_ci 1020e5c31af7Sopenharmony_ci Return value indicates whether it was handled internally (True) or if it may need more validity (False).""" 1021e5c31af7Sopenharmony_ci param_name = getElemName(param) 1022e5c31af7Sopenharmony_ci paramtype = getElemType(param) 1023e5c31af7Sopenharmony_ci if param.get('noautovalidity') is None: 1024e5c31af7Sopenharmony_ci 1025e5c31af7Sopenharmony_ci if self.conventions.is_structure_type_member(paramtype, param_name): 1026e5c31af7Sopenharmony_ci validity += self.makeStructureTypeValidity(blockname) 1027e5c31af7Sopenharmony_ci return True 1028e5c31af7Sopenharmony_ci 1029e5c31af7Sopenharmony_ci if self.conventions.is_nextpointer_member(paramtype, param_name): 1030e5c31af7Sopenharmony_ci # Vulkan: the addition of validity here is conditional unlike OpenXR. 1031e5c31af7Sopenharmony_ci if struct.get('structextends') is None: 1032e5c31af7Sopenharmony_ci validity += self.makeStructureExtensionPointer( 1033e5c31af7Sopenharmony_ci blockname, param) 1034e5c31af7Sopenharmony_ci return True 1035e5c31af7Sopenharmony_ci return False 1036e5c31af7Sopenharmony_ci 1037e5c31af7Sopenharmony_ci def makeOutputOnlyStructValidity(self, cmd, blockname, params): 1038e5c31af7Sopenharmony_ci """Generate all the valid usage information for a struct that is entirely output. 1039e5c31af7Sopenharmony_ci 1040e5c31af7Sopenharmony_ci That is, it is only ever filled out by the implementation other than 1041e5c31af7Sopenharmony_ci the structure type and pointer chain members. 1042e5c31af7Sopenharmony_ci Thus, we only create validity for the pointer chain member. 1043e5c31af7Sopenharmony_ci """ 1044e5c31af7Sopenharmony_ci # Start the validity collection for this struct 1045e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(blockname) 1046e5c31af7Sopenharmony_ci 1047e5c31af7Sopenharmony_ci for param in params: 1048e5c31af7Sopenharmony_ci self.addSharedStructMemberValidity( 1049e5c31af7Sopenharmony_ci cmd, blockname, param, validity) 1050e5c31af7Sopenharmony_ci 1051e5c31af7Sopenharmony_ci return validity 1052e5c31af7Sopenharmony_ci 1053e5c31af7Sopenharmony_ci def isVKVersion11(self): 1054e5c31af7Sopenharmony_ci """Returns true if VK_VERSION_1_1 is being emitted.""" 1055e5c31af7Sopenharmony_ci vk11 = re.match(self.registry.genOpts.emitversions, 'VK_VERSION_1_1') is not None 1056e5c31af7Sopenharmony_ci return vk11 1057e5c31af7Sopenharmony_ci 1058e5c31af7Sopenharmony_ci def videocodingRequired(self): 1059e5c31af7Sopenharmony_ci """Returns true if VK_KHR_video_queue is being emitted and thus validity 1060e5c31af7Sopenharmony_ci with respect to the videocoding attribute should be generated.""" 1061e5c31af7Sopenharmony_ci return 'VK_KHR_video_queue' in self.registry.requiredextensions 1062e5c31af7Sopenharmony_ci 1063e5c31af7Sopenharmony_ci def getVideocoding(self, cmd): 1064e5c31af7Sopenharmony_ci """Returns the value of the videocoding attribute, also considering the 1065e5c31af7Sopenharmony_ci default value when the attribute is not present.""" 1066e5c31af7Sopenharmony_ci videocoding = cmd.get('videocoding') 1067e5c31af7Sopenharmony_ci if videocoding is None: 1068e5c31af7Sopenharmony_ci videocoding = 'outside' 1069e5c31af7Sopenharmony_ci return videocoding 1070e5c31af7Sopenharmony_ci 1071e5c31af7Sopenharmony_ci def conditionallyRemoveQueueType(self, queues, queuetype, condition): 1072e5c31af7Sopenharmony_ci """Removes a queue type from a queue list based on the specified condition.""" 1073e5c31af7Sopenharmony_ci if queuetype in queues and condition: 1074e5c31af7Sopenharmony_ci queues.remove(queuetype) 1075e5c31af7Sopenharmony_ci 1076e5c31af7Sopenharmony_ci def getQueueList(self, cmd): 1077e5c31af7Sopenharmony_ci """Returns the list of queue types a command is supported on.""" 1078e5c31af7Sopenharmony_ci queues = cmd.get('queues') 1079e5c31af7Sopenharmony_ci if queues is None: 1080e5c31af7Sopenharmony_ci return None 1081e5c31af7Sopenharmony_ci queues = queues.split(',') 1082e5c31af7Sopenharmony_ci 1083e5c31af7Sopenharmony_ci # Filter queue types that have dependencies 1084e5c31af7Sopenharmony_ci self.conditionallyRemoveQueueType(queues, 'sparse_binding', self.conventions.xml_api_name == "vulkansc") 1085e5c31af7Sopenharmony_ci self.conditionallyRemoveQueueType(queues, 'decode', 'VK_KHR_video_decode_queue' not in self.registry.requiredextensions) 1086e5c31af7Sopenharmony_ci self.conditionallyRemoveQueueType(queues, 'encode', 'VK_KHR_video_encode_queue' not in self.registry.requiredextensions) 1087e5c31af7Sopenharmony_ci self.conditionallyRemoveQueueType(queues, 'opticalflow', 'VK_NV_optical_flow' not in self.registry.requiredextensions) 1088e5c31af7Sopenharmony_ci 1089e5c31af7Sopenharmony_ci # Verify that no new queue type is introduced accidentally 1090e5c31af7Sopenharmony_ci for queue in queues: 1091e5c31af7Sopenharmony_ci if queue not in [ 'transfer', 'compute', 'graphics', 'sparse_binding', 'decode', 'encode', 'opticalflow' ]: 1092e5c31af7Sopenharmony_ci self.logMsg('error', f'Unknown queue type "{queue}".') 1093e5c31af7Sopenharmony_ci 1094e5c31af7Sopenharmony_ci return queues 1095e5c31af7Sopenharmony_ci 1096e5c31af7Sopenharmony_ci def getPrettyQueueList(self, cmd): 1097e5c31af7Sopenharmony_ci """Returns a prettified version of the queue list which can be included in spec language text.""" 1098e5c31af7Sopenharmony_ci queues = self.getQueueList(cmd) 1099e5c31af7Sopenharmony_ci if queues is None: 1100e5c31af7Sopenharmony_ci return None 1101e5c31af7Sopenharmony_ci 1102e5c31af7Sopenharmony_ci replace = { 1103e5c31af7Sopenharmony_ci 'sparse_binding': 'sparse binding', 1104e5c31af7Sopenharmony_ci 'opticalflow': 'optical flow' 1105e5c31af7Sopenharmony_ci } 1106e5c31af7Sopenharmony_ci return [replace[queue] if queue in replace else queue for queue in queues] 1107e5c31af7Sopenharmony_ci 1108e5c31af7Sopenharmony_ci def makeStructOrCommandValidity(self, cmd, blockname, params): 1109e5c31af7Sopenharmony_ci """Generate all the valid usage information for a given struct or command.""" 1110e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(blockname) 1111e5c31af7Sopenharmony_ci handles = [] 1112e5c31af7Sopenharmony_ci arraylengths = dict() 1113e5c31af7Sopenharmony_ci for param in params: 1114e5c31af7Sopenharmony_ci param_name = getElemName(param) 1115e5c31af7Sopenharmony_ci paramtype = getElemType(param) 1116e5c31af7Sopenharmony_ci 1117e5c31af7Sopenharmony_ci # Valid usage ID tags (VUID) are generated for various 1118e5c31af7Sopenharmony_ci # conditions based on the name of the block (structure or 1119e5c31af7Sopenharmony_ci # command), name of the element (member or parameter), and type 1120e5c31af7Sopenharmony_ci # of VU statement. 1121e5c31af7Sopenharmony_ci 1122e5c31af7Sopenharmony_ci # Get the type's category 1123e5c31af7Sopenharmony_ci typecategory = self.getTypeCategory(paramtype) 1124e5c31af7Sopenharmony_ci 1125e5c31af7Sopenharmony_ci if not self.addSharedStructMemberValidity( 1126e5c31af7Sopenharmony_ci cmd, blockname, param, validity): 1127e5c31af7Sopenharmony_ci if not param.get('selector'): 1128e5c31af7Sopenharmony_ci validity += self.createValidationLineForParameter( 1129e5c31af7Sopenharmony_ci blockname, param, params, typecategory, None, None) 1130e5c31af7Sopenharmony_ci else: 1131e5c31af7Sopenharmony_ci selector = param.get('selector') 1132e5c31af7Sopenharmony_ci if typecategory != 'union': 1133e5c31af7Sopenharmony_ci self.logMsg('warn', 'selector attribute set on non-union parameter', param_name, 'in', blockname) 1134e5c31af7Sopenharmony_ci 1135e5c31af7Sopenharmony_ci paraminfo = self.registry.lookupElementInfo(paramtype, self.registry.typedict) 1136e5c31af7Sopenharmony_ci 1137e5c31af7Sopenharmony_ci for member in paraminfo.getMembers(): 1138e5c31af7Sopenharmony_ci membertype = getElemType(member) 1139e5c31af7Sopenharmony_ci membertypecategory = self.getTypeCategory(membertype) 1140e5c31af7Sopenharmony_ci 1141e5c31af7Sopenharmony_ci validity += self.createValidationLineForParameter( 1142e5c31af7Sopenharmony_ci blockname, member, paraminfo.getMembers(), membertypecategory, selector, param_name) 1143e5c31af7Sopenharmony_ci 1144e5c31af7Sopenharmony_ci # Ensure that any parenting is properly validated, and list that a handle was found 1145e5c31af7Sopenharmony_ci if typecategory == 'handle': 1146e5c31af7Sopenharmony_ci handles.append(param) 1147e5c31af7Sopenharmony_ci 1148e5c31af7Sopenharmony_ci # Get the array length for this parameter 1149e5c31af7Sopenharmony_ci lengths = LengthEntry.parse_len_from_param(param) 1150e5c31af7Sopenharmony_ci if lengths: 1151e5c31af7Sopenharmony_ci arraylengths.update({length.other_param_name: length 1152e5c31af7Sopenharmony_ci for length in lengths 1153e5c31af7Sopenharmony_ci if length.other_param_name}) 1154e5c31af7Sopenharmony_ci 1155e5c31af7Sopenharmony_ci # For any vkQueue* functions, there might be queue type data 1156e5c31af7Sopenharmony_ci if 'vkQueue' in blockname: 1157e5c31af7Sopenharmony_ci # The queue type must be valid 1158e5c31af7Sopenharmony_ci queues = self.getPrettyQueueList(cmd) 1159e5c31af7Sopenharmony_ci if queues: 1160e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=('queuetype',)) 1161e5c31af7Sopenharmony_ci entry += 'The pname:queue must: support ' 1162e5c31af7Sopenharmony_ci entry += self.makeProseList(queues, 1163e5c31af7Sopenharmony_ci fmt=plf.OR, comma_for_two_elts=True) 1164e5c31af7Sopenharmony_ci entry += ' operations' 1165e5c31af7Sopenharmony_ci validity += entry 1166e5c31af7Sopenharmony_ci 1167e5c31af7Sopenharmony_ci if 'vkCmd' in blockname: 1168e5c31af7Sopenharmony_ci # The commandBuffer parameter must be being recorded 1169e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=('commandBuffer', 'recording')) 1170e5c31af7Sopenharmony_ci entry += 'pname:commandBuffer must: be in the <<commandbuffers-lifecycle, recording state>>' 1171e5c31af7Sopenharmony_ci validity += entry 1172e5c31af7Sopenharmony_ci 1173e5c31af7Sopenharmony_ci # 1174e5c31af7Sopenharmony_ci # Start of valid queue type validation - command pool must have been 1175e5c31af7Sopenharmony_ci # allocated against a queue with at least one of the valid queue types 1176e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=('commandBuffer', 'cmdpool')) 1177e5c31af7Sopenharmony_ci 1178e5c31af7Sopenharmony_ci # 1179e5c31af7Sopenharmony_ci # This test for vkCmdFillBuffer is a hack, since we have no path 1180e5c31af7Sopenharmony_ci # to conditionally have queues enabled or disabled by an extension. 1181e5c31af7Sopenharmony_ci # As the VU stuff is all moving out (hopefully soon), this hack solves the issue for now 1182e5c31af7Sopenharmony_ci if blockname == 'vkCmdFillBuffer': 1183e5c31af7Sopenharmony_ci entry += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support ' 1184e5c31af7Sopenharmony_ci if self.isVKVersion11() or 'VK_KHR_maintenance1' in self.registry.requiredextensions: 1185e5c31af7Sopenharmony_ci entry += 'transfer, graphics or compute operations' 1186e5c31af7Sopenharmony_ci else: 1187e5c31af7Sopenharmony_ci entry += 'graphics or compute operations' 1188e5c31af7Sopenharmony_ci else: 1189e5c31af7Sopenharmony_ci # The queue type must be valid 1190e5c31af7Sopenharmony_ci queues = self.getPrettyQueueList(cmd) 1191e5c31af7Sopenharmony_ci assert(queues) 1192e5c31af7Sopenharmony_ci entry += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support ' 1193e5c31af7Sopenharmony_ci entry += self.makeProseList(queues, 1194e5c31af7Sopenharmony_ci fmt=plf.OR, comma_for_two_elts=True) 1195e5c31af7Sopenharmony_ci entry += ' operations' 1196e5c31af7Sopenharmony_ci validity += entry 1197e5c31af7Sopenharmony_ci 1198e5c31af7Sopenharmony_ci # Must be called inside/outside a render pass appropriately 1199e5c31af7Sopenharmony_ci renderpass = cmd.get('renderpass') 1200e5c31af7Sopenharmony_ci 1201e5c31af7Sopenharmony_ci if renderpass != 'both': 1202e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=('renderpass',)) 1203e5c31af7Sopenharmony_ci entry += 'This command must: only be called ' 1204e5c31af7Sopenharmony_ci entry += renderpass 1205e5c31af7Sopenharmony_ci entry += ' of a render pass instance' 1206e5c31af7Sopenharmony_ci validity += entry 1207e5c31af7Sopenharmony_ci 1208e5c31af7Sopenharmony_ci # Must be called inside/outside a video coding scope appropriately 1209e5c31af7Sopenharmony_ci if self.videocodingRequired(): 1210e5c31af7Sopenharmony_ci videocoding = self.getVideocoding(cmd) 1211e5c31af7Sopenharmony_ci if videocoding != 'both': 1212e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=('videocoding',)) 1213e5c31af7Sopenharmony_ci entry += 'This command must: only be called ' 1214e5c31af7Sopenharmony_ci entry += videocoding 1215e5c31af7Sopenharmony_ci entry += ' of a video coding scope' 1216e5c31af7Sopenharmony_ci validity += entry 1217e5c31af7Sopenharmony_ci 1218e5c31af7Sopenharmony_ci # Must be in the right level command buffer 1219e5c31af7Sopenharmony_ci cmdbufferlevel = cmd.get('cmdbufferlevel') 1220e5c31af7Sopenharmony_ci 1221e5c31af7Sopenharmony_ci if cmdbufferlevel != 'primary,secondary': 1222e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=('bufferlevel',)) 1223e5c31af7Sopenharmony_ci entry += 'pname:commandBuffer must: be a ' 1224e5c31af7Sopenharmony_ci entry += cmdbufferlevel 1225e5c31af7Sopenharmony_ci entry += ' sname:VkCommandBuffer' 1226e5c31af7Sopenharmony_ci validity += entry 1227e5c31af7Sopenharmony_ci 1228e5c31af7Sopenharmony_ci # Any non-optional arraylengths should specify they must be greater than 0 1229e5c31af7Sopenharmony_ci array_length_params = ((param, getElemName(param)) 1230e5c31af7Sopenharmony_ci for param in params 1231e5c31af7Sopenharmony_ci if getElemName(param) in arraylengths) 1232e5c31af7Sopenharmony_ci 1233e5c31af7Sopenharmony_ci for param, param_name in array_length_params: 1234e5c31af7Sopenharmony_ci if param.get('optional') is not None: 1235e5c31af7Sopenharmony_ci continue 1236e5c31af7Sopenharmony_ci 1237e5c31af7Sopenharmony_ci length = arraylengths[param_name] 1238e5c31af7Sopenharmony_ci full_length = length.full_reference 1239e5c31af7Sopenharmony_ci 1240e5c31af7Sopenharmony_ci # Is this just a name of a param? If false, then it is some kind 1241e5c31af7Sopenharmony_ci # of qualified name (a member of a param for instance) 1242e5c31af7Sopenharmony_ci simple_param_reference = (len(length.param_ref_parts) == 1) 1243e5c31af7Sopenharmony_ci if not simple_param_reference: 1244e5c31af7Sopenharmony_ci # Loop through to see if any parameters in the chain are optional 1245e5c31af7Sopenharmony_ci array_length_parent = cmd 1246e5c31af7Sopenharmony_ci array_length_optional = False 1247e5c31af7Sopenharmony_ci for part in length.param_ref_parts: 1248e5c31af7Sopenharmony_ci # Overwrite the param so it ends up as the bottom level parameter for later checks 1249e5c31af7Sopenharmony_ci param = array_length_parent.find("*/[name='{}']".format(part)) 1250e5c31af7Sopenharmony_ci 1251e5c31af7Sopenharmony_ci # If any parameter in the chain is optional, skip the implicit length requirement 1252e5c31af7Sopenharmony_ci array_length_optional |= (param.get('optional') is not None) 1253e5c31af7Sopenharmony_ci 1254e5c31af7Sopenharmony_ci # Lookup the type of the parameter for the next loop iteration 1255e5c31af7Sopenharmony_ci type = param.findtext('type') 1256e5c31af7Sopenharmony_ci array_length_parent = self.registry.tree.find("./types/type/[@name='{}']".format(type)) 1257e5c31af7Sopenharmony_ci 1258e5c31af7Sopenharmony_ci if array_length_optional: 1259e5c31af7Sopenharmony_ci continue 1260e5c31af7Sopenharmony_ci 1261e5c31af7Sopenharmony_ci # Get all the array dependencies 1262e5c31af7Sopenharmony_ci arrays = cmd.findall( 1263e5c31af7Sopenharmony_ci "param/[@len='{}'][@optional='true']".format(full_length)) 1264e5c31af7Sopenharmony_ci 1265e5c31af7Sopenharmony_ci # Get all the optional array dependencies, including those not generating validity for some reason 1266e5c31af7Sopenharmony_ci optionalarrays = arrays + \ 1267e5c31af7Sopenharmony_ci cmd.findall( 1268e5c31af7Sopenharmony_ci "param/[@len='{}'][@noautovalidity='true']".format(full_length)) 1269e5c31af7Sopenharmony_ci 1270e5c31af7Sopenharmony_ci entry = ValidityEntry(anchor=(full_length, 'arraylength')) 1271e5c31af7Sopenharmony_ci # Allow lengths to be arbitrary if all their dependents are optional 1272e5c31af7Sopenharmony_ci if optionalarrays and len(optionalarrays) == len(arrays): 1273e5c31af7Sopenharmony_ci entry += 'If ' 1274e5c31af7Sopenharmony_ci # TODO sync this section from OpenXR once cosmetic changes OK 1275e5c31af7Sopenharmony_ci 1276e5c31af7Sopenharmony_ci optional_array_names = (self.makeParameterName(getElemName(array)) 1277e5c31af7Sopenharmony_ci for array in optionalarrays) 1278e5c31af7Sopenharmony_ci entry += self.makeProseListIs(optional_array_names, 1279e5c31af7Sopenharmony_ci plf.ANY_OR, comma_for_two_elts=True) 1280e5c31af7Sopenharmony_ci 1281e5c31af7Sopenharmony_ci entry += ' not {}, '.format(self.null) 1282e5c31af7Sopenharmony_ci 1283e5c31af7Sopenharmony_ci # TODO end needs sync cosmetic 1284e5c31af7Sopenharmony_ci if self.paramIsPointer(param): 1285e5c31af7Sopenharmony_ci entry += 'the value referenced by ' 1286e5c31af7Sopenharmony_ci 1287e5c31af7Sopenharmony_ci # Split and re-join here to insert pname: around :: 1288e5c31af7Sopenharmony_ci entry += '::'.join(self.makeParameterName(part) 1289e5c31af7Sopenharmony_ci for part in full_length.split('::')) 1290e5c31af7Sopenharmony_ci # TODO replace the previous statement with the following when cosmetic changes OK 1291e5c31af7Sopenharmony_ci # entry += length.get_human_readable(make_param_name=self.makeParameterName) 1292e5c31af7Sopenharmony_ci 1293e5c31af7Sopenharmony_ci entry += ' must: be greater than ' 1294e5c31af7Sopenharmony_ci entry += self.conventions.zero 1295e5c31af7Sopenharmony_ci validity += entry 1296e5c31af7Sopenharmony_ci 1297e5c31af7Sopenharmony_ci # Find the parents of all objects referenced in this command 1298e5c31af7Sopenharmony_ci for param in handles: 1299e5c31af7Sopenharmony_ci # Do not detect a parent for return values! 1300e5c31af7Sopenharmony_ci if not self.paramIsPointer(param) or self.paramIsConst(param): 1301e5c31af7Sopenharmony_ci validity += self.makeHandleValidityParent(param, params) 1302e5c31af7Sopenharmony_ci 1303e5c31af7Sopenharmony_ci # Find the common ancestor of all objects referenced in this command 1304e5c31af7Sopenharmony_ci validity += self.makeAsciiDocHandlesCommonAncestor( 1305e5c31af7Sopenharmony_ci blockname, handles, params) 1306e5c31af7Sopenharmony_ci 1307e5c31af7Sopenharmony_ci return validity 1308e5c31af7Sopenharmony_ci 1309e5c31af7Sopenharmony_ci def makeThreadSafetyBlock(self, cmd, paramtext): 1310e5c31af7Sopenharmony_ci """Generate thread-safety validity entries for cmd/structure""" 1311e5c31af7Sopenharmony_ci # See also makeThreadSafetyBlock in validitygenerator.py 1312e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(getElemName(cmd)) 1313e5c31af7Sopenharmony_ci 1314e5c31af7Sopenharmony_ci # This text varies between projects, so an Asciidoctor attribute is used. 1315e5c31af7Sopenharmony_ci extsync_prefix = "{externsyncprefix} " 1316e5c31af7Sopenharmony_ci 1317e5c31af7Sopenharmony_ci # Find and add any parameters that are thread unsafe 1318e5c31af7Sopenharmony_ci explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]") 1319e5c31af7Sopenharmony_ci if explicitexternsyncparams is not None: 1320e5c31af7Sopenharmony_ci for param in explicitexternsyncparams: 1321e5c31af7Sopenharmony_ci externsyncattribs = ExternSyncEntry.parse_externsync_from_param( 1322e5c31af7Sopenharmony_ci param) 1323e5c31af7Sopenharmony_ci param_name = getElemName(param) 1324e5c31af7Sopenharmony_ci 1325e5c31af7Sopenharmony_ci for attrib in externsyncattribs: 1326e5c31af7Sopenharmony_ci entry = ValidityEntry() 1327e5c31af7Sopenharmony_ci entry += extsync_prefix 1328e5c31af7Sopenharmony_ci if attrib.entirely_extern_sync: 1329e5c31af7Sopenharmony_ci if self.paramIsArray(param): 1330e5c31af7Sopenharmony_ci entry += 'each member of ' 1331e5c31af7Sopenharmony_ci elif self.paramIsPointer(param): 1332e5c31af7Sopenharmony_ci entry += 'the object referenced by ' 1333e5c31af7Sopenharmony_ci 1334e5c31af7Sopenharmony_ci entry += self.makeParameterName(param_name) 1335e5c31af7Sopenharmony_ci 1336e5c31af7Sopenharmony_ci if attrib.children_extern_sync: 1337e5c31af7Sopenharmony_ci entry += ', and any child handles,' 1338e5c31af7Sopenharmony_ci 1339e5c31af7Sopenharmony_ci else: 1340e5c31af7Sopenharmony_ci entry += 'pname:' 1341e5c31af7Sopenharmony_ci entry += str(attrib.full_reference) 1342e5c31af7Sopenharmony_ci # TODO switch to the following when cosmetic changes OK 1343e5c31af7Sopenharmony_ci # entry += attrib.get_human_readable(make_param_name=self.makeParameterName) 1344e5c31af7Sopenharmony_ci entry += ' must: be externally synchronized' 1345e5c31af7Sopenharmony_ci validity += entry 1346e5c31af7Sopenharmony_ci 1347e5c31af7Sopenharmony_ci # Vulkan-specific 1348e5c31af7Sopenharmony_ci # For any vkCmd* functions, the command pool is externally synchronized 1349e5c31af7Sopenharmony_ci if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name').text: 1350e5c31af7Sopenharmony_ci entry = ValidityEntry() 1351e5c31af7Sopenharmony_ci entry += extsync_prefix 1352e5c31af7Sopenharmony_ci entry += 'the sname:VkCommandPool that pname:commandBuffer was allocated from must: be externally synchronized' 1353e5c31af7Sopenharmony_ci validity += entry 1354e5c31af7Sopenharmony_ci 1355e5c31af7Sopenharmony_ci # Find and add any "implicit" parameters that are thread unsafe 1356e5c31af7Sopenharmony_ci implicitexternsyncparams = cmd.find('implicitexternsyncparams') 1357e5c31af7Sopenharmony_ci if implicitexternsyncparams is not None: 1358e5c31af7Sopenharmony_ci for elem in implicitexternsyncparams: 1359e5c31af7Sopenharmony_ci entry = ValidityEntry() 1360e5c31af7Sopenharmony_ci entry += extsync_prefix 1361e5c31af7Sopenharmony_ci entry += elem.text 1362e5c31af7Sopenharmony_ci entry += ' must: be externally synchronized' 1363e5c31af7Sopenharmony_ci validity += entry 1364e5c31af7Sopenharmony_ci 1365e5c31af7Sopenharmony_ci return validity 1366e5c31af7Sopenharmony_ci 1367e5c31af7Sopenharmony_ci def makeCommandPropertiesTableHeader(self): 1368e5c31af7Sopenharmony_ci header = '|<<VkCommandBufferLevel,Command Buffer Levels>>' 1369e5c31af7Sopenharmony_ci header += '|<<vkCmdBeginRenderPass,Render Pass Scope>>' 1370e5c31af7Sopenharmony_ci if self.videocodingRequired(): 1371e5c31af7Sopenharmony_ci header += '|<<vkCmdBeginVideoCodingKHR,Video Coding Scope>>' 1372e5c31af7Sopenharmony_ci header += '|<<VkQueueFlagBits,Supported Queue Types>>' 1373e5c31af7Sopenharmony_ci header += '|<<fundamentals-queueoperation-command-types,Command Type>>' 1374e5c31af7Sopenharmony_ci return header 1375e5c31af7Sopenharmony_ci 1376e5c31af7Sopenharmony_ci def makeCommandPropertiesTableEntry(self, cmd, name): 1377e5c31af7Sopenharmony_ci cmdbufferlevel, renderpass, videocoding, queues, tasks = None, None, None, None, None 1378e5c31af7Sopenharmony_ci 1379e5c31af7Sopenharmony_ci if 'vkCmd' in name: 1380e5c31af7Sopenharmony_ci # Must be called in primary/secondary command buffers appropriately 1381e5c31af7Sopenharmony_ci cmdbufferlevel = cmd.get('cmdbufferlevel') 1382e5c31af7Sopenharmony_ci cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(',')) 1383e5c31af7Sopenharmony_ci 1384e5c31af7Sopenharmony_ci # Must be called inside/outside a render pass appropriately 1385e5c31af7Sopenharmony_ci renderpass = cmd.get('renderpass') 1386e5c31af7Sopenharmony_ci renderpass = renderpass.capitalize() 1387e5c31af7Sopenharmony_ci 1388e5c31af7Sopenharmony_ci # Must be called inside/outside a video coding scope appropriately 1389e5c31af7Sopenharmony_ci if self.videocodingRequired(): 1390e5c31af7Sopenharmony_ci videocoding = self.getVideocoding(cmd).capitalize() 1391e5c31af7Sopenharmony_ci 1392e5c31af7Sopenharmony_ci # 1393e5c31af7Sopenharmony_ci # This test for vkCmdFillBuffer is a hack, since we have no path 1394e5c31af7Sopenharmony_ci # to conditionally have queues enabled or disabled by an extension. 1395e5c31af7Sopenharmony_ci # As the VU stuff is all moving out (hopefully soon), this hack solves the issue for now 1396e5c31af7Sopenharmony_ci if name == 'vkCmdFillBuffer': 1397e5c31af7Sopenharmony_ci if self.isVKVersion11() or 'VK_KHR_maintenance1' in self.registry.requiredextensions: 1398e5c31af7Sopenharmony_ci queues = [ 'transfer', 'graphics', 'compute' ] 1399e5c31af7Sopenharmony_ci else: 1400e5c31af7Sopenharmony_ci queues = [ 'graphics', 'compute' ] 1401e5c31af7Sopenharmony_ci else: 1402e5c31af7Sopenharmony_ci queues = self.getQueueList(cmd) 1403e5c31af7Sopenharmony_ci queues = (' + \n').join([queue.title() for queue in queues]) 1404e5c31af7Sopenharmony_ci 1405e5c31af7Sopenharmony_ci tasks = cmd.get('tasks') 1406e5c31af7Sopenharmony_ci tasks = (' + \n').join(tasks.title().split(',')) 1407e5c31af7Sopenharmony_ci elif 'vkQueue' in name: 1408e5c31af7Sopenharmony_ci # For queue commands there are no command buffer level, render 1409e5c31af7Sopenharmony_ci # pass, or video coding scope specific restrictions, 1410e5c31af7Sopenharmony_ci # or command type, but the queue types are considered 1411e5c31af7Sopenharmony_ci cmdbufferlevel = '-' 1412e5c31af7Sopenharmony_ci renderpass = '-' 1413e5c31af7Sopenharmony_ci if self.videocodingRequired(): 1414e5c31af7Sopenharmony_ci videocoding = '-' 1415e5c31af7Sopenharmony_ci 1416e5c31af7Sopenharmony_ci queues = self.getQueueList(cmd) 1417e5c31af7Sopenharmony_ci if queues is None: 1418e5c31af7Sopenharmony_ci queues = 'Any' 1419e5c31af7Sopenharmony_ci else: 1420e5c31af7Sopenharmony_ci queues = (' + \n').join([queue.upper() for queue in queues]) 1421e5c31af7Sopenharmony_ci 1422e5c31af7Sopenharmony_ci tasks = '-' 1423e5c31af7Sopenharmony_ci 1424e5c31af7Sopenharmony_ci table_items = (cmdbufferlevel, renderpass, videocoding, queues, tasks) 1425e5c31af7Sopenharmony_ci entry = '|'.join(filter(None, table_items)) 1426e5c31af7Sopenharmony_ci 1427e5c31af7Sopenharmony_ci return ('|' + entry) if entry else None 1428e5c31af7Sopenharmony_ci 1429e5c31af7Sopenharmony_ci 1430e5c31af7Sopenharmony_ci def findRequiredEnums(self, enums): 1431e5c31af7Sopenharmony_ci """Check each enumerant name in the enums list and remove it if not 1432e5c31af7Sopenharmony_ci required by the generator. This allows specifying success and error 1433e5c31af7Sopenharmony_ci codes for extensions that are only included in validity when needed 1434e5c31af7Sopenharmony_ci for the spec being targeted.""" 1435e5c31af7Sopenharmony_ci return self.keepOnlyRequired(enums, self.registry.enumdict) 1436e5c31af7Sopenharmony_ci 1437e5c31af7Sopenharmony_ci def findRequiredCommands(self, commands): 1438e5c31af7Sopenharmony_ci """Check each command name in the commands list and remove it if not 1439e5c31af7Sopenharmony_ci required by the generator. 1440e5c31af7Sopenharmony_ci 1441e5c31af7Sopenharmony_ci This will allow some state operations to take place before endFile.""" 1442e5c31af7Sopenharmony_ci return self.keepOnlyRequired(commands, self.registry.cmddict) 1443e5c31af7Sopenharmony_ci 1444e5c31af7Sopenharmony_ci def keepOnlyRequired(self, names, info_dict): 1445e5c31af7Sopenharmony_ci """Check each element name in the supplied dictionary and remove it if not 1446e5c31af7Sopenharmony_ci required by the generator. 1447e5c31af7Sopenharmony_ci 1448e5c31af7Sopenharmony_ci This will allow some operations to take place before endFile no matter the order of generation.""" 1449e5c31af7Sopenharmony_ci # TODO Unpleasantly breaks encapsulation. Should be a method in the registry class 1450e5c31af7Sopenharmony_ci 1451e5c31af7Sopenharmony_ci def is_required(name): 1452e5c31af7Sopenharmony_ci info = self.registry.lookupElementInfo(name, info_dict) 1453e5c31af7Sopenharmony_ci if info is None: 1454e5c31af7Sopenharmony_ci return False 1455e5c31af7Sopenharmony_ci if not info.required: 1456e5c31af7Sopenharmony_ci self.logMsg('diag', 'keepOnlyRequired: element', 1457e5c31af7Sopenharmony_ci name, 'IS NOT required, skipping') 1458e5c31af7Sopenharmony_ci return info.required 1459e5c31af7Sopenharmony_ci 1460e5c31af7Sopenharmony_ci return [name 1461e5c31af7Sopenharmony_ci for name in names 1462e5c31af7Sopenharmony_ci if is_required(name)] 1463e5c31af7Sopenharmony_ci 1464e5c31af7Sopenharmony_ci def makeReturnCodeList(self, attrib, cmd, name): 1465e5c31af7Sopenharmony_ci """Return a list of possible return codes for a function. 1466e5c31af7Sopenharmony_ci 1467e5c31af7Sopenharmony_ci attrib is either 'successcodes' or 'errorcodes'. 1468e5c31af7Sopenharmony_ci """ 1469e5c31af7Sopenharmony_ci return_lines = [] 1470e5c31af7Sopenharmony_ci RETURN_CODE_FORMAT = '* ename:{}' 1471e5c31af7Sopenharmony_ci 1472e5c31af7Sopenharmony_ci codes_attr = cmd.get(attrib) 1473e5c31af7Sopenharmony_ci if codes_attr: 1474e5c31af7Sopenharmony_ci codes = self.findRequiredEnums(codes_attr.split(',')) 1475e5c31af7Sopenharmony_ci if codes: 1476e5c31af7Sopenharmony_ci return_lines.extend((RETURN_CODE_FORMAT.format(code) 1477e5c31af7Sopenharmony_ci for code in codes)) 1478e5c31af7Sopenharmony_ci 1479e5c31af7Sopenharmony_ci applicable_ext_codes = (ext_code 1480e5c31af7Sopenharmony_ci for ext_code in self.registry.commandextensionsuccesses 1481e5c31af7Sopenharmony_ci if ext_code.command == name) 1482e5c31af7Sopenharmony_ci for ext_code in applicable_ext_codes: 1483e5c31af7Sopenharmony_ci line = RETURN_CODE_FORMAT.format(ext_code.value) 1484e5c31af7Sopenharmony_ci if ext_code.extension: 1485e5c31af7Sopenharmony_ci line += ' [only if {} is enabled]'.format( 1486e5c31af7Sopenharmony_ci self.conventions.formatExtension(ext_code.extension)) 1487e5c31af7Sopenharmony_ci 1488e5c31af7Sopenharmony_ci return_lines.append(line) 1489e5c31af7Sopenharmony_ci if return_lines: 1490e5c31af7Sopenharmony_ci return '\n'.join(return_lines) 1491e5c31af7Sopenharmony_ci 1492e5c31af7Sopenharmony_ci return None 1493e5c31af7Sopenharmony_ci 1494e5c31af7Sopenharmony_ci def makeSuccessCodes(self, cmd, name): 1495e5c31af7Sopenharmony_ci return self.makeReturnCodeList('successcodes', cmd, name) 1496e5c31af7Sopenharmony_ci 1497e5c31af7Sopenharmony_ci def makeErrorCodes(self, cmd, name): 1498e5c31af7Sopenharmony_ci return self.makeReturnCodeList('errorcodes', cmd, name) 1499e5c31af7Sopenharmony_ci 1500e5c31af7Sopenharmony_ci def genCmd(self, cmdinfo, name, alias): 1501e5c31af7Sopenharmony_ci """Command generation.""" 1502e5c31af7Sopenharmony_ci OutputGenerator.genCmd(self, cmdinfo, name, alias) 1503e5c31af7Sopenharmony_ci 1504e5c31af7Sopenharmony_ci # @@@ (Jon) something needs to be done here to handle aliases, probably 1505e5c31af7Sopenharmony_ci 1506e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(name) 1507e5c31af7Sopenharmony_ci 1508e5c31af7Sopenharmony_ci # OpenXR-only: make sure extension is enabled 1509e5c31af7Sopenharmony_ci # validity.possiblyAddExtensionRequirement(self.currentExtension, 'calling flink:') 1510e5c31af7Sopenharmony_ci 1511e5c31af7Sopenharmony_ci validity += self.makeStructOrCommandValidity( 1512e5c31af7Sopenharmony_ci cmdinfo.elem, name, cmdinfo.getParams()) 1513e5c31af7Sopenharmony_ci 1514e5c31af7Sopenharmony_ci threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param') 1515e5c31af7Sopenharmony_ci commandpropertiesentry = None 1516e5c31af7Sopenharmony_ci 1517e5c31af7Sopenharmony_ci # Vulkan-specific 1518e5c31af7Sopenharmony_ci commandpropertiesentry = self.makeCommandPropertiesTableEntry( 1519e5c31af7Sopenharmony_ci cmdinfo.elem, name) 1520e5c31af7Sopenharmony_ci successcodes = self.makeSuccessCodes(cmdinfo.elem, name) 1521e5c31af7Sopenharmony_ci errorcodes = self.makeErrorCodes(cmdinfo.elem, name) 1522e5c31af7Sopenharmony_ci 1523e5c31af7Sopenharmony_ci # OpenXR-specific 1524e5c31af7Sopenharmony_ci # self.generateStateValidity(validity, name) 1525e5c31af7Sopenharmony_ci 1526e5c31af7Sopenharmony_ci self.writeInclude('protos', name, validity, threadsafety, 1527e5c31af7Sopenharmony_ci commandpropertiesentry, successcodes, errorcodes) 1528e5c31af7Sopenharmony_ci 1529e5c31af7Sopenharmony_ci def genStruct(self, typeinfo, typeName, alias): 1530e5c31af7Sopenharmony_ci """Struct Generation.""" 1531e5c31af7Sopenharmony_ci OutputGenerator.genStruct(self, typeinfo, typeName, alias) 1532e5c31af7Sopenharmony_ci 1533e5c31af7Sopenharmony_ci # @@@ (Jon) something needs to be done here to handle aliases, probably 1534e5c31af7Sopenharmony_ci 1535e5c31af7Sopenharmony_ci # Anything that is only ever returned cannot be set by the user, so 1536e5c31af7Sopenharmony_ci # should not have any validity information. 1537e5c31af7Sopenharmony_ci validity = self.makeValidityCollection(typeName) 1538e5c31af7Sopenharmony_ci threadsafety = [] 1539e5c31af7Sopenharmony_ci 1540e5c31af7Sopenharmony_ci # OpenXR-only: make sure extension is enabled 1541e5c31af7Sopenharmony_ci # validity.possiblyAddExtensionRequirement(self.currentExtension, 'using slink:') 1542e5c31af7Sopenharmony_ci 1543e5c31af7Sopenharmony_ci if typeinfo.elem.get('category') != 'union': 1544e5c31af7Sopenharmony_ci if typeinfo.elem.get('returnedonly') is None: 1545e5c31af7Sopenharmony_ci validity += self.makeStructOrCommandValidity( 1546e5c31af7Sopenharmony_ci typeinfo.elem, typeName, typeinfo.getMembers()) 1547e5c31af7Sopenharmony_ci threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member') 1548e5c31af7Sopenharmony_ci 1549e5c31af7Sopenharmony_ci else: 1550e5c31af7Sopenharmony_ci # Need to generate structure type and next pointer chain member validation 1551e5c31af7Sopenharmony_ci validity += self.makeOutputOnlyStructValidity( 1552e5c31af7Sopenharmony_ci typeinfo.elem, typeName, typeinfo.getMembers()) 1553e5c31af7Sopenharmony_ci 1554e5c31af7Sopenharmony_ci self.writeInclude('structs', typeName, validity, 1555e5c31af7Sopenharmony_ci threadsafety, None, None, None) 1556e5c31af7Sopenharmony_ci 1557e5c31af7Sopenharmony_ci def genGroup(self, groupinfo, groupName, alias): 1558e5c31af7Sopenharmony_ci """Group (e.g. C "enum" type) generation. 1559e5c31af7Sopenharmony_ci For the validity generator, this just tags individual enumerants 1560e5c31af7Sopenharmony_ci as required or not. 1561e5c31af7Sopenharmony_ci """ 1562e5c31af7Sopenharmony_ci OutputGenerator.genGroup(self, groupinfo, groupName, alias) 1563e5c31af7Sopenharmony_ci 1564e5c31af7Sopenharmony_ci # @@@ (Jon) something needs to be done here to handle aliases, probably 1565e5c31af7Sopenharmony_ci 1566e5c31af7Sopenharmony_ci groupElem = groupinfo.elem 1567e5c31af7Sopenharmony_ci 1568e5c31af7Sopenharmony_ci # Loop over the nested 'enum' tags. Keep track of the minimum and 1569e5c31af7Sopenharmony_ci # maximum numeric values, if they can be determined; but only for 1570e5c31af7Sopenharmony_ci # core API enumerants, not extension enumerants. This is inferred 1571e5c31af7Sopenharmony_ci # by looking for 'extends' attributes. 1572e5c31af7Sopenharmony_ci for elem in groupElem.findall('enum'): 1573e5c31af7Sopenharmony_ci name = elem.get('name') 1574e5c31af7Sopenharmony_ci ei = self.registry.lookupElementInfo(name, self.registry.enumdict) 1575e5c31af7Sopenharmony_ci 1576e5c31af7Sopenharmony_ci if ei is None: 1577e5c31af7Sopenharmony_ci self.logMsg('error', 1578e5c31af7Sopenharmony_ci f'genGroup({groupName}) - no element found for enum {name}') 1579e5c31af7Sopenharmony_ci 1580e5c31af7Sopenharmony_ci # Tag enumerant as required or not 1581e5c31af7Sopenharmony_ci ei.required = self.isEnumRequired(elem) 1582e5c31af7Sopenharmony_ci 1583e5c31af7Sopenharmony_ci def genType(self, typeinfo, name, alias): 1584e5c31af7Sopenharmony_ci """Type Generation.""" 1585e5c31af7Sopenharmony_ci OutputGenerator.genType(self, typeinfo, name, alias) 1586e5c31af7Sopenharmony_ci 1587e5c31af7Sopenharmony_ci # @@@ (Jon) something needs to be done here to handle aliases, probably 1588e5c31af7Sopenharmony_ci 1589e5c31af7Sopenharmony_ci category = typeinfo.elem.get('category') 1590e5c31af7Sopenharmony_ci if category in ('struct', 'union'): 1591e5c31af7Sopenharmony_ci self.genStruct(typeinfo, name, alias) 1592