diff --git a/CMakeLists.txt b/CMakeLists.txt index ce1a69bb7e9309123a60d510746215045f8dfa65..4a6cb3804482f40940b9363d7ddfe4707c6da0d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -691,6 +694,7 @@ message("======================================================================= ${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/remote_headset.hpp ${SRC_DIR}/headset/remote_headset.cpp + ${SRC_DIR}/headset/openxr_headset.hpp ${SRC_DIR}/headset/openxr_headset.cpp #Strategy ${SRC_DIR}/strategy/stereo_strategy.hpp ${SRC_DIR}/strategy/stereo_strategy.cpp @@ -767,6 +771,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") diff --git a/src/headset/headset.cpp b/src/headset/headset.cpp index 68303cc28587b04855a34849cdea5c06d2a1908d..ac74909880009888dcfc0e6c34e0d6f0f7835804 100644 --- a/src/headset/headset.cpp +++ b/src/headset/headset.cpp @@ -1,6 +1,7 @@ #include "headset.hpp" #include "emulated_headset.hpp" #include "local_headset.hpp" +#include "openxr_headset.hpp" #include "remote_headset.hpp" void Headset::set_application(VRApplication* application) @@ -45,6 +46,9 @@ Headset::Ptr make_headset(VRApplication* application, HeadsetType headset_type) case HEADSET_TYPE_LOCAL: headset = std::make_shared<LocalHeadset>(); break; + case HEADSET_TYPE_OPENXR: + headset = std::make_shared<OpenXRHeadset>(); + break; case HEADSET_TYPE_REMOTE: headset = std::make_shared<RemoteHeadset>(); break; diff --git a/src/headset/headset.hpp b/src/headset/headset.hpp index 0c4825fd50c324c0c79f75caf9a7aac79a617585..3da65076a14ed1bbc977d8f6e0b0f9a66522cc53 100644 --- a/src/headset/headset.hpp +++ b/src/headset/headset.hpp @@ -33,6 +33,7 @@ enum HeadsetType { HEADSET_TYPE_EMULATED, HEADSET_TYPE_LOCAL, + HEADSET_TYPE_OPENXR, HEADSET_TYPE_REMOTE }; diff --git a/src/headset/openxr_headset.cpp b/src/headset/openxr_headset.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52da5f19b7cf0c076ebafc543cb197cbbb09c671 --- /dev/null +++ b/src/headset/openxr_headset.cpp @@ -0,0 +1,674 @@ +#include "openxr_headset.hpp" +#include "vr_application.hpp" + +#include <iostream> +#include <vector> +#include <array> + +PFN_xrGetVulkanGraphicsRequirementsKHR xrGetVulkanGraphicsRequirementsKHR = nullptr; +PFN_xrGetVulkanInstanceExtensionsKHR xrGetVulkanInstanceExtensionsKHR = nullptr; +PFN_xrGetVulkanDeviceExtensionsKHR xrGetVulkanDeviceExtensionsKHR = nullptr; +PFN_xrGetVulkanGraphicsDeviceKHR xrGetVulkanGraphicsDeviceKHR = nullptr; + +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))) + { + 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))) + { + return false; + } + + if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&xrGetVulkanGraphicsRequirementsKHR))) + { + return false; + } + + if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanInstanceExtensionsKHR", (PFN_xrVoidFunction*)&xrGetVulkanInstanceExtensionsKHR))) + { + return false; + } + + if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanDeviceExtensionsKHR", (PFN_xrVoidFunction*)&xrGetVulkanDeviceExtensionsKHR))) + { + return false; + } + + if (!this->check_result(xrGetInstanceProcAddr(this->instance, "xrGetVulkanGraphicsDeviceKHR", (PFN_xrVoidFunction*)&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))) + { + return false; + } + + uint32_t extension_count = 0; + if (!this->check_result(xrGetVulkanInstanceExtensionsKHR(this->instance, this->system, 0, &extension_count, nullptr))) + { + 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()))) + { + 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))) + { + 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()))) + { + 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))) + { + return false; + } + + XrReferenceSpaceCreateInfo space_info; + space_info.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; + space_info.next = nullptr; + space_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + memset(&space_info.poseInReferenceSpace, 0, sizeof(space_info.poseInReferenceSpace)); //TODO: The Quaternion is invalid!! + + if(!this->check_result(xrCreateReferenceSpace(this->session, &space_info, &this->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_framebuffers()) + { + return false; + } + + return true; +} + +void OpenXRHeadset::on_destroy() +{ + //TODO: ....... +} + +bool OpenXRHeadset::on_interface() +{ + return false; +} + +bool OpenXRHeadset::on_update(lava::delta delta_time) +{ + if (!this->poll_events()) + { + return false; + } + + if (this->active) + { + XrFrameWaitInfo wait_info; + wait_info.type = XR_TYPE_FRAME_WAIT_INFO; + wait_info.next = nullptr; + + XrFrameState frame_state; + memset(&frame_state, 0, sizeof(frame_state)); + frame_state.type = XR_TYPE_FRAME_STATE; + + if(!this->check_result(xrWaitFrame(this->session, &wait_info, &frame_state))) + { + return false; + } + + //TODO..... Begin Frame + //TODO..... Setup View and Projection Matrix + } + + return false; +} + +void OpenXRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout frame_layout, FrameId frame_id) +{ + //TODO.... 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][this->framebuffer_index]; +} + +const char* OpenXRHeadset::get_name() const +{ + return "OpenXR Headset"; +} + +bool OpenXRHeadset::create_framebuffers() +{ + uint32_t view_count = 0; + if (!this->check_result(xrEnumerateViewConfigurationViews(this->instance, this->system, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &view_count, nullptr))) + { + 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()))) + { + 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 = XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; + 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))) + { + return false; + } + + uint32_t image_count = 0; + if (!this->check_result(xrEnumerateSwapchainImages(swapchain, 0, &image_count, nullptr))) + { + 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()))) + { + return false; + } + + for (XrSwapchainImageVulkanKHR swapchain_image : image_list) + { + this->framebuffers[index].push_back(lava::make_image(this->get_format(), swapchain_image.image)); + } + + this->swapchains.push_back(swapchain); + } + + return true; +} + +void OpenXRHeadset::destroy_framebuffers() +{ + //TODO: ..... +} + +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)) + { + 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))) + { + return false; + } + + this->active = true; + + break; + } + case XR_SESSION_STATE_STOPPING: + if (!this->check_result(xrEndSession(this->session))) + { + return false; + } + + this->active = false; + break; + case XR_SESSION_STATE_EXITING: + case XR_SESSION_LOSS_PENDING: + return false; + default: + break; + } + + break; + } + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + return false; + default: + break; + } + + 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 diff --git a/src/headset/openxr_headset.hpp b/src/headset/openxr_headset.hpp new file mode 100644 index 0000000000000000000000000000000000000000..efd0c14ef68c8448b03cf1fc1f6a286d4de4a5e4 --- /dev/null +++ b/src/headset/openxr_headset.hpp @@ -0,0 +1,74 @@ +#pragma once +#include <liblava/lava.hpp> +#include <glm/glm.hpp> +#include <openxr/openxr.h> +#include <openxr/openxr_platform.h> +#include <memory> + +#include "headset.hpp" + +class OpenXRHeadset : public Headset +{ +public: + typedef std::shared_ptr<OpenXRHeadset> Ptr; + +public: + OpenXRHeadset() = default; + + 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 poll_events(); + bool process_event(const XrEventDataBuffer& event); + + 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; + std::vector<XrSwapchain> swapchains; + + bool active = false; + + std::vector<std::string> instance_extensions; + std::vector<std::string> device_extensions; + + glm::uvec2 resolution; + glm::mat4 view_matrix; + std::array<glm::mat4, 2> head_to_eye_matrices; + std::array<glm::mat4, 2> projection_matrices; + + std::array<std::vector<lava::image::ptr>, 2> framebuffers; + uint32_t framebuffer_index = 0; +}; \ No newline at end of file diff --git a/src/utility/command_parser.cpp b/src/utility/command_parser.cpp index 2d5c46816086e73662538cb4c3dc9b010fb5c63c..9cf0d69753c90973bb5ababbaa48aba244bf0cfb 100644 --- a/src/utility/command_parser.cpp +++ b/src/utility/command_parser.cpp @@ -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), local, 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; @@ -278,6 +278,11 @@ bool CommandParser::set_headset(const std::string& name) this->headset = HEADSET_TYPE_LOCAL; } + else if (name == "openxr") + { + this->headset = HEADSET_TYPE_OPENXR; + } + else if (name == "remote") { this->headset = HEADSET_TYPE_REMOTE;