lovr.headset.init -> lovr.headset.start;

- We need some headset initialization to happen upfront
- But we still want some delayed initialization for when graphics is ready
- Go back to headset initialization happening when module is required
- Add lovr.headset.start that can be used for post-graphics init
This commit is contained in:
bjorn 2021-06-10 17:26:15 -06:00
parent 7dc54cc264
commit 92400df89d
11 changed files with 165 additions and 139 deletions

View File

@ -113,59 +113,11 @@ static Device luax_optdevice(lua_State* L, int index) {
return luax_checkenum(L, 1, Device, "head");
}
static int l_lovrHeadsetInit(lua_State* L) {
luax_pushconf(L);
lua_getfield(L, -1, "headset");
size_t driverCount = 0;
HeadsetDriver drivers[8];
float supersample = 1.f;
float offset = 1.7f;
int msaa = 4;
bool overlay = false;
if (lua_istable(L, -1)) {
// Drivers
lua_getfield(L, -1, "drivers");
int n = luax_len(L, -1);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
drivers[driverCount++] = luax_checkenum(L, -1, HeadsetDriver, NULL);
lovrAssert(driverCount < sizeof(drivers) / sizeof(drivers[0]), "Too many headset drivers specified in conf.lua");
lua_pop(L, 1);
}
lua_pop(L, 1);
// Supersample
lua_getfield(L, -1, "supersample");
if (lua_type(L, -1) == LUA_TBOOLEAN) {
supersample = lua_toboolean(L, -1) ? 2.f : 1.f;
} else {
supersample = luax_optfloat(L, -1, 1.f);
}
lua_pop(L, 1);
// Offset
lua_getfield(L, -1, "offset");
offset = luax_optfloat(L, -1, 1.7f);
lua_pop(L, 1);
// MSAA
lua_getfield(L, -1, "msaa");
msaa = luaL_optinteger(L, -1, 4);
lua_pop(L, 1);
// Overlay
lua_getfield(L, -1, "overlay");
overlay = lua_toboolean(L, -1);
lua_pop(L, 1);
static int l_lovrHeadsetStart(lua_State* L) {
lovrHeadsetDisplayDriver->start();
FOREACH_TRACKING_DRIVER(driver) {
driver->start();
}
luax_atexit(L, lovrHeadsetDestroy); // Always make sure the headset module gets cleaned up
lovrHeadsetInit(drivers, driverCount, supersample, offset, msaa, overlay);
lua_pop(L, 2);
return 0;
}
@ -725,7 +677,7 @@ static int l_lovrHeadsetGetHands(lua_State* L) {
}
static const luaL_Reg lovrHeadset[] = {
{ "init", l_lovrHeadsetInit },
{ "start", l_lovrHeadsetStart },
{ "getDriver", l_lovrHeadsetGetDriver },
{ "getName", l_lovrHeadsetGetName },
{ "getOriginType", l_lovrHeadsetGetOriginType },
@ -769,6 +721,61 @@ static const luaL_Reg lovrHeadset[] = {
int luaopen_lovr_headset(lua_State* L) {
lua_newtable(L);
luax_register(L, lovrHeadset);
luax_pushconf(L);
lua_getfield(L, -1, "headset");
size_t driverCount = 0;
HeadsetDriver drivers[8];
float supersample = 1.f;
float offset = 1.7f;
int msaa = 4;
bool overlay = false;
if (lua_istable(L, -1)) {
// Drivers
lua_getfield(L, -1, "drivers");
int n = luax_len(L, -1);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
drivers[driverCount++] = luax_checkenum(L, -1, HeadsetDriver, NULL);
lovrAssert(driverCount < sizeof(drivers) / sizeof(drivers[0]), "Too many headset drivers specified in conf.lua");
lua_pop(L, 1);
}
lua_pop(L, 1);
// Supersample
lua_getfield(L, -1, "supersample");
if (lua_type(L, -1) == LUA_TBOOLEAN) {
supersample = lua_toboolean(L, -1) ? 2.f : 1.f;
} else {
supersample = luax_optfloat(L, -1, 1.f);
}
lua_pop(L, 1);
// Offset
lua_getfield(L, -1, "offset");
offset = luax_optfloat(L, -1, 1.7f);
lua_pop(L, 1);
// MSAA
lua_getfield(L, -1, "msaa");
msaa = luaL_optinteger(L, -1, 4);
lua_pop(L, 1);
// Overlay
lua_getfield(L, -1, "overlay");
overlay = lua_toboolean(L, -1);
lua_pop(L, 1);
}
if (lovrHeadsetInit(drivers, driverCount, supersample, offset, msaa, overlay)) {
luax_atexit(L, lovrHeadsetDestroy);
}
lua_pop(L, 2);
headsetRenderData.ref = LUA_NOREF;
return 1;
}

View File

@ -104,6 +104,8 @@ typedef enum {
} HandJoint;
// Notes:
// - init is called immediately, the graphics module may not exist yet
// - start is called after the graphics module is initialized, can be used to set up graphics objects
// - getDisplayFrequency may return 0.f if the information is unavailable.
// - For isDown, changed can be set to false if change information is unavailable or inconvenient.
// - getAxis may write 4 floats to the output value. The expected number is a constant (see axisCounts in l_headset).
@ -113,6 +115,7 @@ typedef struct HeadsetInterface {
struct HeadsetInterface* next;
HeadsetDriver driverType;
bool (*init)(float supersample, float offset, uint32_t msaa, bool overlay);
void (*start)(void);
void (*destroy)(void);
bool (*getName)(char* name, size_t length);
HeadsetOrigin (*getOriginType)(void);

View File

@ -47,6 +47,10 @@ static bool desktop_init(float supersample, float offset, uint32_t msaa, bool ov
return true;
}
static void desktop_start(void) {
//
}
static void desktop_destroy(void) {
//
}
@ -285,6 +289,7 @@ static void desktop_update(float dt) {
HeadsetInterface lovrHeadsetDesktopDriver = {
.driverType = DRIVER_DESKTOP,
.init = desktop_init,
.start = desktop_start,
.destroy = desktop_destroy,
.getName = desktop_getName,
.getOriginType = desktop_getOriginType,

View File

@ -112,6 +112,37 @@ static bool oculus_init(float supersample, float offset, uint32_t msaa, bool ove
return true;
}
static void oculus_start(void) {
state.size = ovr_GetFovTextureSize(state.session, ovrEye_Left, state.desc.DefaultEyeFov[ovrEye_Left], 1.0f);
state.size.w *= state.supersample;
state.size.h *= state.supersample;
ovrTextureSwapChainDesc swdesc = {
.Type = ovrTexture_2D,
.ArraySize = 1,
.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB,
.Width = 2 * state.size.w,
.Height = state.size.h,
.MipLevels = 1,
.SampleCount = 1,
.StaticImage = ovrFalse
};
lovrAssert(OVR_SUCCESS(ovr_CreateTextureSwapChainGL(state.session, &swdesc, &state.chain)), "Unable to create swapchain");
ovrMirrorTextureDesc mdesc = {
.Width = lovrGraphicsGetWidth(),
.Height = lovrGraphicsGetHeight(),
.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB,
.MirrorOptions = ovrMirrorOption_LeftEyeOnly
};
lovrAssert(OVR_SUCCESS(ovr_CreateMirrorTextureWithOptionsGL(state.session, &mdesc, &state.mirror)), "Unable to create mirror texture");
CanvasFlags flags = { .depth = { .enabled = true, .format = FORMAT_D24S8 }, .stereo = true };
state.canvas = lovrCanvasCreate(state.size.w, state.size.h, flags);
os_window_set_vsync(0);
}
static void oculus_destroy(void) {
for (size_t i = 0; i < state.textures.length; i++) {
lovrRelease(state.textures.data[i], lovrTextureDestroy);
@ -328,37 +359,6 @@ static ModelData* oculus_newModelData(Device device, bool animated) {
}
static void oculus_renderTo(void (*callback)(void*), void* userdata) {
if (!state.canvas) {
state.size = ovr_GetFovTextureSize(state.session, ovrEye_Left, state.desc.DefaultEyeFov[ovrEye_Left], 1.0f);
state.size.w *= state.supersample;
state.size.h *= state.supersample;
ovrTextureSwapChainDesc swdesc = {
.Type = ovrTexture_2D,
.ArraySize = 1,
.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB,
.Width = 2 * state.size.w,
.Height = state.size.h,
.MipLevels = 1,
.SampleCount = 1,
.StaticImage = ovrFalse
};
lovrAssert(OVR_SUCCESS(ovr_CreateTextureSwapChainGL(state.session, &swdesc, &state.chain)), "Unable to create swapchain");
ovrMirrorTextureDesc mdesc = {
.Width = lovrGraphicsGetWidth(),
.Height = lovrGraphicsGetHeight(),
.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB,
.MirrorOptions = ovrMirrorOption_LeftEyeOnly
};
lovrAssert(OVR_SUCCESS(ovr_CreateMirrorTextureWithOptionsGL(state.session, &mdesc, &state.mirror)), "Unable to create mirror texture");
CanvasFlags flags = { .depth = { .enabled = true, .format = FORMAT_D24S8 }, .stereo = true };
state.canvas = lovrCanvasCreate(state.size.w, state.size.h, flags);
os_window_set_vsync(0);
}
ovrPosef EyeRenderPose[2];
double sensorSampleTime;
getEyePoses(EyeRenderPose, &sensorSampleTime);
@ -460,6 +460,7 @@ static void oculus_update(float dt) {
HeadsetInterface lovrHeadsetOculusDriver = {
.driverType = DRIVER_OCULUS,
.init = oculus_init,
.start = oculus_start,
.destroy = oculus_destroy,
.getName = oculus_getName,
.getOriginType = oculus_getOriginType,

View File

@ -254,6 +254,21 @@ static bool openvr_init(float supersample, float offset, uint32_t msaa, bool ove
return true;
}
static void openvr_start(void) {
uint32_t width, height;
state.system->GetRecommendedRenderTargetSize(&width, &height);
width *= state.supersample;
height *= state.supersample;
CanvasFlags flags = { .depth = { true, false, FORMAT_D24S8 }, .stereo = true, .mipmaps = true, .msaa = state.msaa };
state.canvas = lovrCanvasCreate(width, height, flags);
Texture* texture = lovrTextureCreate(TEXTURE_2D, NULL, 0, true, true, state.msaa);
lovrTextureAllocate(texture, width * 2, height, 1, FORMAT_RGBA);
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
lovrCanvasSetAttachments(state.canvas, &(Attachment) { texture, 0, 0 }, 1);
lovrRelease(texture, lovrTextureDestroy);
os_window_set_vsync(0);
}
static void openvr_destroy(void) {
lovrRelease(state.canvas, lovrCanvasDestroy);
VR_ShutdownInternal();
@ -804,21 +819,6 @@ static bool openvr_animate(Device device, Model* model) {
}
static void openvr_renderTo(void (*callback)(void*), void* userdata) {
if (!state.canvas) {
uint32_t width, height;
openvr_getDisplayDimensions(&width, &height);
width *= state.supersample;
height *= state.supersample;
CanvasFlags flags = { .depth = { true, false, FORMAT_D24S8 }, .stereo = true, .mipmaps = true, .msaa = state.msaa };
state.canvas = lovrCanvasCreate(width, height, flags);
Texture* texture = lovrTextureCreate(TEXTURE_2D, NULL, 0, true, true, state.msaa);
lovrTextureAllocate(texture, width * 2, height, 1, FORMAT_RGBA);
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
lovrCanvasSetAttachments(state.canvas, &(Attachment) { texture, 0, 0 }, 1);
lovrRelease(texture, lovrTextureDestroy);
os_window_set_vsync(0);
}
float head[16];
mat4_fromMat34(head, state.renderPoses[k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking.m);
@ -879,6 +879,7 @@ static void openvr_update(float dt) {
HeadsetInterface lovrHeadsetOpenVRDriver = {
.driverType = DRIVER_OPENVR,
.init = openvr_init,
.start = openvr_start,
.destroy = openvr_destroy,
.getName = openvr_getName,
.getOriginType = openvr_getOriginType,

View File

@ -333,6 +333,13 @@ static bool openxr_init(float supersample, float offset, uint32_t msaa, bool ove
}
}
state.clipNear = .1f;
state.clipFar = 100.f;
state.frameState.type = XR_TYPE_FRAME_STATE;
return true;
}
static void openxr_start(void) {
{ // Session
#if defined(LOVR_GL)
XrGraphicsRequirementsOpenGLKHR requirements = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR, NULL };
@ -413,7 +420,7 @@ static bool openxr_init(float supersample, float offset, uint32_t msaa, bool ove
XR_INIT(xrAttachSessionActionSets(state.session, &attachInfo));
}
{ // Spaaaaace
{ // Spaaace
// Main reference space (can be stage or local)
XrReferenceSpaceCreateInfo info = {
@ -535,13 +542,7 @@ static bool openxr_init(float supersample, float offset, uint32_t msaa, bool ove
#endif
}
state.clipNear = .1f;
state.clipFar = 100.f;
state.frameState.type = XR_TYPE_FRAME_STATE;
os_window_set_vsync(0);
return true;
}
static void openxr_destroy(void) {
@ -976,6 +977,7 @@ static void openxr_update(float dt) {
HeadsetInterface lovrHeadsetOpenXRDriver = {
.driverType = DRIVER_OPENXR,
.init = openxr_init,
.start = openxr_start,
.destroy = openxr_destroy,
.getName = openxr_getName,
.getOriginType = openxr_getOriginType,

View File

@ -241,6 +241,10 @@ static bool pico_init(float supersample, float offset, uint32_t msaa, bool overl
return true;
}
static void pico_start(void) {
//
}
static void pico_destroy(void) {
arr_free(&state.canvases);
memset(&state, 0, sizeof(state));
@ -425,6 +429,7 @@ static void pico_update(float dt) {
HeadsetInterface lovrHeadsetPicoDriver = {
.driverType = DRIVER_PICO,
.init = pico_init,
.start = pico_start,
.destroy = pico_destroy,
.getName = pico_getName,
.getOriginType = pico_getOriginType,

View File

@ -70,6 +70,33 @@ static bool vrapi_init(float supersample, float offset, uint32_t msaa, bool over
return true;
}
static void vrapi_start(void) {
CanvasFlags flags = {
.depth.enabled = true,
.depth.readable = false,
.depth.format = FORMAT_D24S8,
.msaa = state.msaa,
.stereo = true,
.mipmaps = false
};
uint32_t width, height;
vrapi_getDisplayDimensions(&width, &height);
width *= state.supersample;
height *= state.supersample;
state.swapchain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D_ARRAY, GL_SRGB8_ALPHA8, width, height, 1, 3);
state.swapchainLength = vrapi_GetTextureSwapChainLength(state.swapchain);
lovrAssert(state.swapchainLength <= sizeof(state.canvases) / sizeof(state.canvases[0]), "VrApi: The swapchain is too long");
for (uint32_t i = 0; i < state.swapchainLength; i++) {
state.canvases[i] = lovrCanvasCreate(width, height, flags);
uint32_t handle = vrapi_GetTextureSwapChainHandle(state.swapchain, i);
Texture* texture = lovrTextureCreateFromHandle(handle, TEXTURE_ARRAY, 2, 1);
lovrCanvasSetAttachments(state.canvases[i], &(Attachment) { .texture = texture }, 1);
lovrRelease(texture, lovrTextureDestroy);
}
}
static void vrapi_destroy() {
if (state.session) {
vrapi_LeaveVrMode(state.session);
@ -628,34 +655,6 @@ static bool vrapi_animate(Device device, struct Model* model) {
static void vrapi_renderTo(void (*callback)(void*), void* userdata) {
if (!state.session) return;
// Lazily create swapchain and canvases
if (!state.swapchain) {
CanvasFlags flags = {
.depth.enabled = true,
.depth.readable = false,
.depth.format = FORMAT_D24S8,
.msaa = state.msaa,
.stereo = true,
.mipmaps = false
};
uint32_t width, height;
vrapi_getDisplayDimensions(&width, &height);
width *= state.supersample;
height *= state.supersample;
state.swapchain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D_ARRAY, GL_SRGB8_ALPHA8, width, height, 1, 3);
state.swapchainLength = vrapi_GetTextureSwapChainLength(state.swapchain);
lovrAssert(state.swapchainLength <= sizeof(state.canvases) / sizeof(state.canvases[0]), "VrApi: The swapchain is too long");
for (uint32_t i = 0; i < state.swapchainLength; i++) {
state.canvases[i] = lovrCanvasCreate(width, height, flags);
uint32_t handle = vrapi_GetTextureSwapChainHandle(state.swapchain, i);
Texture* texture = lovrTextureCreateFromHandle(handle, TEXTURE_ARRAY, 2, 1);
lovrCanvasSetAttachments(state.canvases[i], &(Attachment) { .texture = texture }, 1);
lovrRelease(texture, lovrTextureDestroy);
}
}
ovrTracking2 tracking = vrapi_GetPredictedTracking2(state.session, state.displayTime);
// Camera
@ -806,6 +805,7 @@ static void vrapi_update(float dt) {
HeadsetInterface lovrHeadsetVrApiDriver = {
.driverType = DRIVER_VRAPI,
.init = vrapi_init,
.start = vrapi_start,
.destroy = vrapi_destroy,
.getName = vrapi_getName,
.getOriginType = vrapi_getOriginType,

View File

@ -1,6 +1,7 @@
#include "headset/headset.h"
extern bool webxr_init(float supersample, float offset, uint32_t msaa, bool overlay);
extern void webxr_start(void);
extern void webxr_destroy(void);
extern bool webxr_getName(char* name, size_t length);
extern HeadsetOrigin webxr_getOriginType(void);
@ -69,6 +70,7 @@ void webxr_detach() {
HeadsetInterface lovrHeadsetWebXRDriver = {
.driverType = DRIVER_WEBXR,
.init = webxr_init,
.start = webxr_start,
.destroy = webxr_destroy,
.getName = webxr_getName,
.getOriginType = webxr_getOriginType,

View File

@ -165,11 +165,7 @@ function lovr.boot()
end
if lovr.headset and lovr.graphics and conf.window then
local ok, result = pcall(lovr.headset.init)
if not ok then
print(string.format('Warning: Could not load module %q: %s', 'headset', result))
lovr.headset = nil
end
lovr.headset.start()
end
lovr.handlers = setmetatable({}, { __index = lovr })

View File

@ -163,6 +163,10 @@ var webxr = {
return false;
},
webxr_start: function() {
// Session is handled asynchronously
},
webxr_destroy: function() {
if (state.session) {
state.session.end();