aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-02-24 10:16:36 +0000
committers-ol <s+removethis@s-ol.nu>2025-02-24 13:18:04 +0000
commit96063a5d32d41a70cdbfba68e3f018c9c320aa68 (patch)
tree3adadd5cc7ce41f68919ee28f9e56b2b1fec2c54
parentupdate for zig 0.13.0 (diff)
downloadglsl-view-96063a5d32d41a70cdbfba68e3f018c9c320aa68.tar.gz
glsl-view-96063a5d32d41a70cdbfba68e3f018c9c320aa68.zip
load videos and textures using libav
-rw-r--r--build.zig4
-rw-r--r--src/c.zig4
-rw-r--r--src/control.zig50
-rw-r--r--src/gl.zig327
-rw-r--r--src/main.zig13
-rw-r--r--src/video.zig138
6 files changed, 482 insertions, 54 deletions
diff --git a/build.zig b/build.zig
index 25304b6..db0145c 100644
--- a/build.zig
+++ b/build.zig
@@ -15,6 +15,10 @@ pub fn build(b: *std.Build) void {
exe.linkSystemLibrary("glfw3");
exe.linkSystemLibrary("epoxy");
exe.linkSystemLibrary("liblo");
+ exe.linkSystemLibrary("libavcodec");
+ exe.linkSystemLibrary("libavformat");
+ exe.linkSystemLibrary("libavutil");
+ exe.linkSystemLibrary("libswscale");
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
diff --git a/src/c.zig b/src/c.zig
index 427b3d6..b46d0f4 100644
--- a/src/c.zig
+++ b/src/c.zig
@@ -3,4 +3,8 @@ pub usingnamespace @cImport({
@cInclude("GLFW/glfw3.h");
@cInclude("yaml.h");
@cInclude("lo/lo.h");
+ @cInclude("lo/lo.h");
+ @cInclude("libavcodec/avcodec.h");
+ @cInclude("libavformat/avformat.h");
+ @cInclude("libswscale/swscale.h");
});
diff --git a/src/control.zig b/src/control.zig
index 2e63b44..9482015 100644
--- a/src/control.zig
+++ b/src/control.zig
@@ -1,5 +1,6 @@
const c = @import("c.zig");
const gl = @import("gl.zig");
+const video = @import("video.zig");
const std = @import("std");
const cfg = @import("config.zig");
@@ -85,19 +86,41 @@ fn set_array(
}
}
+fn set_texture(progress: std.Progress.Node, dest: *?gl.Texture, texture_type: gl.Texture.Type, argc: c_int, argv: [*c][*c]c.lo_arg, types: []const u8) !void {
+ if (argc != 1 or types[0] != 's') return error.invalidType;
+
+ if (dest.*) |old| {
+ old.destroy();
+ dest.* = null;
+ }
+
+ dest.* = gl.Texture.create(texture_type);
+ try video.loadVideo(progress, @ptrCast(&argv[0].*.s), &dest.*.?);
+}
+
pub const ControlServer = struct {
server: c.lo_server,
+ reload_requested: bool,
+
cache: *gl.UniformCache,
+ progress: std.Progress.Node,
allocator: std.mem.Allocator,
pub fn init(
allocator: std.mem.Allocator,
+ progress: std.Progress.Node,
config: cfg.OSCConfig,
cache: *gl.UniformCache,
) !*ControlServer {
var self: *ControlServer = try allocator.create(ControlServer);
- self.allocator = allocator;
- self.cache = cache;
+ self.* = .{
+ .server = null,
+ .reload_requested = false,
+
+ .cache = cache,
+ .progress = progress,
+ .allocator = allocator,
+ };
switch (config) {
.Manual => |conf| {
@@ -219,6 +242,24 @@ pub const ControlServer = struct {
.DOUBLE_MAT3x4 => |val| set_array(f64, val.*[0..], argc, argv, types),
.DOUBLE_MAT4x2 => |val| set_array(f64, val.*[0..], argc, argv, types),
.DOUBLE_MAT4x3 => |val| set_array(f64, val.*[0..], argc, argv, types),
+
+ .SAMPLER_2D,
+ .SAMPLER_2D_SHADOW,
+ .INT_SAMPLER_2D,
+ .UNSIGNED_INT_SAMPLER_2D,
+ => |val| set_texture(self.progress, val, .TEXTURE_2D, argc, argv, types),
+
+ .SAMPLER_2D_ARRAY,
+ .SAMPLER_2D_ARRAY_SHADOW,
+ .INT_SAMPLER_2D_ARRAY,
+ .UNSIGNED_INT_SAMPLER_2D_ARRAY,
+ => |val| set_texture(self.progress, val, .TEXTURE_2D_ARRAY, argc, argv, types),
+
+ .SAMPLER_3D,
+ .INT_SAMPLER_3D,
+ .UNSIGNED_INT_SAMPLER_3D,
+ => |val| set_texture(self.progress, val, .TEXTURE_3D, argc, argv, types),
+
else => error.uniformNotSupported,
};
@@ -239,6 +280,11 @@ pub const ControlServer = struct {
const path = _path[0..c.strlen(_path)];
const types = _types[0..@as(u32, @intCast(argc))];
+ if (std.mem.eql(u8, path, "/-/reload")) {
+ self.reload_requested = true;
+ return 1;
+ }
+
self.set_uniform(path, argv, argc, types) catch |err| {
std.debug.print("{} while processing {s}\n", .{ err, path });
return 1;
diff --git a/src/gl.zig b/src/gl.zig
index bfd3bc5..d2f827e 100644
--- a/src/gl.zig
+++ b/src/gl.zig
@@ -10,6 +10,120 @@ fn tmpZ(str: []const u8) ![:0]const u8 {
return try std.fmt.bufPrintZ(state.buf[0..], "{s}", .{str});
}
+var image_unit_map = [_]bool{false} ** 1024;
+
+pub const Texture = struct {
+ pub const Type = enum(c.GLenum) {
+ TEXTURE_1D = c.GL_TEXTURE_1D,
+ TEXTURE_2D = c.GL_TEXTURE_2D,
+ TEXTURE_3D = c.GL_TEXTURE_3D,
+ TEXTURE_CUBEMAP = c.GL_TEXTURE_CUBE_MAP,
+ TEXTURE_RECTANGLE = c.GL_TEXTURE_RECTANGLE,
+ TEXTURE_2D_MULTISAMPLE = c.GL_TEXTURE_2D_MULTISAMPLE,
+ TEXTURE_1D_ARRAY = c.GL_TEXTURE_1D_ARRAY,
+ TEXTURE_2D_ARRAY = c.GL_TEXTURE_2D_ARRAY,
+ TEXTURE_CUBEMAP_ARRAY = c.GL_TEXTURE_CUBE_MAP_ARRAY,
+ TEXTURE_BUFFER = c.GL_TEXTURE_BUFFER,
+ TEXTURE_2D_MULTISAMPLE_ARRAY = c.GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
+ };
+
+ type: Type,
+ id: c.GLuint,
+ image_unit: ?usize,
+
+ pub fn create(texture_type: Type) Texture {
+ var self: Texture = .{
+ .type = texture_type,
+ .id = undefined,
+ .image_unit = null,
+ };
+ c.glGenTextures(1, &self.id);
+
+ c.glBindTexture(@intFromEnum(self.type), self.id);
+ defer c.glBindTexture(@intFromEnum(self.type), 0);
+
+ c.glTexParameteri(@intFromEnum(self.type), c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR);
+ c.glTexParameteri(@intFromEnum(self.type), c.GL_TEXTURE_MAG_FILTER, c.GL_LINEAR);
+
+ return self;
+ }
+
+ pub fn bind(self: *Texture) usize {
+ if (self.image_unit == null) {
+ for (image_unit_map[0..], 0..) |*slot, i| {
+ if (slot.*) continue;
+
+ slot.* = true;
+ self.image_unit = @intCast(i);
+ break;
+ }
+ }
+
+ if (self.image_unit) |image_unit| {
+ c.glActiveTexture(@intCast(c.GL_TEXTURE0 + image_unit));
+ c.glBindTexture(@intFromEnum(self.type), self.id);
+ return image_unit;
+ } else unreachable;
+ }
+
+ pub fn destroy(self: *const Texture) void {
+ if (self.image_unit) |image_unit| {
+ image_unit_map[image_unit] = false;
+ }
+ c.glDeleteTextures(1, &self.id);
+ }
+
+ pub fn setData2D(self: *const Texture, width: i32, height: i32, data: [*]const u8) void {
+ c.glBindTexture(@intFromEnum(self.type), self.id);
+ defer c.glBindTexture(@intFromEnum(self.type), 0);
+
+ c.glTexImage2D(
+ @intFromEnum(self.type),
+ 0,
+ c.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, // c.GL_RGBA,
+ width,
+ height,
+ 0,
+ c.GL_RGBA,
+ c.GL_UNSIGNED_BYTE,
+ data,
+ );
+ }
+
+ pub fn allocate3D(self: *const Texture, width: i32, height: i32, depth: i32) void {
+ c.glBindTexture(@intFromEnum(self.type), self.id);
+ defer c.glBindTexture(@intFromEnum(self.type), 0);
+
+ c.glTexStorage3D(
+ @intFromEnum(self.type),
+ 1,
+ c.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, // c.GL_RGBA,
+ width,
+ height,
+ depth,
+ );
+ }
+
+ pub fn setLayer3D(self: *const Texture, width: i32, height: i32, layer: i32, data: [*]const u8) void {
+ c.glBindTexture(@intFromEnum(self.type), self.id);
+ defer c.glBindTexture(@intFromEnum(self.type), 0);
+
+ c.glTexSubImage3D(
+ @intFromEnum(self.type),
+ 0,
+ 0,
+ 0,
+ layer,
+ width,
+ height,
+ 1,
+ c.GL_RGBA,
+ c.GL_UNSIGNED_BYTE,
+ data,
+ );
+ }
+};
+
const UniformType = enum(c.GLint) {
FLOAT = c.GL_FLOAT,
FLOAT_VEC2 = c.GL_FLOAT_VEC2,
@@ -126,42 +240,42 @@ const UniformValue = union(UniformType) {
DOUBLE_MAT3x4: *[3 * 4]f64,
DOUBLE_MAT4x2: *[4 * 2]f64,
DOUBLE_MAT4x3: *[4 * 3]f64,
- SAMPLER_1D: *c_int,
- SAMPLER_2D: *c_int,
- SAMPLER_3D: *c_int,
- SAMPLER_CUBE: *c_int,
- SAMPLER_1D_SHADOW: *c_int,
- SAMPLER_2D_SHADOW: *c_int,
- SAMPLER_1D_ARRAY: *c_int,
- SAMPLER_2D_ARRAY: *c_int,
- SAMPLER_1D_ARRAY_SHADOW: *c_int,
- SAMPLER_2D_ARRAY_SHADOW: *c_int,
- SAMPLER_2D_MULTISAMPLE: *c_int,
- SAMPLER_2D_MULTISAMPLE_ARRAY: *c_int,
- SAMPLER_CUBE_SHADOW: *c_int,
- SAMPLER_BUFFER: *c_int,
- SAMPLER_2D_RECT: *c_int,
- SAMPLER_2D_RECT_SHADOW: *c_int,
- INT_SAMPLER_1D: *c_int,
- INT_SAMPLER_2D: *c_int,
- INT_SAMPLER_3D: *c_int,
- INT_SAMPLER_CUBE: *c_int,
- INT_SAMPLER_1D_ARRAY: *c_int,
- INT_SAMPLER_2D_ARRAY: *c_int,
- INT_SAMPLER_2D_MULTISAMPLE: *c_int,
- INT_SAMPLER_2D_MULTISAMPLE_ARRAY: *c_int,
- INT_SAMPLER_BUFFER: *c_int,
- INT_SAMPLER_2D_RECT: *c_int,
- UNSIGNED_INT_SAMPLER_1D: *c_int,
- UNSIGNED_INT_SAMPLER_2D: *c_int,
- UNSIGNED_INT_SAMPLER_3D: *c_int,
- UNSIGNED_INT_SAMPLER_CUBE: *c_int,
- UNSIGNED_INT_SAMPLER_1D_ARRAY: *c_int,
- UNSIGNED_INT_SAMPLER_2D_ARRAY: *c_int,
- UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: *c_int,
- UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: *c_int,
- UNSIGNED_INT_SAMPLER_BUFFER: *c_int,
- UNSIGNED_INT_SAMPLER_2D_RECT: *c_int,
+ SAMPLER_1D: *?Texture,
+ SAMPLER_2D: *?Texture,
+ SAMPLER_3D: *?Texture,
+ SAMPLER_CUBE: *?Texture,
+ SAMPLER_1D_SHADOW: *?Texture,
+ SAMPLER_2D_SHADOW: *?Texture,
+ SAMPLER_1D_ARRAY: *?Texture,
+ SAMPLER_2D_ARRAY: *?Texture,
+ SAMPLER_1D_ARRAY_SHADOW: *?Texture,
+ SAMPLER_2D_ARRAY_SHADOW: *?Texture,
+ SAMPLER_2D_MULTISAMPLE: *?Texture,
+ SAMPLER_2D_MULTISAMPLE_ARRAY: *?Texture,
+ SAMPLER_CUBE_SHADOW: *?Texture,
+ SAMPLER_BUFFER: *?Texture,
+ SAMPLER_2D_RECT: *?Texture,
+ SAMPLER_2D_RECT_SHADOW: *?Texture,
+ INT_SAMPLER_1D: *?Texture,
+ INT_SAMPLER_2D: *?Texture,
+ INT_SAMPLER_3D: *?Texture,
+ INT_SAMPLER_CUBE: *?Texture,
+ INT_SAMPLER_1D_ARRAY: *?Texture,
+ INT_SAMPLER_2D_ARRAY: *?Texture,
+ INT_SAMPLER_2D_MULTISAMPLE: *?Texture,
+ INT_SAMPLER_2D_MULTISAMPLE_ARRAY: *?Texture,
+ INT_SAMPLER_BUFFER: *?Texture,
+ INT_SAMPLER_2D_RECT: *?Texture,
+ UNSIGNED_INT_SAMPLER_1D: *?Texture,
+ UNSIGNED_INT_SAMPLER_2D: *?Texture,
+ UNSIGNED_INT_SAMPLER_3D: *?Texture,
+ UNSIGNED_INT_SAMPLER_CUBE: *?Texture,
+ UNSIGNED_INT_SAMPLER_1D_ARRAY: *?Texture,
+ UNSIGNED_INT_SAMPLER_2D_ARRAY: *?Texture,
+ UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: *?Texture,
+ UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: *?Texture,
+ UNSIGNED_INT_SAMPLER_BUFFER: *?Texture,
+ UNSIGNED_INT_SAMPLER_2D_RECT: *?Texture,
pub fn init(allocator: std.mem.Allocator, comptime utype: UniformType) !UniformValue {
const data = try allocator.create(std.meta.Child(std.meta.TagPayload(UniformValue, utype)));
@@ -170,6 +284,46 @@ const UniformValue = union(UniformType) {
pub fn deinit(self: *const UniformValue, allocator: std.mem.Allocator) void {
switch (self.*) {
+ .SAMPLER_1D,
+ .SAMPLER_2D,
+ .SAMPLER_3D,
+ .SAMPLER_CUBE,
+ .SAMPLER_1D_SHADOW,
+ .SAMPLER_2D_SHADOW,
+ .SAMPLER_1D_ARRAY,
+ .SAMPLER_2D_ARRAY,
+ .SAMPLER_1D_ARRAY_SHADOW,
+ .SAMPLER_2D_ARRAY_SHADOW,
+ .SAMPLER_2D_MULTISAMPLE,
+ .SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .SAMPLER_CUBE_SHADOW,
+ .SAMPLER_BUFFER,
+ .SAMPLER_2D_RECT,
+ .SAMPLER_2D_RECT_SHADOW,
+ .INT_SAMPLER_1D,
+ .INT_SAMPLER_2D,
+ .INT_SAMPLER_3D,
+ .INT_SAMPLER_CUBE,
+ .INT_SAMPLER_1D_ARRAY,
+ .INT_SAMPLER_2D_ARRAY,
+ .INT_SAMPLER_2D_MULTISAMPLE,
+ .INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .INT_SAMPLER_BUFFER,
+ .INT_SAMPLER_2D_RECT,
+ .UNSIGNED_INT_SAMPLER_1D,
+ .UNSIGNED_INT_SAMPLER_2D,
+ .UNSIGNED_INT_SAMPLER_3D,
+ .UNSIGNED_INT_SAMPLER_CUBE,
+ .UNSIGNED_INT_SAMPLER_1D_ARRAY,
+ .UNSIGNED_INT_SAMPLER_2D_ARRAY,
+ .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE,
+ .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .UNSIGNED_INT_SAMPLER_BUFFER,
+ .UNSIGNED_INT_SAMPLER_2D_RECT,
+ => |value| {
+ if (value.*) |texture| texture.destroy();
+ allocator.destroy(value);
+ },
inline else => |value| allocator.destroy(value),
}
}
@@ -239,7 +393,6 @@ pub const CachedUniform = struct {
return true;
}
- // zig fmt: off
pub fn setShaderValue(self: *const CachedUniform, shader: ShaderProgram) void {
const program = shader.program_id;
const location = self.location;
@@ -283,7 +436,48 @@ pub const CachedUniform = struct {
.DOUBLE_MAT3x4 => |value| c.glProgramUniformMatrix3x4dv(program, location, 1, c.GL_FALSE, value),
.DOUBLE_MAT4x2 => |value| c.glProgramUniformMatrix4x2dv(program, location, 1, c.GL_FALSE, value),
.DOUBLE_MAT4x3 => |value| c.glProgramUniformMatrix4x3dv(program, location, 1, c.GL_FALSE, value),
- .SAMPLER_1D, .SAMPLER_2D, .SAMPLER_3D, .SAMPLER_CUBE, .SAMPLER_1D_SHADOW, .SAMPLER_2D_SHADOW, .SAMPLER_1D_ARRAY, .SAMPLER_2D_ARRAY, .SAMPLER_1D_ARRAY_SHADOW, .SAMPLER_2D_ARRAY_SHADOW, .SAMPLER_2D_MULTISAMPLE, .SAMPLER_2D_MULTISAMPLE_ARRAY, .SAMPLER_CUBE_SHADOW, .SAMPLER_BUFFER, .SAMPLER_2D_RECT, .SAMPLER_2D_RECT_SHADOW, .INT_SAMPLER_1D, .INT_SAMPLER_2D, .INT_SAMPLER_3D, .INT_SAMPLER_CUBE, .INT_SAMPLER_1D_ARRAY, .INT_SAMPLER_2D_ARRAY, .INT_SAMPLER_2D_MULTISAMPLE, .INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .INT_SAMPLER_BUFFER, .INT_SAMPLER_2D_RECT, .UNSIGNED_INT_SAMPLER_1D, .UNSIGNED_INT_SAMPLER_2D, .UNSIGNED_INT_SAMPLER_3D, .UNSIGNED_INT_SAMPLER_CUBE, .UNSIGNED_INT_SAMPLER_1D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .UNSIGNED_INT_SAMPLER_BUFFER, .UNSIGNED_INT_SAMPLER_2D_RECT => |value| c.glProgramUniform1i(program, location, value.*),
+ .SAMPLER_1D,
+ .SAMPLER_2D,
+ .SAMPLER_3D,
+ .SAMPLER_CUBE,
+ .SAMPLER_1D_SHADOW,
+ .SAMPLER_2D_SHADOW,
+ .SAMPLER_1D_ARRAY,
+ .SAMPLER_2D_ARRAY,
+ .SAMPLER_1D_ARRAY_SHADOW,
+ .SAMPLER_2D_ARRAY_SHADOW,
+ .SAMPLER_2D_MULTISAMPLE,
+ .SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .SAMPLER_CUBE_SHADOW,
+ .SAMPLER_BUFFER,
+ .SAMPLER_2D_RECT,
+ .SAMPLER_2D_RECT_SHADOW,
+ .INT_SAMPLER_1D,
+ .INT_SAMPLER_2D,
+ .INT_SAMPLER_3D,
+ .INT_SAMPLER_CUBE,
+ .INT_SAMPLER_1D_ARRAY,
+ .INT_SAMPLER_2D_ARRAY,
+ .INT_SAMPLER_2D_MULTISAMPLE,
+ .INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .INT_SAMPLER_BUFFER,
+ .INT_SAMPLER_2D_RECT,
+ .UNSIGNED_INT_SAMPLER_1D,
+ .UNSIGNED_INT_SAMPLER_2D,
+ .UNSIGNED_INT_SAMPLER_3D,
+ .UNSIGNED_INT_SAMPLER_CUBE,
+ .UNSIGNED_INT_SAMPLER_1D_ARRAY,
+ .UNSIGNED_INT_SAMPLER_2D_ARRAY,
+ .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE,
+ .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .UNSIGNED_INT_SAMPLER_BUFFER,
+ .UNSIGNED_INT_SAMPLER_2D_RECT,
+ => |value| {
+ if (value.*) |*texture| {
+ const index = texture.bind();
+ c.glProgramUniform1i(program, location, @intCast(index));
+ }
+ },
}
}
@@ -330,10 +524,45 @@ pub const CachedUniform = struct {
.DOUBLE_MAT3x4 => |value| c.glGetUniformdv(program, location, value),
.DOUBLE_MAT4x2 => |value| c.glGetUniformdv(program, location, value),
.DOUBLE_MAT4x3 => |value| c.glGetUniformdv(program, location, value),
- .SAMPLER_1D, .SAMPLER_2D, .SAMPLER_3D, .SAMPLER_CUBE, .SAMPLER_1D_SHADOW, .SAMPLER_2D_SHADOW, .SAMPLER_1D_ARRAY, .SAMPLER_2D_ARRAY, .SAMPLER_1D_ARRAY_SHADOW, .SAMPLER_2D_ARRAY_SHADOW, .SAMPLER_2D_MULTISAMPLE, .SAMPLER_2D_MULTISAMPLE_ARRAY, .SAMPLER_CUBE_SHADOW, .SAMPLER_BUFFER, .SAMPLER_2D_RECT, .SAMPLER_2D_RECT_SHADOW, .INT_SAMPLER_1D, .INT_SAMPLER_2D, .INT_SAMPLER_3D, .INT_SAMPLER_CUBE, .INT_SAMPLER_1D_ARRAY, .INT_SAMPLER_2D_ARRAY, .INT_SAMPLER_2D_MULTISAMPLE, .INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .INT_SAMPLER_BUFFER, .INT_SAMPLER_2D_RECT, .UNSIGNED_INT_SAMPLER_1D, .UNSIGNED_INT_SAMPLER_2D, .UNSIGNED_INT_SAMPLER_3D, .UNSIGNED_INT_SAMPLER_CUBE, .UNSIGNED_INT_SAMPLER_1D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .UNSIGNED_INT_SAMPLER_BUFFER, .UNSIGNED_INT_SAMPLER_2D_RECT => |value| c.glGetnUniformiv(program, location, @sizeOf(@TypeOf(value.*)), value),
+ .SAMPLER_1D,
+ .SAMPLER_2D,
+ .SAMPLER_3D,
+ .SAMPLER_CUBE,
+ .SAMPLER_1D_SHADOW,
+ .SAMPLER_2D_SHADOW,
+ .SAMPLER_1D_ARRAY,
+ .SAMPLER_2D_ARRAY,
+ .SAMPLER_1D_ARRAY_SHADOW,
+ .SAMPLER_2D_ARRAY_SHADOW,
+ .SAMPLER_2D_MULTISAMPLE,
+ .SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .SAMPLER_CUBE_SHADOW,
+ .SAMPLER_BUFFER,
+ .SAMPLER_2D_RECT,
+ .SAMPLER_2D_RECT_SHADOW,
+ .INT_SAMPLER_1D,
+ .INT_SAMPLER_2D,
+ .INT_SAMPLER_3D,
+ .INT_SAMPLER_CUBE,
+ .INT_SAMPLER_1D_ARRAY,
+ .INT_SAMPLER_2D_ARRAY,
+ .INT_SAMPLER_2D_MULTISAMPLE,
+ .INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .INT_SAMPLER_BUFFER,
+ .INT_SAMPLER_2D_RECT,
+ .UNSIGNED_INT_SAMPLER_1D,
+ .UNSIGNED_INT_SAMPLER_2D,
+ .UNSIGNED_INT_SAMPLER_3D,
+ .UNSIGNED_INT_SAMPLER_CUBE,
+ .UNSIGNED_INT_SAMPLER_1D_ARRAY,
+ .UNSIGNED_INT_SAMPLER_2D_ARRAY,
+ .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE,
+ .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+ .UNSIGNED_INT_SAMPLER_BUFFER,
+ .UNSIGNED_INT_SAMPLER_2D_RECT,
+ => |value| value.* = null,
}
}
- // zig fmt: on
};
pub const ShaderProgram = struct {
@@ -418,21 +647,18 @@ pub const UniformCache = struct {
}
pub fn get(self: *UniformCache, name: []const u8) !?*CachedUniform {
- var getValue = false;
-
if (!self.uniforms.contains(name)) {
const cloned_name = try self.allocator.dupe(u8, name);
errdefer self.allocator.free(cloned_name);
- const uniform = CachedUniform.init(self.allocator, self.shader.*, name) catch return null;
+ var uniform = CachedUniform.init(self.allocator, self.shader.*, name) catch return null;
errdefer uniform.deinit(self.allocator);
+ uniform.getShaderValue(self.shader.*);
try self.uniforms.put(cloned_name, uniform);
- getValue = true;
}
if (self.uniforms.getPtr(name)) |ptr| {
- ptr.getShaderValue(self.shader.*);
return ptr;
}
@@ -504,9 +730,11 @@ pub const FramebufferObject = struct {
var self: FramebufferObject = undefined;
c.glGenFramebuffers(1, &self.buffer_id);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.buffer_id);
+ defer c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
c.glGenTextures(1, &self.texture_id);
c.glBindTexture(c.GL_TEXTURE_2D, self.texture_id);
+ defer c.glBindTexture(c.GL_TEXTURE_2D, 0);
c.glTexImage2D(
c.GL_TEXTURE_2D,
0,
@@ -520,7 +748,6 @@ pub const FramebufferObject = struct {
);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_LINEAR);
- c.glBindTexture(c.GL_TEXTURE_2D, 0);
c.glFramebufferTexture2D(
c.GL_FRAMEBUFFER,
@@ -533,7 +760,6 @@ pub const FramebufferObject = struct {
if (c.glCheckFramebufferStatus(c.GL_FRAMEBUFFER) != c.GL_FRAMEBUFFER_COMPLETE)
return error.FBONotComplete;
- c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
return self;
}
@@ -570,7 +796,12 @@ pub const VertexObject = struct {
c.glGenBuffers(1, &self.buffer_id);
c.glBindBuffer(c.GL_ARRAY_BUFFER, self.buffer_id);
- c.glBufferData(c.GL_ARRAY_BUFFER, @as(c_long, @intCast(@sizeOf(T) * vertices.len)), @as(*const anyopaque, @ptrCast(vertices.ptr)), c.GL_STATIC_DRAW);
+ c.glBufferData(
+ c.GL_ARRAY_BUFFER,
+ @as(c_long, @intCast(@sizeOf(T) * vertices.len)),
+ @as(*const anyopaque, @ptrCast(vertices.ptr)),
+ c.GL_STATIC_DRAW,
+ );
return self;
}
diff --git a/src/main.zig b/src/main.zig
index 0181db6..6f172e4 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -18,6 +18,9 @@ fn errorCallback(err: c_int, description: [*c]const u8) callconv(.C) void {
}
pub fn main() !void {
+ const progress = std.Progress.start(.{});
+ defer progress.end();
+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
@@ -115,7 +118,7 @@ pub fn main() !void {
var cache = gl.UniformCache.init(std.heap.c_allocator, &main_program);
defer cache.deinit();
- const control = try ctrl.ControlServer.init(arena.allocator(), config.osc, &cache);
+ const control = try ctrl.ControlServer.init(arena.allocator(), progress, config.osc, &cache);
defer control.destroy();
while (c.glfwWindowShouldClose(window) == c.GL_FALSE) {
@@ -125,14 +128,16 @@ pub fn main() !void {
const now_time = c.glfwGetTime();
prev_time = now_time;
+ control.update();
+
const stat = try shader_file.stat();
- if (stat.mtime > last_stat.mtime) {
+ if (stat.mtime > last_stat.mtime or control.reload_requested) {
try reloadShader(&config, &main_program, config.fragment, stat.size);
try cache.refresh();
last_stat = stat;
- }
- control.update();
+ control.reload_requested = false;
+ }
fbo.bind();
c.glClear(c.GL_COLOR_BUFFER_BIT);
diff --git a/src/video.zig b/src/video.zig
new file mode 100644
index 0000000..368d46f
--- /dev/null
+++ b/src/video.zig
@@ -0,0 +1,138 @@
+const std = @import("std");
+const c = @import("c.zig");
+const gl = @import("gl.zig");
+
+fn check(err: c_int) !void {
+ if (err >= 0) return;
+ var buf: [c.AV_ERROR_MAX_STRING_SIZE]u8 = undefined;
+ _ = c.av_make_error_string(&buf, buf.len, err);
+ std.debug.print("libav error: {s}\n", .{buf});
+ return error.AVError;
+}
+
+pub fn loadVideo(progress_root: std.Progress.Node, filename: [*:0]const u8, texture: *gl.Texture) !void {
+ const outer_progress = progress_root.start("load video", 0);
+ defer outer_progress.end();
+
+ var format_ctx: ?*c.AVFormatContext = null;
+ try check(c.avformat_open_input(&format_ctx, filename, null, null));
+ defer c.avformat_close_input(&format_ctx);
+
+ if (format_ctx) |format| {
+ try check(c.avformat_find_stream_info(format, null));
+
+ const video_stream = for (format.streams, 0..format.nb_streams) |c_stream, _| {
+ const stream = @as(*c.AVStream, c_stream orelse unreachable);
+ if (stream.codecpar.*.codec_type == c.AVMEDIA_TYPE_VIDEO) break stream;
+ } else unreachable;
+
+ const codec_par = video_stream.codecpar.*;
+ const codec = c.avcodec_find_decoder(codec_par.codec_id) orelse unreachable;
+ var codec_ctx = c.avcodec_alloc_context3(codec) orelse unreachable;
+ defer c.avcodec_free_context(&codec_ctx);
+
+ try check(c.avcodec_parameters_to_context(codec_ctx, &codec_par));
+ try check(c.avcodec_open2(codec_ctx, codec, null));
+ defer check(c.avcodec_close(codec_ctx)) catch unreachable;
+
+ std.debug.print("opened codec, size {}x{}, ~{} frames\n", .{ codec_par.width, codec_par.height, video_stream.nb_frames });
+
+ const sws_ctx = c.sws_getContext(
+ codec_par.width,
+ codec_par.height,
+ codec_par.format,
+ codec_par.width,
+ codec_par.height,
+ c.AV_PIX_FMT_RGBA,
+ c.SWS_POINT,
+ null,
+ null,
+ 0,
+ ) orelse unreachable;
+ defer c.sws_freeContext(sws_ctx);
+
+ var packet = c.av_packet_alloc();
+ defer c.av_packet_free(&packet);
+
+ var raw_frame = c.av_frame_alloc();
+ defer c.av_frame_free(&raw_frame);
+ var rgba_frame = c.av_frame_alloc();
+ defer c.av_frame_free(&rgba_frame);
+
+ const frame_count = blk: {
+ const progress = outer_progress.start("scanning video frames", @intCast(video_stream.nb_frames));
+ defer progress.end();
+
+ var i: usize = 0;
+ var res = c.av_read_frame(format, packet);
+ while (res >= 0) : (res = c.av_read_frame(format, packet)) {
+ if (packet.*.stream_index == video_stream.index) {
+ try check(c.avcodec_send_packet(codec_ctx, packet));
+
+ const ires = c.avcodec_receive_frame(codec_ctx, raw_frame);
+ if (ires == c.AVERROR_EOF) break;
+ if (ires == c.AVERROR(c.EAGAIN)) continue;
+ try check(ires);
+
+ i += 1;
+ progress.completeOne();
+ }
+ c.av_packet_unref(packet);
+ }
+
+ try check(c.avformat_seek_file(format, video_stream.index, 0, 0, 0, 0));
+ c.avcodec_flush_buffers(codec_ctx);
+
+ break :blk i;
+ };
+
+ std.debug.print("counted frames, found {}\n", .{frame_count});
+ switch (texture.type) {
+ .TEXTURE_2D => {},
+ .TEXTURE_3D, .TEXTURE_2D_ARRAY => texture.allocate3D(codec_par.width, codec_par.height, @intCast(frame_count)),
+ else => unreachable,
+ }
+
+ {
+ const progress = outer_progress.start("loading video frames", frame_count);
+ defer progress.end();
+
+ defer c.glPixelStorei(c.GL_UNPACK_ROW_LENGTH, 0);
+
+ var i: i32 = 0;
+ var res = c.av_read_frame(format, packet);
+ while (res >= 0) : (res = c.av_read_frame(format, packet)) {
+ if (packet.*.stream_index == video_stream.index) {
+ try check(c.avcodec_send_packet(codec_ctx, packet));
+
+ const ires = c.avcodec_receive_frame(codec_ctx, raw_frame);
+ if (ires == c.AVERROR_EOF) break;
+ if (ires == c.AVERROR(c.EAGAIN)) continue;
+ try check(ires);
+ try check(c.sws_scale_frame(sws_ctx, rgba_frame, raw_frame));
+
+ c.glPixelStorei(c.GL_UNPACK_ROW_LENGTH, @divFloor(rgba_frame.*.linesize[0], @sizeOf(u8) * 4));
+
+ switch (texture.type) {
+ .TEXTURE_2D => {
+ texture.setData2D(codec_par.width, codec_par.height, rgba_frame.*.data[0]);
+ break;
+ },
+
+ .TEXTURE_3D,
+ .TEXTURE_2D_ARRAY,
+ => texture.setLayer3D(codec_par.width, codec_par.height, i, rgba_frame.*.data[0]),
+
+ else => unreachable,
+ }
+
+ i += 1;
+ progress.completeOne();
+ }
+ c.av_packet_unref(packet);
+ } else {
+ if (res != c.AVERROR_EOF) try check(res);
+ }
+ }
+ } else unreachable;
+}