Add helper scripts and wlroots integration

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-06-29 17:34:45 +03:00
parent 1d99b16685
commit d83e7f4337
5 changed files with 232 additions and 19 deletions

View File

@@ -1,6 +1,13 @@
#include "compositor.h" #include "compositor.h"
#include "common.h" #include "common.h"
#include <wayland-server.h>
// wlroots is a C library; wrap headers in extern "C"
extern "C" {
#include <wlr/backend.h>
#include <wlr/util/log.h>
}
#include "wlr/openxr_gl.h"
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
@@ -193,10 +200,40 @@ void Compositor::create_session()
ci.createFlags = 0; ci.createFlags = 0;
OPENXR_CHECK(xrCreateSession, m_xr_instance, &ci, &m_session); OPENXR_CHECK(xrCreateSession, m_xr_instance, &ci, &m_session);
} }
// --- wlroots integration: create Wayland display and OpenXR wlroots
// backend --- initialize wlroots logging
wlr_log_init(WLR_DEBUG, nullptr);
// create Wayland display
m_display = wl_display_create();
if (!m_display) {
throw std::runtime_error("Failed to create Wayland display");
}
// get event loop
m_event_loop = wl_display_get_event_loop(m_display);
if (!m_event_loop) {
wl_display_destroy(m_display);
throw std::runtime_error("Failed to get Wayland event loop");
}
// create the OpenXR wlroots backend
m_backend = wlr_openxr_backend_create(m_display, m_event_loop);
if (!m_backend || !wlr_backend_is_openxr(m_backend)) {
wl_display_destroy(m_display);
throw std::runtime_error("Failed to create OpenXR wlroots backend");
}
} }
void Compositor::destroy_session() void Compositor::destroy_session()
{ {
// destroy wlroots backend and Wayland display
if (m_backend) {
wlr_backend_destroy(m_backend);
m_backend = nullptr;
}
if (m_display) {
wl_display_destroy(m_display);
m_display = nullptr;
}
// destroy OpenXR session
OPENXR_CHECK(xrDestroySession, m_session); OPENXR_CHECK(xrDestroySession, m_session);
} }
@@ -249,11 +286,17 @@ void Compositor::poll_events()
void Compositor::run() void Compositor::run()
{ {
// start the wlroots backend (this also begins XrSession)
if (!wlr_backend_start(m_backend)) {
throw std::runtime_error("Failed to start wlroots backend");
}
while (m_running) { while (m_running) {
this->poll_events(); this->poll_events();
// dispatch Wayland events
wl_event_loop_dispatch(m_event_loop, 0);
if (m_session_running) { if (m_session_running) {
// TODO: Render frame // TODO: Render frame (per-eye rendering via OpenXR swapchain)
} }
} }
} }

View File

