diff --git a/src/third_party/vk_enum_string_helper.h b/apps/common/third_party/vk_enum_string_helper.h similarity index 100% rename from src/third_party/vk_enum_string_helper.h rename to apps/common/third_party/vk_enum_string_helper.h diff --git a/apps/vr_viewer/CMakeLists.txt b/apps/vr_viewer/CMakeLists.txt index c242793..ae32da2 100644 --- a/apps/vr_viewer/CMakeLists.txt +++ b/apps/vr_viewer/CMakeLists.txt @@ -19,9 +19,11 @@ add_executable(3dgs_vr_viewer src/main.cpp src/VRViewer.cpp src/VRViewer.h - src/OXRUtils.h - src/OXRContext.cpp - src/OXRContext.h + src/oxr/Utils.h + src/oxr/OXRContext.cpp + src/oxr/OXRContext.h + src/oxr/Layer.cpp + src/oxr/Layer.h ) target_compile_definitions(3dgs_cpp PUBLIC VKGS_ENABLE_OPENXR) diff --git a/apps/vr_viewer/src/OXRContext.h b/apps/vr_viewer/src/OXRContext.h deleted file mode 100644 index e54a686..0000000 --- a/apps/vr_viewer/src/OXRContext.h +++ /dev/null @@ -1,42 +0,0 @@ - -#ifndef OXRCONTEXT_H -#define OXRCONTEXT_H -#include -#include - -#include "OXRUtils.h" - - -class OXRContext { -public: - void setup(); - void* getPhysicalDevice(void *instance) const; - - void createSession(void *vkInstance, void *vkPhysicalDevice, void *vkDevice, uint32_t vkQueueFamilyIndex, - uint32_t vkQueueIndex); - - void createReferenceSpace(); - - void beginSession(); - -private: - XrInstance oxrInstance = XR_NULL_HANDLE; - XrSystemId systemId = XR_NULL_SYSTEM_ID; - XrViewConfigurationView views[2] = {{XR_TYPE_VIEW_CONFIGURATION_VIEW}, {XR_TYPE_VIEW_CONFIGURATION_VIEW}}; - XrSession oxrSession = XR_NULL_HANDLE; - XrSpace localSpace = XR_NULL_HANDLE; - - void createInstance(); - - void createSystem(); - - void setupViews(); - - [[nodiscard]] std::vector getRequiredVulkanInstanceExtensions() const; - - [[nodiscard]] std::vector getRequiredVulkanDeviceExtensions() const; -}; - - - -#endif //OXRCONTEXT_H diff --git a/apps/vr_viewer/src/VRViewer.cpp b/apps/vr_viewer/src/VRViewer.cpp index 07b6532..8e30ae8 100644 --- a/apps/vr_viewer/src/VRViewer.cpp +++ b/apps/vr_viewer/src/VRViewer.cpp @@ -7,12 +7,20 @@ #include "3dgs.h" #include "spdlog/spdlog.h" +using namespace OXR; + void VRViewer::run() { context = std::make_shared(); context->setup(); VulkanSplatting::OpenXRConfiguration configuration; + configuration.instanceExtensions = context->getRequiredVulkanInstanceExtensions(); + configuration.deviceExtensions = context->getRequiredVulkanDeviceExtensions(); configuration.getPhysicalDevice = std::bind(&OXRContext::getPhysicalDevice, context.get(), std::placeholders::_1); + configuration.postVulkanInit = std::bind(&VRViewer::finishSetup, this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4, std::placeholders::_5); + + auto renderingTarget = VulkanSplatting::createOpenXRRenderingTarget(configuration); } void VRViewer::finishSetup(void *vkInstance, void *vkPhysicalDevice, void *vkDevice, uint32_t vkQueueFamilyIndex, @@ -20,5 +28,10 @@ void VRViewer::finishSetup(void *vkInstance, void *vkPhysicalDevice, void *vkDev context->createSession(vkInstance, vkPhysicalDevice, vkDevice, vkQueueFamilyIndex, vkQueueIndex); context->createReferenceSpace(); context->beginSession(); + createProjectionLayer(); +} + +void VRViewer::createProjectionLayer() { + } diff --git a/apps/vr_viewer/src/VRViewer.h b/apps/vr_viewer/src/VRViewer.h index e388b2e..e943bdd 100644 --- a/apps/vr_viewer/src/VRViewer.h +++ b/apps/vr_viewer/src/VRViewer.h @@ -5,7 +5,7 @@ #include #include -#include "OXRContext.h" +#include "oxr/OXRContext.h" class VRViewer { public: @@ -17,21 +17,15 @@ class VRViewer { explicit VRViewer(const VRViewer::Configuration & config) : config(config) {}; - VRViewer(const VRViewer &other) = delete; - - VRViewer(VRViewer &&other) = delete; - - VRViewer & operator=(const VRViewer &other) = delete; - - VRViewer & operator=(VRViewer &&other) = delete; - void run(); void finishSetup(void *vkInstance, void *vkPhysicalDevice, void *vkDevice, uint32_t vkQueueFamilyIndex, uint32_t vkQueueIndex); private: Configuration config; - std::shared_ptr context = nullptr; + std::shared_ptr context = nullptr; + + void createProjectionLayer(); }; diff --git a/apps/vr_viewer/src/oxr/Layer.cpp b/apps/vr_viewer/src/oxr/Layer.cpp new file mode 100644 index 0000000..1552654 --- /dev/null +++ b/apps/vr_viewer/src/oxr/Layer.cpp @@ -0,0 +1,154 @@ +#include "Layer.h" + +#include "Utils.h" +#include "spdlog/spdlog.h" +#include "vk_enum_string_helper.h" + +namespace OXR { + Layer::Layer(std::shared_ptr context, XrCompositionLayerFlags flags, bool depth_enabled) : depthEnabled( + depth_enabled) { + createSwapchains(context); + createProjectionViews(context, flags); + } + + void Layer::createSwapchains(std::shared_ptr context) { + // Print the swapchain formats + spdlog::info("Swapchain formats:"); + auto swapchainFormats = getVulkanFormatsForSwapchain(context); + for (vk::Format format: swapchainFormats) { + spdlog::info("{}", string_VkFormat(static_cast(format))); + } + + std::vector swapchainImageVectors[2]; + for (int i = 0; i < 2; i++) { + XrSwapchainCreateInfo swapchainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapchainCreateInfo.arraySize = 1; + swapchainCreateInfo.format = static_cast(swapchainFormats[0]); + swapchainCreateInfo.width = context->views[i].recommendedImageRectWidth; + swapchainCreateInfo.height = context->views[i].recommendedImageRectHeight; + swapchainCreateInfo.mipCount = 1; + swapchainCreateInfo.faceCount = 1; + swapchainCreateInfo.sampleCount = 1; + swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | + XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT | + XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapchainCreateInfo.createFlags = 0; + + spdlog::debug("Creating swapchain with format and size: {} {}x{}", string_VkFormat( + static_cast(swapchainCreateInfo.format)), swapchainCreateInfo.width, + swapchainCreateInfo.height); + + XR_CHECK(xrCreateSwapchain(context->oxrSession, &swapchainCreateInfo, &swapchains[i]), + "Failed to create swapchain"); + + uint32_t swapchainLength; + XR_CHECK(xrEnumerateSwapchainImages(swapchains[i], 0, &swapchainLength, nullptr), + "Failed to enumerate swapchain images"); + + std::vector swapchainImages(swapchainLength); + for (uint32_t j = 0; j < swapchainLength; j++) { + swapchainImages[j] = {XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR}; + } + + XR_CHECK(xrEnumerateSwapchainImages(swapchains[i], swapchainLength, &swapchainLength, + reinterpret_cast(swapchainImages.data())), + "Failed to enumerate swapchain images"); + + swapchainImageVectors[i] = swapchainImages; + swapchainSize[i] = swapchainLength; + } + + assert(swapchainSize[0] == swapchainSize[1]); + + images.resize(swapchainSize[0]); + for (uint32_t i = 0; i < swapchainSize[0]; i++) { + images[i][0] = swapchainImageVectors[0][i]; + images[i][1] = swapchainImageVectors[1][i]; + } + + if (!depthEnabled) { + return; + } + + vk::Format selectedDepthFormat = vk::Format::eUndefined; + for (vk::Format format: swapchainFormats) { + if (format == vk::Format::eD32Sfloat) { + selectedDepthFormat = format; + break; + } else if (format == vk::Format::eD16Unorm) { + selectedDepthFormat = format; + } + } + + if (selectedDepthFormat == vk::Format::eUndefined) { + spdlog::error("No depth format found"); + return; + } + + spdlog::info("Depth format: {}", string_VkFormat(static_cast(selectedDepthFormat))); + + for (int i = 0; i < 2; i++) { + XrSwapchainCreateInfo swapchainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapchainCreateInfo.arraySize = 1; + swapchainCreateInfo.format = static_cast(selectedDepthFormat); + swapchainCreateInfo.width = context->views[i].recommendedImageRectWidth; + swapchainCreateInfo.height = context->views[i].recommendedImageRectHeight; + swapchainCreateInfo.mipCount = 1; + swapchainCreateInfo.faceCount = 1; + swapchainCreateInfo.sampleCount = 1; + swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT | + XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + swapchainCreateInfo.createFlags = 0; + + spdlog::debug("Creating depth swapchain with format and size: {} {}x{}", string_VkFormat( + static_cast(swapchainCreateInfo.format)), swapchainCreateInfo.width, + swapchainCreateInfo.height); + + XR_CHECK(xrCreateSwapchain(context->oxrSession, &swapchainCreateInfo, &depthSwapchains[i]), + "Failed to create depth swapchain"); + + uint32_t swapchainLength; + XR_CHECK(xrEnumerateSwapchainImages(depthSwapchains[i], 0, &swapchainLength, nullptr), + "Failed to enumerate depth swapchain images"); + + std::vector swapchainImages(swapchainLength); + for (uint32_t j = 0; j < swapchainLength; j++) { + swapchainImages[j] = {XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR}; + } + + XR_CHECK(xrEnumerateSwapchainImages(depthSwapchains[i], swapchainLength, &swapchainLength, + reinterpret_cast(swapchainImages.data())), + "Failed to enumerate depth swapchain images"); + + depthImages.resize(swapchainLength); + for (uint32_t j = 0; j < swapchainLength; j++) { + depthImages[j][0] = swapchainImages[j]; + depthImages[j][1] = swapchainImages[j]; + } + + depthSwapchainSize[i] = swapchainLength; + } + } + + void Layer::createProjectionViews(std::shared_ptr context, uint64_t flags) { + for (int i = 0; i < 2; i++) { + views[i].pose = {.position = { 0.0f, 0.0f, 0.0f }, .orientation = { 0.0f, 0.0f, 0.0f, 1.0f }}; + views[i].subImage.swapchain = swapchains[i]; + views[i].subImage.imageRect.offset = {0, 0}; + views[i].subImage.imageRect.extent = {static_cast(context->views[i].recommendedImageRectWidth), + static_cast(context->views[i].recommendedImageRectHeight)}; + if (depthEnabled) { + views[i].next = &depthLayer; + depthLayer.subImage.swapchain = depthSwapchains[i]; + depthLayer.subImage.imageRect.offset = {0, 0}; + depthLayer.subImage.imageRect.extent = {static_cast(context->views[i].recommendedImageRectWidth), + static_cast(context->views[i].recommendedImageRectHeight)}; + } + } + + layer.layerFlags = flags; + layer.space = context->localSpace; + layer.viewCount = 2; + layer.views = views; + } +} // OXR diff --git a/apps/vr_viewer/src/oxr/Layer.h b/apps/vr_viewer/src/oxr/Layer.h new file mode 100644 index 0000000..abc1c0b --- /dev/null +++ b/apps/vr_viewer/src/oxr/Layer.h @@ -0,0 +1,40 @@ +#ifndef LAYER_H +#define LAYER_H +#include +#include +#include +#include +#include + +#include "OXRContext.h" + +namespace OXR { + +class Layer { +public: + explicit Layer(std::shared_ptr context, XrCompositionLayerFlags flags, bool depth_enabled); + + +private: + XrCompositionLayerProjection layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + XrCompositionLayerDepthInfoKHR depthLayer = {XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR}; + XrCompositionLayerProjectionView views[2] = {{XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}}; + XrSwapchain swapchains[2] = {XR_NULL_HANDLE, XR_NULL_HANDLE}; + uint32_t swapchainSize[2] = {0, 0}; + uint32_t lastImageAcquired[2] = {0, 0}; + + bool depthEnabled = false; + XrSwapchain depthSwapchains[2] = {XR_NULL_HANDLE, XR_NULL_HANDLE}; + uint32_t depthSwapchainSize[2] = {0, 0}; + uint32_t lastDepthImageAcquired[2] = {0, 0}; + + std::vector> images; + std::vector> depthImages; + + void createSwapchains(std::shared_ptr context); + void createProjectionViews(std::shared_ptr context, uint64_t flags); +}; + +} // OXR + +#endif //LAYER_H diff --git a/apps/vr_viewer/src/OXRContext.cpp b/apps/vr_viewer/src/oxr/OXRContext.cpp similarity index 99% rename from apps/vr_viewer/src/OXRContext.cpp rename to apps/vr_viewer/src/oxr/OXRContext.cpp index 73a6978..b62de10 100644 --- a/apps/vr_viewer/src/OXRContext.cpp +++ b/apps/vr_viewer/src/oxr/OXRContext.cpp @@ -7,8 +7,11 @@ #include #include +#include "Utils.h" #include "spdlog/spdlog.h" +using namespace OXR; + void OXRContext::setup() { bool vulkanSupported = OXR::isExtensionSupported(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME); if (!vulkanSupported) { diff --git a/apps/vr_viewer/src/oxr/OXRContext.h b/apps/vr_viewer/src/oxr/OXRContext.h new file mode 100644 index 0000000..2044126 --- /dev/null +++ b/apps/vr_viewer/src/oxr/OXRContext.h @@ -0,0 +1,40 @@ + +#ifndef OXRCONTEXT_H +#define OXRCONTEXT_H +#include +#include + +namespace OXR { + class OXRContext { + public: + void setup(); + void* getPhysicalDevice(void *instance) const; + + void createSession(void *vkInstance, void *vkPhysicalDevice, void *vkDevice, uint32_t vkQueueFamilyIndex, + uint32_t vkQueueIndex); + + void createReferenceSpace(); + + void beginSession(); + + [[nodiscard]] std::vector getRequiredVulkanInstanceExtensions() const; + + [[nodiscard]] std::vector getRequiredVulkanDeviceExtensions() const; + + XrInstance oxrInstance = XR_NULL_HANDLE; + XrSystemId systemId = XR_NULL_SYSTEM_ID; + XrViewConfigurationView views[2] = {{XR_TYPE_VIEW_CONFIGURATION_VIEW}, {XR_TYPE_VIEW_CONFIGURATION_VIEW}}; + XrSession oxrSession = XR_NULL_HANDLE; + XrSpace localSpace = XR_NULL_HANDLE; + + private: + void createInstance(); + + void createSystem(); + + void setupViews(); + + }; +} + +#endif //OXRCONTEXT_H diff --git a/apps/vr_viewer/src/OXRUtils.h b/apps/vr_viewer/src/oxr/Utils.h similarity index 78% rename from apps/vr_viewer/src/OXRUtils.h rename to apps/vr_viewer/src/oxr/Utils.h index 24a3ad4..43f2403 100644 --- a/apps/vr_viewer/src/OXRUtils.h +++ b/apps/vr_viewer/src/oxr/Utils.h @@ -29,11 +29,27 @@ **/ #include +#include #include #include #include +#define XR_CHECK(x, msg) if (XR_FAILED(x)) { throw std::runtime_error(msg); } + namespace OXR { + inline std::vector getVulkanFormatsForSwapchain(const std::shared_ptr &context) { + uint32_t swapchainFormatCount; + XR_CHECK(xrEnumerateSwapchainFormats(context->oxrSession, 0, &swapchainFormatCount, nullptr), + "Failed to enumerate swapchain formats"); + + std::vector swapchainFormats(swapchainFormatCount); + XR_CHECK( + xrEnumerateSwapchainFormats(context->oxrSession, swapchainFormatCount, &swapchainFormatCount, + reinterpret_cast(swapchainFormats.data())), "Failed to enumerate swapchain formats"); + + return swapchainFormats; + } + inline bool isExtensionSupported(const std::string &extensionName) { uint32_t extensionCount; XrResult result = xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionCount, nullptr); diff --git a/include/3dgs/3dgs.h b/include/3dgs/3dgs.h index e9dfed7..d64ccdf 100644 --- a/include/3dgs/3dgs.h +++ b/include/3dgs/3dgs.h @@ -42,6 +42,7 @@ class VulkanSplatting { std::vector deviceExtensions; std::function getPhysicalDevice; + std::function postVulkanInit; }; static std::shared_ptr createOpenXRRenderingTarget(OpenXRConfiguration configuration); #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cee4d7..7c19462 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,7 +97,7 @@ target_include_directories(3dgs_cpp add_dependencies(3dgs_cpp shaders) -target_link_libraries(3dgs_cpp PUBLIC Vulkan::Vulkan glfw spdlog::spdlog) +target_link_libraries(3dgs_cpp PUBLIC Vulkan::Vulkan glfw spdlog::spdlog third_party) if (UNIX) target_link_libraries(3dgs_cpp PUBLIC ${CMAKE_DL_LIBS}) endif () \ No newline at end of file diff --git a/src/Renderer.cpp b/src/Renderer.cpp index fc3434a..3ea19ff 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -16,6 +16,8 @@ #include +#include "vulkan/targets/OpenXRStereo.h" + void Renderer::initialize() { initializeVulkan(); createGui(); @@ -119,7 +121,7 @@ void Renderer::recreateSwapchain() { void Renderer::initializeVulkan() { spdlog::debug("Initializing Vulkan"); window = configuration.renderingTarget; - context = std::make_shared(window->getRequiredInstanceExtensions(), std::vector{}, + context = std::make_shared(window->getRequiredInstanceExtensions(), window->getRequiredDeviceExtensions(), configuration.enableVulkanValidationLayers); context->createInstance(); @@ -157,6 +159,12 @@ void Renderer::initializeVulkan() { for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { renderFinishedSemaphores[i] = context->device->createSemaphoreUnique(vk::SemaphoreCreateInfo()); } + + if (auto openXRBackend = std::dynamic_pointer_cast(window)) { + openXRBackend->postVulkanInit(context->instance.get(), context->physicalDevice, context->device.get(), + context->queues[VulkanContext::Queue::GRAPHICS].queueFamily, + context->queues[VulkanContext::Queue::GRAPHICS].queueIndex); + } } void Renderer::loadSceneToGPU() { diff --git a/src/vulkan/RenderingTarget.h b/src/vulkan/RenderingTarget.h index 026d56e..46b3ebf 100644 --- a/src/vulkan/RenderingTarget.h +++ b/src/vulkan/RenderingTarget.h @@ -13,7 +13,8 @@ class RenderingTarget { virtual std::array getMouseButton() { return {false, false, false}; } - virtual std::vector getRequiredInstanceExtensions() = 0; + virtual std::vector getRequiredInstanceExtensions() { return {}; }; + virtual std::vector getRequiredDeviceExtensions() { return {}; }; [[nodiscard]] virtual std::pair getFramebufferSize() const = 0; @@ -38,5 +39,4 @@ class RenderingTarget { }; - #endif //WINDOW_H diff --git a/src/vulkan/targets/OpenXRStereo.cpp b/src/vulkan/targets/OpenXRStereo.cpp index 9ace0ff..dcd4951 100644 --- a/src/vulkan/targets/OpenXRStereo.cpp +++ b/src/vulkan/targets/OpenXRStereo.cpp @@ -8,9 +8,15 @@ std::array OpenXRStereo::getMouseButton() { } std::vector OpenXRStereo::getRequiredInstanceExtensions() { + return configuration.instanceExtensions; +} + +std::vector OpenXRStereo::getRequiredDeviceExtensions() { + return configuration.deviceExtensions; } std::pair OpenXRStereo::getFramebufferSize() const { + return RenderingTarget::getFramebufferSize(); } std::array OpenXRStereo::getCursorTranslation() { @@ -41,3 +47,8 @@ std::optional OpenXRStereo::requirePhysicalDevice(vk::Instan void *pdPtr = configuration.getPhysicalDevice(instance); return {static_cast(static_cast(pdPtr))}; } + +void OpenXRStereo::postVulkanInit(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device, + uint32_t queueFamily, uint32_t queueIndex) { + configuration.postVulkanInit(instance, physicalDevice, device, queueFamily, queueIndex); +} diff --git a/src/vulkan/targets/OpenXRStereo.h b/src/vulkan/targets/OpenXRStereo.h index fc19d1d..adba290 100644 --- a/src/vulkan/targets/OpenXRStereo.h +++ b/src/vulkan/targets/OpenXRStereo.h @@ -18,6 +18,8 @@ class OpenXRStereo : public RenderingTarget { std::vector getRequiredInstanceExtensions() override; + std::vector getRequiredDeviceExtensions() override; + [[nodiscard]] std::pair getFramebufferSize() const override; std::array getCursorTranslation() override; @@ -34,6 +36,9 @@ class OpenXRStereo : public RenderingTarget { std::optional requirePhysicalDevice(vk::Instance instance) override; + virtual void postVulkanInit(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device, uint32_t queueFamily, uint32_t queueIndex); + + private: VulkanSplatting::OpenXRConfiguration configuration; };