Submit depth buffer to OpenXR;

This commit is contained in:
bjorn 2022-08-06 22:52:18 -07:00
parent eba71d5921
commit 8aa1cf91b2
4 changed files with 172 additions and 38 deletions

View File

@ -645,6 +645,7 @@ int luaopen_lovr_headset(lua_State* L) {
.offset = 1.7f,
.stencil = false,
.antialias = true,
.submitDepth = true,
.overlay = false
};
@ -688,6 +689,11 @@ int luaopen_lovr_headset(lua_State* L) {
config.antialias = lua_isnil(L, -1) ? true : lua_toboolean(L, -1);
lua_pop(L, 1);
// Depth
lua_getfield(L, -1, "submitdepth");
config.submitDepth = lua_toboolean(L, -1);
lua_pop(L, 1);
// Overlay
lua_getfield(L, -1, "overlay");
config.overlay = lua_toboolean(L, -1);

View File

@ -336,6 +336,7 @@ bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info) {
buffer->handle = (VkBuffer) info->handle;
buffer->memory = ~0u;
buffer->offset = 0;
nickname(buffer->handle, VK_OBJECT_TYPE_BUFFER, info->label);
return true;
}
@ -508,6 +509,7 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
if (info->handle) {
texture->memory = ~0u;
texture->handle = (VkImage) info->handle;
nickname(texture->handle, VK_OBJECT_TYPE_IMAGE, info->label);
return gpu_texture_init_view(texture, &viewInfo);
}

View File

