From 6308a2569a7cf6e38a663aba173f93e8e2734be5 Mon Sep 17 00:00:00 2001 From: Gota7 Date: Mon, 13 Oct 2025 22:33:04 -0400 Subject: [PATCH 1/2] Custom Allocator Fix & Example --- examples/custom-allocator.zig | 50 +++++++++++++++++++++++++ src/sdl3.zig | 70 ++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 34 deletions(-) create mode 100644 examples/custom-allocator.zig diff --git a/examples/custom-allocator.zig b/examples/custom-allocator.zig new file mode 100644 index 0000000..ce707ad --- /dev/null +++ b/examples/custom-allocator.zig @@ -0,0 +1,50 @@ +const sdl3 = @import("sdl3"); +const std = @import("std"); + +const fps = 60; +const screen_width = 640; +const screen_height = 480; + +pub fn main() !void { + + // Setup custom allocator. + var debug_allocator = std.heap.DebugAllocator(.{}).init; + _ = try sdl3.setMemoryFunctionsByAllocator(debug_allocator.allocator()); + defer if (debug_allocator.detectLeaks()) @panic("Memory leaks detected!"); + + // Shutdown SDL. + defer sdl3.shutdown(); + + // Initialize SDL with subsystems you need here. + const init_flags = sdl3.InitFlags{ .video = true }; + try sdl3.init(init_flags); + defer sdl3.quit(init_flags); + + // Initial window setup. + const window = try sdl3.video.Window.init("Hello SDL3", screen_width, screen_height, .{}); + defer window.deinit(); + + // Useful for limiting the FPS and getting the delta time. + var fps_capper = sdl3.extras.FramerateCapper(f32){ .mode = .{ .limited = fps } }; + + var quit = false; + while (!quit) { + + // Delay to limit the FPS, returned delta time not needed. + const dt = fps_capper.delay(); + _ = dt; + + // Update logic. + const surface = try window.getSurface(); + try surface.fillRect(null, surface.mapRgb(128, 30, 255)); + try window.updateSurface(); + + // Event logic. + while (sdl3.events.poll()) |event| + switch (event) { + .quit => quit = true, + .terminating => quit = true, + else => {}, + }; + } +} diff --git a/src/sdl3.zig b/src/sdl3.zig index 4f3130b..a78a821 100644 --- a/src/sdl3.zig +++ b/src/sdl3.zig @@ -1988,51 +1988,53 @@ pub fn stepUtf8( /// Custom allocator to use for `setMemoryFunctionsByAllocator()`. var custom_allocator: std.mem.Allocator = undefined; -const Allocation = struct { +// Special thanks to castholm for fixing up these allocator functions. +const Allocation = extern struct { size: usize, + data: void align(@alignOf(std.c.max_align_t)), }; -fn allocationSize(request_size: usize) usize { - var size: usize = request_size; - while (size % @min(@sizeOf(@cImport(@cInclude("stddef.h")).max_align_t), @sizeOf(?*anyopaque) * 2) != 0) // TODO: Optimize this? - size += 1; - return size; +fn makeAllocation(size: usize, zero_init: bool) ?[*]u8 { + const bytes = custom_allocator.allocWithOptions(u8, @sizeOf(Allocation) + size, .of(Allocation), null) catch return null; + if (zero_init) @memset(bytes, 0); + const allocation: *Allocation = @ptrCast(@alignCast(bytes.ptr)); + allocation.size = bytes.len; + const mem: [*]u8 = @ptrCast(&allocation.data); + return mem; } -fn makeAllocation(total_size: usize, comptime memset: bool) ?[*]u8 { - const total_buf = custom_allocator.alloc(u8, allocationSize(total_size) + @sizeOf(Allocation)) catch return null; - if (memset) - @memset(total_buf, 0); - const allocation: *Allocation = @ptrCast(@alignCast(total_buf.ptr)); - allocation.size = total_buf.len; - const data_ptr: [*]u8 = @ptrFromInt(@intFromPtr(total_buf.ptr) + @sizeOf(Allocation)); - // std.debug.print("MAKE PTR: {p}, {d}\n", .{ data_ptr, allocation.size }); - return data_ptr; -} - -fn allocCalloc(num_members: usize, size: usize) ?[*]u8 { - return makeAllocation(num_members * size, true); +fn allocMalloc(size: usize) ?[*]u8 { + return makeAllocation(size, false); } -fn allocFree(mem: [*]u8) void { - const allocation: *Allocation = @ptrFromInt(@intFromPtr(mem) - @sizeOf(Allocation)); - // std.debug.print("CLEAR PTR: {p}, {d}\n", .{ raw_ptr, allocation.size }); - custom_allocator.free(@as([*]u8, @ptrCast(allocation))[0..allocation.size]); +fn allocCalloc(nmemb: usize, size: usize) ?[*]u8 { + return makeAllocation(nmemb * size, true); } -fn allocMalloc(size: usize) ?[*]u8 { - return makeAllocation(size, false); +fn allocRealloc(mem: ?[*]u8, size: usize) ?[*]u8 { + const old_mem = mem orelse return allocMalloc(size); + const old_data: *align(@alignOf(std.c.max_align_t)) void = @ptrCast(@alignCast(old_mem)); + const old_allocation: *Allocation = @fieldParentPtr("data", old_data); + const old_bytes_ptr: [*]align(@alignOf(Allocation)) u8 = @ptrCast(old_allocation); + const old_bytes = old_bytes_ptr[0..old_allocation.size]; + if (custom_allocator.remap(old_bytes, @sizeOf(Allocation) + size)) |new_bytes| { + const new_allocation: *Allocation = @ptrCast(@alignCast(new_bytes.ptr)); + new_allocation.size = new_bytes.len; + const new_mem: [*]u8 = @ptrCast(&new_allocation.data); + return new_mem; + } + const new_mem = makeAllocation(size, false) orelse return null; + @memcpy(new_mem[0..size], old_mem[0..size]); + custom_allocator.free(old_bytes); + return new_mem; } -fn allocRealloc(mem: ?[*]u8, size: usize) ?[*]u8 { - const raw_ptr = mem orelse return allocMalloc(size); - // const allocation: *Allocation = @alignCast(@fieldParentPtr("buf", @as(*void, @ptrCast(raw_ptr)))); - allocFree(raw_ptr); - return allocMalloc(size); - // const total_buf = custom_allocator.realloc(@as([*]u8, @ptrCast(raw_ptr))[0..allocation.size], allocationSize(size) + @sizeOf(Allocation)) catch return null; - // allocation = @ptrCast(@alignCast(total_buf.ptr)); - // allocation.size = total_buf.len; - // return &allocation.buf; +fn allocFree(mem: [*]u8) void { + const data: *align(@alignOf(std.c.max_align_t)) void = @ptrCast(@alignCast(mem)); + const allocation: *Allocation = @fieldParentPtr("data", data); + const bytes_ptr: [*]align(@alignOf(Allocation)) u8 = @ptrCast(allocation); + const bytes = bytes_ptr[0..allocation.size]; + custom_allocator.free(bytes); } /// Replace SDL's memory allocation functions to use with an allocator. From 4555a2e0db391c0f70ca18ed049a350bab0ee3ec Mon Sep 17 00:00:00 2001 From: Gota7 Date: Mon, 13 Oct 2025 22:34:04 -0400 Subject: [PATCH 2/2] Tag 0.1.4 --- README.md | 4 ++-- build.zig | 2 +- build.zig.zon | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 641cfa5..9e84235 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,9 @@ Some advantages of SDL3 include windowing, audio, gamepad, keyboard, mouse, rend To use zig-sdl3 with zig 0.15.1, you need to add it as a dependency to your project. The master branch of zig is not currently supported. Choose the command that matches your desired zig-sdl3 version and run it in your project's root directory: -* For the latest tag release: +* For the latest tagged release: ```sh -zig fetch --save git+https://github.com/Gota7/zig-sdl3#v0.1.3 +zig fetch --save git+https://github.com/Gota7/zig-sdl3#v0.1.4 ``` * For in progress updates (nightly): ```sh diff --git a/build.zig b/build.zig index 15321b9..26be1e8 100644 --- a/build.zig +++ b/build.zig @@ -23,7 +23,7 @@ pub fn build(b: *std.Build) !void { .version = .{ .major = 0, .minor = 1, - .patch = 3, + .patch = 4, }, }; diff --git a/build.zig.zon b/build.zig.zon index 413d978..c7c40d9 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .sdl3, - .version = "0.1.3", + .version = "0.1.4", .minimum_zig_version = "0.15.1", .dependencies = .{ .sdl = .{