130 lines
3.9 KiB
Zig
130 lines
3.9 KiB
Zig
const core = @import("mach-core");
|
|
const gpu = @import("mach-core").gpu;
|
|
const std = @import("std");
|
|
const ecs = @import("mach-ecs");
|
|
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
const allocator = gpa.allocator();
|
|
|
|
/// The main Mach engine ECS module.
|
|
pub const Engine = struct {
|
|
device: *gpu.Device,
|
|
queue: *gpu.Queue,
|
|
exit: bool,
|
|
pass: *gpu.RenderPassEncoder,
|
|
encoder: *gpu.CommandEncoder,
|
|
|
|
pub const name = .engine;
|
|
|
|
pub const local = struct {
|
|
pub fn init(world: *World) !void {
|
|
core.allocator = allocator;
|
|
try core.init(.{});
|
|
const state = &world.mod.engine.state;
|
|
state.device = core.device;
|
|
state.queue = core.device.getQueue();
|
|
state.exit = false;
|
|
state.encoder = state.device.createCommandEncoder(&gpu.CommandEncoder.Descriptor{
|
|
.label = "engine.state.encoder",
|
|
});
|
|
|
|
try world.send(null, .init, .{});
|
|
}
|
|
|
|
pub fn deinit(
|
|
world: *World,
|
|
engine: *World.Mod(.engine),
|
|
) !void {
|
|
// TODO: this triggers a device loss error, which we should handle correctly
|
|
// engine.state.device.release();
|
|
engine.state.queue.release();
|
|
try world.send(null, .deinit, .{});
|
|
core.deinit();
|
|
world.deinit();
|
|
_ = gpa.deinit();
|
|
}
|
|
|
|
// Engine module's exit handler
|
|
pub fn exit(world: *World) !void {
|
|
try world.send(null, .exit, .{});
|
|
world.mod.engine.state.exit = true;
|
|
}
|
|
|
|
pub fn beginPass(
|
|
engine: *World.Mod(.engine),
|
|
clear_color: gpu.Color,
|
|
) !void {
|
|
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
defer back_buffer_view.release();
|
|
|
|
// TODO: expose options
|
|
const color_attachment = gpu.RenderPassColorAttachment{
|
|
.view = back_buffer_view,
|
|
.clear_value = clear_color,
|
|
.load_op = .clear,
|
|
.store_op = .store,
|
|
};
|
|
const pass_info = gpu.RenderPassDescriptor.init(.{
|
|
.color_attachments = &.{color_attachment},
|
|
});
|
|
|
|
engine.state.pass = engine.state.encoder.beginRenderPass(&pass_info);
|
|
}
|
|
|
|
pub fn endPass(
|
|
engine: *World.Mod(.engine),
|
|
) !void {
|
|
// End this pass
|
|
engine.state.pass.end();
|
|
engine.state.pass.release();
|
|
|
|
var command = engine.state.encoder.finish(null);
|
|
defer command.release();
|
|
engine.state.encoder.release();
|
|
engine.state.queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
|
|
// Prepare for next pass
|
|
engine.state.encoder = engine.state.device.createCommandEncoder(&gpu.CommandEncoder.Descriptor{
|
|
.label = "engine.state.encoder",
|
|
});
|
|
}
|
|
|
|
pub fn present() !void {
|
|
core.swap_chain.present();
|
|
}
|
|
};
|
|
};
|
|
|
|
pub const App = struct {
|
|
world: World,
|
|
|
|
pub fn init(app: *@This()) !void {
|
|
app.* = .{ .world = try World.init(allocator) };
|
|
try app.world.send(.engine, .init, .{});
|
|
}
|
|
|
|
pub fn deinit(app: *@This()) void {
|
|
try app.world.send(.engine, .deinit, .{});
|
|
}
|
|
|
|
pub fn update(app: *@This()) !bool {
|
|
try app.world.send(null, .tick, .{});
|
|
return app.world.mod.engine.state.exit;
|
|
}
|
|
};
|
|
|
|
pub const World = ecs.World(modules());
|
|
|
|
fn Modules() type {
|
|
if (!@hasDecl(@import("root"), "modules")) {
|
|
@compileError("expected `pub const modules = .{};` in root file");
|
|
}
|
|
return @TypeOf(@import("root").modules);
|
|
}
|
|
|
|
fn modules() Modules() {
|
|
if (!@hasDecl(@import("root"), "modules")) {
|
|
@compileError("expected `pub const modules = .{};` in root file");
|
|
}
|
|
return @import("root").modules;
|
|
}
|