From 9fa2a1e8c73d7edf07101772d6bdf191168f17de Mon Sep 17 00:00:00 2001 From: Slendi Date: Wed, 9 Jul 2025 07:20:12 +0300 Subject: [PATCH] Raylib Signed-off-by: Slendi --- CMakeLists.txt | 16 +- flake.nix | 7 + src/LunarWM.cppm | 572 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 417 insertions(+), 178 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d42907d..b4c9d1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ add_compile_definitions( XR_USE_GRAPHICS_API_OPENGL_ES ) -find_package(PkgConfig) +find_package(PkgConfig REQUIRED) pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET GLOBAL wayland-server) pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl) @@ -22,6 +22,19 @@ pkg_check_modules(GLES2 REQUIRED IMPORTED_TARGET glesv2) pkg_check_modules(WLROOTS REQUIRED IMPORTED_TARGET wlroots-0.19) pkg_check_modules(OPENXR REQUIRED IMPORTED_TARGET openxr) +include(FetchContent) + +FetchContent_Declare( + raylib + GIT_REPOSITORY https://github.com/slendidev/raylib.git + GIT_TAG "lunar" + GIT_SHALLOW 1 +) +set(OPENGL_VERSION "ES 3.0") +set(PLATFORM DRM) +set(BUILD_EXAMPLES OFF) +FetchContent_MakeAvailable(raylib) + add_executable(${PROJECT_NAME}) target_sources(${PROJECT_NAME} PUBLIC src/main.cpp @@ -37,5 +50,6 @@ target_link_libraries(${PROJECT_NAME} PUBLIC PkgConfig::GLES2 PkgConfig::WLROOTS PkgConfig::OPENXR + raylib ) diff --git a/flake.nix b/flake.nix index 97c261e..1019b09 100644 --- a/flake.nix +++ b/flake.nix @@ -49,6 +49,13 @@ wlroots vulkan-loader + # For raylib + xorg.libXrandr + xorg.libXinerama + xorg.libXcursor + xorg.libXi + glfw + libffi wayland wayland-scanner diff --git a/src/LunarWM.cppm b/src/LunarWM.cppm index 44bff64..ce278f6 100644 --- a/src/LunarWM.cppm +++ b/src/LunarWM.cppm @@ -25,12 +25,20 @@ extern "C" { #include } +PFNGLDRAWBUFFERSEXTPROC glDrawBuffersEXT = NULL; + +#include +#include +#include + export module LunarWM.LunarWM; import std; import LunarWM.Math; +using Clock = std::chrono::high_resolution_clock; + namespace std { template <> struct formatter { template constexpr auto parse(ParseContext &ctx) { @@ -39,7 +47,8 @@ template <> struct formatter { static constexpr std::string_view to_string(XrResult r) { switch (r) { - case XR_FRAME_DISCARDED: return "XR_FRAME_DISCARDED"; + case XR_FRAME_DISCARDED: + return "XR_FRAME_DISCARDED"; case XR_ERROR_VALIDATION_FAILURE: return "XR_ERROR_VALIDATION_FAILURE: The function usage was invalid in " "some way."; @@ -539,11 +548,65 @@ private: namespace LunarWM { +static Matrix xr_projection_matrix(const XrFovf& fov) +{ + static_assert(RL_CULL_DISTANCE_FAR > RL_CULL_DISTANCE_NEAR); + + Matrix matrix{}; + + const float near = (float)RL_CULL_DISTANCE_NEAR; + const float far = (float)RL_CULL_DISTANCE_FAR; + + const float tan_angle_left = tanf(fov.angleLeft); + const float tan_angle_right = tanf(fov.angleRight); + + const float tan_angle_down = tanf(fov.angleDown); + const float tan_angle_up = tanf(fov.angleUp); + + const float tan_angle_width = tan_angle_right - tan_angle_left; + const float tan_angle_height = tan_angle_up - tan_angle_down; + + matrix.m0 = 2 / tan_angle_width; + matrix.m4 = 0; + matrix.m8 = (tan_angle_right + tan_angle_left) / tan_angle_width; + matrix.m12 = 0; + + matrix.m1 = 0; + matrix.m5 = 2 / tan_angle_height; + matrix.m9 = (tan_angle_up + tan_angle_down) / tan_angle_height; + matrix.m13 = 0; + + matrix.m2 = 0; + matrix.m6 = 0; + matrix.m10 = -(far + near) / (far - near); + matrix.m14 = -(far * (near + near)) / (far - near); + + matrix.m3 = 0; + matrix.m7 = 0; + matrix.m11 = -1; + matrix.m15 = 0; + + return matrix; +} + +static Matrix xr_matrix(const XrPosef& pose) +{ + Matrix translation = MatrixTranslate(pose.position.x, pose.position.y, pose.position.z); + Matrix rotation = QuaternionToMatrix(Quaternion{pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w}); + return rotation * translation; +} + enum class SwapchainType { Color, Depth, }; +struct SwapchainInfo { + XrSwapchain swapchain{XR_NULL_HANDLE}; + std::int64_t swapchain_format{}; + std::vector image_views; +}; + export struct LunarWM { LunarWM() = default; ~LunarWM(); @@ -560,7 +623,8 @@ private: void init_xr(); void poll_events_xr(); - bool render_layer(RenderLayerInfo &info); + bool render_layer(RenderLayerInfo &info, float dt); + void render_3d(float dt); bool m_initialized{}; @@ -587,12 +651,6 @@ private: } m_wayland; struct { - struct SwapchainInfo { - XrSwapchain swapchain{XR_NULL_HANDLE}; - std::int64_t swapchain_format{}; - std::vector image_views; - }; - std::optional instance; std::optional system_id; XrSession session{XR_NULL_HANDLE}; @@ -607,52 +665,97 @@ private: std::pair>> swapchain_images_map; - std::vector view_configuration_views; - XrEnvironmentBlendMode environment_blend_mode; + std::vector view_configuration_views; + XrEnvironmentBlendMode environment_blend_mode; XrSpace local_space{XR_NULL_HANDLE}; + XrSpace view_space{XR_NULL_HANDLE}; std::uint32_t get_swapchain_image(XrSwapchain swapchain, uint32_t index) { return swapchain_images_map[swapchain].second[index].image; } } m_xr; - struct { - GLuint vertex_array{}; - GLuint set_framebuffer{}; + struct RendererFrame { + GLuint fbo{0}; + uint32_t color_idx{UINT32_MAX}; + uint32_t depth_idx{UINT32_MAX}; + bool has_depth{}; + }; - void begin_rendering() { - glGenVertexArrays(1, &vertex_array); - glBindVertexArray(vertex_array); + struct { + GLuint fbo{}; + RenderTexture2D tmp_rt{}; - glGenFramebuffers(1, &set_framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, set_framebuffer); + Camera3D camera{ + .position = { 0, 0, 0 }, + .target = { 0, 0, 1 }, + .up = { 0, 1, 0 }, + .fovy = 45, + .projection = CAMERA_PERSPECTIVE, + }; + + bool begin_render(GLuint color_tex, GLuint depth_tex, uint32_t w, uint32_t h, const XrView& view) { + if (!fbo) // create once + glGenFramebuffers(1, &fbo); + + rlFramebufferAttach(fbo, color_tex, RL_ATTACHMENT_COLOR_CHANNEL0, + RL_ATTACHMENT_TEXTURE2D, 0); + + assert(rlFramebufferComplete(fbo)); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + GLenum fbStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if(fbStatus != GL_FRAMEBUFFER_COMPLETE){ + printf("FBO incomplete: 0x%04x\n", fbStatus); + return false; + } + + tmp_rt = {.id = fbo, + .texture = {color_tex, (int)w, (int)h, 1, -1}, + .depth = {depth_tex, (int)w, (int)h, 1, -1}}; + + BeginTextureMode(tmp_rt); + rlEnableStereoRender(); + + const XrFovf &fov = view.fov; + Matrix proj = xr_projection_matrix(fov); + Matrix viewM = MatrixInvert(xr_matrix(view.pose)); + + rlSetMatrixProjectionStereo(proj, proj); + rlSetMatrixViewOffsetStereo(viewM, viewM); + + return true; + } + + void end_render(LunarWM &wm, SwapchainInfo &color_sc, + SwapchainInfo *depth_sc) { + rlDisableStereoRender(); + EndTextureMode(); + } + + void update_camera(LunarWM &wm, Camera3D &camera, RenderLayerInfo &info) + { + const XrTime time = info.predicted_display_time; + + XrSpaceLocation view_location{ XR_TYPE_SPACE_LOCATION }; + XrResult result = xrLocateSpace(wm.m_xr.view_space, wm.m_xr.local_space, time, &view_location); + if (result != XR_SUCCESS) { + return; + } + + if (view_location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) { + const auto& pos = view_location.pose.position; + camera.position = Vector3{ pos.x, pos.y, pos.z }; + } + if (view_location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) { + const auto& rot = view_location.pose.orientation; + const auto forward = Vector3RotateByQuaternion(Vector3{ 0, 0, -1 }, Quaternion{ rot.x, rot.y, rot.z, rot.w }); + const auto up = Vector3RotateByQuaternion(Vector3{ 0, 1, 0 }, Quaternion{ rot.x, rot.y, rot.z, rot.w }); + camera.target = camera.position + forward; + camera.up = up; + } } - - void end_rendering() { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &set_framebuffer); - set_framebuffer = 0; - - glBindVertexArray(0); - glDeleteVertexArrays(1, &vertex_array); - vertex_array = 0; - } - - void clear_color(GLuint image_view, float r, float g, float b, float a) { - glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)(uint64_t)image_view); - glClearColor(r, g, b, a); - glClear(GL_COLOR_BUFFER_BIT); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - - void clear_depth(GLuint image_view, float d) { - glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)(uint64_t)image_view); - glClearDepthf(d); - glClear(GL_DEPTH_BUFFER_BIT); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - } m_renderer; + } m_renderer; struct RenderLayerInfo { XrTime predicted_display_time; @@ -664,6 +767,8 @@ private: std::vector layer_projection_views; }; + std::chrono::time_point m_last_tick; + bool m_running{}; bool m_session_running{}; bool m_session_state{}; @@ -671,14 +776,28 @@ private: void LunarWM::init() { this->init_wayland(); - if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + + wlr_log(WLR_INFO, "0"); + auto draw = eglGetCurrentSurface(EGL_DRAW); + auto read = eglGetCurrentSurface(EGL_READ); + if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + m_wayland.egl_context) == EGL_FALSE) { + throw std::runtime_error("Failed to eglMakeCurrent"); + } + + wlr_log(WLR_INFO, "1"); + this->init_xr(); + wlr_log(WLR_INFO, "2"); + + wlr_log(WLR_INFO, "OpenGL ES version: %s", glGetString(GL_VERSION)); + InitWindow(0, 0, ""); + wlr_log(WLR_INFO, "3"); + + if (eglMakeCurrent(m_wayland.egl_display, read, draw, m_wayland.egl_context) == EGL_FALSE) { throw std::runtime_error("Failed to eglMakeCurrent"); } - this->init_xr(); - - wlr_log(WLR_INFO, "OpenGL ES version: %s", glGetString(GL_VERSION)); - + wlr_log(WLR_INFO, "4"); m_initialized = true; } @@ -915,6 +1034,7 @@ void LunarWM::init_xr() { glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); { + glDrawBuffersEXT = (PFNGLDRAWBUFFERSEXTPROC) eglGetProcAddress("glDrawBuffersEXT"); XrGraphicsBindingEGLMNDX gbind = { .type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX, .next = nullptr, @@ -969,7 +1089,8 @@ void LunarWM::init_xr() { throw std::runtime_error( "Failed to get amount of OpenXR view configuration views"); } - m_xr.view_configuration_views.resize(count, {XR_TYPE_VIEW_CONFIGURATION_VIEW}); + m_xr.view_configuration_views.resize(count, + {XR_TYPE_VIEW_CONFIGURATION_VIEW}); if (xrEnumerateViewConfigurationViews( *m_xr.instance, *m_xr.system_id, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, count, &count, @@ -995,9 +1116,9 @@ void LunarWM::init_xr() { } std::vector swapchain_format_depth_needles{ - GL_DEPTH_COMPONENT32F, - GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT16, + // GL_DEPTH_COMPONENT32F, + // GL_DEPTH_COMPONENT24, }; const std::vector::const_iterator &swapchain_format_depth_it = std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(), @@ -1009,8 +1130,7 @@ void LunarWM::init_xr() { } auto const swapchain_format_depth = *swapchain_format_depth_it; - std::vector swapchain_format_color_needles{GL_RGBA8, - GL_RGBA8_SNORM}; + std::vector swapchain_format_color_needles{GL_SRGB8_ALPHA8}; const std::vector::const_iterator &swapchain_format_color_it = std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(), std::begin(swapchain_format_color_needles), @@ -1021,9 +1141,6 @@ void LunarWM::init_xr() { } auto const swapchain_format_color = *swapchain_format_color_it; - m_xr.swapchains.color.resize(m_xr.view_configuration_views.size()); - m_xr.swapchains.depth.resize(m_xr.view_configuration_views.size()); - auto const AllocateSwapchainImageData = [&](XrSwapchain swapchain, SwapchainType type, uint32_t count) { m_xr.swapchain_images_map[swapchain].first = type; @@ -1032,10 +1149,19 @@ void LunarWM::init_xr() { return reinterpret_cast( m_xr.swapchain_images_map[swapchain].second.data()); }; - for (std::size_t i = 0; i < m_xr.view_configuration_views.size(); i++) { - auto &color_sc = m_xr.swapchains.color[i]; - auto &depth_sc = m_xr.swapchains.depth[i]; - auto &vcv = m_xr.view_configuration_views[i]; + + const uint32_t view_count = (uint32_t)m_xr.view_configuration_views.size(); + const uint32_t eyeW = m_xr.view_configuration_views[0].recommendedImageRectWidth; + const uint32_t eyeH = m_xr.view_configuration_views[0].recommendedImageRectHeight; + const uint32_t bufW = eyeW * view_count; + + m_xr.swapchains.color.resize(1); + m_xr.swapchains.depth.resize(1); + + { + auto &color_sc = m_xr.swapchains.color[0]; + auto &depth_sc = m_xr.swapchains.depth[0]; + auto &vcv = m_xr.view_configuration_views[0]; { // Create color swapchain XrSwapchainCreateInfo ci{ @@ -1046,8 +1172,8 @@ void LunarWM::init_xr() { XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, .format = swapchain_format_color, .sampleCount = vcv.recommendedSwapchainSampleCount, - .width = vcv.recommendedImageRectWidth, - .height = vcv.recommendedImageRectHeight, + .width = bufW, + .height = eyeH, .faceCount = 1, .arraySize = 1, .mipCount = 1, @@ -1069,8 +1195,8 @@ void LunarWM::init_xr() { XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .format = swapchain_format_depth, .sampleCount = vcv.recommendedSwapchainSampleCount, - .width = vcv.recommendedImageRectWidth, - .height = vcv.recommendedImageRectHeight, + .width = bufW, + .height = eyeH, .faceCount = 1, .arraySize = 1, .mipCount = 1, @@ -1178,14 +1304,14 @@ void LunarWM::init_xr() { throw std::runtime_error("Failed to get XR environment blend modes"); } } - std::vector requested_environment_blend_modes { - XR_ENVIRONMENT_BLEND_MODE_OPAQUE, - XR_ENVIRONMENT_BLEND_MODE_ADDITIVE, - XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM, - }; + std::vector requested_environment_blend_modes{ + XR_ENVIRONMENT_BLEND_MODE_OPAQUE, + XR_ENVIRONMENT_BLEND_MODE_ADDITIVE, + XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM, + }; for (auto const &bm : requested_environment_blend_modes) { - m_xr.environment_blend_mode = bm; + m_xr.environment_blend_mode = bm; if (std::ranges::contains(environment_blend_modes, bm)) { break; } @@ -1200,7 +1326,7 @@ void LunarWM::init_xr() { XrReferenceSpaceCreateInfo ci{ .type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO, .next = nullptr, - .referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL, + .referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE, .poseInReferenceSpace = {.orientation = {0.0f, 0.0f, 0.0f, 1.0f}, .position = {0.0f, 0.0f, 0.0f}}, }; @@ -1210,6 +1336,21 @@ void LunarWM::init_xr() { throw std::runtime_error("Failed to create OpenXR reference space"); } } + + { // View reference space + XrReferenceSpaceCreateInfo ci{ + .type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO, + .next = nullptr, + .referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW, + .poseInReferenceSpace = {.orientation = {0.0f, 0.0f, 0.0f, 1.0f}, + .position = {0.0f, 0.0f, 0.0f}}, + }; + + if (xrCreateReferenceSpace(m_xr.session, &ci, &m_xr.view_space) != + XR_SUCCESS) { + throw std::runtime_error("Failed to create OpenXR reference space"); + } + } } void LunarWM::poll_events_xr() { @@ -1305,83 +1446,134 @@ void LunarWM::poll_events_xr() { } } -bool LunarWM::render_layer(RenderLayerInfo &info) { - std::vector views(m_xr.view_configuration_views.size(), {XR_TYPE_VIEW}); +bool LunarWM::render_layer(RenderLayerInfo &info, float dt) +{ + const uint32_t view_count = (uint32_t)m_xr.view_configuration_views.size(); + std::vector views(view_count, { XR_TYPE_VIEW }); - XrViewState view_state{XR_TYPE_VIEW_STATE}; - XrViewLocateInfo view_locate{XR_TYPE_VIEW_LOCATE_INFO}; - view_locate.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - view_locate.displayTime = info.predicted_display_time; - view_locate.space = m_xr.local_space; + XrViewLocateInfo locInfo{ XR_TYPE_VIEW_LOCATE_INFO }; + locInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + locInfo.displayTime = info.predicted_display_time; + locInfo.space = m_xr.local_space; - uint32_t view_count = 0; - if (xrLocateViews(m_xr.session, &view_locate, &view_state, - (uint32_t)views.size(), &view_count, views.data()) != XR_SUCCESS) { + XrViewState viewState{ XR_TYPE_VIEW_STATE }; + uint32_t located = 0; + if (xrLocateViews(m_xr.session, &locInfo, &viewState, + view_count, &located, views.data()) != XR_SUCCESS || + located != view_count) + { wlr_log(WLR_ERROR, "Failed to locate views"); return false; } - info.layer_projection_views.resize(view_count, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}); + auto &color_sc = m_xr.swapchains.color[0]; + auto &depth_sc = m_xr.swapchains.depth[0]; - auto acquire_wait = [](XrSwapchain sc, uint32_t &idx) -> bool { - XrSwapchainImageAcquireInfo ai{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + auto acquire_wait = [](XrSwapchain sc, uint32_t &idx)->bool{ + XrSwapchainImageAcquireInfo ai{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO }; if (xrAcquireSwapchainImage(sc, &ai, &idx) != XR_SUCCESS) return false; - - XrSwapchainImageWaitInfo wi{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + XrSwapchainImageWaitInfo wi{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO }; wi.timeout = XR_INFINITE_DURATION; return xrWaitSwapchainImage(sc, &wi) == XR_SUCCESS; }; - auto release = [](XrSwapchain sc) { - XrSwapchainImageReleaseInfo ri{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; - xrReleaseSwapchainImage(sc, &ri); - }; - for (uint32_t i = 0; i < view_count; ++i) { - auto &color_sc = m_xr.swapchains.color[i]; - auto &depth_sc = m_xr.swapchains.depth[i]; - - uint32_t color_idx = 0, depth_idx = 0; - if (!acquire_wait(color_sc.swapchain, color_idx) || - !acquire_wait(depth_sc.swapchain, depth_idx)) { - wlr_log(WLR_ERROR, "Failed to acquire swapchain images"); - continue; - } - - const uint32_t w = m_xr.view_configuration_views[i].recommendedImageRectWidth; - const uint32_t h = m_xr.view_configuration_views[i].recommendedImageRectHeight; - - m_renderer.begin_rendering(); - { - if (m_xr.environment_blend_mode == XR_ENVIRONMENT_BLEND_MODE_OPAQUE) - m_renderer.clear_color(color_sc.image_views[color_idx], 0.17f, 0.17f, 0.17f, 1.0f); - else - m_renderer.clear_color(color_sc.image_views[color_idx], 0.0f, 0.0f, 0.0f, 1.0f); - - m_renderer.clear_depth(depth_sc.image_views[depth_idx], 1.0f); - } - m_renderer.end_rendering(); - - auto &pv = info.layer_projection_views[i]; - pv.pose = views[i].pose; - pv.fov = views[i].fov; - pv.subImage.swapchain = color_sc.swapchain; - pv.subImage.imageRect = {{0, 0}, {(int32_t)w, (int32_t)h}}; - pv.subImage.imageArrayIndex = 0; - - release(color_sc.swapchain); - release(depth_sc.swapchain); + uint32_t colIdx = 0, depIdx = 0; + if (!acquire_wait(color_sc.swapchain, colIdx) || + !acquire_wait(depth_sc.swapchain, depIdx)) + { + wlr_log(WLR_ERROR, "Swap-chain acquire failed"); + return false; } + GLuint color_tex = m_xr.get_swapchain_image(color_sc.swapchain, colIdx); + GLuint depth_tex = m_xr.get_swapchain_image(depth_sc.swapchain, depIdx); + + if (!m_renderer.fbo) glGenFramebuffers(1, &m_renderer.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, m_renderer.fbo); + + rlFramebufferAttach(m_renderer.fbo, color_tex, RL_ATTACHMENT_COLOR_CHANNEL0, + RL_ATTACHMENT_TEXTURE2D, 0); + rlFramebufferAttach(m_renderer.fbo, depth_tex, RL_ATTACHMENT_DEPTH, + RL_ATTACHMENT_TEXTURE2D, 0); + assert(rlFramebufferComplete(m_renderer.fbo)); + + const uint32_t eyeW = m_xr.view_configuration_views[0].recommendedImageRectWidth; + const uint32_t eyeH = m_xr.view_configuration_views[0].recommendedImageRectHeight; + + m_renderer.tmp_rt = { m_renderer.fbo, + { color_tex, (int)(eyeW*view_count), (int)eyeH, 1, -1 }, + { depth_tex, (int)(eyeW*view_count), (int)eyeH, 1, -1 } }; + + Matrix projL = xr_projection_matrix(views[0].fov); + Matrix projR = xr_projection_matrix(views[1].fov); + Matrix viewL = MatrixInvert( xr_matrix(views[0].pose) ); + Matrix viewR = MatrixInvert( xr_matrix(views[1].pose) ); + + BeginTextureMode(m_renderer.tmp_rt); + + rlEnableStereoRender(); + rlSetMatrixProjectionStereo(projL, projR); + rlSetMatrixViewOffsetStereo(viewL, viewR); + + glViewport(0, 0, (GLsizei)(eyeW*view_count), (GLsizei)eyeH); + ClearBackground(RAYWHITE); + + BeginMode3D(m_renderer.camera); + { + this->render_3d(dt); + } + EndMode3D(); + + rlDisableStereoRender(); + EndTextureMode(); + + XrSwapchainImageReleaseInfo ri{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO }; + xrReleaseSwapchainImage(color_sc.swapchain, &ri); + xrReleaseSwapchainImage(depth_sc.swapchain, &ri); + + info.layer_projection_views.resize(view_count, + { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW }); + + for (uint32_t i = 0; i < view_count; ++i) + { + const int32_t xOff = (int32_t)i * (int32_t)eyeW; + + auto &pv = info.layer_projection_views[i]; + pv.pose = views[i].pose; + pv.fov = views[i].fov; + pv.subImage.swapchain = color_sc.swapchain; + pv.subImage.imageRect = { { xOff, 0 }, + { (int32_t)eyeW, (int32_t)eyeH } }; + pv.subImage.imageArrayIndex = 0; + } + + info.layer_projection.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + info.layer_projection.space = m_xr.local_space; + info.layer_projection.viewCount = view_count; + info.layer_projection.views = info.layer_projection_views.data(); info.layer_projection.layerFlags = - XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | - XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; - info.layer_projection.space = m_xr.local_space; - info.layer_projection.viewCount = (uint32_t)info.layer_projection_views.size(); - info.layer_projection.views = info.layer_projection_views.data(); + XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | + XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + + info.layers.clear(); + info.layers.push_back( + reinterpret_cast(&info.layer_projection)); return true; } +void LunarWM::render_3d(float dt) { + static float animT {0.0f}; + animT += dt; + + DrawGrid(10, 1); + + Vector3 forward = Vector3Normalize(Vector3Subtract(m_renderer.camera.target, m_renderer.camera.position)); + float distance = 5.0f + sinf(animT) * 3.0f; + Vector3 spherePos = Vector3Add(m_renderer.camera.position, Vector3Scale(forward, distance)); + DrawSphere(spherePos, 0.5f, YELLOW); +} + LunarWM::~LunarWM() { assert(m_initialized); @@ -1435,62 +1627,88 @@ void LunarWM::run() { } auto const *socket = wl_display_add_socket_auto(m_wayland.display); - if (!socket) { - throw std::runtime_error("Failed to add wayland socket to display"); - } + if (!socket) { + throw std::runtime_error("Failed to add wayland socket to display"); + } - setenv("WAYLAND_DISPLAY", socket, true); - wlr_log(WLR_INFO, "Running compositor on WAYLAND_DISPLAY=%s", socket); + setenv("WAYLAND_DISPLAY", socket, true); + wlr_log(WLR_INFO, "Running compositor on WAYLAND_DISPLAY=%s", socket); - m_running = true; - while (m_running) { - wl_display_flush_clients(m_wayland.display); - wl_event_loop_dispatch(m_wayland.event_loop, 0); + m_running = true; + while (m_running) { + auto now = Clock::now(); + float dt = std::chrono::duration(now - m_last_tick).count(); + m_last_tick = now; - poll_events_xr(); + wl_display_flush_clients(m_wayland.display); + wl_event_loop_dispatch(m_wayland.event_loop, 0); - XrFrameState frame_state{ - .type = XR_TYPE_FRAME_STATE, - }; - XrFrameWaitInfo frame_wait_info{ - .type = XR_TYPE_FRAME_WAIT_INFO, - .next = nullptr, - }; - if (xrWaitFrame(m_xr.session, &frame_wait_info, &frame_state) != - XR_SUCCESS) { - throw std::runtime_error("Failed to wait for OpenXR frame"); + auto draw = eglGetCurrentSurface(EGL_DRAW); + auto read = eglGetCurrentSurface(EGL_READ); + if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + m_wayland.egl_context) == EGL_FALSE) { + throw std::runtime_error("Failed to eglMakeCurrent"); } - XrFrameBeginInfo frame_begin_info{ - .type = XR_TYPE_FRAME_BEGIN_INFO, - .next = nullptr, - }; - XrResult res = xrBeginFrame(m_xr.session, &frame_begin_info); - if (res != XR_FRAME_DISCARDED && res != XR_SUCCESS) { - throw std::runtime_error( - std::format("Failed to begin the OpenXR Frame: {}", res)); - } + poll_events_xr(); - RenderLayerInfo render_layer_info{ - .predicted_display_time = frame_state.predictedDisplayTime, - }; - bool session_active = (m_xr.session_state == XR_SESSION_STATE_SYNCHRONIZED || m_xr.session_state == XR_SESSION_STATE_VISIBLE || m_xr.session_state == XR_SESSION_STATE_FOCUSED); - if (session_active && frame_state.shouldRender) { - auto rendered = this->render_layer(render_layer_info); - if (rendered) { - render_layer_info.layers.push_back(reinterpret_cast(&render_layer_info.layer_projection)); - } - } + XrFrameState frame_state{ + .type = XR_TYPE_FRAME_STATE, + }; + XrFrameWaitInfo frame_wait_info{ + .type = XR_TYPE_FRAME_WAIT_INFO, + .next = nullptr, + }; + if (xrWaitFrame(m_xr.session, &frame_wait_info, &frame_state) != + XR_SUCCESS) { + throw std::runtime_error("Failed to wait for OpenXR frame"); + } - XrFrameEndInfo frame_end_info { - .type = XR_TYPE_FRAME_END_INFO, - .displayTime = frame_state.predictedDisplayTime, - .environmentBlendMode = m_xr.environment_blend_mode, - .layerCount = static_cast(render_layer_info.layers.size()), - .layers = render_layer_info.layers.data(), - }; - if (xrEndFrame(m_xr.session, &frame_end_info) != XR_SUCCESS) { - throw std::runtime_error("Failed to end OpenXR frame"); + XrFrameBeginInfo frame_begin_info{ + .type = XR_TYPE_FRAME_BEGIN_INFO, + .next = nullptr, + }; + XrResult res = xrBeginFrame(m_xr.session, &frame_begin_info); + if (res != XR_FRAME_DISCARDED && res != XR_SUCCESS) { + throw std::runtime_error( + std::format("Failed to begin the OpenXR Frame: {}", res)); + } + + WindowShouldClose(); + + RenderLayerInfo render_layer_info{ + .predicted_display_time = frame_state.predictedDisplayTime, + }; + bool session_active = + (m_xr.session_state == XR_SESSION_STATE_SYNCHRONIZED || + m_xr.session_state == XR_SESSION_STATE_VISIBLE || + m_xr.session_state == XR_SESSION_STATE_FOCUSED); + if (session_active && frame_state.shouldRender) { + auto rendered = this->render_layer(render_layer_info, dt); + if (rendered) { + render_layer_info.layers.push_back( + reinterpret_cast( + &render_layer_info.layer_projection)); + } + } + + XrFrameEndInfo frame_end_info{ + .type = XR_TYPE_FRAME_END_INFO, + .displayTime = frame_state.predictedDisplayTime, + .environmentBlendMode = m_xr.environment_blend_mode, + .layerCount = static_cast(render_layer_info.layers.size()), + .layers = render_layer_info.layers.data(), + }; + if (xrEndFrame(m_xr.session, &frame_end_info) != XR_SUCCESS) { + throw std::runtime_error("Failed to end OpenXR frame"); + } + + BeginDrawing(); + EndDrawing(); + + if (eglMakeCurrent(m_wayland.egl_display, read, draw, + m_wayland.egl_context) == EGL_FALSE) { + throw std::runtime_error("Failed to eglMakeCurrent"); } } }