Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-07-09 07:20:12 +03:00
parent cecea144e4
commit 9fa2a1e8c7
3 changed files with 417 additions and 178 deletions

View File

@@ -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
)

View File

@@ -49,6 +49,13 @@
wlroots
vulkan-loader
# For raylib
xorg.libXrandr
xorg.libXinerama
xorg.libXcursor
xorg.libXi
glfw
libffi
wayland
wayland-scanner

View File

@@ -25,12 +25,20 @@ extern "C" {
#include <wlr/util/log.h>
}
PFNGLDRAWBUFFERSEXTPROC glDrawBuffersEXT = NULL;
#include <raylib.h>
#include <raymath.h>
#include <rlgl.h>
export module LunarWM.LunarWM;
import std;
import LunarWM.Math;
using Clock = std::chrono::high_resolution_clock;
namespace std {
template <> struct formatter<XrResult, char> {
template <class ParseContext> constexpr auto parse(ParseContext &ctx) {
@@ -39,7 +47,8 @@ template <> struct formatter<XrResult, char> {
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<GLuint> 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<GLuint> image_views;
};
std::optional<XrInstance> instance;
std::optional<XrSystemId> system_id;
XrSession session{XR_NULL_HANDLE};
@@ -607,52 +665,97 @@ private:
std::pair<SwapchainType, std::vector<XrSwapchainImageOpenGLESKHR>>>
swapchain_images_map;
std::vector<XrViewConfigurationView> view_configuration_views;
XrEnvironmentBlendMode environment_blend_mode;
std::vector<XrViewConfigurationView> 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<XrCompositionLayerProjectionView> layer_projection_views;
};
std::chrono::time_point<Clock> 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<std::int64_t> swapchain_format_depth_needles{
GL_DEPTH_COMPONENT32F,
GL_DEPTH_COMPONENT24,
GL_DEPTH_COMPONENT16,
// GL_DEPTH_COMPONENT32F,
// GL_DEPTH_COMPONENT24,
};
const std::vector<int64_t>::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<std::int64_t> swapchain_format_color_needles{GL_RGBA8,
GL_RGBA8_SNORM};
std::vector<std::int64_t> swapchain_format_color_needles{GL_SRGB8_ALPHA8};
const std::vector<int64_t>::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<XrSwapchainImageBaseHeader *>(
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<XrEnvironmentBlendMode> requested_environment_blend_modes {
XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
XR_ENVIRONMENT_BLEND_MODE_ADDITIVE,
XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM,
};
std::vector<XrEnvironmentBlendMode> 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<XrView> 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<XrView> 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<XrCompositionLayerBaseHeader*>(&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<float>(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<XrCompositionLayerBaseHeader *>(&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<uint32_t>(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<XrCompositionLayerBaseHeader *>(
&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<uint32_t>(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");
}
}
}