diff --git a/src/App.zig b/src/App.zig index bd7c06b35..2dcbe920d 100644 --- a/src/App.zig +++ b/src/App.zig @@ -8,6 +8,7 @@ const Allocator = std.mem.Allocator; const glfw = @import("glfw"); const Window = @import("Window.zig"); const libuv = @import("libuv/main.zig"); +const tracy = @import("tracy/tracy.zig"); const log = std.log.scoped(.app); @@ -84,7 +85,15 @@ pub fn run(self: App) !void { }).callback); while (!self.window.shouldClose()) { - try self.window.run(); + // Mark this so we're in a totally different "frame" + tracy.frameMark(); + + // Track the render part of the frame separately. + { + const frame = tracy.frame("render"); + defer frame.end(); + try self.window.run(); + } // Block for any glfw events. This may also be an "empty" event // posted by the libuv watcher so that we trigger a libuv loop tick. @@ -103,6 +112,8 @@ pub fn run(self: App) !void { } // Run the libuv loop + const frame = tracy.frame("libuv"); + defer frame.end(); try embed.loopRun(); } diff --git a/src/Grid.zig b/src/Grid.zig index d130805ab..fcc33ac19 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -10,6 +10,7 @@ const FontAtlas = @import("FontAtlas.zig"); const Terminal = @import("terminal/Terminal.zig"); const gl = @import("opengl.zig"); const gb = @import("gb_math.zig"); +const trace = @import("tracy/tracy.zig").trace; const log = std.log.scoped(.grid); @@ -343,6 +344,9 @@ pub fn setScreenSize(self: *Grid, dim: ScreenSize) !void { } pub fn render(self: Grid) !void { + const t = trace(@src()); + defer t.end(); + // If we have no cells to render, then we render nothing. if (self.cells.items.len == 0) return; diff --git a/src/Window.zig b/src/Window.zig index b98c6875f..50564fea2 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -17,6 +17,7 @@ const Pty = @import("Pty.zig"); const Command = @import("Command.zig"); const Terminal = @import("terminal/Terminal.zig"); const SegmentedPool = @import("segmented_pool.zig").SegmentedPool; +const trace = @import("tracy/tracy.zig").trace; const log = std.log.scoped(.window); @@ -231,6 +232,9 @@ pub fn shouldClose(self: Window) bool { } pub fn run(self: Window) !void { + const tracy = trace(@src()); + defer tracy.end(); + // Set our background gl.clearColor(0.2, 0.3, 0.3, 1.0); gl.clear(gl.c.GL_COLOR_BUFFER_BIT); @@ -243,6 +247,9 @@ pub fn run(self: Window) !void { } fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { + const tracy = trace(@src()); + defer tracy.end(); + // glfw gives us signed integers, but negative width/height is n // non-sensical so we use unsigned throughout, so assert. assert(width >= 0); @@ -279,6 +286,9 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { } fn charCallback(window: glfw.Window, codepoint: u21) void { + const tracy = trace(@src()); + defer tracy.end(); + const win = window.getUserPointer(Window) orelse return; // Write the character to the pty @@ -299,6 +309,9 @@ fn keyCallback( action: glfw.Action, mods: glfw.Mods, ) void { + const tracy = trace(@src()); + defer tracy.end(); + _ = scancode; _ = mods; @@ -352,6 +365,9 @@ fn keyCallback( } fn focusCallback(window: glfw.Window, focused: bool) void { + const tracy = trace(@src()); + defer tracy.end(); + const win = window.getUserPointer(Window) orelse return; if (focused) { win.wakeup = true; @@ -367,17 +383,27 @@ fn focusCallback(window: glfw.Window, focused: bool) void { } fn cursorTimerCallback(t: *libuv.Timer) void { + const tracy = trace(@src()); + defer tracy.end(); + const win = t.getData(Window) orelse return; win.grid.cursor_visible = !win.grid.cursor_visible; win.grid.updateCells(win.terminal) catch unreachable; } fn ttyReadAlloc(t: *libuv.Tty, size: usize) ?[]u8 { + const tracy = trace(@src()); + defer tracy.end(); + const alloc = t.loop().getData(Allocator).?.*; return alloc.alloc(u8, size) catch null; } fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void { + const tracy = trace(@src()); + tracy.color(0xEAEA7F); // yellow-ish + defer tracy.end(); + const win = t.getData(Window).?; defer win.alloc.free(buf); @@ -411,6 +437,9 @@ fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void { } fn ttyWrite(req: *libuv.WriteReq, status: i32) void { + const tracy = trace(@src()); + defer tracy.end(); + const tty = req.handle(libuv.Tty).?; const win = tty.getData(Window).?; win.write_req_pool.put(); diff --git a/src/main.zig b/src/main.zig index ac1076c5c..69feb796d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,9 +6,6 @@ const App = @import("App.zig"); const trace = @import("tracy/tracy.zig").trace; pub fn main() !void { - const tracy = trace(@src()); - defer tracy.end(); - var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; const gpa = general_purpose_allocator.allocator(); defer _ = general_purpose_allocator.deinit(); diff --git a/src/tracy/tracy.zig b/src/tracy/tracy.zig index 93e99e59b..665e5c43b 100644 --- a/src/tracy/tracy.zig +++ b/src/tracy/tracy.zig @@ -22,28 +22,52 @@ const Impl = struct { const has_callstack_support = @hasDecl(c, "TRACY_HAS_CALLSTACK") and @hasDecl(c, "TRACY_CALLSTACK"); const callstack_enabled: c_int = if (has_callstack_support) c.TRACY_CALLSTACK else 0; - pub const ZoneCtx = struct { + /// A zone represents the lifetime of a special on-stack profiler variable. + /// Typically it would exist for the duration of a whole scope of the + /// profiled function, but you also can measure time spent in scopes of a + /// for-loop or an if-branch. + pub const Zone = struct { zone: c.___tracy_c_zone_context, - pub inline fn text(self: ZoneCtx, val: []const u8) void { + /// Text description of a zone. + pub inline fn text(self: Zone, val: []const u8) void { c.___tracy_emit_zone_text(self.zone, val.ptr, val.len); } - pub inline fn name(self: ZoneCtx, val: []const u8) void { + /// Name of the zone. + pub inline fn name(self: Zone, val: []const u8) void { c.___tracy_emit_zone_name(self.zone, val.ptr, val.len); } - pub inline fn value(self: ZoneCtx, val: u64) void { + /// Color of the zone in the UI. Specify the value as RGB + /// using hex: 0xRRGGBB. + pub inline fn color(self: Zone, val: u32) void { + c.___tracy_emit_zone_color(self.zone, val); + } + + /// A value associated with the zone. + pub inline fn value(self: Zone, val: u64) void { c.___tracy_emit_zone_value(self.zone, val); } - pub inline fn end(self: ZoneCtx) void { + /// End the zone. + pub inline fn end(self: Zone) void { c.___tracy_emit_zone_end(self.zone); } }; + /// Tracy profiles within the context of a frame. This represents + /// a single frame. + pub fn Frame(comptime name: [:0]const u8) type { + return struct { + pub fn end(_: @This()) void { + c.___tracy_emit_frame_mark_end(name.ptr); + } + }; + } + /// Start a trace. Defer calling end() to end the trace. - pub inline fn trace(comptime src: SourceLocation) ZoneCtx { + pub inline fn trace(comptime src: SourceLocation) Zone { const callstack_depth = 10; // TODO configurable const static = struct { @@ -62,20 +86,45 @@ const Impl = struct { else c.___tracy_emit_zone_begin(&static.loc, 1); - return ZoneCtx{ .zone = zone }; + return Zone{ .zone = zone }; + } + + /// Mark the boundary of a frame. Good for continuous frames. For + /// discontinous frames, use frame() and defer end(). + pub inline fn frameMark() void { + c.___tracy_emit_frame_mark(null); + } + + /// Start a discontinuous frame. + pub inline fn frame(comptime name: [:0]const u8) Frame(name) { + c.___tracy_emit_frame_mark_start(name.ptr); + return .{}; } }; const Noop = struct { - pub const ZoneCtx = struct { - pub inline fn text(_: ZoneCtx, _: []const u8) void {} - pub inline fn name(_: ZoneCtx, _: []const u8) void {} - pub inline fn value(_: ZoneCtx, _: u64) void {} - pub inline fn end(_: ZoneCtx) void {} + pub const Zone = struct { + pub inline fn text(_: Zone, _: []const u8) void {} + pub inline fn name(_: Zone, _: []const u8) void {} + pub inline fn color(_: Zone, _: u32) void {} + pub inline fn value(_: Zone, _: u64) void {} + pub inline fn end(_: Zone) void {} }; - pub inline fn trace(comptime src: SourceLocation) ZoneCtx { + pub fn Frame(comptime _: [:0]const u8) type { + return struct { + pub fn end(_: @This()) void {} + }; + } + + pub inline fn trace(comptime src: SourceLocation) Zone { _ = src; return .{}; } + + pub inline fn frameMark() void {} + + pub inline fn frame(comptime name: [*:0]const u8) Frame(name) { + return .{}; + } };