mirror of
https://github.com/bjornbytes/lovr.git
synced 2024-07-04 21:43:34 +00:00
Merge branch 'master' into dev
This commit is contained in:
commit
e12563ad25
|
@ -15,6 +15,7 @@ option(LOVR_ENABLE_SYSTEM "Enable the system module" ON)
|
||||||
option(LOVR_ENABLE_THREAD "Enable the thread module" ON)
|
option(LOVR_ENABLE_THREAD "Enable the thread module" ON)
|
||||||
option(LOVR_ENABLE_TIMER "Enable the timer module" ON)
|
option(LOVR_ENABLE_TIMER "Enable the timer module" ON)
|
||||||
|
|
||||||
|
option(LOVR_USE_GLFW "Use GLFW for desktop windows" ON)
|
||||||
option(LOVR_USE_LUAJIT "Use LuaJIT instead of Lua" ON)
|
option(LOVR_USE_LUAJIT "Use LuaJIT instead of Lua" ON)
|
||||||
option(LOVR_USE_GLSLANG "Use glslang to compile GLSL shaders" ON)
|
option(LOVR_USE_GLSLANG "Use glslang to compile GLSL shaders" ON)
|
||||||
option(LOVR_USE_VULKAN "Use the Vulkan renderer" ON)
|
option(LOVR_USE_VULKAN "Use the Vulkan renderer" ON)
|
||||||
|
@ -78,7 +79,7 @@ if(NOT ANDROID AND LOVR_BUILD_SHARED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# GLFW
|
# GLFW
|
||||||
if(NOT (EMSCRIPTEN OR ANDROID))
|
if(LOVR_USE_GLFW AND NOT (EMSCRIPTEN OR ANDROID))
|
||||||
if(LOVR_SYSTEM_GLFW)
|
if(LOVR_SYSTEM_GLFW)
|
||||||
pkg_search_module(GLFW REQUIRED glfw3)
|
pkg_search_module(GLFW REQUIRED glfw3)
|
||||||
include_directories(${GLFW_INCLUDE_DIRS})
|
include_directories(${GLFW_INCLUDE_DIRS})
|
||||||
|
@ -569,13 +570,16 @@ if(LOVR_ENABLE_SYSTEM)
|
||||||
src/modules/system/system.c
|
src/modules/system/system.c
|
||||||
src/api/l_system.c
|
src/api/l_system.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(LOVR_USE_GLFW)
|
||||||
|
target_compile_definitions(lovr PRIVATE LOVR_USE_GLFW)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_SYSTEM)
|
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_SYSTEM)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LOVR_ENABLE_THREAD)
|
if(LOVR_ENABLE_THREAD)
|
||||||
target_sources(lovr PRIVATE
|
target_sources(lovr PRIVATE
|
||||||
src/modules/thread/channel.c
|
|
||||||
src/modules/thread/thread.c
|
src/modules/thread/thread.c
|
||||||
src/api/l_thread.c
|
src/api/l_thread.c
|
||||||
src/api/l_thread_channel.c
|
src/api/l_thread_channel.c
|
||||||
|
|
|
@ -96,8 +96,7 @@ Resources
|
||||||
|
|
||||||
- [**Documentation**](https://lovr.org/docs): Guides, tutorials, examples, and API documentation.
|
- [**Documentation**](https://lovr.org/docs): Guides, tutorials, examples, and API documentation.
|
||||||
- [**FAQ**](https://lovr.org/docs/FAQ): Frequently Asked Questions.
|
- [**FAQ**](https://lovr.org/docs/FAQ): Frequently Asked Questions.
|
||||||
- [**Slack Group**](https://lovr.org/slack): For general LÖVR discussion and support.
|
- [**Matrix**](https://lovr.org/matrix): The LÖVR community for discussion and support.
|
||||||
- [**Matrix Room**](https://matrix.to/#/!XVAslexgYDYQnYnZBP:matrix.org): Decentralized alternative to Slack.
|
|
||||||
- [**Nightly Builds**](https://lovr.org/download/nightly): Nightly builds for Windows.
|
- [**Nightly Builds**](https://lovr.org/download/nightly): Nightly builds for Windows.
|
||||||
- [**Compiling Guide**](https://lovr.org/docs/Compiling): Information on compiling LÖVR from source.
|
- [**Compiling Guide**](https://lovr.org/docs/Compiling): Information on compiling LÖVR from source.
|
||||||
- [**Contributing**](https://lovr.org/docs/Contributing): Guide for helping out with development 💜
|
- [**Contributing**](https://lovr.org/docs/Contributing): Guide for helping out with development 💜
|
||||||
|
|
|
@ -5,6 +5,7 @@ config = {
|
||||||
supercharge = false,
|
supercharge = false,
|
||||||
sanitize = false,
|
sanitize = false,
|
||||||
strict = true,
|
strict = true,
|
||||||
|
glfw = true,
|
||||||
luajit = false,
|
luajit = false,
|
||||||
glslang = true,
|
glslang = true,
|
||||||
modules = {
|
modules = {
|
||||||
|
@ -221,8 +222,9 @@ else
|
||||||
tup.rule('.obj/lua/*.o', '^ LD %o^ $(cc) $(flags) -o %o %f $(lua_lflags)', lib('lua'))
|
tup.rule('.obj/lua/*.o', '^ LD %o^ $(cc) $(flags) -o %o %f $(lua_lflags)', lib('lua'))
|
||||||
end
|
end
|
||||||
|
|
||||||
if target == 'win32' or target == 'macos' or target == 'linux' then
|
if config.glfw and (target == 'win32' or target == 'macos' or target == 'linux') then
|
||||||
cflags += '-Ideps/glfw/include'
|
cflags += '-Ideps/glfw/include'
|
||||||
|
cflags += '-DLOVR_USE_GLFW'
|
||||||
lflags += '-lglfw'
|
lflags += '-lglfw'
|
||||||
|
|
||||||
glfw_cflags += '-fPIC'
|
glfw_cflags += '-fPIC'
|
||||||
|
|
|
@ -462,7 +462,8 @@ void luax_optcolor(lua_State* L, int index, float color[4]) {
|
||||||
color[3] = luax_optfloat(L, -1, 1.);
|
color[3] = luax_optfloat(L, -1, 1.);
|
||||||
lua_pop(L, 4);
|
lua_pop(L, 4);
|
||||||
break;
|
break;
|
||||||
case LUA_TUSERDATA: {
|
case LUA_TUSERDATA:
|
||||||
|
case LUA_TLIGHTUSERDATA: {
|
||||||
VectorType type;
|
VectorType type;
|
||||||
float* v = luax_tovector(L, index, &type);
|
float* v = luax_tovector(L, index, &type);
|
||||||
if (type == V_VEC3) {
|
if (type == V_VEC3) {
|
||||||
|
@ -473,8 +474,7 @@ void luax_optcolor(lua_State* L, int index, float color[4]) {
|
||||||
memcpy(color, v, 4 * sizeof(float));
|
memcpy(color, v, 4 * sizeof(float));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* fallthrough */
|
} /* fallthrough */
|
||||||
}
|
|
||||||
default: lovrThrow("Expected nil, number, table, vec3, or vec4 for color value");
|
default: lovrThrow("Expected nil, number, table, vec3, or vec4 for color value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,7 @@ static int nextEvent(lua_State* L) {
|
||||||
luax_pushtype(L, Thread, event.data.thread.thread);
|
luax_pushtype(L, Thread, event.data.thread.thread);
|
||||||
lua_pushstring(L, event.data.thread.error);
|
lua_pushstring(L, event.data.thread.error);
|
||||||
lovrRelease(event.data.thread.thread, lovrThreadDestroy);
|
lovrRelease(event.data.thread.thread, lovrThreadDestroy);
|
||||||
|
free(event.data.thread.error);
|
||||||
return 3;
|
return 3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -494,7 +494,7 @@ static Canvas luax_checkcanvas(lua_State* L, int index) {
|
||||||
case LUA_TUSERDATA: canvas.depth.texture = luax_checktype(L, -1, Texture); break;
|
case LUA_TUSERDATA: canvas.depth.texture = luax_checktype(L, -1, Texture); break;
|
||||||
case LUA_TTABLE:
|
case LUA_TTABLE:
|
||||||
lua_getfield(L, -1, "format");
|
lua_getfield(L, -1, "format");
|
||||||
canvas.depth.format = lua_isnil(L, -1) ? canvas.depth.format : luax_checkenum(L, -1, TextureFormat, NULL);
|
canvas.depth.format = lua_isnil(L, -1) ? canvas.depth.format : (uint32_t) luax_checkenum(L, -1, TextureFormat, NULL);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
lua_getfield(L, -1, "texture");
|
lua_getfield(L, -1, "texture");
|
||||||
|
@ -981,12 +981,12 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
||||||
|
|
||||||
if (lua_istable(L, index)) {
|
if (lua_istable(L, index)) {
|
||||||
lua_getfield(L, index, "type");
|
lua_getfield(L, index, "type");
|
||||||
info.type = lua_isnil(L, -1) ? info.type : luax_checkenum(L, -1, TextureType, NULL);
|
info.type = lua_isnil(L, -1) ? info.type : (uint32_t) luax_checkenum(L, -1, TextureType, NULL);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
if (info.imageCount == 0) {
|
if (info.imageCount == 0) {
|
||||||
lua_getfield(L, index, "format");
|
lua_getfield(L, index, "format");
|
||||||
info.format = lua_isnil(L, -1) ? info.format : luax_checkenum(L, -1, TextureFormat, NULL);
|
info.format = lua_isnil(L, -1) ? info.format : (uint32_t) luax_checkenum(L, -1, TextureFormat, NULL);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
lua_getfield(L, index, "samples");
|
lua_getfield(L, index, "samples");
|
||||||
|
@ -1428,6 +1428,7 @@ static int l_lovrGraphicsNewFont(lua_State* L) {
|
||||||
lovrRelease(blob, lovrBlobDestroy);
|
lovrRelease(blob, lovrBlobDestroy);
|
||||||
} else {
|
} else {
|
||||||
info.spread = luaL_optnumber(L, 2, info.spread);
|
info.spread = luaL_optnumber(L, 2, info.spread);
|
||||||
|
lovrRetain(info.rasterizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Font* font = lovrFontCreate(&info);
|
Font* font = lovrFontCreate(&info);
|
||||||
|
|
|
@ -555,12 +555,13 @@ static void luax_readvertices(lua_State* L, int index, float* vertices, uint32_t
|
||||||
*vertices++ = luax_tofloat(L, -1);
|
*vertices++ = luax_tofloat(L, -1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
} else if (innerType == LUA_TUSERDATA) {
|
} else if (innerType == LUA_TUSERDATA || innerType == LUA_TLIGHTUSERDATA) {
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
lua_rawgeti(L, index, i + 1);
|
lua_rawgeti(L, index, i + 1);
|
||||||
vec3_init(vertices, luax_checkvector(L, -1, V_VEC3, NULL));
|
float* v = luax_checkvector(L, -1, V_VEC3, NULL);
|
||||||
lua_pop(L, 1);
|
memcpy(vertices, v, 3 * sizeof(float));
|
||||||
vertices += 3;
|
vertices += 3;
|
||||||
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -644,7 +645,7 @@ static int l_lovrPassSphere(lua_State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool luax_checkendpoints(lua_State* L, int index, float transform[16]) {
|
static bool luax_checkendpoints(lua_State* L, int index, float transform[16], bool center) {
|
||||||
float *v, *u;
|
float *v, *u;
|
||||||
VectorType t1, t2;
|
VectorType t1, t2;
|
||||||
if ((v = luax_tovector(L, index + 0, &t1)) == NULL || t1 != V_VEC3) return false;
|
if ((v = luax_tovector(L, index + 0, &t1)) == NULL || t1 != V_VEC3) return false;
|
||||||
|
@ -658,7 +659,11 @@ static bool luax_checkendpoints(lua_State* L, int index, float transform[16]) {
|
||||||
vec3_normalize(direction);
|
vec3_normalize(direction);
|
||||||
quat_between(orientation, forward, direction);
|
quat_between(orientation, forward, direction);
|
||||||
mat4_identity(transform);
|
mat4_identity(transform);
|
||||||
|
if (center) {
|
||||||
mat4_translate(transform, (v[0] + u[0]) / 2.f, (v[1] + u[1]) / 2.f, (v[2] + u[2]) / 2.f);
|
mat4_translate(transform, (v[0] + u[0]) / 2.f, (v[1] + u[1]) / 2.f, (v[2] + u[2]) / 2.f);
|
||||||
|
} else {
|
||||||
|
mat4_translate(transform, v[0], v[1], v[2]);
|
||||||
|
}
|
||||||
mat4_rotateQuat(transform, orientation);
|
mat4_rotateQuat(transform, orientation);
|
||||||
mat4_scale(transform, radius, radius, length);
|
mat4_scale(transform, radius, radius, length);
|
||||||
return true;
|
return true;
|
||||||
|
@ -667,7 +672,7 @@ static bool luax_checkendpoints(lua_State* L, int index, float transform[16]) {
|
||||||
static int l_lovrPassCylinder(lua_State* L) {
|
static int l_lovrPassCylinder(lua_State* L) {
|
||||||
Pass* pass = luax_checktype(L, 1, Pass);
|
Pass* pass = luax_checktype(L, 1, Pass);
|
||||||
float transform[16];
|
float transform[16];
|
||||||
int index = luax_checkendpoints(L, 2, transform) ? 5 : luax_readmat4(L, 2, transform, -2);
|
int index = luax_checkendpoints(L, 2, transform, true) ? 5 : luax_readmat4(L, 2, transform, -2);
|
||||||
bool capped = lua_isnoneornil(L, index) ? true : lua_toboolean(L, index++);
|
bool capped = lua_isnoneornil(L, index) ? true : lua_toboolean(L, index++);
|
||||||
float angle1 = luax_optfloat(L, index++, 0.f);
|
float angle1 = luax_optfloat(L, index++, 0.f);
|
||||||
float angle2 = luax_optfloat(L, index++, 2.f * (float) M_PI);
|
float angle2 = luax_optfloat(L, index++, 2.f * (float) M_PI);
|
||||||
|
@ -679,7 +684,7 @@ static int l_lovrPassCylinder(lua_State* L) {
|
||||||
static int l_lovrPassCone(lua_State* L) {
|
static int l_lovrPassCone(lua_State* L) {
|
||||||
Pass* pass = luax_checktype(L, 1, Pass);
|
Pass* pass = luax_checktype(L, 1, Pass);
|
||||||
float transform[16];
|
float transform[16];
|
||||||
int index = luax_readmat4(L, 2, transform, -2);
|
int index = luax_checkendpoints(L, 2, transform, false) ? 5 : luax_readmat4(L, 2, transform, -2);
|
||||||
uint32_t segments = luax_optu32(L, index, 64);
|
uint32_t segments = luax_optu32(L, index, 64);
|
||||||
lovrPassCone(pass, transform, segments);
|
lovrPassCone(pass, transform, segments);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -688,7 +693,7 @@ static int l_lovrPassCone(lua_State* L) {
|
||||||
static int l_lovrPassCapsule(lua_State* L) {
|
static int l_lovrPassCapsule(lua_State* L) {
|
||||||
Pass* pass = luax_checktype(L, 1, Pass);
|
Pass* pass = luax_checktype(L, 1, Pass);
|
||||||
float transform[16];
|
float transform[16];
|
||||||
int index = luax_checkendpoints(L, 2, transform) ? 5 : luax_readmat4(L, 2, transform, -2);
|
int index = luax_checkendpoints(L, 2, transform, true) ? 5 : luax_readmat4(L, 2, transform, -2);
|
||||||
uint32_t segments = luax_optu32(L, index, 32);
|
uint32_t segments = luax_optu32(L, index, 32);
|
||||||
lovrPassCapsule(pass, transform, segments);
|
lovrPassCapsule(pass, transform, segments);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,20 +1,13 @@
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
#include "data/blob.h"
|
||||||
#include "event/event.h"
|
#include "event/event.h"
|
||||||
#include "thread/thread.h"
|
#include "thread/thread.h"
|
||||||
#include "thread/channel.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <lualib.h>
|
#include <lualib.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static int threadRunner(void* data) {
|
static char* threadRunner(Thread* thread, Blob* body, Variant* arguments, uint32_t argumentCount) {
|
||||||
Thread* thread = (Thread*) data;
|
|
||||||
|
|
||||||
lovrRetain(thread);
|
|
||||||
mtx_lock(&thread->lock);
|
|
||||||
thread->running = true;
|
|
||||||
mtx_unlock(&thread->lock);
|
|
||||||
|
|
||||||
lua_State* L = luaL_newstate();
|
lua_State* L = luaL_newstate();
|
||||||
luaL_openlibs(L);
|
luaL_openlibs(L);
|
||||||
luax_preload(L);
|
luax_preload(L);
|
||||||
|
@ -23,42 +16,29 @@ static int threadRunner(void* data) {
|
||||||
lua_pushcfunction(L, luax_getstack);
|
lua_pushcfunction(L, luax_getstack);
|
||||||
int errhandler = lua_gettop(L);
|
int errhandler = lua_gettop(L);
|
||||||
|
|
||||||
if (!luaL_loadbuffer(L, thread->body->data, thread->body->size, "thread")) {
|
if (!luaL_loadbuffer(L, body->data, body->size, "thread")) {
|
||||||
for (uint32_t i = 0; i < thread->argumentCount; i++) {
|
for (uint32_t i = 0; i < argumentCount; i++) {
|
||||||
luax_pushvariant(L, &thread->arguments[i]);
|
luax_pushvariant(L, &arguments[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lua_pcall(L, thread->argumentCount, 0, errhandler)) {
|
if (!lua_pcall(L, argumentCount, 0, errhandler)) {
|
||||||
mtx_lock(&thread->lock);
|
return NULL;
|
||||||
thread->running = false;
|
|
||||||
mtx_unlock(&thread->lock);
|
|
||||||
lovrRelease(thread, lovrThreadDestroy);
|
|
||||||
lua_close(L);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx_lock(&thread->lock);
|
|
||||||
|
|
||||||
// Error handling
|
// Error handling
|
||||||
size_t length;
|
size_t length;
|
||||||
const char* error = lua_tolstring(L, -1, &length);
|
const char* message = lua_tolstring(L, -1, &length);
|
||||||
|
char* error = message ? malloc(length + 1) : NULL;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
thread->error = malloc(length + 1);
|
memcpy(error, message, length + 1);
|
||||||
if (thread->error) {
|
lua_close(L);
|
||||||
memcpy(thread->error, error, length + 1);
|
return error;
|
||||||
lovrEventPush((Event) {
|
|
||||||
.type = EVENT_THREAD_ERROR,
|
|
||||||
.data.thread = { thread, thread->error }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->running = false;
|
|
||||||
mtx_unlock(&thread->lock);
|
|
||||||
lovrRelease(thread, lovrThreadDestroy);
|
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
return 1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_lovrThreadNewThread(lua_State* L) {
|
static int l_lovrThreadNewThread(lua_State* L) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
#include "thread/channel.h"
|
#include "thread/thread.h"
|
||||||
#include "event/event.h"
|
#include "event/event.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
#include "event/event.h"
|
||||||
#include "thread/thread.h"
|
#include "thread/thread.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
@ -32,7 +33,8 @@ static int l_lovrThreadGetError(lua_State* L) {
|
||||||
|
|
||||||
static int l_lovrThreadIsRunning(lua_State* L) {
|
static int l_lovrThreadIsRunning(lua_State* L) {
|
||||||
Thread* thread = luax_checktype(L, 1, Thread);
|
Thread* thread = luax_checktype(L, 1, Thread);
|
||||||
lua_pushboolean(L, thread->running);
|
bool running = lovrThreadIsRunning(thread);
|
||||||
|
lua_pushboolean(L, running);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2049,15 +2049,15 @@ bool gpu_init(gpu_config* config) {
|
||||||
enable->largePoints = supports->largePoints;
|
enable->largePoints = supports->largePoints;
|
||||||
|
|
||||||
// Optional features (currently always enabled when supported)
|
// Optional features (currently always enabled when supported)
|
||||||
config->features->textureBC = enable->textureCompressionBC = supports->textureCompressionBC;
|
config->features->textureBC = (enable->textureCompressionBC = supports->textureCompressionBC);
|
||||||
config->features->textureASTC = enable->textureCompressionASTC_LDR = supports->textureCompressionASTC_LDR;
|
config->features->textureASTC = (enable->textureCompressionASTC_LDR = supports->textureCompressionASTC_LDR);
|
||||||
config->features->wireframe = enable->fillModeNonSolid = supports->fillModeNonSolid;
|
config->features->wireframe = (enable->fillModeNonSolid = supports->fillModeNonSolid);
|
||||||
config->features->depthClamp = enable->depthClamp = supports->depthClamp;
|
config->features->depthClamp = (enable->depthClamp = supports->depthClamp);
|
||||||
config->features->indirectDrawFirstInstance = enable->drawIndirectFirstInstance = supports->drawIndirectFirstInstance;
|
config->features->indirectDrawFirstInstance = (enable->drawIndirectFirstInstance = supports->drawIndirectFirstInstance);
|
||||||
config->features->shaderTally = enable->pipelineStatisticsQuery = supports->pipelineStatisticsQuery;
|
config->features->shaderTally = (enable->pipelineStatisticsQuery = supports->pipelineStatisticsQuery);
|
||||||
config->features->float64 = enable->shaderFloat64 = supports->shaderFloat64;
|
config->features->float64 = (enable->shaderFloat64 = supports->shaderFloat64);
|
||||||
config->features->int64 = enable->shaderInt64 = supports->shaderInt64;
|
config->features->int64 = (enable->shaderInt64 = supports->shaderInt64);
|
||||||
config->features->int16 = enable->shaderInt16 = supports->shaderInt16;
|
config->features->int16 = (enable->shaderInt16 = supports->shaderInt16);
|
||||||
|
|
||||||
// Formats
|
// Formats
|
||||||
for (uint32_t i = 0; i < GPU_FORMAT_COUNT; i++) {
|
for (uint32_t i = 0; i < GPU_FORMAT_COUNT; i++) {
|
||||||
|
@ -3070,7 +3070,9 @@ static void nickname(void* handle, VkObjectType type, const char* name) {
|
||||||
static bool vcheck(VkResult result, const char* message) {
|
static bool vcheck(VkResult result, const char* message) {
|
||||||
if (result >= 0) return true;
|
if (result >= 0) return true;
|
||||||
if (!state.config.callback) return false;
|
if (!state.config.callback) return false;
|
||||||
#define CASE(x) case x: state.config.callback(state.config.userdata, "Vulkan error: " #x, true); break;
|
|
||||||
|
const char* errorCode = "";
|
||||||
|
#define CASE(x) case x: errorCode = " (" #x ")"; break;
|
||||||
switch (result) {
|
switch (result) {
|
||||||
CASE(VK_ERROR_OUT_OF_HOST_MEMORY);
|
CASE(VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||||
CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
||||||
|
@ -3088,8 +3090,21 @@ static bool vcheck(VkResult result, const char* message) {
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
#undef CASE
|
#undef CASE
|
||||||
|
|
||||||
|
char string[128];
|
||||||
|
size_t length1 = strlen(message);
|
||||||
|
size_t length2 = strlen(errorCode);
|
||||||
|
|
||||||
|
if (length1 + length2 >= sizeof(string)) {
|
||||||
state.config.callback(state.config.userdata, message, true);
|
state.config.callback(state.config.userdata, message, true);
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
memcpy(string, message, length1);
|
||||||
|
memcpy(string + length1, errorCode, length2);
|
||||||
|
string[length1 + length2] = '\0';
|
||||||
|
state.config.callback(state.config.userdata, string, true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check(bool condition, const char* message) {
|
static bool check(bool condition, const char* message) {
|
||||||
|
|
|
@ -170,6 +170,22 @@ bool os_init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void os_destroy() {
|
void os_destroy() {
|
||||||
|
// There are two ways a quit can happen, which need to be handled slightly differently:
|
||||||
|
// - If the system tells us to quit, we get an event with APP_CMD_DESTROY. In response we push a
|
||||||
|
// QUIT event to lovr.event and main will eventually exit cleanly. No other teardown necessary.
|
||||||
|
// - If the app exits manually (e.g. lovr.event.quit), then Android thinks we're still running and
|
||||||
|
// the app is still in APP_CMD_RESUME. We need to tell it to exit with ANativeActivity_Finish
|
||||||
|
// and poll for events until the app state changes, otherwise the app is left in a broken state.
|
||||||
|
if (state.app->activityState == APP_CMD_RESUME) {
|
||||||
|
state.onQuit = NULL;
|
||||||
|
state.onKeyboardEvent = NULL;
|
||||||
|
state.onTextEvent = NULL;
|
||||||
|
state.onPermissionEvent = NULL;
|
||||||
|
ANativeActivity_finish(state.app->activity);
|
||||||
|
while (!state.app->destroyRequested) {
|
||||||
|
os_poll_events();
|
||||||
|
}
|
||||||
|
}
|
||||||
memset(&state, 0, sizeof(state));
|
memset(&state, 0, sizeof(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,77 @@
|
||||||
|
#ifndef LOVR_USE_GLFW
|
||||||
|
|
||||||
|
void os_destroy() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_poll_events() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_window_open(const os_window_config* config) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_window_is_open() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_window_get_size(uint32_t* width, uint32_t* height) {
|
||||||
|
*width = *height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_window_get_fbsize(uint32_t* width, uint32_t* height) {
|
||||||
|
*width = *height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_on_quit(fn_quit* callback) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_on_focus(fn_focus* callback) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_on_resize(fn_resize* callback) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_on_key(fn_key* callback) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_on_text(fn_text* callback) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_get_mouse_position(double* x, double* y) {
|
||||||
|
*x = *y = 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_set_mouse_mode(os_mouse_mode mode) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_is_mouse_down(os_mouse_button button) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_is_key_down(os_key key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LOVR_VK
|
||||||
|
const char** os_vk_get_instance_extensions(uint32_t* count) {
|
||||||
|
return *count = 0, NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t os_vk_create_surface(void* instance, void** surface) {
|
||||||
|
return -13; // VK_ERROR_UNKNOWN
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifdef LOVR_VK
|
#ifdef LOVR_VK
|
||||||
|
@ -180,6 +254,10 @@ static int convertKey(os_key key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void os_destroy() {
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
void os_poll_events() {
|
void os_poll_events() {
|
||||||
if (glfwState.window) {
|
if (glfwState.window) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
@ -203,12 +281,7 @@ bool os_window_open(const os_window_config* config) {
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
|
||||||
glfwWindowHint(GLFW_RESIZABLE, config->resizable);
|
glfwWindowHint(GLFW_RESIZABLE, config->resizable);
|
||||||
glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
|
|
||||||
|
|
||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||||
|
@ -340,3 +413,5 @@ uint32_t os_vk_create_surface(void* instance, void** surface) {
|
||||||
OS_DLL_EXPORT GLFWwindow* os_get_glfw_window(void) {
|
OS_DLL_EXPORT GLFWwindow* os_get_glfw_window(void) {
|
||||||
return glfwState.window;
|
return glfwState.window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -14,10 +14,6 @@ bool os_init() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void os_destroy() {
|
|
||||||
glfwTerminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* os_get_name() {
|
const char* os_get_name() {
|
||||||
return "Linux";
|
return "Linux";
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,6 @@ bool os_init() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void os_destroy() {
|
|
||||||
glfwTerminate();
|
|
||||||
memset(&state, 0, sizeof(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* os_get_name() {
|
const char* os_get_name() {
|
||||||
return "macOS";
|
return "macOS";
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,10 +62,6 @@ bool os_init() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void os_destroy() {
|
|
||||||
glfwTerminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* os_get_name() {
|
const char* os_get_name() {
|
||||||
return "Windows";
|
return "Windows";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1130,30 +1130,30 @@ static Image* loadKTX2(Blob* blob) {
|
||||||
case 126: image->format = FORMAT_D32F; break;
|
case 126: image->format = FORMAT_D32F; break;
|
||||||
case 129: image->format = FORMAT_D24S8; break;
|
case 129: image->format = FORMAT_D24S8; break;
|
||||||
case 130: image->format = FORMAT_D32FS8; break;
|
case 130: image->format = FORMAT_D32FS8; break;
|
||||||
case 132: image->flags |= IMAGE_SRGB; case 131: image->format = FORMAT_BC1; break;
|
case 132: image->flags |= IMAGE_SRGB; /* fallthrough */ case 131: image->format = FORMAT_BC1; break;
|
||||||
case 136: image->flags |= IMAGE_SRGB; case 135: image->format = FORMAT_BC2; break;
|
case 136: image->flags |= IMAGE_SRGB; /* fallthrough */ case 135: image->format = FORMAT_BC2; break;
|
||||||
case 138: image->flags |= IMAGE_SRGB; case 137: image->format = FORMAT_BC3; break;
|
case 138: image->flags |= IMAGE_SRGB; /* fallthrough */ case 137: image->format = FORMAT_BC3; break;
|
||||||
case 139: image->format = FORMAT_BC4U; break;
|
case 139: image->format = FORMAT_BC4U; break;
|
||||||
case 140: image->format = FORMAT_BC4S; break;
|
case 140: image->format = FORMAT_BC4S; break;
|
||||||
case 141: image->format = FORMAT_BC5U; break;
|
case 141: image->format = FORMAT_BC5U; break;
|
||||||
case 142: image->format = FORMAT_BC5S; break;
|
case 142: image->format = FORMAT_BC5S; break;
|
||||||
case 143: image->format = FORMAT_BC6UF; break;
|
case 143: image->format = FORMAT_BC6UF; break;
|
||||||
case 144: image->format = FORMAT_BC6SF; break;
|
case 144: image->format = FORMAT_BC6SF; break;
|
||||||
case 146: image->flags |= IMAGE_SRGB; case 145: image->format = FORMAT_BC7; break;
|
case 146: image->flags |= IMAGE_SRGB; /* fallthrough */ case 145: image->format = FORMAT_BC7; break;
|
||||||
case 158: image->flags |= IMAGE_SRGB; case 157: image->format = FORMAT_ASTC_4x4; break;
|
case 158: image->flags |= IMAGE_SRGB; /* fallthrough */ case 157: image->format = FORMAT_ASTC_4x4; break;
|
||||||
case 160: image->flags |= IMAGE_SRGB; case 159: image->format = FORMAT_ASTC_5x4; break;
|
case 160: image->flags |= IMAGE_SRGB; /* fallthrough */ case 159: image->format = FORMAT_ASTC_5x4; break;
|
||||||
case 162: image->flags |= IMAGE_SRGB; case 161: image->format = FORMAT_ASTC_5x5; break;
|
case 162: image->flags |= IMAGE_SRGB; /* fallthrough */ case 161: image->format = FORMAT_ASTC_5x5; break;
|
||||||
case 164: image->flags |= IMAGE_SRGB; case 163: image->format = FORMAT_ASTC_6x5; break;
|
case 164: image->flags |= IMAGE_SRGB; /* fallthrough */ case 163: image->format = FORMAT_ASTC_6x5; break;
|
||||||
case 166: image->flags |= IMAGE_SRGB; case 165: image->format = FORMAT_ASTC_6x6; break;
|
case 166: image->flags |= IMAGE_SRGB; /* fallthrough */ case 165: image->format = FORMAT_ASTC_6x6; break;
|
||||||
case 168: image->flags |= IMAGE_SRGB; case 167: image->format = FORMAT_ASTC_8x5; break;
|
case 168: image->flags |= IMAGE_SRGB; /* fallthrough */ case 167: image->format = FORMAT_ASTC_8x5; break;
|
||||||
case 170: image->flags |= IMAGE_SRGB; case 169: image->format = FORMAT_ASTC_8x6; break;
|
case 170: image->flags |= IMAGE_SRGB; /* fallthrough */ case 169: image->format = FORMAT_ASTC_8x6; break;
|
||||||
case 172: image->flags |= IMAGE_SRGB; case 171: image->format = FORMAT_ASTC_8x8; break;
|
case 172: image->flags |= IMAGE_SRGB; /* fallthrough */ case 171: image->format = FORMAT_ASTC_8x8; break;
|
||||||
case 174: image->flags |= IMAGE_SRGB; case 173: image->format = FORMAT_ASTC_10x5; break;
|
case 174: image->flags |= IMAGE_SRGB; /* fallthrough */ case 173: image->format = FORMAT_ASTC_10x5; break;
|
||||||
case 176: image->flags |= IMAGE_SRGB; case 175: image->format = FORMAT_ASTC_10x6; break;
|
case 176: image->flags |= IMAGE_SRGB; /* fallthrough */ case 175: image->format = FORMAT_ASTC_10x6; break;
|
||||||
case 178: image->flags |= IMAGE_SRGB; case 177: image->format = FORMAT_ASTC_10x8; break;
|
case 178: image->flags |= IMAGE_SRGB; /* fallthrough */ case 177: image->format = FORMAT_ASTC_10x8; break;
|
||||||
case 180: image->flags |= IMAGE_SRGB; case 179: image->format = FORMAT_ASTC_10x10; break;
|
case 180: image->flags |= IMAGE_SRGB; /* fallthrough */ case 179: image->format = FORMAT_ASTC_10x10; break;
|
||||||
case 182: image->flags |= IMAGE_SRGB; case 181: image->format = FORMAT_ASTC_12x10; break;
|
case 182: image->flags |= IMAGE_SRGB; /* fallthrough */ case 181: image->format = FORMAT_ASTC_12x10; break;
|
||||||
case 184: image->flags |= IMAGE_SRGB; case 183: image->format = FORMAT_ASTC_12x12; break;
|
case 184: image->flags |= IMAGE_SRGB; /* fallthrough */ case 183: image->format = FORMAT_ASTC_12x12; break;
|
||||||
default: lovrThrow("KTX file uses an unsupported image format");
|
default: lovrThrow("KTX file uses an unsupported image format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,12 @@ void lovrEventPush(Event event) {
|
||||||
#ifndef LOVR_DISABLE_THREAD
|
#ifndef LOVR_DISABLE_THREAD
|
||||||
if (event.type == EVENT_THREAD_ERROR) {
|
if (event.type == EVENT_THREAD_ERROR) {
|
||||||
lovrRetain(event.data.thread.thread);
|
lovrRetain(event.data.thread.thread);
|
||||||
|
size_t length = strlen(event.data.thread.error);
|
||||||
|
char* copy = malloc(length + 1);
|
||||||
|
lovrAssert(copy, "Out of memory");
|
||||||
|
memcpy(copy, event.data.thread.error, length);
|
||||||
|
copy[length] = '\0';
|
||||||
|
event.data.thread.error = copy;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -441,42 +441,101 @@ void lovrFilesystemSetRequirePath(const char* requirePath) {
|
||||||
|
|
||||||
// Archive: dir
|
// Archive: dir
|
||||||
|
|
||||||
static bool dir_resolve(char* buffer, Archive* archive, const char* path) {
|
enum {
|
||||||
char innerBuffer[LOVR_PATH_MAX];
|
PATH_INVALID,
|
||||||
|
PATH_VIRTUAL,
|
||||||
|
PATH_PHYSICAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dir_resolve(Archive* archive, char* buffer, const char* path) {
|
||||||
|
char normalized[LOVR_PATH_MAX];
|
||||||
|
|
||||||
|
// Normalize the path
|
||||||
size_t length = strlen(path);
|
size_t length = strlen(path);
|
||||||
if (length >= sizeof(innerBuffer)) return false;
|
if (length >= sizeof(normalized)) return PATH_INVALID;
|
||||||
length = normalize(innerBuffer, path, length);
|
length = normalize(normalized, path, length);
|
||||||
path = innerBuffer;
|
|
||||||
|
|
||||||
if (archive->mountpoint) {
|
// Compare each component of normalized path and mountpoint
|
||||||
if (strncmp(path, strpool_resolve(&archive->strings, archive->mountpoint), archive->mountpointLength)) {
|
if (archive->mountpointLength > 0) {
|
||||||
return false;
|
const char* mountpoint = strpool_resolve(&archive->strings, archive->mountpoint);
|
||||||
|
size_t mountpointLength = archive->mountpointLength;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
char* slash = strchr(mountpoint, '/');
|
||||||
|
size_t sublength = slash ? slash - mountpoint : mountpointLength;
|
||||||
|
|
||||||
|
// If the path is empty but there was still stuff in the mountpoint, it's a virtual directory
|
||||||
|
if (length == 0) {
|
||||||
|
// Return child directory's name for convenience in getDirectoryItems
|
||||||
|
memcpy(buffer, mountpoint, sublength);
|
||||||
|
buffer[sublength] = '\0';
|
||||||
|
return PATH_VIRTUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for paths that don't match this component of the mountpoint
|
||||||
|
if (length < sublength || strncmp(path, mountpoint, sublength)) {
|
||||||
|
return PATH_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the path matched, make sure there's a slash after the match
|
||||||
|
if (length > sublength && path[sublength] != '/') {
|
||||||
|
return PATH_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip this component off of the path
|
||||||
|
if (length == sublength) {
|
||||||
|
path += sublength;
|
||||||
|
length -= sublength;
|
||||||
} else {
|
} else {
|
||||||
path += archive->mountpointLength;
|
path += sublength + 1;
|
||||||
length -= archive->mountpointLength;
|
length -= sublength + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip this component off of the mountpoint, if mountpoint is empty then we're done
|
||||||
|
if (mountpointLength > sublength) {
|
||||||
|
mountpoint += sublength + 1;
|
||||||
|
mountpointLength -= sublength + 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return concat(buffer, strpool_resolve(&archive->strings, archive->path), archive->pathLength, path, length);
|
// Concat archive path and normalized path (without mountpoint), return full path
|
||||||
|
if (!concat(buffer, strpool_resolve(&archive->strings, archive->path), archive->pathLength, path, length)) {
|
||||||
|
return PATH_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PATH_PHYSICAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dir_stat(Archive* archive, const char* path, FileInfo* info) {
|
static bool dir_stat(Archive* archive, const char* path, FileInfo* info) {
|
||||||
char resolved[LOVR_PATH_MAX];
|
char resolved[LOVR_PATH_MAX];
|
||||||
return dir_resolve(resolved, archive, path) && fs_stat(resolved, info);
|
switch (dir_resolve(archive, resolved, path)) {
|
||||||
|
default:
|
||||||
|
case PATH_INVALID: return false;
|
||||||
|
case PATH_VIRTUAL: return fs_stat(strpool_resolve(&archive->strings, archive->path), info);
|
||||||
|
case PATH_PHYSICAL: return fs_stat(resolved, info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dir_list(Archive* archive, const char* path, fs_list_cb callback, void* context) {
|
static void dir_list(Archive* archive, const char* path, fs_list_cb callback, void* context) {
|
||||||
char resolved[LOVR_PATH_MAX];
|
char resolved[LOVR_PATH_MAX];
|
||||||
if (dir_resolve(resolved, archive, path)) {
|
switch (dir_resolve(archive, resolved, path)) {
|
||||||
fs_list(resolved, callback, context);
|
case PATH_INVALID: return;
|
||||||
|
case PATH_VIRTUAL: callback(context, resolved); return;
|
||||||
|
case PATH_PHYSICAL: fs_list(resolved, callback, context); return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dir_read(Archive* archive, const char* path, size_t bytes, size_t* bytesRead, void** data) {
|
static bool dir_read(Archive* archive, const char* path, size_t bytes, size_t* bytesRead, void** data) {
|
||||||
char resolved[LOVR_PATH_MAX];
|
char resolved[LOVR_PATH_MAX];
|
||||||
fs_handle file;
|
if (dir_resolve(archive, resolved, path) != PATH_PHYSICAL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!dir_resolve(resolved, archive, path) || !fs_open(resolved, OPEN_READ, &file)) {
|
fs_handle file;
|
||||||
|
if (!fs_open(resolved, OPEN_READ, &file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +590,7 @@ static zip_node* zip_lookup(Archive* archive, const char* path) {
|
||||||
size_t length = strlen(path);
|
size_t length = strlen(path);
|
||||||
if (length >= sizeof(buffer)) return NULL;
|
if (length >= sizeof(buffer)) return NULL;
|
||||||
length = normalize(buffer, path, length);
|
length = normalize(buffer, path, length);
|
||||||
uint64_t hash = hash64(buffer, length);
|
uint64_t hash = length ? hash64(buffer, length) : 0;
|
||||||
uint64_t index = map_get(&archive->lookup, hash);
|
uint64_t index = map_get(&archive->lookup, hash);
|
||||||
return index == MAP_NIL ? NULL : &archive->nodes.data[index];
|
return index == MAP_NIL ? NULL : &archive->nodes.data[index];
|
||||||
}
|
}
|
||||||
|
@ -640,8 +699,11 @@ static bool zip_init(Archive* archive, const char* filename, const char* mountpo
|
||||||
}
|
}
|
||||||
|
|
||||||
mountpointLength = normalize(path, mountpoint, mountpointLength);
|
mountpointLength = normalize(path, mountpoint, mountpointLength);
|
||||||
|
|
||||||
|
if (mountpointLength > 0) {
|
||||||
path[mountpointLength++] = '/';
|
path[mountpointLength++] = '/';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Simple root normalization (only strips leading/trailing slashes, sorry)
|
// Simple root normalization (only strips leading/trailing slashes, sorry)
|
||||||
while (root && root[0] == '/') root++;
|
while (root && root[0] == '/') root++;
|
||||||
|
@ -696,7 +758,7 @@ static bool zip_init(Archive* archive, const char* filename, const char* mountpo
|
||||||
// Keep chopping off path segments, building up a tree of paths
|
// Keep chopping off path segments, building up a tree of paths
|
||||||
// We can stop early if we reach a path that has already been indexed
|
// We can stop early if we reach a path that has already been indexed
|
||||||
// Also add individual path segments to the string pool, for zip_list
|
// Also add individual path segments to the string pool, for zip_list
|
||||||
while (length != SIZE_MAX) {
|
for (;;) {
|
||||||
uint64_t hash = hash64(path, length);
|
uint64_t hash = hash64(path, length);
|
||||||
uint64_t index = map_get(&archive->lookup, hash);
|
uint64_t index = map_get(&archive->lookup, hash);
|
||||||
|
|
||||||
|
@ -720,6 +782,16 @@ static bool zip_init(Archive* archive, const char* filename, const char* mountpo
|
||||||
}
|
}
|
||||||
|
|
||||||
archive->nodes.data[index].filename = strpool_append(&archive->strings, path + length, slash - length);
|
archive->nodes.data[index].filename = strpool_append(&archive->strings, path + length, slash - length);
|
||||||
|
|
||||||
|
// Root node
|
||||||
|
if (length == 0) {
|
||||||
|
index = archive->nodes.length;
|
||||||
|
map_set(&archive->lookup, 0, index);
|
||||||
|
arr_push(&archive->nodes, node);
|
||||||
|
archive->nodes.data[index].filename = strpool_append(&archive->strings, path, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
slash = --length;
|
slash = --length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,6 +418,7 @@ static void processReadbacks(void);
|
||||||
static size_t getLayout(gpu_slot* slots, uint32_t count);
|
static size_t getLayout(gpu_slot* slots, uint32_t count);
|
||||||
static gpu_bundle* getBundle(size_t layout);
|
static gpu_bundle* getBundle(size_t layout);
|
||||||
static gpu_texture* getScratchTexture(gpu_texture_info* info);
|
static gpu_texture* getScratchTexture(gpu_texture_info* info);
|
||||||
|
static bool isDepthFormat(TextureFormat format);
|
||||||
static uint32_t measureTexture(TextureFormat format, uint32_t w, uint32_t h, uint32_t d);
|
static uint32_t measureTexture(TextureFormat format, uint32_t w, uint32_t h, uint32_t d);
|
||||||
static void checkTextureBounds(const TextureInfo* info, uint32_t offset[4], uint32_t extent[3]);
|
static void checkTextureBounds(const TextureInfo* info, uint32_t offset[4], uint32_t extent[3]);
|
||||||
static void mipmapTexture(gpu_stream* stream, Texture* texture, uint32_t base, uint32_t count);
|
static void mipmapTexture(gpu_stream* stream, Texture* texture, uint32_t base, uint32_t count);
|
||||||
|
@ -3300,6 +3301,7 @@ Pass* lovrGraphicsGetPass(PassInfo* info) {
|
||||||
for (uint32_t i = 0; i < canvas->count; i++) {
|
for (uint32_t i = 0; i < canvas->count; i++) {
|
||||||
const TextureInfo* texture = &canvas->textures[i]->info;
|
const TextureInfo* texture = &canvas->textures[i]->info;
|
||||||
bool renderable = texture->format == GPU_FORMAT_SURFACE || (state.features.formats[texture->format] & GPU_FEATURE_RENDER);
|
bool renderable = texture->format == GPU_FORMAT_SURFACE || (state.features.formats[texture->format] & GPU_FEATURE_RENDER);
|
||||||
|
lovrCheck(!isDepthFormat(texture->format), "Unable to use a depth texture as a color target");
|
||||||
lovrCheck(renderable, "This GPU does not support rendering to the texture format used by color target #%d", i + 1);
|
lovrCheck(renderable, "This GPU does not support rendering to the texture format used by color target #%d", i + 1);
|
||||||
lovrCheck(texture->usage & TEXTURE_RENDER, "Texture must be created with the 'render' flag to render to it");
|
lovrCheck(texture->usage & TEXTURE_RENDER, "Texture must be created with the 'render' flag to render to it");
|
||||||
lovrCheck(texture->width == t->width, "Render pass texture sizes must match");
|
lovrCheck(texture->width == t->width, "Render pass texture sizes must match");
|
||||||
|
@ -3313,6 +3315,7 @@ Pass* lovrGraphicsGetPass(PassInfo* info) {
|
||||||
if (depth->texture || depth->format) {
|
if (depth->texture || depth->format) {
|
||||||
TextureFormat format = depth->texture ? depth->texture->info.format : depth->format;
|
TextureFormat format = depth->texture ? depth->texture->info.format : depth->format;
|
||||||
bool renderable = state.features.formats[format] & GPU_FEATURE_RENDER;
|
bool renderable = state.features.formats[format] & GPU_FEATURE_RENDER;
|
||||||
|
lovrCheck(isDepthFormat(format), "Unable to use a color texture as a depth target");
|
||||||
lovrCheck(renderable, "This GPU does not support depth buffers with this texture format");
|
lovrCheck(renderable, "This GPU does not support depth buffers with this texture format");
|
||||||
if (depth->texture) {
|
if (depth->texture) {
|
||||||
const TextureInfo* texture = &depth->texture->info;
|
const TextureInfo* texture = &depth->texture->info;
|
||||||
|
@ -4338,6 +4341,8 @@ void lovrPassPoints(Pass* pass, uint32_t count, float** points) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void lovrPassLine(Pass* pass, uint32_t count, float** points) {
|
void lovrPassLine(Pass* pass, uint32_t count, float** points) {
|
||||||
|
lovrCheck(count >= 2, "Need at least 2 points to make a line");
|
||||||
|
|
||||||
uint16_t* indices;
|
uint16_t* indices;
|
||||||
|
|
||||||
lovrPassDraw(pass, &(Draw) {
|
lovrPassDraw(pass, &(Draw) {
|
||||||
|
@ -5661,6 +5666,10 @@ static gpu_texture* getScratchTexture(gpu_texture_info* info) {
|
||||||
return scratch->texture;
|
return scratch->texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isDepthFormat(TextureFormat format) {
|
||||||
|
return format == FORMAT_D16 || format == FORMAT_D32F || format == FORMAT_D24S8 || format == FORMAT_D32FS8;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns number of bytes of a 3D texture region of a given format
|
// Returns number of bytes of a 3D texture region of a given format
|
||||||
static uint32_t measureTexture(TextureFormat format, uint32_t w, uint32_t h, uint32_t d) {
|
static uint32_t measureTexture(TextureFormat format, uint32_t w, uint32_t h, uint32_t d) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
#include "thread/channel.h"
|
|
||||||
#include "event/event.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "lib/tinycthread/tinycthread.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
struct Channel {
|
|
||||||
uint32_t ref;
|
|
||||||
mtx_t lock;
|
|
||||||
cnd_t cond;
|
|
||||||
arr_t(Variant) messages;
|
|
||||||
size_t head;
|
|
||||||
uint64_t sent;
|
|
||||||
uint64_t received;
|
|
||||||
uint64_t hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
Channel* lovrChannelCreate(uint64_t hash) {
|
|
||||||
Channel* channel = calloc(1, sizeof(Channel));
|
|
||||||
lovrAssert(channel, "Out of memory");
|
|
||||||
channel->ref = 1;
|
|
||||||
arr_init(&channel->messages, arr_alloc);
|
|
||||||
mtx_init(&channel->lock, mtx_plain | mtx_timed);
|
|
||||||
cnd_init(&channel->cond);
|
|
||||||
channel->hash = hash;
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lovrChannelDestroy(void* ref) {
|
|
||||||
Channel* channel = ref;
|
|
||||||
lovrChannelClear(channel);
|
|
||||||
arr_free(&channel->messages);
|
|
||||||
mtx_destroy(&channel->lock);
|
|
||||||
cnd_destroy(&channel->cond);
|
|
||||||
free(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lovrChannelPush(Channel* channel, Variant* variant, double timeout, uint64_t* id) {
|
|
||||||
mtx_lock(&channel->lock);
|
|
||||||
if (channel->messages.length == 0) {
|
|
||||||
lovrRetain(channel);
|
|
||||||
}
|
|
||||||
arr_push(&channel->messages, *variant);
|
|
||||||
*id = ++channel->sent;
|
|
||||||
cnd_broadcast(&channel->cond);
|
|
||||||
|
|
||||||
if (isnan(timeout) || timeout < 0) {
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (channel->received < *id && timeout >= 0) {
|
|
||||||
if (isinf(timeout)) {
|
|
||||||
cnd_wait(&channel->cond, &channel->lock);
|
|
||||||
} else {
|
|
||||||
struct timespec start;
|
|
||||||
struct timespec until;
|
|
||||||
struct timespec stop;
|
|
||||||
timespec_get(&start, TIME_UTC);
|
|
||||||
double whole, fraction;
|
|
||||||
fraction = modf(timeout, &whole);
|
|
||||||
until.tv_sec = start.tv_sec + whole;
|
|
||||||
until.tv_nsec = start.tv_nsec + fraction * 1e9;
|
|
||||||
cnd_timedwait(&channel->cond, &channel->lock, &until);
|
|
||||||
timespec_get(&stop, TIME_UTC);
|
|
||||||
timeout -= (stop.tv_sec - start.tv_sec) + (stop.tv_nsec - start.tv_nsec) / 1e9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool read = channel->received >= *id;
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lovrChannelPop(Channel* channel, Variant* variant, double timeout) {
|
|
||||||
mtx_lock(&channel->lock);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (channel->head < channel->messages.length) {
|
|
||||||
*variant = channel->messages.data[channel->head++];
|
|
||||||
if (channel->head == channel->messages.length) {
|
|
||||||
channel->head = channel->messages.length = 0;
|
|
||||||
lovrRelease(channel, lovrChannelDestroy);
|
|
||||||
}
|
|
||||||
channel->received++;
|
|
||||||
cnd_broadcast(&channel->cond);
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return true;
|
|
||||||
} else if (isnan(timeout) || timeout < 0) {
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isinf(timeout)) {
|
|
||||||
cnd_wait(&channel->cond, &channel->lock);
|
|
||||||
} else {
|
|
||||||
struct timespec start;
|
|
||||||
struct timespec until;
|
|
||||||
struct timespec stop;
|
|
||||||
timespec_get(&start, TIME_UTC);
|
|
||||||
double whole, fraction;
|
|
||||||
fraction = modf(timeout, &whole);
|
|
||||||
until.tv_sec = start.tv_sec + whole;
|
|
||||||
until.tv_nsec = start.tv_nsec + fraction * 1e9;
|
|
||||||
cnd_timedwait(&channel->cond, &channel->lock, &until);
|
|
||||||
timespec_get(&stop, TIME_UTC);
|
|
||||||
timeout -= (stop.tv_sec - start.tv_sec) + (stop.tv_nsec - start.tv_nsec) / (double) 1e9;
|
|
||||||
}
|
|
||||||
} while (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lovrChannelPeek(Channel* channel, Variant* variant) {
|
|
||||||
mtx_lock(&channel->lock);
|
|
||||||
|
|
||||||
if (channel->head < channel->messages.length) {
|
|
||||||
*variant = channel->messages.data[channel->head];
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lovrChannelClear(Channel* channel) {
|
|
||||||
mtx_lock(&channel->lock);
|
|
||||||
for (size_t i = channel->head; i < channel->messages.length; i++) {
|
|
||||||
lovrVariantDestroy(&channel->messages.data[i]);
|
|
||||||
}
|
|
||||||
channel->received = channel->sent;
|
|
||||||
arr_clear(&channel->messages);
|
|
||||||
channel->head = 0;
|
|
||||||
cnd_broadcast(&channel->cond);
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t lovrChannelGetCount(Channel* channel) {
|
|
||||||
mtx_lock(&channel->lock);
|
|
||||||
uint64_t length = channel->messages.length - channel->head;
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lovrChannelHasRead(Channel* channel, uint64_t id) {
|
|
||||||
mtx_lock(&channel->lock);
|
|
||||||
bool received = channel->received >= id;
|
|
||||||
mtx_unlock(&channel->lock);
|
|
||||||
return received;
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
struct Variant;
|
|
||||||
|
|
||||||
typedef struct Channel Channel;
|
|
||||||
Channel* lovrChannelCreate(uint64_t hash);
|
|
||||||
void lovrChannelDestroy(void* ref);
|
|
||||||
bool lovrChannelPush(Channel* channel, struct Variant* variant, double timeout, uint64_t* id);
|
|
||||||
bool lovrChannelPop(Channel* channel, struct Variant* variant, double timeout);
|
|
||||||
bool lovrChannelPeek(Channel* channel, struct Variant* variant);
|
|
||||||
void lovrChannelClear(Channel* channel);
|
|
||||||
uint64_t lovrChannelGetCount(Channel* channel);
|
|
||||||
bool lovrChannelHasRead(Channel* channel, uint64_t id);
|
|
|
@ -1,9 +1,35 @@
|
||||||
#include "thread/thread.h"
|
#include "thread/thread.h"
|
||||||
#include "thread/channel.h"
|
#include "data/blob.h"
|
||||||
|
#include "event/event.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "lib/tinycthread/tinycthread.h"
|
||||||
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
struct Thread {
|
||||||
|
uint32_t ref;
|
||||||
|
thrd_t handle;
|
||||||
|
mtx_t lock;
|
||||||
|
ThreadFunction* function;
|
||||||
|
Blob* body;
|
||||||
|
Variant arguments[MAX_THREAD_ARGUMENTS];
|
||||||
|
uint32_t argumentCount;
|
||||||
|
char* error;
|
||||||
|
bool running;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
uint32_t ref;
|
||||||
|
mtx_t lock;
|
||||||
|
cnd_t cond;
|
||||||
|
arr_t(Variant) messages;
|
||||||
|
size_t head;
|
||||||
|
uint64_t sent;
|
||||||
|
uint64_t received;
|
||||||
|
uint64_t hash;
|
||||||
|
};
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
bool initialized;
|
bool initialized;
|
||||||
mtx_t channelLock;
|
mtx_t channelLock;
|
||||||
|
@ -47,12 +73,35 @@ Channel* lovrThreadGetChannel(const char* name) {
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread* lovrThreadCreate(int (*runner)(void*), Blob* body) {
|
// Thread
|
||||||
|
|
||||||
|
static int threadFunction(void* data) {
|
||||||
|
Thread* thread = data;
|
||||||
|
lovrRetain(thread);
|
||||||
|
|
||||||
|
char* error = thread->function(thread, thread->body, thread->arguments, thread->argumentCount);
|
||||||
|
|
||||||
|
mtx_lock(&thread->lock);
|
||||||
|
thread->running = false;
|
||||||
|
if (error) {
|
||||||
|
thread->error = error;
|
||||||
|
lovrEventPush((Event) {
|
||||||
|
.type = EVENT_THREAD_ERROR,
|
||||||
|
.data.thread = { thread, thread->error }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
mtx_unlock(&thread->lock);
|
||||||
|
|
||||||
|
lovrRelease(thread, lovrThreadDestroy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* lovrThreadCreate(ThreadFunction* function, Blob* body) {
|
||||||
Thread* thread = calloc(1, sizeof(Thread));
|
Thread* thread = calloc(1, sizeof(Thread));
|
||||||
lovrAssert(thread, "Out of memory");
|
lovrAssert(thread, "Out of memory");
|
||||||
thread->ref = 1;
|
thread->ref = 1;
|
||||||
thread->runner = runner;
|
|
||||||
thread->body = body;
|
thread->body = body;
|
||||||
|
thread->function = function;
|
||||||
mtx_init(&thread->lock, mtx_plain);
|
mtx_init(&thread->lock, mtx_plain);
|
||||||
lovrRetain(body);
|
lovrRetain(body);
|
||||||
return thread;
|
return thread;
|
||||||
|
@ -61,40 +110,178 @@ Thread* lovrThreadCreate(int (*runner)(void*), Blob* body) {
|
||||||
void lovrThreadDestroy(void* ref) {
|
void lovrThreadDestroy(void* ref) {
|
||||||
Thread* thread = ref;
|
Thread* thread = ref;
|
||||||
mtx_destroy(&thread->lock);
|
mtx_destroy(&thread->lock);
|
||||||
thrd_detach(thread->handle);
|
if (thread->handle) thrd_detach(thread->handle);
|
||||||
lovrRelease(thread->body, lovrBlobDestroy);
|
lovrRelease(thread->body, lovrBlobDestroy);
|
||||||
free(thread->error);
|
free(thread->error);
|
||||||
free(thread);
|
free(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lovrThreadStart(Thread* thread, Variant* arguments, uint32_t argumentCount) {
|
void lovrThreadStart(Thread* thread, Variant* arguments, uint32_t argumentCount) {
|
||||||
bool running = lovrThreadIsRunning(thread);
|
mtx_lock(&thread->lock);
|
||||||
|
if (thread->running) {
|
||||||
if (running) {
|
mtx_unlock(&thread->lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(thread->error);
|
free(thread->error);
|
||||||
thread->error = NULL;
|
thread->error = NULL;
|
||||||
|
|
||||||
lovrAssert(argumentCount <= MAX_THREAD_ARGUMENTS, "Too many Thread arguments (max is %d)", MAX_THREAD_ARGUMENTS);
|
lovrAssert(argumentCount <= MAX_THREAD_ARGUMENTS, "Too many Thread arguments (max is %d)", MAX_THREAD_ARGUMENTS);
|
||||||
thread->argumentCount = argumentCount;
|
|
||||||
memcpy(thread->arguments, arguments, argumentCount * sizeof(Variant));
|
memcpy(thread->arguments, arguments, argumentCount * sizeof(Variant));
|
||||||
if (thrd_create(&thread->handle, thread->runner, thread) != thrd_success) {
|
thread->argumentCount = argumentCount;
|
||||||
|
|
||||||
|
if (thrd_create(&thread->handle, threadFunction, thread) != thrd_success) {
|
||||||
|
mtx_unlock(&thread->lock);
|
||||||
lovrThrow("Could not create thread...sorry");
|
lovrThrow("Could not create thread...sorry");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread->running = true;
|
||||||
|
mtx_unlock(&thread->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lovrThreadWait(Thread* thread) {
|
void lovrThreadWait(Thread* thread) {
|
||||||
thrd_join(thread->handle, NULL);
|
if (thread->handle) thrd_join(thread->handle, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lovrThreadIsRunning(Thread* thread) {
|
bool lovrThreadIsRunning(Thread* thread) {
|
||||||
mtx_lock(&thread->lock);
|
return thread->running;
|
||||||
bool running = thread->running;
|
|
||||||
mtx_unlock(&thread->lock);
|
|
||||||
return running;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* lovrThreadGetError(Thread* thread) {
|
const char* lovrThreadGetError(Thread* thread) {
|
||||||
return thread->error;
|
return thread->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Channel
|
||||||
|
|
||||||
|
Channel* lovrChannelCreate(uint64_t hash) {
|
||||||
|
Channel* channel = calloc(1, sizeof(Channel));
|
||||||
|
lovrAssert(channel, "Out of memory");
|
||||||
|
channel->ref = 1;
|
||||||
|
arr_init(&channel->messages, arr_alloc);
|
||||||
|
mtx_init(&channel->lock, mtx_plain | mtx_timed);
|
||||||
|
cnd_init(&channel->cond);
|
||||||
|
channel->hash = hash;
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lovrChannelDestroy(void* ref) {
|
||||||
|
Channel* channel = ref;
|
||||||
|
lovrChannelClear(channel);
|
||||||
|
arr_free(&channel->messages);
|
||||||
|
mtx_destroy(&channel->lock);
|
||||||
|
cnd_destroy(&channel->cond);
|
||||||
|
free(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lovrChannelPush(Channel* channel, Variant* variant, double timeout, uint64_t* id) {
|
||||||
|
mtx_lock(&channel->lock);
|
||||||
|
if (channel->messages.length == 0) {
|
||||||
|
lovrRetain(channel);
|
||||||
|
}
|
||||||
|
arr_push(&channel->messages, *variant);
|
||||||
|
*id = ++channel->sent;
|
||||||
|
cnd_broadcast(&channel->cond);
|
||||||
|
|
||||||
|
if (isnan(timeout) || timeout < 0) {
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (channel->received < *id && timeout >= 0) {
|
||||||
|
if (isinf(timeout)) {
|
||||||
|
cnd_wait(&channel->cond, &channel->lock);
|
||||||
|
} else {
|
||||||
|
struct timespec start;
|
||||||
|
struct timespec until;
|
||||||
|
struct timespec stop;
|
||||||
|
timespec_get(&start, TIME_UTC);
|
||||||
|
double whole, fraction;
|
||||||
|
fraction = modf(timeout, &whole);
|
||||||
|
until.tv_sec = start.tv_sec + whole;
|
||||||
|
until.tv_nsec = start.tv_nsec + fraction * 1e9;
|
||||||
|
cnd_timedwait(&channel->cond, &channel->lock, &until);
|
||||||
|
timespec_get(&stop, TIME_UTC);
|
||||||
|
timeout -= (stop.tv_sec - start.tv_sec) + (stop.tv_nsec - start.tv_nsec) / 1e9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool read = channel->received >= *id;
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lovrChannelPop(Channel* channel, Variant* variant, double timeout) {
|
||||||
|
mtx_lock(&channel->lock);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (channel->head < channel->messages.length) {
|
||||||
|
*variant = channel->messages.data[channel->head++];
|
||||||
|
if (channel->head == channel->messages.length) {
|
||||||
|
channel->head = channel->messages.length = 0;
|
||||||
|
lovrRelease(channel, lovrChannelDestroy);
|
||||||
|
}
|
||||||
|
channel->received++;
|
||||||
|
cnd_broadcast(&channel->cond);
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return true;
|
||||||
|
} else if (isnan(timeout) || timeout < 0) {
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isinf(timeout)) {
|
||||||
|
cnd_wait(&channel->cond, &channel->lock);
|
||||||
|
} else {
|
||||||
|
struct timespec start;
|
||||||
|
struct timespec until;
|
||||||
|
struct timespec stop;
|
||||||
|
timespec_get(&start, TIME_UTC);
|
||||||
|
double whole, fraction;
|
||||||
|
fraction = modf(timeout, &whole);
|
||||||
|
until.tv_sec = start.tv_sec + whole;
|
||||||
|
until.tv_nsec = start.tv_nsec + fraction * 1e9;
|
||||||
|
cnd_timedwait(&channel->cond, &channel->lock, &until);
|
||||||
|
timespec_get(&stop, TIME_UTC);
|
||||||
|
timeout -= (stop.tv_sec - start.tv_sec) + (stop.tv_nsec - start.tv_nsec) / (double) 1e9;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lovrChannelPeek(Channel* channel, Variant* variant) {
|
||||||
|
mtx_lock(&channel->lock);
|
||||||
|
|
||||||
|
if (channel->head < channel->messages.length) {
|
||||||
|
*variant = channel->messages.data[channel->head];
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lovrChannelClear(Channel* channel) {
|
||||||
|
mtx_lock(&channel->lock);
|
||||||
|
for (size_t i = channel->head; i < channel->messages.length; i++) {
|
||||||
|
lovrVariantDestroy(&channel->messages.data[i]);
|
||||||
|
}
|
||||||
|
channel->received = channel->sent;
|
||||||
|
arr_clear(&channel->messages);
|
||||||
|
channel->head = 0;
|
||||||
|
cnd_broadcast(&channel->cond);
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t lovrChannelGetCount(Channel* channel) {
|
||||||
|
mtx_lock(&channel->lock);
|
||||||
|
uint64_t length = channel->messages.length - channel->head;
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lovrChannelHasRead(Channel* channel, uint64_t id) {
|
||||||
|
mtx_lock(&channel->lock);
|
||||||
|
bool received = channel->received >= id;
|
||||||
|
mtx_unlock(&channel->lock);
|
||||||
|
return received;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
#include "data/blob.h"
|
|
||||||
#include "event/event.h"
|
|
||||||
#include "lib/tinycthread/tinycthread.h"
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -11,28 +8,34 @@
|
||||||
|
|
||||||
#define MAX_THREAD_ARGUMENTS 4
|
#define MAX_THREAD_ARGUMENTS 4
|
||||||
|
|
||||||
struct Channel;
|
struct Blob;
|
||||||
|
struct Variant;
|
||||||
|
|
||||||
typedef struct Thread {
|
typedef struct Thread Thread;
|
||||||
uint32_t ref;
|
typedef struct Channel Channel;
|
||||||
thrd_t handle;
|
|
||||||
mtx_t lock;
|
|
||||||
Blob* body;
|
|
||||||
Variant arguments[MAX_THREAD_ARGUMENTS];
|
|
||||||
uint32_t argumentCount;
|
|
||||||
int (*runner)(void*);
|
|
||||||
char* error;
|
|
||||||
bool running;
|
|
||||||
} Thread;
|
|
||||||
|
|
||||||
bool lovrThreadModuleInit(void);
|
bool lovrThreadModuleInit(void);
|
||||||
void lovrThreadModuleDestroy(void);
|
void lovrThreadModuleDestroy(void);
|
||||||
struct Channel* lovrThreadGetChannel(const char* name);
|
struct Channel* lovrThreadGetChannel(const char* name);
|
||||||
void lovrThreadRemoveChannel(uint64_t hash);
|
|
||||||
|
|
||||||
Thread* lovrThreadCreate(int (*runner)(void*), Blob* body);
|
// Thread
|
||||||
|
|
||||||
|
typedef char* ThreadFunction(Thread* thread, struct Blob* body, struct Variant* arguments, uint32_t argumentCount);
|
||||||
|
|
||||||
|
Thread* lovrThreadCreate(ThreadFunction* function, struct Blob* body);
|
||||||
void lovrThreadDestroy(void* ref);
|
void lovrThreadDestroy(void* ref);
|
||||||
void lovrThreadStart(Thread* thread, Variant* arguments, uint32_t argumentCount);
|
void lovrThreadStart(Thread* thread, struct Variant* arguments, uint32_t argumentCount);
|
||||||
void lovrThreadWait(Thread* thread);
|
void lovrThreadWait(Thread* thread);
|
||||||
const char* lovrThreadGetError(Thread* thread);
|
|
||||||
bool lovrThreadIsRunning(Thread* thread);
|
bool lovrThreadIsRunning(Thread* thread);
|
||||||
|
const char* lovrThreadGetError(Thread* thread);
|
||||||
|
|
||||||
|
// Channel
|
||||||
|
|
||||||
|
Channel* lovrChannelCreate(uint64_t hash);
|
||||||
|
void lovrChannelDestroy(void* ref);
|
||||||
|
bool lovrChannelPush(Channel* channel, struct Variant* variant, double timeout, uint64_t* id);
|
||||||
|
bool lovrChannelPop(Channel* channel, struct Variant* variant, double timeout);
|
||||||
|
bool lovrChannelPeek(Channel* channel, struct Variant* variant);
|
||||||
|
void lovrChannelClear(Channel* channel);
|
||||||
|
uint64_t lovrChannelGetCount(Channel* channel);
|
||||||
|
bool lovrChannelHasRead(Channel* channel, uint64_t id);
|
||||||
|
|
|
@ -67,7 +67,7 @@ typedef void* arr_allocator(void* data, size_t size);
|
||||||
#define arr_free(a) if ((a)->data) (a)->alloc((a)->data, 0)
|
#define arr_free(a) if ((a)->data) (a)->alloc((a)->data, 0)
|
||||||
#define arr_reserve(a, n) _arr_reserve((void**) &((a)->data), n, &(a)->capacity, sizeof(*(a)->data), (a)->alloc)
|
#define arr_reserve(a, n) _arr_reserve((void**) &((a)->data), n, &(a)->capacity, sizeof(*(a)->data), (a)->alloc)
|
||||||
#define arr_expand(a, n) arr_reserve(a, (a)->length + n)
|
#define arr_expand(a, n) arr_reserve(a, (a)->length + n)
|
||||||
#define arr_push(a, x) arr_reserve(a, (a)->length + 1), (a)->data[(a)->length++] = x
|
#define arr_push(a, x) arr_reserve(a, (a)->length + 1), (a)->data[(a)->length] = x, (a)->length++
|
||||||
#define arr_pop(a) (a)->data[--(a)->length]
|
#define arr_pop(a) (a)->data[--(a)->length]
|
||||||
#define arr_append(a, p, n) arr_reserve(a, (a)->length + n), memcpy((a)->data + (a)->length, p, n * sizeof(*(p))), (a)->length += n
|
#define arr_append(a, p, n) arr_reserve(a, (a)->length + n), memcpy((a)->data + (a)->length, p, n * sizeof(*(p))), (a)->length += n
|
||||||
#define arr_splice(a, i, n) memmove((a)->data + (i), (a)->data + ((i) + n), ((a)->length - (i) - (n)) * sizeof(*(a)->data)), (a)->length -= n
|
#define arr_splice(a, i, n) memmove((a)->data + (i), (a)->data + ((i) + n), ((a)->length - (i) - (n)) * sizeof(*(a)->data)), (a)->length -= n
|
||||||
|
|
Loading…
Reference in a new issue