layer-shika/crates/adapters/src/rendering/egl/render_context_manager.rs

113 lines
3.8 KiB
Rust

use crate::errors::{EGLError, LayerShikaError, Result};
use glutin::{
api::egl::{config::Config, context::PossiblyCurrentContext, display::Display},
config::ConfigTemplateBuilder,
context::ContextAttributesBuilder,
prelude::*,
};
use log::{info, warn};
use raw_window_handle::{RawDisplayHandle, WaylandDisplayHandle};
use std::{ffi::c_void, ptr::NonNull, rc::Rc};
use wayland_client::backend::ObjectId;
pub struct RenderContextManager {
display: Display,
config: Config,
root_context: PossiblyCurrentContext,
}
impl RenderContextManager {
pub fn new(display_id: &ObjectId) -> Result<Rc<Self>> {
info!("Initializing RenderContextManager with independent root context");
let display_handle = create_wayland_display_handle(display_id)?;
let display = unsafe { Display::new(display_handle) }
.map_err(|e| EGLError::DisplayCreation { source: e.into() })?;
let config_template = ConfigTemplateBuilder::default();
let config = select_config(&display, config_template)?;
let root_context = Self::create_root_context(&display, &config)?;
info!("RenderContextManager initialized successfully");
Ok(Rc::new(Self {
display,
config,
root_context,
}))
}
fn create_root_context(display: &Display, config: &Config) -> Result<PossiblyCurrentContext> {
if let Ok(context) = Self::try_create_surfaceless_context(display, config) {
info!("Created surfaceless root EGL context");
return Ok(context);
}
warn!(
"Surfaceless context not available, using workaround with make_current_surfaceless anyway"
);
Self::create_surfaceless_fallback(display, config)
}
fn try_create_surfaceless_context(
display: &Display,
config: &Config,
) -> Result<PossiblyCurrentContext> {
let context_attributes = ContextAttributesBuilder::default();
let not_current =
unsafe { display.create_context(config, &context_attributes.build(None)) }
.map_err(|e| EGLError::ContextCreation { source: e.into() })?;
not_current
.make_current_surfaceless()
.map_err(|e| EGLError::MakeCurrent { source: e.into() }.into())
}
fn create_surfaceless_fallback(
display: &Display,
config: &Config,
) -> Result<PossiblyCurrentContext> {
let context_attributes = ContextAttributesBuilder::default();
let not_current =
unsafe { display.create_context(config, &context_attributes.build(None)) }
.map_err(|e| EGLError::ContextCreation { source: e.into() })?;
not_current
.make_current_surfaceless()
.map_err(|e| EGLError::MakeCurrent { source: e.into() }.into())
}
pub fn display(&self) -> &Display {
&self.display
}
pub fn config(&self) -> &Config {
&self.config
}
pub fn root_context(&self) -> &PossiblyCurrentContext {
&self.root_context
}
}
fn create_wayland_display_handle(display_id: &ObjectId) -> Result<RawDisplayHandle> {
let display = NonNull::new(display_id.as_ptr().cast::<c_void>()).ok_or_else(|| {
LayerShikaError::InvalidInput {
message: "Failed to create NonNull pointer for display".into(),
}
})?;
let handle = WaylandDisplayHandle::new(display);
Ok(RawDisplayHandle::Wayland(handle))
}
fn select_config(
glutin_display: &Display,
config_template: ConfigTemplateBuilder,
) -> Result<Config> {
let mut configs = unsafe { glutin_display.find_configs(config_template.build()) }
.map_err(|e| EGLError::ConfigSelection { source: e.into() })?;
configs
.next()
.ok_or_else(|| EGLError::NoCompatibleConfig.into())
}