layers: Add pNext-chain-only structs to PV codegen
Structs accessed exclusively through pNext chains were not
validated. Added this functionality, picked up about 90 VUIDs.
Change-Id: Iefaecb14d5c1941352f675e38c9dd878730b1adb
diff --git a/scripts/parameter_validation_generator.py b/scripts/parameter_validation_generator.py
index d6b1be9..2c6ee70 100644
--- a/scripts/parameter_validation_generator.py
+++ b/scripts/parameter_validation_generator.py
@@ -175,14 +175,17 @@
self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
self.extension_type = '' # Type of active feature (extension), device or instance
self.extension_names = dict() # Dictionary of extension names to extension name defines
+ self.structextends_list = [] # List of extensions which extend another struct
+ self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
self.valid_vuids = set() # Set of all valid VUIDs
self.vuid_dict = dict() # VUID dictionary (from JSON)
self.alias_dict = dict() # Dict of cmd|struct aliases
+ self.returnedonly_structs = []
# Named tuples to store struct and command data
self.StructType = namedtuple('StructType', ['name', 'value'])
self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
- 'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
- 'condition', 'cdecl'])
+ 'isconst', 'isoptional', 'iscount', 'noautovalidity',
+ 'len', 'extstructs', 'condition', 'cdecl'])
self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
@@ -302,6 +305,44 @@
self.func_pointers += '};\n'
write(self.func_pointers, file=self.outFile)
self.newline()
+
+ pnext_handler = 'bool ValidatePnextStructContents(debug_report_data *report_data, const char *api_name, const ParameterName ¶meter_name, const GenericHeader* header) {\n'
+ pnext_handler += ' bool skip = false;\n'
+ pnext_handler += ' switch(header->sType) {\n'
+
+ # Do some processing here to extract data from validatedstructs...
+ for item in self.structextends_list:
+ postProcSpec = {}
+ postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
+ postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
+ postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
+
+ pnext_case = '\n'
+ protect = ''
+ # Guard struct cases with feature ifdefs, if necessary
+ if item in self.struct_feature_protect.keys():
+ protect = self.struct_feature_protect[item]
+ pnext_case += '#ifdef %s\n' % protect
+ pnext_case += ' // Validation code for %s structure members\n' % item
+ pnext_case += ' case %s: {\n' % self.getStructType(item)
+ pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
+ expr = self.expandStructCode(self.validatedStructs[item], item, 'structure->', '', ' ', [], postProcSpec)
+ struct_validation_source = self.ScrubStructCode(expr)
+ pnext_case += '%s' % struct_validation_source
+ pnext_case += ' } break;\n'
+ if protect is not '':
+ pnext_case += '#endif // %s\n' % protect
+ # Skip functions containing no validation
+ if struct_validation_source != '':
+ pnext_handler += pnext_case;
+ pnext_handler += ' default:\n'
+ pnext_handler += ' skip = false;\n'
+ pnext_handler += ' }\n'
+ pnext_handler += ' return skip;\n'
+ pnext_handler += '}\n'
+ write(pnext_handler, file=self.outFile)
+ self.newline()
+
ext_template = 'template <typename T>\n'
ext_template += 'bool OutputExtensionError(const T *layer_data, const std::string &api_name, const std::string &extension_name) {\n'
ext_template += ' return log_msg(layer_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
@@ -436,6 +477,8 @@
OutputGenerator.genStruct(self, typeinfo, typeName, alias)
conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
members = typeinfo.elem.findall('.//member')
+ if self.featureExtraProtect is not None:
+ self.struct_feature_protect[typeName] = self.featureExtraProtect
#
# Iterate over members once to get length parameters for arrays
lens = set()
@@ -480,6 +523,7 @@
noautovalidity = False
if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
noautovalidity = True
+ structextends = False
membersInfo.append(self.CommandParam(type=type, name=name,
ispointer=self.paramIsPointer(member),
isstaticarray=isstaticarray,
@@ -493,6 +537,16 @@
extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
condition=conditions[name] if conditions and name in conditions else None,
cdecl=cdecl))
+ # If this struct extends another, keep its name in list for further processing
+ if typeinfo.elem.attrib.get('structextends') is not None:
+ self.structextends_list.append(typeName)
+ # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
+ # pNext members. Everything else will be overwritten by the callee.
+ if typeinfo.elem.attrib.get('returnedonly') is not None:
+ if typeName == 'VkPhysicalDeviceGroupProperties':
+ stop = 'here'
+ self.returnedonly_structs.append(typeName)
+ membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
#
# Capture group (e.g. C "enum" type) info to be used for param check code generation.
@@ -662,6 +716,7 @@
value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
value = value.replace('D3_D12', 'D3D12')
value = value.replace('Device_IDProp', 'Device_ID_Prop')
+ value = value.replace('LODGather', 'LOD_Gather')
# Change to uppercase
value = value.upper()
# Add STRUCTURE_TYPE_
@@ -736,8 +791,8 @@
if '->' in name:
# The count is obtained by dereferencing a member of a struct parameter
lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
- isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
- condition=None, cdecl=None)
+ isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
+ len=None, extstructs=None, condition=None, cdecl=None)
elif 'latexmath' in name:
lenName, decoratedName = self.parseLateXMath(name)
lenParam = self.getParamByName(params, lenName)
@@ -959,6 +1014,27 @@
return line.format(**kwargs)
return line
#
+ # Process struct member validation code, stripping metadata
+ def ScrubStructCode(self, code):
+ scrubbed_lines = ''
+ for line in code:
+ if 'validate_struct_pnext' in line:
+ continue
+ if 'allowed_structs' in line:
+ continue
+ if 'xml-driven validation' in line:
+ continue
+ line = line.replace('{postProcPrefix}', '')
+ line = line.replace('{postProcSuffix}', '')
+ line = line.replace('{postProcInsert}', '')
+ line = line.replace('{funcName}', '')
+ line = line.replace('{valuePrefix}', '')
+ line = line.replace('{displayNamePrefix}', '')
+ line = line.replace('{IndexVector}', '')
+ line = line.replace('local_data->', '')
+ scrubbed_lines += line
+ return scrubbed_lines
+ #
# Process struct validation code for inclusion in function or parent struct validation code
def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
for line in lines:
@@ -1011,6 +1087,10 @@
def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
lines = [] # Generated lines of code
unused = [] # Unused variable names
+
+ if structTypeName == 'VkPhysicalDeviceGroupProperties':
+ stop = 'here'
+
for value in values:
usedLines = []
lenParam = None
@@ -1028,7 +1108,7 @@
# will be validated with their associated array
if (value.ispointer or value.isstaticarray) and not value.iscount:
# Parameters for function argument generation
- req = 'true' # Paramerter cannot be NULL
+ req = 'true' # Parameter cannot be NULL
cpReq = 'true' # Count pointer cannot be NULL
cvReq = 'true' # Count value cannot be 0
lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
@@ -1080,13 +1160,17 @@
else:
usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
# If this is a pointer to a struct (input), see if it contains members that need to be checked
- if value.type in self.validatedStructs and value.isconst:
- usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
+ ###if value.type in self.validatedStructs and value.isconst:
+ if value.type in self.validatedStructs:
+ if value.isconst: # or value.type in self.returnedonly_structs:
+ usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
+ elif value.type in self.returnedonly_structs:
+ usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
# Non-pointer types
else:
# The parameter will not be processes when tagged as 'noautovalidity'
# For the struct case, the struct type will not be validated, but any
- # members not tagged as 'noatuvalidity' will be validated
+ # members not tagged as 'noautovalidity' will be validated
if value.noautovalidity:
# Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
@@ -1163,6 +1247,11 @@
just_validate = True
# Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
startIndex = 0 if command.name == 'vkCreateInstance' else 1
+
+ if command.name == 'vkEnumeratePhysicalDeviceGroups':
+ stop = 'here'
+
+
lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
# Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
if (command.name in self.required_extensions) and (self.extension_type != 'device' or command.params[0].type != 'VkPhysicalDevice'):