From 951c2d6befe1a7741455b43841ad5269b65e1560 Mon Sep 17 00:00:00 2001 From: Vlad Panazan Date: Mon, 19 Aug 2024 20:31:24 +0200 Subject: [PATCH] add AutoMemPool --- build.zig | 15 +- build.zig.zon | 2 +- toolkit/App.zig | 2 +- toolkit/Surface.zig | 10 +- toolkit/widgets/Scrollable.zig | 1 - wayland/cmsghdr.zig | 14 +- wayland/examples/animation.zig | 193 ++++++++++++++++++++++++ wayland/examples/hello.zig | 86 +---------- wayland/examples/kb_grab.zig | 2 +- wayland/lib.zig | 4 + wayland/shm.zig | 261 ++++++++++++++++++++++----------- 11 files changed, 406 insertions(+), 184 deletions(-) create mode 100644 wayland/examples/animation.zig diff --git a/build.zig b/build.zig index 5652161..1e9f3a3 100644 --- a/build.zig +++ b/build.zig @@ -22,7 +22,7 @@ pub fn build(b: *std.Build) void { }); _ = toolkit; // autofix - inline for (.{ "globals", "seats", "hello", "kb_grab" }) |example| { + inline for (.{ "globals", "seats", "hello", "kb_grab", "animation" }) |example| { const exe = b.addExecutable(.{ .name = example, .root_source_file = b.path("wayland/examples/" ++ example ++ ".zig"), @@ -46,4 +46,17 @@ pub fn build(b: *std.Build) void { const run_step = b.step("run-" ++ example, "Run the app"); run_step.dependOn(&run_cmd.step); } + { + const unit_tests = b.addTest(.{ + .root_source_file = b.path("wayland/lib.zig"), + .target = target, + .optimize = optimize, + }); + unit_tests.root_module.addImport("wayland", wayland); + unit_tests.root_module.addImport("xev", libxev); + + const run_unit_tests = b.addRunArtifact(unit_tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_unit_tests.step); + } } diff --git a/build.zig.zon b/build.zig.zon index d593e4e..3cd1a6b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,7 +6,7 @@ .dependencies = .{ .libxev = .{ .url = "https://github.com/mitchellh/libxev/archive/main.tar.gz", - .hash = "1220b644b45718a869b37bc37bbc476e69922b21090003b38c1b68a7218fc365771a", + .hash = "1220aec83b6367c6bc64ca781828e0ad817fb38e7fca7331bd6d736b6896910f6637", }, }, } diff --git a/toolkit/App.zig b/toolkit/App.zig index 3d1e658..f39012d 100644 --- a/toolkit/App.zig +++ b/toolkit/App.zig @@ -82,7 +82,7 @@ pub fn new_common(app: *App, root_widget: WidgetIdx) !*Surface { .size = size, .last_frame = 0, .root = root_widget, - .pool = try wlnd.shm.Pool.init(client, app.shm.?, size.width, size.height), + .pool = try wlnd.shm.AutoMemPool.init(client, app.shm.?), }; return surf; diff --git a/toolkit/Surface.zig b/toolkit/Surface.zig index 7c393e4..bea2080 100644 --- a/toolkit/Surface.zig +++ b/toolkit/Surface.zig @@ -30,7 +30,7 @@ min_size: Size, last_frame: u32, frame_done: bool = true, initial_draw: bool = false, -pool: shm.Pool = undefined, +pool: shm.AutoMemPool = undefined, pub const SurfaceRole = enum { xdg_toplevel, @@ -176,20 +176,20 @@ pub fn draw(self: *Surface) void { // std.log.info("draw {}", .{std.meta.activeTag(self.role)}); const client = self.app.client; const size = self.app.layout.get(self.root, .rect).get_size(); - const buf = self.pool.get_buffer(client, size.width, size.height); + const buf = self.pool.buffer(client, size.width, size.height); if (size.contains(self.min_size)) { const ctx = PaintCtx{ - .buffer = @ptrCast(std.mem.bytesAsSlice(u32, self.pool.mmap)), + .buffer = @ptrCast(std.mem.bytesAsSlice(u32, buf.mem())), //TODO: use ptrCast directly when implemented in zig // .buffer = @ptrCast(buf.pool.mmap), .width = buf.width, .height = buf.height, .clip = size.to_rect(), }; - @memset(self.pool.mmap, 155); + @memset(buf.mem(), 155); self.draw_root_widget(ctx); } else { - @memset(self.pool.mmap, 200); + @memset(buf.mem(), 200); } client.request(self.wl_surface, .attach, .{ .buffer = buf.wl_buffer, .x = 0, .y = 0 }); // client.request(self.wl_surface, .offset, .{ .x = 220, .y = 220 }); diff --git a/toolkit/widgets/Scrollable.zig b/toolkit/widgets/Scrollable.zig index adb1493..573acac 100644 --- a/toolkit/widgets/Scrollable.zig +++ b/toolkit/widgets/Scrollable.zig @@ -137,7 +137,6 @@ pub fn handle_event(layout: *Layout, idx: WidgetIdx, event: tk.Event) void { c.button.pos = wpos; break :b c; } else ev; - std.log.info("asdf", .{}); _ = layout.call( idxx, .handle_event, diff --git a/wayland/cmsghdr.zig b/wayland/cmsghdr.zig index 2f0448d..d27b944 100644 --- a/wayland/cmsghdr.zig +++ b/wayland/cmsghdr.zig @@ -46,12 +46,12 @@ pub fn Cmsghdr(comptime T: type) type { }; } -test { - std.testing.refAllDecls(Cmsghdr([3]std.os.fd_t)); -} +// test { +// std.testing.refAllDecls(Cmsghdr([3]std.os.fd_t)); +// } test "sendmsg" { - const os = std.os; + const os = std.posix; var address_server = try std.net.Address.parseIp4("127.0.0.1", 0); // const fd = try std.os.socket(os.linux.AF.UNIX, os.linux.SOCK.STREAM, 0); @@ -75,7 +75,7 @@ test "sendmsg" { defer os.close(client); const buffer_send = [_]u8{42} ** 128; const iovecs_send = [_]os.iovec_const{ - os.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len }, + os.iovec_const{ .base = &buffer_send, .len = buffer_send.len }, }; const msg_send = os.msghdr_const{ .name = &address_server.any, @@ -90,7 +90,7 @@ test "sendmsg" { var buffer_recv = [_]u8{0} ** 128; var iovecs_recv = [_]os.iovec{ - os.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len }, + os.iovec{ .base = &buffer_recv, .len = buffer_recv.len }, }; const addr = [_]u8{0} ** 4; var address_recv = std.net.Address.initIp4(addr, 0); @@ -103,7 +103,7 @@ test "sendmsg" { .controllen = 0, .flags = 0, }; - const sqe_recvmsg = os.linux.recvmsg(server, &msg_recv, 0); + const sqe_recvmsg = std.os.linux.recvmsg(server, &msg_recv, 0); try std.testing.expectEqual(buffer_send.len, sqe_sendmsg); try std.testing.expectEqual(buffer_recv.len, sqe_recvmsg); diff --git a/wayland/examples/animation.zig b/wayland/examples/animation.zig new file mode 100644 index 0000000..2051fe0 --- /dev/null +++ b/wayland/examples/animation.zig @@ -0,0 +1,193 @@ +pub const std_options = std.Options{ + .log_level = .info, +}; + +const App = struct { + shm: ?wl.Shm, + compositor: ?wl.Compositor, + wm_base: ?xdg.WmBase, + running: bool = true, +}; + +const SurfaceCtx = struct { + ctx: *App, + wl_surface: wl.Surface, + xdg_surface: xdg.Surface, + xdg_toplevel: xdg.Toplevel, + width: u31, + height: u31, + offset: f32, + last_frame: u32, +}; + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer std.debug.assert(gpa.deinit() == .ok); + const allocator = gpa.allocator(); + + const client = try wayland.Client.connect(allocator); + const registry = client.request(client.wl_display, .get_registry, .{}); + + var context = App{ + .shm = null, + .compositor = null, + .wm_base = null, + }; + + client.set_listener(registry, *App, registryListener, &context); + try client.roundtrip(); + + const compositor = context.compositor orelse return error.NoWlCompositor; + const wm_base = context.wm_base orelse return error.NoXdgWmBase; + + var surface: SurfaceCtx = b: { + const wl_surface = client.request(compositor, .create_surface, .{}); + const xdg_surface = client.request(wm_base, .get_xdg_surface, .{ .surface = wl_surface }); + const xdg_toplevel = client.request(xdg_surface, .get_toplevel, .{}); + + client.request(xdg_toplevel, .set_min_size, .{ .width = 500, .height = 200 }); + client.request(xdg_toplevel, .set_title, .{ .title = "Demo" }); + + client.request(wl_surface, .commit, {}); + + break :b .{ + .xdg_surface = xdg_surface, + .xdg_toplevel = xdg_toplevel, + .wl_surface = wl_surface, + .width = 100, + .height = 100, + .last_frame = 0, + .offset = 0, + .ctx = &context, + }; + }; + defer client.request(surface.wl_surface, .destroy, {}); + defer client.request(surface.xdg_toplevel, .destroy, {}); + defer client.request(surface.xdg_surface, .destroy, {}); + + client.set_listener(surface.xdg_surface, *SurfaceCtx, xdg_surface_listener, &surface); + client.set_listener(surface.xdg_toplevel, *SurfaceCtx, xdg_toplevel_listener, &surface); + try client.roundtrip(); + + const buf = try Buffer.get(client, surface.ctx.shm.?, surface.width, surface.height); + client.request(surface.wl_surface, .attach, .{ .buffer = buf.wl_buffer, .x = 0, .y = 0 }); + + client.request(surface.wl_surface, .commit, {}); + try client.roundtrip(); + + const frame_cb = client.request(surface.wl_surface, .frame, .{}); + client.set_listener(frame_cb, *SurfaceCtx, frame_listener, &surface); + client.request(surface.wl_surface, .commit, {}); + + try client.recvEvents(); +} + +fn registryListener(client: *wayland.Client, registry: wl.Registry, event: wl.Registry.Event, context: *App) void { + switch (event) { + .global => |global| { + if (mem.orderZ(u8, global.interface, wl.Compositor.interface.name) == .eq) { + context.compositor = client.bind(registry, global.name, wl.Compositor, 1); + } else if (mem.orderZ(u8, global.interface, wl.Shm.interface.name) == .eq) { + context.shm = client.bind(registry, global.name, wl.Shm, 1); + } else if (mem.orderZ(u8, global.interface, xdg.WmBase.interface.name) == .eq) { + context.wm_base = client.bind(registry, global.name, xdg.WmBase, 1); + } + }, + .global_remove => {}, + } +} + +const palette = [_]u32{ 0xff1a1c2c, 0xff5d275d, 0xffb13e53, 0xffef7d57, 0xffffcd75, 0xffa7f070, 0xff38b764, 0xff257179, 0xff29366f, 0xff3b5dc9, 0xff41a6f6, 0xff73eff7, 0xfff4f4f4, 0xff94b0c2, 0xff566c86, 0xff333c57 }; + +fn draw(buf: []align(32) u8, width: u32, height: u32, _offset: f32) void { + const data_u32: []u32 = std.mem.bytesAsSlice(u32, buf); + + const sin = std.math.sin; + for (0..height) |y| { + for (0..width) |x| { + const x_f: f32, const y_f: f32 = .{ @floatFromInt(x), @floatFromInt(y) }; + const c = sin(x_f / 80) + sin(y_f / 80) + sin(_offset / 80); + const index: i64 = @intFromFloat(c * 4); + data_u32[y * width + x] = palette[@abs(index) % 16]; + } + } +} + +fn draw2(buf: []align(4096) u8, width: u32, height: u32, _offset: f32) void { + const offset_int: u32 = @intFromFloat(_offset); + const offset = offset_int % 8; + const data_u32: []u32 = std.mem.bytesAsSlice(u32, buf); + for (0..height) |y| { + for (0..width) |x| { + if (((x + offset) + (y + offset) / 8 * 8) % 16 < 8) { + // if ((x + y / 8 * 8) % 16 < 8) { + data_u32[y * width + x] = 0xFF666666; + } else { + data_u32[y * width + x] = 0xFFEEEEEE; + } + } + } +} + +fn xdg_surface_listener(client: *wayland.Client, xdg_surface: xdg.Surface, event: xdg.Surface.Event, surf: *SurfaceCtx) void { + _ = surf; + switch (event) { + .configure => |configure| { + client.request(xdg_surface, .ack_configure, .{ .serial = configure.serial }); + }, + } +} + +fn xdg_toplevel_listener(_: *wayland.Client, _: xdg.Toplevel, event: xdg.Toplevel.Event, surf: *SurfaceCtx) void { + switch (event) { + .configure => |configure| { + std.log.warn("new size {} {}", .{ configure.width, configure.height }); + surf.width = @intCast(configure.width); + surf.height = @intCast(configure.height); + }, + .close => { + surf.ctx.running = false; + }, + else => {}, + } +} + +fn frame_listener(client: *wayland.Client, cb: wl.Callback, event: wl.Callback.Event, surf: *SurfaceCtx) void { + _ = cb; + switch (event) { + .done => |done| { + const time = done.callback_data; + defer surf.last_frame = time; + + const frame_cb = client.request(surf.wl_surface, .frame, .{}); + + client.set_listener(frame_cb, *SurfaceCtx, frame_listener, surf); + + if (surf.last_frame != 0) { + const elapsed: f32 = @floatFromInt(time - surf.last_frame); + surf.offset += elapsed / 1000.0 * 24; + } + + defer client.request(surf.wl_surface, .commit, {}); + + const buf = Buffer.get(client, surf.ctx.shm.?, surf.width, surf.height) catch return; + draw(buf.mem(), surf.width, surf.height, surf.offset); + client.request(surf.wl_surface, .attach, .{ .buffer = buf.wl_buffer, .x = 0, .y = 0 }); + client.request(surf.wl_surface, .damage, .{ + .x = 0, + .y = 0, + .width = std.math.maxInt(i32), + .height = std.math.maxInt(i32), + }); + }, + } +} + +const std = @import("std"); +const mem = std.mem; + +const wayland = @import("wayland"); +const wl = wayland.wl; +const xdg = wayland.xdg; + +const Buffer = wayland.shm.Buffer; diff --git a/wayland/examples/hello.zig b/wayland/examples/hello.zig index 9e62599..b12ca89 100644 --- a/wayland/examples/hello.zig +++ b/wayland/examples/hello.zig @@ -6,7 +6,6 @@ const App = struct { shm: ?wl.Shm, compositor: ?wl.Compositor, wm_base: ?xdg.WmBase, - running: bool = true, }; const SurfaceCtx = struct { @@ -16,8 +15,6 @@ const SurfaceCtx = struct { xdg_toplevel: xdg.Toplevel, width: u31, height: u31, - offset: f32, - last_frame: u32, }; pub fn main() !void { @@ -45,7 +42,6 @@ pub fn main() !void { const xdg_surface = client.request(wm_base, .get_xdg_surface, .{ .surface = wl_surface }); const xdg_toplevel = client.request(xdg_surface, .get_toplevel, .{}); - client.request(xdg_toplevel, .set_min_size, .{ .width = 500, .height = 200 }); client.request(xdg_toplevel, .set_title, .{ .title = "Demo" }); client.request(wl_surface, .commit, {}); @@ -56,8 +52,6 @@ pub fn main() !void { .wl_surface = wl_surface, .width = 100, .height = 100, - .last_frame = 0, - .offset = 0, .ctx = &context, }; }; @@ -66,18 +60,6 @@ pub fn main() !void { defer client.request(surface.xdg_surface, .destroy, {}); client.set_listener(surface.xdg_surface, *SurfaceCtx, xdg_surface_listener, &surface); - client.set_listener(surface.xdg_toplevel, *SurfaceCtx, xdg_toplevel_listener, &surface); - try client.roundtrip(); - - const buf = try Buffer.get(client, surface.ctx.shm.?, surface.width, surface.height); - client.request(surface.wl_surface, .attach, .{ .buffer = buf.wl_buffer, .x = 0, .y = 0 }); - - client.request(surface.wl_surface, .commit, {}); - try client.roundtrip(); - - const frame_cb = client.request(surface.wl_surface, .frame, .{}); - client.set_listener(frame_cb, *SurfaceCtx, frame_listener, &surface); - client.request(surface.wl_surface, .commit, {}); try client.recvEvents(); } @@ -97,30 +79,13 @@ fn registryListener(client: *wayland.Client, registry: wl.Registry, event: wl.Re } } -const palette = [_]u32{ 0xff1a1c2c, 0xff5d275d, 0xffb13e53, 0xffef7d57, 0xffffcd75, 0xffa7f070, 0xff38b764, 0xff257179, 0xff29366f, 0xff3b5dc9, 0xff41a6f6, 0xff73eff7, 0xfff4f4f4, 0xff94b0c2, 0xff566c86, 0xff333c57 }; - -fn draw(buf: []align(4096) u8, width: u32, height: u32, _offset: f32) void { - const data_u32: []u32 = std.mem.bytesAsSlice(u32, buf); - - const sin = std.math.sin; - for (0..height) |y| { - for (0..width) |x| { - const x_f: f32, const y_f: f32 = .{ @floatFromInt(x), @floatFromInt(y) }; - const c = sin(x_f / 80) + sin(y_f / 80) + sin(_offset / 80); - const index: i64 = @intFromFloat(c * 4); - data_u32[y * width + x] = palette[@abs(index) % 16]; - } - } -} - -fn draw2(buf: []align(4096) u8, width: u32, height: u32, _offset: f32) void { +fn draw(buf: []align(32) u8, width: u32, height: u32, _offset: f32) void { const offset_int: u32 = @intFromFloat(_offset); const offset = offset_int % 8; const data_u32: []u32 = std.mem.bytesAsSlice(u32, buf); for (0..height) |y| { for (0..width) |x| { if (((x + offset) + (y + offset) / 8 * 8) % 16 < 8) { - // if ((x + y / 8 * 8) % 16 < 8) { data_u32[y * width + x] = 0xFF666666; } else { data_u32[y * width + x] = 0xFFEEEEEE; @@ -130,55 +95,13 @@ fn draw2(buf: []align(4096) u8, width: u32, height: u32, _offset: f32) void { } fn xdg_surface_listener(client: *wayland.Client, xdg_surface: xdg.Surface, event: xdg.Surface.Event, surf: *SurfaceCtx) void { - _ = surf; switch (event) { .configure => |configure| { client.request(xdg_surface, .ack_configure, .{ .serial = configure.serial }); - }, - } -} - -fn xdg_toplevel_listener(_: *wayland.Client, _: xdg.Toplevel, event: xdg.Toplevel.Event, surf: *SurfaceCtx) void { - switch (event) { - .configure => |configure| { - std.log.warn("new size {} {}", .{ configure.width, configure.height }); - surf.width = @intCast(configure.width); - surf.height = @intCast(configure.height); - }, - .close => { - surf.ctx.running = false; - }, - else => {}, - } -} - -fn frame_listener(client: *wayland.Client, cb: wl.Callback, event: wl.Callback.Event, surf: *SurfaceCtx) void { - _ = cb; - switch (event) { - .done => |done| { - const time = done.callback_data; - defer surf.last_frame = time; - - const frame_cb = client.request(surf.wl_surface, .frame, .{}); - - client.set_listener(frame_cb, *SurfaceCtx, frame_listener, surf); - - if (surf.last_frame != 0) { - const elapsed: f32 = @floatFromInt(time - surf.last_frame); - surf.offset += elapsed / 1000.0 * 24; - } - - defer client.request(surf.wl_surface, .commit, {}); - - const buf = Buffer.get(client, surf.ctx.shm.?, surf.width, surf.height) catch return; - draw(buf.pool.mmap, surf.width, surf.height, surf.offset); + const buf = Buffer.get(client, surf.ctx.shm.?, surf.width, surf.height) catch @panic("TODO"); + draw(buf.mem(), surf.width, surf.height, 0); client.request(surf.wl_surface, .attach, .{ .buffer = buf.wl_buffer, .x = 0, .y = 0 }); - client.request(surf.wl_surface, .damage, .{ - .x = 0, - .y = 0, - .width = std.math.maxInt(i32), - .height = std.math.maxInt(i32), - }); + client.request(surf.wl_surface, .commit, {}); }, } } @@ -189,5 +112,4 @@ const mem = std.mem; const wayland = @import("wayland"); const wl = wayland.wl; const xdg = wayland.xdg; - const Buffer = wayland.shm.Buffer; diff --git a/wayland/examples/kb_grab.zig b/wayland/examples/kb_grab.zig index c0118b8..2645d5b 100644 --- a/wayland/examples/kb_grab.zig +++ b/wayland/examples/kb_grab.zig @@ -79,7 +79,7 @@ pub fn main() !void { try client.roundtrip(); const buf = try Buffer.get(client, shm, surface.width, surface.height); - @memset(buf.pool.mmap, 0xff); + @memset(buf.mem(), 0xff); client.request(surface.wl_surface, .attach, .{ .buffer = buf.wl_buffer, .x = 0, .y = 0 }); client.request(surface.wl_surface, .commit, {}); diff --git a/wayland/lib.zig b/wayland/lib.zig index b8dbdc4..b6bc4e2 100644 --- a/wayland/lib.zig +++ b/wayland/lib.zig @@ -8,3 +8,7 @@ pub const xdg = @import("generated/xdg.zig"); pub const zwlr = @import("generated/zwlr.zig"); pub const wp = @import("generated/wp.zig"); pub const zwp = @import("generated/zwp.zig"); + +test { + _ = @import("shm.zig"); +} diff --git a/wayland/shm.zig b/wayland/shm.zig index 990f321..4364dc9 100644 --- a/wayland/shm.zig +++ b/wayland/shm.zig @@ -3,17 +3,172 @@ const os = std.posix; const wl = @import("generated/wl.zig"); const way = @import("lib.zig"); -fn bufferListener(_: *way.Client, _: wl.Buffer, event: wl.Buffer.Event, buffer: *Buffer) void { - switch (event) { - .release => { - // std.log.warn("release {}x{}", .{ buffer.width, buffer.height }); - std.debug.assert(buffer.busy == true); - buffer.busy = false; - }, +pub const AutoMemPool = struct { + pub const FreeItem = struct { offset: u31, len: u31 }; + pool: Pool, + free_list: std.ArrayListUnmanaged(FreeItem), + buffers: std.AutoHashMapUnmanaged(wl.Buffer, Buffer) = .{}, + pub fn init(client: *way.Client, shm: wl.Shm) !AutoMemPool { + const fl = try std.ArrayListUnmanaged(FreeItem).initCapacity(client.allocator, 10); + var buffers = std.AutoHashMapUnmanaged(wl.Buffer, Buffer){}; + try buffers.ensureTotalCapacity(client.allocator, 6); + return .{ + .pool = try Pool.init(client, shm, 200, 200), + .free_list = fl, + .buffers = buffers, + }; + } + + fn alloc(self: *AutoMemPool, client: *way.Client, size: u31) u31 { + if (self.free_list.items.len == 0) { + self.free_list.appendAssumeCapacity(.{ .offset = 0, .len = @intCast(self.pool.size) }); + } + for (self.free_list.items) |*item| { + if (item.len >= size) { + const r = item.offset; + item.len -= size; + item.offset += size; + return r; + } + } + const pool_size: u31 = @intCast(self.pool.size); + var r = pool_size; + var pop = false; + if (self.free_list.getLastOrNull()) |last| { + if (last.offset + last.len == self.pool.size) { + r -= last.len; + pop = true; + } + } + + const target = @max(r + size, pool_size * 2); + self.pool.resize(client, @intCast(target)) catch unreachable; + + if (pop) _ = self.free_list.pop(); + + if (target > r + size) { + self.free_list.appendAssumeCapacity(.{ .offset = r + size, .len = target - r - size }); + } + return r; + } + + fn free(self: *AutoMemPool, offset_r: u31, len_r: u31) void { + var offset = offset_r; + var len = len_r; + { + const start: usize = for (self.free_list.items, 0..) |item, i| { + if (item.offset + item.len == offset) { + break i; + } + if (item.offset == offset + len) { + break i; + } + } else self.free_list.items.len; + + const l = b: { + var res: u31 = 0; + for (self.free_list.items[start..]) |item| { + if (item.offset + item.len == offset) { + offset = item.offset; + len += item.len; + res += 1; + continue; + } + if (item.offset == offset + len) { + len += item.len; + res += 1; + continue; + } + break :b res; + } + break :b res; + }; + // std.log.info("free={} {}", .{ start, l }); + self.free_list.replaceRangeAssumeCapacity(start, l, &[_]FreeItem{.{ .offset = offset, .len = len }}); + } + } + + pub fn buffer( + self: *AutoMemPool, + client: *way.Client, + width: u31, + height: u31, + ) *Buffer { + const stride = width * 4; + const size = stride * height; + const offset = self.alloc(client, size); + const wl_buffer = client.request(self.pool.wl_pool, .create_buffer, .{ + .offset = @intCast(offset), + .width = @intCast(width), + .height = @intCast(height), + .stride = @intCast(stride), + .format = wl.Shm.Format.argb8888, + }); + + const res = self.buffers.getOrPutAssumeCapacity(wl_buffer) ; + const buf = res.value_ptr; + buf.* = Buffer{ + .amp = self, + .width = width, + .height = height, + .offset = offset, + .wl_buffer = wl_buffer, + }; + + const w = struct { + fn bufferListener(c: *way.Client, _: wl.Buffer, event: wl.Buffer.Event, b: *Buffer) void { + switch (event) { + .release => { + // std.log.info("release", .{}); + c.request(b.wl_buffer, .destroy, {}); + b.amp.free(b.offset, b.size()); + _=b.amp.buffers.remove(b.wl_buffer); + }, + } + } + }; + client.set_listener(wl_buffer, *Buffer, w.bufferListener, buf); + return buf; + } +}; + +test "free" { + const fl = try std.ArrayListUnmanaged(AutoMemPool.FreeItem).initCapacity(std.testing.allocator, 10); + var p = AutoMemPool{ .pool = undefined, .free_list = fl }; + defer p.free_list.clearAndFree(std.testing.allocator); + + { + p.free_list.appendAssumeCapacity(.{ .offset = 3, .len = 2 }); + p.free(2, 1); + try std.testing.expectEqualSlices(AutoMemPool.FreeItem, &.{.{ .offset = 2, .len = 3 }}, p.free_list.items); + p.free_list.clearRetainingCapacity(); } + { + p.free_list.appendAssumeCapacity(.{ .offset = 0, .len = 2 }); + p.free(2, 3); + try std.testing.expectEqualSlices(AutoMemPool.FreeItem, &.{.{ .offset = 0, .len = 5 }}, p.free_list.items); + p.free_list.clearRetainingCapacity(); + } + { + p.free_list.appendAssumeCapacity(.{ .offset = 0, .len = 2 }); + p.free_list.appendAssumeCapacity(.{ .offset = 4, .len = 2 }); + p.free(2, 2); + try std.testing.expectEqualSlices(AutoMemPool.FreeItem, &.{.{ .offset = 0, .len = 6 }}, p.free_list.items); + p.free_list.clearRetainingCapacity(); + } + { + p.free_list.appendAssumeCapacity(.{ .offset = 0, .len = 2 }); + p.free(19, 2); + try std.testing.expectEqualSlices(AutoMemPool.FreeItem, &.{ + .{ .offset = 0, .len = 2 }, + .{ .offset = 19, .len = 2 }, + }, p.free_list.items); + p.free_list.clearRetainingCapacity(); + } + std.debug.print("list {any}", .{p.free_list.items}); } -pub const Pool = struct { +const Pool = struct { const max_size = 512 * 1024 * 1024; wl_pool: wl.ShmPool = undefined, backing_fd: os.fd_t = -1, @@ -43,42 +198,6 @@ pub const Pool = struct { }; } - pub fn get_buffer(pool: *Pool, client: *way.Client, width: u32, height: u32) *Buffer { - const stride = width * 4; - // std.debug.assert(stride * height <= pool.size); - if (pool.size < stride * height) { - pool.resize(client, stride * height) catch unreachable; - } - - defer pool.buffer.?.busy = true; - - if (pool.buffer) |*bfr| { - if (bfr.width == width and bfr.height == height) { - std.debug.assert(bfr.busy == false); - return bfr; - } - client.request(bfr.wl_buffer, .destroy, {}); - } - - const wl_buffer = client.request(pool.wl_pool, .create_buffer, .{ - .offset = 0, - .width = @intCast(width), - .height = @intCast(height), - .stride = @intCast(stride), - .format = wl.Shm.Format.argb8888, - }); - pool.buffer = .{ - .pool = pool, - .width = @intCast(width), - .height = @intCast(height), - .busy = false, - .wl_buffer = wl_buffer, - }; - - client.set_listener(wl_buffer, *Buffer, bufferListener, &pool.buffer.?); - return &pool.buffer.?; - } - pub fn resize(self: *Pool, client: *way.Client, newsize: u32) !void { if (newsize > self.size) { try os.ftruncate(self.backing_fd, newsize); @@ -91,56 +210,28 @@ pub const Pool = struct { }; pub const Buffer = struct { - pool: *Pool, + amp: *AutoMemPool, width: u31, height: u31, - busy: bool, + offset: u31, wl_buffer: wl.Buffer, + pub fn size(b: *const Buffer) u31 { + return b.width * b.height * 4; + } + pub fn mem(b: *const Buffer) []align(32) u8 { + return @alignCast(b.amp.pool.mmap[b.offset..][0..b.size()]); + } + pub fn get(client: *way.Client, shm: wl.Shm, _width: u31, _height: u31) !*Buffer { const w = struct { - var pools: [1]Pool = [1]Pool{.{}} ** 1; + var amp: ?AutoMemPool = null; }; + if (w.amp == null) w.amp = try AutoMemPool.init(client, shm); const width = if (_width == 0) 300 else _width; const height = if (_height == 0) 300 else _height; - std.log.info("pool width={} height={}", .{ width, height }); - - for (&w.pools) |*pool| { - std.log.info("pool fd {}", .{pool.backing_fd}); - } - for (&w.pools) |*pool| { - if (pool.backing_fd == -1) pool.* = try Pool.init(client, shm, width, height); - // if (pool.size < width * height * 4) continue; - if (pool.buffer != null and pool.buffer.?.busy) continue; - // if (pool.buffer == null) return pool.get_buffer(client, width, height); - - return pool.get_buffer(client, width, height); - } - return error.BufferBuzy; - } - - pub fn resize(self: *Buffer, width: u31, height: u31) !void { - const stride = width * 4; - const newsize = stride * height; - try self.pool.resize(newsize); - if (self.width != width or self.height != height) { - self.client.request(self.wl_buffer, .destroy, {}); - self.wl_buffer = self.client.request(self.pool.wl_pool, .create_buffer, .{ - .offset = 0, - .width = @intCast(width), - .height = @intCast(height), - .stride = @intCast(stride), - .format = wl.Shm.Format.argb8888, - }); - self.client.set_listener(self.wl_buffer, *Buffer, bufferListener, self); - self.width = width; - self.height = height; - } + // std.log.info("pool width={} height={}", .{ width, height }); + return w.amp.?.buffer(client, width, height); } - - // pub fn get_mem(self: *Buffer) []align(4096) u8 { - // const pool: *Pool = @alignCast(@fieldParentPtr("buffer", @as(*?Buffer, @ptrCast(self)))); - // return pool.mmap; - // } };