@ -24,6 +24,7 @@ typedef struct {
float offset;
bool stencil;
bool antialias;
bool submitDepth;
bool overlay;
} HeadsetConfig;
@ -110,7 +111,7 @@ typedef enum {
// 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
// - start is called after the graphics module is initialized, can be used to set up textures etc.
// - 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).

View File

@ -60,6 +60,7 @@ uintptr_t gpu_vk_get_queue(uint32_t* queueFamilyIndex, uint32_t* queueIndex);
X(xrDestroySpace)\
X(xrEnumerateViewConfigurations)\
X(xrEnumerateViewConfigurationViews)\
X(xrEnumerateSwapchainFormats)\
X(xrCreateSwapchain)\
X(xrDestroySwapchain)\
X(xrEnumerateSwapchainImages)\
@ -133,6 +134,8 @@ enum {
MAX_ACTIONS
};
enum { COLOR, DEPTH };
static struct {
HeadsetConfig config;
XrInstance instance;
@ -142,15 +145,16 @@ static struct {
XrSpace referenceSpace;
XrReferenceSpaceType referenceSpaceType;
XrSpace spaces[MAX_DEVICES];
XrSwapchain swapchain;
XrSwapchain swapchain[2];
XrCompositionLayerProjection layers[1];
XrCompositionLayerProjectionView layerViews[2];
XrCompositionLayerDepthInfoKHR depthInfo[2];
XrFrameState frameState;
Texture* textures[MAX_IMAGES];
Texture* textures[2][MAX_IMAGES];
Pass* pass;
double lastDisplayTime;
uint32_t textureIndex;
uint32_t textureCount;
uint32_t textureIndex[2];
uint32_t textureCount[2];
uint32_t width;
uint32_t height;
float clipNear;
@ -162,6 +166,7 @@ static struct {
XrPath actionFilters[MAX_DEVICES];
XrHandTrackerEXT handTrackers[2];
struct {
bool depth;
bool gaze;
bool handTracking;
bool handTrackingAim;
@ -319,27 +324,29 @@ static bool openxr_init(HeadsetConfig* config) {
bool androidCreateInstanceExtension = false;
#endif
// Extensions without a feature are required
struct { const char* name; bool* feature; bool disable; } extensions[] = {
// 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 __ANDROID__
{ "XR_KHR_android_create_instance", &androidCreateInstanceExtension, false },
{ "XR_KHR_android_create_instance", &androidCreateInstanceExtension, true },
#endif
#ifdef LOVR_VK
{ "XR_KHR_vulkan_enable2", NULL, false },
{ "XR_KHR_vulkan_enable2", NULL, true },
#endif
{ "XR_EXT_eye_gaze_interaction", &state.features.gaze, false },
{ "XR_EXT_hand_tracking", &state.features.handTracking, false },
{ "XR_FB_display_refresh_rate", &state.features.refreshRate, false },
{ "XR_FB_hand_tracking_aim", &state.features.handTrackingAim, false },
{ "XR_FB_hand_tracking_mesh", &state.features.handTrackingMesh, false },
{ "XR_EXTX_overlay", &state.features.overlay, !config->overlay },
{ "XR_HTCX_vive_tracker_interaction", &state.features.viveTrackers, false },
{ "XR_KHR_composition_layer_depth", &state.features.depth, config->submitDepth },
{ "XR_EXT_eye_gaze_interaction", &state.features.gaze, true },
{ "XR_EXT_hand_tracking", &state.features.handTracking, true },
{ "XR_FB_display_refresh_rate", &state.features.refreshRate, true },
{ "XR_FB_hand_tracking_aim", &state.features.handTrackingAim, true },
{ "XR_FB_hand_tracking_mesh", &state.features.handTrackingMesh, true },
{ "XR_EXTX_overlay", &state.features.overlay, config->overlay },
{ "XR_HTCX_vive_tracker_interaction", &state.features.viveTrackers, true }
};
uint32_t enabledExtensionCount = 0;
const char* enabledExtensionNames[COUNTOF(extensions)];
for (uint32_t i = 0; i < COUNTOF(extensions); i++) {
if (extensions[i].disable) continue;
if (!extensions[i].enable) continue;
if (!extensions[i].feature || hasExtension(extensionProperties, extensionCount, extensions[i].name)) {
enabledExtensionNames[enabledExtensionCount++] = extensions[i].name;
if (extensions[i].feature) *extensions[i].feature = true;
@ -885,17 +892,41 @@ static void openxr_start(void) {
{ // Swapchain
#ifdef LOVR_VK
XrSwapchainImageVulkanKHR images[MAX_IMAGES];
XrSwapchainImageVulkanKHR images[2][MAX_IMAGES];
for (uint32_t i = 0; i < MAX_IMAGES; i++) {
images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;
images[i].next = NULL;
images[COLOR][i].type = images[DEPTH][i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;
images[COLOR][i].next = images[DEPTH][i].next = NULL;
}
int64_t colorFormat = VK_FORMAT_R8G8B8A8_SRGB;
int64_t depthFormat = state.config.stencil ? VK_FORMAT_D32_SFLOAT_S8_UINT : VK_FORMAT_D32_SFLOAT;
#endif
int64_t formats[32];
uint32_t formatCount;
XR(xrEnumerateSwapchainFormats(state.session, COUNTOF(formats), &formatCount, formats));
bool supportsColor = false;
bool supportsDepth = false;
for (uint32_t i = 0; i < formatCount && !supportsColor && !supportsDepth; i++) {
if (formats[i] == colorFormat) {
supportsColor = true;
} else if (formats[i] == depthFormat) {
supportsDepth = true;
}
}
lovrAssert(supportsColor, "This VR runtime does not support sRGB rgba8 textures");
if (!supportsDepth) {
state.features.depth = false;
}
XrSwapchainCreateInfo info = {
.type = XR_TYPE_SWAPCHAIN_CREATE_INFO,
.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT,
.format = VK_FORMAT_R8G8B8A8_SRGB,
.format = colorFormat,
.width = state.width,
.height = state.height,
.sampleCount = 1,
@ -904,11 +935,11 @@ static void openxr_start(void) {
.mipCount = 1
};
XR(xrCreateSwapchain(state.session, &info, &state.swapchain));
XR(xrEnumerateSwapchainImages(state.swapchain, MAX_IMAGES, &state.textureCount, (XrSwapchainImageBaseHeader*) images));
XR(xrCreateSwapchain(state.session, &info, &state.swapchain[COLOR]));
XR(xrEnumerateSwapchainImages(state.swapchain[COLOR], MAX_IMAGES, &state.textureCount[COLOR], (XrSwapchainImageBaseHeader*) images));
for (uint32_t i = 0; i < state.textureCount; i++) {
state.textures[i] = lovrTextureCreate(&(TextureInfo) {
for (uint32_t i = 0; i < state.textureCount[COLOR]; i++) {
state.textures[COLOR][i] = lovrTextureCreate(&(TextureInfo) {
.type = TEXTURE_ARRAY,
.format = FORMAT_RGBA8,
.srgb = true,
@ -918,17 +949,44 @@ static void openxr_start(void) {
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_RENDER | TEXTURE_SAMPLE,
.handle = (uintptr_t) images[i].image,
.handle = (uintptr_t) images[COLOR][i].image,
.label = "OpenXR Color Swapchain",
.xr = true
});
}
if (state.features.depth) {
info.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
info.format = depthFormat;
XR(xrCreateSwapchain(state.session, &info, &state.swapchain[DEPTH]));
XR(xrEnumerateSwapchainImages(state.swapchain[DEPTH], MAX_IMAGES, &state.textureCount[DEPTH], (XrSwapchainImageBaseHeader*) images));
for (uint32_t i = 0; i < state.textureCount[DEPTH]; i++) {
state.textures[DEPTH][i] = lovrTextureCreate(&(TextureInfo) {
.type = TEXTURE_ARRAY,
.format = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F,
.srgb = true,
.width = state.width,
.height = state.height,
.layers = 2,
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_RENDER | TEXTURE_SAMPLE,
.handle = (uintptr_t) images[DEPTH][i].image,
.label = "OpenXR Depth Swapchain",
.xr = true
});
}
}
state.pass = lovrPassCreate(&(PassInfo) {
.type = PASS_RENDER,
.canvas.count = 1,
.canvas.textures[0] = state.textures[0],
.canvas.textures[0] = state.textures[COLOR][0],
.canvas.loads[0] = LOAD_CLEAR,
.canvas.clears[0] = { 0.f, 0.f, 0.f, 0.f },
.canvas.depth.texture = state.textures[DEPTH][0],
.canvas.depth.format = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F,
.canvas.depth.load = LOAD_CLEAR,
.canvas.depth.clear = 0.f,
@ -953,19 +1011,35 @@ static void openxr_start(void) {
// Pre-init composition layer views
state.layerViews[0] = (XrCompositionLayerProjectionView) {
.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW,
.subImage = { state.swapchain, { { 0, 0 }, { state.width, state.height } }, 0 }
.subImage = { state.swapchain[COLOR], { { 0, 0 }, { state.width, state.height } }, 0 }
};
state.layerViews[1] = (XrCompositionLayerProjectionView) {
.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW,
.subImage = { state.swapchain, { { 0, 0 }, { state.width, state.height } }, 1 }
.subImage = { state.swapchain[COLOR], { { 0, 0 }, { state.width, state.height } }, 1 }
};
if (state.features.depth) {
for (uint32_t i = 0; i < 2; i++) {
state.layerViews[i].next = &state.depthInfo[i];
state.depthInfo[i] = (XrCompositionLayerDepthInfoKHR) {
.type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR,
.subImage.swapchain = state.swapchain[DEPTH],
.subImage.imageRect = state.layerViews[i].subImage.imageRect,
.subImage.imageArrayIndex = i,
.minDepth = 0.f,
.maxDepth = 1.f
};
}
}
}
}
static void openxr_destroy(void) {
for (uint32_t i = 0; i < state.textureCount; i++) {
lovrRelease(state.textures[i], lovrTextureDestroy);
for (uint32_t i = 0; i < 2; i++) {
for (uint32_t j = 0; j < state.textureCount[i]; j++) {
lovrRelease(state.textures[i][j], lovrTextureDestroy);
}
}
lovrRelease(state.pass, lovrPassDestroy);
@ -985,7 +1059,8 @@ static void openxr_destroy(void) {
if (state.handTrackers[0]) xrDestroyHandTrackerEXT(state.handTrackers[0]);
if (state.handTrackers[1]) xrDestroyHandTrackerEXT(state.handTrackers[1]);
if (state.actionSet) xrDestroyActionSet(state.actionSet);
if (state.swapchain) xrDestroySwapchain(state.swapchain);
if (state.swapchain[COLOR]) xrDestroySwapchain(state.swapchain[COLOR]);
if (state.swapchain[DEPTH]) xrDestroySwapchain(state.swapchain[DEPTH]);
if (state.referenceSpace) xrDestroySpace(state.referenceSpace);
if (state.session) xrDestroySession(state.session);
if (state.instance) xrDestroyInstance(state.instance);
@ -1673,7 +1748,7 @@ static Texture* openxr_getTexture(void) {
}
if (state.began) {
return state.frameState.shouldRender ? state.textures[state.textureIndex] : NULL;
return state.frameState.shouldRender ? state.textures[COLOR][state.textureIndex[COLOR]] : NULL;
}
XrFrameBeginInfo beginfo = { .type = XR_TYPE_FRAME_BEGIN_INFO };
@ -1685,10 +1760,44 @@ static Texture* openxr_getTexture(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));
XR(xrAcquireSwapchainImage(state.swapchain[COLOR], NULL, &state.textureIndex[COLOR]));
XR(xrWaitSwapchainImage(state.swapchain[COLOR], &waitInfo));
return state.textures[state.textureIndex];
if (state.features.depth) {
XR(xrAcquireSwapchainImage(state.swapchain[DEPTH], NULL, &state.textureIndex[DEPTH]));
XR(xrWaitSwapchainImage(state.swapchain[DEPTH], &waitInfo));
}
return state.textures[COLOR][state.textureIndex[COLOR]];
}
static Texture* openxr_getDepthTexture(void) {
if (!SESSION_ACTIVE(state.sessionState) || !state.features.depth) {
return NULL;
}
if (state.began) {
return state.frameState.shouldRender ? state.textures[DEPTH][state.textureIndex[DEPTH]] : NULL;
}
XrFrameBeginInfo beginfo = { .type = XR_TYPE_FRAME_BEGIN_INFO };
XR(xrBeginFrame(state.session, &beginfo));
state.began = true;
if (!state.frameState.shouldRender) {
return NULL;
}
XrSwapchainImageWaitInfo waitInfo = { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, .timeout = XR_INFINITE_DURATION };
XR(xrAcquireSwapchainImage(state.swapchain[COLOR], NULL, &state.textureIndex[COLOR]));
XR(xrWaitSwapchainImage(state.swapchain[COLOR], &waitInfo));
if (state.features.depth) {
XR(xrAcquireSwapchainImage(state.swapchain[DEPTH], NULL, &state.textureIndex[DEPTH]));
XR(xrWaitSwapchainImage(state.swapchain[DEPTH], &waitInfo));
}
return state.textures[DEPTH][state.textureIndex[DEPTH]];
}
static Pass* openxr_getPass(void) {
@ -1697,6 +1806,7 @@ static Pass* openxr_getPass(void) {
}
Texture* texture = openxr_getTexture();
Texture* depthTexture = openxr_getDepthTexture();
if (!texture) {
return NULL;
@ -1707,7 +1817,7 @@ static Pass* openxr_getPass(void) {
lovrPassGetClear(state.pass, color, &depth, &stencil);
lovrGraphicsGetBackground(color[0]);
lovrPassSetClear(state.pass, color, depth, stencil);
lovrPassSetTarget(state.pass, &texture, NULL);
lovrPassSetTarget(state.pass, &texture, depthTexture);
lovrPassReset(state.pass);
uint32_t count;
@ -1749,8 +1859,23 @@ static void openxr_submit(void) {
}
};
if (state.features.depth) {
if (state.clipFar == 0.f) {
state.depthInfo[0].nearZ = state.depthInfo[1].nearZ = +INFINITY;
state.depthInfo[0].farZ = state.depthInfo[1].farZ = state.clipNear;
} else {
state.depthInfo[0].nearZ = state.depthInfo[1].nearZ = state.clipNear;
state.depthInfo[0].farZ = state.depthInfo[1].farZ = state.clipFar;
}
}
if (state.frameState.shouldRender) {
XR(xrReleaseSwapchainImage(state.swapchain, NULL));
XR(xrReleaseSwapchainImage(state.swapchain[COLOR], NULL));
if (state.features.depth) {
XR(xrReleaseSwapchainImage(state.swapchain[DEPTH], NULL));
}
info.layerCount = 1;
}