55523c50
Fix renderer merge blockers
a73x 2026-04-08 15:29
Commit message
src/renderer.zig
| Old | New | ||
|---|---|---|---|
| @@ -91,6 +91,7 @@ fn createSwapchain( | |||
| 91 | device: vk.Device, | 91 | device: vk.Device, |
| 92 | width: u32, | 92 | width: u32, |
| 93 | height: u32, | 93 | height: u32, |
| 94 | old_swapchain: vk.SwapchainKHR, | ||
| 94 | ) !SwapchainResult { | 95 | ) !SwapchainResult { |
| 95 | const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pd_info.physical, surface); | 96 | const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pd_info.physical, surface); |
| 96 | 97 | ||
| @@ -154,6 +155,7 @@ fn createSwapchain( | |||
| 154 | .composite_alpha = .{ .opaque_bit_khr = true }, | 155 | .composite_alpha = .{ .opaque_bit_khr = true }, |
| 155 | .present_mode = present_mode, | 156 | .present_mode = present_mode, |
| 156 | .clipped = .true, | 157 | .clipped = .true, |
| 158 | .old_swapchain = old_swapchain, | ||
| 157 | }, null); | 159 | }, null); |
| 158 | 160 | ||
| 159 | var sc_count: u32 = 0; | 161 | var sc_count: u32 = 0; |
| @@ -303,6 +305,14 @@ fn findMemoryType( | |||
| 303 | return error.NoSuitableMemoryType; | 305 | return error.NoSuitableMemoryType; |
| 304 | } | 306 | } |
| 305 | 307 | ||
| 308 | fn nextInstanceCapacity(current: u32, needed: u32) u32 { | ||
| 309 | var capacity = @max(current, 1); | ||
| 310 | while (capacity < needed) { | ||
| 311 | capacity = std.math.mul(u32, capacity, 2) catch return needed; | ||
| 312 | } | ||
| 313 | return capacity; | ||
| 314 | } | ||
| 315 | |||
| 306 | pub const Context = struct { | 316 | pub const Context = struct { |
| 307 | alloc: std.mem.Allocator, | 317 | alloc: std.mem.Allocator, |
| 308 | vkb: vk.BaseWrapper, | 318 | vkb: vk.BaseWrapper, |
| @@ -433,7 +443,7 @@ pub const Context = struct { | |||
| 433 | const present_queue = vkd.getDeviceQueue(device, pd_info.present_queue_family, 0); | 443 | const present_queue = vkd.getDeviceQueue(device, pd_info.present_queue_family, 0); |
| 434 | 444 | ||
| 435 | // Create swapchain | 445 | // Create swapchain |
| 436 | const sc = try createSwapchain(alloc, vki, vkd, pd_info, surface, device, width, height); | 446 | const sc = try createSwapchain(alloc, vki, vkd, pd_info, surface, device, width, height, .null_handle); |
| 437 | errdefer { | 447 | errdefer { |
| 438 | for (sc.image_views) |view| vkd.destroyImageView(device, view, null); | 448 | for (sc.image_views) |view| vkd.destroyImageView(device, view, null); |
| 439 | alloc.free(sc.image_views); | 449 | alloc.free(sc.image_views); |
| @@ -898,8 +908,6 @@ pub const Context = struct { | |||
| 898 | } | 908 | } |
| 899 | 909 | ||
| 900 | pub fn recreateSwapchain(self: *Context, width: u32, height: u32) !void { | 910 | pub fn recreateSwapchain(self: *Context, width: u32, height: u32) !void { |
| 901 | self.destroySwapchainResources(); | ||
| 902 | |||
| 903 | const sc = try createSwapchain( | 911 | const sc = try createSwapchain( |
| 904 | self.alloc, | 912 | self.alloc, |
| 905 | self.vki, | 913 | self.vki, |
| @@ -913,6 +921,7 @@ pub const Context = struct { | |||
| 913 | self.device, | 921 | self.device, |
| 914 | width, | 922 | width, |
| 915 | height, | 923 | height, |
| 924 | self.swapchain, | ||
| 916 | ); | 925 | ); |
| 917 | errdefer { | 926 | errdefer { |
| 918 | for (sc.image_views) |view| self.vkd.destroyImageView(self.device, view, null); | 927 | for (sc.image_views) |view| self.vkd.destroyImageView(self.device, view, null); |
| @@ -934,12 +943,50 @@ pub const Context = struct { | |||
| 934 | self.alloc.free(framebuffers); | 943 | self.alloc.free(framebuffers); |
| 935 | } | 944 | } |
| 936 | 945 | ||
| 946 | const old_swapchain = self.swapchain; | ||
| 947 | const old_images = self.swapchain_images; | ||
| 948 | const old_image_views = self.swapchain_image_views; | ||
| 949 | const old_framebuffers = self.framebuffers; | ||
| 950 | |||
| 937 | self.swapchain = sc.swapchain; | 951 | self.swapchain = sc.swapchain; |
| 938 | self.swapchain_format = sc.format; | 952 | self.swapchain_format = sc.format; |
| 939 | self.swapchain_extent = sc.extent; | 953 | self.swapchain_extent = sc.extent; |
| 940 | self.swapchain_images = sc.images; | 954 | self.swapchain_images = sc.images; |
| 941 | self.swapchain_image_views = sc.image_views; | 955 | self.swapchain_image_views = sc.image_views; |
| 942 | self.framebuffers = framebuffers; | 956 | self.framebuffers = framebuffers; |
| 957 | |||
| 958 | for (old_framebuffers) |fb| self.vkd.destroyFramebuffer(self.device, fb, null); | ||
| 959 | self.alloc.free(old_framebuffers); | ||
| 960 | |||
| 961 | for (old_image_views) |view| self.vkd.destroyImageView(self.device, view, null); | ||
| 962 | self.alloc.free(old_image_views); | ||
| 963 | self.alloc.free(old_images); | ||
| 964 | self.vkd.destroySwapchainKHR(self.device, old_swapchain, null); | ||
| 965 | } | ||
| 966 | |||
| 967 | fn ensureInstanceCapacity(self: *Context, needed: u32) !void { | ||
| 968 | if (needed <= self.instance_capacity) return; | ||
| 969 | |||
| 970 | const new_capacity = nextInstanceCapacity(self.instance_capacity, needed); | ||
| 971 | const replacement = try createHostVisibleBuffer( | ||
| 972 | self.vki, | ||
| 973 | self.physical_device, | ||
| 974 | self.vkd, | ||
| 975 | self.device, | ||
| 976 | @as(vk.DeviceSize, @sizeOf(Instance)) * new_capacity, | ||
| 977 | .{ .vertex_buffer_bit = true }, | ||
| 978 | ); | ||
| 979 | errdefer { | ||
| 980 | self.vkd.destroyBuffer(self.device, replacement.buffer, null); | ||
| 981 | self.vkd.freeMemory(self.device, replacement.memory, null); | ||
| 982 | } | ||
| 983 | |||
| 984 | _ = try self.vkd.deviceWaitIdle(self.device); | ||
| 985 | self.vkd.destroyBuffer(self.device, self.instance_buffer, null); | ||
| 986 | self.vkd.freeMemory(self.device, self.instance_memory, null); | ||
| 987 | self.instance_buffer = replacement.buffer; | ||
| 988 | self.instance_memory = replacement.memory; | ||
| 989 | self.instance_capacity = new_capacity; | ||
| 943 | } | 990 | } |
| 944 | 991 | ||
| 945 | /// Record a command buffer that begins the render pass with the given clear color and presents. | 992 | /// Record a command buffer that begins the render pass with the given clear color and presents. |
| @@ -1127,7 +1174,7 @@ pub const Context = struct { | |||
| 1127 | 1174 | ||
| 1128 | /// Map the instance buffer, copy instances in, unmap. | 1175 | /// Map the instance buffer, copy instances in, unmap. |
| 1129 | pub fn uploadInstances(self: *Context, instances: []const Instance) !void { | 1176 | pub fn uploadInstances(self: *Context, instances: []const Instance) !void { |
| 1130 | if (instances.len > self.instance_capacity) return error.TooManyInstances; | 1177 | try self.ensureInstanceCapacity(@intCast(instances.len)); |
| 1131 | const size: vk.DeviceSize = @sizeOf(Instance) * instances.len; | 1178 | const size: vk.DeviceSize = @sizeOf(Instance) * instances.len; |
| 1132 | const mapped = try self.vkd.mapMemory(self.device, self.instance_memory, 0, size, .{}); | 1179 | const mapped = try self.vkd.mapMemory(self.device, self.instance_memory, 0, size, .{}); |
| 1133 | @memcpy(@as([*]Instance, @ptrCast(@alignCast(mapped)))[0..instances.len], instances); | 1180 | @memcpy(@as([*]Instance, @ptrCast(@alignCast(mapped)))[0..instances.len], instances); |
| @@ -1135,7 +1182,12 @@ pub const Context = struct { | |||
| 1135 | } | 1182 | } |
| 1136 | 1183 | ||
| 1137 | /// Full draw pass: bind pipeline, push constants, vertex + instance buffers, draw, present. | 1184 | /// Full draw pass: bind pipeline, push constants, vertex + instance buffers, draw, present. |
| 1138 | pub fn drawCells(self: *Context, instance_count: u32, cell_size: [2]f32) !void { | 1185 | pub fn drawCells( |
| 1186 | self: *Context, | ||
| 1187 | instance_count: u32, | ||
| 1188 | cell_size: [2]f32, | ||
| 1189 | clear_color: [4]f32, | ||
| 1190 | ) !void { | ||
| 1139 | // Wait for previous frame to finish | 1191 | // Wait for previous frame to finish |
| 1140 | _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64)); | 1192 | _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64)); |
| 1141 | try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); | 1193 | try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); |
| @@ -1156,9 +1208,7 @@ pub const Context = struct { | |||
| 1156 | .flags = .{ .one_time_submit_bit = true }, | 1208 | .flags = .{ .one_time_submit_bit = true }, |
| 1157 | }); | 1209 | }); |
| 1158 | 1210 | ||
| 1159 | const clear_value = vk.ClearValue{ | 1211 | const clear_value = vk.ClearValue{ .color = .{ .float_32 = clear_color } }; |
| 1160 | .color = .{ .float_32 = .{ 0.08, 0.08, 0.08, 1.0 } }, | ||
| 1161 | }; | ||
| 1162 | 1212 | ||
| 1163 | self.vkd.cmdBeginRenderPass(self.command_buffer, &vk.RenderPassBeginInfo{ | 1213 | self.vkd.cmdBeginRenderPass(self.command_buffer, &vk.RenderPassBeginInfo{ |
| 1164 | .render_pass = self.render_pass, | 1214 | .render_pass = self.render_pass, |
| @@ -1264,3 +1314,9 @@ test "shaders are embedded with SPIR-V magic" { | |||
| 1264 | const magic2: u32 = std.mem.readInt(u32, cell_frag_spv[0..4], .little); | 1314 | const magic2: u32 = std.mem.readInt(u32, cell_frag_spv[0..4], .little); |
| 1265 | try std.testing.expectEqual(@as(u32, 0x07230203), magic2); | 1315 | try std.testing.expectEqual(@as(u32, 0x07230203), magic2); |
| 1266 | } | 1316 | } |
| 1317 | |||
| 1318 | test "nextInstanceCapacity grows geometrically" { | ||
| 1319 | try std.testing.expectEqual(@as(u32, 16_000), nextInstanceCapacity(16_000, 8_000)); | ||
| 1320 | try std.testing.expectEqual(@as(u32, 32_000), nextInstanceCapacity(16_000, 16_001)); | ||
| 1321 | try std.testing.expectEqual(@as(u32, 4), nextInstanceCapacity(1, 3)); | ||
| 1322 | } | ||