renderer: move custom shader uniforms out of frame state
These should not be independent per-frame, that makes the time calculations all sorts of jank. Also moves the uniforms struct layout in to `shadertoy.zig` and cleans up the handling in general somewhat.pull/7648/head
parent
5bfdb1b9cf
commit
c7a7474be0
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue