Skip to content
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ option(LOVR_ENABLE_HEADSET "Enable the headset module" ON)
option(LOVR_ENABLE_MATH "Enable the math module" ON)
option(LOVR_ENABLE_PHYSICS "Enable the physics module" ON)
option(LOVR_ENABLE_SYSTEM "Enable the system module" ON)
option(LOVR_ENABLE_TASK "Enable the task module" ON)
option(LOVR_ENABLE_THREAD "Enable the thread module" ON)
option(LOVR_ENABLE_TIMER "Enable the timer module" ON)

Expand Down Expand Up @@ -540,6 +541,15 @@ else()
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_SYSTEM)
endif()

if(LOVR_ENABLE_TASK)
target_sources(lovr PRIVATE
src/modules/task/task.c
src/api/l_task.c
)
else()
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_TASK)
endif()

if(LOVR_ENABLE_THREAD)
target_sources(lovr PRIVATE
src/core/job.c
Expand Down
10 changes: 10 additions & 0 deletions etc/boot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ local conf = {
math = true,
physics = true,
system = true,
task = true,
thread = true,
timer = true
},
Expand Down Expand Up @@ -201,6 +202,11 @@ function lovr.run()
dt = lovr.headset.update()
if not lovr.headset.isActive() then lovr.simulate(dt) end
end
if lovr.task then
for task in lovr.task.poll() do
lovr.taskready(task)
end
end
if lovr.update then lovr.update(dt) end
if lovr.audio then lovr.audio.update(dt) end
if lovr.graphics then
Expand Down Expand Up @@ -403,6 +409,10 @@ function lovr.threaderror(thread, err)
error('Thread error\n\n' .. err, 0)
end

function lovr.taskready(task)
assert(lovr.task.resume(task))
end

function lovr.filechanged(path, action, oldpath)
if not path:match('^%.') then
lovr.event.restart()
Expand Down
131 changes: 104 additions & 27 deletions src/api/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ LOVR_EXPORT int luaopen_lovr_headset(lua_State* L);
LOVR_EXPORT int luaopen_lovr_math(lua_State* L);
LOVR_EXPORT int luaopen_lovr_physics(lua_State* L);
LOVR_EXPORT int luaopen_lovr_system(lua_State* L);
LOVR_EXPORT int luaopen_lovr_task(lua_State* L);
LOVR_EXPORT int luaopen_lovr_thread(lua_State* L);
LOVR_EXPORT int luaopen_lovr_timer(lua_State* L);

Expand All @@ -55,27 +56,6 @@ static void luax_destructor(lua_State* L, void* userdata) {
}
#endif

static int luax_release(lua_State* L) {
Object* object = lua_touserdata(L, 1);

if (!object) {
return 0;
}

// Remove from userdata cache
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrobjects");
lua_pushlightuserdata(L, object->pointer);
lua_pushnil(L);
lua_rawset(L, -3);
lua_pop(L, 1);

// Release
lovrRelease(object->pointer, lovrTypeInfo[object->type].destructor);
object->pointer = NULL;

return 0;
}

void luax_preload(lua_State* L) {
static const luaL_Reg lovrModules[] = {
{ "lovr", luaopen_lovr },
Expand Down Expand Up @@ -106,6 +86,9 @@ void luax_preload(lua_State* L) {
#ifndef LOVR_DISABLE_SYSTEM
{ "lovr.system", luaopen_lovr_system },
#endif
#ifndef LOVR_DISABLE_TASK
{ "lovr.task", luaopen_lovr_task },
#endif
#ifndef LOVR_DISABLE_THREAD
{ "lovr.thread", luaopen_lovr_thread },
#endif
Expand All @@ -124,6 +107,27 @@ void luax_preload(lua_State* L) {
lua_pop(L, 2);
}

static int luax_release(lua_State* L) {
Object* object = lua_touserdata(L, 1);

if (!object) {
return 0;
}

// Remove from userdata cache
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrobjects");
lua_pushlightuserdata(L, object->pointer);
lua_pushnil(L);
lua_rawset(L, -3);
lua_pop(L, 1);

// Release
lovrRelease(object->pointer, lovrTypeInfo[object->type].destructor);
object->pointer = NULL;

return 0;
}

void _luax_registertype(lua_State* L, int type, const char* name, void (*destructor)(void*), const luaL_Reg* functions) {
lovrTypeInfo[type] = (TypeInfo) { name, destructor };

Expand All @@ -144,7 +148,7 @@ void _luax_registertype(lua_State* L, int type, const char* name, void (*destruc
lua_pushcfunction(L, luax_release);
lua_setfield(L, -2, "__gc");

// m.__close = gc
// m.__close = luax_release
lua_pushcfunction(L, luax_release);
lua_setfield(L, -2, "__close");
#endif
Expand All @@ -153,11 +157,6 @@ void _luax_registertype(lua_State* L, int type, const char* name, void (*destruc
lua_pushcfunction(L, luax_tostring);
lua_setfield(L, -2, "__tostring");

// Register methods
if (functions) {
luax_register(L, functions);
}

// :release method
lua_pushcfunction(L, luax_release);
lua_setfield(L, -2, "release");
Expand All @@ -166,6 +165,11 @@ void _luax_registertype(lua_State* L, int type, const char* name, void (*destruc
lua_pushcfunction(L, luax_type);
lua_setfield(L, -2, "type");

// Register methods
if (functions) {
luax_register(L, functions);
}

// Pop metatable
lua_pop(L, 1);
}
Expand Down Expand Up @@ -305,6 +309,33 @@ void luax_registerloader(lua_State* L, lua_CFunction loader, int index) {
lua_pop(L, 1);
}

static int luax_wrapasync(lua_State* L) {
if (luax_getthreaddata(L)) {
int n = lua_gettop(L);
lua_pushvalue(L, lua_upvalueindex(1));
lua_insert(L, 1);
return luax_callthread(L, n);
} else {
lua_CFunction function = lua_tocfunction(L, lua_upvalueindex(1));
return function(L);
}
}

void _luax_registerasync(lua_State* L, const char* name, const char** methods) {
luaL_getmetatable(L, name);

if (lua_istable(L, -1)) {
while (*methods) {
const char* method = *methods++;
lua_getfield(L, -1, method);
lua_pushcclosure(L, luax_wrapasync, 1);
lua_setfield(L, -2, method);
}
}

lua_pop(L, 1);
}

int luax_resume(lua_State* T, int n) {
#if LUA_VERSION_NUM >= 504
int results;
Expand Down Expand Up @@ -433,6 +464,52 @@ void luax_pushstash(lua_State* L, const char* name) {
}
}

void* luax_getthreaddata(lua_State* L) {
#ifdef LOVR_USE_LUAU
return lua_getthreaddata(L);
#elif LUA_VERSION_NUM >= 503
return *(void**) lua_getextraspace(L);
#else
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrthreaddata");
if (lua_isnil(L, -1)) return lua_pop(L, 1), NULL;
lua_pushthread(L);
lua_rawget(L, -2);
void* data = lua_touserdata(L, -1);
lua_pop(L, 2);
return data;
#endif
}

void luax_setthreaddata(lua_State* L, void* data) {
#ifdef LOVR_USE_LUAU
lua_setthreaddata(L, data);
#elif LUA_VERSION_NUM >= 503
*(void**) lua_getextraspace(L) = data;
#else
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrthreaddata");
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_newtable(L);

lua_newtable(L);
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);

lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrthreaddata");
}
lua_pushthread(L);
if (data) {
lua_pushlightuserdata(L, data);
} else {
lua_pushnil(L);
}
lua_rawset(L, -3);
lua_pop(L, 1);
#endif
}

void luax_setmainthread(lua_State *L) {
#if LUA_VERSION_NUM < 502
lua_pushthread(L);
Expand Down
15 changes: 15 additions & 0 deletions src/api/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ typedef struct {
#endif

#define luax_registertype(L, T) _luax_registertype(L, T_ ## T, #T, lovr ## T ## Destroy, lovr ## T)
#define luax_registerasync(L, T) _luax_registerasync(L, #T, lovr ## T ## Async)
#define luax_totype(L, i, T) (T*) _luax_totype(L, i, T_ ## T)
#define luax_checktype(L, i, T) (T*) _luax_checktype(L, i, T_ ## T)
#define luax_pushtype(L, T, o) _luax_pushtype(L, T_ ## T, o)
Expand All @@ -123,6 +124,7 @@ int luax_typeerror(lua_State* L, int index, const char* expected);
void _luax_pushtype(lua_State* L, int type, void* object);
int _luax_checkenum(lua_State* L, int index, const StringEntry* map, const char* fallback, const char* label);
void luax_registerloader(lua_State* L, int (*loader)(lua_State* L), int index);
void _luax_registerasync(lua_State* L, const char* type, const char** methods);
int luax_resume(lua_State* T, int n);
int luax_loadbufferx(lua_State* L, const char* buffer, size_t size, const char* name, const char* mode);
void luax_vthrow(void* L, const char* format, va_list args);
Expand All @@ -133,6 +135,8 @@ int luax_pushsuccess(lua_State* L, bool success);
void luax_pushconf(lua_State* L);
int luax_setconf(lua_State* L);
void luax_pushstash(lua_State* L, const char* name);
void* luax_getthreaddata(lua_State* L);
void luax_setthreaddata(lua_State* L, void* data);
void luax_setmainthread(lua_State* L);
void luax_atexit(lua_State* L, void (*finalizer)(void));
void luax_close(lua_State* L);
Expand Down Expand Up @@ -201,3 +205,14 @@ struct Shape* luax_newconvexshape(lua_State* L, int index);
struct Shape* luax_newmeshshape(lua_State* L, int index);
struct Shape* luax_newterrainshape(lua_State* L, int index);
#endif

#ifndef LOVR_DISABLE_TASK
#include "task/task.h"
Task* luax_gettask(lua_State* L);
int luax_yieldpoll(lua_State* L, fn_task* poll, fn_task* block, fn_continuation* continuation, void* context);
int luax_yieldjob(lua_State* L, fn_task* fn, fn_continuation* continuation, void* context);
#endif

#ifndef LOVR_DISABLE_THREAD
int luax_callthread(lua_State* L, int n);
#endif
18 changes: 15 additions & 3 deletions src/api/l_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ static int l_lovrDataNewBlobView(lua_State* L) {
return 1;
}

static int luax_pushimage(lua_State* L, void* context) {
luax_pushtype(L, Image, context);
lovrRelease(context, lovrImageDestroy);
return 1;
}

static bool luax_loadimage(void** context) {
Blob* blob = *context;
Image* image = lovrImageCreateFromFile(blob);
lovrRelease(blob, lovrBlobDestroy);
*context = image;
return !!image;
}

static int l_lovrDataNewImage(lua_State* L) {
Image* image = NULL;
if (lua_type(L, 1) == LUA_TNUMBER) {
Expand Down Expand Up @@ -139,9 +153,7 @@ static int l_lovrDataNewImage(lua_State* L) {
memcpy(lovrImageGetLayerData(image, 0, 0), lovrImageGetLayerData(source, 0, 0), lovrImageGetLayerSize(image, 0));
} else {
Blob* blob = luax_readblob(L, 1, "Texture");
image = lovrImageCreateFromFile(blob);
lovrRelease(blob, lovrBlobDestroy);
luax_assert(L, image);
return luax_yieldjob(L, luax_loadimage, luax_pushimage, blob);
}
}

Expand Down
34 changes: 28 additions & 6 deletions src/api/l_graphics_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -532,22 +532,44 @@ static int l_lovrBufferNewReadback(lua_State* L) {
return 1;
}

static bool luax_pollreadback(void** context) {
return lovrReadbackPoll(*context);
}

static bool luax_waitreadback(void** context) {
return lovrReadbackWait(*context);
}

static int luax_pushreadbackdata(lua_State* L, void* context) {
DataField* format;
uint32_t count;
void* data = lovrReadbackGetData(context, &format, &count);
lovrRelease(context, lovrReadbackDestroy);
luax_assert(L, data && format);
return luax_pushbufferdata(L, format, count, data);
}

static int l_lovrBufferGetData(lua_State* L) {
Buffer* buffer = luax_checktype(L, 1, Buffer);
const DataField* format = lovrBufferGetInfo(buffer)->format;
luax_check(L, format, "Buffer:getData requires the Buffer to have a format");

uint32_t offset, extent;
if (format->length > 0) {
uint32_t index = luax_optu32(L, 2, 1) - 1;
luax_check(L, index < format->length, "Buffer:getData index exceeds the Buffer's length");
uint32_t count = luax_optu32(L, 3, format->length - index);
void* data = lovrBufferGetData(buffer, index * format->stride, count * format->stride);
luax_assert(L, data);
return luax_pushbufferdata(L, format, count, data);
offset = index * format->stride;
extent = count * format->stride;
} else {
void* data = lovrBufferGetData(buffer, 0, format->stride);
luax_assert(L, data);
return luax_pushbufferdata(L, format, 0, data);
offset = 0;
extent = format->stride;
}

Readback* readback = lovrReadbackCreateBuffer(buffer, offset, extent);
luax_assert(L, readback);

return luax_yieldpoll(L, luax_pollreadback, luax_waitreadback, luax_pushreadbackdata, readback);
}

static int l_lovrBufferSetData(lua_State* L) {
Expand Down
Loading
Loading