diff --git a/build.zig b/build.zig index 940de690..27bad36c 100644 --- a/build.zig +++ b/build.zig @@ -114,7 +114,7 @@ const App = struct { src: []const u8, deps: ?[]const Pkg = null, }) App { - const exe = b.addExecutable(options.name, "src/entry_native.zig"); + const exe = b.addExecutable(options.name, "src/native.zig"); exe.addPackage(.{ .name = "app", .path = .{ .path = options.src }, diff --git a/examples/gkurve/main.zig b/examples/gkurve/main.zig index 258fb309..977d5376 100644 --- a/examples/gkurve/main.zig +++ b/examples/gkurve/main.zig @@ -32,10 +32,7 @@ pub const vertices = [_]Vertex{ .{ .pos = .{ WINDOW_WIDTH / 2 - TRIANGLE_SCALE, WINDOW_HEIGHT / 2 + 0, 0, 1 }, .uv = .{ 1, 0 } }, }; -// TODO: Need to ask Ayush about this, ideally we have a square window in this example because it -// would mean our triangles are not being "stretched" out which would make debugging nicer. -// For some reason this doesn't compile atm. -// pub const options = mach.Engine.Options{ .width = 512, .height = 512 }; +pub const options = mach.Options{ .width = 512, .height = 512 }; // The uniform read by the vertex shader, it contains the matrix // that will move vertices diff --git a/src/Engine.zig b/src/Engine.zig index 2e75304b..08744d08 100644 --- a/src/Engine.zig +++ b/src/Engine.zig @@ -6,48 +6,6 @@ const App = @import("app"); const structs = @import("structs.zig"); const enums = @import("enums.zig"); -pub const VSyncMode = enum { - /// Potential screen tearing. - /// No synchronization with monitor, render frames as fast as possible. - none, - - /// No tearing, synchronizes rendering with monitor refresh rate, rendering frames when ready. - /// - /// Tries to stay one frame ahead of the monitor, so when it's ready for the next frame it is - /// already prepared. - double, - - /// No tearing, synchronizes rendering with monitor refresh rate, rendering frames when ready. - /// - /// Tries to stay two frames ahead of the monitor, so when it's ready for the next frame it is - /// already prepared. - triple, -}; - -/// Application options that can be configured at init time. -pub const Options = struct { - /// The title of the window. - title: [*:0]const u8 = "Mach engine", - - /// The width of the window. - width: u32 = 640, - - /// The height of the window. - height: u32 = 480, - - /// Monitor synchronization modes. - vsync: VSyncMode = .double, - - /// GPU features required by the application. - required_features: ?[]gpu.Feature = null, - - /// GPU limits required by the application. - required_limits: ?gpu.Limits = null, - - /// Whether the application has a preference for low power or high performance GPU. - power_preference: gpu.PowerPreference = .none, -}; - const Engine = @This(); /// Window, events, inputs etc. @@ -58,7 +16,7 @@ gpu_driver: GpuDriver, allocator: Allocator, -options: Options, +options: structs.Options, /// The amount of time (in seconds) that has passed since the last frame was rendered. /// @@ -102,7 +60,7 @@ pub const GpuDriver = struct { target_desc: gpu.SwapChain.Descriptor, }; -pub fn init(allocator: std.mem.Allocator, options: Options) !Engine { +pub fn init(allocator: std.mem.Allocator, options: structs.Options) !Engine { var engine = Engine{ .allocator = allocator, .options = options, @@ -111,6 +69,8 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Engine { .gpu_driver = undefined, }; + // Note: if in future, there is a conflict in init() signature of different backends, + // move these calls to the entry point file, which is native.zig for Glfw, for example engine.core.internal = try GetCoreInternalType().init(allocator, &engine); engine.gpu_driver.internal = try GetGpuDriverInternalType().init(allocator, &engine); diff --git a/src/entry_native.zig b/src/entry_native.zig deleted file mode 100644 index 94061eb2..00000000 --- a/src/entry_native.zig +++ /dev/null @@ -1,67 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const App = @import("app"); - -const glfw = @import("glfw"); -const gpu = @import("gpu"); -const util = @import("util.zig"); -const c = @import("c.zig").c; - -const Engine = @import("Engine.zig"); -const Options = Engine.Options; - -// TODO: check signatures -comptime { - if (!@hasDecl(App, "init")) @compileError("App must export 'pub fn init(app: *App, engine: *mach.Engine) !void'"); - if (!@hasDecl(App, "deinit")) @compileError("App must export 'pub fn deinit(app: *App, engine: *mach.Engine) void'"); - if (!@hasDecl(App, "update")) @compileError("App must export 'pub fn update(app: *App, engine: *mach.Engine) !bool'"); -} - -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const allocator = gpa.allocator(); - - const options = if (@hasDecl(App, "options")) App.options else Options{}; - var engine = try Engine.init(allocator, options); - var app: App = undefined; - - try app.init(&engine); - defer app.deinit(&engine); - - if (@hasDecl(@TypeOf(engine.core.internal), "initCallback")) { - engine.core.internal.initCallback(&app, &engine); - } - - const window = engine.core.internal.window; - while (!window.shouldClose()) { - try glfw.pollEvents(); - - engine.delta_time_ns = engine.timer.lap(); - engine.delta_time = @intToFloat(f64, engine.delta_time_ns) / @intToFloat(f64, std.time.ns_per_s); - - var framebuffer_size = try window.getFramebufferSize(); - engine.gpu_driver.target_desc.width = framebuffer_size.width; - engine.gpu_driver.target_desc.height = framebuffer_size.height; - - if (engine.gpu_driver.swap_chain == null or !engine.gpu_driver.current_desc.equal(&engine.gpu_driver.target_desc)) { - const use_legacy_api = engine.gpu_driver.surface == null; - if (!use_legacy_api) { - engine.gpu_driver.swap_chain = engine.gpu_driver.device.nativeCreateSwapChain(engine.gpu_driver.surface, &engine.gpu_driver.target_desc); - } else engine.gpu_driver.swap_chain.?.configure( - engine.gpu_driver.swap_chain_format, - .{ .render_attachment = true }, - engine.gpu_driver.target_desc.width, - engine.gpu_driver.target_desc.height, - ); - - if (@hasDecl(App, "resize")) { - try app.resize(&engine, engine.gpu_driver.target_desc.width, engine.gpu_driver.target_desc.height); - } - engine.gpu_driver.current_desc = engine.gpu_driver.target_desc; - } - - const success = try app.update(&engine); - if (!success) - break; - } -} diff --git a/src/native.zig b/src/native.zig index b2a66124..8379f2ef 100644 --- a/src/native.zig +++ b/src/native.zig @@ -42,7 +42,7 @@ pub const CoreGlfw = struct { }; } - pub fn initCallback(self: *CoreGlfw, app: *App, engine: *Engine) void { + fn initCallback(self: *CoreGlfw, app: *App, engine: *Engine) void { self.user_ptr = UserPtr{ .app = app, .engine = engine, @@ -361,3 +361,58 @@ pub const GpuDriverNative = struct { }; } }; + +// TODO: check signatures +comptime { + if (!@hasDecl(App, "init")) @compileError("App must export 'pub fn init(app: *App, engine: *mach.Engine) !void'"); + if (!@hasDecl(App, "deinit")) @compileError("App must export 'pub fn deinit(app: *App, engine: *mach.Engine) void'"); + if (!@hasDecl(App, "update")) @compileError("App must export 'pub fn update(app: *App, engine: *mach.Engine) !bool'"); +} + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + + const options = if (@hasDecl(App, "options")) App.options else structs.Options{}; + var engine = try Engine.init(allocator, options); + var app: App = undefined; + + try app.init(&engine); + defer app.deinit(&engine); + + // Glfw specific: initialize the user pointer used in callbacks + engine.core.internal.initCallback(&app, &engine); + + const window = engine.core.internal.window; + while (!window.shouldClose()) { + try glfw.pollEvents(); + + engine.delta_time_ns = engine.timer.lap(); + engine.delta_time = @intToFloat(f64, engine.delta_time_ns) / @intToFloat(f64, std.time.ns_per_s); + + var framebuffer_size = try window.getFramebufferSize(); + engine.gpu_driver.target_desc.width = framebuffer_size.width; + engine.gpu_driver.target_desc.height = framebuffer_size.height; + + if (engine.gpu_driver.swap_chain == null or !engine.gpu_driver.current_desc.equal(&engine.gpu_driver.target_desc)) { + const use_legacy_api = engine.gpu_driver.surface == null; + if (!use_legacy_api) { + engine.gpu_driver.swap_chain = engine.gpu_driver.device.nativeCreateSwapChain(engine.gpu_driver.surface, &engine.gpu_driver.target_desc); + } else engine.gpu_driver.swap_chain.?.configure( + engine.gpu_driver.swap_chain_format, + .{ .render_attachment = true }, + engine.gpu_driver.target_desc.width, + engine.gpu_driver.target_desc.height, + ); + + if (@hasDecl(App, "resize")) { + try app.resize(&engine, engine.gpu_driver.target_desc.width, engine.gpu_driver.target_desc.height); + } + engine.gpu_driver.current_desc = engine.gpu_driver.target_desc; + } + + const success = try app.update(&engine); + if (!success) + break; + } +} diff --git a/src/structs.zig b/src/structs.zig index e6eaa534..da697ccb 100644 --- a/src/structs.zig +++ b/src/structs.zig @@ -1,3 +1,5 @@ +const gpu = @import("gpu"); + pub const Size = struct { width: u32, height: u32, @@ -7,3 +9,45 @@ pub const SizeOptional = struct { width: ?u32, height: ?u32, }; + +pub const VSyncMode = enum { + /// Potential screen tearing. + /// No synchronization with monitor, render frames as fast as possible. + none, + + /// No tearing, synchronizes rendering with monitor refresh rate, rendering frames when ready. + /// + /// Tries to stay one frame ahead of the monitor, so when it's ready for the next frame it is + /// already prepared. + double, + + /// No tearing, synchronizes rendering with monitor refresh rate, rendering frames when ready. + /// + /// Tries to stay two frames ahead of the monitor, so when it's ready for the next frame it is + /// already prepared. + triple, +}; + +/// Application options that can be configured at init time. +pub const Options = struct { + /// The title of the window. + title: [*:0]const u8 = "Mach engine", + + /// The width of the window. + width: u32 = 640, + + /// The height of the window. + height: u32 = 480, + + /// Monitor synchronization modes. + vsync: VSyncMode = .double, + + /// GPU features required by the application. + required_features: ?[]gpu.Feature = null, + + /// GPU limits required by the application. + required_limits: ?gpu.Limits = null, + + /// Whether the application has a preference for low power or high performance GPU. + power_preference: gpu.PowerPreference = .none, +};