blob: eb6f97d2ec608110f88d870461cbeb1d0eef8239 [file] [log] [blame]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001#!/usr/bin/python3 -i
2#
Shahbaz Youssefi23aee922019-01-11 14:04:49 -05003# Copyright (c) 2015-2019 The Khronos Group Inc.
4# Copyright (c) 2015-2019 Valve Corporation
5# Copyright (c) 2015-2019 LunarG, Inc.
6# Copyright (c) 2015-2019 Google Inc.
Mark Lobodzinski85672672016-10-13 08:36:42 -06007#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19#
20# Author: Dustin Graves <dustin@lunarg.com>
Mark Lobodzinski26112592017-05-30 12:02:17 -060021# Author: Mark Lobodzinski <mark@lunarg.com>
Dave Houlton413a6782018-05-22 13:01:54 -060022# Author: Dave Houlton <daveh@lunarg.com>
Mark Lobodzinski85672672016-10-13 08:36:42 -060023
Dave Houlton413a6782018-05-22 13:01:54 -060024import os,re,sys,string,json
Mark Lobodzinski85672672016-10-13 08:36:42 -060025import xml.etree.ElementTree as etree
26from generator import *
27from collections import namedtuple
Mark Lobodzinski62f71562017-10-24 13:41:18 -060028from common_codegen import *
Mark Lobodzinski06954ea2017-06-21 12:21:45 -060029
Jamie Madill8d4cda22017-11-08 13:40:09 -050030# This is a workaround to use a Python 2.7 and 3.x compatible syntax.
31from io import open
Mark Lobodzinski85672672016-10-13 08:36:42 -060032
Mark Lobodzinskid4950072017-08-01 13:02:20 -060033# ParameterValidationGeneratorOptions - subclass of GeneratorOptions.
Mark Lobodzinski85672672016-10-13 08:36:42 -060034#
Mark Lobodzinskid4950072017-08-01 13:02:20 -060035# Adds options used by ParameterValidationOutputGenerator object during Parameter validation layer generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -060036#
37# Additional members
38# prefixText - list of strings to prefix generated header with
39# (usually a copyright statement + calling convention macros).
40# protectFile - True if multiple inclusion protection should be
41# generated (based on the filename) around the entire header.
42# protectFeature - True if #ifndef..#endif protection should be
43# generated around a feature interface in the header file.
44# genFuncPointers - True if function pointer typedefs should be
45# generated
46# protectProto - If conditional protection should be generated
47# around prototype declarations, set to either '#ifdef'
48# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
49# to require opt-out (#ifndef protectProtoStr). Otherwise
50# set to None.
51# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
52# declarations, if protectProto is set
53# apicall - string to use for the function declaration prefix,
54# such as APICALL on Windows.
55# apientry - string to use for the calling convention macro,
56# in typedefs, such as APIENTRY.
57# apientryp - string to use for the calling convention macro
58# in function pointer typedefs, such as APIENTRYP.
59# indentFuncProto - True if prototype declarations should put each
60# parameter on a separate line
61# indentFuncPointer - True if typedefed function pointers should put each
62# parameter on a separate line
63# alignFuncParam - if nonzero and parameters are being put on a
64# separate line, align parameter names at the specified column
Mark Lobodzinskid4950072017-08-01 13:02:20 -060065class ParameterValidationGeneratorOptions(GeneratorOptions):
Mark Lobodzinski85672672016-10-13 08:36:42 -060066 def __init__(self,
Mike Schuchardt21638df2019-03-16 10:52:02 -070067 conventions = None,
Mark Lobodzinski85672672016-10-13 08:36:42 -060068 filename = None,
69 directory = '.',
70 apiname = None,
71 profile = None,
72 versions = '.*',
73 emitversions = '.*',
74 defaultExtensions = None,
75 addExtensions = None,
76 removeExtensions = None,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060077 emitExtensions = None,
Mark Lobodzinski85672672016-10-13 08:36:42 -060078 sortProcedure = regSortFeatures,
79 prefixText = "",
Mark Lobodzinski85672672016-10-13 08:36:42 -060080 apicall = '',
81 apientry = '',
82 apientryp = '',
83 indentFuncProto = True,
84 indentFuncPointer = False,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060085 alignFuncParam = 0,
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060086 expandEnumerants = True,
87 valid_usage_path = ''):
Mike Schuchardt21638df2019-03-16 10:52:02 -070088 GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
Mark Lobodzinski85672672016-10-13 08:36:42 -060089 versions, emitversions, defaultExtensions,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060090 addExtensions, removeExtensions, emitExtensions, sortProcedure)
Mark Lobodzinski85672672016-10-13 08:36:42 -060091 self.prefixText = prefixText
Mark Lobodzinski85672672016-10-13 08:36:42 -060092 self.apicall = apicall
93 self.apientry = apientry
94 self.apientryp = apientryp
95 self.indentFuncProto = indentFuncProto
96 self.indentFuncPointer = indentFuncPointer
97 self.alignFuncParam = alignFuncParam
Mark Lobodzinski62f71562017-10-24 13:41:18 -060098 self.expandEnumerants = expandEnumerants
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060099 self.valid_usage_path = valid_usage_path
Mark Lobodzinski85672672016-10-13 08:36:42 -0600100
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600101# ParameterValidationOutputGenerator - subclass of OutputGenerator.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600102# Generates param checker layer code.
103#
104# ---- methods ----
105# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
106# OutputGenerator. Defines additional internal state.
107# ---- methods overriding base class ----
108# beginFile(genOpts)
109# endFile()
110# beginFeature(interface, emit)
111# endFeature()
112# genType(typeinfo,name)
113# genStruct(typeinfo,name)
114# genGroup(groupinfo,name)
115# genEnum(enuminfo, name)
116# genCmd(cmdinfo)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600117class ParameterValidationOutputGenerator(OutputGenerator):
118 """Generate Parameter Validation code based on XML element attributes"""
Mark Lobodzinski85672672016-10-13 08:36:42 -0600119 # This is an ordered list of sections in the header file.
120 ALL_SECTIONS = ['command']
121 def __init__(self,
122 errFile = sys.stderr,
123 warnFile = sys.stderr,
124 diagFile = sys.stdout):
125 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
126 self.INDENT_SPACES = 4
Mark Lobodzinskib6b8bbd2017-02-08 14:37:15 -0700127 self.declarations = []
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700128
129 inline_custom_source_preamble = """
130"""
131
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600132 # These functions have additional, custom-written checks in the utils cpp file. CodeGen will automatically add a call
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700133 # to those functions of the form 'bool manual_PreCallValidateAPIName', where the 'vk' is dropped.
134 # see 'manual_PreCallValidateCreateGraphicsPipelines' as an example.
135 self.functions_with_manual_checks = [
136 'vkCreateInstance',
137 'vkCreateDevice',
138 'vkCreateQueryPool'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700139 'vkCreateRenderPass',
140 'vkCreateRenderPass2KHR',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700141 'vkCreateBuffer',
142 'vkCreateImage',
143 'vkCreateImageView',
144 'vkCreateGraphicsPipelines',
145 'vkCreateComputePipelines',
146 'vkCreateSampler',
147 'vkCreateDescriptorSetLayout',
148 'vkFreeDescriptorSets',
149 'vkUpdateDescriptorSets',
150 'vkCreateRenderPass',
151 'vkCreateRenderPass2KHR',
152 'vkBeginCommandBuffer',
153 'vkCmdSetViewport',
154 'vkCmdSetScissor',
155 'vkCmdSetLineWidth',
156 'vkCmdDraw',
157 'vkCmdDrawIndirect',
158 'vkCmdDrawIndexedIndirect',
159 'vkCmdCopyImage',
160 'vkCmdBlitImage',
161 'vkCmdCopyBufferToImage',
162 'vkCmdCopyImageToBuffer',
163 'vkCmdUpdateBuffer',
164 'vkCmdFillBuffer',
165 'vkCreateSwapchainKHR',
166 'vkQueuePresentKHR',
167 'vkCreateDescriptorPool',
168 'vkCmdDispatch',
169 'vkCmdDispatchIndirect',
170 'vkCmdDispatchBaseKHR',
171 'vkCmdSetExclusiveScissorNV',
172 'vkCmdSetViewportShadingRatePaletteNV',
173 'vkCmdSetCoarseSampleOrderNV',
174 'vkCmdDrawMeshTasksNV',
175 'vkCmdDrawMeshTasksIndirectNV',
176 'vkCmdDrawMeshTasksIndirectCountNV',
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600177 'vkAllocateMemory',
Ricardo Garciaa4935972019-02-21 17:43:18 +0100178 'vkCreateAccelerationStructureNV',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700179 ]
180
Mark Lobodzinski85672672016-10-13 08:36:42 -0600181 # Commands to ignore
182 self.blacklist = [
183 'vkGetInstanceProcAddr',
184 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700185 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600186 'vkEnumerateInstanceLayerProperties',
187 'vkEnumerateInstanceExtensionProperties',
188 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600189 'vkEnumerateDeviceExtensionProperties',
Mike Schuchardt21638df2019-03-16 10:52:02 -0700190 'vkGetDeviceGroupSurfacePresentModes2EXT'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600191 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700192
Dustin Gravesce68f082017-03-30 15:42:16 -0600193 # Structure fields to ignore
194 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600195 # Validation conditions for some special case struct members that are conditionally validated
196 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
197 # Header version
198 self.headerVersion = None
199 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600200 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600201 self.stypes = [] # Values from the VkStructureType enumeration
202 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
203 self.handleTypes = set() # Set of handle type names
204 self.commands = [] # List of CommandData records for all Vulkan commands
205 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
206 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
207 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600208 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600209 self.flags = set() # Map of flags typenames
210 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300211 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600212 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600213 self.extension_type = '' # Type of active feature (extension), device or instance
214 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600215 self.structextends_list = [] # List of extensions which extend another struct
216 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600217 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600218 self.vuid_dict = dict() # VUID dictionary (from JSON)
219 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700220 self.header_file = False # Header file generation flag
221 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600222 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600223 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600224 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600225 'isconst', 'isoptional', 'iscount', 'noautovalidity',
226 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600227 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600228 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600229
Mark Lobodzinski85672672016-10-13 08:36:42 -0600230 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600231 # Generate Copyright comment block for file
232 def GenerateCopyright(self):
233 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
234 copyright += ' * See parameter_validation_generator.py for modifications\n'
235 copyright += ' *\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600236 copyright += ' * Copyright (c) 2015-2018 The Khronos Group Inc.\n'
237 copyright += ' * Copyright (c) 2015-2018 LunarG, Inc.\n'
238 copyright += ' * Copyright (C) 2015-2018 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600239 copyright += ' *\n'
240 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
241 copyright += ' * you may not use this file except in compliance with the License.\n'
242 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
243 copyright += ' * You may obtain a copy of the License at\n'
244 copyright += ' *\n'
245 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
246 copyright += ' *\n'
247 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
248 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
249 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
250 copyright += ' * See the License for the specific language governing permissions and\n'
251 copyright += ' * limitations under the License.\n'
252 copyright += ' *\n'
253 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600254 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600255 copyright += ' */\n\n'
256 return copyright
257 #
258 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600259 def incIndent(self, indent):
260 inc = ' ' * self.INDENT_SPACES
261 if indent:
262 return indent + inc
263 return inc
264 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600265 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600266 def decIndent(self, indent):
267 if indent and (len(indent) > self.INDENT_SPACES):
268 return indent[:-self.INDENT_SPACES]
269 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600270 #
Dave Houlton413a6782018-05-22 13:01:54 -0600271 # Walk the JSON-derived dict and find all "vuid" key values
272 def ExtractVUIDs(self, d):
273 if hasattr(d, 'items'):
274 for k, v in d.items():
275 if k == "vuid":
276 yield v
277 elif isinstance(v, dict):
278 for s in self.ExtractVUIDs(v):
279 yield s
280 elif isinstance (v, list):
281 for l in v:
282 for s in self.ExtractVUIDs(l):
283 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600284 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600285 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600286 def beginFile(self, genOpts):
287 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700288 self.header_file = (genOpts.filename == 'parameter_validation.h')
289 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
290
291 if not self.header_file and not self.source_file:
292 print("Error: Output Filenames have changed, update generator source.\n")
293 sys.exit(1)
294
295 if self.source_file or self.header_file:
296 # Output Copyright text
297 s = self.GenerateCopyright()
298 write(s, file=self.outFile)
299
300 if self.header_file:
301 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600302
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700303 # Build map of structure type names to VkStructureType enum values
304 # Find all types of category "struct"
305 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
306 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
307 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500308 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700309 # Store VkStructureType value for this type
310 self.structTypes[struct.get('name')] = stype.get('values')
311
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600312 self.valid_usage_path = genOpts.valid_usage_path
313 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
314 if os.path.isfile(vu_json_filename):
315 json_file = open(vu_json_filename, 'r')
316 self.vuid_dict = json.load(json_file)
317 json_file.close()
318 if len(self.vuid_dict) == 0:
319 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
320 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600321 #
Dave Houlton413a6782018-05-22 13:01:54 -0600322 # Build a set of all vuid text strings found in validusage.json
323 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
324 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600325 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600326 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700327 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600328 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700329 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600330 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600331 #
332 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600333 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700334 if self.source_file:
335 # C-specific
336 self.newline()
337 write(self.enumValueLists, file=self.outFile)
338 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600339
Locke6b6b7382019-04-16 15:08:49 -0600340 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const VkBaseOutStructure* header) {\n'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700341 pnext_handler += ' bool skip = false;\n'
342 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600343
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700344 # Do some processing here to extract data from validatedstructs...
345 for item in self.structextends_list:
346 postProcSpec = {}
347 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
348 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
349 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600350
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700351 pnext_case = '\n'
352 protect = ''
353 # Guard struct cases with feature ifdefs, if necessary
354 if item in self.struct_feature_protect.keys():
355 protect = self.struct_feature_protect[item]
356 pnext_case += '#ifdef %s\n' % protect
357 pnext_case += ' // Validation code for %s structure members\n' % item
358 pnext_case += ' case %s: {\n' % self.structTypes[item]
359 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
360 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
361 struct_validation_source = self.ScrubStructCode(expr)
362 pnext_case += '%s' % struct_validation_source
363 pnext_case += ' } break;\n'
364 if protect is not '':
365 pnext_case += '#endif // %s\n' % protect
366 # Skip functions containing no validation
367 if struct_validation_source != '':
368 pnext_handler += pnext_case;
369 pnext_handler += ' default:\n'
370 pnext_handler += ' skip = false;\n'
371 pnext_handler += ' }\n'
372 pnext_handler += ' return skip;\n'
373 pnext_handler += '}\n'
374 write(pnext_handler, file=self.outFile)
375 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600376
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700377 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
378 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
379 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
380 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
381 ext_template += '}\n'
382 write(ext_template, file=self.outFile)
383 self.newline()
384 commands_text = '\n'.join(self.validation)
385 write(commands_text, file=self.outFile)
386 self.newline()
387 if self.header_file:
388 # Output declarations and record intercepted procedures
389 write('\n'.join(self.declarations), file=self.outFile)
390 # Finish processing in superclass
391 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600392 #
393 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600394 def beginFeature(self, interface, emit):
395 # Start processing in superclass
396 OutputGenerator.beginFeature(self, interface, emit)
397 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600398 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
399 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600400 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600401 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600402 self.commands = []
403 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300404 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600405 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600406 # Get base list of extension dependencies for all items in this extension
407 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600408 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600409 # Save Name Define to get correct enable name later
410 nameElem = interface[0][1]
411 name = nameElem.get('name')
412 self.extension_names[self.featureName] = name
413 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600414 base_required_extensions.append(self.featureName)
415 # Add any defined extension dependencies to the base dependency list for this extension
416 requires = interface.get('requires')
417 if requires is not None:
418 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600419 # Build dictionary of extension dependencies for each item in this extension
420 self.required_extensions = dict()
421 for require_element in interface.findall('require'):
422 # Copy base extension dependency list
423 required_extensions = list(base_required_extensions)
424 # Add any additional extension dependencies specified in this require block
425 additional_extensions = require_element.get('extension')
426 if additional_extensions:
427 required_extensions.extend(additional_extensions.split(','))
428 # Save full extension list for all named items
429 for element in require_element.findall('*[@name]'):
430 self.required_extensions[element.get('name')] = required_extensions
431
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600432 # And note if this is an Instance or Device extension
433 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600434 #
435 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600436 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700437 if self.header_file:
438 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600439 # C-specific
440 # Actually write the interface to the output file.
441 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600442 # If type declarations are needed by other features based on this one, it may be necessary to suppress the ExtraProtect,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600443 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600444 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100445 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600446 ifdef = '#ifdef %s\n' % self.featureExtraProtect
447 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600448 # Generate the struct member checking code from the captured data
449 self.processStructMemberData()
450 # Generate the command parameter checking code from the captured data
451 self.processCmdData()
452 # Write the declaration for the HeaderVersion
453 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700454 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600455 self.newline()
456 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300457 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600458 flagBits = flag.replace('Flags', 'FlagBits')
459 if flagBits in self.flagBits:
460 bits = self.flagBits[flagBits]
461 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
462 for bit in bits[1:]:
463 decl += '|' + bit
464 decl += ';'
465 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600466 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100467 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600468 endif = '#endif // %s\n' % self.featureExtraProtect
469 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600470 # Finish processing in superclass
471 OutputGenerator.endFeature(self)
472 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600473 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700474 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600475 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100476 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600477 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700478 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600479 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600480 # If the type is a struct type, traverse the embedded <member> tags generating a structure. Otherwise, emit the tag text.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600481 category = typeElem.get('category')
482 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700483 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600484 elif (category == 'handle'):
485 self.handleTypes.add(name)
486 elif (category == 'bitmask'):
487 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300488 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600489 elif (category == 'define'):
490 if name == 'VK_HEADER_VERSION':
491 nameElem = typeElem.find('name')
492 self.headerVersion = noneStr(nameElem.tail).strip()
493 #
494 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600495 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
496 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
497 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700498 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700499 if not self.source_file:
500 return
Dave Houlton413a6782018-05-22 13:01:54 -0600501 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700502 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600503 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
504 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600505 if self.featureExtraProtect is not None:
506 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600507 #
508 # Iterate over members once to get length parameters for arrays
509 lens = set()
510 for member in members:
511 len = self.getLen(member)
512 if len:
513 lens.add(len)
514 #
515 # Generate member info
516 membersInfo = []
517 for member in members:
518 # Get the member's type and name
519 info = self.getTypeNameTuple(member)
520 type = info[0]
521 name = info[1]
522 stypeValue = ''
523 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700524
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600525 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600526 iscount = False
527 if name in lens:
528 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600529 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
530 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600531 isstaticarray = self.paramIsStaticArray(member)
532 isoptional = False
533 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
534 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600535 # Determine if value should be ignored by code generation.
536 noautovalidity = False
537 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
538 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600539 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600540 membersInfo.append(self.CommandParam(type=type, name=name,
541 ispointer=self.paramIsPointer(member),
542 isstaticarray=isstaticarray,
543 isbool=True if type == 'VkBool32' else False,
544 israngedenum=True if type in self.enumRanges else False,
545 isconst=True if 'const' in cdecl else False,
546 isoptional=isoptional,
547 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600548 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600549 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600550 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600551 condition=conditions[name] if conditions and name in conditions else None,
552 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600553 # If this struct extends another, keep its name in list for further processing
554 if typeinfo.elem.attrib.get('structextends') is not None:
555 self.structextends_list.append(typeName)
556 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
557 # pNext members. Everything else will be overwritten by the callee.
558 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600559 self.returnedonly_structs.append(typeName)
560 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600561 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
562 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600563 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600564 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700565 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700566 if not self.source_file:
567 return
Dave Houlton413a6782018-05-22 13:01:54 -0600568 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100569 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600570 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700571 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600572 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600573 # Store the sType values
574 if groupName == 'VkStructureType':
575 for elem in groupElem.findall('enum'):
576 self.stypes.append(elem.get('name'))
577 elif 'FlagBits' in groupName:
578 bits = []
579 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600580 if elem.get('supported') != 'disabled':
581 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600582 if bits:
583 self.flagBits[groupName] = bits
584 else:
585 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
586 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
587 expandPrefix = expandName
588 expandSuffix = ''
589 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
590 if expandSuffixMatch:
591 expandSuffix = '_' + expandSuffixMatch.group()
592 # Strip off the suffix from the prefix
593 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
594 isEnum = ('FLAG_BITS' not in expandPrefix)
595 if isEnum:
596 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600597 # Create definition for a list containing valid enum values for this enumerated type
Mike Schuchardt21638df2019-03-16 10:52:02 -0700598 if self.featureExtraProtect is not None:
599 enum_entry = '\n#ifdef %s\n' % self.featureExtraProtect
600 else:
601 enum_entry = ''
602 enum_entry += 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600603 for enum in groupElem:
604 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600605 if name is not None and enum.get('supported') != 'disabled':
606 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600607 enum_entry += '};\n'
Mike Schuchardt21638df2019-03-16 10:52:02 -0700608 if self.featureExtraProtect is not None:
609 enum_entry += '#endif // %s' % self.featureExtraProtect
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600610 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600611 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600612 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700613 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600614 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100615 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600616 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700617 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600618 decls = self.makeCDecls(cmdinfo.elem)
619 typedef = decls[1]
620 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700621 if self.header_file:
622 if name not in self.blacklist:
623 if (self.featureExtraProtect is not None):
624 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
625 # Strip off 'vk' from API name
626 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
627 if (self.featureExtraProtect is not None):
628 self.declarations += [ '#endif' ]
629 if self.source_file:
630 if name not in self.blacklist:
631 params = cmdinfo.elem.findall('param')
632 # Get list of array lengths
633 lens = set()
634 for param in params:
635 len = self.getLen(param)
636 if len:
637 lens.add(len)
638 # Get param info
639 paramsInfo = []
640 for param in params:
641 paramInfo = self.getTypeNameTuple(param)
642 cdecl = self.makeCParamDecl(param, 0)
643 # Check for parameter name in lens set
644 iscount = False
645 if paramInfo[1] in lens:
646 iscount = True
647 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
648 ispointer=self.paramIsPointer(param),
649 isstaticarray=self.paramIsStaticArray(param),
650 isbool=True if paramInfo[0] == 'VkBool32' else False,
651 israngedenum=True if paramInfo[0] in self.enumRanges else False,
652 isconst=True if 'const' in cdecl else False,
653 isoptional=self.paramIsOptional(param),
654 iscount=iscount,
655 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
656 len=self.getLen(param),
657 extstructs=None,
658 condition=None,
659 cdecl=cdecl))
660 # Save return value information, if any
661 result_type = ''
662 resultinfo = cmdinfo.elem.find('proto/type')
663 if (resultinfo is not None and resultinfo.text != 'void'):
664 result_type = resultinfo.text
665 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0], extension_type=self.extension_type, result=result_type))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600666 #
667 # Check if the parameter passed in is a pointer
668 def paramIsPointer(self, param):
669 ispointer = 0
670 paramtype = param.find('type')
671 if (paramtype.tail is not None) and ('*' in paramtype.tail):
672 ispointer = paramtype.tail.count('*')
673 elif paramtype.text[:4] == 'PFN_':
674 # Treat function pointer typedefs as a pointer to a single value
675 ispointer = 1
676 return ispointer
677 #
678 # Check if the parameter passed in is a static array
679 def paramIsStaticArray(self, param):
680 isstaticarray = 0
681 paramname = param.find('name')
682 if (paramname.tail is not None) and ('[' in paramname.tail):
683 isstaticarray = paramname.tail.count('[')
684 return isstaticarray
685 #
686 # Check if the parameter passed in is optional
687 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
688 def paramIsOptional(self, param):
689 # See if the handle is optional
690 isoptional = False
691 # Simple, if it's optional, return true
692 optString = param.attrib.get('optional')
693 if optString:
694 if optString == 'true':
695 isoptional = True
696 elif ',' in optString:
697 opts = []
698 for opt in optString.split(','):
699 val = opt.strip()
700 if val == 'true':
701 opts.append(True)
702 elif val == 'false':
703 opts.append(False)
704 else:
705 print('Unrecognized len attribute value',val)
706 isoptional = opts
707 return isoptional
708 #
709 # Check if the handle passed in is optional
710 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
711 def isHandleOptional(self, param, lenParam):
712 # Simple, if it's optional, return true
713 if param.isoptional:
714 return True
715 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
716 if param.noautovalidity:
717 return True
718 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
719 if lenParam and lenParam.isoptional:
720 return True
721 return False
722 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600723 # Retrieve the value of the len tag
724 def getLen(self, param):
725 result = None
726 len = param.attrib.get('len')
727 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600728 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
729 # strings. We strip the null-terminated from the 'len' field and only return the parameter specifying the string count
Mark Lobodzinski85672672016-10-13 08:36:42 -0600730 if 'null-terminated' in len:
731 result = len.split(',')[0]
732 else:
733 result = len
734 result = str(result).replace('::', '->')
735 return result
736 #
737 # Retrieve the type and name for a parameter
738 def getTypeNameTuple(self, param):
739 type = ''
740 name = ''
741 for elem in param:
742 if elem.tag == 'type':
743 type = noneStr(elem.text)
744 elif elem.tag == 'name':
745 name = noneStr(elem.text)
746 return (type, name)
747 #
748 # Find a named parameter in a parameter list
749 def getParamByName(self, params, name):
750 for param in params:
751 if param.name == name:
752 return param
753 return None
754 #
755 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
756 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
757 def parseLateXMath(self, source):
758 name = 'ERROR'
759 decoratedName = 'ERROR'
760 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700761 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
762 match = re.match(r'latexmath\s*\:\s*\[\s*\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\s*\]', source)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600763 if not match or match.group(1) != match.group(4):
764 raise 'Unrecognized latexmath expression'
765 name = match.group(2)
766 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
767 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700768 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600769 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
770 name = match.group(2)
771 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600772 return name, decoratedName
773 #
774 # Get the length paramater record for the specified parameter name
775 def getLenParam(self, params, name):
776 lenParam = None
777 if name:
778 if '->' in name:
779 # The count is obtained by dereferencing a member of a struct parameter
780 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600781 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
782 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600783 elif 'latexmath' in name:
784 lenName, decoratedName = self.parseLateXMath(name)
785 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600786 else:
787 lenParam = self.getParamByName(params, name)
788 return lenParam
789 #
790 # Convert a vulkan.h command declaration into a parameter_validation.h definition
791 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600792 # Strip the trailing ';' and split into individual lines
793 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600794 cmd_hdr = '\n'.join(lines)
795 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600796 #
797 # Generate the code to check for a NULL dereference before calling the
798 # validation function
799 def genCheckedLengthCall(self, name, exprs):
800 count = name.count('->')
801 if count:
802 checkedExpr = []
803 localIndent = ''
804 elements = name.split('->')
805 # Open the if expression blocks
806 for i in range(0, count):
807 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
808 localIndent = self.incIndent(localIndent)
809 # Add the validation expression
810 for expr in exprs:
811 checkedExpr.append(localIndent + expr)
812 # Close the if blocks
813 for i in range(0, count):
814 localIndent = self.decIndent(localIndent)
815 checkedExpr.append(localIndent + '}\n')
816 return [checkedExpr]
817 # No if statements were required
818 return exprs
819 #
820 # Generate code to check for a specific condition before executing validation code
821 def genConditionalCall(self, prefix, condition, exprs):
822 checkedExpr = []
823 localIndent = ''
824 formattedCondition = condition.format(prefix)
825 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
826 checkedExpr.append(localIndent + '{\n')
827 localIndent = self.incIndent(localIndent)
828 for expr in exprs:
829 checkedExpr.append(localIndent + expr)
830 localIndent = self.decIndent(localIndent)
831 checkedExpr.append(localIndent + '}\n')
832 return [checkedExpr]
833 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600834 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600835 def GetVuid(self, name, suffix):
836 vuid_string = 'VUID-%s-%s' % (name, suffix)
837 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600838 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600839 return vuid
840 if vuid_string in self.valid_vuids:
841 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600842 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600843 if name in self.alias_dict:
844 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
845 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700846 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600847 return vuid
848 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600849 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600850 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600851 checkExpr = []
852 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600853 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
854 stype_vuid = self.GetVuid(value.type, "sType-sType")
855 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
856
Mark Lobodzinski85672672016-10-13 08:36:42 -0600857 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800858 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
859
Mark Lobodzinski85672672016-10-13 08:36:42 -0600860 # This is an array with a pointer to a count value
861 if lenValue.ispointer:
862 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800863 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
864 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600865 # This is an array with an integer count value
866 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800867 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
868 funcPrintName, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600869 # This is an individual struct
870 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700871 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700872 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600873 return checkExpr
874 #
875 # Generate the handle check string
876 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
877 checkExpr = []
878 if lenValue:
879 if lenValue.ispointer:
880 # This is assumed to be an output array with a pointer to a count value
881 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
882 else:
883 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700884 checkExpr.append('skip |= validate_handle_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
Mark Lobodzinski85672672016-10-13 08:36:42 -0600885 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
886 else:
887 # This is assumed to be an output handle pointer
888 raise('Unsupported parameter validation case: Output handles are not NULL checked')
889 return checkExpr
890 #
891 # Generate check string for an array of VkFlags values
892 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
893 checkExpr = []
894 flagBitsName = value.type.replace('Flags', 'FlagBits')
895 if not flagBitsName in self.flagBits:
896 raise('Unsupported parameter validation case: array of reserved VkFlags')
897 else:
898 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700899 checkExpr.append('skip |= validate_flags_array("{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600900 return checkExpr
901 #
902 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600903 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600904 checkExpr = []
905 # Generate an array of acceptable VkStructureType values for pNext
906 extStructCount = 0
907 extStructVar = 'NULL'
908 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600909 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600910 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600911 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
912 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600913 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700914 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700915 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600916 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600917 return checkExpr
918 #
919 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600920 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600921 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600922 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600923 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600924 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
925 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600926 # This is an array with a pointer to a count value
927 if lenValue.ispointer:
928 # If count and array parameters are optional, there will be no validation
929 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
930 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700931 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600932 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600933 # This is an array with an integer count value
934 else:
935 # If count and array parameters are optional, there will be no validation
936 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600937 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700938 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600939 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
940 else:
941 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700942 checkExpr.append('skip |= validate_string_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600943 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600944 if checkExpr:
945 if lenValue and ('->' in lenValue.name):
946 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
947 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
948 # This is an individual struct that is not allowed to be NULL
949 elif not value.isoptional:
950 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600951 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600952 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600953 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
954 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
955 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600956 }
957 vuid = allocator_dict.get(value.name)
958 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600959 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700960 checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600961 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700962 checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, {}{}, {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
Ricardo Garcia98ba9642019-02-19 11:25:18 +0100963 else:
964 # Special case for optional internal allocation function pointers.
965 if (value.type, value.name) == ('PFN_vkInternalAllocationNotification', 'pfnInternalAllocation'):
Ricardo Garciac52489a2019-04-02 18:15:20 +0200966 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalFree', postProcSpec))
967 elif (value.type, value.name) == ('PFN_vkInternalFreeNotification', 'pfnInternalFree'):
968 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalAllocation', postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600969 return checkExpr
Ricardo Garciac52489a2019-04-02 18:15:20 +0200970
971 #
972 # Generate internal allocation function pointer check.
973 def internalAllocationCheck(self, funcPrintName, prefix, name, complementaryName, postProcSpec):
974 checkExpr = []
975 vuid = '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
976 checkExpr.append('if ({}{} != NULL)'.format(prefix, name))
977 checkExpr.append('{')
978 local_indent = self.incIndent('')
979 # Function pointers need a reinterpret_cast to void*
980 checkExpr.append(local_indent + 'skip |= validate_required_pointer("{}", {ppp}"{}{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, prefix, complementaryName, prefix, complementaryName, vuid, **postProcSpec))
981 checkExpr.append('}\n')
982 return checkExpr
983
Mark Lobodzinski85672672016-10-13 08:36:42 -0600984 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600985 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600986 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
987 # Build format specifier list
988 kwargs = {}
989 if '{postProcPrefix}' in line:
990 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
991 if type(memberDisplayNamePrefix) is tuple:
992 kwargs['postProcPrefix'] = 'ParameterName('
993 else:
994 kwargs['postProcPrefix'] = postProcSpec['ppp']
995 if '{postProcSuffix}' in line:
996 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
997 if type(memberDisplayNamePrefix) is tuple:
998 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
999 else:
1000 kwargs['postProcSuffix'] = postProcSpec['pps']
1001 if '{postProcInsert}' in line:
1002 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1003 if type(memberDisplayNamePrefix) is tuple:
1004 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1005 else:
1006 kwargs['postProcInsert'] = postProcSpec['ppi']
1007 if '{funcName}' in line:
1008 kwargs['funcName'] = funcName
1009 if '{valuePrefix}' in line:
1010 kwargs['valuePrefix'] = memberNamePrefix
1011 if '{displayNamePrefix}' in line:
1012 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
1013 if type(memberDisplayNamePrefix) is tuple:
1014 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
1015 else:
1016 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
1017
1018 if kwargs:
1019 # Need to escape the C++ curly braces
1020 if 'IndexVector' in line:
1021 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
1022 line = line.replace(' }),', ' }}),')
1023 return line.format(**kwargs)
1024 return line
1025 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001026 # Process struct member validation code, stripping metadata
1027 def ScrubStructCode(self, code):
1028 scrubbed_lines = ''
1029 for line in code:
1030 if 'validate_struct_pnext' in line:
1031 continue
1032 if 'allowed_structs' in line:
1033 continue
1034 if 'xml-driven validation' in line:
1035 continue
1036 line = line.replace('{postProcPrefix}', '')
1037 line = line.replace('{postProcSuffix}', '')
1038 line = line.replace('{postProcInsert}', '')
1039 line = line.replace('{funcName}', '')
1040 line = line.replace('{valuePrefix}', '')
1041 line = line.replace('{displayNamePrefix}', '')
1042 line = line.replace('{IndexVector}', '')
1043 line = line.replace('local_data->', '')
1044 scrubbed_lines += line
1045 return scrubbed_lines
1046 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001047 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001048 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1049 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001050 for line in lines:
1051 if output:
1052 output[-1] += '\n'
1053 if type(line) is list:
1054 for sub in line:
1055 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1056 else:
1057 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1058 return output
1059 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001060 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001061 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1062 expr = []
1063 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1064 expr.append('{')
1065 indent = self.incIndent(None)
1066 if lenValue:
1067 # Need to process all elements in the array
1068 indexName = lenValue.name.replace('Count', 'Index')
1069 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001070 if lenValue.ispointer:
1071 # If the length value is a pointer, de-reference it for the count.
1072 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1073 else:
1074 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001075 expr.append(indent + '{')
1076 indent = self.incIndent(indent)
1077 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001078 if value.ispointer == 2:
1079 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1080 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1081 else:
1082 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1083 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001084 else:
1085 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1086 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001087 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001088 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001089 if lenValue:
1090 # Close if and for scopes
1091 indent = self.decIndent(indent)
1092 expr.append(indent + '}\n')
1093 expr.append('}\n')
1094 return expr
1095 #
1096 # Generate the parameter checking code
1097 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1098 lines = [] # Generated lines of code
1099 unused = [] # Unused variable names
1100 for value in values:
1101 usedLines = []
1102 lenParam = None
1103 #
1104 # Prefix and suffix for post processing of parameter names for struct members. Arrays of structures need special processing to include the array index in the full parameter name.
1105 postProcSpec = {}
1106 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1107 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1108 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1109 #
1110 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1111 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1112 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001113 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001114 # will be validated with their associated array
1115 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001116 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001117 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001118 cpReq = 'true' # Count pointer cannot be NULL
1119 cvReq = 'true' # Count value cannot be 0
1120 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001121 # Generate required/optional parameter strings for the pointer and count values
1122 if value.isoptional:
1123 req = 'false'
1124 if value.len:
1125 # The parameter is an array with an explicit count parameter
1126 lenParam = self.getLenParam(values, value.len)
1127 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1128 if lenParam.ispointer:
1129 # Count parameters that are pointers are inout
1130 if type(lenParam.isoptional) is list:
1131 if lenParam.isoptional[0]:
1132 cpReq = 'false'
1133 if lenParam.isoptional[1]:
1134 cvReq = 'false'
1135 else:
1136 if lenParam.isoptional:
1137 cpReq = 'false'
1138 else:
1139 if lenParam.isoptional:
1140 cvReq = 'false'
1141 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001142 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001143 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001144 # members not tagged as 'noautovalidity' will be validated
1145 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
Ricardo Garcia98ba9642019-02-19 11:25:18 +01001146 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction', 'PFN_vkInternalAllocationNotification', 'PFN_vkInternalFreeNotification']
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001147 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001148 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1149 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1150 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001151 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001152 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001153 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001154 # 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
1155 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1156 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1157 elif value.type in self.flags and value.isconst:
1158 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1159 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001160 usedLines.append('skip |= validate_bool32_array("{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001161 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001162 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001163 usedLines.append('skip |= validate_ranged_enum_array("{}", {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))
Tony-LunarG53ca8982018-11-07 14:22:13 -07001164 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001165 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001166 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001167 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001168 # If this is a pointer to a struct (input), see if it contains members that need to be checked
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001169 if value.type in self.validatedStructs:
1170 if value.isconst: # or value.type in self.returnedonly_structs:
1171 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1172 elif value.type in self.returnedonly_structs:
1173 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001174 # Non-pointer types
1175 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001176 # The parameter will not be processes when tagged as 'noautovalidity'
1177 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001178 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001179 if value.noautovalidity:
1180 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1181 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1182 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001183 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001184 if value.type in self.structTypes:
1185 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001186 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001187 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001188 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001189 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001190 elif value.type in self.handleTypes:
1191 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001192 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001193 elif value.type in self.flags:
1194 flagBitsName = value.type.replace('Flags', 'FlagBits')
1195 if not flagBitsName in self.flagBits:
Dave Houlton413a6782018-05-22 13:01:54 -06001196 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001197 usedLines.append('skip |= validate_reserved_flags("{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001198 else:
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001199 if value.isoptional:
1200 flagsRequired = 'false'
Dave Houlton413a6782018-05-22 13:01:54 -06001201 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001202 else:
1203 flagsRequired = 'true'
Dave Houlton413a6782018-05-22 13:01:54 -06001204 vuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001205 allFlagsName = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001206 usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, false, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
Mike Schuchardt47619c82017-05-31 09:14:22 -06001207 elif value.type in self.flagBits:
1208 flagsRequired = 'false' if value.isoptional else 'true'
1209 allFlagsName = 'All' + value.type
Dave Houlton413a6782018-05-22 13:01:54 -06001210 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001211 usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, true, {});\n'.format(funcName, valueDisplayName, value.type, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001212 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001213 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001214 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001215 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001216 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001217 usedLines.append('skip |= validate_ranged_enum("{}", {ppp}"{}"{pps}, "{}", {}, {}{}, {});\n'.format(funcName, valueDisplayName, value.type, enum_value_list, valuePrefix, value.name, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001218 # If this is a struct, see if it contains members that need to be checked
1219 if value.type in self.validatedStructs:
1220 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1221 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001222 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001223 # Append the parameter check to the function body for the current command
1224 if usedLines:
1225 # Apply special conditional checks
1226 if value.condition:
1227 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1228 lines += usedLines
1229 elif not value.iscount:
1230 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1231 # it is an array count, which is indirectly referenced for array valiadation.
1232 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001233 if not lines:
1234 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001235 return lines, unused
1236 #
1237 # Generate the struct member check code from the captured data
1238 def processStructMemberData(self):
1239 indent = self.incIndent(None)
1240 for struct in self.structMembers:
1241 #
1242 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1243 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1244 if lines:
1245 self.validatedStructs[struct.name] = lines
1246 #
1247 # Generate the command param check code from the captured data
1248 def processCmdData(self):
1249 indent = self.incIndent(None)
1250 for command in self.commands:
1251 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1252 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1253 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001254 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001255 if (command.name in self.required_extensions) and (self.extension_type != 'device' or command.params[0].type != 'VkPhysicalDevice'):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001256 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001257 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1258 ext_table_type = 'instance'
1259 else:
1260 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001261 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001262 ext_name_define = ''
1263 ext_enable_name = ''
1264 for extension in self.registry.extensions:
1265 if extension.attrib['name'] == ext:
1266 ext_name_define = extension[0][1].get('name')
1267 ext_enable_name = ext_name_define.lower()
1268 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1269 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001270 ext_test = 'if (!%s_extensions.%s) skip |= OutputExtensionError("%s", %s);\n' % (ext_table_type, ext_enable_name, command.name, ext_name_define)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001271 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001272 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001273 func_sig = self.getCmdDef(command) + ' {\n'
1274 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1275 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001276 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001277 # Insert call to custom-written function if present
1278 if command.name in self.functions_with_manual_checks:
1279 # Generate parameter list for manual fcn and down-chain calls
1280 params_text = ''
1281 for param in command.params:
1282 params_text += '%s, ' % param.name
1283 params_text = params_text[:-2] + ');\n'
1284 cmdDef += ' skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinski85672672016-10-13 08:36:42 -06001285 for line in lines:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001286 if type(line) is list:
1287 for sub in line:
1288 cmdDef += indent + sub
1289 else:
1290 cmdDef += indent + line
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001291 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001292 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001293 self.validation.append(cmdDef)