core: add mach.Core module API
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
69b749879d
commit
013546b189
8 changed files with 309 additions and 41 deletions
67
src/Core.zig
Normal file
67
src/Core.zig
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
const std = @import("std");
|
||||
const mach = @import("main.zig");
|
||||
|
||||
pub const name = .mach_core;
|
||||
|
||||
pub const Mod = mach.Mod(@This());
|
||||
|
||||
pub const global_events = .{
|
||||
.init = .{ .handler = fn () void },
|
||||
.deinit = .{ .handler = fn () void },
|
||||
.tick = .{ .handler = fn () void },
|
||||
};
|
||||
|
||||
pub const local_events = .{
|
||||
.init = .{ .handler = init },
|
||||
|
||||
// TODO(important): need some way to tie event execution to a specific thread once we have a
|
||||
// multithreaded dispatch implementation
|
||||
.main_thread_tick = .{ .handler = mainThreadTick },
|
||||
.main_thread_tick_done = .{ .handler = fn () void },
|
||||
.deinit = .{ .handler = deinit },
|
||||
.exit = .{ .handler = exit },
|
||||
};
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
|
||||
device: *mach.gpu.Device,
|
||||
queue: *mach.gpu.Queue,
|
||||
should_exit: bool = false,
|
||||
|
||||
fn init(core: *Mod) !void {
|
||||
// Initialize GPU implementation
|
||||
if (comptime mach.core.options.use_wgpu) try mach.core.wgpu.Impl.init(mach.core.allocator, .{});
|
||||
if (comptime mach.core.options.use_sysgpu) try mach.core.sysgpu.Impl.init(mach.core.allocator, .{});
|
||||
|
||||
mach.core.allocator = gpa.allocator(); // TODO: banish this global allocator
|
||||
try mach.core.init(.{});
|
||||
|
||||
core.init(.{
|
||||
.device = mach.core.device,
|
||||
.queue = mach.core.device.getQueue(),
|
||||
});
|
||||
|
||||
core.sendGlobal(.init, .{});
|
||||
}
|
||||
|
||||
fn deinit(core: *Mod) void {
|
||||
core.state().queue.release();
|
||||
// TODO: this triggers a device loss error, which we should handle correctly
|
||||
// core.state().device.release();
|
||||
mach.core.deinit();
|
||||
_ = gpa.deinit();
|
||||
}
|
||||
|
||||
fn mainThreadTick(core: *Mod) !void {
|
||||
_ = try mach.core.update(null);
|
||||
|
||||
// Send .tick to anyone interested
|
||||
core.sendGlobal(.tick, .{});
|
||||
|
||||
// Signal that mainThreadTick is done
|
||||
core.send(.main_thread_tick_done, .{});
|
||||
}
|
||||
|
||||
fn exit(core: *Mod) void {
|
||||
core.state().should_exit = true;
|
||||
}
|
||||
|
|
@ -7,6 +7,30 @@ pub const Timer = @import("Timer.zig");
|
|||
const Frequency = @import("Frequency.zig");
|
||||
const platform = @import("platform.zig");
|
||||
|
||||
const mach = @import("../main.zig");
|
||||
pub var mods: mach.Modules = undefined;
|
||||
|
||||
pub fn initModule() !void {
|
||||
// Initialize the global set of Mach modules used in the program.
|
||||
try mods.init(std.heap.c_allocator);
|
||||
mods.mod.mach_core.send(.init, .{});
|
||||
}
|
||||
|
||||
/// Tick runs a single step of the main loop on the main OS thread.
|
||||
///
|
||||
/// Returns true if tick() should be called again, false if the application should exit.
|
||||
pub fn tick() !bool {
|
||||
mods.mod.mach_core.send(.main_thread_tick, .{});
|
||||
|
||||
// Dispatch events until this .mach_core.main_thread_tick_done is sent
|
||||
try mods.dispatch(.{ .until = .{
|
||||
.module_name = mods.moduleNameToID(.mach_core),
|
||||
.local_event = mods.localEventToID(.mach_core, .main_thread_tick_done),
|
||||
} });
|
||||
|
||||
return !mods.mod.mach_core.state().should_exit;
|
||||
}
|
||||
|
||||
/// Returns the error set that the function F returns.
|
||||
fn ErrorSet(comptime F: type) type {
|
||||
return @typeInfo(@typeInfo(F).Fn.return_type.?).ErrorUnion.error_set;
|
||||
|
|
|
|||
|
|
@ -581,13 +581,16 @@ pub fn appUpdateThreadTick(self: *Core, app: anytype) bool {
|
|||
});
|
||||
}
|
||||
|
||||
if (app.update() catch unreachable) {
|
||||
self.done.set();
|
||||
const use_app = @typeInfo(@TypeOf(app)) == .Pointer;
|
||||
if (use_app) {
|
||||
if (app.update() catch unreachable) {
|
||||
self.done.set();
|
||||
|
||||
// Wake the main thread from any event handling, so there is not e.g. a one second delay
|
||||
// in exiting the application.
|
||||
glfw.postEmptyEvent();
|
||||
return false;
|
||||
// Wake the main thread from any event handling, so there is not e.g. a one second delay
|
||||
// in exiting the application.
|
||||
glfw.postEmptyEvent();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
self.gpu_device.tick();
|
||||
self.gpu_device.machWaitForCommandsToBeScheduled();
|
||||
|
|
@ -605,10 +608,13 @@ pub fn appUpdateThread(self: *Core, app: anytype) void {
|
|||
// Called on the main thread
|
||||
pub fn update(self: *Core, app: anytype) !bool {
|
||||
if (self.done.isSet()) return true;
|
||||
if (!self.app_update_thread_started) {
|
||||
self.app_update_thread_started = true;
|
||||
const thread = try std.Thread.spawn(.{}, appUpdateThread, .{ self, app });
|
||||
thread.detach();
|
||||
const use_app = @typeInfo(@TypeOf(app)) == .Pointer;
|
||||
if (use_app) {
|
||||
if (!self.app_update_thread_started) {
|
||||
self.app_update_thread_started = true;
|
||||
const thread = try std.Thread.spawn(.{}, appUpdateThread, .{ self, app });
|
||||
thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
if (self.state_update.isSet()) {
|
||||
|
|
@ -748,14 +754,18 @@ pub fn update(self: *Core, app: anytype) !bool {
|
|||
}
|
||||
}
|
||||
|
||||
const frequency_delay = @as(f32, @floatFromInt(self.input.delay_ns)) / @as(f32, @floatFromInt(std.time.ns_per_s));
|
||||
glfw.waitEventsTimeout(frequency_delay);
|
||||
if (use_app) {
|
||||
const frequency_delay = @as(f32, @floatFromInt(self.input.delay_ns)) / @as(f32, @floatFromInt(std.time.ns_per_s));
|
||||
glfw.waitEventsTimeout(frequency_delay);
|
||||
|
||||
if (@hasDecl(std.meta.Child(@TypeOf(app)), "updateMainThread")) {
|
||||
if (app.updateMainThread() catch unreachable) {
|
||||
self.done.set();
|
||||
return true;
|
||||
if (@hasDecl(std.meta.Child(@TypeOf(app)), "updateMainThread")) {
|
||||
if (app.updateMainThread() catch unreachable) {
|
||||
self.done.set();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glfw.pollEvents();
|
||||
}
|
||||
|
||||
glfw.getErrorCode() catch |err| switch (err) {
|
||||
|
|
@ -764,6 +774,8 @@ pub fn update(self: *Core, app: anytype) !bool {
|
|||
else => unreachable,
|
||||
};
|
||||
self.input.tick();
|
||||
|
||||
if (!use_app) return !self.appUpdateThreadTick(app);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ pub const core = if (build_options.want_core) @import("core/main.zig") else stru
|
|||
pub const Timer = if (build_options.want_core) core.Timer else struct {};
|
||||
pub const gpu = if (build_options.want_core) core.gpu else struct {};
|
||||
pub const sysjs = if (build_options.want_core) @import("mach-sysjs") else struct {};
|
||||
pub const Core = if (build_options.want_core) @import("Core.zig") else struct {};
|
||||
|
||||
// Mach standard library
|
||||
// gamemode requires libc on linux
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue