local ffi = require 'ffi' ffi.cdef [[ // GL/gl.h typedef int GLint; typedef unsigned int GLuint; typedef unsigned int GLenum; void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint *params); // texture_share_ipc/texture_share_ipc.h typedef enum ImgFormat { R8G8B8A8, R8G8B8, B8G8R8A8, B8G8R8, Undefined, } ImgFormat; typedef uint8_t ImgName[1024]; typedef struct ShmemDataInternal { ImgName name; uint32_t handle_id; uint32_t width; uint32_t height; enum ImgFormat format; uint64_t allocation_size; uint64_t gpu_device_uuid_0; uint64_t gpu_device_uuid_1; } ShmemDataInternal; // texture_share_gl_client/texture_share_gl_client.h typedef enum ImageLookupResult { Error = -1, NotFound = 0, Found = 1, RequiresUpdate = 2, } ImageLookupResult; typedef struct ClientImageDataGuard ClientImageDataGuard; typedef struct GlClient GlClient; typedef struct GlImageExtent { GLint top_left[2]; GLint bottom_right[2]; } GlImageExtent; bool gl_client_initialize_external_gl(void); struct GlClient *gl_client_new(const char *socket_path, uint64_t timeout_in_millis); struct GlClient *gl_client_new_with_server_launch(const char *socket_path, uint64_t client_timeout_in_millis, const char *server_program, const char *server_lock_path, const char *server_socket_path, const char *shmem_prefix, uint64_t server_socket_timeout_in_millis, uint64_t server_connection_wait_timeout_in_millis, uint64_t server_ipc_timeout_in_millis, uint64_t server_lockfile_timeout_in_millis, uint64_t server_spawn_timeout_in_millis); void gl_client_destroy(struct GlClient *gl_client); enum ImageLookupResult gl_client_init_image(struct GlClient *gl_client, const char *image_name, uint32_t width, uint32_t height, ImgFormat format, bool overwrite_existing); enum ImageLookupResult gl_client_find_image(struct GlClient *gl_client, const char *image_name, bool force_update); struct ClientImageDataGuard *gl_client_find_image_data(struct GlClient *gl_client, const char *image_name, bool force_update); const ShmemDataInternal *gl_client_image_data_guard_read(const struct ClientImageDataGuard *image_data_guard); void gl_client_image_data_guard_destroy(struct ClientImageDataGuard *image_data_guard); int gl_client_send_image(struct GlClient *gl_client, const char *image_name, GLuint src_texture_id, GLenum src_texture_target, bool invert, GLuint prev_fbo, const struct GlImageExtent *extents); int gl_client_recv_image(struct GlClient *gl_client, const char *image_name, GLuint src_texture_id, GLenum src_texture_target, bool invert, GLuint prev_fbo, const struct GlImageExtent *extents); ]] -- from texture_share_vk/config.h local VK_SERVER_EXECUTABLE = "/usr/bin/texture-share-vk-server" local VK_SERVER_DEFAULT_LOCKFILE_PATH = "/tmp/vk_server/vk_server.lock" local VK_SERVER_DEFAULT_SOCKET_PATH = "/tmp/vk_server/vk_server.sock" local VK_SERVER_DEFAULT_SHMEM_PREFIX = "shmem_img_" -- from GL/gl.h local GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1 local GL_COLOR_ATTACHMENT0 = 0x8CE0 local GL_FRAMEBUFFER = 0x8D40 local GL_TEXTURE_2D = 0x0DE1 local IMAGE_FORMATS = { rgba8 = ffi.C.R8G8B8A8, rgb8 = ffi.C.R8G8B8, [ffi.C.R8G8B8A8] = "rgba8", [ffi.C.R8G8B8] = "rgb8", } local tvs = ffi.load("texture_share_gl_client") local gl = ffi.load("GL") local Client, Canvas = {}, {} Client.__index = Client function Client.new() assert(tvs.gl_client_initialize_external_gl(), "failed to init tsv OpenGL") local client = assert(tvs.gl_client_new_with_server_launch( VK_SERVER_DEFAULT_SOCKET_PATH, 1000, VK_SERVER_EXECUTABLE, VK_SERVER_DEFAULT_LOCKFILE_PATH, VK_SERVER_DEFAULT_SOCKET_PATH, VK_SERVER_DEFAULT_SHMEM_PREFIX, 2000, 2000, 2000, 2000, 2000 ), "failed to connect or launch tsv server") return setmetatable({ client = client }, Client) end function Client:newSharedCanvas(name, ...) local canvas = love.graphics.newCanvas(...) local width, height = canvas:getDimensions() local format = assert(IMAGE_FORMATS[canvas:getFormat()], "unsupported Texture format") assert( tvs.gl_client_init_image(self.client, name, width, height, format, true) > 0, "failed to init image" ) local last_canvas = love.graphics.getCanvas() love.graphics.setCanvas(canvas) local name_ptr = ffi.typeof('GLint[1]')() gl.glGetFramebufferAttachmentParameteriv( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, name_ptr ) love.graphics.setCanvas(last_canvas) return setmetatable({ name = name, canvas = canvas, client = self, texture_id = name_ptr[0], }, Canvas) end function Client:loadSharedCanvas(name) local guard = assert( tvs.gl_client_find_image_data(self.client, name, false), "failed to load image" ) local data = tvs.gl_client_image_data_guard_read(guard) local format = assert(IMAGE_FORMATS[tonumber(data.format)], "unsupported Texture format") local canvas = love.graphics.newCanvas(data.width, data.height, { format = format }) tvs.gl_client_image_data_guard_destroy(guard) local last_canvas = love.graphics.getCanvas() love.graphics.setCanvas(canvas) local name_ptr = ffi.typeof('GLint[1]')() gl.glGetFramebufferAttachmentParameteriv( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, name_ptr ) love.graphics.setCanvas(last_canvas) return setmetatable({ name = name, canvas = canvas, client = self, texture_id = name_ptr[0], }, Canvas) end function Client:__gc() tvs.gl_client_destroy(self) end function Canvas:send(fail_silently) local status = tvs.gl_client_send_image( self.client.client, self.name, self.texture_id, GL_TEXTURE_2D, false, 0, nil ) assert(status >= 0, "error sending image") assert(fail_silently or (status == 1), "send: image not found") end function Canvas:recv(fail_silently) local status = tvs.gl_client_recv_image( self.client.client, self.name, self.texture_id, GL_TEXTURE_2D, false, 0, nil ) assert(status >= 0, "error receiving image") assert(fail_silently or (status == 1), "recv: image not found") end function Canvas:__index(key) local val = Canvas[key] if val then return val end val = self.canvas[key] if val then Canvas[key] = function (shared, ...) return val(shared.canvas, ...) end return Canvas[key] end end return Client