inspector: renderer panel

This commit is contained in:
Mitchell Hashimoto
2026-01-30 10:38:52 -08:00
parent 400d17aa0d
commit dc2cca6490
6 changed files with 117 additions and 11 deletions

View File

@@ -65,6 +65,12 @@ pub fn deinit(self: *Inspector, alloc: Allocator) void {
self.gui.deinit(alloc);
}
/// Returns the renderer info panel. This is a convenience function
/// to access and find this state to read and modify.
pub fn rendererInfo(self: *Inspector) *widgets.renderer.Info {
return &self.gui.renderer_info;
}
/// Record a keyboard event.
pub fn recordKeyEvent(
self: *Inspector,

View File

@@ -3,6 +3,7 @@ const cimgui = @import("dcimgui");
pub const page = @import("widgets/page.zig");
pub const pagelist = @import("widgets/pagelist.zig");
pub const key = @import("widgets/key.zig");
pub const renderer = @import("widgets/renderer.zig");
pub const screen = @import("widgets/screen.zig");
pub const style = @import("widgets/style.zig");
pub const surface = @import("widgets/surface.zig");

View File

@@ -0,0 +1,71 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const cimgui = @import("dcimgui");
const widgets = @import("../widgets.zig");
const renderer = @import("../../renderer.zig");
const log = std.log.scoped(.inspector_renderer);
/// Renderer information inspector widget.
pub const Info = struct {
features: std.AutoArrayHashMapUnmanaged(
std.meta.Tag(renderer.Overlay.Feature),
renderer.Overlay.Feature,
),
pub const empty: Info = .{
.features = .empty,
};
pub fn deinit(self: *Info, alloc: Allocator) void {
self.features.deinit(alloc);
}
/// Grab the features into a new allocated slice. This is used by
pub fn overlayFeatures(
self: *const Info,
alloc: Allocator,
) Allocator.Error![]renderer.Overlay.Feature {
// The features from our internal state.
const features = self.features.values();
// For now we do a dumb copy since the features have no managed
// memory.
const result = try alloc.dupe(
renderer.Overlay.Feature,
features,
);
errdefer alloc.free(result);
return result;
}
/// Draw the renderer info window.
pub fn draw(
self: *Info,
alloc: Allocator,
open: bool,
) void {
if (!open) return;
cimgui.c.ImGui_SeparatorText("Overlays");
// Hyperlinks
{
var hyperlinks: bool = self.features.contains(.highlight_hyperlinks);
_ = cimgui.c.ImGui_Checkbox("Overlay Hyperlinks", &hyperlinks);
cimgui.c.ImGui_SameLine();
widgets.helpMarker("When enabled, highlights OSC8 hyperlinks.");
if (!hyperlinks) {
_ = self.features.swapRemove(.highlight_hyperlinks);
} else {
self.features.put(
alloc,
.highlight_hyperlinks,
.highlight_hyperlinks,
) catch log.warn("error enabling hyperlink overlay feature", .{});
}
}
}
};

View File

@@ -16,6 +16,7 @@ const window_keyboard = "Keyboard";
const window_terminal = "Terminal";
const window_surface = "Surface";
const window_termio = "Terminal IO";
const window_renderer = "Renderer";
pub const Inspector = struct {
/// Internal GUI state
@@ -23,6 +24,7 @@ pub const Inspector = struct {
key_stream: widgets.key.Stream,
terminal_info: widgets.terminal.Info,
vt_stream: widgets.termio.Stream,
renderer_info: widgets.renderer.Info,
pub fn init(alloc: Allocator) !Inspector {
return .{
@@ -30,12 +32,14 @@ pub const Inspector = struct {
.key_stream = try .init(alloc),
.terminal_info = .empty,
.vt_stream = try .init(alloc),
.renderer_info = .empty,
};
}
pub fn deinit(self: *Inspector, alloc: Allocator) void {
self.key_stream.deinit(alloc);
self.vt_stream.deinit(alloc);
self.renderer_info.deinit(alloc);
}
pub fn draw(
@@ -116,6 +120,20 @@ pub const Inspector = struct {
);
}
}
// Renderer info window
{
const open = cimgui.c.ImGui_Begin(
window_renderer,
null,
cimgui.c.ImGuiWindowFlags_NoFocusOnAppearing,
);
defer cimgui.c.ImGui_End();
self.renderer_info.draw(
surface.alloc,
open,
);
}
}
if (first_render) {
@@ -157,6 +175,7 @@ pub const Inspector = struct {
cimgui.ImGui_DockBuilderDockWindow(window_surface, dock_id_main);
cimgui.ImGui_DockBuilderDockWindow(window_keyboard, dock_id_main);
cimgui.ImGui_DockBuilderDockWindow(window_termio, dock_id_main);
cimgui.ImGui_DockBuilderDockWindow(window_renderer, dock_id_main);
cimgui.ImGui_DockBuilderDockWindow(window_imgui_demo, dock_id_main);
cimgui.ImGui_DockBuilderFinish(dockspace_id);
}

View File

@@ -19,6 +19,7 @@ pub const Metal = @import("renderer/Metal.zig");
pub const OpenGL = @import("renderer/OpenGL.zig");
pub const WebGL = @import("renderer/WebGL.zig");
pub const Options = @import("renderer/Options.zig");
pub const Overlay = @import("renderer/Overlay.zig");
pub const Thread = @import("renderer/Thread.zig");
pub const State = @import("renderer/State.zig");
pub const CursorStyle = cursor.Style;

View File

@@ -225,13 +225,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
/// Our overlay state, if any.
overlay: ?Overlay = null,
// Right now, the debug overlay is turned on and configured by
// modifying these and recompiling. In the future, we will expose
// all of this at runtime via the inspector.
const overlay_features: []const Overlay.Feature = &.{
//.highlight_hyperlinks,
};
const HighlightTag = enum(u8) {
search_match,
search_match_selected,
@@ -1152,6 +1145,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
mouse: renderer.State.Mouse,
preedit: ?renderer.State.Preedit,
scrollbar: terminal.Scrollbar,
overlay_features: []const Overlay.Feature,
};
// Update all our data as tightly as possible within the mutex.
@@ -1231,11 +1225,20 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
};
};
const overlay_features: []const Overlay.Feature = overlay: {
const insp = state.inspector orelse break :overlay &.{};
const renderer_info = insp.rendererInfo();
break :overlay renderer_info.overlayFeatures(
arena_alloc,
) catch &.{};
};
break :critical .{
.links = links,
.mouse = state.mouse,
.preedit = preedit,
.scrollbar = scrollbar,
.overlay_features = overlay_features,
};
};
@@ -1306,7 +1309,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Rebuild the overlay image if we have one. We can do this
// outside of any critical areas.
self.rebuildOverlay() catch |err| {
self.rebuildOverlay(
critical.overlay_features,
) catch |err| {
log.warn(
"error rebuilding overlay surface err={}",
.{err},
@@ -2241,7 +2246,10 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
/// Build the overlay as configured. Returns null if there is no
/// overlay currently configured.
fn rebuildOverlay(self: *Self) Overlay.InitError!void {
fn rebuildOverlay(
self: *Self,
features: []const Overlay.Feature,
) Overlay.InitError!void {
// const start = std.time.Instant.now() catch unreachable;
// const start_micro = std.time.microTimestamp();
// defer {
@@ -2256,7 +2264,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// If we have no features enabled, don't build an overlay.
// If we had a previous overlay, deallocate it.
if (overlay_features.len == 0) {
if (features.len == 0) {
if (self.overlay) |*old| {
old.deinit(alloc);
self.overlay = null;
@@ -2277,7 +2285,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
overlay.applyFeatures(
alloc,
&self.terminal_state,
overlay_features,
features,
);
}