| #!/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 |
| |