Skip to content
Snippets Groups Projects
Commit 4c45bb13 authored by Jens Koenen's avatar Jens Koenen
Browse files

Merge remote-tracking branch 'remotes/origin/develop' into feature/remote-controllers

parents 8ab32c6b 93d92eb1
No related branches found
No related tags found
No related merge requests found
......@@ -51,3 +51,6 @@
[submodule "ext/asio"]
path = ext/asio
url = https://github.com/chriskohlhoff/asio.git
[submodule "ext/openxr"]
path = ext/openxr
url = https://github.com/KhronosGroup/OpenXR-SDK.git
......@@ -28,6 +28,9 @@ message("> module")
# Enable Vulkan Beta Extension needed for Vulkan Video
add_compile_definitions(VK_ENABLE_BETA_EXTENSIONS)
# Use OpenXR together with Vulkan
add_compile_definitions(XR_USE_GRAPHICS_API_VULKAN)
set(LIBLAVA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/liblava)
set(LIBLAVA_EXT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext)
set(LIBLAVA_RES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/res)
......@@ -384,6 +387,13 @@ endif ()
message("<<< openvr")
add_subdirectory(${LIBLAVA_EXT_DIR}/openxr openxr)
message(">>> openxr")
message("<<< openxr")
message(">>> assimp")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
......@@ -682,7 +692,8 @@ message("=======================================================================
#Headset
${SRC_DIR}/headset/headset.hpp ${SRC_DIR}/headset/headset.cpp
${SRC_DIR}/headset/emulated_headset.hpp ${SRC_DIR}/headset/emulated_headset.cpp
${SRC_DIR}/headset/local_headset.hpp ${SRC_DIR}/headset/local_headset.cpp
${SRC_DIR}/headset/openvr_headset.hpp ${SRC_DIR}/headset/openvr_headset.cpp
${SRC_DIR}/headset/openxr_headset.hpp ${SRC_DIR}/headset/openxr_headset.cpp
${SRC_DIR}/headset/remote_headset.hpp ${SRC_DIR}/headset/remote_headset.cpp
#Strategy
......@@ -761,6 +772,7 @@ message("=======================================================================
else ()
target_link_libraries(${NAME} lava::app openvr_api)
endif ()
target_link_libraries(${NAME} lava::app openxr_loader)
target_link_libraries(${NAME} lava::app assimp::assimp)
target_include_directories(${NAME} PUBLIC ${OPENVR_HEADER_DIR})
target_link_libraries(${NAME} "${CMAKE_SOURCE_DIR}/ext/openvr/lib/win64/openvr_api.lib")
......
Subproject commit 1ca7bec6b531185530c9b4f1e7a50e1fd55e7641
#include "headset.hpp"
#include "emulated_headset.hpp"
#include "local_headset.hpp"
#include "openvr_headset.hpp"
#include "openxr_headset.hpp"
#include "remote_headset.hpp"
void Headset::set_application(VRApplication* application)
......@@ -42,8 +43,11 @@ Headset::Ptr make_headset(VRApplication* application, HeadsetType headset_type)
case HEADSET_TYPE_EMULATED:
headset = std::make_shared<EmulatedHeadset>();
break;
case HEADSET_TYPE_LOCAL:
headset = std::make_shared<LocalHeadset>();
case HEADSET_TYPE_OPEN_VR:
headset = std::make_shared<OpenVRHeadset>();
break;
case HEADSET_TYPE_OPEN_XR:
headset = std::make_shared<OpenXRHeadset>();
break;
case HEADSET_TYPE_REMOTE:
headset = std::make_shared<RemoteHeadset>();
......
......@@ -32,7 +32,8 @@ enum Eye
enum HeadsetType
{
HEADSET_TYPE_EMULATED,
HEADSET_TYPE_LOCAL,
HEADSET_TYPE_OPEN_VR,
HEADSET_TYPE_OPEN_XR,
HEADSET_TYPE_REMOTE
};
......
#include "local_headset.hpp"
#include "openvr_headset.hpp"
#include "vr_application.hpp"
#include <iostream>
......@@ -6,7 +6,7 @@
#include <glm/gtx/matrix_operation.hpp>
#include <imgui.h>
bool LocalHeadset::on_setup_instance(lava::frame_config& config)
bool OpenVRHeadset::on_setup_instance(lava::frame_config& config)
{
vr::EVRInitError error;
this->system = VR_Init(&error, vr::EVRApplicationType::VRApplication_Scene);
......@@ -34,7 +34,7 @@ bool LocalHeadset::on_setup_instance(lava::frame_config& config)
return true;
}
bool LocalHeadset::on_setup_device(lava::device::create_param& parameters)
bool OpenVRHeadset::on_setup_device(lava::device::create_param& parameters)
{
uint32_t extension_count = vr::VRCompositor()->GetVulkanDeviceExtensionsRequired(parameters.physical_device->get(), nullptr, 0);
std::string extension_string(extension_count, '\0');
......@@ -50,7 +50,7 @@ bool LocalHeadset::on_setup_device(lava::device::create_param& parameters)
return true;
}
bool LocalHeadset::on_create()
bool OpenVRHeadset::on_create()
{
this->system->GetRecommendedRenderTargetSize(&this->resolution.x, &this->resolution.y);
......@@ -70,7 +70,7 @@ bool LocalHeadset::on_create()
return true;
}
void LocalHeadset::on_destroy()
void OpenVRHeadset::on_destroy()
{
this->destroy_framebuffer(EYE_LEFT);
this->destroy_framebuffer(EYE_RIGHT);
......@@ -78,7 +78,7 @@ void LocalHeadset::on_destroy()
vr::VR_Shutdown();
}
bool LocalHeadset::on_interface()
bool OpenVRHeadset::on_interface()
{
if (ImGui::DragFloat("Near Plane", &this->near_plane))
{
......@@ -95,7 +95,7 @@ bool LocalHeadset::on_interface()
return true;
}
bool LocalHeadset::on_update(lava::delta delta_time)
bool OpenVRHeadset::on_update(lava::delta delta_time)
{
vr::VREvent_t event;
......@@ -126,7 +126,7 @@ bool LocalHeadset::on_update(lava::delta delta_time)
return true;
}
void LocalHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout frame_layout, FrameId frame_id)
void OpenVRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout frame_layout, FrameId frame_id)
{
lava::image::ptr frame_image = this->get_framebuffer(frame_id);
......@@ -167,42 +167,42 @@ void LocalHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout fr
}
}
VkFormat LocalHeadset::get_format() const
VkFormat OpenVRHeadset::get_format() const
{
return VK_FORMAT_R8G8B8A8_SRGB;
}
const glm::uvec2& LocalHeadset::get_resolution() const
const glm::uvec2& OpenVRHeadset::get_resolution() const
{
return this->resolution;
}
const glm::mat4& LocalHeadset::get_view_matrix() const
const glm::mat4& OpenVRHeadset::get_view_matrix() const
{
return this->view_matrix;
}
const glm::mat4& LocalHeadset::get_head_to_eye_matrix(Eye eye) const
const glm::mat4& OpenVRHeadset::get_head_to_eye_matrix(Eye eye) const
{
return this->head_to_eye_matrices[eye];
}
const glm::mat4& LocalHeadset::get_projection_matrix(Eye eye) const
const glm::mat4& OpenVRHeadset::get_projection_matrix(Eye eye) const
{
return this->projection_matrices[eye];
}
lava::image::ptr LocalHeadset::get_framebuffer(FrameId frame_id) const
lava::image::ptr OpenVRHeadset::get_framebuffer(FrameId frame_id) const
{
return this->framebuffers[frame_id];
}
const char* LocalHeadset::get_name() const
const char* OpenVRHeadset::get_name() const
{
return "Local Headset";
return "OpenVR Headset";
}
bool LocalHeadset::create_framebuffer(Eye eye)
bool OpenVRHeadset::create_framebuffer(Eye eye)
{
lava::image::ptr framebuffer = lava::make_image(this->get_format());
framebuffer->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
......@@ -217,13 +217,13 @@ bool LocalHeadset::create_framebuffer(Eye eye)
return true;
}
void LocalHeadset::destroy_framebuffer(Eye eye)
void OpenVRHeadset::destroy_framebuffer(Eye eye)
{
this->framebuffers[eye]->destroy();
this->framebuffers[eye] = nullptr;
}
void LocalHeadset::compute_matrices(Eye eye)
void OpenVRHeadset::compute_matrices(Eye eye)
{
vr::HmdMatrix44_t projection_matrix = this->system->GetProjectionMatrix((vr::EVREye)eye, this->near_plane, this->far_plane);
vr::HmdMatrix34_t eye_to_head_matrix = this->system->GetEyeToHeadTransform((vr::EVREye)eye);
......@@ -232,7 +232,7 @@ void LocalHeadset::compute_matrices(Eye eye)
this->head_to_eye_matrices[eye] = glm::mat4(glm::transpose(glm::make_mat3x4(&eye_to_head_matrix.m[0][0])));
}
std::vector<std::string> LocalHeadset::split_string(std::string string, char delimiter)
std::vector<std::string> OpenVRHeadset::split_string(std::string string, char delimiter)
{
std::vector<std::string> strings;
......
......@@ -9,13 +9,13 @@
#include "headset.hpp"
class LocalHeadset : public Headset
class OpenVRHeadset : public Headset
{
public:
typedef std::shared_ptr<LocalHeadset> Ptr;
typedef std::shared_ptr<OpenVRHeadset> Ptr;
public:
LocalHeadset() = default;
OpenVRHeadset() = default;
bool on_setup_instance(lava::frame_config& config) override;
bool on_setup_device(lava::device::create_param& parameters) override;
......
#include "openxr_headset.hpp"
#include "vr_application.hpp"
#include <iostream>
#include <imgui.h>
#include <glm/gtx/matrix_operation.hpp>
PFN_xrGetVulkanGraphicsRequirementsKHR xrGetVulkanGraphicsRequirementsKHR = nullptr;
PFN_xrGetVulkanInstanceExtensionsKHR xrGetVulkanInstanceExtensionsKHR = nullptr;
PFN_xrGetVulkanDeviceExtensionsKHR xrGetVulkanDeviceExtensionsKHR = nullptr;
PFN_xrGetVulkanGraphicsDeviceKHR xrGetVulkanGraphicsDeviceKHR = nullptr;
OpenXRHeadset::OpenXRHeadset()
{
memset(&this->swapchain_indices, 0, sizeof(this->swapchain_indices));
}
bool OpenXRHeadset::on_setup_instance(lava::frame_config& config)
{
std::vector<const char*> required_layers =
{
};
std::vector<const char*> required_extensions =
{
XR_KHR_VULKAN_ENABLE_EXTENSION_NAME
};
if (!this->check_layer_support(required_layers))
{
return false;
}
if (!this->check_extension_support(required_extensions))
{
return false;
}
XrApplicationInfo application_info;
strncpy(application_info.applicationName, config.info.app_name, XR_MAX_APPLICATION_NAME_SIZE);
application_info.applicationVersion = XR_MAKE_VERSION(config.info.app_version.major, config.info.app_version.minor, config.info.app_version.patch);
strncpy(application_info.engineName, config.info.engine_name, XR_MAX_ENGINE_NAME_SIZE);
application_info.engineVersion = XR_MAKE_VERSION(config.info.engine_version.major, config.info.engine_version.minor, config.info.engine_version.patch);
application_info.apiVersion = XR_CURRENT_API_VERSION;
XrInstanceCreateInfo create_info;
create_info.type = XR_TYPE_INSTANCE_CREATE_INFO;
create_info.next = nullptr;
create_info.createFlags = 0;
create_info.applicationInfo = application_info;
create_info.enabledApiLayerCount = required_layers.size();
create_info.enabledApiLayerNames = required_layers.data();
create_info.enabledExtensionCount = required_extensions.size();
create_info.enabledExtensionNames = required_extensions.data();
if (!this->check_result(xrCreateInstance(&create_info, &this->instance)))
{
lava::log()->error("OpenXR: Can't create instance!");
return false;
}
XrSystemGetInfo get_info;
get_info.type = XR_TYPE_SYSTEM_GET_INFO;
get_info.next = nullptr;
get_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
if (!this->check_result(xrGetSystem(this->instance, &get_info, &this->system)))
{
lava::log()->error("OpenXR: Can't get system!");
return false;
}
if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&xrGetVulkanGraphicsRequirementsKHR)))
{
lava::log()->error("OpenXR: Can't get function pointer for function 'xrGetVulkanGraphicsRequirementsKHR' !");
return false;
}
if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanInstanceExtensionsKHR", (PFN_xrVoidFunction*)&xrGetVulkanInstanceExtensionsKHR)))
{
lava::log()->error("OpenXR: Can't get function pointer for function 'xrGetVulkanInstanceExtensionsKHR' !");
return false;
}
if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanDeviceExtensionsKHR", (PFN_xrVoidFunction*)&xrGetVulkanDeviceExtensionsKHR)))
{
lava::log()->error("OpenXR: Can't get function pointer for function 'xrGetVulkanDeviceExtensionsKHR' !");
return false;
}
if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanGraphicsDeviceKHR", (PFN_xrVoidFunction*)&xrGetVulkanGraphicsDeviceKHR)))
{
lava::log()->error("OpenXR: Can't get function pointer for function 'xrGetVulkanGraphicsDeviceKHR' !");
return false;
}
XrGraphicsRequirementsVulkanKHR requirements;
memset(&requirements, 0, sizeof(requirements));
requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR;
if(!this->check_result(xrGetVulkanGraphicsRequirementsKHR(this->instance, this->system, &requirements)))
{
lava::log()->error("OpenXR: Can't get Vulkan requirements!");
return false;
}
uint32_t extension_count = 0;
if (!this->check_result(xrGetVulkanInstanceExtensionsKHR(this->instance, this->system, 0, &extension_count, nullptr)))
{
lava::log()->error("OpenXR: Can't get Vulkan instance extension count!");
return false;
}
std::string extension_string(extension_count, '\0');
if (!this->check_result(xrGetVulkanInstanceExtensionsKHR(this->instance, this->system, extension_count, &extension_count, extension_string.data())))
{
lava::log()->error("OpenXR: Can't get Vulkan instance extension list!");
return false;
}
this->instance_extensions = OpenXRHeadset::split_string(extension_string);
for (const std::string& extension : this->instance_extensions)
{
config.param.extensions.push_back(extension.c_str());
}
switch (XR_VERSION_MINOR(requirements.maxApiVersionSupported))
{
case 0:
config.info.req_api_version = lava::api_version::v1_0;
break;
case 1:
config.info.req_api_version = lava::api_version::v1_1;
break;
case 2:
config.info.req_api_version = lava::api_version::v1_2;
break;
default:
lava::log()->error("OpenXR: Unkown Vulkan version!");
return false;
}
return true;
}
bool OpenXRHeadset::on_setup_device(lava::device::create_param& parameters)
{
VkPhysicalDevice physical_device = VK_NULL_HANDLE;
if(!this->check_result(xrGetVulkanGraphicsDeviceKHR(this->instance, this->system, this->get_application()->get_instance().get(), &physical_device)))
{
if (physical_device != parameters.physical_device->get())
{
lava::log()->error("OpenXR: Incorrect physical device selected!");
return false;
}
}
uint32_t extension_count = 0;
if (!this->check_result(xrGetVulkanDeviceExtensionsKHR(this->instance, this->system, 0, &extension_count, nullptr)))
{
lava::log()->error("OpenXR: Can't get Vulkan device extension count!");
return false;
}
std::string extension_string(extension_count, '\0');
if (!this->check_result(xrGetVulkanDeviceExtensionsKHR(this->instance, this->system, extension_count, &extension_count, extension_string.data())))
{
lava::log()->error("OpenXR: Can't get Vulkan device extension list!");
return false;
}
this->device_extensions = OpenXRHeadset::split_string(extension_string);
for (const std::string& extension : this->device_extensions)
{
parameters.extensions.push_back(extension.c_str());
}
return true;
}
bool OpenXRHeadset::on_create()
{
XrGraphicsBindingVulkanKHR vulkan_info;
vulkan_info.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR;
vulkan_info.next = nullptr;
vulkan_info.instance = this->get_application()->get_instance().get();
vulkan_info.physicalDevice = this->get_application()->get_device()->get_physical_device()->get();
vulkan_info.device = this->get_application()->get_device()->get();
vulkan_info.queueFamilyIndex = this->get_application()->get_device()->get_graphics_queue().family;
vulkan_info.queueIndex = 0;
XrSessionCreateInfo create_info;
create_info.type = XR_TYPE_SESSION_CREATE_INFO;
create_info.next = &vulkan_info;
create_info.createFlags = 0;
create_info.systemId = this->system;
if(!this->check_result(xrCreateSession(this->instance, &create_info, &this->session)))
{
lava::log()->error("OpenXR: Can't create session!");
return false;
}
XrReferenceSpaceCreateInfo space_info;
space_info.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
space_info.next = nullptr;
// OPTION:
// XR_REFERENCE_SPACE_TYPE_LOCAL: The origin of the world coordinate frame is the point that was defined during the calibration of the headset.
// XR_REFERENCE_SPACE_TYPE_STAGE: The origin of the world coordinate frame is the ground point that was defined during the calibration of the environment.
// For more information see:
// https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrReferenceSpaceType
space_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
memset(&space_info.poseInReferenceSpace, 0, sizeof(space_info.poseInReferenceSpace));
space_info.poseInReferenceSpace.orientation.w = 1.0f;
if(!this->check_result(xrCreateReferenceSpace(this->session, &space_info, &this->space)))
{
lava::log()->error("OpenXR: Can't create reference space!");
return false;
}
if (!this->check_swapchain_format_support(this->get_format()))
{
return false;
}
if (!this->check_view_type_support(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO))
{
return false;
}
if (!this->check_blend_mode_support(XR_ENVIRONMENT_BLEND_MODE_OPAQUE))
{
return false;
}
if (!this->create_swapchains())
{
return false;
}
if (!this->create_framebuffers())
{
return false;
}
return true;
}
void OpenXRHeadset::on_destroy()
{
this->destroy_framebuffers();
this->destroy_swapchains();
xrDestroySpace(this->space);
xrDestroySession(this->session);
xrDestroyInstance(this->instance);
}
bool OpenXRHeadset::on_interface()
{
ImGui::DragFloat("Near Plane", &this->near_plane);
ImGui::DragFloat("Far Plane", &this->far_plane);
return true;
}
bool OpenXRHeadset::on_update(lava::delta delta_time)
{
if (!this->poll_events())
{
return false;
}
if (this->active)
{
if (!this->begin_frame())
{
return false;
}
if (!this->update_views())
{
return false;
}
this->submit_count = 0;
}
return true;
}
void OpenXRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout frame_layout, FrameId frame_id)
{
if (this->active)
{
this->acquire_image(frame_id);
lava::image::ptr frame_image = this->get_framebuffer(frame_id);
uint32_t swapchain_index = this->swapchain_indices[frame_id];
VkImage swapchain_image = this->swapchain_images[frame_id][swapchain_index];
std::vector<VkImageMemoryBarrier> image_barriers;
if (frame_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
VkImageMemoryBarrier frame_barrier = lava::image_memory_barrier(frame_image->get(), frame_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
frame_barrier.srcAccessMask = 0;
frame_barrier.dstAccessMask = 0;
frame_barrier.subresourceRange = frame_image->get_subresource_range();
image_barriers.push_back(frame_barrier);
}
VkImageMemoryBarrier swapchain_begin_barrier = lava::image_memory_barrier(swapchain_image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
swapchain_begin_barrier.srcAccessMask = 0;
swapchain_begin_barrier.dstAccessMask = 0;
swapchain_begin_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
swapchain_begin_barrier.subresourceRange.baseMipLevel = 0;
swapchain_begin_barrier.subresourceRange.levelCount = 1;
swapchain_begin_barrier.subresourceRange.baseArrayLayer = 0;
swapchain_begin_barrier.subresourceRange.layerCount = 1;
image_barriers.push_back(swapchain_begin_barrier);
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, image_barriers.size(), image_barriers.data());
VkImageCopy copy_region;
copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_region.srcSubresource.mipLevel = 0;
copy_region.srcSubresource.baseArrayLayer = 0;
copy_region.srcSubresource.layerCount = 1;
copy_region.srcOffset.x = 0;
copy_region.srcOffset.y = 0;
copy_region.srcOffset.z = 0;
copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_region.dstSubresource.mipLevel = 0;
copy_region.dstSubresource.baseArrayLayer = 0;
copy_region.dstSubresource.layerCount = 1;
copy_region.dstOffset.x = 0;
copy_region.dstOffset.y = 0;
copy_region.dstOffset.z = 0;
copy_region.extent.width = this->resolution.x;
copy_region.extent.height = this->resolution.y;
copy_region.extent.depth = 1;
vkCmdCopyImage(command_buffer, frame_image->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
VkImageMemoryBarrier swapchain_end_barrier = lava::image_memory_barrier(swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
swapchain_end_barrier.srcAccessMask = 0;
swapchain_end_barrier.dstAccessMask = 0;
swapchain_end_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
swapchain_end_barrier.subresourceRange.baseMipLevel = 0;
swapchain_end_barrier.subresourceRange.levelCount = 1;
swapchain_end_barrier.subresourceRange.baseArrayLayer = 0;
swapchain_end_barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &swapchain_end_barrier);
this->release_image(frame_id);
this->submit_count++;
if (this->submit_count == this->views.size())
{
this->end_frame();
}
}
}
VkFormat OpenXRHeadset::get_format() const
{
return VK_FORMAT_R8G8B8A8_SRGB;
}
const glm::uvec2& OpenXRHeadset::get_resolution() const
{
return this->resolution;
}
const glm::mat4& OpenXRHeadset::get_view_matrix() const
{
return this->view_matrix;
}
const glm::mat4& OpenXRHeadset::get_head_to_eye_matrix(Eye eye) const
{
return this->head_to_eye_matrices[eye];
}
const glm::mat4& OpenXRHeadset::get_projection_matrix(Eye eye) const
{
return this->projection_matrices[eye];
}
lava::image::ptr OpenXRHeadset::get_framebuffer(FrameId frame_id) const
{
return this->framebuffers[frame_id];
}
const char* OpenXRHeadset::get_name() const
{
return "OpenXR Headset";
}
bool OpenXRHeadset::create_framebuffers()
{
for (uint32_t index = 0; index < this->framebuffers.size(); index++)
{
lava::image::ptr framebuffer = lava::make_image(this->get_format());
framebuffer->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
if (!framebuffer->create(this->get_application()->get_device(), this->resolution))
{
lava::log()->error("OpenXR: Can't create framebuffer!");
return false;
}
this->framebuffers[index] = framebuffer;
}
return true;
}
void OpenXRHeadset::destroy_framebuffers()
{
for (uint32_t index = 0; index < this->framebuffers.size(); index++)
{
this->framebuffers[index]->destroy();
}
}
bool OpenXRHeadset::create_swapchains()
{
uint32_t view_count = 0;
if (!this->check_result(xrEnumerateViewConfigurationViews(this->instance, this->system, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &view_count, nullptr)))
{
lava::log()->error("OpenXR: Can't get view count!");
return false;
}
if (view_count != 2)
{
lava::log()->error("OpenXR: Invalid number of views!");
return false;
}
XrViewConfigurationView view_template;
memset(&view_template, 0, sizeof(view_template));
view_template.type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
std::vector<XrViewConfigurationView> view_list(view_count, view_template);
if (!this->check_result(xrEnumerateViewConfigurationViews(this->instance, this->system, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, view_count, &view_count, view_list.data())))
{
lava::log()->error("OpenXR: Can't get view list!");
return false;
}
this->resolution.x = view_list[0].recommendedImageRectWidth;
this->resolution.y = view_list[0].recommendedImageRectHeight;
uint32_t sample_count = view_list[0].recommendedSwapchainSampleCount;
for (uint32_t index = 0; index < view_count; index++)
{
XrSwapchainCreateInfo create_info;
create_info.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
create_info.next = nullptr;
create_info.createFlags = 0;
create_info.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_TRANSFER_SRC_BIT | XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT;
create_info.format = this->get_format();
create_info.sampleCount = sample_count;
create_info.width = this->resolution.x;
create_info.height = this->resolution.y;
create_info.faceCount = 1;
create_info.arraySize = 1;
create_info.mipCount = 1;
XrSwapchain swapchain = XR_NULL_HANDLE;
if (!this->check_result(xrCreateSwapchain(this->session, &create_info, &swapchain)))
{
lava::log()->error("OpenXR: Can't create swapchain!");
return false;
}
uint32_t image_count = 0;
if (!this->check_result(xrEnumerateSwapchainImages(swapchain, 0, &image_count, nullptr)))
{
lava::log()->error("OpenXR: Can't get swapchain image count!");
return false;
}
XrSwapchainImageVulkanKHR image_template;
memset(&image_template, 0, sizeof(image_template));
image_template.type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;
std::vector<XrSwapchainImageVulkanKHR> image_list(image_count, image_template);
if (!this->check_result(xrEnumerateSwapchainImages(swapchain, image_count, &image_count, (XrSwapchainImageBaseHeader*)image_list.data())))
{
lava::log()->error("OpenXR: Can't get swapchain image list!");
return false;
}
for (const XrSwapchainImageVulkanKHR& swapchain_image : image_list)
{
this->swapchain_images[index].push_back(swapchain_image.image);
}
this->swapchains.push_back(swapchain);
}
return true;
}
void OpenXRHeadset::destroy_swapchains()
{
for (uint32_t index = 0; index < this->swapchains.size(); index++)
{
xrDestroySwapchain(this->swapchains[index]);
}
this->swapchains.clear();
}
bool OpenXRHeadset::poll_events()
{
while (true)
{
XrEventDataBuffer event;
memset(&event, 0, sizeof(event));
event.type = XR_TYPE_EVENT_DATA_BUFFER;
XrResult result = xrPollEvent(this->instance, &event);
if (result == XR_EVENT_UNAVAILABLE)
{
break;
}
if (!this->check_result(result))
{
lava::log()->error("OpenXR: Error during polling of events!");
return false;
}
if (!this->process_event(event))
{
return false;
}
}
return true;
}
bool OpenXRHeadset::process_event(const XrEventDataBuffer& event)
{
switch (event.type)
{
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
{
const XrEventDataSessionStateChanged* state_change = (const XrEventDataSessionStateChanged*)&event;
switch (state_change->state)
{
case XR_SESSION_STATE_READY:
{
XrSessionBeginInfo begin_info;
begin_info.type = XR_TYPE_SESSION_BEGIN_INFO;
begin_info.next = nullptr;
begin_info.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
if (!this->check_result(xrBeginSession(this->session, &begin_info)))
{
lava::log()->error("OpenXR: Can't begin session!");
return false;
}
this->active = true;
break;
}
case XR_SESSION_STATE_STOPPING:
if (!this->check_result(xrEndSession(this->session)))
{
lava::log()->error("OpenXR: Can't end session!");
return false;
}
this->active = false;
break;
case XR_SESSION_STATE_EXITING:
case XR_SESSION_STATE_LOSS_PENDING:
return false;
default:
break;
}
break;
}
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
return false;
default:
break;
}
return true;
}
bool OpenXRHeadset::begin_frame()
{
XrFrameWaitInfo wait_info;
wait_info.type = XR_TYPE_FRAME_WAIT_INFO;
wait_info.next = nullptr;
memset(&this->frame_state, 0, sizeof(this->frame_state));
this->frame_state.type = XR_TYPE_FRAME_STATE;
if (!this->check_result(xrWaitFrame(this->session, &wait_info, &this->frame_state)))
{
lava::log()->error("OpenXR: Can't wait for frame!");
return false;
}
XrFrameBeginInfo begin_info;
begin_info.type = XR_TYPE_FRAME_BEGIN_INFO;
begin_info.next = nullptr;
if (!this->check_result(xrBeginFrame(this->session, &begin_info)))
{
lava::log()->error("OpenXR: Can't begin frame!");
return false;
}
return true;
}
bool OpenXRHeadset::update_views()
{
XrViewLocateInfo view_info;
view_info.type = XR_TYPE_VIEW_LOCATE_INFO;
view_info.next = nullptr;
view_info.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
view_info.displayTime = frame_state.predictedDisplayTime;
view_info.space = this->space;
XrViewState view_state;
view_state.type = XR_TYPE_VIEW_STATE;
view_state.next = nullptr;
view_state.viewStateFlags = 0;
uint32_t view_count = 0;
if (!this->check_result(xrLocateViews(this->session, &view_info, &view_state, 0, &view_count, nullptr)))
{
lava::log()->error("OpenXR: Can't get location and orientation of views!");
return false;
}
XrView view_template;
memset(&view_template, 0, sizeof(view_template));
view_template.type = XR_TYPE_VIEW;
view_template.pose.orientation.w = 1.0f;
this->views.resize(view_count, view_template);
if (!this->check_result(xrLocateViews(this->session, &view_info, &view_state, view_count, &view_count, this->views.data())))
{
return false;
}
if ((view_state.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) == 0 || (view_state.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) == 0)
{
return true;
}
if ((view_state.viewStateFlags & XR_VIEW_STATE_ORIENTATION_TRACKED_BIT) == 0 || (view_state.viewStateFlags & XR_VIEW_STATE_POSITION_TRACKED_BIT) == 0)
{
lava::log()->warn("OpenXR: Headset not tracked correctly. Recalibration maybe neccessary!");
return true;
}
this->view_matrix = glm::mat4(1.0f);
for (uint32_t index = 0; index < view_count; index++)
{
const XrView& view = this->views[index];
XrQuaternionf quaternion = view.pose.orientation;
XrVector3f position = view.pose.position;
glm::mat4 pose_transform = (glm::mat4)glm::inverse(glm::quat(quaternion.w, quaternion.x, quaternion.y, quaternion.z));
pose_transform[3] = glm::vec4(-position.x, -position.y, -position.z, 1.0f);
this->head_to_eye_matrices[index] = glm::diagonal4x4(glm::vec4(1.0f, -1.0f, 1.0f, 1.0f)) * pose_transform;
float left = this->near_plane * glm::tan(view.fov.angleLeft);
float right = this->near_plane * glm::tan(view.fov.angleRight);
float bottom = this->near_plane * glm::tan(view.fov.angleDown);
float top = this->near_plane * glm::tan(view.fov.angleUp);
this->projection_matrices[index] = glm::frustum(left, right, bottom, top, this->near_plane, this->far_plane);
}
return true;
}
bool OpenXRHeadset::acquire_image(FrameId frame_id)
{
XrSwapchainImageAcquireInfo acquire_info;
acquire_info.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO;
acquire_info.next = nullptr;
if (!this->check_result(xrAcquireSwapchainImage(this->swapchains[frame_id], &acquire_info, &this->swapchain_indices[frame_id])))
{
lava::log()->error("OpenXR: Can't acquire swapchain image!");
return false;
}
XrSwapchainImageWaitInfo wait_info;
wait_info.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
wait_info.next = nullptr;
wait_info.timeout = XR_INFINITE_DURATION;
if (!this->check_result(xrWaitSwapchainImage(this->swapchains[frame_id], &wait_info)))
{
lava::log()->error("OpenXR: Can't wait for swapchain image!");
return false;
}
return true;
}
bool OpenXRHeadset::release_image(FrameId frame_id)
{
XrSwapchainImageReleaseInfo release_info;
release_info.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
release_info.next = nullptr;
if(!this->check_result(xrReleaseSwapchainImage(this->swapchains[frame_id], &release_info)))
{
lava::log()->error("OpenXR: Can't release swapchain image!");
return false;
}
return true;
}
bool OpenXRHeadset::end_frame()
{
std::vector<XrCompositionLayerProjectionView> projection_views;
for (uint32_t index = 0; index < this->views.size(); index++)
{
const XrView& view = this->views[index];
XrCompositionLayerProjectionView projection_view;
projection_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
projection_view.next = nullptr;
projection_view.pose = view.pose;
projection_view.fov = view.fov;
projection_view.subImage.swapchain = this->swapchains[index];
projection_view.subImage.imageRect.offset.x = 0;
projection_view.subImage.imageRect.offset.y = 0;
projection_view.subImage.imageRect.extent.width = this->resolution.x;
projection_view.subImage.imageRect.extent.height = this->resolution.y;
projection_view.subImage.imageArrayIndex = 0;
projection_views.push_back(projection_view);
}
XrCompositionLayerProjection projection_layer;
projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
projection_layer.next = nullptr;
projection_layer.layerFlags = 0;
projection_layer.space = this->space;
projection_layer.viewCount = projection_views.size();
projection_layer.views = projection_views.data();
XrCompositionLayerBaseHeader* layer_pointer = (XrCompositionLayerBaseHeader*)&projection_layer;
XrFrameEndInfo end_info;
end_info.type = XR_TYPE_FRAME_END_INFO;
end_info.next = nullptr;
end_info.displayTime = this->frame_state.predictedDisplayTime;
end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
end_info.layerCount = 1;
end_info.layers = &layer_pointer;
if(!this->check_result(xrEndFrame(this->session, &end_info)))
{
lava::log()->error("OpenXR: Can't end frame!");
return false;
}
return true;
}
bool OpenXRHeadset::check_result(XrResult result) const
{
if (result != XR_SUCCESS)
{
if (this->instance != XR_NULL_HANDLE)
{
std::array<char, XR_MAX_RESULT_STRING_SIZE> result_string;
xrResultToString(this->instance, result, result_string.data());
lava::log()->error("OpenXR: Error '{}' detected!", result_string.data());
}
else
{
std::cout << "OpenXR: Error detected!" << std::endl;
}
return false;
}
return true;
}
bool OpenXRHeadset::check_layer_support(const std::vector<const char*>& required_layers) const
{
uint32_t layer_count = 0;
if (!this->check_result(xrEnumerateApiLayerProperties(0, &layer_count, nullptr)))
{
return false;
}
XrApiLayerProperties layer_template;
memset(&layer_template, 0, sizeof(layer_template));
layer_template.type = XR_TYPE_API_LAYER_PROPERTIES;
std::vector<XrApiLayerProperties> layer_list(layer_count, layer_template);
if (!this->check_result(xrEnumerateApiLayerProperties(layer_count, &layer_count, layer_list.data())))
{
return false;
}
for (const char* required_layer : required_layers)
{
bool found = false;
for (const XrApiLayerProperties& layer : layer_list)
{
if (strcmp(required_layer, layer.layerName) == 0)
{
found = true;
break;
}
}
if (!found)
{
lava::log()->error("OpenXR: Required layer '{}' not supported!", required_layer);
return false;
}
}
return true;
}
bool OpenXRHeadset::check_extension_support(const std::vector<const char*>& required_extensions) const
{
uint32_t extension_count = 0;
if(!this->check_result(xrEnumerateInstanceExtensionProperties(nullptr, 0, &extension_count, nullptr)))
{
return false;
}
XrExtensionProperties extension_template;
memset(&extension_template, 0, sizeof(extension_template));
extension_template.type = XR_TYPE_EXTENSION_PROPERTIES;
std::vector<XrExtensionProperties> extension_list(extension_count, extension_template);
if(!this->check_result(xrEnumerateInstanceExtensionProperties(nullptr, extension_count, &extension_count, extension_list.data())))
{
return false;
}
for (const char* required_extension : required_extensions)
{
bool found = false;
for (const XrExtensionProperties& extension : extension_list)
{
if (strcmp(required_extension, extension.extensionName) == 0)
{
found = true;
break;
}
}
if (!found)
{
lava::log()->error("OpenXR: Required extension '{}' not supported!", required_extension);
return false;
}
}
return true;
}
bool OpenXRHeadset::check_blend_mode_support(XrEnvironmentBlendMode required_mode) const
{
uint32_t mode_count = 0;
if (!this->check_result(xrEnumerateEnvironmentBlendModes(this->instance, this->system, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &mode_count, nullptr)))
{
return false;
}
std::vector<XrEnvironmentBlendMode> mode_list(mode_count, (XrEnvironmentBlendMode)0);
if (!this->check_result(xrEnumerateEnvironmentBlendModes(this->instance, this->system, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, mode_count, &mode_count, mode_list.data())))
{
return false;
}
for (XrEnvironmentBlendMode mode : mode_list)
{
if (mode == required_mode)
{
return true;
}
}
lava::log()->error("OpenXR: The system does not support the required blend mode!");
return false;
}
bool OpenXRHeadset::check_view_type_support(XrViewConfigurationType required_type) const
{
uint32_t view_type_count = 0;
if(!this->check_result(xrEnumerateViewConfigurations(this->instance, this->system, 0, &view_type_count, nullptr)))
{
return false;
}
std::vector<XrViewConfigurationType> view_type_list(view_type_count, (XrViewConfigurationType)0);
if (!this->check_result(xrEnumerateViewConfigurations(this->instance, this->system, view_type_count, &view_type_count, view_type_list.data())))
{
return false;
}
for (XrViewConfigurationType view_type : view_type_list)
{
if (view_type == required_type)
{
return true;
}
}
lava::log()->error("OpenXR: The system does not support the required view type!");
return false;
}
bool OpenXRHeadset::check_swapchain_format_support(int64_t required_format) const
{
uint32_t format_count = 0;
if(!this->check_result(xrEnumerateSwapchainFormats(this->session, 0, &format_count, nullptr)))
{
return false;
}
std::vector<int64_t> format_list(format_count, 0);
if (!this->check_result(xrEnumerateSwapchainFormats(this->session, format_count, &format_count, format_list.data())))
{
return false;
}
for (int64_t format : format_list)
{
if (format == required_format)
{
return true;
}
}
lava::log()->error("OpenXR: The system does not support the required swapchain format!");
return false;
}
std::vector<std::string> OpenXRHeadset::split_string(std::string string, char delimiter)
{
std::vector<std::string> strings;
while (true)
{
size_t next_occurence = string.find(delimiter);
strings.emplace_back(string.substr(0, next_occurence));
if (next_occurence == std::string::npos)
{
break;
}
string = string.substr(next_occurence + 1);
}
return strings;
}
\ No newline at end of file
#pragma once
#include <liblava/lava.hpp>
#include <glm/glm.hpp>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <memory>
#include <vector>
#include <array>
#include "headset.hpp"
class OpenXRHeadset : public Headset
{
public:
typedef std::shared_ptr<OpenXRHeadset> Ptr;
public:
OpenXRHeadset();
bool on_setup_instance(lava::frame_config& config) override;
bool on_setup_device(lava::device::create_param& parameters) override;
bool on_create() override;
void on_destroy() override;
bool on_interface() override;
bool on_update(lava::delta delta_time) override;
void submit_frame(VkCommandBuffer command_buffer, VkImageLayout frame_layout, FrameId frame_id) override;
VkFormat get_format() const override;
const glm::uvec2& get_resolution() const override;
const glm::mat4& get_view_matrix() const override;
const glm::mat4& get_head_to_eye_matrix(Eye eye) const override;
const glm::mat4& get_projection_matrix(Eye eye) const override;
lava::image::ptr get_framebuffer(FrameId frame_id) const override;
const char* get_name() const override;
private:
bool create_framebuffers();
void destroy_framebuffers();
bool create_swapchains();
void destroy_swapchains();
bool poll_events();
bool process_event(const XrEventDataBuffer& event);
bool begin_frame();
bool update_views();
bool acquire_image(FrameId frame_id);
bool release_image(FrameId frame_id);
bool end_frame();
bool check_result(XrResult result) const;
bool check_layer_support(const std::vector<const char*>& required_layers) const;
bool check_extension_support(const std::vector<const char*>& required_extensions) const;
bool check_blend_mode_support(XrEnvironmentBlendMode required_mode) const;
bool check_view_type_support(XrViewConfigurationType required_type) const;
bool check_swapchain_format_support(int64_t required_format) const;
static std::vector<std::string> split_string(std::string string, char delimiter = ' ');
private:
XrInstance instance = XR_NULL_HANDLE;
XrSystemId system = XR_NULL_SYSTEM_ID;
XrSession session = XR_NULL_HANDLE;
XrSpace space = XR_NULL_HANDLE;
XrFrameState frame_state;
std::vector<XrSwapchain> swapchains;
std::vector<XrView> views;
std::array<uint32_t, 2> swapchain_indices;
std::array<std::vector<VkImage>, 2> swapchain_images;
std::array<lava::image::ptr, 2> framebuffers;
std::vector<std::string> instance_extensions;
std::vector<std::string> device_extensions;
bool active = false;
uint32_t submit_count = 0;
float near_plane = 10.0f;
float far_plane = 10000.0f;
glm::uvec2 resolution;
glm::mat4 view_matrix;
std::array<glm::mat4, 2> head_to_eye_matrices;
std::array<glm::mat4, 2> projection_matrices;
};
\ No newline at end of file
......@@ -246,7 +246,7 @@ void CommandParser::show_help()
std::cout << " --benchmark Play animation once and close program after completion." << std::endl;
std::cout << " If not set, the application runs indefinitely and the interface is enabled." << std::endl;
std::cout << " --headset={headset} The headset that should be used." << std::endl;
std::cout << " Options: emulated (default), local, remote" << std::endl;
std::cout << " Options: emulated (default), openvr, openxr, remote" << std::endl;
std::cout << " --stereo-strategy={strategy} The stereo strategy that should be used." << std::endl;
std::cout << " Options: native_forward (default), native_deferred, dpr" << std::endl;
std::cout << " --animation={animation_name} The name of the animation that should be played." << std::endl;
......@@ -273,9 +273,14 @@ bool CommandParser::set_headset(const std::string& name)
this->headset = HEADSET_TYPE_EMULATED;
}
else if (name == "local")
else if (name == "openvr")
{
this->headset = HEADSET_TYPE_LOCAL;
this->headset = HEADSET_TYPE_OPEN_VR;
}
else if (name == "openxr")
{
this->headset = HEADSET_TYPE_OPEN_XR;
}
else if (name == "remote")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment