const std = @import("std"); const c = @import("c.zig"); const gl = @import("gl.zig"); const Source = gl.Source; const Output = @import("output.zig").Output; var global_client: ?*c.GlClient = null; fn get_or_init_client() *c.GlClient { if (global_client == null) { _ = c.gl_client_initialize_external_gl(); global_client = c.gl_client_new_with_server_launch( c.VK_SERVER_DEFAULT_SOCKET_PATH, 1000, c.VK_SERVER_EXECUTABLE, c.VK_SERVER_DEFAULT_LOCKFILE_PATH, c.VK_SERVER_DEFAULT_SOCKET_PATH, c.VK_SERVER_DEFAULT_SHMEM_PREFIX, 2000, 2000, 2000, 2000, 2000, ) orelse unreachable; } return global_client.?; } pub const TSVSource = struct { source: gl.Source, name: [:0]const u8, pub fn init( allocator: std.mem.Allocator, name: [*:0]const u8, texture_type: gl.Texture.Type, ) !*gl.Source { const self = try allocator.create(TSVSource); errdefer allocator.destroy(self); self.* = .{ .source = .{ .texture = gl.Texture.create(texture_type), .update_fn = update, .deinit_fn = deinit, }, .name = try allocator.dupeZ(u8, std.mem.span(name)), }; errdefer allocator.free(self.name); const guard = c.gl_client_find_image_data(get_or_init_client(), name, false) orelse return error.notFound; defer c.gl_client_image_data_guard_destroy(guard); const metadata = c.gl_client_image_data_guard_read(guard) orelse return error.tsvError; self.source.texture.allocate( @intCast(metadata.*.width), @intCast(metadata.*.height), 0, c.GL_RGBA8, ); self.source.update(); return &self.source; } fn deinit(source: *const gl.Source, allocator: std.mem.Allocator) void { const self: *const TSVSource = @fieldParentPtr("source", source); allocator.free(self.name); allocator.destroy(self); } fn update(source: *const gl.Source) void { const self: *const TSVSource = @fieldParentPtr("source", source); var fbo: c.GLint = undefined; c.glGetIntegerv(c.GL_DRAW_FRAMEBUFFER_BINDING, &fbo); _ = c.gl_client_recv_image( get_or_init_client(), self.name.ptr, self.source.texture.id, @intFromEnum(self.source.texture.type), false, @intCast(fbo), null, ); } }; pub const TSVOutput = struct { pub const Config = struct { name: [:0]const u8, pub const default: Config = .{ .name = "glsl-view", }; pub fn create( config: *const Config, allocator: std.mem.Allocator, constants: *gl.Constants, ) *Output { const self = allocator.create(TSVOutput) catch unreachable; self.* = .{ .output = .{ .update_fn = update, .destroy_fn = destroy, }, .config = config, }; _ = c.gl_client_init_image( get_or_init_client(), config.name, @intCast(constants.config.width), @intCast(constants.config.height), c.R8G8B8A8, true, ); return &self.output; } }; output: Output, config: *const Config, fn update(output: *Output, texture_id: c.GLuint) bool { const self: *TSVOutput = @fieldParentPtr("output", output); var fbo: c.GLint = undefined; c.glGetIntegerv(c.GL_DRAW_FRAMEBUFFER_BINDING, &fbo); _ = c.gl_client_send_image( get_or_init_client(), self.config.name, texture_id, c.GL_TEXTURE_2D, false, @intCast(fbo), null, ); return false; } fn destroy(output: *Output, allocator: std.mem.Allocator) void { const self: *TSVOutput = @fieldParentPtr("output", output); allocator.destroy(self); } };