Compare commits

...

15 Commits

Author SHA1 Message Date
bjorn dd4fa5c382 Synchronize Material textures; 2022-08-06 00:50:25 -07:00
bjorn 6011e61d52 Fix lovr.headset.newModel and lovr.headset.animate; 2022-08-06 00:29:46 -07:00
bjorn dd40e6c829 Fix errhand; 2022-08-06 00:29:40 -07:00
bjorn b60a9b7b40 Update glslang; 2022-08-05 23:41:48 -07:00
bjorn ed59eeb11c gpu: don't use VkPipelineCacheHeaderVersionOne;
Doesn't exist in NDK...
2022-08-05 23:41:25 -07:00
bjorn ff0d397146 tup: fix empty android package; 2022-08-05 22:20:47 -07:00
bjorn 73f61b35b3 Fix background color space; 2022-08-05 22:14:09 -07:00
bjorn 2a3215fa05 Make indirect draws a :mesh variant; 2022-08-05 22:11:43 -07:00
bjorn fcbeccdfb1 Fix background colors; 2022-08-05 21:05:02 -07:00
bjorn cb59f64a7b Adjust desktop fov; 2022-08-05 19:18:41 -07:00
bjorn 804a1e6844 Fix stereoblit shader; 2022-08-05 19:18:29 -07:00
bjorn a23f0351cc WIP OpenXR layout transitions; 2022-08-05 18:36:51 -07:00
bjorn 7ceefcf4c2 Fix mat4_getFov; 2022-08-04 21:55:22 -07:00
bjorn 019814e2c1 Change the way modules are destroyed;
They are now destroyed explicitly after tearing down the Lua state
instead of relying on finalizers.  It's definitely annoying to make it
coordinated in a centralized way like this instead of being distributed,
but there's not really any reliabel way to ensure that graphics objects
are destroyed before the graphics module/device is destroyed, which is a
problem.
2022-08-04 21:44:34 -07:00
bjorn aa4fce2842 Rename luax_atexit internals; 2022-08-04 21:33:37 -07:00
28 changed files with 364 additions and 164 deletions

View File

@ -497,7 +497,7 @@ if target == 'android' then
apk = 'bin/lovr.apk'
manifest = config.android.manifest or ('etc/AndroidManifest_%s.xml'):format(config.android.flavor)
package = #config.android.package > 0 and ('--rename-manifest-package ' .. config.android.package) or ''
package = config.android.package and #config.android.package > 0 and ('--rename-manifest-package ' .. config.android.package) or ''
project = config.android.project and #config.android.project > 0 and ('-A ' .. config.android.project) or ''
version = config.android.version

2
deps/glslang vendored

@ -1 +1 @@
Subproject commit 3c132259fa0a151e60a558e81a5bf27b10e167e5
Subproject commit de4daf201a2acd83ad523b4c6148e4d8e5b7dab4

View File

