a73x

1b76e08f

Test partial upload API contract

a73x   2026-04-08 19:40


diff --git a/src/renderer.zig b/src/renderer.zig
index 4e66c13..4726717 100644
--- a/src/renderer.zig
+++ b/src/renderer.zig
@@ -335,6 +335,13 @@ const InstanceRangeWrite = struct {
    byte_len: vk.DeviceSize,
};

const InstanceRangeUploadAction = enum {
    no_op,
    partial,
    full,
    invalid_range,
};

fn planInstanceUpload(req: InstanceUploadRequest) InstanceUploadDecision {
    const needed_capacity = std.math.add(u32, req.offset_instances, req.write_len) catch {
        return .{
@@ -355,6 +362,24 @@ fn planInstanceRangeWrite(offset_instances: u32, len_instances: u32) InstanceRan
    };
}

fn planUploadInstanceRangeAction(
    current_capacity: u32,
    offset_instances: u32,
    len_instances: u32,
) InstanceRangeUploadAction {
    if (len_instances == 0) return .no_op;

    return switch (planInstanceUpload(.{
        .current_capacity = current_capacity,
        .offset_instances = offset_instances,
        .write_len = len_instances,
    }).upload_mode) {
        .partial => .partial,
        .full => .full,
        .invalid_range => .invalid_range,
    };
}

fn writeInstanceRange(
    target: []Instance,
    offset_instances: u32,
@@ -1265,16 +1290,15 @@ pub const Context = struct {
        offset_instances: u32,
        instances: []const Instance,
    ) !bool {
        if (instances.len == 0) return false;

        const decision = planInstanceUpload(.{
            .current_capacity = self.instance_capacity,
            .offset_instances = offset_instances,
            .write_len = std.math.cast(u32, instances.len) orelse return error.InvalidInstanceRange,
        });
        switch (decision.upload_mode) {
            .invalid_range => return error.InvalidInstanceRange,
        const action = planUploadInstanceRangeAction(
            self.instance_capacity,
            offset_instances,
            std.math.cast(u32, instances.len) orelse return error.InvalidInstanceRange,
        );
        switch (action) {
            .no_op => return false,
            .full => return true,
            .invalid_range => return error.InvalidInstanceRange,
            .partial => {},
        }

@@ -1575,3 +1599,23 @@ test "writeInstanceRange rejects overflowing ranges" {
        writeInstanceRange(target[0..], std.math.maxInt(u32), replacement[0..]),
    );
}

test "uploadInstanceRange contract treats zero-length writes as a no-op" {
    const action = planUploadInstanceRangeAction(4, 99, 0);
    try std.testing.expectEqual(InstanceRangeUploadAction.no_op, action);
}

test "uploadInstanceRange contract reports partial writes when capacity fits" {
    const action = planUploadInstanceRangeAction(8, 3, 2);
    try std.testing.expectEqual(InstanceRangeUploadAction.partial, action);
}

test "uploadInstanceRange contract reports full-upload fallback on growth" {
    const action = planUploadInstanceRangeAction(4, 3, 2);
    try std.testing.expectEqual(InstanceRangeUploadAction.full, action);
}

test "uploadInstanceRange contract reports invalid ranges explicitly" {
    const action = planUploadInstanceRangeAction(std.math.maxInt(u32), std.math.maxInt(u32), 1);
    try std.testing.expectEqual(InstanceRangeUploadAction.invalid_range, action);
}