Add helper scripts and wlroots integration
Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
#include "compositor.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 <iostream>
|
||||
@@ -193,10 +200,40 @@ void Compositor::create_session()
|
||||
ci.createFlags = 0;
|
||||
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()
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -249,11 +286,17 @@ void Compositor::poll_events()
|
||||
|
||||
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) {
|
||||
this->poll_events();
|
||||
// dispatch Wayland events
|
||||
wl_event_loop_dispatch(m_event_loop, 0);
|
||||
|
||||
if (m_session_running) {
|
||||
// TODO: Render frame
|
||||
// TODO: Render frame (per-eye rendering via OpenXR swapchain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
// Forward declarations for wlroots integration
|
||||
struct wl_display;
|
||||
struct wl_event_loop;
|
||||
struct wlr_backend;
|
||||
|
||||
struct Compositor {
|
||||
Compositor()
|
||||
{
|
||||
@@ -34,4 +39,8 @@ private:
|
||||
bool m_session_running = false;
|
||||
XrSession m_session = {};
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -10,10 +10,29 @@ extern "C" {
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GL/glx.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <openxr/openxr.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; }
|
||||
|
||||
@@ -28,8 +47,77 @@ static bool output_test(
|
||||
static bool output_commit(
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -48,23 +136,6 @@ static const struct wlr_output_impl output_impl = {
|
||||
.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)
|
||||
{
|
||||
auto* xr = reinterpret_cast<openxr_backend*>(backend);
|
||||
@@ -156,6 +227,68 @@ static bool backend_start(wlr_backend* backend)
|
||||
wlr_log(WLR_ERROR, "xrCreateSession failed");
|
||||
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)));
|
||||
if (xr->output) {
|
||||
@@ -173,6 +306,20 @@ static bool backend_start(wlr_backend* backend)
|
||||
static void backend_destroy(wlr_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) {
|
||||
xrDestroySession(xr->session);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user