Add lovr.headset.isPassthroughEnabled and lovr.headset.setPassthroughEnabled;

This commit is contained in:
bjorn 2023-02-05 19:51:12 -08:00
parent 5a27a0f819
commit 89edccbf4c
6 changed files with 154 additions and 41 deletions

View File

@ -182,6 +182,11 @@ var webxr = {
return 1; /* ORIGIN_FLOOR */
},
webxr_getDisplayDimensions: function(width, height) {
HEAPU32[width >> 2] = state.layer.framebufferWidth;
HEAPU32[height >> 2] = state.layer.framebufferHeight;
},
webxr_getDisplayTime: function() {
return state.displayTime / 1000.0;
},
@ -190,11 +195,6 @@ var webxr = {
return (state.displayTime - state.lastDisplayTime) / 1000.0;
},
webxr_getDisplayDimensions: function(width, height) {
HEAPU32[width >> 2] = state.layer.framebufferWidth;
HEAPU32[height >> 2] = state.layer.framebufferHeight;
},
webxr_getDisplayFrequency: function() {
return 0.0;
},
@ -411,6 +411,14 @@ var webxr = {
return true;
},
webxr_isPassthroughEnabled: function() {
return false;
},
webxr_setPassthroughEnabled: function() {
return false;
},
webxr_update: function() {
return (state.displayTime - state.lastDisplayTime) / 1000.0;
}

View File

