Skip to content

Commit

Permalink
t.headset.mask;
Browse files Browse the repository at this point in the history
  • Loading branch information
bjornbytes committed Dec 9, 2024
1 parent ac3bbe9 commit 0e2e98e
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 5 deletions.
1 change: 1 addition & 0 deletions etc/boot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ local conf = {
start = true,
debug = false,
seated = false,
mask = true,
stencil = false,
antialias = true,
supersample = false,
Expand Down
1 change: 1 addition & 0 deletions etc/shaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "shaders/equirect.frag.h"
#include "shaders/fill.vert.h"
#include "shaders/fill_array.frag.h"
#include "shaders/mask.vert.h"
#include "shaders/animator.comp.h"
#include "shaders/blender.comp.h"
#include "shaders/tallymerge.comp.h"
Expand Down
15 changes: 15 additions & 0 deletions etc/shaders/mask.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#version 460
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require

#include "lovr.glsl"

vec4 lovrmain() {
if (VertexPosition.z != ViewIndex) {
return vec4(0.);
}

vec4 clip = Projection * vec4(VertexPosition.xy, -1., 1.);
clip.z = clip.w; // Sets NDC z to 1 (near plane)
return clip;
}
5 changes: 5 additions & 0 deletions src/api/l_headset.c
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,7 @@ int luaopen_lovr_headset(lua_State* L) {
.driverCount = 0,
.supersample = 1.f,
.seated = false,
.mask = true,
.stencil = false,
.antialias = true,
.submitDepth = true,
Expand Down Expand Up @@ -1002,6 +1003,10 @@ int luaopen_lovr_headset(lua_State* L) {
config.seated = lua_toboolean(L, -1);
lua_pop(L, 1);

lua_getfield(L, -1, "mask");
config.mask = lua_isnil(L, -1) ? true : lua_toboolean(L, -1);
lua_pop(L, 1);

lua_getfield(L, -1, "stencil");
config.stencil = lua_toboolean(L, -1);
lua_pop(L, 1);
Expand Down
4 changes: 4 additions & 0 deletions src/modules/graphics/graphics.c
Original file line number Diff line number Diff line change
Expand Up @@ -3163,6 +3163,10 @@ ShaderSource lovrGraphicsGetDefaultShaderSource(DefaultShader type, ShaderStage
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert) },
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_fill_array_frag, sizeof(lovr_shader_fill_array_frag) }
},
[SHADER_MASK] = {
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_mask_vert, sizeof(lovr_shader_mask_vert) },
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag) }
},
[SHADER_ANIMATOR] = {
[STAGE_COMPUTE] = { STAGE_COMPUTE, lovr_shader_animator_comp, sizeof(lovr_shader_animator_comp) }
},
Expand Down
1 change: 1 addition & 0 deletions src/modules/graphics/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ typedef enum {
SHADER_EQUIRECT,
SHADER_FILL_2D,
SHADER_FILL_ARRAY,
SHADER_MASK,
SHADER_ANIMATOR,
SHADER_BLENDER,
SHADER_TALLY_MERGE,
Expand Down
1 change: 1 addition & 0 deletions src/modules/headset/headset.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ typedef struct {
float supersample;
bool debug;
bool seated;
bool mask;
bool stencil;
bool antialias;
bool submitDepth;
Expand Down
125 changes: 120 additions & 5 deletions src/modules/headset/headset_openxr.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ uintptr_t gpu_vk_get_queue(uint32_t* queueFamilyIndex, uint32_t* queueIndex);
X(xrSyncActions)\
X(xrApplyHapticFeedback)\
X(xrStopHapticFeedback)\
X(xrGetVisibilityMaskKHR)\
X(xrCreateHandTrackerEXT)\
X(xrDestroyHandTrackerEXT)\
X(xrLocateHandJointsEXT)\
Expand Down Expand Up @@ -227,6 +228,7 @@ static struct {
Layer* layers[MAX_LAYERS];
uint32_t layerCount;
bool showMainLayer;
Mesh* mask;
XrFrameState frameState;
XrTime lastDisplayTime;
XrTime epoch;
Expand Down Expand Up @@ -278,6 +280,7 @@ static struct {
bool questPassthrough;
bool refreshRate;
bool threadHint;
bool visibilityMask;
bool viveTrackers;
} extensions;
} state;
Expand Down Expand Up @@ -500,6 +503,87 @@ static XrControllerModelKeyMSFT getControllerModelKey(Device device) {
return *modelKey;
}

static bool loadVisibilityMask(void) {
XrViewConfigurationType viewConfig = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrVisibilityMaskTypeKHR type = XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR;
XrVisibilityMaskKHR info = { .type = XR_TYPE_VISIBILITY_MASK_KHR };

uint32_t vertexCount = 0;
uint32_t indexCount = 0;

for (uint32_t i = 0; i < 2; i++) {
XR(xrGetVisibilityMaskKHR(state.session, viewConfig, i, type, &info), "xrGetVisibilityMask");
lovrCheck(UINT32_MAX - vertexCount >= info.vertexCountOutput, "Too many mask vertices");
lovrCheck(UINT32_MAX - indexCount >= info.indexCountOutput, "Too many mask indices");
vertexCount += info.vertexCountOutput;
indexCount += info.indexCountOutput;
}

if (!state.mask || lovrMeshGetVertexFormat(state.mask)->length < vertexCount) {
state.mask = lovrMeshCreate(&(MeshInfo) {
.vertexFormat = &(DataField) {
.name = "VertexPosition",
.type = TYPE_F32x3,
.length = vertexCount,
.stride = 12
}
}, NULL);

if (!state.mask) {
return false;
}
}

float* vertices = lovrMeshSetVertices(state.mask, 0, vertexCount);
info.vertices = (void*) vertices;
info.indices = lovrMeshSetIndices(state.mask, indexCount, TYPE_U32);
info.vertexCapacityInput = vertexCount;
info.indexCapacityInput = indexCount;

if (!info.vertices || !info.indices) {
lovrRelease(state.mask, lovrMeshDestroy);
state.mask = NULL;
return false;
}

uint32_t baseVertex = 0;
for (uint32_t i = 0; i < 2; i++) {
if (XR_FAILED(xrGetVisibilityMaskKHR(state.session, viewConfig, i, type, &info))) {
lovrRelease(state.mask, lovrMeshDestroy);
state.mask = NULL;
return false;
}

info.vertexCapacityInput -= info.vertexCountOutput;
info.indexCapacityInput -= info.indexCountOutput;

// Each vertex from OpenXR is 2 floats, but we store them as vec3 with z == view index
for (uint32_t j = 0; j < info.vertexCountOutput; j++) {
uint32_t index = info.vertexCountOutput - j - 1;
float* dst = vertices + 3 * index;
float* src = vertices + 2 * index;
dst[2] = (float) i;
dst[1] = src[1];
dst[0] = src[0];
}

if (i > 0) {
for (uint32_t j = 0; j < info.indexCountOutput; j++) {
info.indices[j] += baseVertex;
}
}

baseVertex += info.vertexCountOutput;
vertices += 3 * info.vertexCountOutput;
info.vertices = (void*) vertices;
info.indices += info.indexCountOutput;
}

lovrMeshSetDrawRange(state.mask, 0, indexCount, 0);

return true;
}

static bool swapchain_init(Swapchain* swapchain, uint32_t width, uint32_t height, bool stereo, bool depth, bool cube, bool immutable) {
XrSwapchainCreateInfo info = {
.type = XR_TYPE_SWAPCHAIN_CREATE_INFO,
Expand Down Expand Up @@ -722,9 +806,6 @@ static bool openxr_init(HeadsetConfig* config) {
// Extensions with feature == NULL must be present. The enable flag can be used to
// conditionally enable extensions based on config, platform, etc.
struct { const char* name; bool* feature; bool enable; } extensions[] = {
#ifdef LOVR_VK
{ "XR_KHR_vulkan_enable2", NULL, true },
#endif
#ifdef __ANDROID__
{ "XR_KHR_android_create_instance", NULL, true },
{ "XR_KHR_android_thread_settings", &state.extensions.threadHint, true },
Expand All @@ -735,10 +816,15 @@ static bool openxr_init(HeadsetConfig* config) {
{ "XR_KHR_composition_layer_depth", &state.extensions.depth, config->submitDepth },
{ "XR_KHR_composition_layer_equirect", &state.extensions.layerEquirect, true },
{ "XR_KHR_composition_layer_equirect2", &state.extensions.layerEquirect2, true },
#ifndef _WIN32
{ "XR_KHR_convert_timespec_time", NULL, true },
#endif
{ "XR_KHR_visibility_mask", &state.extensions.visibilityMask, config->mask },
#ifdef LOVR_VK
{ "XR_KHR_vulkan_enable2", NULL, true },
#endif
#ifdef _WIN32
{ "XR_KHR_win32_convert_performance_counter_time", NULL, true },
#else
{ "XR_KHR_convert_timespec_time", NULL, true },
#endif
{ "XR_EXT_debug_utils", &state.extensions.debug, true },
{ "XR_EXT_eye_gaze_interaction", &state.extensions.gaze, true },
Expand Down Expand Up @@ -1676,6 +1762,10 @@ static bool openxr_start(void) {
XRG(xrEnumerateDisplayRefreshRatesFB(state.session, state.refreshRateCount, &state.refreshRateCount, state.refreshRates), "xrEnumerateDisplayRefreshRatesFB", stop);
}

if (state.extensions.visibilityMask && !loadVisibilityMask()) {
lovrLog(LOG_WARN, "XR", "Failed to load headset mask: %s", lovrGetError());
}

state.showMainLayer = true;

return true;
Expand Down Expand Up @@ -1714,6 +1804,9 @@ static void openxr_stop(void) {
lovrRelease(state.pass, lovrPassDestroy);
state.pass = NULL;

lovrRelease(state.mask, lovrMeshDestroy);
state.mask = NULL;

if (state.handTrackers[0]) xrDestroyHandTrackerEXT(state.handTrackers[0]);
if (state.handTrackers[1]) xrDestroyHandTrackerEXT(state.handTrackers[1]);

Expand Down Expand Up @@ -3311,6 +3404,19 @@ static bool openxr_getPass(Pass** pass) {
}
}

if (state.extensions.visibilityMask && state.mask) {
Shader* shader = lovrGraphicsGetDefaultShader(SHADER_MASK);
lovrPassPush(state.pass, STACK_STATE);
lovrPassSetShader(state.pass, shader);
lovrPassSetColor(state.pass, (float[4]) { 0.f, 0.f, 0.f, 1.f });
if (!shader || !lovrPassDrawMesh(state.pass, state.mask, NULL, 1)) {
lovrLog(LOG_WARN, "XR", "Failed to draw headset mask: %s", lovrGetError());
lovrRelease(state.mask, lovrMeshDestroy);
state.mask = NULL;
}
lovrPassPop(state.pass, STACK_STATE);
}

*pass = state.pass;
return true;
}
Expand Down Expand Up @@ -3485,6 +3591,8 @@ static bool openxr_update(double* dt) {
e.type = XR_TYPE_EVENT_DATA_BUFFER;
e.next = NULL;

bool visibilityMaskDirty = false;

while (xrPollEvent(state.instance, &e) == XR_SUCCESS) {
switch (e.type) {
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
Expand Down Expand Up @@ -3535,6 +3643,9 @@ static bool openxr_update(double* dt) {
}
break;
}
case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR:
visibilityMaskDirty = true;
break;
case XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT: {
XrEventDataUserPresenceChangedEXT* event = (XrEventDataUserPresenceChangedEXT*) &e;
state.mounted = event->isUserPresent;
Expand All @@ -3547,6 +3658,10 @@ static bool openxr_update(double* dt) {
}

if (SESSION_ACTIVE(state.sessionState)) {
if (visibilityMaskDirty && !loadVisibilityMask()) {
lovrLog(LOG_WARN, "XR", "Failed to load headset mask: %s", lovrGetError());
}

state.lastDisplayTime = state.frameState.predictedDisplayTime;
XR(xrWaitFrame(state.session, NULL, &state.frameState), "xrWaitFrame");
state.waited = true;
Expand Down

0 comments on commit 0e2e98e

Please sign in to comment.