From 7c640720050e088a37c15fc352aac1c9bd8d009a Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Fri, 27 Oct 2023 15:30:28 +0200 Subject: [PATCH] wlbg: only do one frame per minute F gpu --- scripts/wlbg/build.zig | 4 ++ scripts/wlbg/build.zig.zon | 4 ++ scripts/wlbg/src/Gfx.zig | 14 ++-- scripts/wlbg/src/main.zig | 134 ++++++++++++++++++++++++++++++------- 4 files changed, 125 insertions(+), 31 deletions(-) diff --git a/scripts/wlbg/build.zig b/scripts/wlbg/build.zig index d729927..181de7a 100644 --- a/scripts/wlbg/build.zig +++ b/scripts/wlbg/build.zig @@ -16,6 +16,10 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); + exe.addModule("xev", b.dependency("xev", .{ + .target = target, + .optimize = optimize, + }).module("xev")); exe.addModule("wayland", wayland_mod); scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml"); diff --git a/scripts/wlbg/build.zig.zon b/scripts/wlbg/build.zig.zon index 27e219c..758d799 100644 --- a/scripts/wlbg/build.zig.zon +++ b/scripts/wlbg/build.zig.zon @@ -7,5 +7,9 @@ .url = "https://git.mzte.de/LordMZTE/zig-wayland/archive/85722422985f928087e56d90c3617ecb04232486.tar.gz", .hash = "1220d992b223e473988d203d66d262e54141b59559c09587eb00231c800d46f9b408", }, + .xev = .{ + .url = "https://github.com/mitchellh/libxev/archive/5ecbc871f3bfa80fb7bf0fa853866cb93b99bc18.tar.gz", + .hash = "1220416854e424601ecc9814afb461a5dc9cf95db5917d82f794594a58ffc723b82c", + }, }, } diff --git a/scripts/wlbg/src/Gfx.zig b/scripts/wlbg/src/Gfx.zig index 70e49b1..a99093f 100644 --- a/scripts/wlbg/src/Gfx.zig +++ b/scripts/wlbg/src/Gfx.zig @@ -7,7 +7,6 @@ const OutputInfo = @import("OutputInfo.zig"); egl_dpy: c.EGLDisplay, bg_shader_program: c_uint, -time: f64, const Gfx = @This(); @@ -31,7 +30,6 @@ pub fn init(egl_dpy: c.EGLDisplay) !Gfx { return .{ .egl_dpy = egl_dpy, .bg_shader_program = program, - .time = 0.0, }; } @@ -40,15 +38,15 @@ pub fn deinit(self: *Gfx) void { self.* = undefined; } -pub fn draw( +pub fn drawBackground( self: *Gfx, - dt: f32, + dt: i64, egl_surface: c.EGLSurface, info: OutputInfo, base_xoff: i32, base_yoff: i32, ) !void { - self.time += dt; + _ = dt; // There's just about a 0% chance this works properly when monitors have different resolutions, // but I can't even begin thinking about that. @@ -76,9 +74,11 @@ pub fn draw( c.glVertexAttribPointer(1, 2, c.GL_FLOAT, c.GL_FALSE, @sizeOf(f32) * 5, @ptrFromInt(@intFromPtr(&vertices) + @sizeOf(f32) * 3)); c.glEnableVertexAttribArray(1); - c.glUniform1f(c.glGetUniformLocation(self.bg_shader_program, "time"), @as(f32, @floatCast(self.time / 10000.0))); + const rand = std.crypto.random.float(f32); + c.glUniform1f(c.glGetUniformLocation(self.bg_shader_program, "time"), rand * 2000.0 - 1000.0); c.glDrawArrays(c.GL_TRIANGLES, 0, vertices.len / 3); - if (c.eglSwapBuffers(self.egl_dpy, egl_surface) != c.EGL_TRUE) return error.EGLError; + if (c.eglSwapInterval(self.egl_dpy, 0) != c.EGL_TRUE or + c.eglSwapBuffers(self.egl_dpy, egl_surface) != c.EGL_TRUE) return error.EGLError; } diff --git a/scripts/wlbg/src/main.zig b/scripts/wlbg/src/main.zig index 81901e0..c6d6049 100644 --- a/scripts/wlbg/src/main.zig +++ b/scripts/wlbg/src/main.zig @@ -12,9 +12,11 @@ const wl = wayland.client.wl; const zwlr = wayland.client.zwlr; const zxdg = wayland.client.zxdg; -const fps = 10; - pub fn main() !void { + std.log.info("initializing event loop", .{}); + var loop = try xev.Loop.init(.{}); + defer loop.deinit(); + std.log.info("connecting to wayland display", .{}); const dpy = try wl.Display.connect(null); defer dpy.disconnect(); @@ -146,32 +148,116 @@ pub fn main() !void { var gfx = try Gfx.init(egl_dpy); defer gfx.deinit(); - var timer = try std.time.Timer.start(); - var prev_time = std.time.milliTimestamp(); - while (true) { - const now_time = std.time.milliTimestamp(); - const delta_time: f32 = @floatFromInt(now_time - prev_time); - prev_time = now_time; + var rbgdata = RenderBackgroundData{ + .gfx = &gfx, + .egl_dpy = egl_dpy, + .egl_ctx = egl_ctx, + .outputs = output_windows, + .output_info = output_info, + .last_time = loop.now(), + .base_offset = .{ base_xoff, base_yoff }, + }; - for (output_windows, output_info) |output, info| { - if (c.eglMakeCurrent( - egl_dpy, - output.egl_surface, - output.egl_surface, - egl_ctx, - ) != c.EGL_TRUE) return error.EGLError; - try gfx.draw(delta_time, output.egl_surface, info, base_xoff, base_yoff); + var rbg_timer_completion: xev.Completion = undefined; + var rbg_timer = try xev.Timer.init(); + defer rbg_timer.deinit(); + + rbg_timer.run( + &loop, + &rbg_timer_completion, + 0, + RenderBackgroundData, + &rbgdata, + renderBackgroundCb, + ); + + var wl_poll_completion = xev.Completion{ + .op = .{ .poll = .{ .fd = dpy.getFd() } }, + .userdata = dpy, + .callback = wlPollCb, + }; + loop.add(&wl_poll_completion); + + std.log.info("running event loop", .{}); + try loop.run(.until_done); +} + +const RenderBackgroundData = struct { + gfx: *Gfx, + egl_dpy: c.EGLDisplay, + egl_ctx: c.EGLContext, + outputs: []const OutputWindow, + output_info: []const OutputInfo, + last_time: isize, + base_offset: [2]c_int, +}; + +fn renderBackgroundCb( + data: ?*RenderBackgroundData, + loop: *xev.Loop, + completion: *xev.Completion, + result: xev.Timer.RunError!void, +) xev.CallbackAction { + result catch unreachable; + + const now = loop.now(); + + const delta_time = now - data.?.last_time; + data.?.last_time = now; + const next_time = now + std.time.ms_per_min; + completion.op.timer.reset = .{ + .tv_sec = @divTrunc(next_time, std.time.ms_per_s), + .tv_nsec = @mod(next_time, std.time.ms_per_s) * std.time.ns_per_ms, + }; + + for (data.?.outputs, data.?.output_info) |output, info| { + if (c.eglMakeCurrent( + data.?.egl_dpy, + output.egl_surface, + output.egl_surface, + data.?.egl_ctx, + ) != c.EGL_TRUE) { + std.log.err("failed to set EGL context", .{}); + loop.stop(); + return .disarm; } - if (dpy.dispatchPending() != .SUCCESS or dpy.flush() != .SUCCESS) { - std.log.err("error processing wayland events", .{}); - return error.DispatchFail; - } - - const elapsed = timer.lap(); - if (elapsed < 1000 * std.time.ns_per_ms / fps) - std.os.nanosleep(0, (1000 * std.time.ns_per_ms) / fps - elapsed); + data.?.gfx.drawBackground( + delta_time, + output.egl_surface, + info, + data.?.base_offset[0], + data.?.base_offset[1], + ) catch |e| { + std.log.err("drawing: {}", .{e}); + loop.stop(); + return .disarm; + }; } + + return .rearm; +} + +fn wlPollCb( + userdata: ?*anyopaque, + loop: *xev.Loop, + _: *xev.Completion, + result: xev.Result, +) xev.CallbackAction { + result.poll catch |e| { + std.log.err("unable to poll wayland FD: {}", .{e}); + loop.stop(); + return .disarm; + }; + + const dpy: *wl.Display = @ptrCast(@alignCast(userdata)); + if (dpy.dispatchPending() != .SUCCESS or dpy.flush() != .SUCCESS) { + std.log.err("error processing wayland events", .{}); + loop.stop(); + return .disarm; + } + + return .rearm; } const OutputWindow = struct {