mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-23 11:25:54 +00:00
feat: shared egl context across outputs
This commit is contained in:
parent
fc4604c758
commit
4fb4d87125
5 changed files with 273 additions and 14 deletions
|
|
@ -107,6 +107,14 @@ impl EGLContext {
|
|||
EGLContextBuilder::new()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(super) fn from_raw(
|
||||
surface: Surface<WindowSurface>,
|
||||
context: PossiblyCurrentContext,
|
||||
) -> Self {
|
||||
Self { surface, context }
|
||||
}
|
||||
|
||||
fn ensure_current(&self) -> Result<()> {
|
||||
if !self.context.is_current() {
|
||||
self.context
|
||||
|
|
|
|||
237
crates/adapters/src/rendering/egl/context_factory.rs
Normal file
237
crates/adapters/src/rendering/egl/context_factory.rs
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
use super::context::EGLContext;
|
||||
use crate::errors::{EGLError, LayerShikaError, Result};
|
||||
use glutin::{
|
||||
api::egl::{
|
||||
config::Config,
|
||||
context::{NotCurrentContext, PossiblyCurrentContext},
|
||||
display::Display,
|
||||
surface::Surface,
|
||||
},
|
||||
config::ConfigTemplateBuilder,
|
||||
context::ContextAttributesBuilder,
|
||||
prelude::*,
|
||||
surface::{SurfaceAttributesBuilder, WindowSurface},
|
||||
};
|
||||
use log::{debug, info};
|
||||
use raw_window_handle::{
|
||||
RawDisplayHandle, RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle,
|
||||
};
|
||||
use slint::PhysicalSize;
|
||||
use std::{cell::RefCell, ffi::c_void, num::NonZeroU32, ptr::NonNull, rc::Rc};
|
||||
use wayland_client::backend::ObjectId;
|
||||
|
||||
pub struct RenderContextFactory {
|
||||
shared_state: RefCell<Option<SharedRenderState>>,
|
||||
}
|
||||
|
||||
struct SharedRenderState {
|
||||
display: Display,
|
||||
config: Config,
|
||||
primary_context: PossiblyCurrentContext,
|
||||
}
|
||||
|
||||
impl RenderContextFactory {
|
||||
#[must_use]
|
||||
pub fn new() -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
shared_state: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_context(
|
||||
&self,
|
||||
display_id: &ObjectId,
|
||||
surface_id: &ObjectId,
|
||||
size: PhysicalSize,
|
||||
) -> Result<EGLContext> {
|
||||
let mut state = self.shared_state.borrow_mut();
|
||||
|
||||
if state.is_none() {
|
||||
info!("Creating primary EGL context (will be shared with subsequent contexts)");
|
||||
let new_state = self.create_primary_context(display_id, surface_id, size)?;
|
||||
*state = Some(new_state);
|
||||
}
|
||||
|
||||
let Some(shared_state) = state.as_ref() else {
|
||||
return Err(LayerShikaError::InvalidInput {
|
||||
message: "Shared state initialization failed".into(),
|
||||
});
|
||||
};
|
||||
|
||||
if shared_state.primary_context.is_current() {
|
||||
debug!("Creating shared context while primary is current");
|
||||
self.create_shared_context_from_current(
|
||||
&shared_state.display,
|
||||
&shared_state.config,
|
||||
&shared_state.primary_context,
|
||||
surface_id,
|
||||
size,
|
||||
)
|
||||
} else {
|
||||
debug!("Creating shared context (primary not current)");
|
||||
self.create_shared_context(
|
||||
&shared_state.display,
|
||||
&shared_state.config,
|
||||
&shared_state.primary_context,
|
||||
surface_id,
|
||||
size,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
fn create_primary_context(
|
||||
&self,
|
||||
display_id: &ObjectId,
|
||||
surface_id: &ObjectId,
|
||||
size: PhysicalSize,
|
||||
) -> Result<SharedRenderState> {
|
||||
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 context_attributes = ContextAttributesBuilder::default();
|
||||
let not_current = create_context(&display, &config, context_attributes)?;
|
||||
|
||||
let surface_handle = create_surface_handle(surface_id)?;
|
||||
let surface = create_surface(&display, &config, surface_handle, size)?;
|
||||
|
||||
let primary_context = not_current
|
||||
.make_current(&surface)
|
||||
.map_err(|e| EGLError::MakeCurrent { source: e.into() })?;
|
||||
|
||||
info!("Primary EGL context created successfully");
|
||||
|
||||
Ok(SharedRenderState {
|
||||
display,
|
||||
config,
|
||||
primary_context,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
fn create_shared_context(
|
||||
&self,
|
||||
display: &Display,
|
||||
config: &Config,
|
||||
share_context: &PossiblyCurrentContext,
|
||||
surface_id: &ObjectId,
|
||||
size: PhysicalSize,
|
||||
) -> Result<EGLContext> {
|
||||
let context_attributes = ContextAttributesBuilder::default().with_sharing(share_context);
|
||||
|
||||
let not_current =
|
||||
unsafe { display.create_context(config, &context_attributes.build(None)) }
|
||||
.map_err(|e| EGLError::ContextCreation { source: e.into() })?;
|
||||
|
||||
let surface_handle = create_surface_handle(surface_id)?;
|
||||
let surface = create_surface(display, config, surface_handle, size)?;
|
||||
|
||||
let context = not_current
|
||||
.make_current(&surface)
|
||||
.map_err(|e| EGLError::MakeCurrent { source: e.into() })?;
|
||||
|
||||
info!("Shared EGL context created successfully");
|
||||
|
||||
Ok(EGLContext::from_raw(surface, context))
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
fn create_shared_context_from_current(
|
||||
&self,
|
||||
display: &Display,
|
||||
config: &Config,
|
||||
share_context: &PossiblyCurrentContext,
|
||||
surface_id: &ObjectId,
|
||||
size: PhysicalSize,
|
||||
) -> Result<EGLContext> {
|
||||
let context_attributes = ContextAttributesBuilder::default().with_sharing(share_context);
|
||||
|
||||
let not_current =
|
||||
unsafe { display.create_context(config, &context_attributes.build(None)) }
|
||||
.map_err(|e| EGLError::ContextCreation { source: e.into() })?;
|
||||
|
||||
let surface_handle = create_surface_handle(surface_id)?;
|
||||
let surface = create_surface(display, config, surface_handle, size)?;
|
||||
|
||||
let context = not_current
|
||||
.make_current(&surface)
|
||||
.map_err(|e| EGLError::MakeCurrent { source: e.into() })?;
|
||||
|
||||
info!("Shared EGL context created successfully (from current)");
|
||||
|
||||
Ok(EGLContext::from_raw(surface, context))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RenderContextFactory {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
shared_state: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
fn create_context(
|
||||
glutin_display: &Display,
|
||||
config: &Config,
|
||||
context_attributes: ContextAttributesBuilder,
|
||||
) -> Result<NotCurrentContext> {
|
||||
unsafe { glutin_display.create_context(config, &context_attributes.build(None)) }
|
||||
.map_err(|e| EGLError::ContextCreation { source: e.into() }.into())
|
||||
}
|
||||
|
||||
fn create_surface_handle(surface_id: &ObjectId) -> Result<RawWindowHandle> {
|
||||
let surface = NonNull::new(surface_id.as_ptr().cast::<c_void>()).ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput {
|
||||
message: "Failed to create NonNull pointer for surface".into(),
|
||||
}
|
||||
})?;
|
||||
let handle = WaylandWindowHandle::new(surface);
|
||||
Ok(RawWindowHandle::Wayland(handle))
|
||||
}
|
||||
|
||||
fn create_surface(
|
||||
glutin_display: &Display,
|
||||
config: &Config,
|
||||
surface_handle: RawWindowHandle,
|
||||
size: PhysicalSize,
|
||||
) -> Result<Surface<WindowSurface>> {
|
||||
let width = NonZeroU32::new(size.width).ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Width cannot be zero".into(),
|
||||
})?;
|
||||
|
||||
let height = NonZeroU32::new(size.height).ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Height cannot be zero".into(),
|
||||
})?;
|
||||
|
||||
let attrs =
|
||||
SurfaceAttributesBuilder::<WindowSurface>::new().build(surface_handle, width, height);
|
||||
|
||||
unsafe { glutin_display.create_window_surface(config, &attrs) }
|
||||
.map_err(|e| EGLError::SurfaceCreation { source: e.into() }.into())
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
pub mod context;
|
||||
pub mod context_factory;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::z
|
|||
use crate::{
|
||||
errors::{EventLoopError, LayerShikaError, RenderingError, Result},
|
||||
rendering::{
|
||||
egl::context::EGLContext,
|
||||
egl::context_factory::RenderContextFactory,
|
||||
femtovg::{main_window::FemtoVGWindow, renderable_window::RenderableWindow},
|
||||
slint_integration::platform::CustomSlintPlatform,
|
||||
},
|
||||
|
|
@ -55,6 +55,8 @@ pub struct WaylandWindowingSystem {
|
|||
connection: Rc<Connection>,
|
||||
event_queue: EventQueue<AppState>,
|
||||
event_loop: EventLoop<'static, AppState>,
|
||||
#[allow(dead_code)]
|
||||
render_factory: Rc<RenderContextFactory>,
|
||||
}
|
||||
|
||||
impl WaylandWindowingSystem {
|
||||
|
|
@ -64,13 +66,15 @@ impl WaylandWindowingSystem {
|
|||
let event_loop =
|
||||
EventLoop::try_new().map_err(|e| EventLoopError::Creation { source: e })?;
|
||||
|
||||
let state = Self::init_state(config, &connection, &mut event_queue)?;
|
||||
let render_factory = RenderContextFactory::new();
|
||||
let state = Self::init_state(config, &connection, &mut event_queue, &render_factory)?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
connection,
|
||||
event_queue,
|
||||
event_loop,
|
||||
render_factory,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +101,7 @@ impl WaylandWindowingSystem {
|
|||
event_queue: &mut EventQueue<AppState>,
|
||||
pointer: &Rc<WlPointer>,
|
||||
layer_surface_config: &LayerSurfaceConfig,
|
||||
render_factory: &Rc<RenderContextFactory>,
|
||||
) -> Result<Vec<OutputSetup>> {
|
||||
let mut setups = Vec::new();
|
||||
|
||||
|
|
@ -128,7 +133,7 @@ impl WaylandWindowingSystem {
|
|||
let main_surface_id = surface_ctx.surface.id();
|
||||
|
||||
let window =
|
||||
Self::initialize_renderer(&surface_ctx.surface, &connection.display(), config)?;
|
||||
Self::initialize_renderer(&surface_ctx.surface, &connection.display(), config, render_factory)?;
|
||||
|
||||
let mut builder = WindowStateBuilder::new()
|
||||
.with_component_definition(config.component_definition.clone())
|
||||
|
|
@ -217,6 +222,7 @@ impl WaylandWindowingSystem {
|
|||
config: &WaylandWindowConfig,
|
||||
connection: &Connection,
|
||||
event_queue: &mut EventQueue<AppState>,
|
||||
render_factory: &Rc<RenderContextFactory>,
|
||||
) -> Result<AppState> {
|
||||
let global_ctx = GlobalContext::initialize(connection, &event_queue.handle())?;
|
||||
let layer_surface_config = Self::create_layer_surface_config(config);
|
||||
|
|
@ -237,6 +243,7 @@ impl WaylandWindowingSystem {
|
|||
global_ctx.viewporter.clone(),
|
||||
connection.display(),
|
||||
Rc::new(connection.clone()),
|
||||
Rc::clone(render_factory),
|
||||
);
|
||||
|
||||
let setups = Self::create_output_setups(
|
||||
|
|
@ -246,6 +253,7 @@ impl WaylandWindowingSystem {
|
|||
event_queue,
|
||||
&pointer,
|
||||
&layer_surface_config,
|
||||
render_factory,
|
||||
)?;
|
||||
|
||||
let platform = Self::setup_platform(&setups)?;
|
||||
|
|
@ -321,14 +329,15 @@ impl WaylandWindowingSystem {
|
|||
surface: &Rc<WlSurface>,
|
||||
display: &WlDisplay,
|
||||
config: &WaylandWindowConfig,
|
||||
render_factory: &Rc<RenderContextFactory>,
|
||||
) -> Result<Rc<FemtoVGWindow>> {
|
||||
let init_size = PhysicalSize::new(1, 1);
|
||||
|
||||
let context = EGLContext::builder()
|
||||
.with_display_id(display.id())
|
||||
.with_surface_id(surface.id())
|
||||
.with_size(init_size)
|
||||
.build()?;
|
||||
let context = render_factory.create_context(
|
||||
&display.id(),
|
||||
&surface.id(),
|
||||
init_size,
|
||||
)?;
|
||||
|
||||
let renderer = FemtoVGRenderer::new(context)
|
||||
.map_err(|e| LayerShikaError::FemtoVGRendererCreation { source: e })?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::errors::{LayerShikaError, Result};
|
||||
use crate::rendering::egl::context::EGLContext;
|
||||
use crate::rendering::egl::context_factory::RenderContextFactory;
|
||||
use crate::rendering::femtovg::{popup_window::PopupWindow, renderable_window::RenderableWindow};
|
||||
use crate::wayland::surfaces::display_metrics::{DisplayMetrics, SharedDisplayMetrics};
|
||||
use layer_shika_domain::dimensions::LogicalSize as DomainLogicalSize;
|
||||
|
|
@ -74,10 +74,12 @@ pub struct PopupContext {
|
|||
fractional_scale_manager: Option<WpFractionalScaleManagerV1>,
|
||||
viewporter: Option<WpViewporter>,
|
||||
display: WlDisplay,
|
||||
render_factory: Rc<RenderContextFactory>,
|
||||
}
|
||||
|
||||
impl PopupContext {
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
compositor: WlCompositor,
|
||||
xdg_wm_base: Option<XdgWmBase>,
|
||||
|
|
@ -86,6 +88,7 @@ impl PopupContext {
|
|||
viewporter: Option<WpViewporter>,
|
||||
display: WlDisplay,
|
||||
_connection: Rc<Connection>,
|
||||
render_factory: Rc<RenderContextFactory>,
|
||||
) -> Self {
|
||||
Self {
|
||||
compositor,
|
||||
|
|
@ -94,6 +97,7 @@ impl PopupContext {
|
|||
fractional_scale_manager,
|
||||
viewporter,
|
||||
display,
|
||||
render_factory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -328,11 +332,11 @@ impl PopupManager {
|
|||
popup_surface.surface.commit();
|
||||
}
|
||||
|
||||
let context = EGLContext::builder()
|
||||
.with_display_id(self.context.display.id())
|
||||
.with_surface_id(popup_surface.surface.id())
|
||||
.with_size(popup_size)
|
||||
.build()?;
|
||||
let context = self.context.render_factory.create_context(
|
||||
&self.context.display.id(),
|
||||
&popup_surface.surface.id(),
|
||||
popup_size,
|
||||
)?;
|
||||
|
||||
let renderer = FemtoVGRenderer::new(context)
|
||||
.map_err(|e| LayerShikaError::FemtoVGRendererCreation { source: e })?;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue