diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index f178d7bef..f34812da4 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -156,6 +156,19 @@ pub fn Renderer(comptime GraphicsAPI: type) type { /// The current GPU uniform values. uniforms: shaderpkg.Uniforms, + /// Custom shader uniform values. + custom_shader_uniforms: shadertoy.Uniforms, + + /// Timestamp we rendered out first frame. + /// + /// This is used when updating custom shader uniforms. + first_frame_time: ?std.time.Instant = null, + + /// Timestamp when we rendered out more recent frame. + /// + /// This is used when updating custom shader uniforms. + last_frame_time: ?std.time.Instant = null, + /// The font structures. font_grid: *font.SharedGrid, font_shaper: font.Shaper, @@ -382,16 +395,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type { front_texture: Texture, back_texture: Texture, - uniforms: shaderpkg.PostUniforms, - - /// The first time a frame was drawn. - /// This is used to update the time uniform. - first_frame_time: std.time.Instant, - - /// The last time a frame was drawn. - /// This is used to update the time uniform. - last_frame_time: std.time.Instant, - /// Swap the front and back textures. pub fn swap(self: *CustomShaderState) void { std.mem.swap(Texture, &self.front_texture, &self.back_texture); @@ -417,22 +420,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type { return .{ .front_texture = front_texture, .back_texture = back_texture, - - .uniforms = .{ - .resolution = .{ 0, 0, 1 }, - .time = 1, - .time_delta = 1, - .frame_rate = 1, - .frame = 1, - .channel_time = @splat(@splat(0)), - .channel_resolution = @splat(@splat(0)), - .mouse = .{ 0, 0, 0, 0 }, - .date = .{ 0, 0, 0, 0 }, - .sample_rate = 1, - }, - - .first_frame_time = try std.time.Instant.now(), - .last_frame_time = try std.time.Instant.now(), }; } @@ -467,18 +454,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type { self.front_texture = front_texture; self.back_texture = back_texture; - - self.uniforms.resolution = .{ - @floatFromInt(width), - @floatFromInt(height), - 1, - }; - self.uniforms.channel_resolution[0] = .{ - @floatFromInt(width), - @floatFromInt(height), - 1, - 0, - }; } }; @@ -689,6 +664,18 @@ pub fn Renderer(comptime GraphicsAPI: type) type { .use_linear_correction = options.config.blending == .@"linear-corrected", }, }, + .custom_shader_uniforms = .{ + .resolution = .{ 0, 0, 1 }, + .time = 0, + .time_delta = 0, + .frame_rate = 60, // not currently updated + .frame = 0, + .channel_time = @splat(@splat(0)), + .channel_resolution = @splat(@splat(0)), + .mouse = @splat(0), // not currently updated + .date = @splat(0), // not currently updated + .sample_rate = 0, // N/A, we don't have any audio + }, // Fonts .font_grid = options.font_grid, @@ -1359,21 +1346,14 @@ pub fn Renderer(comptime GraphicsAPI: type) type { } } + // Update custom shader uniforms if necessary. + try self.updateCustomShaderUniforms(); + // Setup our frame data try frame.uniforms.sync(&.{self.uniforms}); try frame.cells_bg.sync(self.cells.bg_cells); const fg_count = try frame.cells.syncFromArrayLists(self.cells.fg_rows.lists); - // If we have custom shaders, update the animation time. - if (frame.custom_shader_state) |*state| { - const now = std.time.Instant.now() catch state.first_frame_time; - const since_ns: f32 = @floatFromInt(now.since(state.first_frame_time)); - const delta_ns: f32 = @floatFromInt(now.since(state.last_frame_time)); - state.uniforms.time = since_ns / std.time.ns_per_s; - state.uniforms.time_delta = delta_ns / std.time.ns_per_s; - state.last_frame_time = now; - } - // If our font atlas changed, sync the texture data texture: { const modified = self.font_grid.atlas_grayscale.modified.load(.monotonic); @@ -1446,10 +1426,10 @@ pub fn Renderer(comptime GraphicsAPI: type) type { if (frame.custom_shader_state) |*state| { // We create a buffer on the GPU for our post uniforms. // TODO: This should be a part of the frame state tbqh. - const PostBuffer = Buffer(shaderpkg.PostUniforms); + const PostBuffer = Buffer(shadertoy.Uniforms); const uniform_buffer = try PostBuffer.initFill( self.api.bufferOptions(), - &.{state.uniforms}, + &.{self.custom_shader_uniforms}, ); defer uniform_buffer.deinit(); @@ -1961,6 +1941,44 @@ pub fn Renderer(comptime GraphicsAPI: type) type { }; } + /// Update uniforms for the custom shaders, if necessary. + /// + /// This should be called exactly once per frame, inside `drawFrame`. + fn updateCustomShaderUniforms( + self: *Self, + ) !void { + // We only need to do this if we have custom shaders. + if (!self.has_custom_shaders) return; + + const now = try std.time.Instant.now(); + defer self.last_frame_time = now; + const first_frame_time = self.first_frame_time orelse t: { + self.first_frame_time = now; + break :t now; + }; + const last_frame_time = self.last_frame_time orelse now; + + const since_ns: f32 = @floatFromInt(now.since(first_frame_time)); + self.custom_shader_uniforms.time = since_ns / std.time.ns_per_s; + + const delta_ns: f32 = @floatFromInt(now.since(last_frame_time)); + self.custom_shader_uniforms.time_delta = delta_ns / std.time.ns_per_s; + + self.custom_shader_uniforms.frame += 1; + + self.custom_shader_uniforms.resolution = .{ + @floatFromInt(self.size.screen.width), + @floatFromInt(self.size.screen.height), + 1, + }; + self.custom_shader_uniforms.channel_resolution[0] = .{ + @floatFromInt(self.size.screen.width), + @floatFromInt(self.size.screen.height), + 1, + 0, + }; + } + /// Convert the terminal state to GPU cells stored in CPU memory. These /// are then synced to the GPU in the next frame. This only updates CPU /// memory and doesn't touch the GPU. diff --git a/src/renderer/metal/shaders.zig b/src/renderer/metal/shaders.zig index 68994882e..dc5d1122c 100644 --- a/src/renderer/metal/shaders.zig +++ b/src/renderer/metal/shaders.zig @@ -182,23 +182,6 @@ pub const Uniforms = extern struct { }; }; -/// The uniforms used for custom postprocess shaders. -pub const PostUniforms = extern struct { - // Note: all of the explicit alignments are copied from the - // MSL developer reference just so that we can be sure that we got - // it all exactly right. - resolution: [3]f32 align(16), - time: f32 align(4), - time_delta: f32 align(4), - frame_rate: f32 align(4), - frame: i32 align(4), - channel_time: [4][4]f32 align(16), - channel_resolution: [4][4]f32 align(16), - mouse: [4]f32 align(16), - date: [4]f32 align(16), - sample_rate: f32 align(4), -}; - /// Initialize the MTLLibrary. A MTLLibrary is a collection of shaders. fn initLibrary(device: objc.Object) !objc.Object { const start = try std.time.Instant.now(); diff --git a/src/renderer/opengl/shaders.zig b/src/renderer/opengl/shaders.zig index e509b723a..7e54fd37b 100644 --- a/src/renderer/opengl/shaders.zig +++ b/src/renderer/opengl/shaders.zig @@ -165,20 +165,6 @@ pub const Uniforms = extern struct { }; }; -/// The uniforms used for custom postprocess shaders. -pub const PostUniforms = extern struct { - resolution: [3]f32 align(16), - time: f32 align(4), - time_delta: f32 align(4), - frame_rate: f32 align(4), - frame: i32 align(4), - channel_time: [4][4]f32 align(16), - channel_resolution: [4][4]f32 align(16), - mouse: [4]f32 align(16), - date: [4]f32 align(16), - sample_rate: f32 align(4), -}; - /// Initialize our custom shader pipelines. The shaders argument is a /// set of shader source code, not file paths. fn initPostPipelines( diff --git a/src/renderer/shadertoy.zig b/src/renderer/shadertoy.zig index 68171a23e..e6bd66857 100644 --- a/src/renderer/shadertoy.zig +++ b/src/renderer/shadertoy.zig @@ -9,6 +9,20 @@ const configpkg = @import("../config.zig"); const log = std.log.scoped(.shadertoy); +/// The uniform struct used for shadertoy shaders. +pub const Uniforms = extern struct { + resolution: [3]f32 align(16), + time: f32 align(4), + time_delta: f32 align(4), + frame_rate: f32 align(4), + frame: i32 align(4), + channel_time: [4][4]f32 align(16), + channel_resolution: [4][4]f32 align(16), + mouse: [4]f32 align(16), + date: [4]f32 align(16), + sample_rate: f32 align(4), +}; + /// The target to load shaders for. pub const Target = enum { glsl, msl };