diff --git a/src/Application.cpp b/src/Application.cpp index 8579814..ecf1a39 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -439,6 +439,14 @@ auto xr_rotate_vector(XrQuaternionf q, smath::Vec3 v) -> smath::Vec3 return (u * (2.0f * dot)) + (v * (s * s - u_dot)) + (cross * (2.0f * s)); } +[[maybe_unused]] auto xr_result_to_string(XrInstance instance, XrResult result) + -> std::string +{ + std::array buffer {}; + xrResultToString(instance, result, buffer.data()); + return std::string { buffer.data() }; +} + } // namespace namespace Lunar { @@ -452,6 +460,7 @@ struct OpenXrSwapchain { struct OpenXrState { bool enabled { false }; bool session_running { false }; + bool use_vulkan_enable2 { false }; XrInstance instance { XR_NULL_HANDLE }; XrSystemId system_id { XR_NULL_SYSTEM_ID }; XrSession session { XR_NULL_HANDLE }; @@ -466,7 +475,10 @@ struct OpenXrState { std::vector swapchains {}; std::vector instance_extensions {}; std::vector device_extensions {}; - PFN_xrGetVulkanGraphicsDevice2KHR get_graphics_device { nullptr }; + PFN_xrGetVulkanGraphicsDevice2KHR get_graphics_device2 { nullptr }; + PFN_xrGetVulkanGraphicsDeviceKHR get_graphics_device { nullptr }; + PFN_xrGetVulkanGraphicsRequirements2KHR get_requirements2 { nullptr }; + PFN_xrGetVulkanGraphicsRequirementsKHR get_requirements { nullptr }; }; Application::Application() @@ -939,25 +951,61 @@ auto Application::run() -> void auto view { smath::matrix_look_at( m_camera.position, m_camera.target, m_camera.up) }; auto const draw_extent = m_renderer->draw_extent(); - auto const aspect = static_cast(draw_extent.width) - / static_cast(draw_extent.height); - auto const proj = smath::matrix_perspective( - m_camera.fovy, aspect, 0.1f, 10000.0f); - auto const view_projection = proj * view; - gl.set_transform(view_projection); - for (auto const &mesh : m_test_meshes) { - for (auto const &surface : mesh->surfaces) { - gl.draw_mesh(mesh->mesh_buffers, smath::Mat4::identity(), - surface.count, surface.start_index); - } - } - m_skybox.draw(gl, *m_renderer, view_projection); + auto const aspect = draw_extent.height == 0 + ? 1.0f + : static_cast(draw_extent.width) + / static_cast(draw_extent.height); + auto projection { smath::matrix_perspective( + m_camera.fovy, aspect, 0.1f, 10000.0f) }; + projection[1][1] *= -1; + auto view_projection { projection * view }; + + auto skybox_view { view }; + skybox_view[3][0] = 0.0f; + skybox_view[3][1] = 0.0f; + skybox_view[3][2] = 0.0f; + m_skybox.draw(gl, *m_renderer, projection * skybox_view); gl.set_transform(view_projection); - gl.draw_sphere({ 0.0f, 0.0f, 0.0f }, 0.1f, 16, 32, - smath::Vec4 { Colors::RED, 1.0f }); - gl.draw_sphere({ 0.0f, 0.0f, 0.0f }, 0.11f, 16, 32, - smath::Vec4 { Colors::DARK_RED, 1.0f }); + gl.set_texture(); + auto const &meshes { m_test_meshes }; + if (meshes.size() > 2 && !meshes[2]->surfaces.empty()) { + auto const &surface = meshes[2]->surfaces[0]; + gl.draw_mesh(meshes[2]->mesh_buffers, + view_projection + * smath::translate(smath::Vec3 { 0.0f, 0.0f, -5.0f }), + surface.count, surface.start_index); + } + + gl.push_transform(); + gl.set_transform(view_projection + * smath::translate(smath::Vec3 { 0.0f, 0.0f, 5.0f }) + * smath::Quaternion::from_axis_angle( + smath::Vec3 { 0.0f, 1.0f, 0.0f }, smath::deg(180)) + .as_matrix()); + + gl.set_texture(&m_renderer->white_texture()); + + gl.begin(VulkanRenderer::GL::GeometryKind::Quads); + + gl.color(smath::Vec3 { 0.0f, 0.0f, 0.0f }); + gl.uv(smath::Vec2 { 1.0f, 1.0f }); + gl.vert(smath::Vec3 { 0.5f, -0.5f, 0.0f }); + + gl.color(smath::Vec3 { 0.5f, 0.5f, 0.5f }); + gl.uv(smath::Vec2 { 1.0f, 0.0f }); + gl.vert(smath::Vec3 { 0.5f, 0.5f, 0.0f }); + + gl.color(smath::Vec3 { 1.0f, 0.0f, 0.0f }); + gl.uv(smath::Vec2 { 0.0f, 1.0f }); + gl.vert(smath::Vec3 { -0.5f, -0.5f, 0.0f }); + + gl.color(smath::Vec3 { 0.0f, 1.0f, 0.0f }); + gl.uv(smath::Vec2 { 0.0f, 0.0f }); + gl.vert(smath::Vec3 { -0.5f, 0.5f, 0.0f }); + + gl.end(); + gl.draw_rectangle({ -0.5f, 0.5f }, { 0.5f, 0.5f }); gl.draw_rectangle( { 0, 0.5f }, { 0.5f, 0.5f }, { Colors::TEAL, 1.0f }); @@ -1031,6 +1079,9 @@ auto Application::shutdown_input() -> void auto Application::init_openxr() -> void { + if (auto const *no_xr = getenv("LUNAR_NO_XR"); no_xr) { + return; + } m_openxr = std::make_unique(); XrInstanceCreateInfo create_info {}; @@ -1044,9 +1095,58 @@ auto Application::init_openxr() -> void create_info.applicationInfo.engineVersion = 1; create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; - std::array const extensions { - XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME, + uint32_t extension_count = 0; + auto extension_count_result = xrEnumerateInstanceExtensionProperties( + nullptr, 0, &extension_count, nullptr); + if (XR_FAILED(extension_count_result) || extension_count == 0) { + m_logger.warn("OpenXR instance extensions unavailable: {}", + xr_result_to_string(XR_NULL_HANDLE, extension_count_result)); + m_openxr.reset(); + return; + } + + std::vector available_extensions(extension_count); + for (auto &extension : available_extensions) { + extension = XrExtensionProperties {}; + extension.type = XR_TYPE_EXTENSION_PROPERTIES; + extension.next = nullptr; + } + auto enumerate_result = xrEnumerateInstanceExtensionProperties(nullptr, + extension_count, &extension_count, available_extensions.data()); + if (XR_FAILED(enumerate_result)) { + m_logger.warn("Failed to enumerate OpenXR extensions: {}", + xr_result_to_string(XR_NULL_HANDLE, enumerate_result)); + m_openxr.reset(); + return; + } + + auto has_extension = [&](char const *name) { + return std::any_of(available_extensions.begin(), + available_extensions.end(), [&](auto const &extension) { + return std::strcmp(extension.extensionName, name) == 0; + }); }; + + bool const has_enable2 + = has_extension(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME); + bool const has_enable1 = has_extension(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME); + if (!has_enable2 && !has_enable1) { + m_logger.warn("OpenXR Vulkan extensions missing"); + m_openxr.reset(); + return; + } + + std::vector extensions {}; + if (has_enable2) { + extensions.push_back(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME); + m_openxr->use_vulkan_enable2 = true; + } else { + m_openxr->use_vulkan_enable2 = false; + } + if (has_enable1) { + extensions.push_back(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME); + } + create_info.enabledExtensionCount = static_cast(extensions.size()); create_info.enabledExtensionNames = extensions.data(); @@ -1077,7 +1177,7 @@ auto Application::init_openxr() -> void "xrGetVulkanInstanceExtensionsKHR", reinterpret_cast(&get_instance_exts)); if (XR_FAILED(instance_ext_result) || !get_instance_exts) { - m_logger.warn("OpenXR missing Vulkan instance extensions"); + m_logger.warn("OpenXR missing Vulkan instance extensions (proc)"); xrDestroyInstance(m_openxr->instance); m_openxr.reset(); return; @@ -1138,12 +1238,44 @@ auto Application::init_openxr() -> void = split_extension_list(std::string_view { device_ext_string.c_str() }); m_openxr->enabled = true; - auto graphics_device_result = xrGetInstanceProcAddr(m_openxr->instance, - "xrGetVulkanGraphicsDevice2KHR", - reinterpret_cast(&m_openxr->get_graphics_device)); - if (XR_FAILED(graphics_device_result) || !m_openxr->get_graphics_device) { - m_logger.warn("OpenXR missing Vulkan graphics device hook"); - m_openxr->get_graphics_device = nullptr; + if (m_openxr->use_vulkan_enable2) { + auto graphics_device_result = xrGetInstanceProcAddr(m_openxr->instance, + "xrGetVulkanGraphicsDevice2KHR", + reinterpret_cast( + &m_openxr->get_graphics_device2)); + if (XR_FAILED(graphics_device_result) + || !m_openxr->get_graphics_device2) { + m_logger.warn("OpenXR missing Vulkan graphics device hook"); + m_openxr->get_graphics_device2 = nullptr; + } + + auto requirements_result = xrGetInstanceProcAddr(m_openxr->instance, + "xrGetVulkanGraphicsRequirements2KHR", + reinterpret_cast( + &m_openxr->get_requirements2)); + if (XR_FAILED(requirements_result) || !m_openxr->get_requirements2) { + m_logger.warn("OpenXR missing Vulkan requirements hook"); + m_openxr->get_requirements2 = nullptr; + } + } else { + auto graphics_device_result = xrGetInstanceProcAddr(m_openxr->instance, + "xrGetVulkanGraphicsDeviceKHR", + reinterpret_cast( + &m_openxr->get_graphics_device)); + if (XR_FAILED(graphics_device_result) + || !m_openxr->get_graphics_device) { + m_logger.warn("OpenXR missing Vulkan graphics device hook"); + m_openxr->get_graphics_device = nullptr; + } + + auto requirements_result = xrGetInstanceProcAddr(m_openxr->instance, + "xrGetVulkanGraphicsRequirementsKHR", + reinterpret_cast( + &m_openxr->get_requirements)); + if (XR_FAILED(requirements_result) || !m_openxr->get_requirements) { + m_logger.warn("OpenXR missing Vulkan requirements hook"); + m_openxr->get_requirements = nullptr; + } } m_logger.info("OpenXR system detected"); @@ -1154,25 +1286,93 @@ auto Application::init_openxr_session() -> void if (!m_openxr || !m_renderer) { return; } - if (!m_openxr->get_graphics_device) { - m_logger.warn("OpenXR graphics device hook unavailable"); - shutdown_openxr(); - return; - } - - XrVulkanGraphicsDeviceGetInfoKHR device_info {}; - device_info.type = XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR; - device_info.next = nullptr; - device_info.systemId = m_openxr->system_id; - device_info.vulkanInstance - = static_cast(m_renderer->instance()); VkPhysicalDevice xr_physical_device {}; - auto const phys_result = m_openxr->get_graphics_device( - m_openxr->instance, &device_info, &xr_physical_device); - if (XR_FAILED(phys_result)) { - m_logger.warn("Failed to fetch OpenXR Vulkan device"); - shutdown_openxr(); - return; + XrGraphicsBindingVulkan2KHR binding2 {}; + XrGraphicsBindingVulkanKHR binding1 {}; + void *binding_ptr = nullptr; + + if (m_openxr->use_vulkan_enable2) { + if (!m_openxr->get_graphics_device2 || !m_openxr->get_requirements2) { + m_logger.warn("OpenXR graphics device hook unavailable"); + shutdown_openxr(); + return; + } + + XrGraphicsRequirementsVulkan2KHR requirements {}; + requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR; + requirements.next = nullptr; + auto const requirements_result = m_openxr->get_requirements2( + m_openxr->instance, m_openxr->system_id, &requirements); + if (XR_FAILED(requirements_result)) { + m_logger.warn("OpenXR Vulkan requirements failed: {}", + xr_result_to_string(m_openxr->instance, requirements_result)); + shutdown_openxr(); + return; + } + + XrVulkanGraphicsDeviceGetInfoKHR device_info {}; + device_info.type = XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR; + device_info.next = nullptr; + device_info.systemId = m_openxr->system_id; + device_info.vulkanInstance + = static_cast(m_renderer->instance()); + auto const phys_result = m_openxr->get_graphics_device2( + m_openxr->instance, &device_info, &xr_physical_device); + if (XR_FAILED(phys_result)) { + m_logger.warn("Failed to fetch OpenXR Vulkan device: {}", + xr_result_to_string(m_openxr->instance, phys_result)); + shutdown_openxr(); + return; + } + + binding2.type = XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR; + binding2.next = nullptr; + binding2.instance = static_cast(m_renderer->instance()); + binding2.physicalDevice + = static_cast(m_renderer->physical_device()); + binding2.device = static_cast(m_renderer->device()); + binding2.queueFamilyIndex = m_renderer->graphics_queue_family(); + binding2.queueIndex = 0; + binding_ptr = &binding2; + } else { + if (!m_openxr->get_graphics_device || !m_openxr->get_requirements) { + m_logger.warn("OpenXR graphics device hook unavailable"); + shutdown_openxr(); + return; + } + + XrGraphicsRequirementsVulkanKHR requirements {}; + requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR; + requirements.next = nullptr; + auto const requirements_result = m_openxr->get_requirements( + m_openxr->instance, m_openxr->system_id, &requirements); + if (XR_FAILED(requirements_result)) { + m_logger.warn("OpenXR Vulkan requirements failed: {}", + xr_result_to_string(m_openxr->instance, requirements_result)); + shutdown_openxr(); + return; + } + + auto const phys_result = m_openxr->get_graphics_device( + m_openxr->instance, m_openxr->system_id, + static_cast(m_renderer->instance()), + &xr_physical_device); + if (XR_FAILED(phys_result)) { + m_logger.warn("Failed to fetch OpenXR Vulkan device: {}", + xr_result_to_string(m_openxr->instance, phys_result)); + shutdown_openxr(); + return; + } + + binding1.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR; + binding1.next = nullptr; + binding1.instance = static_cast(m_renderer->instance()); + binding1.physicalDevice + = static_cast(m_renderer->physical_device()); + binding1.device = static_cast(m_renderer->device()); + binding1.queueFamilyIndex = m_renderer->graphics_queue_family(); + binding1.queueIndex = 0; + binding_ptr = &binding1; } if (xr_physical_device @@ -1180,24 +1380,15 @@ auto Application::init_openxr_session() -> void m_logger.warn("OpenXR device differs from selected Vulkan device"); } - XrGraphicsBindingVulkan2KHR binding {}; - binding.type = XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR; - binding.next = nullptr; - binding.instance = static_cast(m_renderer->instance()); - binding.physicalDevice - = static_cast(m_renderer->physical_device()); - binding.device = static_cast(m_renderer->device()); - binding.queueFamilyIndex = m_renderer->graphics_queue_family(); - binding.queueIndex = 0; - XrSessionCreateInfo session_info {}; session_info.type = XR_TYPE_SESSION_CREATE_INFO; - session_info.next = &binding; + session_info.next = binding_ptr; session_info.systemId = m_openxr->system_id; auto const session_result = xrCreateSession( m_openxr->instance, &session_info, &m_openxr->session); if (XR_FAILED(session_result)) { - m_logger.warn("Failed to create OpenXR session"); + m_logger.warn("Failed to create OpenXR session: {}", + xr_result_to_string(m_openxr->instance, session_result)); shutdown_openxr(); return; } @@ -1210,7 +1401,8 @@ auto Application::init_openxr_session() -> void auto const space_result = xrCreateReferenceSpace( m_openxr->session, &space_info, &m_openxr->app_space); if (XR_FAILED(space_result)) { - m_logger.warn("Failed to create OpenXR space"); + m_logger.warn("Failed to create OpenXR space: {}", + xr_result_to_string(m_openxr->instance, space_result)); shutdown_openxr(); return; } @@ -1220,7 +1412,8 @@ auto Application::init_openxr_session() -> void = xrEnumerateViewConfigurationViews(m_openxr->instance, m_openxr->system_id, m_openxr->view_type, 0, &view_count, nullptr); if (XR_FAILED(view_count_result) || view_count == 0) { - m_logger.warn("OpenXR view configuration missing"); + m_logger.warn("OpenXR view configuration missing: {}", + xr_result_to_string(m_openxr->instance, view_count_result)); shutdown_openxr(); return; } @@ -1234,7 +1427,8 @@ auto Application::init_openxr_session() -> void m_openxr->instance, m_openxr->system_id, m_openxr->view_type, view_count, &view_count, m_openxr->view_configs.data()); if (XR_FAILED(view_result)) { - m_logger.warn("Failed to enumerate OpenXR views"); + m_logger.warn("Failed to enumerate OpenXR views: {}", + xr_result_to_string(m_openxr->instance, view_result)); shutdown_openxr(); return; } @@ -1249,7 +1443,8 @@ auto Application::init_openxr_session() -> void auto const format_count_result = xrEnumerateSwapchainFormats( m_openxr->session, 0, &format_count, nullptr); if (XR_FAILED(format_count_result) || format_count == 0) { - m_logger.warn("OpenXR swapchain formats unavailable"); + m_logger.warn("OpenXR swapchain formats unavailable: {}", + xr_result_to_string(m_openxr->instance, format_count_result)); shutdown_openxr(); return; } @@ -1257,7 +1452,8 @@ auto Application::init_openxr_session() -> void auto const format_result = xrEnumerateSwapchainFormats( m_openxr->session, format_count, &format_count, formats.data()); if (XR_FAILED(format_result)) { - m_logger.warn("Failed to enumerate OpenXR swapchain formats"); + m_logger.warn("Failed to enumerate OpenXR swapchain formats: {}", + xr_result_to_string(m_openxr->instance, format_result)); shutdown_openxr(); return; } @@ -1298,7 +1494,8 @@ auto Application::init_openxr_session() -> void auto const swapchain_result = xrCreateSwapchain(m_openxr->session, &swapchain_info, &m_openxr->swapchains[i].handle); if (XR_FAILED(swapchain_result)) { - m_logger.warn("Failed to create OpenXR swapchain"); + m_logger.warn("Failed to create OpenXR swapchain: {}", + xr_result_to_string(m_openxr->instance, swapchain_result)); shutdown_openxr(); return; } @@ -1310,7 +1507,8 @@ auto Application::init_openxr_session() -> void auto const image_count_result = xrEnumerateSwapchainImages( m_openxr->swapchains[i].handle, 0, &image_count, nullptr); if (XR_FAILED(image_count_result) || image_count == 0) { - m_logger.warn("Failed to enumerate OpenXR swapchain images"); + m_logger.warn("Failed to enumerate OpenXR swapchain images: {}", + xr_result_to_string(m_openxr->instance, image_count_result)); shutdown_openxr(); return; } @@ -1325,7 +1523,8 @@ auto Application::init_openxr_session() -> void reinterpret_cast( m_openxr->swapchains[i].images.data())); if (XR_FAILED(image_result)) { - m_logger.warn("Failed to read OpenXR swapchain images"); + m_logger.warn("Failed to read OpenXR swapchain images: {}", + xr_result_to_string(m_openxr->instance, image_result)); shutdown_openxr(); return; } diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp index 27269ef..312e0cc 100644 --- a/src/VulkanRenderer.cpp +++ b/src/VulkanRenderer.cpp @@ -1097,13 +1097,17 @@ auto VulkanRenderer::vk_init() -> void VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, }; + std::vector required_extensions {}; for (auto const &extension : m_extra_device_extensions) { - desired_extensions.push_back(extension.c_str()); + required_extensions.push_back(extension.c_str()); } phys_device_selector.set_surface(m_vk.surface) .add_desired_extensions(desired_extensions) .set_required_features_13(features_13) .add_required_extension_features(buffer_device_address_features); + if (!required_extensions.empty()) { + phys_device_selector.add_required_extensions(required_extensions); + } auto physical_device_selector_return { phys_device_selector.select() }; if (!physical_device_selector_return) { std::println(std::cerr,