layers: Re-architect parameter validation layer
Changed the codegen to autogenerate validation functions for any new
APIs without user intervention. Manual updates are only necessary to
extend PV functionality.
- parameter_validation.h is now checked into the layers directory
- parameter_validation.cpp is now generated
- parameter_validation_utils.cpp is new and contains any custom
(non-generated) or housekeeping functions.
- parameter_validation_utils.h is deprecated
- updated database for new VUID coverage
Change-Id: Ib9261894386145573ba9e8906ba4ad4e9fecbdfd
diff --git a/scripts/lvl_genvk.py b/scripts/lvl_genvk.py
index 49bf2cb..194106e 100644
--- a/scripts/lvl_genvk.py
+++ b/scripts/lvl_genvk.py
@@ -20,7 +20,7 @@
from cgenerator import CGeneratorOptions, COutputGenerator
# LoaderAndValidationLayer Generator Modifications
from threading_generator import ThreadGeneratorOptions, ThreadOutputGenerator
-from parameter_validation_generator import ParamCheckerGeneratorOptions, ParamCheckerOutputGenerator
+from parameter_validation_generator import ParameterValidationGeneratorOptions, ParameterValidationOutputGenerator
from unique_objects_generator import UniqueObjectsGeneratorOptions, UniqueObjectsOutputGenerator
from object_tracker_generator import ObjectTrackerGeneratorOptions, ObjectTrackerOutputGenerator
from dispatch_table_helper_generator import DispatchTableHelperOutputGenerator, DispatchTableHelperOutputGeneratorOptions
@@ -122,11 +122,12 @@
alignFuncParam = 48)
]
+
# Options for parameter validation layer
- genOpts['parameter_validation.h'] = [
- ParamCheckerOutputGenerator,
- ParamCheckerGeneratorOptions(
- filename = 'parameter_validation.h',
+ genOpts['parameter_validation.cpp'] = [
+ ParameterValidationOutputGenerator,
+ ParameterValidationGeneratorOptions(
+ filename = 'parameter_validation.cpp',
directory = directory,
apiname = 'vulkan',
profile = None,
diff --git a/scripts/parameter_validation_generator.py b/scripts/parameter_validation_generator.py
index 9a613d1..8e04f75 100644
--- a/scripts/parameter_validation_generator.py
+++ b/scripts/parameter_validation_generator.py
@@ -27,11 +27,9 @@
from vuid_mapping import *
-
-# ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
+# ParameterValidationGeneratorOptions - subclass of GeneratorOptions.
#
-# Adds options used by ParamCheckerOutputGenerator object during Parameter
-# validation layer generation.
+# Adds options used by ParameterValidationOutputGenerator object during Parameter validation layer generation.
#
# Additional members
# prefixText - list of strings to prefix generated header with
@@ -61,7 +59,7 @@
# parameter on a separate line
# alignFuncParam - if nonzero and parameters are being put on a
# separate line, align parameter names at the specified column
-class ParamCheckerGeneratorOptions(GeneratorOptions):
+class ParameterValidationGeneratorOptions(GeneratorOptions):
def __init__(self,
filename = None,
directory = '.',
@@ -101,7 +99,7 @@
self.indentFuncPointer = indentFuncPointer
self.alignFuncParam = alignFuncParam
-# ParamCheckerOutputGenerator - subclass of OutputGenerator.
+# ParameterValidationOutputGenerator - subclass of OutputGenerator.
# Generates param checker layer code.
#
# ---- methods ----
@@ -117,8 +115,8 @@
# genGroup(groupinfo,name)
# genEnum(enuminfo, name)
# genCmd(cmdinfo)
-class ParamCheckerOutputGenerator(OutputGenerator):
- """Generate ParamChecker code based on XML element attributes"""
+class ParameterValidationOutputGenerator(OutputGenerator):
+ """Generate Parameter Validation code based on XML element attributes"""
# This is an ordered list of sections in the header file.
ALL_SECTIONS = ['command']
def __init__(self,
@@ -137,8 +135,24 @@
'vkEnumerateInstanceExtensionsProperties',
'vkEnumerateDeviceLayerProperties',
'vkEnumerateDeviceExtensionsProperties',
+ 'vkCreateDebugReportCallbackKHR',
+ 'vkDestroyDebugReportCallbackKHR',
+ 'vkEnumerateInstanceLayerProperties',
+ 'vkEnumerateInstanceExtensionProperties',
+ 'vkEnumerateDeviceLayerProperties',
+ 'vkCmdDebugMarkerEndEXT',
+ 'vkEnumerateDeviceExtensionProperties',
+ ]
+ self.validate_only = [
+ 'vkCreateInstance',
+ 'vkDestroyInstance',
+ 'vkCreateDevice',
+ 'vkDestroyDevice',
+ 'vkCreateQueryPool',
'vkCreateDebugReportCallbackEXT',
- 'vkDebugReportMessageEXT']
+ 'vkDestroyDebugReportCallbackEXT',
+ 'vkCreateCommandPool',
+ ]
# Structure fields to ignore
self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
# Validation conditions for some special case struct members that are conditionally validated
@@ -156,6 +170,8 @@
self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
self.enumRanges = dict() # Map of enum name to BEGIN/END range values
self.enumValueLists = '' # String containing enumerated type map definitions
+ self.func_pointers = '' # String containing function pointers for manual PV functions
+ self.typedefs = '' # String containing function pointer typedefs
self.flags = set() # Map of flags typenames
self.flagBits = dict() # Map of flag bits typename to list of values
self.newFlags = set() # Map of flags typenames /defined in the current feature/
@@ -168,7 +184,7 @@
self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
'condition', 'cdecl'])
- self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type'])
+ self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
self.vuid_file = None
@@ -187,16 +203,45 @@
print("Error: Could not find vk_validation_error_messages.h")
quit()
#
+ # Generate Copyright comment block for file
+ def GenerateCopyright(self):
+ copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
+ copyright += ' * See parameter_validation_generator.py for modifications\n'
+ copyright += ' *\n'
+ copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n'
+ copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n'
+ copyright += ' * Copyright (C) 2015-2017 Google Inc.\n'
+ copyright += ' *\n'
+ copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
+ copyright += ' * you may not use this file except in compliance with the License.\n'
+ copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
+ copyright += ' * You may obtain a copy of the License at\n'
+ copyright += ' *\n'
+ copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
+ copyright += ' *\n'
+ copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
+ copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
+ copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
+ copyright += ' * See the License for the specific language governing permissions and\n'
+ copyright += ' * limitations under the License.\n'
+ copyright += ' *\n'
+ copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
+ copyright += ' */\n\n'
+ return copyright
+ #
+ # Increases the global indent variable
def incIndent(self, indent):
inc = ' ' * self.INDENT_SPACES
if indent:
return indent + inc
return inc
#
+ # Decreases the global indent variable
def decIndent(self, indent):
if indent and (len(indent) > self.INDENT_SPACES):
return indent[:-self.INDENT_SPACES]
return ''
+ #
# Convert decimal number to 8 digit hexadecimal lower-case representation
def IdToHex(self, dec_num):
if dec_num > 4294967295:
@@ -205,6 +250,7 @@
hex_num = hex(dec_num)
return hex_num[2:].zfill(8)
#
+ # Called at file creation time
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
# C-specific
@@ -221,23 +267,16 @@
self.valid_vuids.add(vuid_num)
#
# User-supplied prefix text, if any (list of strings)
- if (genOpts.prefixText):
- for s in genOpts.prefixText:
- write(s, file=self.outFile)
- #
- # Multiple inclusion protection & C++ wrappers.
- if (genOpts.protectFile and self.genOpts.filename):
- headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
- write('#ifndef', headerSym, file=self.outFile)
- write('#define', headerSym, '1', file=self.outFile)
- self.newline()
+ s = self.GenerateCopyright()
+ write(s, file=self.outFile)
#
# Headers
write('#include <string>', file=self.outFile)
self.newline()
+ write('#include "vk_loader_platform.h"', file=self.outFile)
write('#include "vulkan/vulkan.h"', file=self.outFile)
write('#include "vk_layer_extension_utils.h"', file=self.outFile)
- write('#include "parameter_validation_utils.h"', file=self.outFile)
+ write('#include "parameter_validation.h"', file=self.outFile)
#
# Macros
self.newline()
@@ -248,11 +287,30 @@
# Namespace
self.newline()
write('namespace parameter_validation {', file = self.outFile)
+ self.newline()
+ write('extern std::mutex global_lock;', file = self.outFile)
+ write('extern std::unordered_map<void *, layer_data *> layer_data_map;', file = self.outFile)
+ write('extern std::unordered_map<void *, instance_layer_data *> instance_layer_data_map;', file = self.outFile)
+ self.newline()
+ #
+ # Called at end-time for final content output
def endFile(self):
# C-specific
self.newline()
write(self.enumValueLists, file=self.outFile)
self.newline()
+ write(self.typedefs, file=self.outFile)
+ self.newline()
+ write(self.func_pointers, 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, __LINE__,\n'
+ ext_template += ' EXTENSION_NOT_ENABLED, LayerName, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
+ ext_template += ' api_name.c_str(), extension_name.c_str());\n'
+ ext_template += '}\n'
+ write(ext_template, file=self.outFile)
+ self.newline()
commands_text = '\n'.join(self.validation)
write(commands_text, file=self.outFile)
self.newline()
@@ -260,25 +318,22 @@
write('// Declarations', file=self.outFile)
write('\n'.join(self.declarations), file=self.outFile)
write('// Map of all APIs to be intercepted by this layer', file=self.outFile)
- write('static const std::unordered_map<std::string, void*> name_to_funcptr_map = {', file=self.outFile)
+ write('const std::unordered_map<std::string, void*> name_to_funcptr_map = {', file=self.outFile)
write('\n'.join(self.intercepts), file=self.outFile)
write('};\n', file=self.outFile)
self.newline()
# Namespace
write('} // namespace parameter_validation', file = self.outFile)
- # Finish C++ wrapper and multiple inclusion protection
- if (self.genOpts.protectFile and self.genOpts.filename):
- self.newline()
- write('#endif', file=self.outFile)
# Finish processing in superclass
OutputGenerator.endFile(self)
+ #
+ # Processing at beginning of each feature or extension
def beginFeature(self, interface, emit):
# Start processing in superclass
OutputGenerator.beginFeature(self, interface, emit)
# C-specific
- # Accumulate includes, defines, types, enums, function pointer typedefs,
- # end function prototypes separately for this feature. They're only
- # printed in endFeature().
+ # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
+ # feature. They're only printed in endFeature().
self.headerVersion = None
self.structNames = []
self.stypes = []
@@ -301,12 +356,13 @@
self.required_extensions.extend(required_extensions.split(','))
# And note if this is an Instance or Device extension
self.extension_type = interface.get('type')
+ #
+ # Called at the end of each extension (feature)
def endFeature(self):
# C-specific
# Actually write the interface to the output file.
if (self.emit):
- # If type declarations are needed by other features based on
- # this one, it may be necessary to suppress the ExtraProtect,
+ # If type declarations are needed by other features based on this one, it may be necessary to suppress the ExtraProtect,
# or move it below the 'for section...' loop.
ifdef = ''
if (self.featureExtraProtect != None):
@@ -341,8 +397,7 @@
def genType(self, typeinfo, name):
OutputGenerator.genType(self, typeinfo, name)
typeElem = typeinfo.elem
- # If the type is a struct type, traverse the imbedded <member> tags
- # generating a structure. Otherwise, emit the tag text.
+ # If the type is a struct type, traverse the imbedded <member> tags generating a structure. Otherwise, emit the tag text.
category = typeElem.get('category')
if (category == 'struct' or category == 'union'):
self.structNames.append(name)
@@ -358,12 +413,9 @@
self.headerVersion = noneStr(nameElem.tail).strip()
#
# Struct parameter check generation.
- # This is a special case of the <type> tag where the contents are
- # interpreted as a set of <member> tags instead of freeform C
- # C type declarations. The <member> tags are just like <param>
- # tags - they are a declaration of a struct or union member.
- # Only simple member declarations are supported (no nested
- # structs etc.)
+ # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
+ # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
+ # Only simple member declarations are supported (no nested structs etc.)
def genStruct(self, typeinfo, typeName):
OutputGenerator.genStruct(self, typeinfo, typeName)
conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
@@ -387,8 +439,8 @@
cdecl = self.makeCParamDecl(member, 0)
# Process VkStructureType
if type == 'VkStructureType':
- # Extract the required struct type value from the comments
- # embedded in the original text defining the 'typeinfo' element
+ # Extract the required struct type value from the comments embedded in the original text defining the
+ # 'typeinfo' element
rawXml = etree.tostring(typeinfo.elem).decode('ascii')
result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
if result:
@@ -398,15 +450,12 @@
# Store the required type value
self.structTypes[typeName] = self.StructType(name=name, value=value)
#
- # Store pointer/array/string info
- # Check for parameter name in lens set
+ # Store pointer/array/string info -- Check for parameter name in lens set
iscount = False
if name in lens:
iscount = True
- # The pNext members are not tagged as optional, but are treated as
- # optional for parameter NULL checks. Static array members
- # are also treated as optional to skip NULL pointer validation, as
- # they won't be NULL.
+ # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
+ # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
isstaticarray = self.paramIsStaticArray(member)
isoptional = False
if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
@@ -435,7 +484,6 @@
def genGroup(self, groupinfo, groupName):
OutputGenerator.genGroup(self, groupinfo, groupName)
groupElem = groupinfo.elem
- #
# Store the sType values
if groupName == 'VkStructureType':
for elem in groupElem.findall('enum'):
@@ -471,24 +519,28 @@
# Capture command parameter info to be used for param check code generation.
def genCmd(self, cmdinfo, name):
OutputGenerator.genCmd(self, cmdinfo, name)
- interface_functions = [
- 'vkEnumerateInstanceLayerProperties',
- 'vkEnumerateInstanceExtensionProperties',
- 'vkEnumerateDeviceLayerProperties',
- 'vkCmdDebugMarkerEndEXT', # No validation!
- ]
- # Record that the function will be intercepted
- if name not in interface_functions:
+ decls = self.makeCDecls(cmdinfo.elem)
+ typedef = decls[1]
+ typedef = typedef.split(')',1)[1]
+ if name not in self.blacklist:
if (self.featureExtraProtect != None):
self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
- self.intercepts += [ ' {"%s", (void*)%s},' % (name,name[2:]) ]
- decls = self.makeCDecls(cmdinfo.elem)
+ if (name not in self.validate_only):
+ self.func_pointers += '#ifdef %s\n' % self.featureExtraProtect
+ self.typedefs += '#ifdef %s\n' % self.featureExtraProtect
+ if (name not in self.validate_only):
+ self.typedefs += 'typedef bool (*PFN_manual_%s)%s\n' % (name, typedef)
+ self.func_pointers += 'PFN_manual_%s manual_%s = (PFN_manual_%s)nullptr;\n' % (name, name, name)
+ self.intercepts += [ ' {"%s", (void*)%s},' % (name,name) ]
# Strip off 'vk' from API name
self.declarations += [ '%s' % decls[0].replace("VKAPI_CALL vk", "VKAPI_CALL ") ]
if (self.featureExtraProtect != None):
self.intercepts += [ '#endif' ]
self.declarations += [ '#endif' ]
+ if (name not in self.validate_only):
+ self.func_pointers += '#endif\n'
+ self.typedefs += '#endif\n'
if name not in self.blacklist:
params = cmdinfo.elem.findall('param')
# Get list of array lengths
@@ -519,7 +571,12 @@
extstructs=None,
condition=None,
cdecl=cdecl))
- self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0], extension_type=self.extension_type))
+ # Save return value information, if any
+ result_type = ''
+ resultinfo = cmdinfo.elem.find('proto/type')
+ if (resultinfo != None and resultinfo.text != 'void'):
+ result_type = resultinfo.text
+ self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0], extension_type=self.extension_type, result=result_type))
#
# Check if the parameter passed in is a pointer
def paramIsPointer(self, param):
@@ -604,10 +661,8 @@
result = None
len = param.attrib.get('len')
if len and len != 'null-terminated':
- # For string arrays, 'len' can look like 'count,null-terminated',
- # indicating that we have a null terminated array of strings. We
- # strip the null-terminated from the 'len' field and only return
- # the parameter specifying the string count
+ # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
+ # strings. We strip the null-terminated from the 'len' field and only return the parameter specifying the string count
if 'null-terminated' in len:
result = len.split(',')[0]
else:
@@ -664,13 +719,6 @@
elif 'latexmath' in name:
lenName, decoratedName = self.parseLateXMath(name)
lenParam = self.getParamByName(params, lenName)
- # TODO: Zero-check the result produced by the equation?
- # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
- #param = self.getParamByName(params, lenName)
- #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
- # isoptional=param.isoptional, type=param.type, len=param.len,
- # isstaticarray=param.isstaticarray, extstructs=param.extstructs,
- # noautovalidity=True, condition=None, cdecl=param.cdecl)
else:
lenParam = self.getParamByName(params, name)
return lenParam
@@ -679,21 +727,8 @@
def getCmdDef(self, cmd):
# Strip the trailing ';' and split into individual lines
lines = cmd.cdecl[:-1].split('\n')
- # Replace Vulkan prototype
- lines[0] = 'static bool parameter_validation_' + cmd.name + '('
- # Replace the first argument with debug_report_data, when the first argument is a handle (not vkCreateInstance)
- if cmd.name == 'vkCreateInstance':
- lines.insert(1, ' instance_layer_data*'.ljust(self.genOpts.alignFuncParam) + 'layer_data,')
- else:
- if cmd.params[0].type in ["VkInstance", "VkPhysicalDevice"]:
- reportData = ' instance_layer_data*'.ljust(self.genOpts.alignFuncParam) + 'layer_data,'
- else:
- reportData = ' layer_data*'.ljust(self.genOpts.alignFuncParam) + 'layer_data,'
- if len(lines) < 3: # Terminate correctly if single parameter
- lines[1] = reportData[:-1] + ')'
- else:
- lines[1] = reportData
- return '\n'.join(lines)
+ cmd_hdr = '\n'.join(lines)
+ return cmd_hdr
#
# Generate the code to check for a NULL dereference before calling the
# validation function
@@ -753,16 +788,16 @@
# This is an array with a pointer to a count value
if lenValue.ispointer:
# When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
- checkExpr.append('skipCall |= validate_struct_type_array(layer_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {});\n'.format(
+ checkExpr.append('skip |= validate_struct_type_array(local_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {});\n'.format(
funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
# This is an array with an integer count value
else:
- checkExpr.append('skipCall |= validate_struct_type_array(layer_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
+ checkExpr.append('skip |= validate_struct_type_array(local_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
funcPrintName, lenValueRequired, valueRequired, vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
# This is an individual struct
else:
vuid = self.GetVuid("VUID-%s-sType-sType" % value.type)
- checkExpr.append('skipCall |= validate_struct_type(layer_data->report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {});\n'.format(
+ checkExpr.append('skip |= validate_struct_type(local_data->report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {});\n'.format(
funcPrintName, valuePrintName, prefix, valueRequired, vuid, vn=value.name, sv=stype.value, vt=value.type, **postProcSpec))
return checkExpr
#
@@ -775,7 +810,7 @@
raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
else:
# This is an array with an integer count value
- checkExpr.append('skipCall |= validate_handle_array(layer_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
+ checkExpr.append('skip |= validate_handle_array(local_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
else:
# This is assumed to be an output handle pointer
@@ -790,7 +825,7 @@
raise('Unsupported parameter validation case: array of reserved VkFlags')
else:
allFlags = 'All' + flagBitsName
- checkExpr.append('skipCall |= validate_flags_array(layer_data->report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
+ checkExpr.append('skip |= validate_flags_array(local_data->report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
return checkExpr
#
# Generate pNext check string
@@ -806,7 +841,7 @@
extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
extStructNames = '"' + ', '.join(value.extstructs) + '"'
checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.getStructType(s) for s in value.extstructs])))
- checkExpr.append('skipCall |= validate_struct_pnext(layer_data->report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion, {});\n'.format(
+ checkExpr.append('skip |= validate_struct_pnext(local_data->report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion, {});\n'.format(
funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
return checkExpr
#
@@ -822,18 +857,18 @@
# If count and array parameters are optional, there will be no validation
if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
# When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
- checkExpr.append('skipCall |= validate_array(layer_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {}, {});\n'.format(
+ checkExpr.append('skip |= validate_array(local_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {}, {});\n'.format(
funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
# This is an array with an integer count value
else:
# If count and array parameters are optional, there will be no validation
if valueRequired == 'true' or lenValueRequired == 'true':
if value.type != 'char':
- checkExpr.append('skipCall |= validate_array(layer_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {});\n'.format(
+ checkExpr.append('skip |= validate_array(local_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {});\n'.format(
funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
else:
# Arrays of strings receive special processing
- checkExpr.append('skipCall |= validate_string_array(layer_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {});\n'.format(
+ checkExpr.append('skip |= validate_string_array(local_data->report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {});\n'.format(
funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
if checkExpr:
if lenValue and ('->' in lenValue.name):
@@ -852,9 +887,9 @@
vuid = allocator_dict.get(value.name)
if vuid is not None:
ptr_required_vuid = 'VALIDATION_ERROR_%s' % vuid
- checkExpr.append('skipCall |= validate_required_pointer(layer_data->report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
+ checkExpr.append('skip |= validate_required_pointer(local_data->report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
else:
- checkExpr.append('skipCall |= validate_required_pointer(layer_data->report_data, "{}", {ppp}"{}"{pps}, {}{}, {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
+ checkExpr.append('skip |= validate_required_pointer(local_data->report_data, "{}", {ppp}"{}"{pps}, {}{}, {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
return checkExpr
#
# Process struct member validation code, performing name suibstitution if required
@@ -937,10 +972,8 @@
else:
memberNamePrefix = '{}{}->'.format(prefix, value.name)
memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
- #
# Expand the struct validation lines
expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
- #
if lenValue:
# Close if and for scopes
indent = self.decIndent(indent)
@@ -968,13 +1001,11 @@
# Check for NULL pointers, ignore the inout count parameters that
# 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
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
- #
# Generate required/optional parameter strings for the pointer and count values
if value.isoptional:
req = 'false'
@@ -1003,7 +1034,6 @@
# 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))
else:
- #
# If this is a pointer to a struct with an sType field, verify the type
if value.type in self.structTypes:
usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
@@ -1013,23 +1043,21 @@
elif value.type in self.flags and value.isconst:
usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
elif value.isbool and value.isconst:
- usedLines.append('skipCall |= validate_bool32_array(layer_data->report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
+ usedLines.append('skip |= validate_bool32_array(local_data->report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
elif value.israngedenum and value.isconst:
enum_value_list = 'All%sEnums' % value.type
- usedLines.append('skipCall |= validate_ranged_enum_array(layer_data->report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enum_value_list, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
+ usedLines.append('skip |= validate_ranged_enum_array(local_data->report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enum_value_list, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
elif value.name == 'pNext':
# We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
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))
# 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
@@ -1041,16 +1069,16 @@
if value.type in self.structTypes:
stype = self.structTypes[value.type]
vuid = self.GetVuid("VUID-%s-sType-sType" % value.type)
- usedLines.append('skipCall |= validate_struct_type(layer_data->report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, {});\n'.format(
+ usedLines.append('skip |= validate_struct_type(local_data->report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, {});\n'.format(
funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype.value, vt=value.type, **postProcSpec))
elif value.type in self.handleTypes:
if not self.isHandleOptional(value, None):
- usedLines.append('skipCall |= validate_required_handle(layer_data->report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
+ usedLines.append('skip |= validate_required_handle(local_data->report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
elif value.type in self.flags:
flagBitsName = value.type.replace('Flags', 'FlagBits')
if not flagBitsName in self.flagBits:
vuid = self.GetVuid("VUID-%s-%s-zerobitmask" % (vuid_name_tag, value.name))
- usedLines.append('skipCall |= validate_reserved_flags(layer_data->report_data, "{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
+ usedLines.append('skip |= validate_reserved_flags(local_data->report_data, "{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
else:
if value.isoptional:
flagsRequired = 'false'
@@ -1059,25 +1087,23 @@
flagsRequired = 'true'
vuid = self.GetVuid("VUID-%s-%s-requiredbitmask" % (vuid_name_tag, value.name))
allFlagsName = 'All' + flagBitsName
- usedLines.append('skipCall |= validate_flags(layer_data->report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, false, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
+ usedLines.append('skip |= validate_flags(local_data->report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, false, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
elif value.type in self.flagBits:
flagsRequired = 'false' if value.isoptional else 'true'
allFlagsName = 'All' + value.type
vuid = self.GetVuid("VUID-%s-%s-parameter" % (vuid_name_tag, value.name))
- usedLines.append('skipCall |= validate_flags(layer_data->report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, true, {});\n'.format(funcName, valueDisplayName, value.type, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
+ usedLines.append('skip |= validate_flags(local_data->report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, true, {});\n'.format(funcName, valueDisplayName, value.type, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
elif value.isbool:
- usedLines.append('skipCall |= validate_bool32(layer_data->report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
+ usedLines.append('skip |= validate_bool32(local_data->report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
elif value.israngedenum:
vuid = self.GetVuid("VUID-%s-%s-parameter" % (vuid_name_tag, value.name))
enum_value_list = 'All%sEnums' % value.type
- usedLines.append('skipCall |= validate_ranged_enum(layer_data->report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}{}, {});\n'.format(funcName, valueDisplayName, value.type, enum_value_list, valuePrefix, value.name, vuid, **postProcSpec))
- #
+ usedLines.append('skip |= validate_ranged_enum(local_data->report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}{}, {});\n'.format(funcName, valueDisplayName, value.type, enum_value_list, valuePrefix, value.name, vuid, **postProcSpec))
# If this is a struct, see if it contains members that need to be checked
if value.type in self.validatedStructs:
memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
- #
# Append the parameter check to the function body for the current command
if usedLines:
# Apply special conditional checks
@@ -1088,6 +1114,8 @@
# If no expression was generated for this value, it is unreferenced by the validation function, unless
# it is an array count, which is indirectly referenced for array valiadation.
unused.append(value.name)
+ if not lines:
+ lines.append('// No xml-driven validation\n')
return lines, unused
#
# Generate the struct member check code from the captured data
@@ -1104,6 +1132,9 @@
def processCmdData(self):
indent = self.incIndent(None)
for command in self.commands:
+ just_validate = False
+ if command.name in self.validate_only:
+ just_validate = True
# Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
startIndex = 0 if command.name == 'vkCreateInstance' else 1
lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
@@ -1121,21 +1152,37 @@
ext_enable_name = ext_name_define.lower()
ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
break
- ext_test = 'if (!layer_data->extensions.%s) skipCall |= OutputExtensionError(layer_data, "%s", %s);\n' % (ext_enable_name, command.name, ext_name_define)
+ ext_test = 'if (!local_data->extensions.%s) skip |= OutputExtensionError(local_data, "%s", %s);\n' % (ext_enable_name, command.name, ext_name_define)
lines.insert(0, ext_test)
if lines:
cmdDef = self.getCmdDef(command) + '\n'
+ # For a validation-only routine, change the function declaration
+ if just_validate:
+ jv_def = '// Generated function handles validation only -- API definition is in non-generated source\n'
+ jv_def += 'extern %s\n\n' % command.cdecl
+ cmdDef = 'bool parameter_validation_' + cmdDef.split('VKAPI_CALL ',1)[1]
+ if command.name == 'vkCreateInstance':
+ cmdDef = cmdDef.replace('(\n', '(\n VkInstance instance,\n')
+ cmdDef = jv_def + cmdDef
cmdDef += '{\n'
- # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
- # processed by parameter_validation (except for vkCreateInstance, which does not have a
- # handle as its first parameter)
- if unused:
- for name in unused:
- cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
- if len(unused) > 0:
- cmdDef += '\n'
- cmdDef += indent + 'bool skipCall = false;\n'
+ # Add list of commands to skip -- just generate the routine signature and put the manual source in PV_utils.cpp
+ if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'VkCreateInstance':
+ map_name = 'instance_layer_data_map'
+ map_type = 'instance_layer_data'
+ else:
+ map_name = 'layer_data_map'
+ map_type = 'layer_data'
+ instance_param = command.params[0].name
+ if command.name == 'vkCreateInstance':
+ instance_param = 'instance'
+ layer_data = ' %s *local_data = GetLayerDataPtr(get_dispatch_key(%s), %s);\n' % (map_type, instance_param, map_name)
+ cmdDef += layer_data
+ cmdDef += '%sbool skip = false;\n' % indent
+ if not just_validate:
+ if command.result != '':
+ cmdDef += indent + '%s result = VK_ERROR_VALIDATION_FAILED_EXT;\n' % command.result
+ cmdDef += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % indent
for line in lines:
cmdDef += '\n'
if type(line) is list:
@@ -1144,6 +1191,31 @@
else:
cmdDef += indent + line
cmdDef += '\n'
- cmdDef += indent + 'return skipCall;\n'
+ if not just_validate:
+ # Generate parameter list for manual fcn and down-chain calls
+ params_text = ''
+ for param in command.params:
+ params_text += '%s, ' % param.name
+ params_text = params_text[:-2]
+ # Generate call to manual function if its function pointer is non-null
+ cmdDef += '%sif (manual_%s != nullptr) {\n' % (indent, command.name)
+ cmdDef += ' %sskip |= manual_%s(%s);\n' % (indent, command.name, params_text)
+ cmdDef += '%s}\n\n' % indent
+ # Release the validation lock
+ cmdDef += '%slock.unlock();\n' % indent
+ # Generate skip check and down-chain call
+ cmdDef += '%sif (!skip) {\n' % indent
+ down_chain_call = ' %s' % indent
+ if command.result != '':
+ down_chain_call += ' result = '
+ # Generate down-chain API call
+ api_call = '%s(%s);' % (command.name, params_text)
+ down_chain_call += 'local_data->dispatch_table.%s\n' % api_call[2:]
+ cmdDef += down_chain_call
+ cmdDef += '%s}\n' % indent
+ if command.result != '':
+ cmdDef += '%sreturn result;\n' % indent
+ else:
+ cmdDef += '%sreturn skip;\n' % indent
cmdDef += '}\n'
self.validation.append(cmdDef)
diff --git a/scripts/vk_validation_stats.py b/scripts/vk_validation_stats.py
index 33493dd..36346b3 100755
--- a/scripts/vk_validation_stats.py
+++ b/scripts/vk_validation_stats.py
@@ -50,13 +50,13 @@
'release',
]
generated_layer_source_files = [
-'parameter_validation.h',
+'parameter_validation.cpp',
'object_tracker.cpp',
]
layer_source_files = [
'../layers/core_validation.cpp',
'../layers/descriptor_sets.cpp',
-'../layers/parameter_validation.cpp',
+'../layers/parameter_validation_utils.cpp',
'../layers/object_tracker_utils.cpp',
'../layers/shader_validation.cpp',
'../layers/buffer_validation.cpp',