Game
now uses execution graph to schedule systems
This commit is contained in:
102
src/game.zig
102
src/game.zig
@@ -1,48 +1,90 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const sdl = @import("sdl");
|
||||
const Graph = @import("graph.zig");
|
||||
const Graphics = @import("graphics.zig");
|
||||
|
||||
graphics: Graphics,
|
||||
running: bool,
|
||||
// TODO:
|
||||
// - Do something about deallocating `Resource`s when `Graph` fails
|
||||
|
||||
const RunInfo = struct { running: bool };
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
graph: Graph,
|
||||
|
||||
const Self = @This();
|
||||
pub fn init() GameError!Self {
|
||||
pub fn init(alloc: std.mem.Allocator) GameError!Self {
|
||||
var graph = try Graph.init(alloc);
|
||||
errdefer graph.deinit();
|
||||
|
||||
const graphics = try Graphics.create();
|
||||
|
||||
var controller = try graph.getController();
|
||||
controller.addResource(graphics);
|
||||
try graph.freeController(controller);
|
||||
|
||||
return Self{
|
||||
.graphics = try Graphics.create(),
|
||||
.running = false,
|
||||
.alloc = alloc,
|
||||
.graph = graph,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn run(self: *Self) GameError!void {
|
||||
self.running = true;
|
||||
{
|
||||
var controller = try self.graph.getController();
|
||||
controller.addResource(RunInfo{ .running = true });
|
||||
try self.graph.freeController(controller);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try self.processEvents();
|
||||
if (!self.running) {
|
||||
if (!self.graph.getResource(RunInfo).?.running) {
|
||||
break;
|
||||
}
|
||||
try self.update();
|
||||
try self.draw();
|
||||
|
||||
var controller = try self.graph.getController();
|
||||
controller.queue(.{
|
||||
.events = processEvents,
|
||||
.draw = draw,
|
||||
.ordered = true,
|
||||
});
|
||||
try self.graph.freeController(controller);
|
||||
|
||||
defer self.graph.reset();
|
||||
try self.graph.runAllSystems();
|
||||
}
|
||||
}
|
||||
|
||||
fn update(self: *Self) GameError!void {
|
||||
_ = self;
|
||||
fn draw(graphics: *Graphics) GameError!void {
|
||||
try graphics.beginDraw();
|
||||
{
|
||||
errdefer graphics.endDraw() catch {};
|
||||
try graphics.drawDebug();
|
||||
}
|
||||
try graphics.endDraw();
|
||||
}
|
||||
|
||||
fn draw(self: *Self) GameError!void {
|
||||
try self.graphics.beginDraw();
|
||||
try self.graphics.drawDebug();
|
||||
try self.graphics.endDraw();
|
||||
fn clean(graphics: *Graphics) !void {
|
||||
graphics.destroy();
|
||||
// TODO: Also remove the resource
|
||||
}
|
||||
|
||||
fn processEvents(self: *Self) GameError!void {
|
||||
fn processEvents(graphics: *Graphics, run_info: *RunInfo) GameError!void {
|
||||
sdl.PumpEvents();
|
||||
while (true) {
|
||||
var buffer: [16]sdl.Event = undefined;
|
||||
const count: usize = @intCast(sdl.PeepEvents(&buffer, buffer.len, sdl.GETEVENT, sdl.EVENT_FIRST, sdl.EVENT_LAST));
|
||||
if (count == -1) return GameError.SdlError;
|
||||
for (buffer[0..count]) |event| {
|
||||
self.processEvent(event);
|
||||
switch (event.type) {
|
||||
sdl.EVENT_QUIT => {
|
||||
run_info.running = false;
|
||||
},
|
||||
sdl.EVENT_WINDOW_RESIZED => {
|
||||
if (event.window.windowID != sdl.GetWindowID(graphics.window)) return;
|
||||
graphics.resize(@intCast(event.window.data1), @intCast(event.window.data2));
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
if (count < buffer.len) {
|
||||
break;
|
||||
@@ -51,25 +93,21 @@ fn processEvents(self: *Self) GameError!void {
|
||||
sdl.FlushEvents(sdl.EVENT_FIRST, sdl.EVENT_LAST);
|
||||
}
|
||||
|
||||
fn processEvent(self: *Self, event: sdl.Event) void {
|
||||
switch (event.type) {
|
||||
sdl.EVENT_QUIT => {
|
||||
self.running = false;
|
||||
},
|
||||
sdl.EVENT_WINDOW_RESIZED => {
|
||||
if (event.window.windowID != sdl.GetWindowID(self.graphics.window)) return;
|
||||
self.graphics.resize(@intCast(event.window.data1), @intCast(event.window.data2));
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.graphics.destroy();
|
||||
var controller = self.graph.getController() catch unreachable;
|
||||
controller.queue(clean);
|
||||
self.graph.freeController(controller) catch unreachable;
|
||||
self.graph.runAllSystems() catch unreachable;
|
||||
|
||||
self.graph.deinit();
|
||||
|
||||
sdl.Quit();
|
||||
}
|
||||
|
||||
pub const GameError = error{
|
||||
SdlError,
|
||||
OSError,
|
||||
OutOfMemory,
|
||||
MissingResource,
|
||||
SystemDeadlock,
|
||||
};
|
||||
|
@@ -8,6 +8,9 @@ const System = @import("graph/system.zig");
|
||||
// - Resolve missing resource problem
|
||||
// - Parse system sets into a properly defined data structure instead of relying on `@typeInfo`
|
||||
// - Find a better way to represent system sets
|
||||
// - Organize a better way to execute single commands on graph
|
||||
// - Handle system errors
|
||||
// - Removing of resources
|
||||
|
||||
pub const Controller = @import("graph/controller.zig");
|
||||
|
||||
@@ -86,12 +89,40 @@ pub fn deinit(self: *Self) void {
|
||||
self.duds.deinit(self.alloc);
|
||||
}
|
||||
|
||||
/// Clear all internal data in preparation for the next run cycle
|
||||
/// Does not clear any `Resource`s
|
||||
pub fn reset(self: *Self) void {
|
||||
// Controller cleanup
|
||||
for (self.controllers.items, 0..) |*controller, i| {
|
||||
for (controller.commands()) |*command| {
|
||||
switch (command.*) {
|
||||
.add_resource => |*resource| resource.deinit(controller.alloc),
|
||||
.queue_system => |*system| system.deinit(controller.alloc),
|
||||
}
|
||||
}
|
||||
controller.clear();
|
||||
controller.setDuds(
|
||||
i * self.duds.items.len / self.controllers.items.len,
|
||||
(i + 1) * self.duds.items.len / self.controllers.items.len,
|
||||
);
|
||||
}
|
||||
// System cleanup
|
||||
for (self.system_queue.items) |*system| {
|
||||
system.deinit(self.alloc);
|
||||
}
|
||||
self.system_queue.clearRetainingCapacity();
|
||||
// Duds cleanup
|
||||
for (self.duds.items) |*dud| {
|
||||
dud.required_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn enqueueSystem(self: *Self, system: System) !void {
|
||||
errdefer system.deinit(self.alloc);
|
||||
try self.system_queue.append(self.alloc, system);
|
||||
}
|
||||
|
||||
fn runAllSystems(self: *Self) GraphError!void {
|
||||
pub fn runAllSystems(self: *Self) GraphError!void {
|
||||
while (self.system_queue.items.len > 0) {
|
||||
var swap_with = self.system_queue.items.len - 1;
|
||||
|
||||
@@ -169,7 +200,7 @@ fn applyCommands(self: *Self, commands: []const Controller.Command) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn getController(self: *Self) !Controller {
|
||||
pub fn getController(self: *Self) !Controller {
|
||||
if (self.controllers.pop()) |c| {
|
||||
return c;
|
||||
}
|
||||
@@ -185,7 +216,7 @@ fn getController(self: *Self) !Controller {
|
||||
}
|
||||
|
||||
/// Evaluates and clears the controller (even if errors out)
|
||||
fn freeController(self: *Self, controller: Controller) !void {
|
||||
pub fn freeController(self: *Self, controller: Controller) !void {
|
||||
var c = controller;
|
||||
try self.applyCommands(c.commands());
|
||||
c.clear();
|
||||
|
@@ -34,7 +34,7 @@ pub fn create(alloc: std.mem.Allocator) !Controller {
|
||||
|
||||
/// Returns command queue, caller is responsible for freeing it's data
|
||||
/// Call `clean()` afterwards, to clear the command queue
|
||||
pub fn commands(self: *Controller) []const Command {
|
||||
pub fn commands(self: *Controller) []Command {
|
||||
return self.command_buffer.items;
|
||||
}
|
||||
|
||||
|
@@ -8,9 +8,9 @@ requires_dud: ?Dud.Id,
|
||||
submit_dud: ?Dud.Id,
|
||||
|
||||
pub const Dud = struct {
|
||||
pub const Id = u16;
|
||||
pub const Id = usize;
|
||||
|
||||
required_count: usize = 0,
|
||||
required_count: u16 = 0,
|
||||
};
|
||||
|
||||
pub const Request = union(enum) {
|
||||
|
@@ -32,7 +32,8 @@ pub fn validateResource(comptime resource_type: type) void {
|
||||
pub fn validateSystem(comptime system: anytype) void {
|
||||
const info = @typeInfo(@TypeOf(system));
|
||||
if (info != .@"fn") @compileError("System can only be a function, got " ++ @typeName(system));
|
||||
if (info.@"fn".return_type != void) @compileError("Systems are not allowed to return any value (" ++ @typeName(info.Fn.return_type.?) ++ " returned)");
|
||||
if (@typeInfo(info.@"fn".return_type.?) != .void and
|
||||
@typeInfo(info.@"fn".return_type.?) != .error_union) @compileError("Systems are not allowed to return any value (" ++ @typeName(info.@"fn".return_type.?) ++ " returned)");
|
||||
if (info.@"fn".is_var_args) @compileError("System cannot be variadic");
|
||||
if (info.@"fn".is_generic) @compileError("System cannot be generic");
|
||||
|
||||
@@ -85,7 +86,13 @@ pub fn generateRunner(comptime system: anytype) fn ([]const *anyopaque) void {
|
||||
inline for (0..@typeInfo(@TypeOf(system)).@"fn".params.len) |index| {
|
||||
args[index] = @alignCast(@ptrCast(resources[index]));
|
||||
}
|
||||
@call(.always_inline, system, args);
|
||||
switch (@typeInfo(@typeInfo(@TypeOf(system)).@"fn".return_type.?)) {
|
||||
.void => @call(.always_inline, system, args),
|
||||
.error_union => @call(.always_inline, system, args) catch |err| {
|
||||
std.debug.print("System error: {s}\n", .{@errorName(err)});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
return RunnerImpl.runner;
|
||||
|
19
src/main.zig
19
src/main.zig
@@ -1,9 +1,26 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const sdl = @import("sdl");
|
||||
const Game = @import("game.zig");
|
||||
|
||||
pub fn runGame() !void {
|
||||
var game = try Game.init();
|
||||
var allocator = switch (builtin.mode) {
|
||||
.ReleaseSafe, .Debug => std.heap.DebugAllocator(.{}).init,
|
||||
.ReleaseFast => std.heap.smp_allocator,
|
||||
.ReleaseSmall => std.heap.c_allocator,
|
||||
};
|
||||
defer switch (builtin.mode) {
|
||||
.ReleaseSafe, .Debug => std.debug.assert(allocator.deinit() == .ok),
|
||||
else => {},
|
||||
};
|
||||
|
||||
var game = try Game.init(
|
||||
switch (builtin.mode) {
|
||||
.ReleaseSafe, .Debug => allocator.allocator(),
|
||||
.ReleaseFast => allocator,
|
||||
.ReleaseSmall => allocator,
|
||||
},
|
||||
);
|
||||
defer game.deinit();
|
||||
try game.run();
|
||||
}
|
||||
|
Reference in New Issue
Block a user