add game code loading
This commit is contained in:
parent
6407263540
commit
9d733902b5
@ -20,7 +20,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.has_imgui = b.option(bool, "imgui", "ImGui integration"),
|
.has_imgui = b.option(bool, "imgui", "ImGui integration"),
|
||||||
.has_tracy = b.option(bool, "tracy", "Tracy integration"),
|
.has_tracy = b.option(bool, "tracy", "Tracy integration"),
|
||||||
.win32_has_console = b.option(bool, "console", "Show console (Window only)"),
|
.win32_has_console = b.option(bool, "console", "Show console (Window only)"),
|
||||||
.win32_png_icon = b.path("src/assets/icon.png")
|
.win32_png_icon = b.path("src/assets/icon.png"),
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@ -14,6 +14,7 @@ const InitOptions = struct {
|
|||||||
|
|
||||||
dep_engine: *std.Build.Dependency,
|
dep_engine: *std.Build.Dependency,
|
||||||
|
|
||||||
|
hot_reload: ?bool = null,
|
||||||
has_imgui: ?bool = null,
|
has_imgui: ?bool = null,
|
||||||
has_tracy: ?bool = null,
|
has_tracy: ?bool = null,
|
||||||
win32_has_console: ?bool = null,
|
win32_has_console: ?bool = null,
|
||||||
@ -23,12 +24,19 @@ const InitOptions = struct {
|
|||||||
pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||||
const b = opts.dep_engine.builder;
|
const b = opts.dep_engine.builder;
|
||||||
|
|
||||||
|
const isWasm = opts.target.result.cpu.arch.isWasm();
|
||||||
|
|
||||||
|
var hot_reload = false;
|
||||||
|
if (!isWasm) {
|
||||||
|
hot_reload = opts.hot_reload orelse (opts.optimize == .Debug);
|
||||||
|
}
|
||||||
|
|
||||||
opts.root_module.resolved_target = opts.target;
|
opts.root_module.resolved_target = opts.target;
|
||||||
opts.root_module.optimize = opts.optimize;
|
opts.root_module.optimize = opts.optimize;
|
||||||
const game_lib = b.addLibrary(.{
|
const game_lib = b.addLibrary(.{
|
||||||
.name = "game",
|
.name = "game",
|
||||||
.root_module = opts.root_module,
|
.root_module = opts.root_module,
|
||||||
.linkage = .static,
|
.linkage = if (hot_reload) .dynamic else .static,
|
||||||
});
|
});
|
||||||
const engine_lib = b.createModule(.{
|
const engine_lib = b.createModule(.{
|
||||||
.root_source_file = b.path("src/lib/root.zig")
|
.root_source_file = b.path("src/lib/root.zig")
|
||||||
@ -39,7 +47,8 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
|||||||
.target = opts.target,
|
.target = opts.target,
|
||||||
.optimize = opts.optimize,
|
.optimize = opts.optimize,
|
||||||
.has_tracy = opts.has_tracy,
|
.has_tracy = opts.has_tracy,
|
||||||
.has_imgui = opts.has_imgui
|
.has_imgui = opts.has_imgui,
|
||||||
|
.hot_reload = hot_reload
|
||||||
});
|
});
|
||||||
|
|
||||||
const mod_main = b.createModule(.{
|
const mod_main = b.createModule(.{
|
||||||
@ -50,10 +59,12 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
|||||||
});
|
});
|
||||||
mod_main.addImport("lib", engine_lib);
|
mod_main.addImport("lib", engine_lib);
|
||||||
mod_main.addImport("engine", engine_module.root_module);
|
mod_main.addImport("engine", engine_module.root_module);
|
||||||
mod_main.linkLibrary(game_lib);
|
if (!hot_reload) {
|
||||||
|
mod_main.linkLibrary(game_lib);
|
||||||
|
}
|
||||||
|
|
||||||
var run_cmd_step: *std.Build.Step = undefined;
|
var run_cmd_step: *std.Build.Step = undefined;
|
||||||
if (opts.target.result.cpu.arch.isWasm()) {
|
if (isWasm) {
|
||||||
const build_step = buildWasm(b, .{
|
const build_step = buildWasm(b, .{
|
||||||
.name = "index",
|
.name = "index",
|
||||||
.root_module = mod_main,
|
.root_module = mod_main,
|
||||||
@ -83,6 +94,9 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
|||||||
if (outer_builder.args) |args| {
|
if (outer_builder.args) |args| {
|
||||||
run_cmd.addArgs(args);
|
run_cmd.addArgs(args);
|
||||||
}
|
}
|
||||||
|
if (hot_reload) {
|
||||||
|
run_cmd.addFileArg(game_lib.getEmittedBin());
|
||||||
|
}
|
||||||
run_cmd_step = &run_cmd.step;
|
run_cmd_step = &run_cmd.step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,6 +115,7 @@ const EngineModule = struct {
|
|||||||
|
|
||||||
has_imgui: ?bool = null,
|
has_imgui: ?bool = null,
|
||||||
has_tracy: ?bool = null,
|
has_tracy: ?bool = null,
|
||||||
|
hot_reload: bool
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -197,6 +212,7 @@ fn createEngineModule(b: *std.Build, opts: EngineModule.Options) EngineModule {
|
|||||||
var options = b.addOptions();
|
var options = b.addOptions();
|
||||||
options.addOption(bool, "has_imgui", has_imgui);
|
options.addOption(bool, "has_imgui", has_imgui);
|
||||||
options.addOption(bool, "has_tracy", has_tracy);
|
options.addOption(bool, "has_tracy", has_tracy);
|
||||||
|
options.addOption(bool, "hot_reload", opts.hot_reload);
|
||||||
mod.addOptions("build_options", options);
|
mod.addOptions("build_options", options);
|
||||||
|
|
||||||
return EngineModule{
|
return EngineModule{
|
||||||
|
|||||||
@ -1,8 +1,34 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
const Engine = @import("engine");
|
const Engine = @import("engine");
|
||||||
|
|
||||||
var engine: Engine = undefined;
|
var engine: Engine = undefined;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
try engine.run(.{});
|
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||||
|
defer _ = debug_allocator.deinit();
|
||||||
|
|
||||||
|
// TODO: Use tracy TracingAllocator
|
||||||
|
var allocator: std.mem.Allocator = undefined;
|
||||||
|
if (builtin.cpu.arch.isWasm()) {
|
||||||
|
allocator = std.heap.wasm_allocator;
|
||||||
|
} else if (builtin.mode == .Debug) {
|
||||||
|
allocator = debug_allocator.allocator();
|
||||||
|
} else {
|
||||||
|
allocator = std.heap.smp_allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = try std.process.argsAlloc(allocator);
|
||||||
|
defer std.process.argsFree(allocator, args);
|
||||||
|
|
||||||
|
var game_library_path: ?[]const u8 = null;
|
||||||
|
if (args.len >= 2) {
|
||||||
|
game_library_path = args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
try engine.run(.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.game_library_path = game_library_path
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,39 +17,72 @@ const STBImage = @import("stb_image");
|
|||||||
|
|
||||||
const Gfx = Graphics;
|
const Gfx = Graphics;
|
||||||
|
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
const Lib = @import("lib");
|
const Lib = @import("lib");
|
||||||
pub const Math = Lib.Math;
|
pub const Math = Lib.Math;
|
||||||
pub const Vec2 = Math.Vec2;
|
pub const Vec2 = Math.Vec2;
|
||||||
const rgb = Math.rgb;
|
const rgb = Math.rgb;
|
||||||
|
|
||||||
const GameCallbacks = struct {
|
const GameCallbacks = struct {
|
||||||
init: *const fn(opts: *const Lib.Init) callconv(.c) void,
|
const Init = *const fn(opts: *const Lib.Init) callconv(.c) void;
|
||||||
tick: *const fn(frame: *Lib.Frame) callconv(.c) void,
|
const Tick = *const fn(frame: *Lib.Frame) callconv(.c) void;
|
||||||
deinit: *const fn() callconv(.c) void,
|
const Deinit = *const fn() callconv(.c) void;
|
||||||
debug: *const fn(imgui: *Lib.ImGui) callconv(.c) void,
|
const Debug = *const fn(imgui: *Lib.ImGui) callconv(.c) void;
|
||||||
|
|
||||||
|
init: Init,
|
||||||
|
tick: Tick,
|
||||||
|
deinit: Deinit,
|
||||||
|
debug: if (build_options.has_imgui) Debug else void
|
||||||
};
|
};
|
||||||
|
|
||||||
extern fn init(opts: *const Lib.Init) void;
|
extern fn init(opts: *const Lib.Init) void;
|
||||||
extern fn tick(frame: *Lib.Frame) void;
|
extern fn tick(frame: *Lib.Frame) void;
|
||||||
extern fn deinit() void;
|
extern fn deinit() void;
|
||||||
extern fn debug(imgui: *Lib.ImGui) void;
|
extern fn debug(imgui: *Lib.ImGui) void;
|
||||||
const static_game_callbacks = GameCallbacks{
|
|
||||||
.init = init,
|
|
||||||
.deinit = deinit,
|
|
||||||
.tick = tick,
|
|
||||||
.debug = debug,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const GameLinking = union(enum) {
|
||||||
|
static,
|
||||||
|
dynamic: struct {
|
||||||
|
lib: std.DynLib
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn getCallbacks(self: *GameLinking) !GameCallbacks {
|
||||||
|
switch (self.*) {
|
||||||
|
.static => {
|
||||||
|
if (build_options.hot_reload) {
|
||||||
|
return error.StaticLinkingDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GameCallbacks{
|
||||||
|
.init = init,
|
||||||
|
.deinit = deinit,
|
||||||
|
.tick = tick,
|
||||||
|
.debug = if (build_options.has_imgui) debug else {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.dynamic => |*opts| {
|
||||||
|
const lib = &opts.lib;
|
||||||
|
return GameCallbacks{
|
||||||
|
.init = lib.lookup(GameCallbacks.Init, "init") orelse return error.MissingFunction,
|
||||||
|
.tick = lib.lookup(GameCallbacks.Tick, "tick") orelse return error.MissingFunction,
|
||||||
|
.deinit = lib.lookup(GameCallbacks.Deinit, "deinit") orelse return error.MissingFunction,
|
||||||
|
.debug = if (build_options.has_imgui) lib.lookup(GameCallbacks.Debug, "debug") orelse return error.MissingFunction else {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Engine = @This();
|
const Engine = @This();
|
||||||
|
|
||||||
pub const Nanoseconds = u64;
|
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
started_at: std.time.Instant,
|
started_at: std.time.Instant,
|
||||||
last_frame_at: Nanoseconds,
|
last_frame_at: Lib.Nanoseconds,
|
||||||
graphics: Graphics,
|
graphics: Graphics,
|
||||||
input: Input,
|
input: Input,
|
||||||
|
|
||||||
|
game_linking: GameLinking,
|
||||||
game_callbacks: GameCallbacks,
|
game_callbacks: GameCallbacks,
|
||||||
|
|
||||||
frame: Lib.Frame,
|
frame: Lib.Frame,
|
||||||
@ -57,34 +90,34 @@ frame: Lib.Frame,
|
|||||||
previous_tick_canvas_size: ?Vec2 = null,
|
previous_tick_canvas_size: ?Vec2 = null,
|
||||||
|
|
||||||
const RunOptions = struct {
|
const RunOptions = struct {
|
||||||
window_title: [*:0]const u8 = "Game",
|
allocator: std.mem.Allocator,
|
||||||
window_width: u31 = 640,
|
game_library_path: ?[]const u8 = null
|
||||||
window_height: u31 = 480,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(self: *Engine, opts: RunOptions) !void {
|
pub fn run(self: *Engine, opts: RunOptions) !void {
|
||||||
self.* = Engine{
|
self.* = Engine{
|
||||||
.allocator = undefined,
|
.allocator = opts.allocator,
|
||||||
.started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"),
|
.started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"),
|
||||||
.last_frame_at = 0,
|
.last_frame_at = 0,
|
||||||
.frame = undefined,
|
.frame = undefined,
|
||||||
.graphics = undefined,
|
.graphics = undefined,
|
||||||
.game_callbacks = static_game_callbacks,
|
.game_linking = undefined,
|
||||||
|
.game_callbacks = undefined,
|
||||||
.input = .{}
|
.input = .{}
|
||||||
};
|
};
|
||||||
|
|
||||||
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
if (opts.game_library_path) |game_library_path| {
|
||||||
defer _ = debug_allocator.deinit();
|
self.game_linking = .{
|
||||||
|
.dynamic = .{
|
||||||
// TODO: Use tracy TracingAllocator
|
.lib = try std.DynLib.open(game_library_path)
|
||||||
if (builtin.cpu.arch.isWasm()) {
|
}
|
||||||
self.allocator = std.heap.wasm_allocator;
|
};
|
||||||
} else if (builtin.mode == .Debug) {
|
|
||||||
self.allocator = debug_allocator.allocator();
|
|
||||||
} else {
|
} else {
|
||||||
self.allocator = std.heap.smp_allocator;
|
self.game_linking = .static;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.game_callbacks = try self.game_linking.getCallbacks();
|
||||||
|
|
||||||
self.frame.init(self.allocator);
|
self.frame.init(self.allocator);
|
||||||
|
|
||||||
tracy.setThreadName("Main");
|
tracy.setThreadName("Main");
|
||||||
@ -120,10 +153,10 @@ pub fn run(self: *Engine, opts: RunOptions) !void {
|
|||||||
.cleanup_userdata_cb = sokolCleanupCallback,
|
.cleanup_userdata_cb = sokolCleanupCallback,
|
||||||
.event_userdata_cb = sokolEventCallback,
|
.event_userdata_cb = sokolEventCallback,
|
||||||
.user_data = self,
|
.user_data = self,
|
||||||
.width = opts.window_width,
|
.width = 640,
|
||||||
.height = opts.window_height,
|
.height = 480,
|
||||||
.icon = icon,
|
.icon = icon,
|
||||||
.window_title = opts.window_title,
|
.window_title = "Game",
|
||||||
.logger = .{ .func = sokolLogCallback },
|
.logger = .{ .func = sokolLogCallback },
|
||||||
.win32 = .{
|
.win32 = .{
|
||||||
.console_utf8 = true
|
.console_utf8 = true
|
||||||
@ -238,7 +271,7 @@ fn sokolFrame(self: *Engine) !void {
|
|||||||
|
|
||||||
self.graphics.drawCommands(frame.graphics_commands.items);
|
self.graphics.drawCommands(frame.graphics_commands.items);
|
||||||
|
|
||||||
if (frame.show_debug) {
|
if (frame.show_debug and build_options.has_imgui) {
|
||||||
try self.showDebugWindow(frame);
|
try self.showDebugWindow(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,6 +292,7 @@ fn showDebugWindow(self: *Engine, frame: *Lib.Frame) !void {
|
|||||||
defer imgui.endWindow();
|
defer imgui.endWindow();
|
||||||
|
|
||||||
_ = imgui.beginTabBar("debug");
|
_ = imgui.beginTabBar("debug");
|
||||||
|
defer imgui.endTabBar();
|
||||||
|
|
||||||
if (imgui.beginTabItem("Game")) {
|
if (imgui.beginTabItem("Game")) {
|
||||||
defer imgui.endTabItem();
|
defer imgui.endTabItem();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user