@@ -2,6 +2,11 @@
#include <openxr/openxr.h> #include <openxr/openxr.h>
// Forward declarations for wlroots integration
struct wl_display;
struct wl_event_loop;
struct wlr_backend;
struct Compositor { struct Compositor {
Compositor() Compositor()
{ {
@@ -34,4 +39,8 @@ private:
bool m_session_running = false; bool m_session_running = false;
XrSession m_session = {}; XrSession m_session = {};
XrSessionState m_session_state = XR_SESSION_STATE_UNKNOWN; XrSessionState m_session_state = XR_SESSION_STATE_UNKNOWN;
// wlroots integration
wl_display* m_display = nullptr;
wl_event_loop* m_event_loop = nullptr;
wlr_backend* m_backend = nullptr;
}; };

View File

@@ -10,10 +10,29 @@ extern "C" {
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h> #include <GL/glx.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <openxr/openxr.h> #include <openxr/openxr.h>
#include <openxr/openxr_platform.h> #include <openxr/openxr_platform.h>
#include <vector>
// Internal backend definition
struct openxr_backend {
wlr_backend base;
wl_display* display;
wl_event_loop* event_loop;
XrInstance instance {};
XrSession session {};
XrSpace app_space { XR_NULL_HANDLE };
XrSwapchain swapchain { XR_NULL_HANDLE };
int32_t width = 0, height = 0;
std::vector<XrSwapchainImageOpenGLKHR> swapchain_images;
std::vector<GLuint> framebuffers;
bool started = false;
struct wlr_output* output = nullptr;
};
static void output_destroy(struct wlr_output* wlr_output) { (void)wlr_output; } static void output_destroy(struct wlr_output* wlr_output) { (void)wlr_output; }
@@ -28,8 +47,77 @@ static bool output_test(
static bool output_commit( static bool output_commit(
struct wlr_output* wlr_output, const struct wlr_output_state* state) struct wlr_output* wlr_output, const struct wlr_output_state* state)
{ {
(void)wlr_output; // Retrieve our backend
auto* xr = reinterpret_cast<openxr_backend*>(wlr_output->backend);
(void)state; (void)state;
// Wait for frame
XrFrameState frameState { XR_TYPE_FRAME_STATE };
xrWaitFrame(xr->session, nullptr, &frameState);
xrBeginFrame(xr->session, nullptr);
// Locate views
uint32_t viewCount = static_cast<uint32_t>(xr->framebuffers.size());
std::vector<XrView> views(viewCount, { XR_TYPE_VIEW });
XrViewLocateInfo viewLocInfo { XR_TYPE_VIEW_LOCATE_INFO };
viewLocInfo.viewConfigurationType
= XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
viewLocInfo.displayTime = frameState.predictedDisplayTime;
viewLocInfo.space = xr->app_space;
XrViewState viewState { XR_TYPE_VIEW_STATE };
uint32_t viewCountOutput;
xrLocateViews(xr->session, &viewLocInfo, &viewState, viewCount,
&viewCountOutput, views.data());
// Prepare projection views
std::vector<XrCompositionLayerProjectionView> projViews(
viewCountOutput, { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW });
for (uint32_t i = 0; i < viewCountOutput; ++i) {
// Acquire swapchain image
XrSwapchainImageAcquireInfo acqInfo {
XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO
};
uint32_t imgIndex;
xrAcquireSwapchainImage(xr->swapchain, &acqInfo, &imgIndex);
XrSwapchainImageWaitInfo waitInfo { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
waitInfo.timeout = XR_INFINITE_DURATION;
xrWaitSwapchainImage(xr->swapchain, &waitInfo);
// Bind framebuffer and clear
glBindFramebuffer(GL_FRAMEBUFFER, xr->framebuffers[imgIndex]);
glViewport(0, 0, xr->width, xr->height);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// TODO: render scene (e.g., wlr_scene_render)
// Release swapchain image
{
XrSwapchainImageReleaseInfo relInfo {
XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO
};
xrReleaseSwapchainImage(xr->swapchain, &relInfo);
}
// Setup projection view
projViews[i].pose = views[i].pose;
projViews[i].fov = views[i].fov;
projViews[i].subImage.swapchain = xr->swapchain;
projViews[i].subImage.imageArrayIndex = imgIndex;
// Set image rectangle offset and extent
projViews[i].subImage.imageRect.offset.x = 0;
projViews[i].subImage.imageRect.offset.y = 0;
projViews[i].subImage.imageRect.extent.width = xr->width;
projViews[i].subImage.imageRect.extent.height = xr->height;
}
// Unbind
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// End frame
XrCompositionLayerProjection layer { XR_TYPE_COMPOSITION_LAYER_PROJECTION };
layer.space = xr->app_space;
layer.viewCount = static_cast<uint32_t>(projViews.size());
layer.views = projViews.data();
XrCompositionLayerBaseHeader const* layers[]
= { reinterpret_cast<XrCompositionLayerBaseHeader const*>(&layer) };
XrFrameEndInfo frameEndInfo { XR_TYPE_FRAME_END_INFO };
frameEndInfo.displayTime = frameState.predictedDisplayTime;
frameEndInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
frameEndInfo.layerCount = 1;
frameEndInfo.layers = layers;
xrEndFrame(xr->session, &frameEndInfo);
return true; return true;
} }
@@ -48,23 +136,6 @@ static const struct wlr_output_impl output_impl = {
.get_primary_formats = output_get_primary_formats, .get_primary_formats = output_get_primary_formats,
}; };
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <cstring>
#include <memory>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
struct openxr_backend {
wlr_backend base;
wl_display* display;
wl_event_loop* event_loop;
XrInstance instance {};
XrSession session {};
bool started = false;
struct wlr_output* output = nullptr;
};
static bool backend_start(wlr_backend* backend) static bool backend_start(wlr_backend* backend)
{ {
auto* xr = reinterpret_cast<openxr_backend*>(backend); auto* xr = reinterpret_cast<openxr_backend*>(backend);
@@ -156,6 +227,68 @@ static bool backend_start(wlr_backend* backend)
wlr_log(WLR_ERROR, "xrCreateSession failed"); wlr_log(WLR_ERROR, "xrCreateSession failed");
return false; return false;
} }
// Create reference space
XrReferenceSpaceCreateInfo spaceInfo {
XR_TYPE_REFERENCE_SPACE_CREATE_INFO
};
spaceInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
spaceInfo.poseInReferenceSpace = { { 0, 0, 0, 1 }, { 0, 0, 0 } };
if (xrCreateReferenceSpace(xr->session, &spaceInfo, &xr->app_space)
!= XR_SUCCESS) {
wlr_log(WLR_ERROR, "xrCreateReferenceSpace failed");
return false;
}
// Get recommended view configuration
uint32_t viewCount;
xrEnumerateViewConfigurationViews(xr->instance, system_id,
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &viewCount, nullptr);
std::vector<XrViewConfigurationView> viewConfigs(
viewCount, { XR_TYPE_VIEW_CONFIGURATION_VIEW });
xrEnumerateViewConfigurationViews(xr->instance, system_id,
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, viewCount, &viewCount,
viewConfigs.data());
xr->width = viewConfigs[0].recommendedImageRectWidth;
xr->height = viewConfigs[0].recommendedImageRectHeight;
// Create swapchain
uint32_t formatCount;
xrEnumerateSwapchainFormats(xr->session, 0, &formatCount, nullptr);
std::vector<int64_t> formats(formatCount);
xrEnumerateSwapchainFormats(
xr->session, formatCount, &formatCount, formats.data());
int64_t swapFormat = formats.empty() ? 0 : formats[0];
XrSwapchainCreateInfo swapchain_ci { XR_TYPE_SWAPCHAIN_CREATE_INFO };
swapchain_ci.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
swapchain_ci.format = swapFormat;
swapchain_ci.sampleCount = 1;
swapchain_ci.width = xr->width;
swapchain_ci.height = xr->height;
swapchain_ci.faceCount = 1;
swapchain_ci.arraySize = viewCount;
swapchain_ci.mipCount = 1;
if (xrCreateSwapchain(xr->session, &swapchain_ci, &xr->swapchain)
!= XR_SUCCESS) {
wlr_log(WLR_ERROR, "xrCreateSwapchain failed");
return false;
}
// Enumerate swapchain images
uint32_t imageCount;
xrEnumerateSwapchainImages(xr->swapchain, 0, &imageCount, nullptr);
xr->swapchain_images.resize(
imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
xrEnumerateSwapchainImages(xr->swapchain, imageCount, &imageCount,
reinterpret_cast<XrSwapchainImageBaseHeader*>(
xr->swapchain_images.data()));
// Create framebuffers for each image
xr->framebuffers.resize(imageCount);
for (uint32_t i = 0; i < imageCount; ++i) {
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, xr->swapchain_images[i].image, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
xr->framebuffers[i] = fbo;
}
xr->output = static_cast<wlr_output*>(calloc(1, sizeof(wlr_output))); xr->output = static_cast<wlr_output*>(calloc(1, sizeof(wlr_output)));
if (xr->output) { if (xr->output) {
@@ -173,6 +306,20 @@ static bool backend_start(wlr_backend* backend)
static void backend_destroy(wlr_backend* backend) static void backend_destroy(wlr_backend* backend)
{ {
auto* xr = reinterpret_cast<openxr_backend*>(backend); auto* xr = reinterpret_cast<openxr_backend*>(backend);
// destroy swapchain and framebuffers
if (xr->swapchain) {
xrDestroySwapchain(xr->swapchain);
xr->swapchain = XR_NULL_HANDLE;
}
for (auto fbo : xr->framebuffers) {
glDeleteFramebuffers(1, &fbo);
}
// destroy reference space
if (xr->app_space) {
xrDestroySpace(xr->app_space);
xr->app_space = XR_NULL_HANDLE;
}
// destroy XR session and instance
if (xr->session) { if (xr->session) {
xrDestroySession(xr->session); xrDestroySession(xr->session);
} }

7
tools/clang-format.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
cd $(dirname $0)/.. || exit 1
find src '(' -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.hpp' ')' -print0 | xargs -0 clang-format -i
echo 'Formatted source files in src/'

7
tools/tokei.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
cd $(dirname $0)/.. || exit 1
tokei src
exit 0