mirror of
https://github.com/Smithay/smithay.git
synced 2024-09-28 03:21:14 +02:00
drm/compositor: Use pixman for cursor
This allows the removal of Offscreen and ExportMem trait usage. Aside from unnecessary GL for small/simple cursor, it also should help with WLCS for virtual devices (like VKMS/kmsro)
This commit is contained in:
parent
0507c89955
commit
5857a843c5
3 changed files with 137 additions and 80 deletions
|
@ -82,7 +82,7 @@ pkg-config = { version = "0.3.17", optional = true }
|
|||
cc = { version = "1.0.79", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_libseat", "backend_x11", "backend_winit", "desktop", "renderer_gl", "renderer_multi", "xwayland", "wayland_frontend", "backend_vulkan"]
|
||||
default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_libseat", "backend_x11", "backend_winit", "desktop", "renderer_gl", "renderer_pixman", "renderer_multi", "xwayland", "wayland_frontend", "backend_vulkan"]
|
||||
backend_winit = ["winit", "backend_egl", "wayland-egl", "renderer_gl"]
|
||||
backend_x11 = ["x11rb", "x11rb/dri3", "x11rb/xfixes", "x11rb/present", "x11rb_event_source", "backend_gbm", "backend_drm", "backend_egl"]
|
||||
backend_drm = ["drm", "drm-ffi"]
|
||||
|
|
|
@ -51,6 +51,7 @@ udev = [
|
|||
"smithay/backend_session_libseat",
|
||||
"image",
|
||||
"smithay/renderer_gl",
|
||||
"smithay/renderer_pixman",
|
||||
"smithay/renderer_multi",
|
||||
"xcursor",
|
||||
]
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
//! will be rendered on the primary plane using the provided [`Renderer`].
|
||||
//! Additionally it will try to assign the top most element that fit's into the cursor size (as specified
|
||||
//! by the [`DrmDevice`](crate::backend::drm::DrmDevice)) on the cursor plane. If the element can not be
|
||||
//! directly scanned out the renderer will be used to render the element into an [`Offscreen`] buffer that
|
||||
//! is copied over to the allocated gbm buffer for the cursor plane.
|
||||
//! directly scanned out, pixman will be used to render the element.
|
||||
//!
|
||||
//! Note: While the [`DrmCompositor`] also works on *legacy* drm the use of overlay and cursor planes is disabled in that case.
|
||||
//! Direct scan-out will only work with an atomic [`DrmSurface`].
|
||||
|
@ -105,7 +104,7 @@
|
|||
//!
|
||||
//! # let elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = Vec::new();
|
||||
//! let render_frame_result = compositor
|
||||
//! .render_frame::<_, _, GlesTexture>(&mut renderer, &elements, CLEAR_COLOR)
|
||||
//! .render_frame::<_, _>(&mut renderer, &elements, CLEAR_COLOR)
|
||||
//! .expect("failed to render frame");
|
||||
//!
|
||||
//! if render_frame_result.damage.is_some() {
|
||||
|
@ -141,6 +140,11 @@ use smallvec::SmallVec;
|
|||
use tracing::{debug, error, info, info_span, instrument, trace, warn};
|
||||
use wayland_server::{protocol::wl_buffer::WlBuffer, Resource};
|
||||
|
||||
#[cfg(feature = "renderer_pixman")]
|
||||
use crate::backend::renderer::{
|
||||
pixman::{PixmanError, PixmanRenderBuffer, PixmanRenderer, PixmanTexture},
|
||||
ImportAll, Unbind,
|
||||
};
|
||||
use crate::{
|
||||
backend::{
|
||||
allocator::{
|
||||
|
@ -159,7 +163,7 @@ use crate::{
|
|||
},
|
||||
sync::SyncPoint,
|
||||
utils::{CommitCounter, DamageBag, DamageSnapshot},
|
||||
Bind, Blit, DebugFlags, ExportMem, Frame as RendererFrame, Offscreen, Renderer, Texture,
|
||||
Bind, Blit, DebugFlags, Frame as RendererFrame, Renderer, Texture,
|
||||
},
|
||||
SwapBuffersError,
|
||||
},
|
||||
|
@ -1289,6 +1293,8 @@ struct CursorState<G: AsFd + 'static> {
|
|||
framebuffer_exporter: GbmDevice<G>,
|
||||
previous_output_transform: Option<Transform>,
|
||||
previous_output_scale: Option<Scale<f64>>,
|
||||
#[cfg(feature = "renderer_pixman")]
|
||||
pixman_renderer: Option<PixmanRenderer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, Copy, Clone)]
|
||||
|
@ -1522,6 +1528,15 @@ where
|
|||
) {
|
||||
Ok((swapchain, current_frame, is_opaque)) => {
|
||||
let cursor_state = gbm.map(|gbm| {
|
||||
#[cfg(feature = "renderer_pixman")]
|
||||
let pixman_renderer = match PixmanRenderer::new() {
|
||||
Ok(pixman_renderer) => Some(pixman_renderer),
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "failed to initialize pixman renderer for cursor plane");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let cursor_allocator = GbmAllocator::new(
|
||||
gbm.clone(),
|
||||
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||
|
@ -1531,6 +1546,8 @@ where
|
|||
framebuffer_exporter: gbm,
|
||||
previous_output_scale: None,
|
||||
previous_output_transform: None,
|
||||
#[cfg(feature = "renderer_pixman")]
|
||||
pixman_renderer,
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1755,7 +1772,7 @@ where
|
|||
/// - `elements` for this frame in front-to-back order
|
||||
#[instrument(level = "trace", parent = &self.span, skip_all)]
|
||||
#[profiling::function]
|
||||
pub fn render_frame<'a, R, E, Target>(
|
||||
pub fn render_frame<'a, R, E>(
|
||||
&mut self,
|
||||
renderer: &mut R,
|
||||
elements: &'a [E],
|
||||
|
@ -1763,7 +1780,7 @@ where
|
|||
) -> Result<RenderFrameResult<'a, A::Buffer, F::Framebuffer, E>, RenderFrameErrorType<A, F, R>>
|
||||
where
|
||||
E: RenderElement<R>,
|
||||
R: Renderer + Bind<Dmabuf> + Offscreen<Target> + ExportMem,
|
||||
R: Renderer + Bind<Dmabuf>,
|
||||
<R as Renderer>::TextureId: Texture + 'static,
|
||||
{
|
||||
if !self.surface.is_active() {
|
||||
|
@ -2646,7 +2663,7 @@ where
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
#[profiling::function]
|
||||
fn try_assign_element<'a, R, E, Target>(
|
||||
fn try_assign_element<'a, R, E>(
|
||||
&mut self,
|
||||
renderer: &mut R,
|
||||
element: &'a E,
|
||||
|
@ -2666,7 +2683,7 @@ where
|
|||
try_assign_primary_plane: bool,
|
||||
) -> Result<PlaneAssignment, Option<RenderingReason>>
|
||||
where
|
||||
R: Renderer + Bind<Dmabuf> + Offscreen<Target> + ExportMem,
|
||||
R: Renderer + Bind<Dmabuf>,
|
||||
E: RenderElement<R>,
|
||||
{
|
||||
// Check if we have a free plane, otherwise we can exit early
|
||||
|
@ -2840,7 +2857,7 @@ where
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
#[profiling::function]
|
||||
fn try_assign_cursor_plane<R, E, Target>(
|
||||
fn try_assign_cursor_plane<R, E>(
|
||||
&mut self,
|
||||
renderer: &mut R,
|
||||
element: &E,
|
||||
|
@ -2853,7 +2870,7 @@ where
|
|||
output_geometry: Rectangle<i32, Physical>,
|
||||
) -> Option<PlaneAssignment>
|
||||
where
|
||||
R: Renderer + Offscreen<Target> + ExportMem,
|
||||
R: Renderer,
|
||||
E: RenderElement<R>,
|
||||
{
|
||||
// if we have no cursor plane we can exit early
|
||||
|
@ -3024,6 +3041,21 @@ where
|
|||
|
||||
let cursor_buffer_size = self.cursor_size.to_logical(1).to_buffer(1, Transform::Normal);
|
||||
|
||||
#[cfg(not(feature = "renderer_pixman"))]
|
||||
if !copy_element_to_cursor_bo(
|
||||
renderer,
|
||||
element,
|
||||
element_size,
|
||||
self.cursor_size,
|
||||
output_transform,
|
||||
&cursor_state.framebuffer_exporter,
|
||||
&mut cursor_buffer,
|
||||
) {
|
||||
tracing::trace!("failed to copy element to cursor bo, skipping element on cursor plane");
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(feature = "renderer_pixman")]
|
||||
if !copy_element_to_cursor_bo(
|
||||
renderer,
|
||||
element,
|
||||
|
@ -3036,81 +3068,105 @@ where
|
|||
profiling::scope!("render cursor plane");
|
||||
tracing::trace!("cursor fast-path copy failed, falling back to rendering using offscreen buffer");
|
||||
|
||||
let offscreen_buffer = match renderer.create_buffer(DrmFourcc::Argb8888, cursor_buffer_size) {
|
||||
Ok(buffer) => buffer,
|
||||
Err(err) => {
|
||||
debug!(
|
||||
"failed to create offscreen buffer for cursor {:?}: {}",
|
||||
plane_info.handle, err
|
||||
);
|
||||
let Some(storage) = element.underlying_storage(renderer) else {
|
||||
trace!("Can't obtain cursor's underlying storage");
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(pixman_renderer) = cursor_state.pixman_renderer.as_mut() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// Create a pixman image from the source cursor data. This will either be set by the
|
||||
// client, or the compositor's choice.
|
||||
let cursor_texture = match storage {
|
||||
UnderlyingStorage::Wayland(buffer) => pixman_renderer
|
||||
.import_buffer(&buffer, None, &[element.src().to_i32_up()])
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten(),
|
||||
UnderlyingStorage::Memory(memory) => {
|
||||
let format = memory.format();
|
||||
let size = memory.size();
|
||||
let Ok(pixman_format) = pixman::FormatCode::try_from(format) else {
|
||||
debug!("No pixman format for {format}");
|
||||
return None;
|
||||
};
|
||||
unsafe {
|
||||
match pixman::Image::from_raw_mut(
|
||||
pixman_format,
|
||||
size.w as usize,
|
||||
size.h as usize,
|
||||
memory.as_ptr() as *mut u32,
|
||||
memory.stride() as usize,
|
||||
false,
|
||||
) {
|
||||
Ok(image) => Some(PixmanTexture::from(image)),
|
||||
Err(e) => {
|
||||
debug!("pixman cursor: {e}");
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
if let Err(err) = renderer.bind(offscreen_buffer) {
|
||||
debug!(
|
||||
"failed to bind cursor buffer for cursor {:?}: {}",
|
||||
plane_info.handle, err
|
||||
);
|
||||
return None;
|
||||
};
|
||||
|
||||
// save the renderer debug flags and disable all for the cursor plane
|
||||
let renderer_debug_flags = renderer.debug_flags();
|
||||
renderer.set_debug_flags(DebugFlags::empty());
|
||||
|
||||
let mut render = || {
|
||||
let mut frame = renderer.render(self.cursor_size, output_transform)?;
|
||||
let ret = cursor_buffer
|
||||
.map_mut::<_, _, Result<_, <PixmanRenderer as Renderer>::Error>>(
|
||||
&cursor_state.framebuffer_exporter,
|
||||
0,
|
||||
0,
|
||||
cursor_buffer_size.w as u32,
|
||||
cursor_buffer_size.h as u32,
|
||||
|mbo| {
|
||||
let plane_pixman_format = pixman::FormatCode::try_from(DrmFourcc::Argb8888).unwrap();
|
||||
let cursor_dst = unsafe {
|
||||
pixman::Image::from_raw_mut(
|
||||
plane_pixman_format,
|
||||
mbo.width() as usize,
|
||||
mbo.height() as usize,
|
||||
mbo.buffer_mut().as_mut_ptr() as *mut u32,
|
||||
mbo.stride() as usize,
|
||||
false,
|
||||
)
|
||||
}
|
||||
.map_err(|_| PixmanError::ImportFailed)?;
|
||||
pixman_renderer.bind(PixmanRenderBuffer::from(cursor_dst))?;
|
||||
|
||||
let mut frame = pixman_renderer.render(self.cursor_size, output_transform)?;
|
||||
frame.clear(
|
||||
[0f32, 0f32, 0f32, 0f32],
|
||||
&[Rectangle::from_loc_and_size((0, 0), self.cursor_size)],
|
||||
)?;
|
||||
|
||||
let src = element.src();
|
||||
let dst = Rectangle::from_loc_and_size((0, 0), element_geometry.size);
|
||||
element.draw(&mut frame, src, dst, &[dst])?;
|
||||
|
||||
frame.render_texture_from_to(
|
||||
&cursor_texture,
|
||||
src,
|
||||
dst,
|
||||
&[dst],
|
||||
element.transform(),
|
||||
element.alpha(),
|
||||
)?;
|
||||
frame.finish()?.wait();
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.expect("Lost track of cursor device");
|
||||
|
||||
Ok::<(), <R as Renderer>::Error>(())
|
||||
};
|
||||
_ = pixman_renderer.unbind();
|
||||
|
||||
let render_res = render();
|
||||
|
||||
// restore the renderer debug flags
|
||||
renderer.set_debug_flags(renderer_debug_flags);
|
||||
|
||||
if let Err(err) = render_res {
|
||||
debug!("failed to render cursor element: {}", err);
|
||||
return None;
|
||||
}
|
||||
|
||||
let copy_rect = Rectangle::from_loc_and_size((0, 0), cursor_buffer_size);
|
||||
let mapping = match renderer.copy_framebuffer(copy_rect, DrmFourcc::Argb8888) {
|
||||
Ok(mapping) => mapping,
|
||||
match ret {
|
||||
Err(err) => {
|
||||
info!("failed to export cursor offscreen buffer: {}", err);
|
||||
debug!("{err}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let data = match renderer.map_texture(&mapping) {
|
||||
Ok(data) => data,
|
||||
Err(err) => {
|
||||
info!("failed to map exported cursor offscreen buffer: {}", err);
|
||||
Ok(Err(err)) => {
|
||||
debug!("{err}");
|
||||
return None;
|
||||
}
|
||||
Ok(Ok(_)) => (),
|
||||
};
|
||||
|
||||
let Ok(res) = cursor_buffer.write(data) else {
|
||||
info!("failed to write cursor buffer, device destroyed");
|
||||
return None;
|
||||
};
|
||||
|
||||
if let Err(err) = res {
|
||||
info!("failed to write cursor buffer; {}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let src = Rectangle::from_loc_and_size(Point::default(), cursor_buffer_size).to_f64();
|
||||
|
|
Loading…
Reference in a new issue