a73x

55523c50

Fix renderer merge blockers

a73x   2026-04-08 15:29


diff --git a/src/renderer.zig b/src/renderer.zig
index 12003ef..682b8be 100644
--- a/src/renderer.zig
+++ b/src/renderer.zig
@@ -91,6 +91,7 @@ fn createSwapchain(
    device: vk.Device,
    width: u32,
    height: u32,
    old_swapchain: vk.SwapchainKHR,
) !SwapchainResult {
    const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pd_info.physical, surface);

@@ -154,6 +155,7 @@ fn createSwapchain(
        .composite_alpha = .{ .opaque_bit_khr = true },
        .present_mode = present_mode,
        .clipped = .true,
        .old_swapchain = old_swapchain,
    }, null);

    var sc_count: u32 = 0;
@@ -303,6 +305,14 @@ fn findMemoryType(
    return error.NoSuitableMemoryType;
}

fn nextInstanceCapacity(current: u32, needed: u32) u32 {
    var capacity = @max(current, 1);
    while (capacity < needed) {
        capacity = std.math.mul(u32, capacity, 2) catch return needed;
    }
    return capacity;
}

pub const Context = struct {
    alloc: std.mem.Allocator,
    vkb: vk.BaseWrapper,
@@ -433,7 +443,7 @@ pub const Context = struct {
        const present_queue = vkd.getDeviceQueue(device, pd_info.present_queue_family, 0);

        // Create swapchain
        const sc = try createSwapchain(alloc, vki, vkd, pd_info, surface, device, width, height);
        const sc = try createSwapchain(alloc, vki, vkd, pd_info, surface, device, width, height, .null_handle);
        errdefer {
            for (sc.image_views) |view| vkd.destroyImageView(device, view, null);
            alloc.free(sc.image_views);
@@ -898,8 +908,6 @@ pub const Context = struct {
    }

    pub fn recreateSwapchain(self: *Context, width: u32, height: u32) !void {
        self.destroySwapchainResources();

        const sc = try createSwapchain(
            self.alloc,
            self.vki,
@@ -913,6 +921,7 @@ pub const Context = struct {
            self.device,
            width,
            height,
            self.swapchain,
        );
        errdefer {
            for (sc.image_views) |view| self.vkd.destroyImageView(self.device, view, null);
@@ -934,12 +943,50 @@ pub const Context = struct {
            self.alloc.free(framebuffers);
        }

        const old_swapchain = self.swapchain;
        const old_images = self.swapchain_images;
        const old_image_views = self.swapchain_image_views;
        const old_framebuffers = self.framebuffers;

        self.swapchain = sc.swapchain;
        self.swapchain_format = sc.format;
        self.swapchain_extent = sc.extent;
        self.swapchain_images = sc.images;
        self.swapchain_image_views = sc.image_views;
        self.framebuffers = framebuffers;

        for (old_framebuffers) |fb| self.vkd.destroyFramebuffer(self.device, fb, null);
        self.alloc.free(old_framebuffers);

        for (old_image_views) |view| self.vkd.destroyImageView(self.device, view, null);
        self.alloc.free(old_image_views);
        self.alloc.free(old_images);
        self.vkd.destroySwapchainKHR(self.device, old_swapchain, null);
    }

    fn ensureInstanceCapacity(self: *Context, needed: u32) !void {
        if (needed <= self.instance_capacity) return;

        const new_capacity = nextInstanceCapacity(self.instance_capacity, needed);
        const replacement = try createHostVisibleBuffer(
            self.vki,
            self.physical_device,
            self.vkd,
            self.device,
            @as(vk.DeviceSize, @sizeOf(Instance)) * new_capacity,
            .{ .vertex_buffer_bit = true },
        );
        errdefer {
            self.vkd.destroyBuffer(self.device, replacement.buffer, null);
            self.vkd.freeMemory(self.device, replacement.memory, null);
        }

        _ = try self.vkd.deviceWaitIdle(self.device);
        self.vkd.destroyBuffer(self.device, self.instance_buffer, null);
        self.vkd.freeMemory(self.device, self.instance_memory, null);
        self.instance_buffer = replacement.buffer;
        self.instance_memory = replacement.memory;
        self.instance_capacity = new_capacity;
    }

    /// Record a command buffer that begins the render pass with the given clear color and presents.
@@ -1127,7 +1174,7 @@ pub const Context = struct {

    /// Map the instance buffer, copy instances in, unmap.
    pub fn uploadInstances(self: *Context, instances: []const Instance) !void {
        if (instances.len > self.instance_capacity) return error.TooManyInstances;
        try self.ensureInstanceCapacity(@intCast(instances.len));
        const size: vk.DeviceSize = @sizeOf(Instance) * instances.len;
        const mapped = try self.vkd.mapMemory(self.device, self.instance_memory, 0, size, .{});
        @memcpy(@as([*]Instance, @ptrCast(@alignCast(mapped)))[0..instances.len], instances);
@@ -1135,7 +1182,12 @@ pub const Context = struct {
    }

    /// Full draw pass: bind pipeline, push constants, vertex + instance buffers, draw, present.
    pub fn drawCells(self: *Context, instance_count: u32, cell_size: [2]f32) !void {
    pub fn drawCells(
        self: *Context,
        instance_count: u32,
        cell_size: [2]f32,
        clear_color: [4]f32,
    ) !void {
        // Wait for previous frame to finish
        _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64));
        try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence));
@@ -1156,9 +1208,7 @@ pub const Context = struct {
            .flags = .{ .one_time_submit_bit = true },
        });

        const clear_value = vk.ClearValue{
            .color = .{ .float_32 = .{ 0.08, 0.08, 0.08, 1.0 } },
        };
        const clear_value = vk.ClearValue{ .color = .{ .float_32 = clear_color } };

        self.vkd.cmdBeginRenderPass(self.command_buffer, &vk.RenderPassBeginInfo{
            .render_pass = self.render_pass,
@@ -1264,3 +1314,9 @@ test "shaders are embedded with SPIR-V magic" {
    const magic2: u32 = std.mem.readInt(u32, cell_frag_spv[0..4], .little);
    try std.testing.expectEqual(@as(u32, 0x07230203), magic2);
}

test "nextInstanceCapacity grows geometrically" {
    try std.testing.expectEqual(@as(u32, 16_000), nextInstanceCapacity(16_000, 8_000));
    try std.testing.expectEqual(@as(u32, 32_000), nextInstanceCapacity(16_000, 16_001));
    try std.testing.expectEqual(@as(u32, 4), nextInstanceCapacity(1, 3));
}