diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-02-24 10:16:36 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-02-24 13:18:04 +0000 |
| commit | 96063a5d32d41a70cdbfba68e3f018c9c320aa68 (patch) | |
| tree | 3adadd5cc7ce41f68919ee28f9e56b2b1fec2c54 | |
| parent | update for zig 0.13.0 (diff) | |
| download | glsl-view-96063a5d32d41a70cdbfba68e3f018c9c320aa68.tar.gz glsl-view-96063a5d32d41a70cdbfba68e3f018c9c320aa68.zip | |
load videos and textures using libav
| -rw-r--r-- | build.zig | 4 | ||||
| -rw-r--r-- | src/c.zig | 4 | ||||
| -rw-r--r-- | src/control.zig | 50 | ||||
| -rw-r--r-- | src/gl.zig | 327 | ||||
| -rw-r--r-- | src/main.zig | 13 | ||||
| -rw-r--r-- | src/video.zig | 138 |
6 files changed, 482 insertions, 54 deletions
@@ -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); @@ -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; @@ -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; +} |
