| #!/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. |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import os |
| import re |
| from vulkan_object import Struct, Member |
| from base_generator import BaseGenerator |
| from generators.generator_utils import PlatformGuardHelper |
| |
| class SafeStructOutputGenerator(BaseGenerator): |
| def __init__(self): |
| BaseGenerator.__init__(self) |
| |
| # Will skip completly, mainly used for things that have no one consuming the safe struct |
| self.no_autogen = [ |
| # These WSI struct have nothing deep to copy, except the WSI releated field which we can't make a copy |
| 'VkXlibSurfaceCreateInfoKHR', |
| 'VkXcbSurfaceCreateInfoKHR', |
| 'VkWaylandSurfaceCreateInfoKHR', |
| 'VkAndroidSurfaceCreateInfoKHR', |
| 'VkWin32SurfaceCreateInfoKHR', |
| 'VkIOSSurfaceCreateInfoMVK', |
| 'VkMacOSSurfaceCreateInfoMVK', |
| 'VkMetalSurfaceCreateInfoEXT' |
| ] |
| |
| # Will skip generating the source logic (to be implemented in vk_safe_struct_manual.cpp) |
| # The header will still be generated |
| self.manual_source = [ |
| # This needs to know if we're doing a host or device build, logic becomes complex and very specialized |
| 'VkAccelerationStructureBuildGeometryInfoKHR', |
| 'VkAccelerationStructureGeometryKHR', |
| # Special case because its pointers may be non-null but ignored |
| 'VkGraphicsPipelineCreateInfo', |
| # Special case because it has custom construct parameters |
| 'VkPipelineViewportStateCreateInfo', |
| ] |
| |
| # For abstract types just want to save the pointer away |
| # since we cannot make a copy. |
| self.abstract_types = [ |
| 'AHardwareBuffer', |
| '_screen_context', |
| '_screen_window', |
| '_screen_buffer' |
| ] |
| |
| # These 'data' union are decided by the 'type' in the same parent struct |
| self.union_of_pointers = [ |
| 'VkDescriptorDataEXT', |
| 'VkIndirectCommandsTokenDataEXT', |
| 'VkIndirectExecutionSetInfoEXT', |
| 'VkResourceDescriptorDataEXT', |
| ] |
| self.union_of_pointer_callers = [ |
| 'VkDescriptorGetInfoEXT', |
| 'VkResourceDescriptorInfoEXT', |
| ] |
| |
| # Will update the the function interface |
| self.custom_construct_params = { |
| # vku::safe::GraphicsPipelineCreateInfo needs to know if subpass has color and\or depth\stencil attachments to use its pointers |
| 'VkGraphicsPipelineCreateInfo' : |
| ', const bool uses_color_attachment, const bool uses_depthstencil_attachment', |
| # vku::safe::PipelineViewportStateCreateInfo needs to know if viewport and scissor is dynamic to use its pointers |
| 'VkPipelineViewportStateCreateInfo' : |
| ', const bool is_dynamic_viewports, const bool is_dynamic_scissors', |
| # vku::safe::AccelerationStructureBuildGeometryInfoKHR needs to know if we're doing a host or device build |
| 'VkAccelerationStructureBuildGeometryInfoKHR' : |
| ', const bool is_host, const VkAccelerationStructureBuildRangeInfoKHR *build_range_infos', |
| # vku::safe::AccelerationStructureGeometryKHR needs to know if we're doing a host or device build |
| 'VkAccelerationStructureGeometryKHR' : |
| ', const bool is_host, const VkAccelerationStructureBuildRangeInfoKHR *build_range_info', |
| } |
| |
| def isInPnextChain(self, struct: Struct) -> bool: |
| # Can appear in VkPipelineCreateInfoKHR::pNext even though it isn't listed in the xml structextends attribute |
| # VUID-VkPipelineCreateInfoKHR-pNext-09604 |
| pipeline_create_infos = [ |
| 'VkGraphicsPipelineCreateInfo', |
| 'VkExecutionGraphPipelineCreateInfoAMDX', |
| 'VkRayTracingPipelineCreateInfoKHR', |
| 'VkComputePipelineCreateInfo', |
| ] |
| return struct.extends or struct.name in pipeline_create_infos |
| |
| # Determine if a structure needs a safe_struct helper function |
| # That is, it has an sType or one of its members is a pointer |
| def needSafeStruct(self, struct: Struct) -> bool: |
| if struct.name in self.no_autogen: |
| return False |
| if struct.name in self.union_of_pointers: |
| return False |
| if 'VkBase' in struct.name: |
| return False # Ingore structs like VkBaseOutStructure |
| if struct.sType is not None: |
| return True |
| for member in struct.members: |
| if member.pointer: |
| return True |
| # The VK_EXT_sample_locations design created edge case, easiest to handle here |
| if struct.name == 'VkAttachmentSampleLocationsEXT' or struct.name == 'VkSubpassSampleLocationsEXT': |
| return True |
| return False |
| |
| def containsObjectHandle(self, member: Member) -> bool: |
| if member.type in self.vk.handles: |
| return True |
| if member.type in self.vk.structs: |
| for subMember in self.vk.structs[member.type].members: |
| if self.containsObjectHandle(subMember): |
| return True |
| return False |
| |
| def typeContainsObjectHandle(self, handle_type: str, dispatchable: bool) -> bool: |
| if handle_type in self.vk.handles: |
| if dispatchable == self.vk.handles[handle_type].dispatchable: |
| return True |
| # if handle_type is a struct, search its members |
| if handle_type in self.vk.structs: |
| struct = self.vk.structs[handle_type] |
| for member in [x for x in struct.members if x.type in self.vk.handles]: |
| if dispatchable == self.vk.handles[member.type].dispatchable: |
| return True |
| return False |
| |
| def generate(self): |
| # Should be fixed in 1.4.310 headers |
| # https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/7196 |
| manual_protect = ["VkCudaModuleNV", "VkCudaFunctionNV", "VkCudaModuleCreateInfoNV", "VkCudaFunctionCreateInfoNV", "VkCudaLaunchInfoNV", "VkPhysicalDeviceCudaKernelLaunchFeaturesNV", "VkPhysicalDeviceCudaKernelLaunchPropertiesNV", "VkSetPresentConfigNV", "VkPhysicalDevicePresentMeteringFeaturesNV"] |
| for struct in [x for x in self.vk.structs.values() if x.name in manual_protect]: |
| struct.protect = "VK_ENABLE_BETA_EXTENSIONS" |
| |
| 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. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| ****************************************************************************/\n''') |
| self.write('// NOLINTBEGIN') # Wrap for clang-tidy to ignore |
| |
| if self.filename == 'vk_safe_struct.hpp': |
| self.generateHeader() |
| elif self.filename == 'vk_safe_struct_utils.cpp': |
| self.generateUtil() |
| elif self.filename.startswith('vk_safe_struct_'): |
| 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 convertName(self, vk_name): |
| return "safe_" + vk_name |
| |
| def generateHeader(self): |
| out = [] |
| out.append(''' |
| #pragma once |
| #include <vulkan/vulkan.h> |
| #include <algorithm> |
| |
| #include <vulkan/utility/vk_safe_struct_utils.hpp> |
| |
| namespace vku { |
| \n''') |
| |
| guard_helper = PlatformGuardHelper() |
| for struct in [x for x in self.vk.structs.values() if self.needSafeStruct(x)]: |
| safe_name = self.convertName(struct.name) |
| out.extend(guard_helper.add_guard(struct.protect)) |
| out.append(f'{"union" if struct.union else "struct"} {safe_name} {{\n') |
| # Can only initialize first member of an Union |
| canInitialize = True |
| copy_pnext = ', bool copy_pnext = true' if struct.sType is not None else '' |
| for member in struct.members: |
| if member.type in self.vk.structs: |
| if self.needSafeStruct(self.vk.structs[member.type]): |
| safe_member_type = self.convertName(member.type) |
| if member.pointer: |
| pointer = '*' * member.cDeclaration.count('*') |
| brackets = '' if struct.union else '{}' |
| out.append(f'{safe_member_type}{pointer} {member.name}{brackets};\n') |
| else: |
| out.append(f'{safe_member_type} {member.name};\n') |
| continue |
| |
| explicitInitialize = member.pointer and 'PFN_' not in member.type and canInitialize |
| initialize = '{}' if explicitInitialize else '' |
| # Prevents union from initializing agian |
| canInitialize = not struct.union if explicitInitialize else canInitialize |
| |
| if member.length and self.containsObjectHandle(member) and not member.fixedSizeArray: |
| out.append(f' {member.type}* {member.name}{initialize};\n') |
| else: |
| out.append(f'{member.cDeclaration}{initialize};\n') |
| |
| constructParam = self.custom_construct_params.get(struct.name, '') |
| out.append(f''' |
| {safe_name}(const {struct.name}* in_struct{constructParam}, PNextCopyState* copy_state = {{}}{copy_pnext}); |
| {safe_name}(const {safe_name}& copy_src); |
| {safe_name}& operator=(const {safe_name}& copy_src); |
| {safe_name}(); |
| ~{safe_name}(); |
| void initialize(const {struct.name}* in_struct{constructParam}, PNextCopyState* copy_state = {{}}); |
| void initialize(const {safe_name}* copy_src, PNextCopyState* copy_state = {{}}); |
| {struct.name} *ptr() {{ return reinterpret_cast<{struct.name} *>(this); }} |
| {struct.name} const *ptr() const {{ return reinterpret_cast<{struct.name} const *>(this); }} |
| ''') |
| |
| if struct.name == 'VkShaderModuleCreateInfo': |
| out.append(''' |
| // Primarily intended for use by GPUAV when replacing shader module code with instrumented code |
| template<typename Container> |
| void SetCode(const Container &code) { |
| delete[] pCode; |
| codeSize = static_cast<uint32_t>(code.size() * sizeof(uint32_t)); |
| pCode = new uint32_t[code.size()]; |
| std::copy(&code.front(), &code.back() + 1, const_cast<uint32_t*>(pCode)); |
| } |
| ''') |
| out.append('};\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append(''' |
| // Safe struct that spans NV and KHR VkRayTracingPipelineCreateInfo structures. |
| // It is a VkRayTracingPipelineCreateInfoKHR and supports construction from |
| // a VkRayTracingPipelineCreateInfoNV. |
| class safe_VkRayTracingPipelineCreateInfoCommon : public safe_VkRayTracingPipelineCreateInfoKHR { |
| public: |
| safe_VkRayTracingPipelineCreateInfoCommon() : safe_VkRayTracingPipelineCreateInfoKHR() {} |
| safe_VkRayTracingPipelineCreateInfoCommon(const VkRayTracingPipelineCreateInfoNV *pCreateInfo) |
| : safe_VkRayTracingPipelineCreateInfoKHR() { |
| initialize(pCreateInfo); |
| } |
| safe_VkRayTracingPipelineCreateInfoCommon(const VkRayTracingPipelineCreateInfoKHR *pCreateInfo) |
| : safe_VkRayTracingPipelineCreateInfoKHR(pCreateInfo) {} |
| |
| void initialize(const VkRayTracingPipelineCreateInfoNV *pCreateInfo); |
| void initialize(const VkRayTracingPipelineCreateInfoKHR *pCreateInfo); |
| uint32_t maxRecursionDepth = 0; // NV specific |
| }; |
| ''') |
| out.append(''' |
| } // namespace vku |
| ''') |
| self.write("".join(out)) |
| |
| def generateUtil(self): |
| out = [] |
| out.append(''' |
| #include <vulkan/vk_layer.h> |
| #include <vulkan/utility/vk_safe_struct.hpp> |
| |
| #include <vector> |
| #include <cstring> |
| |
| namespace vku { |
| char *SafeStringCopy(const char *in_string) { |
| if (nullptr == in_string) return nullptr; |
| size_t len = std::strlen(in_string); |
| char* dest = new char[len + 1]; |
| dest[len] = '\\0'; |
| std::memcpy(dest, in_string, len); |
| return dest; |
| } |
| |
| ''') |
| out.append(''' |
| // clang-format off |
| void *SafePnextCopy(const void *pNext, PNextCopyState* copy_state) { |
| void *first_pNext{}; |
| VkBaseOutStructure *prev_pNext{}; |
| void *safe_pNext{}; |
| |
| while (pNext) { |
| const VkBaseOutStructure *header = reinterpret_cast<const VkBaseOutStructure *>(pNext); |
| |
| switch (header->sType) { |
| // Add special-case code to copy beloved secret loader structs |
| // Special-case Loader Instance Struct passed to/from layer in pNext chain |
| case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO: { |
| VkLayerInstanceCreateInfo *struct_copy = new VkLayerInstanceCreateInfo; |
| // TODO: Uses original VkLayerInstanceLink* chain, which should be okay for our uses |
| memcpy(struct_copy, pNext, sizeof(VkLayerInstanceCreateInfo)); |
| safe_pNext = struct_copy; |
| break; |
| } |
| // Special-case Loader Device Struct passed to/from layer in pNext chain |
| case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO: { |
| VkLayerDeviceCreateInfo *struct_copy = new VkLayerDeviceCreateInfo; |
| // TODO: Uses original VkLayerDeviceLink*, which should be okay for our uses |
| memcpy(struct_copy, pNext, sizeof(VkLayerDeviceCreateInfo)); |
| safe_pNext = struct_copy; |
| break; |
| } |
| ''') |
| guard_helper = PlatformGuardHelper() |
| for struct in filter(self.isInPnextChain, self.vk.structs.values()): |
| safe_name = self.convertName(struct.name) |
| out.extend(guard_helper.add_guard(struct.protect)) |
| out.append(f' case {struct.sType}:\n') |
| out.append(f' safe_pNext = new {safe_name}(reinterpret_cast<const {struct.name} *>(pNext), copy_state, false);\n') |
| out.append(' break;\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append(''' |
| default: |
| break; |
| } |
| if (!first_pNext) { |
| first_pNext = safe_pNext; |
| } |
| pNext = header->pNext; |
| if (prev_pNext && safe_pNext) { |
| prev_pNext->pNext = (VkBaseOutStructure*)safe_pNext; |
| } |
| if (safe_pNext) { |
| prev_pNext = (VkBaseOutStructure*)safe_pNext; |
| } |
| safe_pNext = nullptr; |
| } |
| |
| return first_pNext; |
| } |
| |
| void FreePnextChain(const void *pNext) { |
| // The pNext parameter is const for convenience, since it is called by code |
| // for many structures where the pNext field is const. |
| void *current = const_cast<void*>(pNext); |
| while (current) { |
| auto header = reinterpret_cast<VkBaseOutStructure *>(current); |
| void *next = header->pNext; |
| // prevent destructors from recursing behind our backs. |
| header->pNext = nullptr; |
| |
| switch (header->sType) { |
| // Special-case Loader Instance Struct passed to/from layer in pNext chain |
| case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO: |
| delete reinterpret_cast<VkLayerInstanceCreateInfo *>(current); |
| break; |
| // Special-case Loader Device Struct passed to/from layer in pNext chain |
| case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO: |
| delete reinterpret_cast<VkLayerDeviceCreateInfo *>(current); |
| break; |
| ''') |
| |
| for struct in filter(self.isInPnextChain, self.vk.structs.values()): |
| safe_name = self.convertName(struct.name) |
| out.extend(guard_helper.add_guard(struct.protect)) |
| out.append(f' case {struct.sType}:\n') |
| out.append(f' delete reinterpret_cast<{safe_name} *>(header);\n') |
| out.append(' break;\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append(''' |
| default: |
| break; |
| } |
| current = next; |
| } |
| }''') |
| out.append('// clang-format on\n') |
| out.append(''' |
| } // namespace vku |
| ''') |
| self.write("".join(out)) |
| |
| def generateSource(self): |
| out = [] |
| out.append(''' |
| #include <vulkan/utility/vk_safe_struct.hpp> |
| #include <vulkan/utility/vk_struct_helper.hpp> |
| |
| #include <cstddef> |
| #include <cstring> |
| |
| namespace vku { |
| ''') |
| |
| custom_defeault_construct_txt = {} |
| |
| custom_construct_txt = { |
| # VkWriteDescriptorSet is special case because pointers may be non-null but ignored |
| 'VkWriteDescriptorSet' : ''' |
| switch (descriptorType) { |
| case VK_DESCRIPTOR_TYPE_SAMPLER: |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM: |
| case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM: |
| if (descriptorCount && in_struct->pImageInfo) { |
| pImageInfo = new VkDescriptorImageInfo[descriptorCount]; |
| for (uint32_t i = 0; i < descriptorCount; ++i) { |
| pImageInfo[i] = in_struct->pImageInfo[i]; |
| } |
| } |
| break; |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| if (descriptorCount && in_struct->pBufferInfo) { |
| pBufferInfo = new VkDescriptorBufferInfo[descriptorCount]; |
| for (uint32_t i = 0; i < descriptorCount; ++i) { |
| pBufferInfo[i] = in_struct->pBufferInfo[i]; |
| } |
| } |
| break; |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| if (descriptorCount && in_struct->pTexelBufferView) { |
| pTexelBufferView = new VkBufferView[descriptorCount]; |
| for (uint32_t i = 0; i < descriptorCount; ++i) { |
| pTexelBufferView[i] = in_struct->pTexelBufferView[i]; |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| ''', |
| 'VkShaderModuleCreateInfo' : ''' |
| if (in_struct->pCode) { |
| pCode = reinterpret_cast<uint32_t *>(new uint8_t[codeSize]); |
| memcpy((void *)pCode, (void *)in_struct->pCode, codeSize); |
| } |
| ''', |
| # VkFrameBufferCreateInfo is special case because its pAttachments pointer may be non-null but ignored |
| 'VkFramebufferCreateInfo' : ''' |
| if (attachmentCount && in_struct->pAttachments && !(flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT)) { |
| pAttachments = new VkImageView[attachmentCount]; |
| for (uint32_t i = 0; i < attachmentCount; ++i) { |
| pAttachments[i] = in_struct->pAttachments[i]; |
| } |
| } |
| ''', |
| # VkDescriptorSetLayoutBinding is special case because its pImmutableSamplers pointer may be non-null but ignored |
| 'VkDescriptorSetLayoutBinding' : ''' |
| const bool sampler_type = in_struct->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || in_struct->descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; |
| if (descriptorCount && in_struct->pImmutableSamplers && sampler_type) { |
| pImmutableSamplers = new VkSampler[descriptorCount]; |
| for (uint32_t i = 0; i < descriptorCount; ++i) { |
| pImmutableSamplers[i] = in_struct->pImmutableSamplers[i]; |
| } |
| } |
| ''', |
| 'VkPipelineRenderingCreateInfo': ''' |
| bool custom_init = copy_state && copy_state->init; |
| if (custom_init) { |
| custom_init = copy_state->init(reinterpret_cast<VkBaseOutStructure*>(this), reinterpret_cast<const VkBaseOutStructure*>(in_struct)); |
| } |
| if (!custom_init) { |
| // The custom iniitalization was not used, so do the regular initialization |
| if (in_struct->pColorAttachmentFormats) { |
| pColorAttachmentFormats = new VkFormat[in_struct->colorAttachmentCount]; |
| memcpy ((void *)pColorAttachmentFormats, (void *)in_struct->pColorAttachmentFormats, sizeof(VkFormat)*in_struct->colorAttachmentCount); |
| } |
| } |
| ''', |
| # TODO: VkPushDescriptorSetWithTemplateInfo needs a custom constructor to handle pData |
| # https://github.com/KhronosGroup/Vulkan-Utility-Libraries/issues/193 |
| 'VkPushDescriptorSetWithTemplateInfo': ''' |
| ''', |
| } |
| |
| custom_copy_txt = { |
| 'VkFramebufferCreateInfo' : ''' |
| pNext = SafePnextCopy(copy_src.pNext); |
| if (attachmentCount && copy_src.pAttachments && !(flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT)) { |
| pAttachments = new VkImageView[attachmentCount]; |
| for (uint32_t i = 0; i < attachmentCount; ++i) { |
| pAttachments[i] = copy_src.pAttachments[i]; |
| } |
| } |
| ''', |
| 'VkPipelineRenderingCreateInfo': ''' |
| if (copy_src.pColorAttachmentFormats) { |
| pColorAttachmentFormats = new VkFormat[copy_src.colorAttachmentCount]; |
| memcpy ((void *)pColorAttachmentFormats, (void *)copy_src.pColorAttachmentFormats, sizeof(VkFormat)*copy_src.colorAttachmentCount); |
| } |
| ''' |
| } |
| |
| custom_destruct_txt = { |
| 'VkShaderModuleCreateInfo' : ''' |
| if (pCode) |
| delete[] reinterpret_cast<const uint8_t *>(pCode); |
| ''', |
| } |
| |
| member_init_transforms = { |
| 'queueFamilyIndexCount': lambda m: f'{m.name}(0)' |
| } |
| |
| def qfi_construct(item, member): |
| true_index_setter = lambda i: f'{i}queueFamilyIndexCount = in_struct->queueFamilyIndexCount;\n' |
| false_index_setter = lambda i: f'{i}queueFamilyIndexCount = 0;\n' |
| if item.name == 'VkSwapchainCreateInfoKHR': |
| return (f'(in_struct->imageSharingMode == VK_SHARING_MODE_CONCURRENT) && in_struct->{member.name}', true_index_setter, false_index_setter) |
| else: |
| return (f'(in_struct->sharingMode == VK_SHARING_MODE_CONCURRENT) && in_struct->{member.name}', true_index_setter, false_index_setter) |
| |
| # map of: |
| # <member name>: function(item, member) -> (condition, true statement, false statement) |
| member_construct_conditions = { |
| 'pQueueFamilyIndices': qfi_construct |
| } |
| |
| # Find what types of safe structs need to be generated based on output file name |
| splitRegex = r'.*'; |
| if self.filename.endswith('_khr.cpp'): |
| splitRegex = r'.*KHR$' |
| elif self.filename.endswith('_ext.cpp'): |
| splitRegex = r'.*EXT$' |
| elif self.filename.endswith('_vendor.cpp'): |
| splitRegex = r'^(?!.*(KHR|EXT)$).*[A-Z]$' # Matches all words finishing with an upper case letter, but not ending with KHRor EXT |
| else: # elif self.filename.endswith('_core.cpp'): |
| splitRegex = r'.*[a-z0-9]$' |
| |
| guard_helper = PlatformGuardHelper() |
| |
| for struct in [x for x in self.vk.structs.values() if self.needSafeStruct(x) and x.name not in self.manual_source and re.match(splitRegex, x.name)]: |
| out.extend(guard_helper.add_guard(struct.protect)) |
| |
| init_list = '' # list of members in struct constructor initializer |
| default_init_list = '' # Default constructor just inits ptrs to nullptr in initializer |
| init_func_txt = '' # Txt for initialize() function that takes struct ptr and inits members |
| construct_txt = '' # Body of constuctor as well as body of initialize() func following init_func_txt |
| destruct_txt = '' |
| |
| has_pnext = struct.sType is not None |
| copy_pnext = '' |
| copy_pnext_if = '' |
| copy_strings = '' |
| for member in struct.members: |
| m_type = member.type |
| m_type_safe = False |
| m_shallow_copy = False |
| |
| if member.pointer and 'PFN_' in member.type: |
| m_shallow_copy = True |
| |
| if member.name == 'pNext': |
| copy_pnext = 'pNext = SafePnextCopy(in_struct->pNext, copy_state);\n' |
| copy_pnext_if = ''' |
| if (copy_pnext) { |
| pNext = SafePnextCopy(in_struct->pNext, copy_state); |
| }''' |
| if member.type in self.vk.structs and self.needSafeStruct(self.vk.structs[member.type]): |
| m_type = self.convertName(member.type) |
| m_type_safe = True; |
| |
| if member.pointer and not m_type_safe and not m_shallow_copy and not self.typeContainsObjectHandle(member.type, False): |
| # Ptr types w/o a safe_struct, for non-null case need to allocate new ptr and copy data in |
| if m_type in ['void', 'char']: |
| if member.name != 'pNext': |
| if m_type == 'char': |
| # Create deep copies of strings |
| if member.length: |
| copy_strings += f''' |
| if (in_struct->{member.length} > 0) {{ |
| char **tmp_{member.name} = new char *[in_struct->{member.length}]; |
| for (uint32_t i = 0; i < {member.length}; ++i) {{ |
| tmp_{member.name}[i] = SafeStringCopy(in_struct->{member.name}[i]); |
| }} |
| {member.name} = tmp_{member.name}; |
| }} else {{ |
| {member.name} = nullptr; |
| }} |
| ''' |
| |
| destruct_txt += f''' |
| if ({member.name}) {{ |
| for (uint32_t i = 0; i < {member.length}; ++i) {{ |
| delete [] {member.name}[i]; |
| }} |
| delete [] {member.name}; |
| }}''' |
| else: |
| copy_strings += f'{member.name} = SafeStringCopy(in_struct->{member.name});\n' |
| destruct_txt += f'if ({member.name}) delete [] {member.name};\n' |
| else: |
| # We need a deep copy of pData / dataSize combos |
| if member.name == 'pData': |
| init_list += f'\n {member.name}(nullptr),' |
| construct_txt += ''' |
| if (in_struct->pData != nullptr) { |
| auto temp = new std::byte[in_struct->dataSize]; |
| std::memcpy(temp, in_struct->pData, in_struct->dataSize); |
| pData = temp; |
| } |
| ''' |
| |
| destruct_txt += ''' |
| if (pData != nullptr) { |
| auto temp = reinterpret_cast<const std::byte*>(pData); |
| delete [] temp; |
| } |
| ''' |
| else: |
| init_list += f'\n{member.name}(in_struct->{member.name}),' |
| init_func_txt += f'{member.name} = in_struct->{member.name};\n' |
| default_init_list += f'\n{member.name}(nullptr),' |
| elif member.name == 'ppUsageCounts': |
| # This is the same issue above, but instead of void/char* we have custom Vulkan types |
| # Currently just search for 'ppUsageCounts' until something that isn't also needs this, then make it more general |
| default_init_list += f'\n{member.name}(nullptr),' |
| init_list += f'\n{member.name}(nullptr),' |
| init_func_txt += f'{member.name} = nullptr;\n' |
| |
| construct_txt += f''' |
| if (in_struct->{member.name}) {{ |
| {m_type}** pointer_array = new {m_type}*[in_struct->usageCountsCount]; |
| for (uint32_t i = 0; i < in_struct->usageCountsCount; ++i) {{ |
| pointer_array[i] = new {m_type}(*in_struct->{member.name}[i]); |
| }} |
| {member.name} = pointer_array; |
| }} |
| ''' |
| |
| destruct_txt += f''' |
| if ({member.name}) {{ |
| for (uint32_t i = 0; i < usageCountsCount; ++i) {{ |
| delete {member.name}[i]; |
| }} |
| delete[] {member.name}; |
| }} |
| ''' |
| else: |
| default_init_list += f'\n{member.name}(nullptr),' |
| init_list += f'\n{member.name}(nullptr),' |
| if m_type in self.abstract_types: |
| construct_txt += f'{member.name} = in_struct->{member.name};\n' |
| else: |
| init_func_txt += f'{member.name} = nullptr;\n' |
| if not member.fixedSizeArray and (member.length is None or '/' in member.length): |
| construct_txt += f''' |
| if (in_struct->{member.name}) {{ |
| {member.name} = new {m_type}(*in_struct->{member.name}); |
| }} |
| ''' |
| destruct_txt += f'if ({member.name})\n' |
| destruct_txt += f' delete {member.name};\n' |
| else: |
| # Prepend struct members with struct name |
| decorated_length = member.length |
| for other_member in struct.members: |
| decorated_length = re.sub(r'\b({})\b'.format(other_member.name), r'in_struct->\1', decorated_length) |
| try: |
| concurrent_clause = member_construct_conditions[member.name](struct, member) |
| except: |
| concurrent_clause = (f'in_struct->{member.name}', lambda x: '') |
| construct_txt += f''' |
| if ({concurrent_clause[0]}) {{ |
| {member.name} = new {m_type}[{decorated_length}]; |
| memcpy ((void *){member.name}, (void *)in_struct->{member.name}, sizeof({m_type})*{decorated_length}); |
| {concurrent_clause[1](' ')}''' |
| if len(concurrent_clause) > 2: |
| construct_txt += '} else {\n' |
| construct_txt += concurrent_clause[2](' ') |
| construct_txt += '}\n' |
| destruct_txt += f'if ({member.name})\n' |
| destruct_txt += f' delete[] {member.name};\n' |
| elif member.fixedSizeArray or member.length is not None: |
| if member.fixedSizeArray: |
| construct_txt += f''' |
| for (uint32_t i = 0; i < {member.fixedSizeArray[0]}; ++i) {{ |
| {member.name}[i] = in_struct->{member.name}[i]; |
| }} |
| ''' |
| else: |
| # Init array ptr to NULL |
| default_init_list += f'\n{member.name}(nullptr),' |
| init_list += f'\n{member.name}(nullptr),' |
| init_func_txt += f'{member.name} = nullptr;\n' |
| array_element = f'in_struct->{member.name}[i]' |
| if member.type in self.vk.structs and self.needSafeStruct(self.vk.structs[member.type]): |
| array_element = f'{member.type}(&in_struct->safe_{member.name}[i])' |
| construct_txt += f'if ({member.length} && in_struct->{member.name}) {{\n' |
| construct_txt += f' {member.name} = new {m_type}[{member.length}];\n' |
| destruct_txt += f'if ({member.name})\n' |
| destruct_txt += f' delete[] {member.name};\n' |
| construct_txt += f'for (uint32_t i = 0; i < {member.length}; ++i) {{\n' |
| if m_type_safe: |
| construct_txt += f'{member.name}[i].initialize(&in_struct->{member.name}[i]);\n' |
| else: |
| construct_txt += f'{member.name}[i] = {array_element};\n' |
| construct_txt += '}\n' |
| construct_txt += '}\n' |
| elif member.pointer and not m_shallow_copy: |
| default_init_list += f'\n{member.name}(nullptr),' |
| init_list += f'\n{member.name}(nullptr),' |
| init_func_txt += f'{member.name} = nullptr;\n' |
| construct_txt += f'if (in_struct->{member.name})\n' |
| construct_txt += f' {member.name} = new {m_type}(in_struct->{member.name});\n' |
| destruct_txt += f'if ({member.name})\n' |
| destruct_txt += f' delete {member.name};\n' |
| elif m_type_safe and member.type in self.union_of_pointers: |
| init_list += f'\n{member.name}(&in_struct->{member.name}, in_struct->type),' |
| init_func_txt += f'{member.name}.initialize(&in_struct->{member.name}, in_struct->type);\n' |
| elif m_type_safe: |
| init_list += f'\n{member.name}(&in_struct->{member.name}),' |
| init_func_txt += f'{member.name}.initialize(&in_struct->{member.name});\n' |
| else: |
| try: |
| init_list += f'\n{member_init_transforms[member.name](member)},' |
| except: |
| init_list += f'\n{member.name}(in_struct->{member.name}),' |
| init_func_txt += f'{member.name} = in_struct->{member.name};\n' |
| if not struct.union: |
| if member.name == 'sType' and struct.sType: |
| default_init_list += f'\n{member.name}({struct.sType}),' |
| else: |
| default_init_list += f'\n{member.name}(),' |
| if '' != init_list: |
| init_list = init_list[:-1] # hack off final comma |
| |
| if struct.name in custom_construct_txt: |
| construct_txt = custom_construct_txt[struct.name] |
| |
| construct_txt = copy_strings + construct_txt |
| |
| if struct.name in custom_destruct_txt: |
| destruct_txt = custom_destruct_txt[struct.name] |
| |
| copy_pnext_param = '' |
| if has_pnext: |
| copy_pnext_param = ', bool copy_pnext' |
| destruct_txt += ' FreePnextChain(pNext);\n' |
| |
| safe_name = self.convertName(struct.name) |
| if struct.union: |
| # Unions don't allow multiple members in the initialization list, so just call initialize |
| out.append(f''' |
| {safe_name}::{safe_name}(const {struct.name}* in_struct{self.custom_construct_params.get(struct.name, '')}, PNextCopyState*) |
| {{ |
| initialize(in_struct); |
| }} |
| ''') |
| else: |
| out.append(f''' |
| {safe_name}::{safe_name}(const {struct.name}* in_struct{self.custom_construct_params.get(struct.name, '')}, [[maybe_unused]] PNextCopyState* copy_state{copy_pnext_param}) :{init_list} |
| {{ |
| {copy_pnext_if + construct_txt}}} |
| ''') |
| if '' != default_init_list: |
| # trim trailing comma from initializer list |
| default_init_list = f' :{default_init_list[:-1]}' |
| # truncate union initializer list to first element |
| if struct.union: |
| default_init_list = default_init_list.split(',')[0] |
| default_init_body = '\n' + custom_defeault_construct_txt[struct.name] if struct.name in custom_defeault_construct_txt else '' |
| out.append(f''' |
| {safe_name}::{safe_name}(){default_init_list} |
| {{{default_init_body}}} |
| ''') |
| # Create slight variation of init and construct txt for copy constructor that takes a copy_src object reference vs. struct ptr |
| construct_txt = copy_pnext + construct_txt |
| copy_construct_init = init_func_txt.replace('in_struct->', 'copy_src.') |
| copy_construct_init = copy_construct_init.replace(', copy_state', '') |
| if struct.name in self.union_of_pointer_callers: |
| copy_construct_init = copy_construct_init.replace(', copy_src.type', '') |
| # Pass object to copy constructors |
| copy_construct_txt = re.sub('(new \\w+)\\(in_struct->', '\\1(*copy_src.', construct_txt) |
| # Modify remaining struct refs for copy_src object |
| copy_construct_txt = copy_construct_txt.replace('in_struct->', 'copy_src.') |
| # Modify remaining struct refs for copy_src object |
| copy_construct_txt = copy_construct_txt .replace(', copy_state', '') |
| if struct.name in custom_copy_txt: |
| copy_construct_txt = custom_copy_txt[struct.name] |
| copy_assign_txt = ' if (©_src == this) return *this;\n\n' + destruct_txt + '\n' + copy_construct_init + copy_construct_txt + '\n return *this;' |
| # Copy constructor |
| out.append(f''' |
| {safe_name}::{safe_name}(const {safe_name}& copy_src) |
| {{ |
| {copy_construct_init}{copy_construct_txt}}} |
| ''') |
| # Copy assignment operator |
| out.append(f''' |
| {safe_name}& {safe_name}::operator=(const {safe_name}& copy_src)\n{{ |
| {copy_assign_txt} |
| }} |
| ''') |
| out.append(f''' |
| {safe_name}::~{safe_name}() |
| {{ |
| {destruct_txt}}} |
| ''') |
| out.append(f''' |
| void {safe_name}::initialize(const {struct.name}* in_struct{self.custom_construct_params.get(struct.name, '')}, [[maybe_unused]] PNextCopyState* copy_state) |
| {{ |
| {destruct_txt}{init_func_txt}{construct_txt}}} |
| ''') |
| # Copy initializer uses same txt as copy constructor but has a ptr and not a reference |
| init_copy = copy_construct_init.replace('copy_src.', 'copy_src->') |
| # Replace '©_src' with 'copy_src' unless it's followed by a dereference |
| init_copy = re.sub(r'©_src(?!->)', 'copy_src', init_copy) |
| init_construct = copy_construct_txt.replace('copy_src.', 'copy_src->') |
| # Replace '©_src' with 'copy_src' unless it's followed by a dereference |
| init_construct = re.sub(r'©_src(?!->)', 'copy_src', init_construct) |
| out.append(f''' |
| void {safe_name}::initialize(const {safe_name}* copy_src, [[maybe_unused]] PNextCopyState* copy_state) |
| {{ |
| {init_copy}{init_construct}}} |
| ''') |
| out.extend(guard_helper.add_guard(None)) |
| out.append(''' |
| } // namespace vku |
| ''') |
| |
| self.write("".join(out)) |