aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDustin Graves <dustin@lunarg.com>2016-04-26 15:37:10 -0600
committerDustin Graves <dustin@lunarg.com>2016-04-28 17:24:42 -0600
commitd33465e2c8a578a4e1bbd15e76d5e0f674aa7364 (patch)
treeb06eedc0c87de76650c68c31607bb3cea07aebb8
parent298e942df7ae24098c8338a98163556566afed1e (diff)
downloadusermoji-d33465e2c8a578a4e1bbd15e76d5e0f674aa7364.tar.xz
layers: Add VkFlags parameter validation
Add parameter validation for VkFlags derived types to the parameter_validation layer's code generation scripts. The following validation checks are performed: - If a VkFlags parameter is not marked as optional in the XML, a message is generated when the parameter is 0. - If a VkFlags parameter is not 0, a message is generated if it combines bits that are not defined by its associated flag bits enumeration. - If a VkFlags parameter does not have an associated flag bits enumeration it is treated as a reserved value that must be 0. Change-Id: I6daed360cde46e2a27c84deda1e0798621f92d50
-rw-r--r--generator.py55
-rw-r--r--layers/parameter_validation_utils.h106
2 files changed, 155 insertions, 6 deletions
diff --git a/generator.py b/generator.py
index 249313d6..7b4516f9 100644
--- a/generator.py
+++ b/generator.py
@@ -2850,6 +2850,8 @@ class ParamCheckerOutputGenerator(OutputGenerator):
self.structMembers = [] # List of StructMemberData records for all Vulkan structs
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.flags = dict() # Map of flags typenames to a Boolean value indicating that validation code is generated for a value of this type
+ self.flagBits = dict() # Map of flag bits typename to list of values
# Named tuples to store struct and command data
self.StructType = namedtuple('StructType', ['name', 'value'])
self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
@@ -2922,6 +2924,8 @@ class ParamCheckerOutputGenerator(OutputGenerator):
self.structMembers = []
self.validatedStructs = dict()
self.enumRanges = dict()
+ self.flags = dict()
+ self.flagBits = dict()
def endFeature(self):
# C-specific
# Actually write the interface to the output file.
@@ -2936,6 +2940,18 @@ class ParamCheckerOutputGenerator(OutputGenerator):
self.processStructMemberData()
# Generate the command parameter checking code from the captured data
self.processCmdData()
+ # Write the declarations for the VkFlags values combining all flag bits
+ for flag in sorted(self.flags):
+ if self.flags[flag]:
+ flagBits = flag.replace('Flags', 'FlagBits')
+ bits = self.flagBits[flagBits]
+ decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
+ for bit in bits[1:]:
+ decl += '|' + bit
+ decl += ';'
+ write(decl, file=self.outFile)
+ self.newline()
+ # Write the parameter validation code to the file
if (self.sections['command']):
if (self.genOpts.protectProto):
write(self.genOpts.protectProto,
@@ -2965,6 +2981,8 @@ class ParamCheckerOutputGenerator(OutputGenerator):
self.genStruct(typeinfo, name)
elif (category == 'handle'):
self.handleTypes.add(name)
+ elif (category == 'bitmask'):
+ self.flags[name] = False
#
# Struct parameter check generation.
# This is a special case of the <type> tag where the contents are
@@ -3057,6 +3075,12 @@ class ParamCheckerOutputGenerator(OutputGenerator):
if groupName == 'VkStructureType':
for elem in groupElem.findall('enum'):
self.stypes.append(elem.get('name'))
+ elif 'FlagBits' in groupName:
+ bits = []
+ for elem in groupElem.findall('enum'):
+ bits.append(elem.get('name'))
+ if bits:
+ self.flagBits[groupName] = bits
else:
# Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
@@ -3311,22 +3335,34 @@ class ParamCheckerOutputGenerator(OutputGenerator):
funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value))
return checkExpr
#
- # Generate the sType check string
+ # Generate the handle check string
def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName):
checkExpr = []
if lenValue:
- # This is assumed to be an output array with a pointer to a count value
if lenValue.ispointer:
+ # This is assumed to be an output array with a pointer to a count value
raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
- # This is an array with an integer count value
else:
+ # This is an array with an integer count value
checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
- # This is assumed to be an output handle pointer
else:
+ # This is assumed to be an output handle pointer
raise('Unsupported parameter validation case: Output handles are not NULL checked')
return checkExpr
#
+ # Generate check string for an array of VkFlags values
+ def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName):
+ checkExpr = []
+ flagBitsName = value.type.replace('Flags', 'FlagBits')
+ if not flagBitsName in self.flagBits:
+ raise('Unsupported parameter validation case: array of reserved VkFlags')
+ else:
+ allFlags = 'All' + flagBitsName
+ checkExpr.append('skipCall |= validate_flags_array(report_data, "{}", "{}", "{}", "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix))
+ self.flags[value.type] = True
+ return checkExpr
+ #
# Generate pNext check string
def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName):
checkExpr = []
@@ -3472,6 +3508,8 @@ class ParamCheckerOutputGenerator(OutputGenerator):
# If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName)
+ elif value.type in self.flags and value.isconst:
+ usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName)
elif value.isbool and value.isconst:
usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", "{}", "{}", {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix))
elif value.israngedenum and value.isconst:
@@ -3500,6 +3538,15 @@ class ParamCheckerOutputGenerator(OutputGenerator):
elif value.type in self.handleTypes:
if not self.isHandleOptional(value, None):
usedLines.append('skipCall |= validate_required_handle(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
+ elif value.type in self.flags:
+ flagBitsName = value.type.replace('Flags', 'FlagBits')
+ if not flagBitsName in self.flagBits:
+ usedLines.append('skipCall |= validate_reserved(report_data, "{}", "{}", {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix))
+ else:
+ flagsRequired = 'false' if value.isoptional else 'true'
+ allFlagsName = 'All' + flagBitsName
+ usedLines.append('skipCall |= validate_flags(report_data, "{}", "{}", "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix))
+ self.flags[value.type] = True
elif value.isbool:
usedLines.append('skipCall |= validate_bool32(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
elif value.israngedenum:
diff --git a/layers/parameter_validation_utils.h b/layers/parameter_validation_utils.h
index fb37add3..5a9ff503 100644
--- a/layers/parameter_validation_utils.h
+++ b/layers/parameter_validation_utils.h
@@ -24,6 +24,7 @@
#include <algorithm>
#include <cstdlib>
#include <string>
+#include <type_traits>
#include "vulkan/vulkan.h"
#include "vk_enum_string_helper.h"
@@ -63,6 +64,31 @@ template <> bool is_extension_added_token(VkSamplerAddressMode value) {
}
/**
+* Verify that a reserved value is zero.
+*
+* Verify that the specified value is zero. Primarily intended to check VkFlags values that are reserved for
+* future use.
+*
+* @param report_data debug_report_data object for routing validation messages.
+* @param api_name Name of API call being validated.
+* @param parameter_name Name of parameter being validated.
+* @param value Value to validate.
+* @return Boolean value indicating that the call should be skipped.
+*/
+template <typename T>
+bool validate_reserved(debug_report_data *report_data, const char *api_name, const char *parameter_name, T value) {
+ static_assert(std::is_integral<T>::value, "validate_reserved is only designed to process integer types");
+ bool skip_call = false;
+
+ if (value != 0) {
+ skip_call |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+ ParameterValidationName, "%s: parameter %s must be 0", api_name, parameter_name);
+ }
+
+ return skip_call;
+}
+
+/**
* Validate a required pointer.
*
* Verify that a required pointer is not NULL.
@@ -475,7 +501,7 @@ static bool validate_bool32(debug_report_data *report_data, const char *apiName,
* @param enumName Name of the enumeration being validated.
* @param begin The begin range value for the enumeration.
* @param end The end range value for the enumeration.
-* @param value Boolean value to validate.
+* @param value Enumeration value to validate.
* @return Boolean value indicating that the call should be skipped.
*/
template <typename T>
@@ -541,9 +567,85 @@ static bool validate_ranged_enum_array(debug_report_data *report_data, const cha
}
/**
+* Validate a Vulkan bitmask value.
+*
+* Generate a warning if a value with a VkFlags derived type does not contain valid flag bits
+* for that type.
+*
+* @param report_data debug_report_data object for routing validation messages.
+* @param api_name Name of API call being validated.
+* @param parameter_name Name of parameter being validated.
+* @param flag_bits_name Name of the VkFlags type being validated.
+* @param all_flags A bit mask combining all valid flag bits for the VkFlags type being validated.
+* @param value VkFlags value to validate.
+* @param flags_required The 'value' parameter may not be 0 when true.
+* @return Boolean value indicating that the call should be skipped.
+*/
+template <typename T>
+bool validate_flags(debug_report_data *report_data, const char *api_name, const char *parameter_name, const char *flag_bits_name,
+ T all_flags, T value, bool flags_required) {
+ bool skip_call = false;
+
+ if (value == 0) {
+ if (flags_required) {
+ skip_call |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+ ParameterValidationName, "%s: value of %s must not be 0", api_name, parameter_name);
+ }
+ } else if ((value & (~all_flags)) != 0) {
+ skip_call |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+ ParameterValidationName, "%s: value of %s contains flag bits that are not recognized members of %s",
+ api_name, parameter_name, flag_bits_name);
+ }
+
+ return skip_call;
+}
+
+/**
+* Validate an array of Vulkan bitmask values.
+*
+* Generate a warning if a value with a VkFlags derived type does not contain valid flag bits
+* for that type.
+*
+* @param report_data debug_report_data object for routing validation messages.
+* @param api_name Name of API call being validated.
+* @param count_name Name of parameter being validated.
+* @param array_name Name of parameter being validated.
+* @param flag_bits_name Name of the VkFlags type being validated.
+* @param all_flags A bitmask combining all valid flag bits for the VkFlags type being validated.
+* @param count Number of VkFlags values in the array.
+* @param array Array of VkFlags value to validate.
+* @param count_required The 'count' parameter may not be 0 when true.
+* @param array_required The 'array' parameter may not be NULL when true.
+* @return Boolean value indicating that the call should be skipped.
+*/
+template <typename T>
+bool validate_flags_array(debug_report_data *report_data, const char *api_name, const char *count_name, const char *array_name,
+ const char *flag_bits_name, T all_flags, uint32_t count, const T *array, bool count_required,
+ bool array_required) {
+ bool skip_call = false;
+
+ if ((count == 0) || (array == NULL)) {
+ skip_call |= validate_array(report_data, api_name, count_name, array_name, count, array, count_required, array_required);
+ } else {
+ // Verify that all VkFlags values in the array
+ for (uint32_t i = 0; i < count; ++i) {
+ if ((array[i] & (~all_flags)) != 0) {
+ skip_call |=
+ log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+ ParameterValidationName, "%s: value of %s[%d] contains flag bits that are not recognized members of %s",
+ api_name, array_name, i, flag_bits_name);
+ }
+ }
+ }
+
+ return skip_call;
+}
+
+/**
* Get VkResult code description.
*
-* Returns a string describing the specified VkResult code. The description is based on the language in the Vulkan API specification.
+* Returns a string describing the specified VkResult code. The description is based on the language in the Vulkan API
+* specification.
*
* @param value VkResult code to process.
* @return String describing the specified VkResult code.