From 8987c88bba84125e358319c2d4085f958401ff11 Mon Sep 17 00:00:00 2001 From: s-ol Date: Fri, 21 Mar 2025 13:25:10 +0100 Subject: package for luarocks --- LICENSE | 21 +++++ README.md | 14 ++++ example/main.lua | 32 ++++++++ example/texture-share-vk.lua | 1 + main.lua | 30 ------- texture-share-vk-scm-0.rockspec | 28 +++++++ texture-share-vk.lua | 172 ++++++++++++++++++++++++++++++++++++++++ texture_share_vk.lua | 172 ---------------------------------------- 8 files changed, 268 insertions(+), 202 deletions(-) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 example/main.lua create mode 120000 example/texture-share-vk.lua delete mode 100644 main.lua create mode 100644 texture-share-vk-scm-0.rockspec create mode 100644 texture-share-vk.lua delete mode 100644 texture_share_vk.lua diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c1900fa --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 s-ol + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c234d19 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# lua texture-share-vk + +A LÖVE library for sharing GPU textures between processes. + +This wraps the OpenGL C API of [texture-share-vk](https://github.com/DigitOtter/texture-share-vk) and allows publishing a LÖVE Canvas object. + +## API + +- `tvs.new()`: creates a new `Client` object (`texture-share-gl` client). +- `Client:newSharedCanvas(name, ...)`: creates a new `SharedCanvas` object by delegating to [`love.graphics.newCanvas(...)`](https://love2d.org/wiki/love.graphics.newCanvas) and publishes it. +- `Canvas.name`: the name passed in the SharedCanvas constructor +- `Canvas.canvas`: the love2d `Canvas` object +- `Canvas:send()`: Sends the current contents of the Canvas to subscribers, call this after drawing to the Canvas. +- `Canvas:any_other_method()`: delegated to the love2d `Canvas` object diff --git a/example/main.lua b/example/main.lua new file mode 100644 index 0000000..4788361 --- /dev/null +++ b/example/main.lua @@ -0,0 +1,32 @@ +local tvs = require 'texture-share-vk' +local client, shared + +function love.load() + client = tvs.new() + shared = client:newSharedCanvas("love2d", love.graphics.getDimensions()) + + print(shared:getDimensions()) -- NOTE: delegated to LÖVE Canvas +end + +local angle = 0 + +function love.update(dt) + angle = angle + 5 * dt +end + +function love.draw() + local width, height = love.graphics.getDimensions() + love.graphics.setCanvas(shared.canvas) -- NOTE: different from normal canvas + love.graphics.clear(0, 0, 0, 0) + love.graphics.translate(width/2, height/2) + love.graphics.rotate(angle) + + love.graphics.setColor(1, 0, 0, 1) + love.graphics.rectangle("fill", -150,-150, 300,300) + love.graphics.setCanvas() + shared:send() + + love.graphics.reset() + love.graphics.setBlendMode("alpha", "premultiplied") + love.graphics.draw(shared.canvas) -- NOTE: different from normal canvas +end diff --git a/example/texture-share-vk.lua b/example/texture-share-vk.lua new file mode 120000 index 0000000..f828f1e --- /dev/null +++ b/example/texture-share-vk.lua @@ -0,0 +1 @@ +../texture-share-vk.lua \ No newline at end of file diff --git a/main.lua b/main.lua deleted file mode 100644 index d1b6d70..0000000 --- a/main.lua +++ /dev/null @@ -1,30 +0,0 @@ -local tvs = require 'texture_share_vk' -local client, shared - -function love.load() - client = tvs.new() - shared = client:newSharedCanvas("love2d", love.graphics.getDimensions()) -end - -local angle = 0 - -function love.update(dt) - angle = angle + 5 * dt -end - -function love.draw() - local width, height = love.graphics.getDimensions() - love.graphics.setCanvas(shared.canvas) -- NOTE: different from normal canvas - love.graphics.clear(0, 0, 0, 0) - love.graphics.translate(width/2, height/2) - love.graphics.rotate(angle) - - love.graphics.setColor(1, 0, 0, 1) - love.graphics.rectangle("fill", -150,-150, 300,300) - love.graphics.setCanvas() - shared:send() - - love.graphics.reset() - love.graphics.setBlendMode("alpha", "premultiplied") - love.graphics.draw(shared.canvas) -- NOTE: different from normal canvas -end diff --git a/texture-share-vk-scm-0.rockspec b/texture-share-vk-scm-0.rockspec new file mode 100644 index 0000000..2c70e89 --- /dev/null +++ b/texture-share-vk-scm-0.rockspec @@ -0,0 +1,28 @@ +package = "texture-share-vk" +version = "scm-0" + +source = { + url = "git+https://git.s-ol.nu/lua-texture-share-vk.git", +} + +description = { + summary = "A LÖVE library for sharing GPU textures between processes", + detailed = [[ + This wraps the OpenGL C API of https://github.com/DigitOtter/texture-share-vk + and allows publishing a LÖVE Canvas object. + ]], + homepage = "https://git.s-ol.nu/lua-texture-share-vk/-/", + license = "MIT", +} + +dependencies = { + -- requires luajit FFI + "lua == 5.1", +} + +build = { + type = "builtin", + modules = { + ["texture-share-vk"] = "texture-share-vk.lua", + } +} diff --git a/texture-share-vk.lua b/texture-share-vk.lua new file mode 100644 index 0000000..b8e2523 --- /dev/null +++ b/texture-share-vk.lua @@ -0,0 +1,172 @@ +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; + +// 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); + +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); +]] + +-- 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, +} + +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:__gc() + tvs.gl_client_destroy(self) +end + +function Canvas:send() + tvs.gl_client_send_image( + self.client.client, + self.name, + self.texture_id, + GL_TEXTURE_2D, + false, + 0, + nil + ) +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 diff --git a/texture_share_vk.lua b/texture_share_vk.lua deleted file mode 100644 index b8e2523..0000000 --- a/texture_share_vk.lua +++ /dev/null @@ -1,172 +0,0 @@ -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; - -// 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); - -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); -]] - --- 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, -} - -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:__gc() - tvs.gl_client_destroy(self) -end - -function Canvas:send() - tvs.gl_client_send_image( - self.client.client, - self.name, - self.texture_id, - GL_TEXTURE_2D, - false, - 0, - nil - ) -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 -- cgit v1.2.3