@ -163,7 +163,6 @@ function lovr.errhand(message, traceback)
print('Error:\n' .. message)
if not lovr.graphics then return function() return 1 end end
lovr.graphics.submit()
lovr.graphics.setBackground(.11, .10, .14)
local scale = .3
@ -194,21 +193,15 @@ function lovr.errhand(message, traceback)
local passes = {}
if lovr.headset then
lovr.headset.update()
local texture = lovr.headset.getTexture()
if texture then
local pass = lovr.graphics.getPass('render', texture)
local near, far = lovr.headset.getClipDistance()
for i = 1, lovr.headset.getViewCount() do
pass:setViewPose(i, lovr.headset.getViewPose(i))
local left, right, up, down = lovr.headset.getViewAngles(i)
pass:setProjection(i, left, right, up, down, near, far)
end
local pass = lovr.headset.getPass()
if pass then
render(pass)
passes[#passes + 1] = pass
end
end
if lovr.system.isWindowOpen() then
local pass = lovr.graphics.getPass('render', 'window')
local pass = lovr.graphics.getWindowPass()
pass:reset()
local width, height = lovr.system.getWindowDimensions()
local projection = lovr.math.mat4():perspective(1.0, width / height, .1, 100)
pass:setProjection(1, projection)
@ -216,6 +209,7 @@ function lovr.errhand(message, traceback)
passes[#passes + 1] = pass
end
lovr.graphics.submit(passes)
if lovr.headset then lovr.headset.submit() end
if lovr.math then lovr.math.drain() end
end
end

View File

@ -14,7 +14,7 @@ function lovr.load()
return
end
lovr.graphics.getWindowPass():setClear(0x20232c)
lovr.graphics.setBackground(0x20232c)
--[=[
logo = lovr.graphics.newShader([[

View File

@ -8,6 +8,6 @@ layout(set = 1, binding = 1) uniform texture2DArray ArrayTexture;
vec4 lovrmain() {
vec2 uv = vec2(2 * UV.x, UV.y);
vec3 uvw = vec3(uv, round(uv.x));
vec3 uvw = vec3(uv, round(UV.x));
return Color * getPixel(ArrayTexture, uvw);
}

View File

@ -28,6 +28,17 @@ LOVR_EXPORT int luaopen_lovr_system(lua_State* L);
LOVR_EXPORT int luaopen_lovr_thread(lua_State* L);
LOVR_EXPORT int luaopen_lovr_timer(lua_State* L);
void lovrAudioDestroy(void);
void lovrEventDestroy(void);
void lovrFilesystemDestroy(void);
void lovrGraphicsDestroy(void);
void lovrHeadsetDestroy(void);
void lovrMathDestroy(void);
void lovrPhysicsDestroy(void);
void lovrSystemDestroy(void);
void lovrThreadModuleDestroy(void);
void lovrTimerDestroy(void);
// Object names are lightuserdata because Variants need a non-Lua string due to threads.
static int luax_meta__tostring(lua_State* L) {
lua_getfield(L, -1, "__info");
@ -60,12 +71,12 @@ static int luax_meta__gc(lua_State* L) {
return 0;
}
static int luax_module__gc(lua_State* L) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrmodules");
static int luax_runfinalizers(lua_State* L) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrfinalizers");
for (int i = luax_len(L, 2); i >= 1; i--) {
lua_rawgeti(L, 2, i);
voidFn* destructor = (voidFn*) lua_tocfunction(L, -1);
destructor();
voidFn* finalizer = (voidFn*) lua_tocfunction(L, -1);
finalizer();
lua_pop(L, 1);
}
return 0;
@ -116,6 +127,40 @@ void luax_preload(lua_State* L) {
lua_pop(L, 2);
}
// Destroys modules in a specific order
void luax_unload(lua_State* L) {
#ifndef LOVR_DISABLE_TIMER
lovrTimerDestroy();
#endif
#ifndef LOVR_DISABLE_MATH
lovrMathDestroy();
#endif
#ifndef LOVR_DISABLE_EVENT
lovrEventDestroy();
#endif
#ifndef LOVR_DISABLE_THREAD
lovrThreadModuleDestroy();
#endif
#ifndef LOVR_DISABLE_PHYSICS
lovrPhysicsDestroy();
#endif
#ifndef LOVR_DISABLE_AUDIO
lovrAudioDestroy();
#endif
#ifndef LOVR_DISABLE_HEADSET
lovrHeadsetDestroy();
#endif
#ifndef LOVR_DISABLE_GRAPHICS
lovrGraphicsDestroy();
#endif
#ifndef LOVR_DISABLE_SYSTEM
lovrSystemDestroy();
#endif
#ifndef LOVR_DISABLE_FILESYSTEM
lovrFilesystemDestroy();
#endif
}
void _luax_registertype(lua_State* L, const char* name, const luaL_Reg* functions, destructorFn* destructor) {
// Push metatable
@ -362,8 +407,8 @@ void luax_setmainthread(lua_State *L) {
#endif
}
void luax_atexit(lua_State* L, voidFn* destructor) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrmodules");
void luax_atexit(lua_State* L, voidFn* finalizer) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrfinalizers");
if (lua_isnil(L, -1)) {
lua_newtable(L);
@ -372,18 +417,18 @@ void luax_atexit(lua_State* L, voidFn* destructor) {
// Userdata sentinel since tables don't have __gc (yet)
lua_newuserdata(L, sizeof(void*));
lua_createtable(L, 0, 1);
lua_pushcfunction(L, luax_module__gc);
lua_pushcfunction(L, luax_runfinalizers);
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "");
// Write to the registry
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrmodules");
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrfinalizers");
}
int length = luax_len(L, -1);
lua_pushcfunction(L, (lua_CFunction) destructor);
lua_pushcfunction(L, (lua_CFunction) finalizer);
lua_rawseti(L, -2, length + 1);
lua_pop(L, 1);
}

View File

@ -111,6 +111,7 @@ typedef struct {
#define luax_clearerror(L) lua_pushnil(L), luax_seterror(L)
void luax_preload(struct lua_State* L);
void luax_unload(struct lua_State* L);
void _luax_registertype(struct lua_State* L, const char* name, const struct luaL_Reg* functions, void (*destructor)(void*));
void* _luax_totype(struct lua_State* L, int index, uint64_t hash);
void* _luax_checktype(struct lua_State* L, int index, uint64_t hash, const char* debug);
@ -126,7 +127,7 @@ int luax_getstack(struct lua_State* L);
void luax_pushconf(struct lua_State* L);
int luax_setconf(struct lua_State* L);
void luax_setmainthread(struct lua_State* L);
void luax_atexit(struct lua_State* L, void (*destructor)(void));
void luax_atexit(struct lua_State* L, void (*finalizer)(void));
uint32_t _luax_checku32(struct lua_State* L, int index);
uint32_t _luax_optu32(struct lua_State* L, int index, uint32_t fallback);
void luax_readcolor(struct lua_State* L, int index, float color[4]);

View File

@ -331,7 +331,6 @@ int luaopen_lovr_audio(lua_State* L) {
lua_pop(L, 1);
if (lovrAudioInit(spatializer, sampleRate)) {
luax_atexit(L, lovrAudioDestroy);
if (start) {
lovrAudioSetDevice(AUDIO_PLAYBACK, NULL, 0, NULL, AUDIO_SHARED);
lovrAudioStart(AUDIO_PLAYBACK);

View File

@ -214,9 +214,6 @@ int luaopen_lovr_event(lua_State* L) {
lua_pushcfunction(L, nextEvent);
pollRef = luaL_ref(L, LUA_REGISTRYINDEX);
if (lovrEventInit()) {
luax_atexit(L, lovrEventDestroy);
}
lovrEventInit();
return 1;
}

View File

@ -516,9 +516,7 @@ int luaopen_lovr_filesystem(lua_State* L) {
}
lua_pop(L, 1);
if (lovrFilesystemInit(archive)) {
luax_atexit(L, lovrFilesystemDestroy);
}
lovrFilesystemInit(archive);
lua_newtable(L);
luax_register(L, lovrFilesystem);

View File

@ -658,17 +658,10 @@ static int l_lovrGraphicsInit(lua_State* L) {
if (shaderCache) {
config.cacheData = luax_readfile(".lovrshadercache", &config.cacheSize);
luax_atexit(L, luax_writeshadercache);
}
if (lovrGraphicsInit(&config)) {
luax_atexit(L, lovrGraphicsDestroy);
// Finalizers run in the opposite order they were added, so this has to go last
if (shaderCache) {
luax_atexit(L, luax_writeshadercache);
}
}
lovrGraphicsInit(&config);
return 0;
}
@ -812,6 +805,23 @@ static int l_lovrGraphicsIsFormatSupported(lua_State* L) {
return 1;
}
static int l_lovrGraphicsGetBackground(lua_State* L) {
float color[4];
lovrGraphicsGetBackground(color);
lua_pushnumber(L, color[0]);
lua_pushnumber(L, color[1]);
lua_pushnumber(L, color[2]);
lua_pushnumber(L, color[3]);
return 4;
}
static int l_lovrGraphicsSetBackground(lua_State* L) {
float color[4];
luax_readcolor(L, 1, color);
lovrGraphicsSetBackground(color);
return 0;
}
static int l_lovrGraphicsGetWindowPass(lua_State* L) {
Pass* pass = lovrGraphicsGetWindowPass();
luax_pushtype(L, Pass, pass);
@ -1488,6 +1498,8 @@ static const luaL_Reg lovrGraphics[] = {
{ "getFeatures", l_lovrGraphicsGetFeatures },
{ "getLimits", l_lovrGraphicsGetLimits },
{ "isFormatSupported", l_lovrGraphicsIsFormatSupported },
{ "getBackground", l_lovrGraphicsGetBackground },
{ "setBackground", l_lovrGraphicsSetBackground },
{ "getWindowPass", l_lovrGraphicsGetWindowPass },
{ "getDefaultFont", l_lovrGraphicsGetDefaultFont },
{ "getBuffer", l_lovrGraphicsGetBuffer },

View File

@ -823,24 +823,20 @@ static int l_lovrPassMesh(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Buffer* vertices = !lua_toboolean(L, 2) ? NULL : luax_checktype(L, 2, Buffer);
Buffer* indices = luax_totype(L, 3, Buffer);
float transform[16];
int index = luax_readmat4(L, indices ? 4 : 3, transform, 1);
uint32_t start = luax_optu32(L, index++, 1) - 1;
uint32_t count = luax_optu32(L, index++, ~0u);
uint32_t instances = luax_optu32(L, index, 1);
lovrPassMesh(pass, vertices, indices, transform, start, count, instances);
return 0;
}
static int l_lovrPassMultimesh(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Buffer* vertices = !lua_toboolean(L, 2) ? NULL : luax_totype(L, 2, Buffer);
Buffer* indices = luax_totype(L, 3, Buffer);
Buffer* draws = luax_checktype(L, 4, Buffer);
uint32_t count = luax_optu32(L, 5, 1);
uint32_t offset = luax_optu32(L, 6, 0);
uint32_t stride = luax_optu32(L, 7, 0);
lovrPassMultimesh(pass, vertices, indices, draws, count, offset, stride);
Buffer* indirect = luax_totype(L, 4, Buffer);
if (indirect) {
uint32_t count = luax_optu32(L, 5, 1);
uint32_t offset = luax_optu32(L, 6, 0);
uint32_t stride = luax_optu32(L, 7, 0);
lovrPassMeshIndirect(pass, vertices, indices, indirect, count, offset, stride);
} else {
float transform[16];
int index = luax_readmat4(L, indices ? 4 : 3, transform, 1);
uint32_t start = luax_optu32(L, index++, 1) - 1;
uint32_t count = luax_optu32(L, index++, ~0u);
uint32_t instances = luax_optu32(L, index, 1);
lovrPassMesh(pass, vertices, indices, transform, start, count, instances);
}
return 0;
}
@ -1164,7 +1160,6 @@ const luaL_Reg lovrPass[] = {
{ "monkey", l_lovrPassMonkey },
{ "draw", l_lovrPassDraw },
{ "mesh", l_lovrPassMesh },
{ "multimesh", l_lovrPassMultimesh },
{ "compute", l_lovrPassCompute },

View File

@ -488,12 +488,37 @@ static int l_lovrHeadsetVibrate(lua_State* L) {
}
static int l_lovrHeadsetNewModel(lua_State* L) {
lua_pushnil(L); // TODO
return 1;
Device device = luax_optdevice(L, 1);
bool animated = false;
if (lua_istable(L, 2)) {
lua_getfield(L, 2, "animated");
animated = lua_toboolean(L, -1);
lua_pop(L, 1);
}
ModelData* modelData = lovrHeadsetInterface->newModelData(device, animated);
if (modelData) {
ModelInfo info = { .data = modelData, .mipmaps = true };
Model* model = lovrModelCreate(&info);
luax_pushtype(L, Model, model);
lovrRelease(modelData, lovrModelDataDestroy);
lovrRelease(model, lovrModelDestroy);
return 1;
}
return 0;
}
static int l_lovrHeadsetAnimate(lua_State* L) {
lua_pushboolean(L, false); // TODO
Device device = luax_optdevice(L, 1);
Model* model = luax_checktype(L, 2, Model);
if (lovrHeadsetInterface->animate(device, model)) {
lua_pushboolean(L, true);
return 1;
}
lua_pushboolean(L, false);
return 1;
}
@ -672,7 +697,6 @@ int luaopen_lovr_headset(lua_State* L) {
}
lua_pop(L, 1);
luax_atexit(L, lovrHeadsetDestroy);
lovrHeadsetInit(&config);
return 1;
}

View File

@ -389,9 +389,7 @@ int luaopen_lovr_math(lua_State* L) {
lua_pop(L, 1);
// Module
if (lovrMathInit()) {
luax_atexit(L, lovrMathDestroy);
}
lovrMathInit();
// Each Lua state gets its own thread-local Pool
pool = lovrPoolCreate();

View File

@ -169,8 +169,6 @@ int luaopen_lovr_physics(lua_State* L) {
luax_registertype(L, CapsuleShape);
luax_registertype(L, CylinderShape);
luax_registertype(L, MeshShape);
if (lovrPhysicsInit()) {
luax_atexit(L, lovrPhysicsDestroy);
}
lovrPhysicsInit();
return 1;
}

View File

@ -205,8 +205,6 @@ static const luaL_Reg lovrSystem[] = {
int luaopen_lovr_system(lua_State* L) {
lua_newtable(L);
luax_register(L, lovrSystem);
if (lovrSystemInit()) {
luax_atexit(L, lovrSystemDestroy);
}
lovrSystemInit();
return 1;
}

View File

@ -110,8 +110,6 @@ int luaopen_lovr_thread(lua_State* L) {
luax_register(L, lovrThreadModule);
luax_registertype(L, Thread);
luax_registertype(L, Channel);
if (lovrThreadModuleInit()) {
luax_atexit(L, lovrThreadModuleDestroy);
}
lovrThreadModuleInit();
return 1;
}

View File

@ -47,8 +47,7 @@ static const luaL_Reg lovrTimer[] = {
int luaopen_lovr_timer(lua_State* L) {
lua_newtable(L);
luax_register(L, lovrTimer);
if (lovrTimerInit()) {
luax_atexit(L, lovrTimerDestroy);
}
lovrTimerInit();
luax_atexit(L, lovrTimerDestroy);
return 1;
}

View File

@ -143,6 +143,8 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info);
bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info);
void gpu_texture_destroy(gpu_texture* texture);
gpu_texture* gpu_surface_acquire(void);
void gpu_xr_acquire(gpu_stream* stream, gpu_texture* texture);
void gpu_xr_release(gpu_stream* stream, gpu_texture* texture);
// Sampler

View File

@ -706,6 +706,56 @@ gpu_texture* gpu_surface_acquire(void) {
return &state.surfaceTextures[state.currentSurfaceTexture];
}
void gpu_xr_acquire(gpu_stream* stream, gpu_texture* texture) {
if (texture->layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { // TODO depth
return;
}
VkImageMemoryBarrier transition = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = texture->layout,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
};
VkPipelineStageFlags prev = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags next = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
vkCmdPipelineBarrier(stream->commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
}
void gpu_xr_release(gpu_stream* stream, gpu_texture* texture) {
if (texture->layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { // TODO depth
return;
}
VkImageMemoryBarrier transition = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
.dstAccessMask = 0,
.oldLayout = texture->layout,
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
};
VkPipelineStageFlags prev = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkPipelineStageFlags next = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
vkCmdPipelineBarrier(stream->commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
}
// Sampler
bool gpu_sampler_init(gpu_sampler* sampler, gpu_sampler_info* info) {
@ -2249,9 +2299,12 @@ bool gpu_init(gpu_config* config) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO
};
if (config->vk.cacheSize >= sizeof(VkPipelineCacheHeaderVersionOne)) {
VkPipelineCacheHeaderVersionOne* header = config->vk.cacheData;
if (header->headerSize == sizeof(*header) && header->headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_ONE) {
// Not using VkPipelineCacheHeaderVersionOne since it's missing from Android headers
if (config->vk.cacheSize >= 16 + VK_UUID_SIZE) {
uint32_t headerSize, headerVersion;
memcpy(&headerSize, config->vk.cacheData, 4);
memcpy(&headerVersion, (char*) config->vk.cacheData + 4, 4);
if (headerSize == 16 + VK_UUID_SIZE && headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_ONE) {
cacheInfo.initialDataSize = config->vk.cacheSize;
cacheInfo.pInitialData = config->vk.cacheData;
}

View File

@ -651,8 +651,8 @@ MAF void mat4_getFov(mat4 m, float* left, float* right, float* up, float* down)
float v[4][4] = {
{ 1.f, 0.f, 0.f, 1.f },
{ -1.f, 0.f, 0.f, 1.f },
{ 0.f, -1.f, 0.f, 1.f },
{ 0.f, 1.f, 0.f, 1.f }
{ 0.f, 1.f, 0.f, 1.f },
{ 0.f, -1.f, 0.f, 1.f }
};
float transpose[16];

View File

@ -132,6 +132,7 @@ int main(int argc, char** argv) {
memset(&cookie.value, 0, sizeof(cookie.value));
}
lua_close(L);
luax_unload(L);
} while (restart);
os_destroy();
@ -147,6 +148,7 @@ void lovrDestroy(void* arg) {
lua_State* L = context->L;
emscripten_cancel_main_loop();
lua_close(L);
luax_unload(L);
os_destroy();
}
}
@ -165,6 +167,7 @@ static void emscriptenLoop(void* arg) {
}
lua_close(context->L);
luax_unload(L);
emscripten_cancel_main_loop();
if (restart) {

View File

@ -29,65 +29,9 @@ ModelData* lovrModelDataCreate(Blob* source, ModelDataIO* io) {
}
}
// Precomputed properties and validation
lovrModelDataFinalize(model);
for (uint32_t i = 0; i < model->primitiveCount; i++) {
model->primitives[i].skin = 0xaaaaaaaa;
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
ModelNode* node = &model->nodes[i];
for (uint32_t j = 0, index = node->primitiveIndex; j < node->primitiveCount; j++, index++) {
if (model->primitives[index].skin != 0xaaaaaaaa) {
lovrCheck(model->primitives[index].skin == node->skin, "Model has a mesh used with multiple skins, which is not supported");
} else {
model->primitives[index].skin = node->skin;
}
}
}
model->indexType = U16;
for (uint32_t i = 0; i < model->primitiveCount; i++) {
ModelPrimitive* primitive = &model->primitives[i];
uint32_t vertexCount = primitive->attributes[ATTR_POSITION]->count;
if (primitive->skin != ~0u) {
model->skins[primitive->skin].vertexCount += vertexCount;
model->skinnedVertexCount += vertexCount;
}
model->vertexCount += vertexCount;
model->indexCount += primitive->indices ? primitive->indices->count : 0;
if (primitive->indices) {
if (primitive->indices->type == U32) {
primitive->indices->stride = 4;
model->indexType = U32;
} else {
primitive->indices->stride = 2;
}
}
for (uint32_t i = 0; i < MAX_DEFAULT_ATTRIBUTES; i++) {
ModelAttribute* attribute = primitive->attributes[i];
if (attribute) {
attribute->stride = model->buffers[attribute->buffer].stride;
if (attribute->stride == 0) {
attribute->stride = typeSizes[attribute->type] * attribute->components;
}
}
}
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
model->nodes[i].parent = ~0u;
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
ModelNode* node = &model->nodes[i];
for (uint32_t j = 0; j < node->childCount; j++) {
model->nodes[node->children[j]].parent = i;
}
}
return model;
return model;
}
@ -151,6 +95,66 @@ void lovrModelDataAllocate(ModelData* model) {
map_init(&model->nodeMap, model->nodeCount);
}
void lovrModelDataFinalize(ModelData* model) {
for (uint32_t i = 0; i < model->primitiveCount; i++) {
model->primitives[i].skin = 0xaaaaaaaa;
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
ModelNode* node = &model->nodes[i];
for (uint32_t j = 0, index = node->primitiveIndex; j < node->primitiveCount; j++, index++) {
if (model->primitives[index].skin != 0xaaaaaaaa) {
lovrCheck(model->primitives[index].skin == node->skin, "Model has a mesh used with multiple skins, which is not supported");
} else {
model->primitives[index].skin = node->skin;
}
}
}
model->indexType = U16;
for (uint32_t i = 0; i < model->primitiveCount; i++) {
ModelPrimitive* primitive = &model->primitives[i];
uint32_t vertexCount = primitive->attributes[ATTR_POSITION]->count;
if (primitive->skin != ~0u) {
model->skins[primitive->skin].vertexCount += vertexCount;
model->skinnedVertexCount += vertexCount;
}
model->vertexCount += vertexCount;
model->indexCount += primitive->indices ? primitive->indices->count : 0;
if (primitive->indices) {
if (primitive->indices->type == U32) {
primitive->indices->stride = 4;
model->indexType = U32;
} else {
primitive->indices->stride = 2;
}
}
for (uint32_t i = 0; i < MAX_DEFAULT_ATTRIBUTES; i++) {
ModelAttribute* attribute = primitive->attributes[i];
if (attribute) {
attribute->stride = model->buffers[attribute->buffer].stride;
if (attribute->stride == 0) {
attribute->stride = typeSizes[attribute->type] * attribute->components;
}
}
}
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
model->nodes[i].parent = ~0u;
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
ModelNode* node = &model->nodes[i];
for (uint32_t j = 0; j < node->childCount; j++) {
model->nodes[node->children[j]].parent = i;
}
}
}
void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char* dst, AttributeType type, uint32_t components, bool normalized, uint32_t count, size_t stride, uint8_t clear) {
char* src = attribute ? data->buffers[attribute->buffer].data + attribute->offset : NULL;
size_t size = components * typeSizes[type];
@ -195,6 +199,12 @@ void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char
((uint8_t*) dst)[j] = (uint8_t) ((uint16_t*) src)[j];
}
}
} else if (attribute->type == I16 && !attribute->normalized && !normalized) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
for (uint32_t j = 0; j < components; j++) {
((uint8_t*) dst)[j] = (uint8_t) ((int16_t*) src)[j];
}
}
} else if (attribute->type == F32 && normalized) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
for (uint32_t j = 0; j < components; j++) {

View File

@ -215,6 +215,7 @@ ModelData* lovrModelDataInitObj(ModelData* model, struct Blob* blob, ModelDataIO
ModelData* lovrModelDataInitStl(ModelData* model, struct Blob* blob, ModelDataIO* io);
void lovrModelDataDestroy(void* ref);
void lovrModelDataAllocate(ModelData* model);
void lovrModelDataFinalize(ModelData* model);
void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char* dst, AttributeType type, uint32_t components, bool normalized, uint32_t count, size_t stride, uint8_t clear);
void lovrModelDataGetBoundingBox(ModelData* data, float box[6]);
void lovrModelDataGetBoundingSphere(ModelData* data, float sphere[4]);

View File

@ -62,6 +62,7 @@ struct Buffer {
struct Texture {
uint32_t ref;
uint32_t xrTick;
gpu_texture* gpu;
gpu_texture* renderView;
Material* material;
@ -127,6 +128,7 @@ struct Material {
uint16_t block;
gpu_bundle* bundle;
MaterialInfo info;
bool hasWritableTexture;
};
typedef struct {
@ -361,6 +363,7 @@ static struct {
gpu_device_info device;
gpu_features features;
gpu_limits limits;
float background[4];
Texture* window;
Pass* windowPass;
Font* defaultFont;
@ -401,6 +404,7 @@ static void mipmapTexture(gpu_stream* stream, Texture* texture, uint32_t base, u
static ShaderResource* findShaderResource(Shader* shader, const char* name, size_t length, uint32_t slot);
static void trackBuffer(Pass* pass, Buffer* buffer, gpu_phase phase, gpu_cache cache);
static void trackTexture(Pass* pass, Texture* texture, gpu_phase phase, gpu_cache cache);
static void trackMaterial(Pass* pass, Material* material, gpu_phase phase, gpu_cache cache);
static void updateModelTransforms(Model* model, uint32_t nodeIndex, float* parent);
static void checkShaderFeatures(uint32_t* features, uint32_t count);
static void onMessage(void* context, const char* message, bool severe);
@ -753,6 +757,20 @@ void lovrGraphicsGetShaderCache(void* data, size_t* size) {
gpu_pipeline_get_cache(data, size);
}
void lovrGraphicsGetBackground(float background[4]) {
background[0] = lovrMathLinearToGamma(state.background[0]);
background[1] = lovrMathLinearToGamma(state.background[1]);
background[2] = lovrMathLinearToGamma(state.background[2]);
background[3] = state.background[3];
}
void lovrGraphicsSetBackground(float background[4]) {
state.background[0] = lovrMathGammaToLinear(background[0]);
state.background[1] = lovrMathGammaToLinear(background[1]);
state.background[2] = lovrMathGammaToLinear(background[2]);
state.background[3] = background[3];
}
void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
if (!state.active) {
return;
@ -872,8 +890,7 @@ void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
// finish. In between, the 'flush' cache is flushed and the 'clear' cache is cleared.
gpu_barrier* barrier = &barriers[sync->lastWriteIndex];
// Only the first write in a pass is considered, because each pass can only perform one type
// of write to a resource, and there is not currently any intra-pass synchronization.
// Only the first write in a pass is considered for a barrier (and there's no intra-pass sync)
if (sync->lastWriteIndex == i + 1) {
continue;
}
@ -939,6 +956,16 @@ void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
for (uint32_t i = 0; i < count; i++) {
for (uint32_t j = 0; j < passes[i]->access.length; j++) {
passes[i]->access.data[j].sync->lastWriteIndex = 0;
// OpenXR swapchain texture layout transitions >__>
Texture* texture = passes[i]->access.data[j].texture;
if (texture && texture->info.xr && texture->xrTick != state.tick) {
gpu_xr_acquire(streams[0], texture->gpu);
gpu_xr_release(streams[total - 1], texture->gpu);
texture->xrTick = state.tick;
}
}
}
@ -1896,6 +1923,7 @@ Material* lovrMaterialCreate(const MaterialInfo* info) {
Texture* texture = textures[i] ? textures[i] : state.defaultTexture;
lovrCheck(i == 0 || texture->info.type == TEXTURE_2D, "Material textures must be 2D");
bindings[i + 1] = (gpu_binding) { i + 1, GPU_SLOT_SAMPLED_TEXTURE, .texture = texture->gpu };
material->hasWritableTexture |= texture->info.usage != TEXTURE_SAMPLE;
}
gpu_bundle_info bundleInfo = {
@ -3399,6 +3427,10 @@ void lovrPassReset(Pass* pass) {
} else {
pass->target.color[i].texture = lovrGraphicsGetWindowTexture()->gpu;
}
// The window pass always uses the global clear color. The intent is that exposing a global
// clear color that applies to the window/headset is more friendly than messing with passes.
memcpy(pass->target.color[0].clear, state.background, 4 * sizeof(float));
}
if (canvas->mipmap) {
@ -4113,10 +4145,12 @@ static void flushBuiltins(Pass* pass, Draw* draw, Shader* shader) {
static void flushMaterial(Pass* pass, Draw* draw, Shader* shader) {
if (draw->material && draw->material != pass->pipeline->material) {
gpu_bind_bundle(pass->stream, shader->gpu, 1, draw->material->bundle, NULL, 0);
trackMaterial(pass, draw->material, GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT, GPU_CACHE_TEXTURE);
pass->materialDirty = true;
} else if (pass->materialDirty) {
Material* material = pass->pipeline->material ? pass->pipeline->material : state.defaultMaterial;
gpu_bind_bundle(pass->stream, shader->gpu, 1, material->bundle, NULL, 0);
trackMaterial(pass, material, GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT, GPU_CACHE_TEXTURE);
pass->materialDirty = false;
}
}
@ -5015,13 +5049,13 @@ void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transfor
});
}
void lovrPassMultimesh(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* draws, uint32_t count, uint32_t offset, uint32_t stride) {
void lovrPassMeshIndirect(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* draws, uint32_t count, uint32_t offset, uint32_t stride) {
lovrCheck(pass->info.type == PASS_RENDER, "This function can only be called on a render pass");
lovrCheck(offset % 4 == 0, "Multimesh draw buffer offset must be a multiple of 4");
lovrCheck(offset % 4 == 0, "Buffer offset must be a multiple of 4 when sourcing draws from a Buffer");
uint32_t commandSize = indices ? 20 : 16;
stride = stride ? stride : commandSize;
uint32_t totalSize = stride * (count - 1) + commandSize;
lovrCheck(offset + totalSize < draws->size, "Multimesh draw range exceeds size of draw buffer");
lovrCheck(offset + totalSize < draws->size, "Draw buffer range exceeds the size of the buffer");
Draw draw = (Draw) {
.mode = pass->pipeline->mode,
@ -5030,12 +5064,13 @@ void lovrPassMultimesh(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* dr
};
Shader* shader = pass->pipeline->shader;
lovrCheck(shader, "A custom Shader must be bound to draw a multimesh");
lovrCheck(shader, "A custom Shader must be bound to source draws from a Buffer");
flushPipeline(pass, &draw, shader);
flushConstants(pass, shader);
flushBindings(pass, shader);
flushBuiltins(pass, &draw, shader);
flushMaterial(pass, &draw, shader);
flushBuffers(pass, &draw);
if (indices) {
@ -5581,7 +5616,7 @@ static void trackBuffer(Pass* pass, Buffer* buffer, gpu_phase phase, gpu_cache c
}
static void trackTexture(Pass* pass, Texture* texture, gpu_phase phase, gpu_cache cache) {
if (texture == state.window) {
if (!texture || texture == state.window) {
return;
}
@ -5604,6 +5639,20 @@ static void trackTexture(Pass* pass, Texture* texture, gpu_phase phase, gpu_cach
lovrRetain(texture);
}
static void trackMaterial(Pass* pass, Material* material, gpu_phase phase, gpu_cache cache) {
if (!material->hasWritableTexture) {
return;
}
trackTexture(pass, material->info.texture, phase, cache);
trackTexture(pass, material->info.glowTexture, phase, cache);
trackTexture(pass, material->info.occlusionTexture, phase, cache);
trackTexture(pass, material->info.metalnessTexture, phase, cache);
trackTexture(pass, material->info.roughnessTexture, phase, cache);
trackTexture(pass, material->info.clearcoatTexture, phase, cache);
trackTexture(pass, material->info.normalTexture, phase, cache);
}
static void updateModelTransforms(Model* model, uint32_t nodeIndex, float* parent) {
mat4 global = model->globalTransforms + 16 * nodeIndex;
NodeTransform* local = &model->localTransforms[nodeIndex];

View File

@ -103,6 +103,9 @@ void lovrGraphicsGetLimits(GraphicsLimits* limits);
bool lovrGraphicsIsFormatSupported(uint32_t format, uint32_t features);
void lovrGraphicsGetShaderCache(void* data, size_t* size);
void lovrGraphicsGetBackground(float background[4]);
void lovrGraphicsSetBackground(float background[4]);
void lovrGraphicsSubmit(Pass** passes, uint32_t count);
void lovrGraphicsPresent(void);
void lovrGraphicsWait(void);
@ -212,6 +215,7 @@ typedef struct {
uint32_t samples;
uint32_t usage;
bool srgb;
bool xr;
uintptr_t handle;
uint32_t imageCount;
struct Image** images;
@ -644,7 +648,7 @@ void lovrPassFill(Pass* pass, Texture* texture);
void lovrPassMonkey(Pass* pass, float* transform);
void lovrPassDrawModel(Pass* pass, Model* model, float* transform, uint32_t node, bool recurse, uint32_t instances);
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances);
void lovrPassMultimesh(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* indirect, uint32_t count, uint32_t offset, uint32_t stride);
void lovrPassMeshIndirect(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* indirect, uint32_t count, uint32_t offset, uint32_t stride);
void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* indirect, uint32_t offset);

View File

@ -104,7 +104,7 @@ static bool desktop_getViewAngles(uint32_t view, float* left, float* right, floa
uint32_t width, height;
desktop_getDisplayDimensions(&width, &height);
aspect = (float) width / height;
fov = .5f;
fov = .7f;
*left = atanf(tanf(fov) * aspect);
*right = atanf(tanf(fov) * aspect);
*up = fov;

View File

@ -918,7 +918,8 @@ static void openxr_start(void) {
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_RENDER | TEXTURE_SAMPLE,
.handle = (uintptr_t) images[i].image
.handle = (uintptr_t) images[i].image,
.xr = true
});
}
@ -986,7 +987,7 @@ static void openxr_destroy(void) {
if (state.actionSet) xrDestroyActionSet(state.actionSet);
if (state.swapchain) xrDestroySwapchain(state.swapchain);
if (state.referenceSpace) xrDestroySpace(state.referenceSpace);
if (state.session) xrEndSession(state.session);
if (state.session) xrDestroySession(state.session);
if (state.instance) xrDestroyInstance(state.instance);
memset(&state, 0, sizeof(state));
}
@ -1429,7 +1430,7 @@ static ModelData* openxr_newModelData(Device device, bool animated) {
mesh.vertexBlendWeights = (XrVector4f*) (meshData + offset), offset += sizes[7];
mesh.indices = (int16_t*) (meshData + offset), offset += sizes[8];
float* inverseBindMatrices = (float*) (meshData + offset); offset += sizes[9];
lovrAssert(offset == totalSize, "Unreachable");
lovrAssert(offset == totalSize, "Unreachable!");
result = xrGetHandMeshFB(tracker, &mesh);
if (XR_FAILED(result)) {
@ -1494,7 +1495,7 @@ static ModelData* openxr_newModelData(Device device, bool animated) {
.stride = sizeof(mesh.indices[0])
};
model->attributes[0] = (ModelAttribute) { .buffer = 0, .type = F32, .components = 3 };
model->attributes[0] = (ModelAttribute) { .buffer = 0, .type = F32, .components = 3, .count = vertexCount };
model->attributes[1] = (ModelAttribute) { .buffer = 1, .type = F32, .components = 3 };
model->attributes[2] = (ModelAttribute) { .buffer = 2, .type = F32, .components = 2 };
model->attributes[3] = (ModelAttribute) { .buffer = 3, .type = I16, .components = 4 };
@ -1573,6 +1574,8 @@ static ModelData* openxr_newModelData(Device device, bool animated) {
*children++ = XR_HAND_JOINT_WRIST_EXT;
*children++ = model->jointCount;
lovrModelDataFinalize(model);
return model;
}
@ -1633,11 +1636,12 @@ static bool openxr_animate(Device device, Model* model) {
XR_HAND_JOINT_LITTLE_DISTAL_EXT
};
float scale[4] = { 1.f, 1.f, 1.f, 1.f };
// The following can be optimized a lot (ideally we would set the global transform for the nodes)
for (uint32_t i = 0; i < COUNTOF(joints); i++) {
if (jointParents[i] == ~0u) {
float position[4] = { 0.f, 0.f, 0.f };
float scale[4] = { 1.f, 1.f, 1.f, 1.f };
float orientation[4] = { 0.f, 0.f, 0.f, 1.f };
lovrModelSetNodeTransform(model, i, position, scale, orientation, 1.f);
} else {
@ -1656,8 +1660,6 @@ static bool openxr_animate(Device device, Model* model) {
quat_rotate(orientation, position);
quat_mul(orientation, orientation, &pose->orientation.x);
float scale[4] = { 1.f, 1.f, 1.f, 1.f };
lovrModelSetNodeTransform(model, i, position, scale, orientation, 1.f);
}
}
@ -1666,12 +1668,12 @@ static bool openxr_animate(Device device, Model* model) {
}
static Texture* openxr_getTexture(void) {
return state.began && state.frameState.shouldRender ? state.textures[state.textureIndex] : NULL;
}
if (!SESSION_ACTIVE(state.sessionState)) {
return NULL;
}
static Pass* openxr_getPass(void) {
if (state.began) {
return state.frameState.shouldRender ? state.pass : NULL;
return state.frameState.shouldRender ? state.textures[state.textureIndex] : NULL;
}
XrFrameBeginInfo beginfo = { .type = XR_TYPE_FRAME_BEGIN_INFO };
@ -1685,7 +1687,27 @@ static Pass* openxr_getPass(void) {
XrSwapchainImageWaitInfo waitInfo = { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, .timeout = XR_INFINITE_DURATION };
XR(xrAcquireSwapchainImage(state.swapchain, NULL, &state.textureIndex));
XR(xrWaitSwapchainImage(state.swapchain, &waitInfo));
lovrPassSetTarget(state.pass, &state.textures[state.textureIndex], NULL);
return state.textures[state.textureIndex];
}
static Pass* openxr_getPass(void) {
if (state.began) {
return state.frameState.shouldRender ? state.pass : NULL;
}
Texture* texture = openxr_getTexture();
if (!texture) {
return NULL;
}
uint8_t stencil;
float color[4][4], depth;
lovrPassGetClear(state.pass, color, &depth, &stencil);
lovrGraphicsGetBackground(color[0]);
lovrPassSetClear(state.pass, color, depth, stencil);
lovrPassSetTarget(state.pass, &texture, NULL);
lovrPassReset(state.pass);
uint32_t count;