diff --git a/examples/core-custom-entrypoint/Game.zig b/examples/core-custom-entrypoint/Game.zig index 20e8282d..265da066 100644 --- a/examples/core-custom-entrypoint/Game.zig +++ b/examples/core-custom-entrypoint/Game.zig @@ -104,7 +104,7 @@ fn tick(core: *mach.Core.Mod, game: *Mod) !void { core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); // Present the frame - mach.core.swap_chain.present(); + core.send(.present_frame, .{}); // update the window title every second if (game.state().title_timer.read() >= 1.0) { diff --git a/examples/custom-renderer/Game.zig b/examples/custom-renderer/Game.zig index 5d46e1f5..b6f72f05 100644 --- a/examples/custom-renderer/Game.zig +++ b/examples/custom-renderer/Game.zig @@ -47,10 +47,6 @@ fn init( renderer: *Renderer.Mod, game: *Mod, ) !void { - // The Mach .core is where we set window options, etc. - // TODO(important): replace this API with something better - mach.core.setTitle("Hello, ECS!"); - // Create our player entity. const player = try core.newEntity(); diff --git a/examples/custom-renderer/Renderer.zig b/examples/custom-renderer/Renderer.zig index e4ab654d..08abba09 100644 --- a/examples/custom-renderer/Renderer.zig +++ b/examples/custom-renderer/Renderer.zig @@ -123,22 +123,29 @@ fn tick( renderer: *Mod, ) !void { const device = core.state().device; + _ = device; // autofix - // Begin our render pass + // Grab the back buffer of the swapchain const back_buffer_view = mach.core.swap_chain.getCurrentTextureView().?; - const color_attachment = gpu.RenderPassColorAttachment{ + defer back_buffer_view.release(); + + // Create a command encoder + const label = @tagName(name) ++ ".tick"; + const encoder = core.state().device.createCommandEncoder(&.{ .label = label }); + defer encoder.release(); + + // Begin render pass + const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; + const color_attachments = [_]gpu.RenderPassColorAttachment{.{ .view = back_buffer_view, - .clear_value = std.mem.zeroes(gpu.Color), + .clear_value = sky_blue_background, .load_op = .clear, .store_op = .store, - }; - - const label = @tagName(name) ++ ".tick"; - const encoder = device.createCommandEncoder(&.{ .label = label }); - const render_pass_info = gpu.RenderPassDescriptor.init(.{ + }}; + const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ .label = label, - .color_attachments = &.{color_attachment}, - }); + .color_attachments = &color_attachments, + })); // Update uniform buffer var archetypes_iter = core.entities.query(.{ .all = &.{ @@ -161,20 +168,21 @@ fn tick( } } - const pass = encoder.beginRenderPass(&render_pass_info); + // Draw for (renderer.state().bind_groups[0..num_entities]) |bind_group| { - pass.setPipeline(renderer.state().pipeline); - pass.setBindGroup(0, bind_group, &.{0}); - pass.draw(3, 1, 0, 0); + render_pass.setPipeline(renderer.state().pipeline); + render_pass.setBindGroup(0, bind_group, &.{0}); + render_pass.draw(3, 1, 0, 0); } - pass.end(); - pass.release(); + // Finish render pass + render_pass.end(); + + // Submit our commands to the queue var command = encoder.finish(&.{ .label = label }); - encoder.release(); + defer command.release(); + core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); - renderer.state().queue.submit(&[_]*gpu.CommandBuffer{command}); - command.release(); - mach.core.swap_chain.present(); - back_buffer_view.release(); + // Present the frame + core.send(.present_frame, .{}); } diff --git a/examples/glyphs/Game.zig b/examples/glyphs/Game.zig index 57cae514..a77cdfe4 100644 --- a/examples/glyphs/Game.zig +++ b/examples/glyphs/Game.zig @@ -238,7 +238,7 @@ fn endFrame(game: *Mod, core: *mach.Core.Mod) !void { core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); // Present the frame - mach.core.swap_chain.present(); + core.send(.present_frame, .{}); // Every second, update the window title with the FPS if (game.state().fps_timer.read() >= 1.0) { diff --git a/examples/piano/Piano.zig b/examples/piano/Piano.zig index 2287b074..112bf6f3 100644 --- a/examples/piano/Piano.zig +++ b/examples/piano/Piano.zig @@ -12,6 +12,7 @@ const std = @import("std"); const builtin = @import("builtin"); const mach = @import("mach"); +const gpu = mach.gpu; const math = mach.math; const sysaudio = mach.sysaudio; @@ -109,10 +110,40 @@ fn tick( } } + // Grab the back buffer of the swapchain const back_buffer_view = mach.core.swap_chain.getCurrentTextureView().?; + defer back_buffer_view.release(); - mach.core.swap_chain.present(); - back_buffer_view.release(); + // Create a command encoder + const label = @tagName(name) ++ ".tick"; + const encoder = core.state().device.createCommandEncoder(&.{ .label = label }); + defer encoder.release(); + + // Begin render pass + const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; + const color_attachments = [_]gpu.RenderPassColorAttachment{.{ + .view = back_buffer_view, + .clear_value = sky_blue_background, + .load_op = .clear, + .store_op = .store, + }}; + const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ + .label = label, + .color_attachments = &color_attachments, + })); + + // Draw nothing + + // Finish render pass + render_pass.end(); + + // Submit our commands to the queue + var command = encoder.finish(&.{ .label = label }); + defer command.release(); + core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); + + // Present the frame + core.send(.present_frame, .{}); } fn fillTone(audio: *mach.Audio.Mod, frequency: f32) ![]const f32 { diff --git a/examples/sprite/Game.zig b/examples/sprite/Game.zig index 553c3f5e..6b7f0a78 100644 --- a/examples/sprite/Game.zig +++ b/examples/sprite/Game.zig @@ -222,7 +222,7 @@ fn endFrame(game: *Mod, core: *mach.Core.Mod) !void { core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); // Present the frame - mach.core.swap_chain.present(); + core.send(.present_frame, .{}); // Every second, update the window title with the FPS if (game.state().fps_timer.read() >= 1.0) { diff --git a/examples/text/Game.zig b/examples/text/Game.zig index 4dbe0abb..281cce4a 100644 --- a/examples/text/Game.zig +++ b/examples/text/Game.zig @@ -63,9 +63,6 @@ fn init( text_style: *gfx.TextStyle.Mod, game: *Mod, ) !void { - // The Mach .core is where we set window options, etc. - mach.core.setTitle("gfx.Text example"); - // TODO: a better way to initialize entities with default values // TODO(text): most of these style options are not respected yet. const style1 = try core.newEntity(); @@ -268,7 +265,7 @@ fn endFrame(game: *Mod, text: *gfx.Text.Mod, core: *mach.Core.Mod) !void { core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); // Present the frame - mach.core.swap_chain.present(); + core.send(.present_frame, .{}); // Every second, update the window title with the FPS if (game.state().fps_timer.read() >= 1.0) { diff --git a/src/Core.zig b/src/Core.zig index d5d0d5e2..94ce8a06 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -19,6 +19,10 @@ pub const local_events = .{ \\ Send this when window entities have been updated and you want the new values respected. }, + .present_frame = .{ .handler = presentFrame, .description = + \\ Send this when rendering has finished and the swapchain should be presented. + }, + .init = .{ .handler = init }, .init_done = .{ .handler = fn () void }, @@ -49,7 +53,7 @@ pub const components = .{ /// try mach.Core.printTitle(core_mod, core_mod.state().main_window, "Hello, {s}!", .{"Mach"}); /// ``` pub fn printTitle( - core: *mach.Core.Mod, + core: *Mod, window_id: mach.EntityID, comptime fmt: []const u8, args: anytype, @@ -106,6 +110,13 @@ fn update(core: *Mod) !void { if (num_windows > 1) @panic("mach: Core currently only supports a single window"); } +fn presentFrame(core: *Mod) !void { + mach.core.swap_chain.present(); + + // Signal that mainThreadTick is done + core.send(.main_thread_tick_done, .{}); +} + fn deinit(core: *Mod) void { core.state().queue.release(); // TODO: this triggers a device loss error, which we should handle correctly @@ -129,9 +140,6 @@ fn mainThreadTick(core: *Mod) !void { // Send .tick to anyone interested core.sendGlobal(.tick, .{}); - - // Signal that mainThreadTick is done - core.send(.main_thread_tick_done, .{}); } fn exit(core: *Mod) void { diff --git a/src/module/module.zig b/src/module/module.zig index 96c7ac5c..14326fff 100644 --- a/src/module/module.zig +++ b/src/module/module.zig @@ -342,6 +342,7 @@ pub fn Modules(comptime modules: anytype) type { options: DispatchOptions, injectable: anytype, ) !void { + @setEvalBranchQuota(10000); // TODO: optimize to reduce send contention // TODO: parallel / multi-threaded dispatch // TODO: PGO