text: use udmabuf for text upload
This commit is contained in:
parent
5758e16658
commit
c008b7ea35
12 changed files with 301 additions and 80 deletions
|
|
@ -821,7 +821,7 @@ pub trait GfxContext: Debug {
|
||||||
|
|
||||||
fn create_dmabuf_buffer(
|
fn create_dmabuf_buffer(
|
||||||
&self,
|
&self,
|
||||||
dmabuf: &Rc<OwnedFd>,
|
dmabuf: &OwnedFd,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> Result<Rc<dyn GfxBuffer>, GfxError> {
|
) -> Result<Rc<dyn GfxBuffer>, GfxError> {
|
||||||
|
|
|
||||||
|
|
@ -398,7 +398,7 @@ impl GfxContext for Context {
|
||||||
|
|
||||||
fn create_dmabuf_buffer(
|
fn create_dmabuf_buffer(
|
||||||
&self,
|
&self,
|
||||||
dmabuf: &Rc<OwnedFd>,
|
dmabuf: &OwnedFd,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> Result<Rc<dyn GfxBuffer>, GfxError> {
|
) -> Result<Rc<dyn GfxBuffer>, GfxError> {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ pub struct VulkanDmabufBuffer {
|
||||||
impl VulkanDevice {
|
impl VulkanDevice {
|
||||||
pub fn create_dmabuf_buffer(
|
pub fn create_dmabuf_buffer(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
dmabuf: &Rc<OwnedFd>,
|
dmabuf: &OwnedFd,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
size: u64,
|
size: u64,
|
||||||
) -> Result<Rc<VulkanDmabufBuffer>, VulkanError> {
|
) -> Result<Rc<VulkanDmabufBuffer>, VulkanError> {
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ impl WlBuffer {
|
||||||
if *udmabuf_impossible {
|
if *udmabuf_impossible {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let Some(dev) = self.client.state.udmabuf() else {
|
let Some(dev) = self.client.state.udmabuf.get() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let mask = page_size() - 1;
|
let mask = page_size() - 1;
|
||||||
|
|
|
||||||
48
src/pango.rs
48
src/pango.rs
|
|
@ -7,7 +7,10 @@ use {
|
||||||
},
|
},
|
||||||
std::{cell::Cell, ptr, rc::Rc},
|
std::{cell::Cell, ptr, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{IntoUstr, c},
|
uapi::{
|
||||||
|
IntoUstr,
|
||||||
|
c::{self, memset},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
|
|
@ -26,6 +29,13 @@ unsafe extern "C" {
|
||||||
width: c::c_int,
|
width: c::c_int,
|
||||||
height: c::c_int,
|
height: c::c_int,
|
||||||
) -> *mut cairo_surface_t;
|
) -> *mut cairo_surface_t;
|
||||||
|
fn cairo_image_surface_create_for_data(
|
||||||
|
data: *mut u8,
|
||||||
|
format: cairo_format_t,
|
||||||
|
width: c::c_int,
|
||||||
|
height: c::c_int,
|
||||||
|
stride: c::c_int,
|
||||||
|
) -> *mut cairo_surface_t;
|
||||||
fn cairo_image_surface_get_height(surface: *mut cairo_surface_t) -> c::c_int;
|
fn cairo_image_surface_get_height(surface: *mut cairo_surface_t) -> c::c_int;
|
||||||
fn cairo_image_surface_get_stride(surface: *mut cairo_surface_t) -> c::c_int;
|
fn cairo_image_surface_get_stride(surface: *mut cairo_surface_t) -> c::c_int;
|
||||||
fn cairo_image_surface_get_data(surface: *mut cairo_surface_t) -> *mut u8;
|
fn cairo_image_surface_get_data(surface: *mut cairo_surface_t) -> *mut u8;
|
||||||
|
|
@ -41,6 +51,8 @@ unsafe extern "C" {
|
||||||
fn cairo_set_operator(cr: *mut cairo_t, op: cairo_operator_t);
|
fn cairo_set_operator(cr: *mut cairo_t, op: cairo_operator_t);
|
||||||
fn cairo_set_source_rgba(cr: *mut cairo_t, red: f64, green: f64, blue: f64, alpha: f64);
|
fn cairo_set_source_rgba(cr: *mut cairo_t, red: f64, green: f64, blue: f64, alpha: f64);
|
||||||
fn cairo_move_to(cr: *mut cairo_t, x: f64, y: f64);
|
fn cairo_move_to(cr: *mut cairo_t, x: f64, y: f64);
|
||||||
|
|
||||||
|
fn cairo_format_stride_for_width(format: cairo_format_t, width: c::c_int) -> c::c_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
|
@ -141,6 +153,30 @@ impl CairoImageSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn new_image_surface_with_data(
|
||||||
|
format: CairoFormat,
|
||||||
|
data: *mut u8,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
stride: i32,
|
||||||
|
) -> Result<Rc<Self>, PangoError> {
|
||||||
|
unsafe {
|
||||||
|
memset(data.cast(), 0, (stride * height) as usize);
|
||||||
|
let s = cairo_image_surface_create_for_data(
|
||||||
|
data,
|
||||||
|
format.raw() as _,
|
||||||
|
width as _,
|
||||||
|
height as _,
|
||||||
|
stride as _,
|
||||||
|
);
|
||||||
|
let status = cairo_surface_status(s);
|
||||||
|
if status != 0 {
|
||||||
|
return Err(PangoError::CreateSurface(status as _));
|
||||||
|
}
|
||||||
|
Ok(Rc::new(Self { s }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_context(self: &Rc<Self>) -> Result<Rc<CairoContext>, PangoError> {
|
pub fn create_context(self: &Rc<Self>) -> Result<Rc<CairoContext>, PangoError> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let c = cairo_create(self.s);
|
let c = cairo_create(self.s);
|
||||||
|
|
@ -372,3 +408,13 @@ impl Drop for PangoLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cairo_size(format: CairoFormat, width: i32, height: i32) -> Option<(i32, usize)> {
|
||||||
|
let stride = unsafe { cairo_format_stride_for_width(format.raw() as _, width as _) };
|
||||||
|
if stride < 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let stride = stride as i32;
|
||||||
|
let size = height.checked_mul(stride)? as usize;
|
||||||
|
Some((stride, size))
|
||||||
|
}
|
||||||
|
|
|
||||||
26
src/state.rs
26
src/state.rs
|
|
@ -99,7 +99,7 @@ use {
|
||||||
TearingMode, ToplevelData, ToplevelNode, ToplevelNodeBase, VrrMode, WorkspaceNode,
|
TearingMode, ToplevelData, ToplevelNode, ToplevelNodeBase, VrrMode, WorkspaceNode,
|
||||||
generic_node_visitor,
|
generic_node_visitor,
|
||||||
},
|
},
|
||||||
udmabuf::{Udmabuf, UdmabufError},
|
udmabuf::UdmabufHolder,
|
||||||
utils::{
|
utils::{
|
||||||
activation_token::ActivationToken,
|
activation_token::ActivationToken,
|
||||||
asyncevent::AsyncEvent,
|
asyncevent::AsyncEvent,
|
||||||
|
|
@ -112,7 +112,6 @@ use {
|
||||||
hash_map_ext::HashMapExt,
|
hash_map_ext::HashMapExt,
|
||||||
linkedlist::LinkedList,
|
linkedlist::LinkedList,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
oserror::OsError,
|
|
||||||
queue::AsyncQueue,
|
queue::AsyncQueue,
|
||||||
refcounted::RefCounted,
|
refcounted::RefCounted,
|
||||||
run_toplevel::RunToplevel,
|
run_toplevel::RunToplevel,
|
||||||
|
|
@ -152,7 +151,7 @@ use {
|
||||||
time::Duration,
|
time::Duration,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{OwnedFd, c},
|
uapi::OwnedFd,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
|
@ -290,7 +289,7 @@ pub struct State {
|
||||||
pub xdg_surface_configure_events: AsyncQueue<XdgSurfaceConfigureEvent>,
|
pub xdg_surface_configure_events: AsyncQueue<XdgSurfaceConfigureEvent>,
|
||||||
pub workspace_display_order: Cell<WorkspaceDisplayOrder>,
|
pub workspace_display_order: Cell<WorkspaceDisplayOrder>,
|
||||||
pub outputs_without_hc: NumCell<usize>,
|
pub outputs_without_hc: NumCell<usize>,
|
||||||
pub udmabuf: CloneCell<Option<Option<Rc<Udmabuf>>>>,
|
pub udmabuf: Rc<UdmabufHolder>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -1568,25 +1567,6 @@ impl State {
|
||||||
found_tree.clear();
|
found_tree.clear();
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn udmabuf(&self) -> Option<Rc<Udmabuf>> {
|
|
||||||
if let Some(u) = self.udmabuf.get() {
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
match Udmabuf::new() {
|
|
||||||
Ok(u) => {
|
|
||||||
let u = Rc::new(u);
|
|
||||||
self.udmabuf.set(Some(Some(u.clone())));
|
|
||||||
Some(u)
|
|
||||||
}
|
|
||||||
Err(UdmabufError::Open(OsError(c::EPERM))) => None,
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("Could not create udmabuf device: {}", ErrorFmt(e));
|
|
||||||
self.udmabuf.set(Some(None));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
255
src/text.rs
255
src/text.rs
|
|
@ -4,20 +4,24 @@ use {
|
||||||
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker, PendingJob},
|
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker, PendingJob},
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AsyncShmGfxTexture, AsyncShmGfxTextureCallback, GfxContext, GfxError, GfxStagingBuffer,
|
AsyncShmGfxTexture, AsyncShmGfxTextureCallback, GfxBuffer, GfxContext, GfxError,
|
||||||
GfxTexture, PendingShmTransfer, STAGING_UPLOAD,
|
GfxStagingBuffer, GfxTexture, PendingShmTransfer, STAGING_UPLOAD,
|
||||||
},
|
},
|
||||||
pango::{
|
pango::{
|
||||||
CairoContext, CairoImageSurface, PangoCairoContext, PangoError, PangoFontDescription,
|
CairoContext, CairoImageSurface, PangoCairoContext, PangoError, PangoFontDescription,
|
||||||
PangoLayout,
|
PangoLayout, cairo_size,
|
||||||
consts::{
|
consts::{
|
||||||
CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE, PANGO_ELLIPSIZE_END, PANGO_SCALE,
|
CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE, CairoFormat, PANGO_ELLIPSIZE_END,
|
||||||
|
PANGO_SCALE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
|
state::State,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
|
udmabuf::UdmabufHolder,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, double_buffered::DoubleBuffered, on_drop_event::OnDropEvent,
|
clonecell::CloneCell, double_buffered::DoubleBuffered, errorfmt::ErrorFmt,
|
||||||
|
on_drop_event::OnDropEvent, oserror::OsError, page_size::page_size,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -25,10 +29,20 @@ use {
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
mem,
|
mem,
|
||||||
ops::Neg,
|
ops::Neg,
|
||||||
|
ptr,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::Arc,
|
slice,
|
||||||
|
sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering::Relaxed},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
uapi::{
|
||||||
|
OwnedFd,
|
||||||
|
c::{self, off_t},
|
||||||
|
ftruncate,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -41,14 +55,18 @@ pub enum TextError {
|
||||||
PangoContext(#[source] PangoError),
|
PangoContext(#[source] PangoError),
|
||||||
#[error("Could not create a pango layout")]
|
#[error("Could not create a pango layout")]
|
||||||
CreateLayout(#[source] PangoError),
|
CreateLayout(#[source] PangoError),
|
||||||
#[error("Could not access the cairo image data")]
|
|
||||||
ImageData(#[source] PangoError),
|
|
||||||
#[error("Texture upload failed")]
|
#[error("Texture upload failed")]
|
||||||
Upload(#[source] GfxError),
|
Upload(#[source] GfxError),
|
||||||
#[error("Could not create a texture")]
|
#[error("Could not create a texture")]
|
||||||
CreateTexture(#[source] GfxError),
|
CreateTexture(#[source] GfxError),
|
||||||
#[error("Rendering is not scheduled or not yet completed")]
|
#[error("Rendering is not scheduled or not yet completed")]
|
||||||
NotScheduled,
|
NotScheduled,
|
||||||
|
#[error("The size calculation overflowed")]
|
||||||
|
SizeOverflow,
|
||||||
|
#[error("Could not resize the memfd")]
|
||||||
|
ResizeMemfd(#[source] OsError),
|
||||||
|
#[error("Could not map the memfd")]
|
||||||
|
MapMemfd(#[source] OsError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Config<'a> {
|
impl<'a> Config<'a> {
|
||||||
|
|
@ -107,8 +125,22 @@ struct Data {
|
||||||
layout: PangoLayout,
|
layout: PangoLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_data(font: &str, width: i32, height: i32, scale: Option<f64>) -> Result<Data, TextError> {
|
const FORMAT: CairoFormat = CAIRO_FORMAT_ARGB32;
|
||||||
let image = match CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height) {
|
|
||||||
|
fn create_data(
|
||||||
|
memfd: &Memfd,
|
||||||
|
font: &str,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
scale: Option<f64>,
|
||||||
|
) -> Result<Data, TextError> {
|
||||||
|
let Some((stride, size)) = cairo_size(FORMAT, width, height) else {
|
||||||
|
return Err(TextError::SizeOverflow);
|
||||||
|
};
|
||||||
|
let data = memfd.get_pointer_for_size(size)?;
|
||||||
|
let image = match unsafe {
|
||||||
|
CairoImageSurface::new_image_surface_with_data(FORMAT, data, width, height, stride)
|
||||||
|
} {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return Err(TextError::CreateImage(e)),
|
Err(e) => return Err(TextError::CreateImage(e)),
|
||||||
};
|
};
|
||||||
|
|
@ -139,12 +171,13 @@ fn create_data(font: &str, width: i32, height: i32, scale: Option<f64>) -> Resul
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure(
|
fn measure(
|
||||||
|
memfd: &Memfd,
|
||||||
font: &str,
|
font: &str,
|
||||||
text: &str,
|
text: &str,
|
||||||
markup: bool,
|
markup: bool,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<TextMeasurement, TextError> {
|
) -> Result<TextMeasurement, TextError> {
|
||||||
let data = create_data(font, 1, 1, scale)?;
|
let data = create_data(memfd, font, 1, 1, scale)?;
|
||||||
if markup {
|
if markup {
|
||||||
data.layout.set_markup(text);
|
data.layout.set_markup(text);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -156,6 +189,7 @@ fn measure(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
|
memfd: &Memfd,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: Option<i32>,
|
y: Option<i32>,
|
||||||
width: i32,
|
width: i32,
|
||||||
|
|
@ -173,10 +207,9 @@ fn render(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
stride: width * 4,
|
stride: width * 4,
|
||||||
data: vec![],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let data = create_data(font, width, height, scale)?;
|
let data = create_data(memfd, font, width, height, scale)?;
|
||||||
if ellipsize {
|
if ellipsize {
|
||||||
data.layout
|
data.layout
|
||||||
.set_width((width - 2 * padding).max(0) * PANGO_SCALE);
|
.set_width((width - 2 * padding).max(0) * PANGO_SCALE);
|
||||||
|
|
@ -199,11 +232,11 @@ fn render(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
stride: data.image.stride(),
|
stride: data.image.stride(),
|
||||||
data: data.image.data().map_err(TextError::ImageData)?.to_vec(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_fitting(
|
fn render_fitting(
|
||||||
|
memfd: &Memfd,
|
||||||
height: Option<i32>,
|
height: Option<i32>,
|
||||||
font: &str,
|
font: &str,
|
||||||
text: &str,
|
text: &str,
|
||||||
|
|
@ -211,7 +244,7 @@ fn render_fitting(
|
||||||
markup: bool,
|
markup: bool,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<RenderedText, TextError> {
|
) -> Result<RenderedText, TextError> {
|
||||||
let measurement = measure(font, text, markup, scale)?;
|
let measurement = measure(memfd, font, text, markup, scale)?;
|
||||||
let x = measurement.ink_rect.x1().neg();
|
let x = measurement.ink_rect.x1().neg();
|
||||||
let y = match height {
|
let y = match height {
|
||||||
Some(_) => None,
|
Some(_) => None,
|
||||||
|
|
@ -220,7 +253,7 @@ fn render_fitting(
|
||||||
let width = measurement.ink_rect.width();
|
let width = measurement.ink_rect.width();
|
||||||
let height = height.unwrap_or(measurement.ink_rect.height());
|
let height = height.unwrap_or(measurement.ink_rect.height());
|
||||||
render(
|
render(
|
||||||
x, y, width, height, 0, font, text, color, false, markup, scale,
|
memfd, x, y, width, height, 0, font, text, color, false, markup, scale,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,11 +266,10 @@ struct RenderedText {
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
stride: i32,
|
stride: i32,
|
||||||
data: Vec<Cell<u8>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct RenderWork {
|
struct RenderWork {
|
||||||
|
memfd: Arc<Memfd>,
|
||||||
config: Config<'static>,
|
config: Config<'static>,
|
||||||
result: Option<Result<RenderedText, TextError>>,
|
result: Option<Result<RenderedText, TextError>>,
|
||||||
}
|
}
|
||||||
|
|
@ -265,7 +297,7 @@ impl RenderWork {
|
||||||
color,
|
color,
|
||||||
markup,
|
markup,
|
||||||
scale,
|
scale,
|
||||||
} => render_fitting(height, font, text, color, markup, scale),
|
} => render_fitting(&self.memfd, height, font, text, color, markup, scale),
|
||||||
Config::Render {
|
Config::Render {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
|
@ -279,7 +311,18 @@ impl RenderWork {
|
||||||
markup,
|
markup,
|
||||||
scale,
|
scale,
|
||||||
} => render(
|
} => render(
|
||||||
x, y, width, height, padding, font, text, color, ellipsize, markup, scale,
|
&self.memfd,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
padding,
|
||||||
|
font,
|
||||||
|
text,
|
||||||
|
color,
|
||||||
|
ellipsize,
|
||||||
|
markup,
|
||||||
|
scale,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -303,6 +346,7 @@ impl Drop for TextTexture {
|
||||||
struct Shared {
|
struct Shared {
|
||||||
cpu_worker: Rc<CpuWorker>,
|
cpu_worker: Rc<CpuWorker>,
|
||||||
ctx: Rc<dyn GfxContext>,
|
ctx: Rc<dyn GfxContext>,
|
||||||
|
udmabuf: Rc<UdmabufHolder>,
|
||||||
staging: CloneCell<Option<Rc<dyn GfxStagingBuffer>>>,
|
staging: CloneCell<Option<Rc<dyn GfxStagingBuffer>>>,
|
||||||
textures: DoubleBuffered<TextBuffer>,
|
textures: DoubleBuffered<TextBuffer>,
|
||||||
pending_render: Cell<Option<PendingJob>>,
|
pending_render: Cell<Option<PendingJob>>,
|
||||||
|
|
@ -312,6 +356,15 @@ struct Shared {
|
||||||
waiter: Cell<Option<Rc<dyn OnCompleted>>>,
|
waiter: Cell<Option<Rc<dyn OnCompleted>>>,
|
||||||
busy: Cell<bool>,
|
busy: Cell<bool>,
|
||||||
flip_is_noop: Cell<bool>,
|
flip_is_noop: Cell<bool>,
|
||||||
|
memfd: Arc<Memfd>,
|
||||||
|
gfx_buffer: CloneCell<Option<Option<Rc<dyn GfxBuffer>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Memfd {
|
||||||
|
fd: OwnedFd,
|
||||||
|
size: AtomicUsize,
|
||||||
|
size_changed: AtomicBool,
|
||||||
|
mapping: AtomicPtr<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Shared {
|
impl Shared {
|
||||||
|
|
@ -325,6 +378,36 @@ impl Shared {
|
||||||
waiter.completed();
|
waiter.completed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_gfx_buffer(&self) -> Option<Rc<dyn GfxBuffer>> {
|
||||||
|
if self.memfd.size_changed.load(Relaxed) {
|
||||||
|
self.gfx_buffer.take();
|
||||||
|
self.memfd.size_changed.store(false, Relaxed);
|
||||||
|
}
|
||||||
|
if let Some(res) = self.gfx_buffer.get() {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
let size = self.memfd.size.load(Relaxed);
|
||||||
|
let udmabuf = self.udmabuf.get()?;
|
||||||
|
let res = 'res: {
|
||||||
|
let dmabuf = match udmabuf.create_dmabuf_from_memfd(&self.memfd.fd, 0, size) {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not create udmabuf: {}", ErrorFmt(e));
|
||||||
|
break 'res None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match self.ctx.create_dmabuf_buffer(&dmabuf, 0, size) {
|
||||||
|
Ok(b) => Some(b),
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!("Could not create GfxBuffer: {}", ErrorFmt(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.gfx_buffer.set(Some(res.clone()));
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Default)]
|
#[derive(PartialEq, Default)]
|
||||||
|
|
@ -365,10 +448,14 @@ pub trait OnCompleted {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextTexture {
|
impl TextTexture {
|
||||||
pub fn new(cpu_worker: &Rc<CpuWorker>, ctx: &Rc<dyn GfxContext>) -> Self {
|
pub fn new(state: &Rc<State>, ctx: &Rc<dyn GfxContext>) -> Self {
|
||||||
|
let memfd = uapi::memfd_create("text", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING)
|
||||||
|
.expect("Could not create memfd");
|
||||||
|
let _ = uapi::fcntl_add_seals(memfd.raw(), c::F_SEAL_SHRINK);
|
||||||
let data = Rc::new(Shared {
|
let data = Rc::new(Shared {
|
||||||
cpu_worker: cpu_worker.clone(),
|
cpu_worker: state.cpu_worker.clone(),
|
||||||
ctx: ctx.clone(),
|
ctx: ctx.clone(),
|
||||||
|
udmabuf: state.udmabuf.clone(),
|
||||||
staging: Default::default(),
|
staging: Default::default(),
|
||||||
textures: Default::default(),
|
textures: Default::default(),
|
||||||
pending_render: Default::default(),
|
pending_render: Default::default(),
|
||||||
|
|
@ -378,6 +465,13 @@ impl TextTexture {
|
||||||
waiter: Default::default(),
|
waiter: Default::default(),
|
||||||
busy: Default::default(),
|
busy: Default::default(),
|
||||||
flip_is_noop: Default::default(),
|
flip_is_noop: Default::default(),
|
||||||
|
memfd: Arc::new(Memfd {
|
||||||
|
fd: memfd,
|
||||||
|
size: Default::default(),
|
||||||
|
size_changed: Default::default(),
|
||||||
|
mapping: Default::default(),
|
||||||
|
}),
|
||||||
|
gfx_buffer: Default::default(),
|
||||||
});
|
});
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
|
|
@ -403,13 +497,18 @@ impl TextTexture {
|
||||||
}
|
}
|
||||||
let mut job = self.data.render_job.take().unwrap_or_else(|| {
|
let mut job = self.data.render_job.take().unwrap_or_else(|| {
|
||||||
Box::new(RenderJob {
|
Box::new(RenderJob {
|
||||||
work: Default::default(),
|
work: RenderWork {
|
||||||
|
memfd: self.data.memfd.clone(),
|
||||||
|
config: Default::default(),
|
||||||
|
result: Default::default(),
|
||||||
|
},
|
||||||
data: Rc::downgrade(&self.data),
|
data: Rc::downgrade(&self.data),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
job.work = RenderWork {
|
job.work = RenderWork {
|
||||||
config: config.to_static(),
|
config: config.to_static(),
|
||||||
result: None,
|
result: None,
|
||||||
|
..job.work
|
||||||
};
|
};
|
||||||
let pending = self.data.cpu_worker.submit(job);
|
let pending = self.data.cpu_worker.submit(job);
|
||||||
self.data.pending_render.set(Some(pending));
|
self.data.pending_render.set(Some(pending));
|
||||||
|
|
@ -527,29 +626,36 @@ impl CpuJob for RenderJob {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut staging_opt = data.staging.take();
|
let mut staging_opt = data.staging.take();
|
||||||
if let Some(staging) = &staging_opt
|
let pending = if let Some(gfx_buffer) = data.get_gfx_buffer() {
|
||||||
&& staging.size() != tex.staging_size()
|
tex.clone()
|
||||||
{
|
.async_upload_from_buffer(
|
||||||
staging_opt = None;
|
&gfx_buffer,
|
||||||
}
|
data.clone(),
|
||||||
let staging = match staging_opt {
|
Region::new(Rect::new_sized_unchecked(0, 0, rt.width, rt.height)),
|
||||||
Some(s) => s,
|
)
|
||||||
None => data
|
.map_err(TextError::Upload)
|
||||||
.ctx
|
} else {
|
||||||
.create_staging_buffer(tex.staging_size(), STAGING_UPLOAD),
|
if let Some(staging) = &staging_opt
|
||||||
|
&& staging.size() != tex.staging_size()
|
||||||
|
{
|
||||||
|
staging_opt = None;
|
||||||
|
}
|
||||||
|
let staging = staging_opt.get_or_insert_with(|| {
|
||||||
|
data.ctx
|
||||||
|
.create_staging_buffer(tex.staging_size(), STAGING_UPLOAD)
|
||||||
|
});
|
||||||
|
tex.clone()
|
||||||
|
.async_upload(
|
||||||
|
&staging,
|
||||||
|
data.clone(),
|
||||||
|
Rc::new(data.memfd.data(rt.stride, rt.height)),
|
||||||
|
Region::new(Rect::new_sized_unchecked(0, 0, rt.width, rt.height)),
|
||||||
|
)
|
||||||
|
.map_err(TextError::Upload)
|
||||||
};
|
};
|
||||||
let pending = tex
|
|
||||||
.clone()
|
|
||||||
.async_upload(
|
|
||||||
&staging,
|
|
||||||
data.clone(),
|
|
||||||
Rc::new(rt.data),
|
|
||||||
Region::new(Rect::new_sized_unchecked(0, 0, rt.width, rt.height)),
|
|
||||||
)
|
|
||||||
.map_err(TextError::Upload);
|
|
||||||
if pending.is_ok() {
|
if pending.is_ok() {
|
||||||
data.textures.back().tex.set(Some(tex));
|
data.textures.back().tex.set(Some(tex));
|
||||||
data.staging.set(Some(staging));
|
data.staging.set(staging_opt);
|
||||||
}
|
}
|
||||||
match pending {
|
match pending {
|
||||||
Ok(Some(p)) => data.pending_upload.set(Some(p)),
|
Ok(Some(p)) => data.pending_upload.set(Some(p)),
|
||||||
|
|
@ -571,3 +677,66 @@ impl OnCompleted for OnDropEvent {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Memfd {
|
||||||
|
fn get_pointer_for_size(&self, size: usize) -> Result<*mut u8, TextError> {
|
||||||
|
let old_size = self.size.load(Relaxed);
|
||||||
|
if old_size >= size {
|
||||||
|
return Ok(self.mapping.load(Relaxed));
|
||||||
|
}
|
||||||
|
let Some(size) = size.checked_next_multiple_of(page_size()) else {
|
||||||
|
return Err(TextError::SizeOverflow);
|
||||||
|
};
|
||||||
|
let Ok(isize) = off_t::try_from(size) else {
|
||||||
|
return Err(TextError::SizeOverflow);
|
||||||
|
};
|
||||||
|
if let Err(e) = ftruncate(self.fd.raw(), isize) {
|
||||||
|
return Err(TextError::ResizeMemfd(e.into()));
|
||||||
|
}
|
||||||
|
let old_ptr = self.mapping.load(Relaxed);
|
||||||
|
let new_ptr = if old_ptr.is_null() {
|
||||||
|
unsafe {
|
||||||
|
c::mmap(
|
||||||
|
ptr::null_mut(),
|
||||||
|
size,
|
||||||
|
c::PROT_READ | c::PROT_WRITE,
|
||||||
|
c::MAP_SHARED,
|
||||||
|
self.fd.raw(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe { c::mremap(old_ptr.cast(), old_size, size, c::MREMAP_MAYMOVE) }
|
||||||
|
};
|
||||||
|
if new_ptr == c::MAP_FAILED {
|
||||||
|
return Err(TextError::MapMemfd(OsError::default()));
|
||||||
|
}
|
||||||
|
let new_ptr = new_ptr.cast();
|
||||||
|
self.mapping.store(new_ptr, Relaxed);
|
||||||
|
self.size.store(size, Relaxed);
|
||||||
|
self.size_changed.store(true, Relaxed);
|
||||||
|
Ok(new_ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data(&self, stride: i32, height: i32) -> Vec<Cell<u8>> {
|
||||||
|
let size = (stride * height) as usize;
|
||||||
|
assert!(size <= self.size.load(Relaxed));
|
||||||
|
if size == 0 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
let mapping = self.mapping.load(Relaxed);
|
||||||
|
unsafe { slice::from_raw_parts(mapping.cast(), size).to_vec() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Memfd {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let ptr = self.mapping.load(Relaxed);
|
||||||
|
if ptr.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
c::munmap(ptr.cast(), self.size.load(Relaxed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -725,8 +725,7 @@ impl ContainerNode {
|
||||||
let title = child.title.borrow_mut();
|
let title = child.title.borrow_mut();
|
||||||
let tt = &mut *child.title_tex.borrow_mut();
|
let tt = &mut *child.title_tex.borrow_mut();
|
||||||
for (scale, _) in scales.iter() {
|
for (scale, _) in scales.iter() {
|
||||||
let tex = tt
|
let tex = tt.get_or_insert_with(*scale, || TextTexture::new(&self.state, &ctx));
|
||||||
.get_or_insert_with(*scale, || TextTexture::new(&self.state.cpu_worker, &ctx));
|
|
||||||
let mut th = th;
|
let mut th = th;
|
||||||
let mut scalef = None;
|
let mut scalef = None;
|
||||||
let mut width = rect.width();
|
let mut width = rect.width();
|
||||||
|
|
|
||||||
|
|
@ -215,8 +215,7 @@ impl FloatNode {
|
||||||
let tr = self.title_rect.get();
|
let tr = self.title_rect.get();
|
||||||
let tt = &mut *self.title_textures.borrow_mut();
|
let tt = &mut *self.title_textures.borrow_mut();
|
||||||
for (scale, _) in scales.iter() {
|
for (scale, _) in scales.iter() {
|
||||||
let tex =
|
let tex = tt.get_or_insert_with(*scale, || TextTexture::new(&self.state, &ctx));
|
||||||
tt.get_or_insert_with(*scale, || TextTexture::new(&self.state.cpu_worker, &ctx));
|
|
||||||
let mut th = tr.height();
|
let mut th = tr.height();
|
||||||
let mut scalef = None;
|
let mut scalef = None;
|
||||||
let mut width = tr.width();
|
let mut width = tr.width();
|
||||||
|
|
|
||||||
|
|
@ -530,7 +530,7 @@ impl OutputNode {
|
||||||
let active_id = self.workspace.get().map(|w| w.id);
|
let active_id = self.workspace.get().map(|w| w.id);
|
||||||
for ws in self.workspaces.iter() {
|
for ws in self.workspaces.iter() {
|
||||||
let tex = &mut *ws.title_texture.borrow_mut();
|
let tex = &mut *ws.title_texture.borrow_mut();
|
||||||
let tex = tex.get_or_insert_with(|| TextTexture::new(&self.state.cpu_worker, &ctx));
|
let tex = tex.get_or_insert_with(|| TextTexture::new(&self.state, &ctx));
|
||||||
let tc = match active_id == Some(ws.id) {
|
let tc = match active_id == Some(ws.id) {
|
||||||
true => theme.colors.focused_title_text.get(),
|
true => theme.colors.focused_title_text.get(),
|
||||||
false => theme.colors.unfocused_title_text.get(),
|
false => theme.colors.unfocused_title_text.get(),
|
||||||
|
|
@ -548,7 +548,7 @@ impl OutputNode {
|
||||||
let mut rd = self.render_data.borrow_mut();
|
let mut rd = self.render_data.borrow_mut();
|
||||||
let tex = rd.status.get_or_insert_with(|| OutputStatus {
|
let tex = rd.status.get_or_insert_with(|| OutputStatus {
|
||||||
tex_x: 0,
|
tex_x: 0,
|
||||||
tex: TextTexture::new(&self.state.cpu_worker, &ctx),
|
tex: TextTexture::new(&self.state, &ctx),
|
||||||
});
|
});
|
||||||
let status = self.status.get();
|
let status = self.status.get();
|
||||||
let tc = self.state.theme.colors.bar_text.get();
|
let tc = self.state.theme.colors.bar_text.get();
|
||||||
|
|
|
||||||
|
|
@ -111,8 +111,7 @@ impl PlaceholderNode {
|
||||||
let rect = self.toplevel.pos.get();
|
let rect = self.toplevel.pos.get();
|
||||||
let mut textures = self.textures.borrow_mut();
|
let mut textures = self.textures.borrow_mut();
|
||||||
for (scale, _) in scales.iter() {
|
for (scale, _) in scales.iter() {
|
||||||
let tex = textures
|
let tex = textures.get_or_insert_with(*scale, || TextTexture::new(&self.state, &ctx));
|
||||||
.get_or_insert_with(*scale, || TextTexture::new(&self.state.cpu_worker, &ctx));
|
|
||||||
let mut width = rect.width();
|
let mut width = rect.width();
|
||||||
let mut height = rect.height();
|
let mut height = rect.height();
|
||||||
if *scale != 1 {
|
if *scale != 1 {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage, MappedBuffer},
|
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage, MappedBuffer},
|
||||||
format::Format,
|
format::Format,
|
||||||
utils::{compat::IoctlNumber, oserror::OsError, page_size::page_size},
|
utils::{
|
||||||
|
clonecell::CloneCell, compat::IoctlNumber, errorfmt::ErrorFmt, oserror::OsError,
|
||||||
|
page_size::page_size,
|
||||||
|
},
|
||||||
video::{
|
video::{
|
||||||
LINEAR_MODIFIER, Modifier,
|
LINEAR_MODIFIER, Modifier,
|
||||||
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
|
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
|
||||||
|
|
@ -51,6 +54,32 @@ pub enum UdmabufError {
|
||||||
Map(#[source] OsError),
|
Map(#[source] OsError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UdmabufHolder {
|
||||||
|
udmabuf: CloneCell<Option<Option<Rc<Udmabuf>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UdmabufHolder {
|
||||||
|
pub fn get(&self) -> Option<Rc<Udmabuf>> {
|
||||||
|
if let Some(u) = self.udmabuf.get() {
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
match Udmabuf::new() {
|
||||||
|
Ok(u) => {
|
||||||
|
let u = Rc::new(u);
|
||||||
|
self.udmabuf.set(Some(Some(u.clone())));
|
||||||
|
Some(u)
|
||||||
|
}
|
||||||
|
Err(UdmabufError::Open(OsError(c::EPERM))) => None,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not create udmabuf device: {}", ErrorFmt(e));
|
||||||
|
self.udmabuf.set(Some(None));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Udmabuf {
|
pub struct Udmabuf {
|
||||||
fd: OwnedFd,
|
fd: OwnedFd,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue