diff --git a/src/game.zig b/src/game.zig index d92e94f..9d312f8 100644 --- a/src/game.zig +++ b/src/game.zig @@ -1,14 +1,62 @@ const std = @import("std"); const c = @import("c.zig"); +pub const InitMemory = struct { + global_allocator: std.mem.Allocator, + window: *c.SDL_Window, + context: ?*anyopaque, + some_var: i32 = 0, +}; + pub const GameMemory = struct { global_allocator: std.mem.Allocator, counter: i32 = 0, }; +var g_init: *InitMemory = undefined; var g_mem: *GameMemory = undefined; +fn game_init_window_err(global_allocator: std.mem.Allocator) !void { + try sdl_try(c.SDL_Init(c.SDL_INIT_EVERYTHING)); + + try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_DOUBLEBUFFER, 1)); + try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MAJOR_VERSION, 4)); + try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MINOR_VERSION, 1)); + try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_PROFILE_MASK, c.SDL_GL_CONTEXT_PROFILE_CORE)); + + const maybe_window = c.SDL_CreateWindow("Learn OpenGL with Zig!", c.SDL_WINDOWPOS_CENTERED, c.SDL_WINDOWPOS_CENTERED, 800, 600, c.SDL_WINDOW_SHOWN | c.SDL_WINDOW_OPENGL); + if (maybe_window == null) { + std.log.err("SDL Error: {s}", .{c.SDL_GetError()}); + return error.SDLWindowError; + } + const window = maybe_window.?; + + const context = c.SDL_GL_CreateContext(window); + + if (c.gladLoadGLLoader(c.SDL_GL_GetProcAddress) == 0) { + return error.GladInitError; + } + + std.log.debug("OpenGL Version: {}.{}", .{ c.GLVersion.major, c.GLVersion.minor }); + + g_init = try global_allocator.create(InitMemory); + g_init.* = .{ + .global_allocator = global_allocator, + .window = window, + .context = context, + }; +} + +export fn game_init_window(global_allocator: *std.mem.Allocator) void { + std.log.debug("game_init_window\n", .{}); + game_init_window_err(global_allocator.*) catch |err| { + std.log.err("Failed to init window {}\n", .{err}); + @panic("Failed to init window"); + }; +} + export fn game_init(global_allocator: *std.mem.Allocator) void { + std.log.debug("game_init\n", .{}); g_mem = global_allocator.create(GameMemory) catch @panic("OOM"); g_mem.* = .{ .global_allocator = global_allocator.*, @@ -28,29 +76,71 @@ export fn game_update() bool { return false; } }, + c.SDL_WINDOWEVENT => { + switch (event.window.type) { + c.SDL_WINDOWEVENT_SIZE_CHANGED => { + // var width = event.window.data1; + // var height = event.window.data2; + }, + else => {}, + } + }, else => {}, } g_mem.counter += 1; c.glClearColor(0.3, 0.6, 0.3, 1.0); c.glClear(c.GL_COLOR_BUFFER_BIT); + + c.SDL_GL_SwapWindow(g_init.window); + c.SDL_Delay(1); } return true; } export fn game_shutdown() void { + std.log.debug("game_shutdown\n", .{}); g_mem.global_allocator.destroy(g_mem); } -export fn game_hot_reload(memory: *anyopaque) void { - g_mem = @alignCast(@ptrCast(memory)); +export fn game_shutdown_window() void { + std.log.debug("game_shutdown_window\n", .{}); + c.SDL_GL_DeleteContext(g_init.context); + c.SDL_DestroyWindow(g_init.window); + g_init.global_allocator.destroy(g_init); + c.SDL_Quit(); +} + +export fn game_hot_reload(init_memory: ?*anyopaque, gmemory: ?*anyopaque) void { + std.log.debug("game_hot_reload {any} {any}\n", .{ init_memory, gmemory }); + if (init_memory) |init_mem| { + g_init = @alignCast(@ptrCast(init_mem)); + } + if (gmemory) |gmem| { + g_mem = @alignCast(@ptrCast(gmem)); + } } export fn game_memory() *anyopaque { return @ptrCast(g_mem); } +export fn game_init_memory() *anyopaque { + return @ptrCast(g_init); +} + export fn game_memory_size() usize { return @sizeOf(GameMemory); } + +export fn game_init_memory_size() usize { + return @sizeOf(InitMemory); +} + +fn sdl_try(result: c_int) !void { + if (result < 0) { + std.log.err("SDL Error: {s}", .{c.SDL_GetError()}); + return error.SDLError; + } +} diff --git a/src/main.zig b/src/main.zig index 822aca1..e01c95d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,13 +2,6 @@ const std = @import("std"); const c = @import("c.zig"); const builtin = @import("builtin"); -fn sdl_try(result: c_int) !void { - if (result < 0) { - std.log.err("SDL Error: {s}", .{c.SDL_GetError()}); - return error.SDLError; - } -} - const game_lib_basename = "learnopengl"; const game_lib_name: [:0]const u8 = builtin.target.libPrefix() ++ game_lib_basename ++ builtin.target.dynamicLibSuffix(); @@ -23,31 +16,11 @@ pub fn main() !void { const game_lib_path = try getGameLibPath(allocator); - try sdl_try(c.SDL_Init(c.SDL_INIT_EVERYTHING)); - defer c.SDL_Quit(); - - try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_DOUBLEBUFFER, 1)); - try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MAJOR_VERSION, 4)); - try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MINOR_VERSION, 1)); - try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_PROFILE_MASK, c.SDL_GL_CONTEXT_PROFILE_CORE)); - - const maybe_window = c.SDL_CreateWindow("Learn OpenGL with Zig!", c.SDL_WINDOWPOS_CENTERED, c.SDL_WINDOWPOS_CENTERED, 800, 600, c.SDL_WINDOW_SHOWN | c.SDL_WINDOW_OPENGL); - if (maybe_window == null) { - std.log.err("SDL Error: {s}", .{c.SDL_GetError()}); - return error.SDLWindowError; - } - const window = maybe_window.?; - - const context = c.SDL_GL_CreateContext(window); - defer c.SDL_GL_DeleteContext(context); - - _ = c.gladLoadGLLoader(c.SDL_GL_GetProcAddress); - - std.log.debug("OpenGL Version: {}.{}", .{ c.GLVersion.major, c.GLVersion.minor }); - var modified = try getFileModifiedZ(game_lib_path); game_api = try loadGameAPI(allocator, game_lib_path); + game_api.game_init_window(&allocator); + game_api.game_init(&allocator); defer { game_api.game_shutdown(); @@ -65,25 +38,42 @@ pub fn main() !void { modified = new_modified; var new_game_api = try loadGameAPI(allocator, game_lib_path); - if (game_api.game_memory_size() == new_game_api.game_memory_size()) { - std.log.debug("Hot reload with state!\n", .{}); - const game_memory = game_api.game_memory(); - game_api.deinit(allocator); - new_game_api.game_hot_reload(game_memory); - game_api = new_game_api; - } else { - std.log.debug("Hot reload with shutdown!\n", .{}); - game_api.game_shutdown(); - game_api.deinit(allocator); - game_api = new_game_api; - game_api.game_init(&allocator); + var recreate_state = false; + var recreate_window = false; + + const game_init_memory = game_api.game_init_memory(); + const game_memory = game_api.game_memory(); + + if (game_api.game_memory_size() != new_game_api.game_memory_size()) { + recreate_state = true; } + if (game_api.game_init_memory_size() != new_game_api.game_init_memory_size()) { + recreate_window = true; + } + + if (recreate_state) { + game_api.game_shutdown(); + } + if (recreate_window) { + game_api.game_shutdown_window(); + } + + if (recreate_window) { + new_game_api.game_init_window(&allocator); + } else { + new_game_api.game_hot_reload(game_init_memory, null); + } + if (recreate_state) { + new_game_api.game_init(&allocator); + } else { + new_game_api.game_hot_reload(null, game_memory); + } + + game_api.deinit(allocator); + game_api = new_game_api; } exit = !game_api.game_update(); - - c.SDL_GL_SwapWindow(window); - c.SDL_Delay(1); } } @@ -128,22 +118,30 @@ fn loadGameAPI(arena: std.mem.Allocator, game_lib_path: [:0]const u8) !GameAPI { } const GameAPI = struct { + game_init_window: *const fn (*std.mem.Allocator) callconv(.C) void, game_init: *const fn (*std.mem.Allocator) callconv(.C) void, game_update: *const fn () callconv(.C) bool, game_shutdown: *const fn () callconv(.C) void, + game_shutdown_window: *const fn () callconv(.C) void, game_memory_size: *const fn () callconv(.C) usize, + game_init_memory_size: *const fn () callconv(.C) usize, game_memory: *const fn () callconv(.C) *anyopaque, - game_hot_reload: *const fn (*anyopaque) callconv(.C) void, + game_init_memory: *const fn () callconv(.C) *anyopaque, + game_hot_reload: *const fn (?*anyopaque, ?*anyopaque) callconv(.C) void, dll: ?*anyopaque, path: [:0]const u8, pub fn init(dll: ?*anyopaque, path: [:0]const u8) !GameAPI { return GameAPI{ + .game_init_window = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_init_window") orelse return error.MissingGameInitWindow)), .game_init = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_init") orelse return error.MissingGameInit)), .game_update = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_update") orelse return error.MissingGameUpdate)), .game_shutdown = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_shutdown") orelse return error.MissingGameShutdown)), + .game_shutdown_window = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_shutdown_window") orelse return error.MissingGameShutdownWindow)), .game_memory_size = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_memory_size") orelse return error.MissingGameMemorySize)), + .game_init_memory_size = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_init_memory_size") orelse return error.MissingGameInitMemorySize)), .game_memory = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_memory") orelse return error.MissingGameMemory)), + .game_init_memory = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_init_memory") orelse return error.MissingGameInitMemory)), .game_hot_reload = @alignCast(@ptrCast(c.SDL_LoadFunction(dll, "game_hot_reload") orelse return error.MissingGameHotReload)), .dll = dll, .path = path,