259
									
								
								src/compositor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								src/compositor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,259 @@ | ||||
| #include "compositor.h" | ||||
|  | ||||
| #include "common.h" | ||||
|  | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <print> | ||||
| #include <vector> | ||||
|  | ||||
| #include <GL/glx.h> | ||||
| #include <X11/Xlib.h> | ||||
| #include <openxr/openxr.h> | ||||
| #include <openxr/openxr_platform.h> | ||||
|  | ||||
| void Compositor::create_instance() | ||||
| { | ||||
| 	// Wow, can't believe this compositor uses AI! | ||||
| 	XrApplicationInfo ai { | ||||
| 		.applicationName = {}, | ||||
| 		.applicationVersion = 1, | ||||
| 		.engineName = {}, | ||||
| 		.engineVersion = 1, | ||||
| 		.apiVersion = XR_API_VERSION_1_0, | ||||
| 	}; | ||||
|  | ||||
| 	std::strncpy( | ||||
| 	    ai.applicationName, "LunarWM", XR_MAX_APPLICATION_NAME_SIZE - 1); | ||||
| 	std::strncpy(ai.engineName, "LunarWM Engine", XR_MAX_ENGINE_NAME_SIZE - 1); | ||||
|  | ||||
| 	std::vector<char const*> const instance_extensions { | ||||
| 		XR_EXT_DEBUG_UTILS_EXTENSION_NAME, | ||||
| 		XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, | ||||
| 	}; | ||||
|  | ||||
| 	std::vector<XrExtensionProperties> extension_props; | ||||
| 	{ // Populate extension_props | ||||
| 		uint32_t len = 0; | ||||
| 		OPENXR_CHECK( | ||||
| 		    xrEnumerateInstanceExtensionProperties, nullptr, 0, &len, nullptr); | ||||
| 		extension_props.resize(len, { XR_TYPE_EXTENSION_PROPERTIES }); | ||||
| 		OPENXR_CHECK(xrEnumerateInstanceExtensionProperties, nullptr, len, &len, | ||||
| 		    extension_props.data()); | ||||
| 	} | ||||
|  | ||||
| 	// Check that we have all our requested extensions available | ||||
| 	for (auto const& requested_instance_extension : instance_extensions) { | ||||
| 		bool found = false; | ||||
|  | ||||
| 		for (auto const& eprop : extension_props) { | ||||
| 			if (strcmp(requested_instance_extension, eprop.extensionName) | ||||
| 			    == 0) { | ||||
| 				found = true; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (!found) { | ||||
| 			throw std::runtime_error(std::format( | ||||
| 			    "Failed to find extension: {}", requested_instance_extension)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	{ // Create our instance | ||||
| 		XrInstanceCreateInfo ci { | ||||
| 			.type = XR_TYPE_INSTANCE_CREATE_INFO, | ||||
| 			.next = nullptr, | ||||
| 			.createFlags = 0, | ||||
| 			.applicationInfo = ai, | ||||
| 			.enabledApiLayerCount = 0, | ||||
| 			.enabledApiLayerNames = nullptr, | ||||
| 			.enabledExtensionCount | ||||
| 			= static_cast<uint32_t>(instance_extensions.size()), | ||||
| 			.enabledExtensionNames = instance_extensions.data(), | ||||
| 		}; | ||||
|  | ||||
| 		OPENXR_CHECK(xrCreateInstance, &ci, &m_xr_instance); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Compositor::destroy_instance() | ||||
| { | ||||
| 	OPENXR_CHECK(xrDestroyInstance, m_xr_instance); | ||||
| } | ||||
|  | ||||
| void Compositor::create_session() | ||||
| { | ||||
| 	XrSystemId system_id; | ||||
|  | ||||
| 	{ // Get system ID. | ||||
| 		XrSystemGetInfo system_gi { XR_TYPE_SYSTEM_GET_INFO }; | ||||
| 		system_gi.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; | ||||
| 		OPENXR_CHECK(xrGetSystem, m_xr_instance, &system_gi, &system_id); | ||||
|  | ||||
| 		// NOTE: Might be useful later. | ||||
| 		XrSystemProperties system_properties = { XR_TYPE_SYSTEM_PROPERTIES }; | ||||
| 		OPENXR_CHECK(xrGetSystemProperties, m_xr_instance, system_id, | ||||
| 		    &system_properties); | ||||
| 	} | ||||
|  | ||||
| 	{ // Get GLX graphics requirements | ||||
| 		PFN_xrGetOpenGLGraphicsRequirementsKHR xr_get_gl_req = nullptr; | ||||
| 		xrGetInstanceProcAddr(m_xr_instance, | ||||
| 		    "xrGetOpenGLGraphicsRequirementsKHR", | ||||
| 		    reinterpret_cast<PFN_xrVoidFunction*>(&xr_get_gl_req)); | ||||
|  | ||||
| 		XrGraphicsRequirementsOpenGLKHR gl_req { | ||||
| 			XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR | ||||
| 		}; | ||||
| 		OPENXR_CHECK(xr_get_gl_req, m_xr_instance, system_id, &gl_req); | ||||
|  | ||||
| 		std::println("OpenGL requirements: min=0x{:X}, max=0x{:X}", | ||||
| 		    gl_req.minApiVersionSupported, gl_req.maxApiVersionSupported); | ||||
| 	} | ||||
|  | ||||
| 	XrGraphicsBindingOpenGLXlibKHR graphics_binding {}; | ||||
|  | ||||
| 	{ // Xlib stuff | ||||
| 		Display* display = XOpenDisplay(nullptr); | ||||
| 		if (!display) | ||||
| 			throw std::runtime_error("Failed to open X display"); | ||||
|  | ||||
| 		int screen = DefaultScreen(display); | ||||
|  | ||||
| 		static int visual_attribs[] = { | ||||
| 			GLX_X_RENDERABLE, | ||||
| 			True, | ||||
| 			GLX_DRAWABLE_TYPE, | ||||
| 			GLX_WINDOW_BIT, | ||||
| 			GLX_RENDER_TYPE, | ||||
| 			GLX_RGBA_BIT, | ||||
| 			GLX_X_VISUAL_TYPE, | ||||
| 			GLX_TRUE_COLOR, | ||||
| 			GLX_RED_SIZE, | ||||
| 			8, | ||||
| 			GLX_GREEN_SIZE, | ||||
| 			8, | ||||
| 			GLX_BLUE_SIZE, | ||||
| 			8, | ||||
| 			GLX_DEPTH_SIZE, | ||||
| 			24, | ||||
| 			GLX_DOUBLEBUFFER, | ||||
| 			True, | ||||
| 			None, | ||||
| 		}; | ||||
|  | ||||
| 		int fbcount; | ||||
| 		GLXFBConfig* fbc | ||||
| 		    = glXChooseFBConfig(display, screen, visual_attribs, &fbcount); | ||||
| 		if (!fbc) | ||||
| 			throw std::runtime_error("No GLXFBConfig found"); | ||||
|  | ||||
| 		// just grab first config | ||||
| 		GLXFBConfig best_fbc = fbc[0]; | ||||
| 		XFree(fbc); | ||||
|  | ||||
| 		XVisualInfo* vi = glXGetVisualFromFBConfig(display, best_fbc); | ||||
| 		if (!vi) | ||||
| 			throw std::runtime_error("Failed to get XVisualInfo"); | ||||
|  | ||||
| 		Window root = RootWindow(display, screen); | ||||
|  | ||||
| 		XSetWindowAttributes swa; | ||||
| 		swa.colormap = XCreateColormap(display, root, vi->visual, AllocNone); | ||||
| 		swa.event_mask = ExposureMask | KeyPressMask; | ||||
|  | ||||
| 		Window win = XCreateWindow(display, root, 0, 0, 800, 600, 0, vi->depth, | ||||
| 		    InputOutput, vi->visual, CWColormap | CWEventMask, &swa); | ||||
| 		XMapWindow(display, win); | ||||
| 		XStoreName(display, win, "LunarWM OpenXR"); | ||||
|  | ||||
| 		GLXContext glc = glXCreateNewContext( | ||||
| 		    display, best_fbc, GLX_RGBA_TYPE, nullptr, True); | ||||
| 		if (!glc) | ||||
| 			throw std::runtime_error("Failed to create GLX context"); | ||||
|  | ||||
| 		if (!glXMakeContextCurrent(display, win, win, glc)) | ||||
| 			throw std::runtime_error("glXMakeContextCurrent failed"); | ||||
|  | ||||
| 		// fill in OpenXR graphics binding | ||||
| 		graphics_binding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; | ||||
| 		graphics_binding.next = nullptr; | ||||
| 		graphics_binding.xDisplay = display; | ||||
| 		graphics_binding.visualid = vi->visualid; | ||||
| 		graphics_binding.glxFBConfig = best_fbc; | ||||
| 		graphics_binding.glxDrawable = win; | ||||
| 		graphics_binding.glxContext = glc; | ||||
| 	} | ||||
|  | ||||
| 	{ // Create session | ||||
| 		XrSessionCreateInfo ci { XR_TYPE_SESSION_CREATE_INFO }; | ||||
| 		ci.next = &graphics_binding; | ||||
| 		ci.systemId = system_id; | ||||
| 		ci.createFlags = 0; | ||||
| 		OPENXR_CHECK(xrCreateSession, m_xr_instance, &ci, &m_session); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Compositor::destroy_session() | ||||
| { | ||||
| 	OPENXR_CHECK(xrDestroySession, m_session); | ||||
| } | ||||
|  | ||||
| void Compositor::poll_events() | ||||
| { | ||||
| 	XrEventDataBuffer event_data { XR_TYPE_EVENT_DATA_BUFFER }; | ||||
| 	auto xr_poll_events = [&]() -> bool { | ||||
| 		event_data = { XR_TYPE_EVENT_DATA_BUFFER }; | ||||
| 		return xrPollEvent(m_xr_instance, &event_data) == XR_SUCCESS; | ||||
| 	}; | ||||
|  | ||||
| 	while (xr_poll_events()) { | ||||
| 		switch (event_data.type) { | ||||
| 		case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { | ||||
| 			XrEventDataSessionStateChanged* session_state_changed | ||||
| 			    = reinterpret_cast<XrEventDataSessionStateChanged*>( | ||||
| 			        &event_data); | ||||
| 			if (session_state_changed->session != m_session) { | ||||
| 				std::println(std::cerr, | ||||
| 				    "XrEventDataSessionStateChanged for unknown Session"); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (session_state_changed->state == XR_SESSION_STATE_READY) { | ||||
| 				XrSessionBeginInfo session_begin_info { | ||||
| 					XR_TYPE_SESSION_BEGIN_INFO | ||||
| 				}; | ||||
| 				session_begin_info.primaryViewConfigurationType | ||||
| 				    = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; | ||||
| 				OPENXR_CHECK(xrBeginSession, m_session, &session_begin_info); | ||||
| 				m_session_running = true; | ||||
| 			} | ||||
| 			if (session_state_changed->state == XR_SESSION_STATE_STOPPING) { | ||||
| 				OPENXR_CHECK(xrEndSession, m_session); | ||||
| 				m_session_running = false; | ||||
| 			} | ||||
| 			if (session_state_changed->state == XR_SESSION_STATE_EXITING | ||||
| 			    || session_state_changed->state | ||||
| 			        == XR_SESSION_STATE_LOSS_PENDING) { | ||||
| 				m_session_running = false; | ||||
| 				m_running = false; | ||||
| 			} | ||||
| 			m_session_state = session_state_changed->state; | ||||
| 			break; | ||||
| 		} | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Compositor::run() | ||||
| { | ||||
| 	while (m_running) { | ||||
| 		this->poll_events(); | ||||
|  | ||||
| 		if (m_session_running) { | ||||
| 			// TODO: Render frame | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user