blob: 56c4d940b67b90e69537fbd3ec9db64fc2f3c1e4 [file] [log] [blame]
#!/usr/bin/python3 -i
#
# Copyright (c) 2015-2025 The Khronos Group Inc.
# Copyright (c) 2015-2025 Valve Corporation
# Copyright (c) 2015-2025 LunarG, Inc.
# Copyright (c) 2015-2025 Google Inc.
# Copyright (c) 2023-2025 RasterGrid Kft.
# Copyright (C) 2025 Arm Limited.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from generators.generator_utils import buildListVUID, getVUID, createObject, destroyObject, PlatformGuardHelper
from vulkan_object import Handle, Command, Struct, Member, Param
from base_generator import BaseGenerator
# This class is a container for any source code, data, or other behavior that is necessary to
# customize the generator script for a specific target API variant (e.g. Vulkan SC). As such,
# all of these API-specific interfaces and their use in the generator script are part of the
# contract between this repository and its downstream users. Changing or removing any of these
# interfaces or their use in the generator script will have downstream effects and thus
# should be avoided unless absolutely necessary.
class APISpecific:
# Tells whether an object handle type is implicitly destroyed because it does not have
# destroy APIs or its parent object type does not have destroy APIs
@staticmethod
def IsImplicitlyDestroyed(targetApiName: str, handleType: str) -> bool:
match targetApiName:
# Vulkan specific implicitly destroyed handle types
case 'vulkan':
implicitly_destroyed_set = {
'VkDisplayKHR',
'VkDisplayModeKHR'
}
return handleType in implicitly_destroyed_set
# Returns whether allocation callback related VUIDs are enabled
@staticmethod
def AreAllocVUIDsEnabled(targetApiName: str) -> bool:
match targetApiName:
# Vulkan has allocation callback related VUIDs
case 'vulkan':
return True
class ObjectTrackerOutputGenerator(BaseGenerator):
def __init__(self,
valid_usage_file):
BaseGenerator.__init__(self)
self.valid_vuids = buildListVUID(valid_usage_file)
# Commands which are not autogenerated but still intercepted
self.no_autogen_list = [
'vkCreateInstance',
'vkDestroyInstance',
'vkCreateDevice',
'vkDestroyDevice',
# These don't have object tracing but will fail in loader if not here
'vkGetPhysicalDeviceQueueFamilyProperties',
'vkGetPhysicalDeviceQueueFamilyProperties2',
'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
# Need to manually track commmand buffer life times
'vkDestroyCommandPool',
'vkAllocateCommandBuffers',
'vkFreeCommandBuffers',
'vkBeginCommandBuffer',
# issues with VkDeferredOperationKHR
'vkCreateRayTracingPipelinesKHR',
# Descriptor management is done by hand
'vkDestroyDescriptorPool',
'vkAllocateDescriptorSets',
'vkFreeDescriptorSets',
'vkUpdateDescriptorSets',
'vkCmdPushDescriptorSet',
'vkCmdPushDescriptorSetKHR',
'vkCmdPushDescriptorSet2',
'vkCmdPushDescriptorSet2KHR',
'vkResetDescriptorPool',
'vkCreateDescriptorUpdateTemplate',
'vkCreateDescriptorUpdateTemplateKHR',
]
self.post_call_record_additional_parameters = {
"vkCreateRayTracingPipelinesKHR": "PipelineStates &pipeline_states, std::shared_ptr<chassis::CreateRayTracingPipelinesKHR> chassis_state"
}
# Commands which source are not autogenerated for PreCallValidate
self.no_validate_autogen_list = [
'vkExportMetalObjectsEXT',
# Need to validate the handles here manually
'vkDebugMarkerSetObjectNameEXT',
'vkDebugMarkerSetObjectTagEXT',
'vkSetDebugUtilsObjectNameEXT',
'vkSetDebugUtilsObjectTagEXT',
'vkGetPrivateData',
'vkSetPrivateData',
'vkReleaseCapturedPipelineDataKHR',
# Need to check the VkDescriptorType
'vkCreateDescriptorSetLayout',
'vkGetDescriptorSetLayoutSupport',
# Need to check the flag and attachmentCount
'vkCreateFramebuffer',
# Can't handle VkAccelerationStructureBuildGeometryInfoKHR in code gen currently
'vkBuildAccelerationStructuresKHR',
'vkCmdBuildAccelerationStructuresKHR',
'vkCmdBuildAccelerationStructuresIndirectKHR',
# Union of pointers
'vkGetDescriptorEXT',
'vkCreateIndirectExecutionSetEXT',
]
# Commands which source are not autogenerated for PreCallRecord
self.no_pre_record_autogen_list = [
'vkDestroySwapchainKHR',
# For tracking lifetime of linked GPL libraries
'vkDestroyPipeline',
]
# Commands which source are not autogenerated for PostCallRecord
self.no_post_record_autogen_list = [
# VkQueue are not created and instead acquired, so track differently
'vkGetDeviceQueue',
'vkGetDeviceQueue2',
# Need to get and allocate the VkDisplayKHR
'vkGetPhysicalDeviceDisplayPropertiesKHR',
'vkGetPhysicalDeviceDisplayProperties2KHR',
'vkGetDisplayModePropertiesKHR',
'vkGetDisplayModeProperties2KHR',
# Swapchain images handles are acquired
'vkGetSwapchainImagesKHR',
# For tracking lifetime of linked GPL libraries
'vkCreateGraphicsPipelines',
'vkCreatePipelineBinariesKHR',
# For tracking poisoned objects
'vkCreateDescriptorSetLayout',
'vkCreatePipelineLayout',
'vkCreateComputePipelines',
'vkCreateRayTracingPipelinesNV',
'vkCreateDataGraphPipelinesARM',
]
# These VUIDS are not implicit, but are best handled in this layer. Codegen for vkDestroy calls will generate a key
# which is translated here into a good VU. Saves ~40 checks.
self.manual_vuids = dict()
self.manual_vuids = {
"fence-compatalloc": '"VUID-vkDestroyFence-fence-01121"',
"fence-nullalloc": '"VUID-vkDestroyFence-fence-01122"',
"event-compatalloc": '"VUID-vkDestroyEvent-event-01146"',
"event-nullalloc": '"VUID-vkDestroyEvent-event-01147"',
"buffer-compatalloc": '"VUID-vkDestroyBuffer-buffer-00923"',
"buffer-nullalloc": '"VUID-vkDestroyBuffer-buffer-00924"',
"image-compatalloc": '"VUID-vkDestroyImage-image-01001"',
"image-nullalloc": '"VUID-vkDestroyImage-image-01002"',
"shaderModule-compatalloc": '"VUID-vkDestroyShaderModule-shaderModule-01092"',
"shaderModule-nullalloc": '"VUID-vkDestroyShaderModule-shaderModule-01093"',
"pipeline-compatalloc": '"VUID-vkDestroyPipeline-pipeline-00766"',
"pipeline-nullalloc": '"VUID-vkDestroyPipeline-pipeline-00767"',
"sampler-compatalloc": '"VUID-vkDestroySampler-sampler-01083"',
"sampler-nullalloc": '"VUID-vkDestroySampler-sampler-01084"',
"renderPass-compatalloc": '"VUID-vkDestroyRenderPass-renderPass-00874"',
"renderPass-nullalloc": '"VUID-vkDestroyRenderPass-renderPass-00875"',
"descriptorUpdateTemplate-compatalloc": '"VUID-vkDestroyDescriptorUpdateTemplate-descriptorSetLayout-00356"',
"descriptorUpdateTemplate-nullalloc": '"VUID-vkDestroyDescriptorUpdateTemplate-descriptorSetLayout-00357"',
"imageView-compatalloc": '"VUID-vkDestroyImageView-imageView-01027"',
"imageView-nullalloc": '"VUID-vkDestroyImageView-imageView-01028"',
"pipelineCache-compatalloc": '"VUID-vkDestroyPipelineCache-pipelineCache-00771"',
"pipelineCache-nullalloc": '"VUID-vkDestroyPipelineCache-pipelineCache-00772"',
"pipelineLayout-compatalloc": '"VUID-vkDestroyPipelineLayout-pipelineLayout-00299"',
"pipelineLayout-nullalloc": '"VUID-vkDestroyPipelineLayout-pipelineLayout-00300"',
"descriptorSetLayout-compatalloc": '"VUID-vkDestroyDescriptorSetLayout-descriptorSetLayout-00284"',
"descriptorSetLayout-nullalloc": '"VUID-vkDestroyDescriptorSetLayout-descriptorSetLayout-00285"',
"semaphore-compatalloc": '"VUID-vkDestroySemaphore-semaphore-01138"',
"semaphore-nullalloc": '"VUID-vkDestroySemaphore-semaphore-01139"',
"queryPool-compatalloc": '"VUID-vkDestroyQueryPool-queryPool-00794"',
"queryPool-nullalloc": '"VUID-vkDestroyQueryPool-queryPool-00795"',
"bufferView-compatalloc": '"VUID-vkDestroyBufferView-bufferView-00937"',
"bufferView-nullalloc": '"VUID-vkDestroyBufferView-bufferView-00938"',
"surface-compatalloc": '"VUID-vkDestroySurfaceKHR-surface-01267"',
"surface-nullalloc": '"VUID-vkDestroySurfaceKHR-surface-01268"',
"framebuffer-compatalloc": '"VUID-vkDestroyFramebuffer-framebuffer-00893"',
"framebuffer-nullalloc": '"VUID-vkDestroyFramebuffer-framebuffer-00894"',
"VkGraphicsPipelineCreateInfo-basePipelineHandle": '"VUID-VkGraphicsPipelineCreateInfo-flags-07984"',
"VkComputePipelineCreateInfo-basePipelineHandle": '"VUID-VkComputePipelineCreateInfo-flags-07984"',
"VkRayTracingPipelineCreateInfoNV-basePipelineHandle": '"VUID-VkRayTracingPipelineCreateInfoNV-flags-07984"',
"VkRayTracingPipelineCreateInfoKHR-basePipelineHandle": '"VUID-VkRayTracingPipelineCreateInfoKHR-flags-07984"',
"VkVideoSessionKHR-videoSession-compatalloc": '"VUID-vkDestroyVideoSessionKHR-videoSession-07193"',
"VkVideoSessionKHR-videoSession-nullalloc": '"VUID-vkDestroyVideoSessionKHR-videoSession-07194"',
"VkVideoSessionParametersKHR-videoSessionParameters-compatalloc": '"VUID-vkDestroyVideoSessionParametersKHR-videoSessionParameters-07213"',
"VkVideoSessionParametersKHR-videoSessionParameters-nullalloc": '"VUID-vkDestroyVideoSessionParametersKHR-videoSessionParameters-07214"',
"VkAccelerationStructureKHR-accelerationStructure-compatalloc": '"VUID-vkDestroyAccelerationStructureKHR-accelerationStructure-02443"',
"VkAccelerationStructureKHR-accelerationStructure-nullalloc": '"VUID-vkDestroyAccelerationStructureKHR-accelerationStructure-02444"',
"VkAccelerationStructureNV-accelerationStructure-compatalloc": '"VUID-vkDestroyAccelerationStructureNV-accelerationStructure-03753"',
"VkAccelerationStructureNV-accelerationStructure-nullalloc": '"VUID-vkDestroyAccelerationStructureNV-accelerationStructure-03754"',
"shader-compatalloc": '"VUID-vkDestroyShaderEXT-pAllocator-08483"',
"shader-nullalloc": '"VUID-vkDestroyShaderEXT-pAllocator-08484"',
"callback-compatalloc": '"VUID-vkDestroyDebugReportCallbackEXT-instance-01242"',
"callback-nullalloc": '"VUID-vkDestroyDebugReportCallbackEXT-instance-01243"',
"messenger-compatalloc": '"VUID-vkDestroyDebugUtilsMessengerEXT-messenger-01915"',
"messenger-nullalloc": '"VUID-vkDestroyDebugUtilsMessengerEXT-messenger-01916"',
"operation-compatalloc": '"VUID-vkDestroyDeferredOperationKHR-operation-03434"',
"operation-nullalloc": '"VUID-vkDestroyDeferredOperationKHR-operation-03435"',
"micromap-compatalloc": '"VUID-vkDestroyMicromapEXT-micromap-07442"',
"micromap-nullalloc": '"VUID-vkDestroyMicromapEXT-micromap-07443"',
"privateDataSlot-compatalloc": '"VUID-vkDestroyPrivateDataSlot-privateDataSlot-04062"',
"privateDataSlot-nullalloc": '"VUID-vkDestroyPrivateDataSlot-privateDataSlot-04063"',
"validationCache-compatalloc": '"VUID-vkDestroyValidationCacheEXT-validationCache-01537"',
"validationCache-nullalloc": '"VUID-vkDestroyValidationCacheEXT-validationCache-01538"',
"swapchain-compatalloc": '"VUID-vkDestroySwapchainKHR-swapchain-01283"',
"swapchain-nullalloc": '"VUID-vkDestroySwapchainKHR-swapchain-01284"',
"pipelineBinary-compatalloc": '"VUID-vkDestroyPipelineBinaryKHR-pipelineBinary-09614"',
"pipelineBinary-nullalloc": '"VUID-vkDestroyPipelineBinaryKHR-pipelineBinary-09615"',
"VkIndirectCommandsLayoutEXT-indirectCommandsLayout-compatalloc": '"VUID-vkDestroyIndirectCommandsLayoutEXT-indirectCommandsLayout-11115"',
"VkIndirectCommandsLayoutEXT-indirectCommandsLayout-nullalloc": '"VUID-vkDestroyIndirectCommandsLayoutEXT-indirectCommandsLayout-11116"',
"tensor-compatalloc": '"VUID-vkDestroyTensorARM-tensor-09731"',
"tensor-nullalloc": '"VUID-vkDestroyTensorARM-tensor-09732"',
"tensorView-compatalloc": '"VUID-vkDestroyTensorViewARM-tensorView-09751"',
"tensorView-nullalloc": '"VUID-vkDestroyTensorViewARM-tensorView-09752"',
"VkDataGraphPipelineSessionARM-session-compatalloc": '"VUID-vkDestroyDataGraphPipelineSessionARM-session-09794"',
"VkDataGraphPipelineSessionARM-session-nullalloc": '"VUID-vkDestroyDataGraphPipelineSessionARM-session-09795"',
}
# Structures that do not define parent/commonparent VUIDs for vulkan handles.
# This overlaps with https://gitlab.khronos.org/vulkan/vulkan/-/issues/3553#note_424431
self.structs_that_forgot_about_parent_vuids = [
'VkPhysicalDeviceSurfaceInfo2KHR',
'VkMicromapBuildInfoEXT',
# Handled in manual check
'VkDescriptorSetLayoutBinding',
]
# Commands that define parent requirements using "-parent" instead of "-commonparent" VUID
# for the cases when multiple objects share the same parent.
self.use_parent_instead_of_commonparent_commands = [
'vkBindBufferMemory',
'vkBindImageMemory',
'vkMergePipelineCaches',
'vkCreateGraphicsPipelines',
'vkCreateComputePipelines',
'vkUpdateDescriptorSetWithTemplate',
'vkUpdateDescriptorSetWithTemplateKHR',
'vkAcquireNextImageKHR',
'vkAcquireImageOHOS',
'vkMergeValidationCachesEXT',
'vkBindOpticalFlowSessionImageNV',
]
# Commands that include the dispatchable parameter in the "-commonparent" list of handles.
# For example, if the dispatchable parameter is VkDevice, but commonparent VUID defines
# VkInstance as a parent then such VUID will also ask to validate VkDevice againt VkInstance.
# In other cases, the dispatchable parameter is considered to be a parent and it is not
# included in the VUID's list of handles.
self.dispatchable_has_parent_vuid_commands = [
'vkGetDeviceGroupSurfacePresentModesKHR',
'vkDisplayPowerControlEXT',
'vkRegisterDisplayEventEXT',
'vkGetPhysicalDeviceSurfaceSupportKHR',
'vkGetPhysicalDeviceSurfaceCapabilitiesKHR',
'vkGetPhysicalDeviceSurfaceFormatsKHR',
'vkGetPhysicalDeviceSurfacePresentModesKHR',
'vkGetPhysicalDevicePresentRectanglesKHR',
'vkGetPhysicalDeviceSurfaceCapabilities2EXT'
]
# Work up Handle's parents to see if it VkDevice
def isParentDevice(self, handle: Handle) -> bool:
while handle.parent is not None:
if handle.parent.name == 'VkDevice':
return True
handle = handle.parent
return False
def allComments(self, lines: list[str]) -> bool:
not_empty_lines = [line for line in lines.splitlines() if line.strip()]
return all(line.startswith('//') for line in not_empty_lines)
def generate(self):
self.write(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
// See {os.path.basename(__file__)} for modifications
/***************************************************************************
*
* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 LunarG, Inc.
* Copyright (c) 2015-2025 Google Inc.
* Copyright (c) 2015-2025 RasterGrid Kft.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
****************************************************************************/\n''')
self.write('// NOLINTBEGIN') # Wrap for clang-tidy to ignore
if self.filename == 'object_tracker_device_methods.h':
self.generateDeviceHeader()
elif self.filename == 'object_tracker_instance_methods.h':
self.generateInstanceHeader()
elif self.filename == 'object_tracker.cpp':
self.generateSource()
else:
self.write(f'\nFile name {self.filename} has no code to generate\n')
self.write('// NOLINTEND') # Wrap for clang-tidy to ignore
def generateHeader(self, want_instance):
out = []
guard_helper = PlatformGuardHelper()
for command in [x for x in self.vk.commands.values() if x.instance == want_instance]:
out.extend(guard_helper.add_guard(command.protect))
(pre_call_validate, pre_call_record, post_call_record) = self.generateFunctionBody(command)
prototype = (command.cPrototype.split('VKAPI_CALL ')[1])[2:-1]
terminator = ';\n' if 'ValidationCache' in command.name else ' override;\n'
# If a function has manual implementation we still need to generate its signature in the header file
function_signature_for_no_autogen = command.name in self.no_autogen_list and not command.name == 'vkCreateInstance'
generate_pre_call_validate = (pre_call_validate and not self.allComments(pre_call_validate)) or function_signature_for_no_autogen
if generate_pre_call_validate or command.name in self.no_validate_autogen_list:
prePrototype = prototype.replace(')', ', const ErrorObject& error_obj)')
out.append(f'bool PreCallValidate{prePrototype} const{terminator}')
prototype = prototype.replace(')', ', const RecordObject& record_obj)')
if pre_call_record or command.name in self.no_pre_record_autogen_list:
out.append(f'void PreCallRecord{prototype}{terminator}')
if post_call_record or command.name in self.no_post_record_autogen_list:
if command.name in self.post_call_record_additional_parameters:
post_call_record_additional_parameters = self.post_call_record_additional_parameters[command.name]
prototype = prototype.replace(')', f', {post_call_record_additional_parameters})')
out.append(f'void PostCallRecord{prototype}{terminator}')
out.extend(guard_helper.add_guard(None))
self.write("".join(out))
def generateDeviceHeader(self):
self.generateHeader(False)
out = []
# These are Post/Pre call that normally would not be created but we need have manual object tracking logic for them
out.append('''
void PreCallRecordResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags, const RecordObject& record_obj) override;
void PreCallRecordFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers, const RecordObject& record_obj) override;
void PreCallRecordFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets, const RecordObject& record_obj) override;
''')
self.write("".join(out))
def generateInstanceHeader(self):
self.generateHeader(True)
out = []
# These are Post/Pre call that normally would not be created but we need have manual object tracking logic for them
out.append('''
void PostCallRecordDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2 *pQueueFamilyProperties, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2 *pQueueFamilyProperties, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceDisplayPlaneProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlaneProperties2KHR* pProperties, const RecordObject& record_obj) override;
''')
self.write("".join(out))
def generateSource(self):
out = []
out.append('''
#include "object_tracker/object_lifetime_validation.h"
namespace object_lifetimes {
ReadLockGuard Device::ReadLock() const { return ReadLockGuard(validation_object_mutex, std::defer_lock); }
WriteLockGuard Device::WriteLock() { return WriteLockGuard(validation_object_mutex, std::defer_lock); }
// ObjectTracker undestroyed objects validation function
bool Instance::ReportUndestroyedObjects(const Location& loc) const {
bool skip = false;
const std::string error_code = "VUID-vkDestroyInstance-instance-00629";
''')
for handle in [x for x in self.vk.handles.values() if not x.dispatchable and not self.isParentDevice(x)]:
comment_prefix = ''
if APISpecific.IsImplicitlyDestroyed(self.targetApiName, handle.name):
comment_prefix = '// No destroy API or implicitly freed/destroyed -- do not report: '
out.append(f' {comment_prefix}skip |= ReportLeakedObjects(kVulkanObjectType{handle.name[2:]}, error_code, loc);\n')
out.append(' return skip;\n')
out.append('}\n')
out.append('''
bool Device::ReportUndestroyedObjects(const Location& loc) const {
bool skip = false;
const std::string error_code = "VUID-vkDestroyDevice-device-05137";
''')
comment_prefix = ''
if APISpecific.IsImplicitlyDestroyed(self.targetApiName, 'VkCommandBuffer'):
comment_prefix = '// No destroy API or implicitly freed/destroyed -- do not report: '
out.append(f' {comment_prefix}skip |= ReportLeakedObjects(kVulkanObjectTypeCommandBuffer, error_code, loc);\n')
for handle in [x for x in self.vk.handles.values() if not x.dispatchable and self.isParentDevice(x)]:
comment_prefix = ''
if APISpecific.IsImplicitlyDestroyed(self.targetApiName, handle.name):
comment_prefix = '// No destroy API or implicitly freed/destroyed -- do not report: '
out.append(f' {comment_prefix}skip |= ReportLeakedObjects(kVulkanObjectType{handle.name[2:]}, error_code, loc);\n')
out.append(' return skip;\n')
out.append('}\n')
out.append('\nvoid Instance::DestroyLeakedObjects() {\n')
out.append(' const Location loc = Func::vkDestroyInstance;\n')
for handle in [x for x in self.vk.handles.values() if not x.dispatchable and not self.isParentDevice(x)]:
out.append(f' DestroyUndestroyedObjects(kVulkanObjectType{handle.name[2:]}, loc);\n')
out.append('}\n')
out.append('\nvoid Device::DestroyLeakedObjects() {\n')
out.append(' const Location loc = Func::vkDestroyDevice;\n')
out.append(' DestroyUndestroyedObjects(kVulkanObjectTypeCommandBuffer, loc);\n')
for handle in [x for x in self.vk.handles.values() if not x.dispatchable and self.isParentDevice(x)]:
out.append(f' DestroyUndestroyedObjects(kVulkanObjectType{handle.name[2:]}, loc);\n')
out.append('}\n')
guard_helper = PlatformGuardHelper()
for command in [x for x in self.vk.commands.values() if x.name not in self.no_autogen_list]:
out.extend(guard_helper.add_guard(command.protect))
class_name = "Device" if not command.instance else "Instance"
# Generate object handling code
(pre_call_validate, pre_call_record, post_call_record) = self.generateFunctionBody(command)
prototype = (command.cPrototype.split('VKAPI_CALL ')[1])[2:-1]
# Output PreCallValidateAPI function if necessary
if pre_call_validate and command.name not in self.no_validate_autogen_list:
prePrototype = prototype.replace(')', ', const ErrorObject& error_obj)')
if self.allComments(pre_call_validate):
out.append(f'''
// {command.name}:
{pre_call_validate}
''')
elif command.alias:
# For alias that are promoted, just point to new function, ErrorObject will allow us to distinguish the caller
paramList = [param.name for param in command.params]
paramList.append('error_obj')
params = ', '.join(paramList)
out.append(f'''
bool {class_name}::PreCallValidate{prePrototype} const {{
return PreCallValidate{command.alias[2:]}({params});
}}
''')
else:
out.append(f'''
bool {class_name}::PreCallValidate{prePrototype} const {{
bool skip = false;
{pre_call_validate}
return skip;
}}
''')
# Output PreCallRecordAPI function if necessary
if pre_call_record and command.name not in self.no_pre_record_autogen_list:
postPrototype = prototype.replace(')', ', const RecordObject& record_obj)')
out.append(f'''
void {class_name}::PreCallRecord{postPrototype} {{
{pre_call_record}
}}
''')
# Output PostCallRecordAPI function if necessary
if post_call_record and command.name not in self.no_post_record_autogen_list:
out.append('\n')
postPrototype = f'void {class_name}::PostCallRecord{prototype} {{\n'
postPrototype = postPrototype.replace(')', ', const RecordObject& record_obj)')
if command.returnType == 'VkResult':
# Some commands can have partial valid handles be created
partial_success_commands = ['vkCreateGraphicsPipelines', 'vkCreateComputePipelines', 'vkCreateRayTracingPipelinesNV', 'vkCreateRayTracingPipelinesKHR', 'vkCreateShadersEXT', 'vkCreateDataGraphPipelinesARM']
if command.name not in partial_success_commands:
postPrototype = postPrototype.replace('{', '{\n if (record_obj.result < VK_SUCCESS) return;')
out.append(postPrototype)
out.append(f'{post_call_record}\n')
out.append('}\n')
out.extend(guard_helper.add_guard(None))
out.append('''
} // namespace object_lifetimes
''')
self.write("".join(out))
def structContainsObject(self, struct: Struct) -> bool:
for member in struct.members:
if member.type in self.vk.handles:
return True
# recurse for member structs, guard against infinite recursion
elif member.type in self.vk.structs and member.type != struct.name:
if self.structContainsObject(self.vk.structs[member.type]):
return True
return False
def getAllocVUID(self, param: Param, allocType: str) -> str:
# Do not report allocation callback VUIDs if the target API does not support them
if not APISpecific.AreAllocVUIDsEnabled(self.targetApiName):
return "kVUIDUndefined"
lookup_string = '%s-%s' %(param.name, allocType)
vuid = self.manual_vuids.get(lookup_string, None)
if vuid is not None:
return vuid
lookup_string = '%s-%s-%s' %(param.type, param.name, allocType)
vuid = self.manual_vuids.get(lookup_string, None)
if vuid is not None:
return vuid
return "kVUIDUndefined"
def getParamVUID(self, member: Member, parentName: str) -> str:
# Exceptions
if (member.name == 'pCounterBuffers'):
if parentName == 'vkCmdBeginTransformFeedbackEXT':
return '"VUID-vkCmdBeginTransformFeedbackEXT-counterBufferCount-02607"'
if parentName == 'vkCmdEndTransformFeedbackEXT':
return '"VUID-vkCmdEndTransformFeedbackEXT-counterBufferCount-02608"'
# Replace with alias if one
alias = self.vk.commands[parentName].alias if parentName in self.vk.commands else None
parent = alias if alias else parentName
vuid_string = f'VUID-{parent}-{member.name}-parameter'
# TODO: Currently just brute force check all VUs, but should be smarter what makes these `-parameter` VUs
param_vuid = f'"{vuid_string}"' if vuid_string in self.valid_vuids else "kVUIDUndefined"
return param_vuid
def hasFieldParentVUID(self, member: Member, structName: str) -> bool:
# Not a vulkan handle. Parent VUIDs are only for vulkan handles
if member.type not in self.vk.handles:
return False
# All struct members that are vulkan handles should have parent VUID.
# There is a bunch of structs though, for which the specification does not do this.
return structName not in self.structs_that_forgot_about_parent_vuids
def hasParameterParentVUID(self, parameter: Member, commandName: str) -> bool:
# Check for commands that, except the first dispatchable parameter,
# do not have other parameters that are Vulkan handles.
# Such commands can't have parent VUIDs (e.g. vkQueueWaitIdle)
params = self.vk.commands[commandName].params
only_dispatchable_parameter = len([x for x in params if x.type in self.vk.handles and (not x.pointer or x.const)]) == 1
if only_dispatchable_parameter:
return False
# Special case: vkReleaseFullScreenExclusiveModeEXT.
# The specification does not define a parent VUID for the swapchain parameter.
# It mentions in a free form that device should be associated with a swapchain.
if commandName == 'vkReleaseFullScreenExclusiveModeEXT' or commandName == 'vkCreateDataGraphPipelinesARM':
return False
# Not a vulkan handle. Parent VUIDs are only for vulkan handles
if parameter.type not in self.vk.handles:
return False
# Skip output parameter
if parameter.pointer and not parameter.const:
return False
# Non-dispatchable handles need parent
if not self.vk.handles[parameter.type].dispatchable:
return True
# Queue/command buffer handles have parent vuids
if parameter.type == 'VkQueue' or parameter.type == 'VkCommandBuffer':
return True
# For other dispatchable handles it depends on the API function
return commandName in self.dispatchable_has_parent_vuid_commands
# It is very complex for the spec handle walking through structs and finding Handles and generating implicit VUs,
# We instead just have to do it manually for now.
# (details at https://gitlab.khronos.org/vulkan/vulkan/-/issues/3553#note_424431)
#
# This was attempted to be solved in https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/6711
# but we need a more automatic way, but having this here helps as a working reference in the future
def getManualParentVUID(self, memberName: str, structName: str, commandName: str):
# These are cases where there is only a single caller of the struct
# We check by command name incase a new command would use the struct
if commandName == 'vkCreateImageView' and memberName == 'image':
return '"VUID-vkCreateImageView-image-09179"'
if commandName =='vkGetPipelineExecutablePropertiesKHR' and memberName == 'pipeline':
return '"VUID-vkGetPipelineExecutablePropertiesKHR-pipeline-03271"'
if commandName == 'vkCmdCudaLaunchKernelNV' and memberName == 'function':
return '"UNASSIGNED-VkCudaLaunchInfoNV-function-parent"'
if commandName == 'vkLatencySleepNV' and memberName == 'signalSemaphore':
return '"UNASSIGNED-VkLatencySleepInfoNV-signalSemaphore-parent"'
if commandName == 'vkCreateCudaFunctionNV' and memberName == 'module':
return '"UNASSIGNED-VkCudaFunctionCreateInfoNV-module-parent"'
if (commandName == 'vkCmdPushConstants2' or commandName == 'vkCmdPushConstants2KHR') and memberName == 'layout':
return '"UNASSIGNED-VkPushConstantsInfo-layout-parent"'
if commandName == 'vkCmdSetDescriptorBufferOffsets2EXT' and memberName == 'layout':
return '"UNASSIGNED-VkSetDescriptorBufferOffsetsInfoEXT-layout-parent"'
if commandName == 'vkCmdBindDescriptorBufferEmbeddedSamplers2EXT' and memberName == 'layout':
return '"UNASSIGNED-VkBindDescriptorBufferEmbeddedSamplersInfoEXT-layout-parent"'
if commandName == 'vkCreateBufferView' and memberName == 'buffer':
return '"UNASSIGNED-VkBufferViewCreateInfo-buffer-parent"'
if commandName == 'vkCreateShadersEXT' and memberName == 'pSetLayouts':
return '"UNASSIGNED-VkShaderCreateInfoEXT-pSetLayouts-parent"'
if commandName == 'vkGetMemoryWin32HandleKHR' and memberName == 'memory':
return '"UNASSIGNED-VkMemoryGetWin32HandleInfoKHR-memory-parent"'
if commandName == 'vkImportSemaphoreWin32HandleKHR' and memberName == 'semaphore':
return '"UNASSIGNED-VkImportSemaphoreWin32HandleInfoKHR-semaphore-parent"'
if commandName == 'vkGetSemaphoreWin32HandleKHR' and memberName == 'semaphore':
return '"UNASSIGNED-VkSemaphoreGetWin32HandleInfoKHR-semaphore-parent"'
if commandName == 'vkImportFenceWin32HandleKHR' and memberName == 'fence':
return '"UNASSIGNED-VkImportFenceWin32HandleInfoKHR-fence-parent"'
if commandName == 'vkGetFenceWin32HandleKHR' and memberName == 'fence':
return '"UNASSIGNED-VkFenceGetWin32HandleInfoKHR-fence-parent"'
if commandName == 'vkGetMemoryFdKHR' and memberName == 'memory':
return '"UNASSIGNED-VkMemoryGetFdInfoKHR-memory-parent"'
if commandName == 'vkImportSemaphoreFdKHR' and memberName == 'semaphore':
return '"UNASSIGNED-VkImportSemaphoreFdInfoKHR-semaphore-parent"'
if commandName == 'vkGetSemaphoreFdKHR' and memberName == 'semaphore':
return '"UNASSIGNED-VkSemaphoreGetFdInfoKHR-semaphore-parent"'
if commandName == 'vkImportFenceFdKHR' and memberName == 'fence':
return '"UNASSIGNED-VkImportFenceFdInfoKHR-fence-parent"'
if commandName == 'vkGetFenceFdKHR' and memberName == 'fence':
return '"UNASSIGNED-VkFenceGetFdInfoKHR-fence-parent"'
if commandName == 'vkGetMemoryAndroidHardwareBufferANDROID' and memberName == 'memory':
return '"UNASSIGNED-VkMemoryGetAndroidHardwareBufferInfoANDROID-memory-parent"'
if commandName == 'vkGetMemoryZirconHandleFUCHSIA' and memberName == 'memory':
return '"UNASSIGNED-VkMemoryGetZirconHandleInfoFUCHSIA-memory-parent"'
if commandName == 'vkImportSemaphoreZirconHandleFUCHSIA' and memberName == 'semaphore':
return '"UNASSIGNED-VkImportSemaphoreZirconHandleInfoFUCHSIA-semaphore-parent"'
if commandName == 'vkGetSemaphoreZirconHandleFUCHSIA' and memberName == 'semaphore':
return '"UNASSIGNED-VkSemaphoreGetZirconHandleInfoFUCHSIA-semaphore-parent"'
if commandName == 'vkGetMemoryRemoteAddressNV' and memberName == 'memory':
return '"UNASSIGNED-VkMemoryGetRemoteAddressInfoNV-memory-parent"'
if commandName == 'vkAllocateMemory' and memberName == 'collection':
return '"UNASSIGNED-VkImportMemoryBufferCollectionFUCHSIA-collection-parent"'
if commandName == 'vkCreateBuffer' and memberName == 'collection':
return '"UNASSIGNED-VkBufferCollectionBufferCreateInfoFUCHSIA-collection-parent"'
if commandName == 'vkCreateImage' and memberName == 'collection':
return '"UNASSIGNED-VkBufferCollectionImageCreateInfoFUCHSIA-collection-parent"'
if commandName == 'vkGetDescriptorSetLayoutHostMappingInfoVALVE' and memberName == 'descriptorSetLayout':
return '"UNASSIGNED-VkDescriptorSetBindingReferenceVALVE-descriptorSetLayout-parent"'
if commandName == 'vkGetAccelerationStructureMemoryRequirementsNV' and memberName == 'accelerationStructure':
return '"UNASSIGNED-VkAccelerationStructureMemoryRequirementsInfoNV-accelerationStructure-parent"'
if commandName == 'vkGetPipelineIndirectDeviceAddressNV' and memberName == 'pipeline':
return '"UNASSIGNED-VkPipelineIndirectDeviceAddressInfoNV-pipeline-parent"'
if commandName == 'vkCreateCuFunctionNVX' and memberName == 'module':
return '"UNASSIGNED-VkCuFunctionCreateInfoNVX-module-parent"'
if commandName == 'vkCmdCuLaunchKernelNVX' and memberName == 'function':
return '"UNASSIGNED-VkCuLaunchInfoNVX-function-parent"'
if commandName == 'vkCreateIndirectCommandsLayoutNV' and memberName == 'pushconstantPipelineLayout':
return '"UNASSIGNED-VkIndirectCommandsLayoutTokenNV-pushconstantPipelineLayout-parent"'
if commandName == 'vkGetBufferOpaqueCaptureDescriptorDataEXT' and memberName == 'buffer':
return '"UNASSIGNED-VkBufferCaptureDescriptorDataInfoEXT-buffer-parent"'
if commandName == 'vkGetImageOpaqueCaptureDescriptorDataEXT' and memberName == 'image':
return '"UNASSIGNED-VkImageCaptureDescriptorDataInfoEXT-image-parent"'
if commandName == 'vkGetImageViewOpaqueCaptureDescriptorDataEXT' and memberName == 'imageView':
return '"UNASSIGNED-VkImageViewCaptureDescriptorDataInfoEXT-imageView-parent"'
if commandName == 'vkGetSamplerOpaqueCaptureDescriptorDataEXT' and memberName == 'sampler':
return '"UNASSIGNED-VkSamplerCaptureDescriptorDataInfoEXT-sampler-parent"'
if commandName == 'vkBindVideoSessionMemoryKHR' and memberName == 'memory':
return '"UNASSIGNED-VkBindVideoSessionMemoryInfoKHR-memory-parent"'
if commandName == 'vkCmdDecodeVideoKHR' and memberName == 'srcBuffer':
return '"UNASSIGNED-VkVideoDecodeInfoKHR-srcBuffer-parent"'
if commandName == 'vkGetEncodedVideoSessionParametersKHR' and memberName == 'videoSessionParameters':
return '"UNASSIGNED-VkVideoEncodeSessionParametersGetInfoKHR-videoSessionParameters-parent"'
if commandName == 'vkCmdEncodeVideoKHR' and memberName == 'dstBuffer':
return '"UNASSIGNED-VkVideoEncodeInfoKHR-dstBuffer-parent"'
if commandName == 'vkCreateDisplayPlaneSurfaceKHR' and memberName == 'displayMode':
return '"UNASSIGNED-VkDisplaySurfaceCreateInfoKHR-displayMode-parent"'
if commandName == 'vkGetDisplayPlaneCapabilities2KHR' and memberName == 'mode':
return '"UNASSIGNED-VkDisplayPlaneInfo2KHR-mode-parent"'
if commandName == 'vkCmdBindDescriptorBuffersEXT' and memberName == 'buffer':
return '"UNASSIGNED-VkDescriptorBufferBindingPushDescriptorBufferHandleEXT-buffer-parent"'
if commandName == 'vkReleaseSwapchainImagesEXT' and memberName == 'swapchain':
return '"UNASSIGNED-VkReleaseSwapchainImagesInfoEXT-swapchain-parent"'
if commandName == 'vkReleaseSwapchainImagesKHR' and memberName == 'swapchain':
return '"UNASSIGNED-VkReleaseSwapchainImagesInfoKHR-swapchain-parent"'
if commandName == 'vkCmdBeginConditionalRenderingEXT' and memberName == 'buffer':
return '"UNASSIGNED-VkConditionalRenderingBeginInfoEXT-buffer-parent"'
if (commandName == 'vkMapMemory2' or commandName == 'vkMapMemory2KHR') and memberName == 'memory':
return '"UNASSIGNED-VkMemoryMapInfo-memory-parent"'
if (commandName == 'vkUnmapMemory2' or commandName == 'vkUnmapMemory2KHR') and memberName == 'memory':
return '"UNASSIGNED-VkMemoryUnmapInfo-memory-parent"'
if (commandName == 'vkCopyMemoryToImage' or commandName == 'vkCopyMemoryToImageEXT') and memberName == 'dstImage':
return '"UNASSIGNED-VkCopyMemoryToImageInfo-dstImage-parent"'
if (commandName == 'vkCopyImageToMemory' or commandName == 'vkCopyImageToMemoryEXT') and memberName == 'srcImage':
return '"UNASSIGNED-VkCopyImageToMemoryInfo-srcImage-parent"'
if (commandName == 'vkTransitionImageLayout' or commandName == 'vkTransitionImageLayoutEXT') and memberName == 'image':
return '"UNASSIGNED-VkHostImageLayoutTransitionInfo-image-parent"'
if commandName == 'vkCreateMicromapEXT' and memberName == 'buffer':
return '"UNASSIGNED-VkMicromapCreateInfoEXT-buffer-parent"'
if commandName == 'vkCreateAccelerationStructureKHR' and memberName == 'buffer':
return '"UNASSIGNED-VkAccelerationStructureCreateInfoKHR-buffer-parent"'
if commandName == 'vkCreateImage' and memberName == 'swapchain':
return '"UNASSIGNED-VkImageSwapchainCreateInfoKHR-swapchain-parent"'
if commandName == 'vkQueuePresentKHR' and memberName == 'pFences':
return '"UNASSIGNED-VkSwapchainPresentFenceInfoKHR-pFences-parent"'
if commandName == 'vkGetAccelerationStructureDeviceAddressKHR' and memberName == 'accelerationStructure':
return '"UNASSIGNED-VkAccelerationStructureDeviceAddressInfoKHR-accelerationStructure-parent"'
if commandName == 'vkCreatePipelineBinariesKHR' and memberName == 'pipeline':
return '"UNASSIGNED-VkPipelineBinaryCreateInfoKHR-pipeline-parent"'
if commandName == 'vkGetPipelineBinaryDataKHR' and memberName == 'pipelineBinary':
return '"UNASSIGNED-VkPipelineBinaryDataInfoKHR-pipelineBinary-parent"'
if commandName == 'vkReleaseCapturedPipelineDataKHR' and memberName == 'pipeline':
return '"UNASSIGNED-VkReleaseCapturedPipelineDataInfoKHR-pipeline-parent"'
if commandName.startswith('vkWaitSemaphores') and memberName == 'pSemaphores':
return '"UNASSIGNED-VkSemaphoreWaitInfo-pSemaphores-parent"'
if commandName.startswith('vkSignalSemaphore') and memberName == 'semaphore':
return '"UNASSIGNED-VkSemaphoreSignalInfo-semaphore-parent"'
if commandName.startswith('vkGetImageMemoryRequirements2') and memberName == 'image':
return '"UNASSIGNED-VkSemaphoreSignalInfo-image-parent"'
if commandName.startswith('vkGetBufferMemoryRequirements2') and memberName == 'buffer':
return '"UNASSIGNED-VkSemaphoreSignalInfo-buffer-parent"'
if commandName.startswith('vkGetImageSparseMemoryRequirements2') and memberName == 'image':
return '"UNASSIGNED-VkSemaphoreSignalInfo-image-parent"'
if commandName.startswith('vkQueueSubmit2') and memberName == 'commandBuffer':
return '"UNASSIGNED-VkCommandBufferSubmitInfo-commandBuffer-parent"'
if commandName.startswith('vkBindImageMemory2') and memberName == 'swapchain':
return '"UNASSIGNED-VkBindImageMemorySwapchainInfoKHR-swapchain-parent"'
if commandName.startswith('vkGetDeviceMemoryOpaqueCaptureAddress') and memberName == 'memory':
return '"UNASSIGNED-VkDeviceMemoryOpaqueCaptureAddressInfo-memory-parent"'
# Same as above, but memberName has naming collision so need to use struct name as well
if commandName == 'vkCreateGraphicsPipelines' and structName == 'VkGraphicsPipelineShaderGroupsCreateInfoNV' and memberName == 'pPipelines':
return '"UNASSIGNED-VkGraphicsPipelineShaderGroupsCreateInfoNV-pPipelines-parent"'
if commandName == 'vkGetDataGraphPipelineSessionBindPointRequirementsARM' and memberName == 'session':
return '"VUID-vkGetDataGraphPipelineSessionBindPointRequirementsARM-session-09783"'
if structName == 'VkDataGraphPipelineSessionMemoryRequirementsInfoARM' and memberName == 'session':
return '"VUID-vkGetDataGraphPipelineSessionMemoryRequirementsARM-session-09950"'
if structName == 'VkDataGraphPipelineInfoARM' and memberName == 'dataGraphPipeline':
if commandName == 'vkGetDataGraphPipelinePropertiesARM':
return '"VUID-vkGetDataGraphPipelinePropertiesARM-dataGraphPipeline-09802"'
if commandName == 'vkGetDataGraphPipelineAvailablePropertiesARM':
return '"VUID-vkGetDataGraphPipelineAvailablePropertiesARM-dataGraphPipeline-09888"'
# These are cases where multiple commands call the struct
if structName == 'VkPipelineExecutableInfoKHR' and memberName == 'pipeline':
if commandName == 'vkGetPipelineExecutableStatisticsKHR':
return '"VUID-vkGetPipelineExecutableStatisticsKHR-pipeline-03273"'
elif commandName == 'vkGetPipelineExecutableInternalRepresentationsKHR':
return '"VUID-vkGetPipelineExecutableInternalRepresentationsKHR-pipeline-03277"'
# These are cases are also where multiple commands call the struct,
# but for simplicity, use same VUID because the Location will provide the name of the caller.
# The only reason these have seperate VUs is because they were listed in the command, not the struct
if structName == 'VkRenderingFragmentDensityMapAttachmentInfoEXT' and memberName == 'imageView':
return '"UNASSIGNED-VkRenderingFragmentDensityMapAttachmentInfoEXT-imageView-commonparent"'
if structName == 'VkRenderingFragmentShadingRateAttachmentInfoKHR' and memberName == 'imageView':
return '"UNASSIGNED-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-commonparent"'
if structName == 'VkSubpassShadingPipelineCreateInfoHUAWEI' and memberName == 'renderPass':
return '"UNASSIGNED-VkSubpassShadingPipelineCreateInfoHUAWEI-renderPass-parent"'
if structName == 'VkIndirectCommandsStreamNV' and memberName == 'buffer':
return '"UNASSIGNED-VkIndirectCommandsStreamNV-buffer-parent"'
if structName == 'VkPipelineLayoutCreateInfo' and memberName == 'pSetLayouts':
return '"UNASSIGNED-VkPipelineLayoutCreateInfo-pSetLayouts-commonparent"'
if structName == 'VkVideoInlineQueryInfoKHR' and memberName == 'queryPool':
return '"UNASSIGNED-VkVideoInlineQueryInfoKHR-queryPool-parent"'
if structName == 'VkMappedMemoryRange' and memberName == 'memory':
return '"UNASSIGNED-VkMappedMemoryRange-memory-device"'
if structName == 'VkPipelineShaderStageCreateInfo' and memberName == 'module':
return '"UNASSIGNED-VkPipelineShaderStageCreateInfo-module-parent"'
if structName == 'VkVideoPictureResourceInfoKHR' and memberName == 'imageViewBinding':
return '"UNASSIGNED-VkVideoPictureResourceInfoKHR-imageViewBinding-parent"'
if structName == 'VkGeometryAABBNV' and memberName == 'aabbData':
return '"UNASSIGNED-VkGeometryAABBNV-aabbData-parent"'
if structName == 'VkPipelineLibraryCreateInfoKHR' and memberName == 'pLibraries':
return '"UNASSIGNED-VkPipelineLibraryCreateInfoKHR-pLibraries-parent"'
if structName == 'VkCopyMicromapToMemoryInfoEXT' and memberName == 'src':
return '"UNASSIGNED-VkCopyMicromapToMemoryInfoEXT-src-parent"'
if structName == 'VkCopyMemoryToMicromapInfoEXT' and memberName == 'dst':
return '"UNASSIGNED-VkCopyMemoryToMicromapInfoEXT-dst-parent"'
if structName == 'VkCopyAccelerationStructureToMemoryInfoKHR' and memberName == 'src':
return '"UNASSIGNED-VkCopyAccelerationStructureToMemoryInfoKHR-src-parent"'
if structName == 'VkCopyMemoryToAccelerationStructureInfoKHR' and memberName == 'dst':
return '"UNASSIGNED-VkCopyMemoryToAccelerationStructureInfoKHR-dst-parent"'
if structName == 'VkSamplerYcbcrConversionInfo' and memberName == 'conversion':
return '"UNASSIGNED-VkSamplerYcbcrConversionInfo-conversion-parent"'
if structName == 'VkShaderModuleValidationCacheCreateInfoEXT' and memberName == 'validationCache':
return '"UNASSIGNED-VkShaderModuleValidationCacheCreateInfoEXT-validationCache-parent"'
if structName == 'VkBufferDeviceAddressInfo' and memberName == 'buffer':
return '"UNASSIGNED-VkBufferDeviceAddressInfo-buffer-parent"'
if structName == 'VkPipelineBinaryInfoKHR' and memberName == 'pPipelineBinaries':
return '"UNASSIGNED-VkPipelineBinaryInfoKHR-pPipelineBinaries-parent"'
if structName == 'VkGeneratedCommandsPipelineInfoEXT' and memberName == 'pipeline':
return '"UNASSIGNED-VkGeneratedCommandsPipelineInfoEXT-pipeline-parent"'
if structName == 'VkGeneratedCommandsShaderInfoEXT' and memberName == 'pShaders':
return '"UNASSIGNED-VkGeneratedCommandsShaderInfoEXT-pShaders-parent"'
if structName == 'VkIndirectCommandsLayoutCreateInfoEXT' and memberName == 'pipelineLayout':
return '"UNASSIGNED-VkIndirectCommandsLayoutCreateInfoEXT-pipelineLayout-parent"'
if structName == 'VkIndirectExecutionSetPipelineInfoEXT' and memberName == 'initialPipeline':
return '"UNASSIGNED-VkIndirectExecutionSetPipelineInfoEXT-initialPipeline-parent"'
if structName == 'VkIndirectExecutionSetShaderInfoEXT' and memberName == 'pInitialShaders':
return '"UNASSIGNED-VkIndirectExecutionSetShaderInfoEXT-pInitialShaders-parent"'
if structName == 'VkIndirectExecutionSetShaderLayoutInfoEXT' and memberName == 'pSetLayouts':
return '"UNASSIGNED-VkIndirectExecutionSetShaderLayoutInfoEXT-pSetLayouts-parent"'
if structName == 'VkWriteIndirectExecutionSetPipelineEXT' and memberName == 'pipeline':
return '"UNASSIGNED-VkWriteIndirectExecutionSetPipelineEXT-pipeline-parent"'
if structName == 'VkWriteIndirectExecutionSetShaderEXT' and memberName == 'shader':
return '"UNASSIGNED-VkWriteIndirectExecutionSetShaderEXT-shader-parent"'
if structName == 'VkDescriptorDataEXT' and memberName == 'pSampler':
return '"UNASSIGNED-VkDescriptorDataEXT-pSampler-parent"'
if structName == 'VkVideoEncodeQuantizationMapInfoKHR' and memberName == 'quantizationMap':
return '"UNASSIGNED-VkVideoEncodeQuantizationMapInfoKHR-quantizationMap-parent"'
if structName == 'VkPipelineInfoKHR' and memberName == 'pipeline':
return '"UNASSIGNED-VkPipelineInfoKHR-pipeline-parent"'
if structName == 'VkMemoryGetMetalHandleInfoEXT' and memberName == 'memory':
return '"UNASSIGNED-VkMemoryGetMetalHandleInfoEXT-memory-parent"'
if structName == 'VkTileMemoryBindInfoQCOM' and memberName == 'memory':
return '"UNASSIGNED-VkTileMemoryBindInfoQCOM-memory-parent"'
if structName == 'VkExternalComputeQueueCreateInfoNV' and memberName == 'preferredQueue':
return '"UNASSIGNED-VkExternalComputeQueueCreateInfoNV-preferredQueue-parent"'
if structName == 'VkFrameBoundaryTensorsARM' and memberName == 'pTensors':
return '"UNASSIGNED-VkFrameBoundaryTensorsARM-pTensors-parent"'
if structName == 'VkTensorMemoryBarrierARM' and memberName == 'tensor':
return '"UNASSIGNED-VkTensorMemoryBarrierARM-tensor-parent"'
if structName == 'VkMemoryDedicatedAllocateInfoTensorARM' and memberName == 'tensor':
return '"UNASSIGNED-VkMemoryDedicatedAllocateInfoTensorARM-tensor-parent"'
if structName == 'VkDescriptorGetTensorInfoARM' and memberName == 'tensorView':
return '"UNASSIGNED-VkDescriptorGetTensorInfoARM-tensorView-parent"'
if structName == 'VkTensorViewCreateInfoARM' and memberName == 'tensor':
return '"UNASSIGNED-VkTensorViewCreateInfoARM-tensor-parent"'
if structName == 'VkTensorMemoryRequirementsInfoARM' and memberName == 'tensor':
return '"UNASSIGNED-VkTensorMemoryRequirementsInfoARM-tensor-parent"'
if structName == 'VkTensorCaptureDescriptorDataInfoARM' and memberName == 'tensor':
return '"UNASSIGNED-VkTensorCaptureDescriptorDataInfoARM-tensor-parent"'
if structName == 'VkTensorViewCaptureDescriptorDataInfoARM' and memberName == 'tensorView':
return '"UNASSIGNED-VkTensorViewCaptureDescriptorDataInfoARM-tensorView-parent"'
if structName == 'VkDataGraphPipelineCreateInfoARM' and memberName == 'layout':
return '"UNASSIGNED-VkDataGraphPipelineCreateInfoARM-layout-parent"'
if structName == 'VkDataGraphPipelineShaderModuleCreateInfoARM' and memberName == 'module':
return '"UNASSIGNED-VkDataGraphPipelineShaderModuleCreateInfoARM-module-parent"'
if structName == 'VkDataGraphPipelineSessionCreateInfoARM' and memberName == 'dataGraphPipeline':
return '"UNASSIGNED-VkDataGraphPipelineSessionCreateInfoARM-dataGraphPipeline-parent"'
if structName == 'VkDataGraphPipelineSessionBindPointRequirementsInfoARM' and memberName == 'session':
return '"UNASSIGNED-VkDataGraphPipelineSessionBindPointRequirementsInfoARM-session-parent"'
if structName == 'VkCopyMemoryToImageIndirectInfoKHR' and memberName == 'dstImage':
return '"UNASSIGNED-VkCopyMemoryToImageIndirectInfoKHR-dstImage-parent"'
if structName == 'VkSwapchainCalibratedTimestampInfoEXT' and memberName == 'swapchain':
return '"UNASSIGNED-VkSwapchainCalibratedTimestampInfoEXT-swapchain-parent"'
if structName == 'VkPastPresentationTimingInfoEXT' and memberName == 'swapchain':
return '"UNASSIGNED-VkPastPresentationTimingInfoEXT-swapchain-parent"'
if structName == 'VkMemoryGetNativeBufferInfoOHOS' and memberName == 'memory':
return '"UNASSIGNED-VkMemoryGetNativeBufferInfoOHOS-memory-parent"'
# Common parents because the structs have more then one handle that needs to be check
if (structName == 'VkBufferMemoryBarrier' and memberName == 'buffer') or (structName == 'VkImageMemoryBarrier' and memberName == 'image'):
if commandName == 'vkCmdPipelineBarrier':
return '"UNASSIGNED-vkCmdPipelineBarrier-commandBuffer-commonparent"'
elif commandName == 'vkCmdWaitEvents':
return '"UNASSIGNED-vkCmdWaitEvents-commandBuffer-commonparent"'
if (structName == 'VkBufferMemoryBarrier2' and memberName == 'buffer') or (structName == 'VkImageMemoryBarrier2' and memberName == 'image'):
if commandName.startswith('vkCmdPipelineBarrier2'):
return '"UNASSIGNED-vkCmdPipelineBarrier2-commandBuffer-commonparent"'
elif commandName.startswith('vkCmdWaitEvents2'):
return '"UNASSIGNED-vkCmdWaitEvents2-commandBuffer-commonparent"'
elif commandName.startswith('vkCmdSetEvent2'):
return '"UNASSIGNED-vkCmdSetEvent2-commandBuffer-commonparent"'
# Single command calls same struct through 2 different structs
if commandName == 'vkQueueBindSparse':
if structName == 'VkSparseMemoryBind' and memberName == 'memory':
return '"UNASSIGNED-VkSparseMemoryBind-memory-parent"'
if structName == 'VkSparseImageMemoryBind' and memberName == 'memory':
return '"UNASSIGNED-VkSparseImageMemoryBind-memory-parent"'
if structName == 'VkSparseBufferMemoryBindInfo' and memberName == 'buffer':
return '"UNASSIGNED-VkSparseBufferMemoryBindInfo-buffer-parent"'
if structName == 'VkSparseImageOpaqueMemoryBindInfo' and memberName == 'image':
return '"UNASSIGNED-VkSparseImageOpaqueMemoryBindInfo-image-parent"'
if structName == 'VkSparseImageMemoryBindInfo' and memberName == 'image':
return '"UNASSIGNED-VkSparseImageMemoryBindInfo-image-parent"'
if commandName.startswith('vkQueueSubmit2'):
if structName == 'VkSemaphoreSubmitInfo' and memberName == 'semaphore':
return '"UNASSIGNED-VkSemaphoreSubmitInfo-semaphore-parent"'
# Common parents
if structName =='VkRenderPassAttachmentBeginInfo' and memberName == 'pAttachments':
return '"VUID-VkRenderPassBeginInfo-framebuffer-02780"'
return None
def getFieldParentVUID(self, member: Member, structName: str, commandName: str, singleParentVuid: bool) -> str:
if not self.hasFieldParentVUID(member, structName):
return 'kVUIDUndefined'
manualVuid = self.getManualParentVUID(member.name, structName, commandName)
if manualVuid is not None:
return manualVuid
elif singleParentVuid:
return getVUID(self.valid_vuids, f'VUID-{structName}-{member.name}-parent')
else:
return getVUID(self.valid_vuids, f'VUID-{structName}-commonparent')
def getParameterParentVUID(self, parameter: Member, commandName: str, singleParentVuid: bool) -> str:
if not self.hasParameterParentVUID(parameter, commandName):
return 'kVUIDUndefined'
# Replace with alias if one
alias = self.vk.commands[commandName].alias
parent = alias if alias else commandName
if singleParentVuid or (commandName in self.use_parent_instead_of_commonparent_commands):
return getVUID(self.valid_vuids, f'VUID-{parent}-{parameter.name}-parent')
else:
return getVUID(self.valid_vuids, f'VUID-{parent}-commonparent')
# recursively walks struct members (and command params)
# parentName == Struct or Command calling into this
# topCommand == The command called from (when in a struct)
def validateObjects(self, members: list[Member], prefix: str, arrayIndex: int, parentName: str, topCommand: str, errorLoc: str) -> str:
pre_call_validate = ''
index = f'index{str(arrayIndex)}'
arrayIndex += 1
is_struct = (parentName != topCommand)
if is_struct:
handle_types = [x.type for x in members if self.hasFieldParentVUID(x, parentName)]
else:
handle_types = [x.type for x in members if self.hasParameterParentVUID(x, parentName)]
single_parent_vuid = (len(handle_types) == 1)
# Parent type in parent/commonparent VUIDs: Device, PhysicalDevice or Instance
parent_type = 'Device'
for type in handle_types:
current_type = self.vk.handles[type].parent.type
if current_type in ['VK_OBJECT_TYPE_PHYSICAL_DEVICE', 'VK_OBJECT_TYPE_DISPLAY_KHR', 'VK_OBJECT_TYPE_DISPLAY_MODE_KHR']:
parent_type = 'PhysicalDevice'
# continue search in case instance parent exists
elif current_type == 'VK_OBJECT_TYPE_INSTANCE':
parent_type = 'Instance'
# end search
break
# Process any objects in this structure and recurse for any sub-structs in this struct
for member in members:
if member.pointer and not member.const:
continue # ignore output parameters
if member.type in self.vk.handles:
if member.noAutoValidity:
nullAllowed = 'true'
elif member.length:
nullAllowed = str(member.optionalPointer).lower()
else:
nullAllowed = str(member.optional).lower()
param_vuid = self.getParamVUID(member, parentName)
if is_struct:
parent_vuid = self.getFieldParentVUID(member, parentName, topCommand, single_parent_vuid)
else:
parent_vuid = self.getParameterParentVUID(member, parentName, single_parent_vuid)
# Do not generate validation code for the function's dispatchable parameter (the first one).
# Validation of such parameters is always successful based on the model of how the chassis
# dispactches the calls to vvl::base::Device. If invalid handle is used it will cause
# crash/corruption on the chassis level (in GetDispatchKey or later). And if correct handle
# is passed, then due to the mapping done by GetDispatchKey() the handle will belong to the
# retrieved validation object, which guarantees positive result of the parenting test.
function_dispatchable_parameter = not is_struct and member == members[0]
if function_dispatchable_parameter:
chassis_parent_vuid = parent_vuid # keep parent vuid for "Checked by chassis" comment
parent_vuid = 'kVUIDUndefined'
if member.length:
location = f'{errorLoc}.dot(Field::{member.name}, {index})'
countName = f'{prefix}{member.length}'
pre_call_validate += f'''
if (({countName} > 0) && ({prefix}{member.name})) {{
for (uint32_t {index} = 0; {index} < {countName}; ++{index}) {{
skip |= ValidateObject({prefix}{member.name}[{index}], kVulkanObjectType{member.type[2:]}, {nullAllowed}, {param_vuid}, {parent_vuid}, {location});
}}
}}\n'''
elif 'basePipelineHandle' in member.name:
pre_call_validate += f'if (({prefix}flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT) && ({prefix}basePipelineIndex == -1))\n'
manual_vuid_index = parentName + '-' + member.name
param_vuid = self.manual_vuids.get(manual_vuid_index, "kVUIDUndefined")
pre_call_validate += f'skip |= ValidateObject({prefix}{member.name}, kVulkanObjectType{member.type[2:]}, false, {param_vuid}, {parent_vuid}, error_obj.location);\n'
elif function_dispatchable_parameter:
pre_call_validate += f'// Checked by chassis: {member.name}: {param_vuid}\n'
if chassis_parent_vuid != 'kVUIDUndefined':
pre_call_validate += f'// Checked by chassis: {member.name}: {chassis_parent_vuid}\n'
elif param_vuid == 'kVUIDUndefined':
# These cases are 'commonparent' VUs for "non-ignored parameters"
if parent_vuid == 'kVUIDUndefined':
location = f'{errorLoc}.dot(Field::{member.name})'
if parentName == 'VkPhysicalDeviceSurfaceInfo2KHR':
param_vuid = '"VUID-VkPhysicalDeviceSurfaceInfo2KHR-surface-07919"'
pre_call_validate += 'if (!IsExtEnabled(extensions.vk_google_surfaceless_query)) {\n'
else:
pre_call_validate += '// There should be an explicit VU (if not that is a spec bug)\n'
pre_call_validate += '{\n'
pre_call_validate += f'skip |= ValidateObject({prefix}{member.name}, kVulkanObjectType{member.type[2:]}, {nullAllowed}, {param_vuid}, {parent_vuid}, {location});\n'
pre_call_validate += '}\n'
else:
location = f'{errorLoc}.dot(Field::{member.name})'
if self.vk.commands[topCommand].device and self.vk.handles[member.type].instance:
# Use case when for device-level API call we should use instance-level validation object
pre_call_validate += 'auto instance_object_lifetimes = static_cast<Instance*>(dispatch_instance_->GetValidationObject(container_type));\n'
pre_call_validate += f'skip |= instance_object_lifetimes->ValidateObject({prefix}{member.name}, kVulkanObjectType{member.type[2:]}, {nullAllowed}, {param_vuid}, {parent_vuid}, {location});\n'
else:
# TODO: describe in more general way which commands and which parameters to check for poisoned objects
# NOTE: graphics pipeline is not checked because of GPL, ray tracing KHR is also in the manual code
check_poisoned_layout = (topCommand in ['vkCreateComputePipelines', 'vkCreateRayTracingPipelinesNV', 'vkCreateDataGraphPipelinesARM']) and member.name == 'layout'
check_poisoned_layout = check_poisoned_layout or (topCommand == 'vkCmdBindPipeline' and member.name == 'pipeline')
if check_poisoned_layout:
pre_call_validate += f'skip |= ValidateObject({prefix}{member.name}, kVulkanObjectType{member.type[2:]}, {nullAllowed}, false, {param_vuid}, {parent_vuid}, {location});\n'
else:
pre_call_validate += f'skip |= ValidateObject({prefix}{member.name}, kVulkanObjectType{member.type[2:]}, {nullAllowed}, {param_vuid}, {parent_vuid}, {location});\n'
# Handle Structs that contain objects at some level
elif member.type in self.vk.structs:
nested_struct = []
struct = self.vk.structs[member.type]
# Structs at first level will have an object
contains_object = self.structContainsObject(struct)
# Struct Array
if member.length is not None:
# Update struct prefix
nested_struct.append(f'if ({prefix}{member.name}) {{\n')
nested_struct.append(f' for (uint32_t {index} = 0; {index} < {prefix}{member.length}; ++{index}) {{\n')
new_error_loc = f'{index}_loc'
nested_struct.append(f'[[maybe_unused]] const Location {new_error_loc} = {errorLoc}.dot(Field::{member.name}, {index});\n')
new_prefix = f'{prefix}{member.name}[{index}].'
# Single Struct Pointer
elif member.pointer:
# Update struct prefix
new_prefix = f'{prefix}{member.name}->'
# Declare safe_VarType for struct
nested_struct.append(f'if ({prefix}{member.name}) {{\n')
new_error_loc = f'{member.name}_loc'
nested_struct.append(f' [[maybe_unused]] const Location {new_error_loc} = {errorLoc}.dot(Field::{member.name});\n')
# Single Nested Struct
else:
# Update struct prefix
new_prefix = f'{prefix}{member.name}.'
new_error_loc = f'{member.name}_loc'
nested_struct.append(f'[[maybe_unused]] const Location {new_error_loc} = {errorLoc}.dot(Field::{member.name});\n')
# Process sub-structs
if contains_object:
nested_struct.append(self.validateObjects(struct.members, new_prefix, arrayIndex, member.type, topCommand, new_error_loc))
contains_pNext = False
if struct.extendedBy:
guard_helper = PlatformGuardHelper()
for extendedBy in struct.extendedBy:
extended_struct = self.vk.structs[extendedBy]
extended_members = [x for x in extended_struct.members if x.type in self.vk.handles]
if not extended_members:
continue
contains_pNext = True
nested_struct.extend(guard_helper.add_guard(extended_struct.protect))
nested_struct.append(f'if ([[maybe_unused]] auto pNext = vku::FindStructInPNextChain<{extendedBy}>({new_prefix}pNext)) {{\n')
nested_struct.append(f' [[maybe_unused]] const Location pNext_loc = {new_error_loc}.pNext(Struct::{extendedBy});\n')
nested_struct.append(self.validateObjects(extended_members, 'pNext->', arrayIndex + 1, extendedBy, topCommand, 'pNext_loc'))
nested_struct.append('}\n')
nested_struct.extend(guard_helper.add_guard(None))
# Close indentation
if member.length is not None:
nested_struct.append('}\n')
nested_struct.append('}\n')
elif member.pointer:
nested_struct.append('}\n')
# Only print if called into validateObjects
if contains_object or contains_pNext:
pre_call_validate += "".join(nested_struct)
return pre_call_validate
#
# For a particular API, generate the object handling code
def generateFunctionBody(self, command: Command):
pre_call_validate = ''
pre_call_record = ''
post_call_record = ''
isGetCreate = 'vkGet' in command.name and command.params[-1].pointer and not command.params[-1].const
isCreate = createObject(command.name) or isGetCreate
isDestroy = destroyObject(command.name)
# TODO - we need to wrap with autogen list here for header to still build the function definition,
# but this function is being used in the header to duplicate work to know if the function will be used
if (command.name not in self.no_autogen_list):
pre_call_validate += self.validateObjects(command.params, '', 0, command.name, command.name, 'error_obj.location')
# Handle object create operations if last parameter is created by this call
if isCreate:
handle_type = command.params[-1].type
partial_success_commands = ['vkCreateGraphicsPipelines', 'vkCreateComputePipelines', 'vkCreateRayTracingPipelinesNV', 'vkCreateRayTracingPipelinesKHR', 'vkCreateShadersEXT', 'vkCreateDataGraphPipelinesARM']
if handle_type in self.vk.handles:
# Check for special case where multiple handles are returned
objectArray = command.params[-1].length is not None
if objectArray:
if command.name in partial_success_commands:
post_call_record += 'if (VK_ERROR_VALIDATION_FAILED_EXT == record_obj.result) return;\n'
post_call_record += f'if ({command.params[-1].name}) {{\n'
countIsPointer = '*' if command.params[-2].type == 'uint32_t' and command.params[-2].pointer else ''
post_call_record += f'for (uint32_t index = 0; index < {countIsPointer}{command.params[-1].length}; index++) {{\n'
if command.name in partial_success_commands:
if command.name == 'vkCreateShadersEXT':
post_call_record += 'if (!pShaders[index]) continue;\n'
else:
post_call_record += 'if (!pPipelines[index]) continue;\n'
allocator = command.params[-2].name if command.params[-2].type == 'VkAllocationCallbacks' else 'nullptr'
objectDest = f'{command.params[-1].name}[index]' if objectArray else f'*{command.params[-1].name}'
location = f'record_obj.location.dot(Field::{command.params[-1].name}, index)' if objectArray else 'record_obj.location'
parent = command.params[0].name
post_call_record += f'tracker.CreateObject({objectDest}, kVulkanObjectType{handle_type[2:]}, {allocator}, {location}, {parent});\n'
if objectArray:
post_call_record += '}\n'
post_call_record += '}\n'
# Physical device groups are not handles, but a set of handles, they need to be tracked as well
elif handle_type == 'VkPhysicalDeviceGroupProperties':
post_call_record += f'''
if ({command.params[-1].name}) {{
const RecordObject record_obj(vvl::Func::vkEnumeratePhysicalDevices, VK_SUCCESS);
for (uint32_t device_group_index = 0; device_group_index < *{command.params[-2].name}; device_group_index++) {{
PostCallRecordEnumeratePhysicalDevices({command.params[0].name}, &{command.params[-1].name}[device_group_index].physicalDeviceCount, {command.params[-1].name}[device_group_index].physicalDevices, record_obj);
}}
}}\n'''
# Handle object destroy operations
if isDestroy:
# Check for special case where multiple handles are returned
handle_param = command.params[-1] if 'ReleasePerformanceConfigurationINTEL' in command.name else command.params[-2]
allocator = 'nullptr' if 'ReleasePerformanceConfigurationINTEL' in command.name else 'pAllocator'
compatallocVUID = self.getAllocVUID(handle_param, "compatalloc")
nullallocVUID = self.getAllocVUID(handle_param, "nullalloc")
if handle_param.type in self.vk.handles:
# Call Destroy a single time
pre_call_validate += f'skip |= ValidateDestroyObject({handle_param.name}, kVulkanObjectType{handle_param.type[2:]}, {allocator}, {compatallocVUID}, {nullallocVUID}, error_obj.location);\n'
pre_call_record += f'RecordDestroyObject({handle_param.name}, kVulkanObjectType{handle_param.type[2:]}, record_obj.location);\n'
return pre_call_validate, pre_call_record, post_call_record