renderer mailbox, focus message

This commit is contained in:
Mitchell Hashimoto
2022-11-05 18:51:39 -07:00
parent 9a44e45785
commit e2d8ffc3c1
7 changed files with 77 additions and 12 deletions

View File

@@ -7,11 +7,16 @@ const builtin = @import("builtin");
const glfw = @import("glfw");
const libuv = @import("libuv");
const renderer = @import("../renderer.zig");
const gl = @import("../opengl.zig");
const BlockingQueue = @import("../blocking_queue.zig").BlockingQueue;
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.renderer_thread);
/// The type used for sending messages to the IO thread. For now this is
/// hardcoded with a capacity. We can make this a comptime parameter in
/// the future if we want it configurable.
const Mailbox = BlockingQueue(renderer.Message, 64);
/// The main event loop for the application. The user data of this loop
/// is always the allocator used to create the loop. This is a convenience
/// so that users of the loop always have an allocator.
@@ -36,6 +41,10 @@ renderer: *renderer.Renderer,
/// Pointer to the shared state that is used to generate the final render.
state: *renderer.State,
/// The mailbox that can be used to send this thread messages. Note
/// this is a blocking queue so if it is full you will get errors (or block).
mailbox: *Mailbox,
/// Initialize the thread. This does not START the thread. This only sets
/// up all the internal state necessary prior to starting the thread. It
/// is up to the caller to start the thread with the threadMain entrypoint.
@@ -83,6 +92,10 @@ pub fn init(
}
}).callback);
// The mailbox for messaging this thread
var mailbox = try Mailbox.create(alloc);
errdefer mailbox.destroy(alloc);
return Thread{
.loop = loop,
.wakeup = wakeup_h,
@@ -91,6 +104,7 @@ pub fn init(
.window = window,
.renderer = renderer_impl,
.state = state,
.mailbox = mailbox,
};
}
@@ -127,6 +141,9 @@ pub fn deinit(self: *Thread) void {
_ = self.loop.run(.default) catch |err|
log.err("error finalizing event loop: {}", .{err});
// Nothing can possibly access the mailbox anymore, destroy it.
self.mailbox.destroy(alloc);
// Dealloc our allocator copy
alloc.destroy(alloc_ptr);
@@ -164,6 +181,23 @@ fn threadMain_(self: *Thread) !void {
_ = try self.loop.run(.default);
}
/// Drain the mailbox.
fn drainMailbox(self: *Thread) !void {
// This holds the mailbox lock for the duration of the drain. The
// expectation is that all our message handlers will be non-blocking
// ENOUGH to not mess up throughput on producers.
var drain = self.mailbox.drain();
defer drain.deinit();
while (drain.next()) |message| {
log.debug("mailbox message={}", .{message});
switch (message) {
.focus => |v| try self.renderer.setFocus(v),
}
}
}
fn wakeupCallback(h: *libuv.Async) void {
const t = h.getData(Thread) orelse {
// This shouldn't happen so we log it.
@@ -171,6 +205,11 @@ fn wakeupCallback(h: *libuv.Async) void {
return;
};
// When we wake up, we check the mailbox. Mailbox producers should
// wake up our thread after publishing.
t.drainMailbox() catch |err|
log.err("error draining mailbox err={}", .{err});
// If the timer is already active then we don't have to do anything.
const active = t.render_h.isActive() catch true;
if (active) return;