I want to stream the rendered output of love2d to other applications, directly on the GPU, using texture-share-vk. It provides a C API that I can use easily using the luajit C FFI and the following works perfectly:
Code: Select all
local ffi = require 'ffi'
ffi.cdef [[
// gl.h
typedef int GLint;
typedef unsigned int GLuint;
typedef unsigned int GLenum;
// texture_share_ipc.h
typedef enum ImgFormat {
R8G8B8A8,
R8G8B8,
B8G8R8A8,
B8G8R8,
Undefined,
} ImgFormat;
// 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);
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,
void*, // GLuint src_texture_id,
GLenum src_texture_target,
bool invert,
GLuint prev_fbo,
const struct GlImageExtent *extents);
]]
local tvs = ffi.load("texture_share_gl_client")
local width, height
local canvas
local client, image
function love.load()
width, height = love.graphics.getDimensions()
canvas = love.graphics.newCanvas(width, height)
assert(tvs.gl_client_initialize_external_gl(), "no init")
client = assert(tvs.gl_client_new("/tmp/vk_server/vk_server.sock", 1000))
image = assert(tvs.gl_client_init_image(client, "love2d", width, height, ffi.C.R8G8B8A8, true) > 0)
end
local angle = 0
function love.update(dt)
angle = angle + 5 * dt
end
function love.draw()
love.graphics.setCanvas(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()
tvs.gl_client_send_image(
client, "love2d",
canvas:getHandle(),
0x0DE1, -- GL_TEXTURE_2D
false, 0, nil
)
love.graphics.reset()
love.graphics.setBlendMode("alpha", "premultiplied")
love.graphics.draw(canvas)
end
Code: Select all
--- old/src/modules/graphics/wrap_Texture.cpp.pre 2025-03-20 12:46:59.334954849 +0100
+++ new/src/modules/graphics/wrap_Texture.cpp 2025-03-20 12:46:23.144789811 +0100
@@ -300,6 +300,14 @@
return 1;
}
+int w_Texture_getHandle(lua_State *L)
+{
+ Texture *t = luax_checktexture(L, 1);
+ void* handle = (void*) t->getHandle();
+ lua_pushlightuserdata(L, handle);
+ return 1;
+}
+
const luaL_Reg w_Texture_functions[] =
{
{ "getTextureType", w_Texture_getTextureType },
@@ -323,6 +331,7 @@
{ "isReadable", w_Texture_isReadable },
{ "getDepthSampleMode", w_Texture_getDepthSampleMode },
{ "setDepthSampleMode", w_Texture_setDepthSampleMode },
+ { "getHandle", w_Texture_getHandle },
{ 0, 0 }
};
I was wondering if there could be some way to access the C++ function using the C FFI, but there are two hurdles:
- love2d internals are written in C++, luajit FFI is C-only
- liblove.so has internal symbols stripped
Code: Select all
nm -sC /usr/lib/debug/usr/lib/liblove-11.5.so.debug | grep 'Canvas.*getHandle'
000000000009f960 t love::graphics::opengl::Canvas::getRenderTargetHandle() const
000000000009f980 t love::graphics::opengl::Canvas::getHandle() const
000000000009f990 t non-virtual thunk to love::graphics::opengl::Canvas::getHandle() const
PS: If anyone wants to try my example, note that texture-share-vk is Linux only and the generated C headers are invalid without a ptach. The patch shouldn't be needed though as the FFI doesn't load the original headers directly. You can use the OBS source plugin provided by the author to visualize the stream by entering the name I've hardcoded as "love2d" in the example above.