@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -49,6 +49,13 @@
|
||||
wlroots
|
||||
vulkan-loader
|
||||
|
||||
# For raylib
|
||||
xorg.libXrandr
|
||||
xorg.libXinerama
|
||||
xorg.libXcursor
|
||||
xorg.libXi
|
||||
glfw
|
||||
|
||||
libffi
|
||||
wayland
|
||||
wayland-scanner
|
||||
|
||||
572
src/LunarWM.cppm
572
src/LunarWM.cppm
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user