From 493937f8701953296850df4449c7b8695fba6a75 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Sat, 29 Nov 2025 00:28:32 +0100 Subject: [PATCH] Actual deleak, debug object names --- src/Game.zig | 22 +++++++++++--- src/assets/Chunk.zig | 10 ++++++ src/assets/Materials.zig | 1 + src/assets/Textures.zig | 5 +++ src/engine/Engine.zig | 59 ++++++++++++++++++++++++++++++++++++ src/engine/GenericBuffer.zig | 7 +++++ src/engine/Swapchain.zig | 15 ++++++++- src/engine/Texture.zig | 10 ++++++ 8 files changed, 123 insertions(+), 6 deletions(-) diff --git a/src/Game.zig b/src/Game.zig index 8ac5e05..1ff8ca4 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -226,6 +226,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain }, }); errdefer engine.destroyDescriptorSetLayout(global_descriptor_set_layout); + engine.setObjectName(global_descriptor_set_layout, "DSL Global", .{}); const per_batch_descriptor_set_layout = try engine.createDescriptorSetLayout(.{ .bindings = &.{ @@ -238,6 +239,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain }, }); errdefer engine.destroyDescriptorSetLayout(per_batch_descriptor_set_layout); + engine.setObjectName(per_batch_descriptor_set_layout, "DSL PerBatch", .{}); const pipeline_layout = try engine.createPipelineLayout(.{ .set_layouts = &.{ @@ -246,17 +248,21 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain }, }); errdefer engine.destroyPipelineLayout(pipeline_layout); + engine.setObjectName(pipeline_layout, "PL", .{}); const vertex_shader = try engine.createShaderModule(.{ .code = &main_vert_spv }); defer engine.destroyShaderModule(vertex_shader); + engine.setObjectName(vertex_shader, "SM main_vert", .{}); const fragment_shader = try engine.createShaderModule(.{ .code = &main_frag_spv }); defer engine.destroyShaderModule(fragment_shader); + engine.setObjectName(fragment_shader, "SM main_frag", .{}); var vertex_buffer = try VertexBuffer.init(engine, .{ .usage = .vertex, .target_queue = .graphics, .array_capacity = 4, + .name = "QuadVB", }); errdefer vertex_buffer.deinit(engine); try vertex_buffer.write(engine, .{ @@ -292,6 +298,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .usage = .index, .target_queue = .graphics, .array_capacity = 6, + .name = "QuadIB", }); errdefer index_buffer.deinit(engine); try index_buffer.write(engine, .{ @@ -301,6 +308,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain var global_uniforms = try GlobalUniformsBuffer.init(engine, .{ .usage = .uniform, .target_queue = .graphics, + .name = "GlobalUniforms", }); errdefer global_uniforms.deinit(engine); @@ -333,8 +341,10 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain errdefer for (semaphores.items) |x| engine.destroySemaphore(x); - for (0..swapchain.swapchain_images.len) |_| { - semaphores.appendAssumeCapacity(try engine.createSemaphore()); + for (0..swapchain.swapchain_images.len) |i| { + const semaphore = try engine.createSemaphore(); + engine.setObjectName(semaphore, "S Transfer[{d}]", .{i}); + semaphores.appendAssumeCapacity(semaphore); } break :blk try semaphores.toOwnedSlice(allocator); @@ -346,13 +356,11 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain allocator.free(global_uniforms_transfer_semaphores); } - const global_uniforms_transfer_semaphore = try engine.createSemaphore(); - errdefer engine.destroySemaphore(global_uniforms_transfer_semaphore); - var point_lights = try PointLightBuffer.init(engine, .{ .usage = .storage, .target_queue = .graphics, .array_capacity = max_point_lights, + .name = "PointLights", }); errdefer point_lights.deinit(engine); @@ -360,6 +368,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .usage = .storage, .target_queue = .graphics, .array_capacity = max_directional_lights, + .name = "DirectionalLights", }); errdefer directional_lights.deinit(engine); @@ -479,6 +488,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .subpass = 0, }); errdefer engine.destroyPipeline(pipeline); + engine.setObjectName(pipeline, "P", .{}); const descriptor_pool = try engine.createDescriptorPool(.{ .max_sets = 1 + 256, @@ -502,12 +512,14 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain }, }); errdefer engine.destroyDescriptorPool(descriptor_pool); + engine.setObjectName(descriptor_pool, "DP", .{}); const global_descriptor_set = try engine.allocateDescriptorSet(.{ .descriptor_pool = descriptor_pool, .set_layout = global_descriptor_set_layout, .variable_descriptor_count = @intCast(textures.textures.items.len), }); + engine.setObjectName(global_descriptor_set, "DS Global", .{}); const descriptor_images = try allocator.alloc(vk.DescriptorImageInfo, textures.textures.items.len); for (textures.textures.items, descriptor_images) |texture, *info| { diff --git a/src/assets/Chunk.zig b/src/assets/Chunk.zig index 9b391c4..75dfd53 100644 --- a/src/assets/Chunk.zig +++ b/src/assets/Chunk.zig @@ -18,12 +18,14 @@ const Vector3 = math.Vector3; pub const chunk_size = 16; const initial_capacity = 256; +var next_chunk_id: std.atomic.Value(u64) = .init(0); blocks: [chunk_size][chunk_size][chunk_size]Blocks.Id, origin: Vector3, descriptor_set: vk.DescriptorSet, object_buffer: ObjectUniformsBuffer, object_count: u32, +chunk_id: u64, pub const InitInfo = struct { origin: Vector3, @@ -65,12 +67,18 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Chunk { }, }); + const chunk_id = next_chunk_id.fetchAdd(1, .monotonic); + engine.setObjectName(descriptor_set, "DS Chunk[{d}]", .{chunk_id}); + engine.setObjectName(object_buffer.buffer, "B Chunk[{d}]", .{chunk_id}); + engine.setObjectName(object_buffer.device_memory, "DM Chunk[{d}]", .{chunk_id}); + return .{ .blocks = .{.{[_]Blocks.Id{.air} ** chunk_size} ** chunk_size} ** chunk_size, .origin = init_info.origin, .descriptor_set = descriptor_set, .object_buffer = object_buffer, .object_count = 0, + .chunk_id = chunk_id, }; } @@ -128,6 +136,8 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, temp_alloca self.object_buffer.deinit(engine); self.object_buffer = new_object_buffer; + engine.setObjectName(new_object_buffer.buffer, "B Chunk[{d}]", .{self.chunk_id}); + engine.setObjectName(new_object_buffer.device_memory, "DM Chunk[{d}]", .{self.chunk_id}); } const uniforms = try temp_allocator.alloc(Game.ObjectUniforms, object_count); diff --git a/src/assets/Materials.zig b/src/assets/Materials.zig index 57b6df0..3c3b60c 100644 --- a/src/assets/Materials.zig +++ b/src/assets/Materials.zig @@ -53,6 +53,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials { .usage = .storage, .target_queue = .graphics, .array_capacity = capacity, + .name = "Materials", }); errdefer material_buffer.deinit(engine); diff --git a/src/assets/Textures.zig b/src/assets/Textures.zig index 5c0d7e4..0fb7dfe 100644 --- a/src/assets/Textures.zig +++ b/src/assets/Textures.zig @@ -50,6 +50,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures { .height = 1, .usage = .base_color, .target_queue = .graphics, + .name = "@Empty", }); textures.appendAssumeCapacity(empty_base_color_texture); @@ -58,6 +59,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures { .height = 1, .usage = .emissive, .target_queue = .graphics, + .name = "@Empty", }); textures.appendAssumeCapacity(empty_emissive_texture); @@ -66,6 +68,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures { .height = 1, .usage = .normal, .target_queue = .graphics, + .name = "@Empty", }); textures.appendAssumeCapacity(empty_normal_texture); @@ -74,6 +77,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures { .height = 1, .usage = .occlusion_roughness_metallic, .target_queue = .graphics, + .name = "@Empty", }); textures.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture); @@ -176,6 +180,7 @@ fn loadTexture(engine: *Engine, filename: []const u8, usage: Texture.Usage, temp .height = img.height, .usage = usage, .target_queue = .graphics, + .name = filename, }); errdefer texture.deinit(engine); diff --git a/src/engine/Engine.zig b/src/engine/Engine.zig index 1c28869..dc1a959 100644 --- a/src/engine/Engine.zig +++ b/src/engine/Engine.zig @@ -447,6 +447,65 @@ pub fn allocate(self: *const Engine, memory_requirements: vk.MemoryRequirements, return error.NoSuitableMemoryType; } +pub fn setObjectName(self: *const Engine, handle: anytype, comptime fmt: []const u8, args: anytype) void { + if (debug) { + const HandleType = @TypeOf(handle); + const type_name = @typeName(HandleType); + + if (@as(std.builtin.TypeId, @typeInfo(HandleType)) != .@"enum") { + @compileError("Cannot set object name for a handle of type " ++ type_name ++ ", which is not an enum."); + } + + const object_type: vk.ObjectType = switch (HandleType) { + vk.Instance => .instance, + vk.PhysicalDevice => .physical_device, + vk.Device => .device, + vk.Queue => .queue, + vk.Semaphore => .semaphore, + vk.CommandBuffer => .command_buffer, + vk.Fence => .fence, + vk.DeviceMemory => .device_memory, + vk.Buffer => .buffer, + vk.Image => .image, + vk.Event => .event, + vk.QueryPool => .query_pool, + vk.BufferView => .buffer_view, + vk.ImageView => .image_view, + vk.ShaderModule => .shader_module, + vk.PipelineCache => .pipeline_cache, + vk.PipelineLayout => .pipeline_layout, + vk.RenderPass => .render_pass, + vk.Pipeline => .pipeline, + vk.DescriptorSetLayout => .descriptor_set_layout, + vk.Sampler => .sampler, + vk.DescriptorPool => .descriptor_pool, + vk.DescriptorSet => .descriptor_set, + vk.Framebuffer => .framebuffer, + vk.CommandPool => .command_pool, + vk.SurfaceKHR => .surface_khr, + vk.SwapchainKHR => .swapchain_khr, + else => @compileError("Unsupported type " ++ type_name ++ " for setting and object name"), + }; + + const handle_value: u64 = @intFromEnum(handle); + + var buf: [256]u8 = undefined; + const name = std.fmt.bufPrintZ(&buf, fmt, args) catch |err| { + std.log.debug("Failed to set object name for {s}#{X}: {s}", .{ type_name, handle_value, @errorName(err) }); + return; + }; + + self.device.setDebugUtilsObjectNameEXT(&.{ + .object_type = object_type, + .object_handle = handle_value, + .p_object_name = name.ptr, + }) catch |err| { + std.log.debug("Failed to set object name for {s}#{X}: {s}", .{ type_name, handle_value, @errorName(err) }); + return; + }; + } +} + fn debugUtilsMessengerCallback( severity: vk.DebugUtilsMessageSeverityFlagsEXT, message_type: vk.DebugUtilsMessageTypeFlagsEXT, diff --git a/src/engine/GenericBuffer.zig b/src/engine/GenericBuffer.zig index 65a5821..8b21cdb 100644 --- a/src/engine/GenericBuffer.zig +++ b/src/engine/GenericBuffer.zig @@ -29,6 +29,7 @@ pub const InitInfo = struct { usage: Usage, target_queue: TargetQueue, array_capacity: u32 = 0, + name: ?[]const u8 = null, }; pub fn GenericBuffer(comptime Header: type, comptime Element: type) type { @@ -66,10 +67,16 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type { .queue_family_indices = &.{ target_queue_family, transfer_queue_family }, }); errdefer engine.destroyBuffer(buffer); + if (init_info.name) |name| { + engine.setObjectName(buffer, "B {s} [{s}, {s}]", .{ name, @typeName(Header), @typeName(Element) }); + } const memory_requirements = engine.device.getBufferMemoryRequirements(buffer); const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true }); errdefer engine.freeMemory(device_memory); + if (init_info.name) |name| { + engine.setObjectName(device_memory, "DM {s} [{s}, {s}]", .{ name, @typeName(Header), @typeName(Element) }); + } try engine.device.bindBufferMemory(buffer, device_memory, 0); diff --git a/src/engine/Swapchain.zig b/src/engine/Swapchain.zig index fc05659..2e1cfd1 100644 --- a/src/engine/Swapchain.zig +++ b/src/engine/Swapchain.zig @@ -104,6 +104,7 @@ pub fn init(engine: *Engine) !Swapchain { }, &engine.vk_allocator.interface); }; errdefer engine.device.destroyRenderPass(render_pass, &engine.vk_allocator.interface); + engine.setObjectName(render_pass, "RP", .{}); var swapchain: Swapchain = .{ .params = params, @@ -133,6 +134,7 @@ pub fn deinit(self: *Swapchain, engine: *Engine) void { } engine.device.destroyRenderPass(self.render_pass, &engine.vk_allocator.interface); + engine.destroySemaphore(self.semaphore_image_acquired); self.* = undefined; } @@ -173,6 +175,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { .old_swapchain = old_swapchain, }, &engine.vk_allocator.interface); errdefer engine.device.destroySwapchainKHR(new_swapchain, &engine.vk_allocator.interface); + engine.setObjectName(new_swapchain, "SC {d}×{d}", .{ extent.width, extent.height }); // --- DESTROY OLD SWAPCHAIN AND ITS IMAGES -------------------------------- @@ -209,6 +212,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { .height = extent.height, .target_queue = .graphics, .usage = .depth, + .name = "@Depth", }); errdefer new_depth_texture.deinit(engine); @@ -223,13 +227,14 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { errdefer for (swapchain_images.items) |*x| x.deinit(engine); - for (images) |image| { + for (images, 0..) |image, index| { swapchain_images.appendAssumeCapacity(try SwapchainImage.init(engine, .{ .image = image, .format = self.params.surface_format.format, .render_pass = self.render_pass, .extent = extent, .depth_stencil_image_view = new_depth_texture.image_view, + .index = index, })); } @@ -246,6 +251,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { var semaphore_image_acquired = try engine.createSemaphore(); errdefer engine.destroySemaphore(semaphore_image_acquired); + engine.setObjectName(semaphore_image_acquired, "S Acquired[E]", .{}); const res = try engine.device.acquireNextImageKHR(new_swapchain, std.math.maxInt(u64), semaphore_image_acquired, .null_handle); if (res.result == .not_ready or res.result == .timeout) { @@ -407,6 +413,8 @@ const SwapchainImage = struct { render_pass: vk.RenderPass, extent: vk.Extent2D, depth_stencil_image_view: vk.ImageView, + + index: usize, }; fn init(engine: *Engine, props: InitProps) !SwapchainImage { @@ -423,17 +431,21 @@ const SwapchainImage = struct { }, }); errdefer engine.destroyImageView(image_view); + engine.setObjectName(image_view, "IV Swapchain[{d}]", .{props.index}); const semaphore_image_acquired = try engine.createSemaphore(); errdefer engine.destroySemaphore(semaphore_image_acquired); + engine.setObjectName(image_view, "S Acquired[{d}]", .{props.index}); const semaphore_render_finished = try engine.createSemaphore(); errdefer engine.destroySemaphore(semaphore_render_finished); + engine.setObjectName(image_view, "S Rendered[{d}]", .{props.index}); const fence = try engine.createFence(.{ .flags = .{ .signaled_bit = true }, }); errdefer engine.destroyFence(fence); + engine.setObjectName(image_view, "F Rendered[{d}]", .{props.index}); const framebuffer = try engine.createFramebuffer(.{ .render_pass = props.render_pass, @@ -443,6 +455,7 @@ const SwapchainImage = struct { .layers = 1, }); errdefer engine.destroyFramebuffer(framebuffer); + engine.setObjectName(image_view, "FB Swapchain[{d}]", .{props.index}); return .{ .image = props.image, diff --git a/src/engine/Texture.zig b/src/engine/Texture.zig index 6265beb..2babd74 100644 --- a/src/engine/Texture.zig +++ b/src/engine/Texture.zig @@ -67,6 +67,7 @@ pub const InitInfo = struct { height: u32, usage: Usage, target_queue: TargetQueue, + name: ?[]const u8 = null, }; image: vk.Image, @@ -111,10 +112,16 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture { .initial_layout = .undefined, }); errdefer engine.destroyImage(image); + if (init_info.name) |name| { + engine.setObjectName(image, "I {s} [{s}]", .{ name, @tagName(init_info.usage) }); + } const memory_requirements = engine.device.getImageMemoryRequirements(image); const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true }); errdefer engine.freeMemory(device_memory); + if (init_info.name) |name| { + engine.setObjectName(device_memory, "DM {s} [{s}]", .{ name, @tagName(init_info.usage) }); + } try engine.device.bindImageMemory(image, device_memory, 0); @@ -134,6 +141,9 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture { }, }); errdefer engine.destroyImageView(image_view); + if (init_info.name) |name| { + engine.setObjectName(image_view, "IV {s} [{s}]", .{ name, @tagName(init_info.usage) }); + } return .{ .image = image,