@ -539,6 +539,18 @@ static int l_lovrHeadsetIsFocused(lua_State* L) {
return 1;
}
static int l_lovrHeadsetIsPassthroughEnabled(lua_State* L) {
lua_pushboolean(L, lovrHeadsetInterface->isPassthroughEnabled());
return 1;
}
static int l_lovrHeadsetSetPassthroughEnabled(lua_State* L) {
bool enable = lua_toboolean(L, 1);
bool success = lovrHeadsetInterface->setPassthroughEnabled(enable);
lua_pushboolean(L, success);
return 1;
}
static int l_lovrHeadsetUpdate(lua_State* L) {
double dt = 0.;
@ -620,6 +632,8 @@ static const luaL_Reg lovrHeadset[] = {
{ "getPass", l_lovrHeadsetGetPass },
{ "submit", l_lovrHeadsetSubmit },
{ "isFocused", l_lovrHeadsetIsFocused },
{ "isPassthroughEnabled", l_lovrHeadsetIsPassthroughEnabled },
{ "setPassthroughEnabled", l_lovrHeadsetSetPassthroughEnabled },
{ "update", l_lovrHeadsetUpdate },
{ "getTime", l_lovrHeadsetGetTime },
{ "getDeltaTime", l_lovrHeadsetGetDeltaTime },

View File

@ -155,6 +155,8 @@ typedef struct HeadsetInterface {
struct Pass* (*getPass)(void);
void (*submit)(void);
bool (*isFocused)(void);
bool (*isPassthroughEnabled)(void);
bool (*setPassthroughEnabled)(bool enable);
double (*update)(void);
} HeadsetInterface;

View File

@ -75,6 +75,10 @@ static HeadsetOrigin desktop_getOriginType(void) {
return ORIGIN_HEAD;
}
static void desktop_getDisplayDimensions(uint32_t* width, uint32_t* height) {
os_window_get_size(width, height);
}
static double desktop_getDisplayTime(void) {
return state.nextDisplayTime - state.epoch;
}
@ -83,10 +87,6 @@ static double desktop_getDeltaTime(void) {
return state.nextDisplayTime - state.prevDisplayTime;
}
static void desktop_getDisplayDimensions(uint32_t* width, uint32_t* height) {
os_window_get_size(width, height);
}
static uint32_t desktop_getViewCount(void) {
return 1;
}
@ -225,6 +225,14 @@ static bool desktop_isFocused(void) {
return state.focused;
}
static bool desktop_isPassthroughEnabled(void) {
return false;
}
static bool desktop_setPassthroughEnabled(bool enable) {
return false;
}
static double desktop_update(void) {
bool front = os_is_key_down(KEY_W) || os_is_key_down(KEY_UP);
bool back = os_is_key_down(KEY_S) || os_is_key_down(KEY_DOWN);
@ -325,9 +333,9 @@ HeadsetInterface lovrHeadsetDesktopDriver = {
.destroy = desktop_destroy,
.getName = desktop_getName,
.getOriginType = desktop_getOriginType,
.getDisplayDimensions = desktop_getDisplayDimensions,
.getDisplayTime = desktop_getDisplayTime,
.getDeltaTime = desktop_getDeltaTime,
.getDisplayDimensions = desktop_getDisplayDimensions,
.getViewCount = desktop_getViewCount,
.getViewPose = desktop_getViewPose,
.getViewAngles = desktop_getViewAngles,
@ -348,5 +356,7 @@ HeadsetInterface lovrHeadsetDesktopDriver = {
.getPass = desktop_getPass,
.submit = desktop_submit,
.isFocused = desktop_isFocused,
.isPassthroughEnabled = desktop_isPassthroughEnabled,
.setPassthroughEnabled = desktop_setPassthroughEnabled,
.update = desktop_update
};

View File

@ -98,7 +98,13 @@ uintptr_t gpu_vk_get_queue(uint32_t* queueFamilyIndex, uint32_t* queueIndex);
X(xrEnumerateDisplayRefreshRatesFB)\
X(xrRequestDisplayRefreshRateFB)\
X(xrQuerySystemTrackedKeyboardFB)\
X(xrCreateKeyboardSpaceFB)
X(xrCreateKeyboardSpaceFB)\
X(xrCreatePassthroughFB)\
X(xrDestroyPassthroughFB)\
X(xrPassthroughStartFB)\
X(xrPassthroughPauseFB)\
X(xrCreatePassthroughLayerFB)\
X(xrDestroyPassthroughLayerFB)
#define XR_DECLARE(fn) static PFN_##fn fn;
#define XR_LOAD(fn) xrGetInstanceProcAddr(state.instance, #fn, (PFN_xrVoidFunction*) &fn);
@ -156,6 +162,7 @@ static struct {
XrCompositionLayerProjection layers[1];
XrCompositionLayerProjectionView layerViews[2];
XrCompositionLayerDepthInfoKHR depthInfo[2];
XrCompositionLayerPassthroughFB passthroughLayer;
XrFrameState frameState;
TextureFormat depthFormat;
Texture* textures[2][MAX_IMAGES];
@ -174,6 +181,9 @@ static struct {
XrPath actionFilters[MAX_DEVICES];
XrHandTrackerEXT handTrackers[2];
XrControllerModelKeyMSFT controllerModelKeys[2];
XrPassthroughFB passthrough;
XrPassthroughLayerFB passthroughLayerHandle;
bool passthroughActive;
struct {
bool controllerModel;
bool depth;
@ -185,6 +195,7 @@ static struct {
bool headless;
bool keyboardTracking;
bool overlay;
bool passthrough;
bool refreshRate;
bool viveTrackers;
} features;
@ -371,6 +382,7 @@ static bool openxr_init(HeadsetConfig* config) {
{ "XR_FB_hand_tracking_aim", &state.features.handTrackingAim, true },
{ "XR_FB_hand_tracking_mesh", &state.features.handTrackingMesh, true },
{ "XR_FB_keyboard_tracking", &state.features.keyboardTracking, true },
{ "XR_FB_passthrough", &state.features.passthrough, true },
{ "XR_MND_headless", &state.features.headless, true },
{ "XR_MSFT_controller_model", &state.features.controllerModel, true },
{ "XR_ULTRALEAP_hand_tracking_forearm", &state.features.handTrackingElbow, true },
@ -413,24 +425,11 @@ static bool openxr_init(HeadsetConfig* config) {
XR_INIT(xrGetSystem(state.instance, &info, &state.system));
XrSystemEyeGazeInteractionPropertiesEXT eyeGazeProperties = {
.type = XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT,
.supportsEyeGazeInteraction = false
};
XrSystemHandTrackingPropertiesEXT handTrackingProperties = {
.type = XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT,
.supportsHandTracking = false
};
XrSystemKeyboardTrackingPropertiesFB keyboardTrackingProperties = {
.type = XR_TYPE_SYSTEM_KEYBOARD_TRACKING_PROPERTIES_FB,
.supportsKeyboardTracking = false
};
XrSystemProperties properties = {
.type = XR_TYPE_SYSTEM_PROPERTIES
};
XrSystemEyeGazeInteractionPropertiesEXT eyeGazeProperties = { .type = XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT };
XrSystemHandTrackingPropertiesEXT handTrackingProperties = { .type = XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT };
XrSystemKeyboardTrackingPropertiesFB keyboardTrackingProperties = { .type = XR_TYPE_SYSTEM_KEYBOARD_TRACKING_PROPERTIES_FB };
XrSystemPassthroughProperties2FB passthroughProperties = { .type = XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES2_FB };
XrSystemProperties properties = { .type = XR_TYPE_SYSTEM_PROPERTIES };
if (state.features.gaze) {
eyeGazeProperties.next = properties.next;
@ -447,10 +446,16 @@ static bool openxr_init(HeadsetConfig* config) {
properties.next = &keyboardTrackingProperties;
}
if (state.features.passthrough) {
passthroughProperties.next = properties.next;
properties.next = &passthroughProperties;
}
XR_INIT(xrGetSystemProperties(state.instance, state.system, &properties));
state.features.gaze = eyeGazeProperties.supportsEyeGazeInteraction;
state.features.handTracking = handTrackingProperties.supportsHandTracking;
state.features.keyboardTracking = keyboardTrackingProperties.supportsKeyboardTracking;
state.features.passthrough = passthroughProperties.capabilities & XR_PASSTHROUGH_CAPABILITY_BIT_FB;
uint32_t viewConfigurationCount;
XrViewConfigurationType viewConfigurations[2];
@ -1042,17 +1047,10 @@ static void openxr_start(void) {
}
}
XrCompositionLayerFlags layerFlags = 0;
if (state.features.overlay) {
layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
}
// Pre-init composition layer
state.layers[0] = (XrCompositionLayerProjection) {
.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION,
.space = state.referenceSpace,
.layerFlags = layerFlags,
.viewCount = 2,
.views = state.layerViews
};
@ -1122,6 +1120,9 @@ static void openxr_stop(void) {
if (state.handTrackers[0]) xrDestroyHandTrackerEXT(state.handTrackers[0]);
if (state.handTrackers[1]) xrDestroyHandTrackerEXT(state.handTrackers[1]);
if (state.passthrough) xrDestroyPassthroughFB(state.passthrough);
if (state.passthroughLayerHandle) xrDestroyPassthroughLayerFB(state.passthroughLayerHandle);
for (size_t i = 0; i < MAX_DEVICES; i++) {
if (state.spaces[i]) {
xrDestroySpace(state.spaces[i]);
@ -2055,13 +2056,13 @@ static void openxr_submit(void) {
return;
}
XrCompositionLayerBaseHeader const* layers[2];
XrFrameEndInfo info = {
.type = XR_TYPE_FRAME_END_INFO,
.displayTime = state.frameState.predictedDisplayTime,
.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
.layers = (const XrCompositionLayerBaseHeader*[1]) {
(XrCompositionLayerBaseHeader*) &state.layers[0]
}
.layers = layers
};
if (state.features.depth) {
@ -2081,7 +2082,20 @@ static void openxr_submit(void) {
XR(xrReleaseSwapchainImage(state.swapchain[DEPTH], NULL));
}
info.layerCount = 1;
if (state.passthroughActive) {
layers[0] = (const XrCompositionLayerBaseHeader*) &state.passthroughLayer;
layers[1] = (const XrCompositionLayerBaseHeader*) &state.layers[0];
info.layerCount = 2;
} else {
layers[0] = (const XrCompositionLayerBaseHeader*) &state.layers[0];
info.layerCount = 1;
}
if (state.features.overlay || state.passthroughActive) {
state.layers[0].layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
} else {
state.layers[0].layerFlags = 0;
}
}
XR(xrEndFrame(state.session, &info));
@ -2093,6 +2107,67 @@ static bool openxr_isFocused(void) {
return state.sessionState == XR_SESSION_STATE_FOCUSED;
}
static bool openxr_isPassthroughEnabled(void) {
return state.passthroughActive;
}
static bool openxr_setPassthroughEnabled(bool enable) {
if (!state.features.passthrough) return false;
XrResult result = XR_SUCCESS;
if (!state.passthrough) {
XrPassthroughCreateInfoFB info = { .type = XR_TYPE_PASSTHROUGH_CREATE_INFO_FB };
result = xrCreatePassthroughFB(state.session, &info, &state.passthrough);
if (XR_FAILED(result)) {
return false;
}
XrPassthroughLayerCreateInfoFB layerInfo = {
.type = XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB,
.passthrough = state.passthrough,
.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB,
.flags = XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB
};
result = xrCreatePassthroughLayerFB(state.session, &layerInfo, &state.passthroughLayerHandle);
if (XR_FAILED(result)) {
xrDestroyPassthroughFB(state.passthrough);
state.passthrough = NULL;
return false;
}
state.passthroughLayer = (XrCompositionLayerPassthroughFB) {
.type = XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB,
.layerHandle = state.passthroughLayerHandle
};
}
if (enable == state.passthroughActive) {
return true;
}
if (enable) {
result = xrPassthroughStartFB(state.passthrough);
if (XR_SUCCEEDED(result)) {
state.passthroughActive = true;
return true;
}
} else {
result = xrPassthroughPauseFB(state.passthrough);
if (XR_SUCCEEDED(result)) {
state.passthroughActive = false;
return true;
}
}
return false;
}
static double openxr_update(void) {
if (state.waited) return openxr_getDeltaTime();
@ -2202,5 +2277,7 @@ HeadsetInterface lovrHeadsetOpenXRDriver = {
.getPass = openxr_getPass,
.submit = openxr_submit,
.isFocused = openxr_isFocused,
.isPassthroughEnabled = openxr_isPassthroughEnabled,
.setPassthroughEnabled = openxr_setPassthroughEnabled,
.update = openxr_update
};

View File

@ -5,9 +5,9 @@ extern void webxr_start(void);
extern void webxr_destroy(void);
extern bool webxr_getName(char* name, size_t length);
extern HeadsetOrigin webxr_getOriginType(void);
extern void webxr_getDisplayDimensions(uint32_t* width, uint32_t* height);
extern double webxr_getDisplayTime(void);
extern double webxr_getDeltaTime(void);
extern void webxr_getDisplayDimensions(uint32_t* width, uint32_t* height);
extern uint32_t webxr_getViewCount(void);
extern bool webxr_getViewPose(uint32_t view, float* position, float* orientation);
extern bool webxr_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down);
@ -26,6 +26,8 @@ extern struct ModelData* webxr_newModelData(Device device, bool animated);
extern bool webxr_animate(struct Model* model);
extern void webxr_renderTo(void (*callback)(void*), void* userdata);
extern bool webxr_isFocused(void);
extern bool webxr_isPassthroughEnabled(void);
extern bool webxr_setPassthroughEnabled(bool enable);
extern double webxr_update(void);
static bool webxrAttached = false;