Merge pull request #109 from mahkoh/jorth/output-transform
Implement output transforms
This commit is contained in:
commit
7a67784502
37 changed files with 872 additions and 647 deletions
|
|
@ -15,7 +15,7 @@ use {
|
|||
timer::Timer,
|
||||
video::{
|
||||
connector_type::{ConnectorType, CON_UNKNOWN},
|
||||
Connector, DrmDevice, GfxApi, Mode,
|
||||
Connector, DrmDevice, GfxApi, Mode, Transform,
|
||||
},
|
||||
Axis, Direction, ModifiedKeySym, PciId, Workspace,
|
||||
},
|
||||
|
|
@ -473,6 +473,13 @@ impl Client {
|
|||
self.send(&ClientMessage::ConnectorSetEnabled { connector, enabled });
|
||||
}
|
||||
|
||||
pub fn connector_set_transform(&self, connector: Connector, transform: Transform) {
|
||||
self.send(&ClientMessage::ConnectorSetTransform {
|
||||
connector,
|
||||
transform,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn device_connectors(&self, device: DrmDevice) -> Vec<Connector> {
|
||||
let res = self.send_with_response(&ClientMessage::GetDeviceConnectors { device });
|
||||
get_response!(res, vec![], GetDeviceConnectors { connectors });
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use {
|
|||
logging::LogLevel,
|
||||
theme::{colors::Colorable, sized::Resizable, Color},
|
||||
timer::Timer,
|
||||
video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi},
|
||||
video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi, Transform},
|
||||
Axis, Direction, PciId, Workspace,
|
||||
},
|
||||
serde::{Deserialize, Serialize},
|
||||
|
|
@ -342,6 +342,10 @@ pub enum ClientMessage<'a> {
|
|||
device: Option<DrmDevice>,
|
||||
enabled: bool,
|
||||
},
|
||||
ConnectorSetTransform {
|
||||
connector: Connector,
|
||||
transform: Transform,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -158,6 +158,15 @@ impl Connector {
|
|||
}
|
||||
get!().connector_set_enabled(self, enabled);
|
||||
}
|
||||
|
||||
/// Sets the transformation to apply to the content of this connector.
|
||||
pub fn set_transform(self, transform: Transform) {
|
||||
if !self.exists() {
|
||||
log::warn!("set_transform called on a connector that does not exist");
|
||||
return;
|
||||
}
|
||||
get!().connector_set_transform(self, transform);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all available DRM devices.
|
||||
|
|
@ -403,3 +412,25 @@ pub fn set_gfx_api(gfx_api: GfxApi) {
|
|||
pub fn set_direct_scanout_enabled(enabled: bool) {
|
||||
get!().set_direct_scanout_enabled(None, enabled);
|
||||
}
|
||||
|
||||
/// A transformation.
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||
pub enum Transform {
|
||||
/// No transformation.
|
||||
#[default]
|
||||
None,
|
||||
/// Rotate 90 degrees counter-clockwise.
|
||||
Rotate90,
|
||||
/// Rotate 180 degrees counter-clockwise.
|
||||
Rotate180,
|
||||
/// Rotate 270 degrees counter-clockwise.
|
||||
Rotate270,
|
||||
/// Flip around the vertical axis.
|
||||
Flip,
|
||||
/// Flip around the vertical axis, then rotate 90 degrees counter-clockwise.
|
||||
FlipRotate90,
|
||||
/// Flip around the vertical axis, then rotate 180 degrees counter-clockwise.
|
||||
FlipRotate180,
|
||||
/// Flip around the vertical axis, then rotate 270 degrees counter-clockwise.
|
||||
FlipRotate270,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ pub trait HardwareCursor: Debug {
|
|||
fn set_position(&self, x: i32, y: i32);
|
||||
fn swap_buffer(&self);
|
||||
fn commit(&self);
|
||||
fn max_size(&self) -> (i32, i32);
|
||||
fn size(&self) -> (i32, i32);
|
||||
}
|
||||
|
||||
pub type TransformMatrix = [[f64; 2]; 2];
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@ use {
|
|||
drm_feedback::DrmFeedback,
|
||||
edid::Descriptor,
|
||||
format::{Format, ARGB8888, XRGB8888},
|
||||
gfx_api::{
|
||||
AbsoluteRect, BufferPoints, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass,
|
||||
GfxTexture,
|
||||
},
|
||||
gfx_api::{GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass, GfxTexture},
|
||||
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
||||
renderer::RenderResult,
|
||||
state::State,
|
||||
|
|
@ -23,6 +20,7 @@ use {
|
|||
asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap, debug_fn::debug_fn, errorfmt::ErrorFmt, numcell::NumCell,
|
||||
opaque_cell::OpaqueCell, oserror::OsError, syncqueue::SyncQueue,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
video::{
|
||||
dmabuf::DmaBufId,
|
||||
|
|
@ -287,7 +285,7 @@ impl HardwareCursor for MetalHardwareCursor {
|
|||
}
|
||||
}
|
||||
|
||||
fn max_size(&self) -> (i32, i32) {
|
||||
fn size(&self) -> (i32, i32) {
|
||||
(
|
||||
self.connector.dev.cursor_width as _,
|
||||
self.connector.dev.cursor_height as _,
|
||||
|
|
@ -428,8 +426,6 @@ impl MetalConnector {
|
|||
pass: &GfxRenderPass,
|
||||
plane: &Rc<MetalPlane>,
|
||||
) -> Option<DirectScanoutData> {
|
||||
let plane_w = plane.mode_w.get();
|
||||
let plane_h = plane.mode_h.get();
|
||||
let ct = 'ct: {
|
||||
let mut ops = pass.ops.iter().rev();
|
||||
let ct = 'ct2: {
|
||||
|
|
@ -445,13 +441,7 @@ impl MetalConnector {
|
|||
}
|
||||
return None;
|
||||
};
|
||||
let plane_rect = AbsoluteRect {
|
||||
x1: 0.0,
|
||||
x2: plane_w as f32,
|
||||
y1: 0.0,
|
||||
y2: plane_h as f32,
|
||||
};
|
||||
if !ct.tex.format().has_alpha && ct.target == plane_rect {
|
||||
if !ct.tex.format().has_alpha && ct.target.is_covering() {
|
||||
// Texture covers the entire screen and is opaque.
|
||||
break 'ct ct;
|
||||
}
|
||||
|
|
@ -461,7 +451,7 @@ impl MetalConnector {
|
|||
GfxApiOpt::FillRect(fr) => {
|
||||
if fr.color == Color::SOLID_BLACK {
|
||||
// Black fills can be ignored because this is the CRTC background color.
|
||||
if fr.rect == plane_rect {
|
||||
if fr.rect.is_covering() {
|
||||
// If fill covers the entire screen, we don't have to look further.
|
||||
break 'ct ct;
|
||||
}
|
||||
|
|
@ -484,20 +474,34 @@ impl MetalConnector {
|
|||
}
|
||||
ct
|
||||
};
|
||||
if ct.source != BufferPoints::identity() {
|
||||
// Non-trivial transforms are not supported.
|
||||
if ct.source.buffer_transform != ct.target.output_transform {
|
||||
// Rotations and mirroring are not supported.
|
||||
return None;
|
||||
}
|
||||
if ct.target.x1 < 0.0
|
||||
|| ct.target.y1 < 0.0
|
||||
|| ct.target.x2 > plane_w as f32
|
||||
|| ct.target.y2 > plane_h as f32
|
||||
{
|
||||
if !ct.source.is_covering() {
|
||||
// Viewports are not supported.
|
||||
return None;
|
||||
}
|
||||
if ct.target.x1 < -1.0 || ct.target.y1 < -1.0 || ct.target.x2 > 1.0 || ct.target.y2 > 1.0 {
|
||||
// Rendering outside the screen is not supported.
|
||||
return None;
|
||||
}
|
||||
let (tex_w, tex_h) = ct.tex.size();
|
||||
let (crtc_w, crtc_h) = (ct.target.x2 - ct.target.x1, ct.target.y2 - ct.target.y1);
|
||||
let (x1, x2, y1, y2) = {
|
||||
let plane_w = plane.mode_w.get() as f32;
|
||||
let plane_h = plane.mode_h.get() as f32;
|
||||
let ((x1, x2), (y1, y2)) = ct
|
||||
.target
|
||||
.output_transform
|
||||
.maybe_swap(((ct.target.x1, ct.target.x2), (ct.target.y1, ct.target.y2)));
|
||||
(
|
||||
(x1 + 1.0) * plane_w / 2.0,
|
||||
(x2 + 1.0) * plane_w / 2.0,
|
||||
(y1 + 1.0) * plane_h / 2.0,
|
||||
(y2 + 1.0) * plane_h / 2.0,
|
||||
)
|
||||
};
|
||||
let (crtc_w, crtc_h) = (x2 - x1, y2 - y1);
|
||||
if crtc_w < 0.0 || crtc_h < 0.0 {
|
||||
// Flipping x or y axis is not supported.
|
||||
return None;
|
||||
|
|
@ -513,8 +517,8 @@ impl MetalConnector {
|
|||
let position = DirectScanoutPosition {
|
||||
src_width: tex_w,
|
||||
src_height: tex_h,
|
||||
crtc_x: ct.target.x1 as _,
|
||||
crtc_y: ct.target.y1 as _,
|
||||
crtc_x: x1 as _,
|
||||
crtc_y: y1 as _,
|
||||
crtc_width: crtc_w as _,
|
||||
crtc_height: crtc_h as _,
|
||||
};
|
||||
|
|
@ -595,9 +599,9 @@ impl MetalConnector {
|
|||
output.global.preferred_scale.get(),
|
||||
render_hw_cursor,
|
||||
output.has_fullscreen(),
|
||||
output.global.transform.get(),
|
||||
);
|
||||
let try_direct_scanout = try_direct_scanout
|
||||
&& !output.global.have_shm_screencopies()
|
||||
&& self.direct_scanout_enabled()
|
||||
// at least on AMD, using a FB on a different device for rendering will fail
|
||||
// and destroy the render context. it's possible to work around this by waiting
|
||||
|
|
@ -609,7 +613,6 @@ impl MetalConnector {
|
|||
if try_direct_scanout {
|
||||
if let Some(dsd) = self.prepare_direct_scanout(&pass, plane) {
|
||||
output.perform_screencopies(
|
||||
None,
|
||||
&dsd.tex,
|
||||
!render_hw_cursor,
|
||||
dsd.position.crtc_x,
|
||||
|
|
@ -634,14 +637,7 @@ impl MetalConnector {
|
|||
if let Some(tex) = &buffer.dev_tex {
|
||||
buffer.dev_fb.copy_texture(tex, 0, 0);
|
||||
}
|
||||
output.perform_screencopies(
|
||||
Some(&*buffer_fb),
|
||||
&buffer.render_tex,
|
||||
!render_hw_cursor,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
);
|
||||
output.perform_screencopies(&buffer.render_tex, !render_hw_cursor, 0, 0, None);
|
||||
buffer.drm.clone()
|
||||
}
|
||||
Some(dsd) => dsd.fb.clone(),
|
||||
|
|
@ -729,7 +725,7 @@ impl MetalConnector {
|
|||
buffer.dev_fb.copy_texture(tex, 0, 0);
|
||||
}
|
||||
}
|
||||
let (width, height) = buffer.dev_fb.size();
|
||||
let (width, height) = buffer.dev_fb.physical_size();
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
||||
c.change(plane.crtc_id.id, crtc.id.0 as _);
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ fn start_compositor2(
|
|||
dma_buf_ids: Default::default(),
|
||||
drm_feedback_ids: Default::default(),
|
||||
direct_scanout_enabled: Cell::new(true),
|
||||
output_transforms: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ use {
|
|||
logging::LogLevel,
|
||||
theme::{colors::Colorable, sized::Resizable},
|
||||
timer::Timer as JayTimer,
|
||||
video::{Connector, DrmDevice, GfxApi},
|
||||
video::{Connector, DrmDevice, GfxApi, Transform},
|
||||
Axis, Direction, Workspace,
|
||||
},
|
||||
libloading::Library,
|
||||
|
|
@ -751,6 +751,17 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_set_transform(
|
||||
&self,
|
||||
connector: Connector,
|
||||
transform: Transform,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
connector.node.update_transform(transform);
|
||||
self.state.damage();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_set_position(
|
||||
&self,
|
||||
connector: Connector,
|
||||
|
|
@ -1338,6 +1349,12 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetDirectScanoutEnabled { device, enabled } => self
|
||||
.handle_set_direct_scanout_enabled(device, enabled)
|
||||
.wrn("set_direct_scanout_enabled")?,
|
||||
ClientMessage::ConnectorSetTransform {
|
||||
connector,
|
||||
transform,
|
||||
} => self
|
||||
.handle_connector_set_transform(connector, transform)
|
||||
.wrn("connector_set_transform")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
|
|||
} else {
|
||||
img.extents.move_(x.round_down(), y.round_down())
|
||||
};
|
||||
if extents.intersects(&renderer.physical_extents()) {
|
||||
if extents.intersects(&renderer.pixel_extents()) {
|
||||
renderer.base.render_texture(
|
||||
&img.tex,
|
||||
extents.x1(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_apis::gl::sys::{GLint, GL_BGRA_EXT, GL_RGBA, GL_UNSIGNED_BYTE},
|
||||
gfx_apis::gl::sys::{GLenum, GLint, GL_BGRA_EXT, GL_RGBA, GL_RGBA8, GL_UNSIGNED_BYTE},
|
||||
pipewire::pw_pod::{
|
||||
SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SpaVideoFormat, SPA_VIDEO_FORMAT_BGRA,
|
||||
SPA_VIDEO_FORMAT_RGBA,
|
||||
|
|
@ -18,6 +18,7 @@ pub struct Format {
|
|||
pub name: &'static str,
|
||||
pub bpp: u32,
|
||||
pub gl_format: GLint,
|
||||
pub gl_internal_format: GLenum,
|
||||
pub gl_type: GLint,
|
||||
pub vk_format: vk::Format,
|
||||
pub drm: u32,
|
||||
|
|
@ -92,6 +93,7 @@ pub static ARGB8888: &Format = &Format {
|
|||
name: "argb8888",
|
||||
bpp: 4,
|
||||
gl_format: GL_BGRA_EXT,
|
||||
gl_internal_format: GL_RGBA8,
|
||||
gl_type: GL_UNSIGNED_BYTE,
|
||||
vk_format: vk::Format::B8G8R8A8_UNORM,
|
||||
drm: ARGB8888_DRM,
|
||||
|
|
@ -107,6 +109,7 @@ pub static XRGB8888: &Format = &Format {
|
|||
name: "xrgb8888",
|
||||
bpp: 4,
|
||||
gl_format: GL_BGRA_EXT,
|
||||
gl_internal_format: GL_RGBA8,
|
||||
gl_type: GL_UNSIGNED_BYTE,
|
||||
vk_format: vk::Format::B8G8R8A8_UNORM,
|
||||
drm: XRGB8888_DRM,
|
||||
|
|
@ -122,6 +125,7 @@ static ABGR8888: &Format = &Format {
|
|||
name: "abgr8888",
|
||||
bpp: 4,
|
||||
gl_format: GL_RGBA,
|
||||
gl_internal_format: GL_RGBA8,
|
||||
gl_type: GL_UNSIGNED_BYTE,
|
||||
vk_format: vk::Format::R8G8B8A8_UNORM,
|
||||
drm: fourcc_code('A', 'B', '2', '4'),
|
||||
|
|
@ -137,6 +141,7 @@ static XBGR8888: &Format = &Format {
|
|||
name: "xbgr8888",
|
||||
bpp: 4,
|
||||
gl_format: GL_RGBA,
|
||||
gl_internal_format: GL_RGBA8,
|
||||
gl_type: GL_UNSIGNED_BYTE,
|
||||
vk_format: vk::Format::R8G8B8A8_UNORM,
|
||||
drm: fourcc_code('X', 'B', '2', '4'),
|
||||
|
|
|
|||
236
src/gfx_api.rs
236
src/gfx_api.rs
|
|
@ -9,12 +9,12 @@ use {
|
|||
state::State,
|
||||
theme::Color,
|
||||
tree::{Node, OutputNode},
|
||||
utils::numcell::NumCell,
|
||||
utils::{numcell::NumCell, transform_ext::TransformExt},
|
||||
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
indexmap::IndexSet,
|
||||
jay_config::video::GfxApi,
|
||||
jay_config::video::{GfxApi, Transform},
|
||||
std::{
|
||||
any::Any,
|
||||
cell::Cell,
|
||||
|
|
@ -38,98 +38,109 @@ pub struct GfxRenderPass {
|
|||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||
pub struct BufferPoint {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub struct SampleRect {
|
||||
pub x1: f32,
|
||||
pub y1: f32,
|
||||
pub x2: f32,
|
||||
pub y2: f32,
|
||||
pub buffer_transform: Transform,
|
||||
}
|
||||
|
||||
impl BufferPoint {
|
||||
pub fn is_leq_1(&self) -> bool {
|
||||
self.x <= 1.0 && self.y <= 1.0
|
||||
}
|
||||
|
||||
pub fn top_left() -> Self {
|
||||
Self { x: 0.0, y: 0.0 }
|
||||
}
|
||||
|
||||
pub fn top_right() -> Self {
|
||||
Self { x: 1.0, y: 0.0 }
|
||||
}
|
||||
|
||||
pub fn bottom_left() -> Self {
|
||||
Self { x: 0.0, y: 1.0 }
|
||||
}
|
||||
|
||||
pub fn bottom_right() -> Self {
|
||||
Self { x: 1.0, y: 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||
pub struct BufferPoints {
|
||||
pub top_left: BufferPoint,
|
||||
pub top_right: BufferPoint,
|
||||
pub bottom_left: BufferPoint,
|
||||
pub bottom_right: BufferPoint,
|
||||
}
|
||||
|
||||
impl BufferPoints {
|
||||
pub fn norm(&self, width: f32, height: f32) -> Self {
|
||||
impl SampleRect {
|
||||
pub fn identity() -> Self {
|
||||
Self {
|
||||
top_left: BufferPoint {
|
||||
x: self.top_left.x / width,
|
||||
y: self.top_left.y / height,
|
||||
},
|
||||
top_right: BufferPoint {
|
||||
x: self.top_right.x / width,
|
||||
y: self.top_right.y / height,
|
||||
},
|
||||
bottom_left: BufferPoint {
|
||||
x: self.bottom_left.x / width,
|
||||
y: self.bottom_left.y / height,
|
||||
},
|
||||
bottom_right: BufferPoint {
|
||||
x: self.bottom_right.x / width,
|
||||
y: self.bottom_right.y / height,
|
||||
},
|
||||
x1: 0.0,
|
||||
y1: 0.0,
|
||||
x2: 1.0,
|
||||
y2: 1.0,
|
||||
buffer_transform: Transform::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_leq_1(&self) -> bool {
|
||||
self.top_left.is_leq_1()
|
||||
&& self.top_right.is_leq_1()
|
||||
&& self.bottom_left.is_leq_1()
|
||||
&& self.bottom_right.is_leq_1()
|
||||
pub fn is_covering(&self) -> bool {
|
||||
self.x1 == 0.0 && self.y1 == 0.0 && self.x2 == 1.0 && self.y2 == 1.0
|
||||
}
|
||||
|
||||
pub fn identity() -> Self {
|
||||
Self {
|
||||
top_left: BufferPoint::top_left(),
|
||||
top_right: BufferPoint::top_right(),
|
||||
bottom_left: BufferPoint::bottom_left(),
|
||||
bottom_right: BufferPoint::bottom_right(),
|
||||
pub fn to_points(&self) -> [[f32; 2]; 4] {
|
||||
use Transform::*;
|
||||
let x1 = self.x1;
|
||||
let x2 = self.x2;
|
||||
let y1 = self.y1;
|
||||
let y2 = self.y2;
|
||||
match self.buffer_transform {
|
||||
None => [[x2, y1], [x1, y1], [x2, y2], [x1, y2]],
|
||||
Rotate90 => [[y1, x1], [y1, x2], [y2, x1], [y2, x2]],
|
||||
Rotate180 => [[x1, y2], [x2, y2], [x1, y1], [x2, y1]],
|
||||
Rotate270 => [[y2, x2], [y2, x1], [y1, x2], [y1, x1]],
|
||||
Flip => [[x1, y1], [x2, y1], [x1, y2], [x2, y2]],
|
||||
FlipRotate90 => [[y1, x2], [y1, x1], [y2, x2], [y2, x1]],
|
||||
FlipRotate180 => [[x2, y2], [x1, y2], [x2, y1], [x1, y1]],
|
||||
FlipRotate270 => [[y2, x1], [y2, x2], [y1, x1], [y1, x2]],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct AbsoluteRect {
|
||||
pub struct FramebufferRect {
|
||||
pub x1: f32,
|
||||
pub x2: f32,
|
||||
pub y1: f32,
|
||||
pub y2: f32,
|
||||
pub output_transform: Transform,
|
||||
}
|
||||
|
||||
impl FramebufferRect {
|
||||
pub fn new(
|
||||
x1: f32,
|
||||
y1: f32,
|
||||
x2: f32,
|
||||
y2: f32,
|
||||
transform: Transform,
|
||||
width: f32,
|
||||
height: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
x1: 2.0 * x1 / width - 1.0,
|
||||
x2: 2.0 * x2 / width - 1.0,
|
||||
y1: 2.0 * y1 / height - 1.0,
|
||||
y2: 2.0 * y2 / height - 1.0,
|
||||
output_transform: transform,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_points(&self) -> [[f32; 2]; 4] {
|
||||
use Transform::*;
|
||||
let x1 = self.x1;
|
||||
let x2 = self.x2;
|
||||
let y1 = self.y1;
|
||||
let y2 = self.y2;
|
||||
match self.output_transform {
|
||||
None => [[x2, y1], [x1, y1], [x2, y2], [x1, y2]],
|
||||
Rotate90 => [[y1, -x2], [y1, -x1], [y2, -x2], [y2, -x1]],
|
||||
Rotate180 => [[-x2, -y1], [-x1, -y1], [-x2, -y2], [-x1, -y2]],
|
||||
Rotate270 => [[-y1, x2], [-y1, x1], [-y2, x2], [-y2, x1]],
|
||||
Flip => [[-x2, y1], [-x1, y1], [-x2, y2], [-x1, y2]],
|
||||
FlipRotate90 => [[y1, x2], [y1, x1], [y2, x2], [y2, x1]],
|
||||
FlipRotate180 => [[x2, -y1], [x1, -y1], [x2, -y2], [x1, -y2]],
|
||||
FlipRotate270 => [[-y1, -x2], [-y1, -x1], [-y2, -x2], [-y2, -x1]],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_covering(&self) -> bool {
|
||||
self.x1 == -1.0 && self.y1 == -1.0 && self.x2 == 1.0 && self.y2 == 1.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FillRect {
|
||||
pub rect: AbsoluteRect,
|
||||
pub rect: FramebufferRect,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
pub struct CopyTexture {
|
||||
pub tex: Rc<dyn GfxTexture>,
|
||||
pub source: BufferPoints,
|
||||
pub target: AbsoluteRect,
|
||||
pub source: SampleRect,
|
||||
pub target: FramebufferRect,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -145,17 +156,18 @@ pub trait GfxFramebuffer: Debug {
|
|||
|
||||
fn take_render_ops(&self) -> Vec<GfxApiOpt>;
|
||||
|
||||
fn size(&self) -> (i32, i32);
|
||||
fn physical_size(&self) -> (i32, i32);
|
||||
|
||||
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>);
|
||||
|
||||
fn copy_to_shm(
|
||||
&self,
|
||||
self: Rc<Self>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: &Format,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
shm: &[Cell<u8>],
|
||||
) -> Result<(), GfxError>;
|
||||
|
||||
|
|
@ -172,15 +184,32 @@ impl dyn GfxFramebuffer {
|
|||
self.render(ops, Some(&Color { r, g, b, a }));
|
||||
}
|
||||
|
||||
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
|
||||
transform.maybe_swap(self.physical_size())
|
||||
}
|
||||
|
||||
pub fn renderer_base<'a>(
|
||||
&self,
|
||||
ops: &'a mut Vec<GfxApiOpt>,
|
||||
scale: Scale,
|
||||
transform: Transform,
|
||||
) -> RendererBase<'a> {
|
||||
let (width, height) = self.logical_size(transform);
|
||||
RendererBase {
|
||||
ops,
|
||||
scaled: scale != 1,
|
||||
scale,
|
||||
scalef: scale.to_f64(),
|
||||
transform,
|
||||
fb_width: width as _,
|
||||
fb_height: height as _,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_texture(&self, texture: &Rc<dyn GfxTexture>, x: i32, y: i32) {
|
||||
let mut ops = self.take_render_ops();
|
||||
let scale = Scale::from_int(1);
|
||||
let mut renderer = RendererBase {
|
||||
ops: &mut ops,
|
||||
scaled: false,
|
||||
scale,
|
||||
scalef: 1.0,
|
||||
};
|
||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||
renderer.render_texture(texture, x, y, None, None, scale, None);
|
||||
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
|
||||
self.render(ops, clear);
|
||||
|
|
@ -193,12 +222,7 @@ impl dyn GfxFramebuffer {
|
|||
f: &mut dyn FnMut(&mut RendererBase),
|
||||
) {
|
||||
let mut ops = self.take_render_ops();
|
||||
let mut renderer = RendererBase {
|
||||
ops: &mut ops,
|
||||
scaled: scale != 1,
|
||||
scale,
|
||||
scalef: scale.to_f64(),
|
||||
};
|
||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||
f(&mut renderer);
|
||||
self.render(ops, clear);
|
||||
}
|
||||
|
|
@ -212,20 +236,18 @@ impl dyn GfxFramebuffer {
|
|||
scale: Scale,
|
||||
render_hardware_cursor: bool,
|
||||
black_background: bool,
|
||||
transform: Transform,
|
||||
) -> GfxRenderPass {
|
||||
let mut ops = self.take_render_ops();
|
||||
let (width, height) = self.size();
|
||||
let mut renderer = Renderer {
|
||||
base: RendererBase {
|
||||
ops: &mut ops,
|
||||
scaled: scale != 1,
|
||||
scale,
|
||||
scalef: scale.to_f64(),
|
||||
},
|
||||
base: self.renderer_base(&mut ops, scale, transform),
|
||||
state,
|
||||
result,
|
||||
logical_extents: node.node_absolute_position().at_point(0, 0),
|
||||
physical_extents: Rect::new(0, 0, width, height).unwrap(),
|
||||
pixel_extents: {
|
||||
let (width, height) = self.logical_size(transform);
|
||||
Rect::new(0, 0, width, height).unwrap()
|
||||
},
|
||||
};
|
||||
node.node_render(&mut renderer, 0, 0, None);
|
||||
if let Some(rect) = cursor_rect {
|
||||
|
|
@ -283,6 +305,7 @@ impl dyn GfxFramebuffer {
|
|||
scale,
|
||||
render_hardware_cursor,
|
||||
node.has_fullscreen(),
|
||||
node.global.transform.get(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -295,6 +318,7 @@ impl dyn GfxFramebuffer {
|
|||
scale: Scale,
|
||||
render_hardware_cursor: bool,
|
||||
black_background: bool,
|
||||
transform: Transform,
|
||||
) {
|
||||
let pass = self.create_render_pass(
|
||||
node,
|
||||
|
|
@ -304,24 +328,28 @@ impl dyn GfxFramebuffer {
|
|||
scale,
|
||||
render_hardware_cursor,
|
||||
black_background,
|
||||
transform,
|
||||
);
|
||||
self.perform_render_pass(pass);
|
||||
}
|
||||
|
||||
pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) {
|
||||
pub fn render_hardware_cursor(
|
||||
&self,
|
||||
cursor: &dyn Cursor,
|
||||
state: &State,
|
||||
scale: Scale,
|
||||
transform: Transform,
|
||||
) {
|
||||
let mut ops = self.take_render_ops();
|
||||
let (width, height) = self.size();
|
||||
let mut renderer = Renderer {
|
||||
base: RendererBase {
|
||||
ops: &mut ops,
|
||||
scaled: scale != 1,
|
||||
scale,
|
||||
scalef: scale.to_f64(),
|
||||
},
|
||||
base: self.renderer_base(&mut ops, scale, transform),
|
||||
state,
|
||||
result: None,
|
||||
logical_extents: Rect::new_empty(0, 0),
|
||||
physical_extents: Rect::new(0, 0, width, height).unwrap(),
|
||||
pixel_extents: {
|
||||
let (width, height) = self.logical_size(transform);
|
||||
Rect::new(0, 0, width, height).unwrap()
|
||||
},
|
||||
};
|
||||
cursor.render_hardware_cursor(&mut renderer);
|
||||
self.render(ops, Some(&Color::TRANSPARENT));
|
||||
|
|
@ -414,6 +442,14 @@ pub trait GfxContext: Debug {
|
|||
fn gbm(&self) -> &GbmDevice;
|
||||
|
||||
fn gfx_api(&self) -> GfxApi;
|
||||
|
||||
fn create_fb(
|
||||
self: Rc<Self>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ macro_rules! dynload {
|
|||
use {
|
||||
crate::{
|
||||
gfx_api::{
|
||||
BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||
CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||
SampleRect,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
gl::texture::image_target,
|
||||
|
|
@ -178,13 +179,13 @@ enum RenderError {
|
|||
ExternalUnsupported,
|
||||
#[error("OpenGL context does not support any formats")]
|
||||
NoSupportedFormats,
|
||||
#[error("Unsupported operation")]
|
||||
UnsupportedOperation,
|
||||
#[error("Cannot convert a shm texture into a framebuffer")]
|
||||
ShmTextureToFb,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct GfxGlState {
|
||||
triangles: RefCell<Vec<f32>>,
|
||||
triangles: RefCell<Vec<[f32; 2]>>,
|
||||
fill_rect: VecStorage<&'static FillRect>,
|
||||
copy_tex: VecStorage<&'static CopyTexture>,
|
||||
}
|
||||
|
|
@ -198,8 +199,6 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
|||
let copy_tex = &mut *copy_tex;
|
||||
let mut triangles = state.triangles.borrow_mut();
|
||||
let triangles = &mut *triangles;
|
||||
let width = fb.gl.width as f32;
|
||||
let height = fb.gl.height as f32;
|
||||
let mut i = 0;
|
||||
while i < ops.len() {
|
||||
macro_rules! has_ops {
|
||||
|
|
@ -240,19 +239,14 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
|||
Some(c) if c == fr.color => {}
|
||||
_ => break,
|
||||
}
|
||||
let x1 = 2.0 * (fr.rect.x1 / width) - 1.0;
|
||||
let x2 = 2.0 * (fr.rect.x2 / width) - 1.0;
|
||||
let y1 = 2.0 * (fr.rect.y1 / height) - 1.0;
|
||||
let y2 = 2.0 * (fr.rect.y2 / height) - 1.0;
|
||||
let [top_right, top_left, bottom_right, bottom_left] = fr.rect.to_points();
|
||||
triangles.extend_from_slice(&[
|
||||
// triangle 1
|
||||
x2, y1, // top right
|
||||
x1, y1, // top left
|
||||
x1, y2, // bottom left
|
||||
// triangle 2
|
||||
x2, y1, // top right
|
||||
x1, y2, // bottom left
|
||||
x2, y2, // bottom right
|
||||
top_right,
|
||||
top_left,
|
||||
bottom_left,
|
||||
top_right,
|
||||
bottom_left,
|
||||
bottom_right,
|
||||
]);
|
||||
i += 1;
|
||||
}
|
||||
|
|
@ -262,16 +256,12 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
|||
}
|
||||
}
|
||||
for tex in &*copy_tex {
|
||||
let x1 = 2.0 * (tex.target.x1 / width) - 1.0;
|
||||
let y1 = 2.0 * (tex.target.y1 / height) - 1.0;
|
||||
let x2 = 2.0 * (tex.target.x2 / width) - 1.0;
|
||||
let y2 = 2.0 * (tex.target.y2 / height) - 1.0;
|
||||
render_texture(&fb.ctx, &tex.tex.as_gl(), x1, y1, x2, y2, &tex.source)
|
||||
render_texture(&fb.ctx, &tex.tex.as_gl(), &tex.target, &tex.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
||||
fn fill_boxes3(ctx: &GlRenderContext, boxes: &[[f32; 2]], color: &Color) {
|
||||
let gles = ctx.ctx.dpy.gles;
|
||||
unsafe {
|
||||
(gles.glUseProgram)(ctx.fill_prog.prog);
|
||||
|
|
@ -285,7 +275,7 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
|||
boxes.as_ptr() as _,
|
||||
);
|
||||
(gles.glEnableVertexAttribArray)(ctx.fill_prog_pos as _);
|
||||
(gles.glDrawArrays)(GL_TRIANGLES, 0, (boxes.len() / 2) as _);
|
||||
(gles.glDrawArrays)(GL_TRIANGLES, 0, boxes.len() as _);
|
||||
(gles.glDisableVertexAttribArray)(ctx.fill_prog_pos as _);
|
||||
}
|
||||
}
|
||||
|
|
@ -293,11 +283,8 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
|||
fn render_texture(
|
||||
ctx: &GlRenderContext,
|
||||
texture: &Texture,
|
||||
x1: f32,
|
||||
y1: f32,
|
||||
x2: f32,
|
||||
y2: f32,
|
||||
src: &BufferPoints,
|
||||
target_rect: &FramebufferRect,
|
||||
src: &SampleRect,
|
||||
) {
|
||||
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
|
||||
let gles = ctx.ctx.dpy.gles;
|
||||
|
|
@ -334,23 +321,8 @@ fn render_texture(
|
|||
|
||||
(gles.glUniform1i)(prog.tex, 0);
|
||||
|
||||
let texcoord = [
|
||||
src.top_right.x,
|
||||
src.top_right.y,
|
||||
src.top_left.x,
|
||||
src.top_left.y,
|
||||
src.bottom_right.x,
|
||||
src.bottom_right.y,
|
||||
src.bottom_left.x,
|
||||
src.bottom_left.y,
|
||||
];
|
||||
|
||||
let pos = [
|
||||
x2, y1, // top right
|
||||
x1, y1, // top left
|
||||
x2, y2, // bottom right
|
||||
x1, y2, // bottom left
|
||||
];
|
||||
let texcoord = src.to_points();
|
||||
let pos = target_rect.to_points();
|
||||
|
||||
(gles.glVertexAttribPointer)(
|
||||
prog.texcoord as _,
|
||||
|
|
|
|||
|
|
@ -1,25 +1,53 @@
|
|||
use {
|
||||
crate::gfx_apis::gl::{
|
||||
egl::{context::EglContext, image::EglImage},
|
||||
gl::{
|
||||
frame_buffer::GlFrameBuffer,
|
||||
sys::{
|
||||
GLeglImageOES, GLuint, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER,
|
||||
GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER,
|
||||
crate::{
|
||||
format::Format,
|
||||
gfx_apis::gl::{
|
||||
egl::{context::EglContext, image::EglImage},
|
||||
gl::{
|
||||
frame_buffer::GlFrameBuffer,
|
||||
sys::{
|
||||
GLeglImageOES, GLuint, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER,
|
||||
GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER,
|
||||
},
|
||||
},
|
||||
RenderError,
|
||||
},
|
||||
RenderError,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct GlRenderBuffer {
|
||||
pub img: Rc<EglImage>,
|
||||
pub _img: Option<Rc<EglImage>>,
|
||||
pub ctx: Rc<EglContext>,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub format: &'static Format,
|
||||
rbo: GLuint,
|
||||
}
|
||||
|
||||
impl GlRenderBuffer {
|
||||
pub(in crate::gfx_apis::gl) unsafe fn new(
|
||||
ctx: &Rc<EglContext>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<GlRenderBuffer>, RenderError> {
|
||||
let gles = &ctx.dpy.gles;
|
||||
let mut rbo = 0;
|
||||
(gles.glGenRenderbuffers)(1, &mut rbo);
|
||||
(gles.glBindRenderbuffer)(GL_RENDERBUFFER, rbo);
|
||||
(gles.glRenderbufferStorage)(GL_RENDERBUFFER, format.gl_internal_format, width, height);
|
||||
(gles.glBindRenderbuffer)(GL_RENDERBUFFER, 0);
|
||||
Ok(Rc::new(GlRenderBuffer {
|
||||
_img: None,
|
||||
ctx: ctx.clone(),
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
rbo,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(in crate::gfx_apis::gl) unsafe fn from_image(
|
||||
img: &Rc<EglImage>,
|
||||
ctx: &Rc<EglContext>,
|
||||
|
|
@ -36,8 +64,11 @@ impl GlRenderBuffer {
|
|||
.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, GLeglImageOES(img.img.0));
|
||||
(gles.glBindRenderbuffer)(GL_RENDERBUFFER, 0);
|
||||
Ok(Rc::new(GlRenderBuffer {
|
||||
img: img.clone(),
|
||||
_img: Some(img.clone()),
|
||||
ctx: ctx.clone(),
|
||||
width: img.dmabuf.width,
|
||||
height: img.dmabuf.height,
|
||||
format: img.dmabuf.format,
|
||||
rbo,
|
||||
}))
|
||||
}
|
||||
|
|
@ -62,8 +93,8 @@ impl GlRenderBuffer {
|
|||
_tex: None,
|
||||
ctx: self.ctx.clone(),
|
||||
fbo,
|
||||
width: self.img.dmabuf.width,
|
||||
height: self.img.dmabuf.height,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
};
|
||||
if status != GL_FRAMEBUFFER_COMPLETE {
|
||||
return Err(RenderError::CreateFramebuffer);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ pub type GLuint = c::c_uint;
|
|||
egl_transparent!(GLeglImageOES);
|
||||
|
||||
pub const GL_RGBA: GLint = 0x1908;
|
||||
pub const GL_RGBA8: GLenum = 0x8058;
|
||||
pub const GL_BGRA_EXT: GLint = 0x80E1;
|
||||
pub const GL_CLAMP_TO_EDGE: GLint = 0x812F;
|
||||
pub const GL_COLOR_ATTACHMENT0: GLenum = 0x8CE0;
|
||||
|
|
@ -49,6 +50,7 @@ dynload! {
|
|||
GLESV2: GlesV2 from "libGLESv2.so" {
|
||||
glGetString: unsafe fn(name: GLenum) -> *const u8,
|
||||
glGenRenderbuffers: unsafe fn(n: GLsizei, renderbuffers: *mut GLuint),
|
||||
glRenderbufferStorage: unsafe fn(target: GLenum, format: GLenum, width: GLsizei, height: GLsizei),
|
||||
glDeleteRenderbuffers: unsafe fn(n: GLsizei, renderbuffers: *const GLuint),
|
||||
glBindRenderbuffer: unsafe fn(target: GLenum, renderbuffer: GLuint),
|
||||
glGenFramebuffers: unsafe fn(n: GLsizei, framebuffers: *mut GLuint),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use {
|
|||
ResetStatus,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
egl::{context::EglContext, display::EglDisplay},
|
||||
egl::{context::EglContext, display::EglDisplay, image::EglImage},
|
||||
ext::GL_OES_EGL_IMAGE_EXTERNAL,
|
||||
gl::{
|
||||
program::GlProgram, render_buffer::GlRenderBuffer, sys::GLint, texture::GlTexture,
|
||||
|
|
@ -190,6 +190,20 @@ impl GlRenderContext {
|
|||
format,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn image_to_fb(
|
||||
self: &Rc<Self>,
|
||||
img: &Rc<EglImage>,
|
||||
) -> Result<Rc<Framebuffer>, RenderError> {
|
||||
self.ctx.with_current(|| unsafe {
|
||||
let rb = GlRenderBuffer::from_image(img, &self.ctx)?;
|
||||
let fb = rb.create_framebuffer()?;
|
||||
Ok(Rc::new(Framebuffer {
|
||||
ctx: self.clone(),
|
||||
gl: fb,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GfxContext for GlRenderContext {
|
||||
|
|
@ -241,4 +255,17 @@ impl GfxContext for GlRenderContext {
|
|||
fn gfx_api(&self) -> GfxApi {
|
||||
GfxApi::OpenGl
|
||||
}
|
||||
|
||||
fn create_fb(
|
||||
self: Rc<Self>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
_stride: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
|
||||
let fb = self.ctx.with_current(|| unsafe {
|
||||
GlRenderBuffer::new(&self.ctx, width, height, format)?.create_framebuffer()
|
||||
})?;
|
||||
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ impl GfxFramebuffer for Framebuffer {
|
|||
ops
|
||||
}
|
||||
|
||||
fn size(&self) -> (i32, i32) {
|
||||
fn physical_size(&self) -> (i32, i32) {
|
||||
(self.gl.width, self.gl.height)
|
||||
}
|
||||
|
||||
|
|
@ -106,19 +106,20 @@ impl GfxFramebuffer for Framebuffer {
|
|||
}
|
||||
|
||||
fn copy_to_shm(
|
||||
&self,
|
||||
self: Rc<Self>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: &Format,
|
||||
_stride: i32,
|
||||
format: &'static Format,
|
||||
shm: &[Cell<u8>],
|
||||
) -> Result<(), GfxError> {
|
||||
self.copy_to_shm(x, y, width, height, format, shm);
|
||||
(*self).copy_to_shm(x, y, width, height, format, shm);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format(&self) -> &'static Format {
|
||||
self.gl.rb.img.dmabuf.format
|
||||
self.gl.rb.format
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ use {
|
|||
crate::{
|
||||
gfx_api::{GfxError, GfxFramebuffer, GfxImage, GfxTexture},
|
||||
gfx_apis::gl::{
|
||||
egl::image::EglImage,
|
||||
gl::{render_buffer::GlRenderBuffer, texture::GlTexture},
|
||||
Framebuffer, GlRenderContext, RenderError, Texture,
|
||||
egl::image::EglImage, gl::texture::GlTexture, Framebuffer, GlRenderContext,
|
||||
RenderError, Texture,
|
||||
},
|
||||
},
|
||||
std::rc::Rc,
|
||||
|
|
@ -34,14 +33,7 @@ impl Image {
|
|||
}
|
||||
|
||||
fn to_framebuffer(&self) -> Result<Rc<Framebuffer>, RenderError> {
|
||||
self.ctx.ctx.with_current(|| unsafe {
|
||||
let rb = GlRenderBuffer::from_image(&self.gl, &self.ctx.ctx)?;
|
||||
let fb = rb.create_framebuffer()?;
|
||||
Ok(Rc::new(Framebuffer {
|
||||
ctx: self.ctx.clone(),
|
||||
gl: fb,
|
||||
}))
|
||||
})
|
||||
self.ctx.image_to_fb(&self.gl)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ use {
|
|||
crate::{
|
||||
format::Format,
|
||||
gfx_api::{GfxError, GfxTexture, TextureReservations},
|
||||
gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext, RenderError},
|
||||
gfx_apis::gl::{
|
||||
gl::texture::GlTexture,
|
||||
renderer::{context::GlRenderContext, framebuffer::Framebuffer},
|
||||
RenderError,
|
||||
},
|
||||
video::dmabuf::DmaBuf,
|
||||
},
|
||||
std::{
|
||||
|
|
@ -34,6 +38,13 @@ impl Texture {
|
|||
pub fn height(&self) -> i32 {
|
||||
self.gl.height
|
||||
}
|
||||
|
||||
pub fn to_framebuffer(&self) -> Result<Rc<Framebuffer>, RenderError> {
|
||||
match &self.gl.img {
|
||||
Some(img) => self.ctx.image_to_fb(img),
|
||||
_ => Err(RenderError::ShmTextureToFb),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GfxTexture for Texture {
|
||||
|
|
@ -51,15 +62,17 @@ impl GfxTexture for Texture {
|
|||
|
||||
fn read_pixels(
|
||||
self: Rc<Self>,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
_stride: i32,
|
||||
_format: &Format,
|
||||
_shm: &[Cell<u8>],
|
||||
format: &Format,
|
||||
shm: &[Cell<u8>],
|
||||
) -> Result<(), GfxError> {
|
||||
Err(RenderError::UnsupportedOperation.into())
|
||||
self.to_framebuffer()?
|
||||
.copy_to_shm(x, y, width, height, format, shm);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dmabuf(&self) -> Option<&DmaBuf> {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ use {
|
|||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
format::Format,
|
||||
gfx_api::{GfxContext, GfxError, GfxFormat, GfxImage, GfxTexture, ResetStatus},
|
||||
gfx_api::{
|
||||
GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, GfxTexture, ResetStatus,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
|
||||
},
|
||||
|
|
@ -173,8 +175,6 @@ pub enum VulkanError {
|
|||
height: i32,
|
||||
stride: i32,
|
||||
},
|
||||
#[error("Unsupported operation")]
|
||||
UnsupportedOperation,
|
||||
}
|
||||
|
||||
impl From<VulkanError> for GfxError {
|
||||
|
|
@ -257,6 +257,19 @@ impl GfxContext for Context {
|
|||
fn gfx_api(&self) -> GfxApi {
|
||||
GfxApi::Vulkan
|
||||
}
|
||||
|
||||
fn create_fb(
|
||||
self: Rc<Self>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
|
||||
let fb = self
|
||||
.0
|
||||
.create_shm_texture(format, width, height, stride, &[], true)?;
|
||||
Ok(fb)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
|
|
|
|||
|
|
@ -524,7 +524,7 @@ impl GfxFramebuffer for VulkanImage {
|
|||
self.render_ops.take()
|
||||
}
|
||||
|
||||
fn size(&self) -> (i32, i32) {
|
||||
fn physical_size(&self) -> (i32, i32) {
|
||||
(self.width as _, self.height as _)
|
||||
}
|
||||
|
||||
|
|
@ -533,15 +533,18 @@ impl GfxFramebuffer for VulkanImage {
|
|||
}
|
||||
|
||||
fn copy_to_shm(
|
||||
&self,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
_format: &Format,
|
||||
_shm: &[Cell<u8>],
|
||||
self: Rc<Self>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
shm: &[Cell<u8>],
|
||||
) -> Result<(), GfxError> {
|
||||
return Err(VulkanError::UnsupportedOperation.into());
|
||||
self.renderer
|
||||
.read_pixels(&self, x, y, width, height, stride, format, shm)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn format(&self) -> &'static Format {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ impl VulkanDevice {
|
|||
let vertex_input_state = PipelineVertexInputStateCreateInfo::builder();
|
||||
let rasterization_state = PipelineRasterizationStateCreateInfo::builder()
|
||||
.polygon_mode(PolygonMode::FILL)
|
||||
.cull_mode(CullModeFlags::BACK)
|
||||
.cull_mode(CullModeFlags::NONE)
|
||||
.line_width(1.0)
|
||||
.front_face(FrontFace::COUNTER_CLOCKWISE);
|
||||
let multisampling_state = PipelineMultisampleStateCreateInfo::builder()
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ use {
|
|||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
AbsoluteRect, BufferPoint, BufferPoints, GfxApiOpt, GfxFormat, GfxFramebuffer,
|
||||
GfxTexture,
|
||||
},
|
||||
gfx_api::{GfxApiOpt, GfxFormat, GfxFramebuffer, GfxTexture},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocator,
|
||||
command::{VulkanCommandBuffer, VulkanCommandPool},
|
||||
|
|
@ -386,12 +383,7 @@ impl VulkanRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
fn record_draws(
|
||||
&self,
|
||||
buf: CommandBuffer,
|
||||
fb: &VulkanImage,
|
||||
opts: &[GfxApiOpt],
|
||||
) -> Result<(), VulkanError> {
|
||||
fn record_draws(&self, buf: CommandBuffer, opts: &[GfxApiOpt]) -> Result<(), VulkanError> {
|
||||
let dev = &self.device.device;
|
||||
let mut current_pipeline = None;
|
||||
let mut bind = |pipeline: &VulkanPipeline| {
|
||||
|
|
@ -402,15 +394,13 @@ impl VulkanRenderer {
|
|||
}
|
||||
}
|
||||
};
|
||||
let width = fb.width as f32;
|
||||
let height = fb.height as f32;
|
||||
for opt in opts {
|
||||
match opt {
|
||||
GfxApiOpt::Sync => {}
|
||||
GfxApiOpt::FillRect(r) => {
|
||||
bind(&self.fill_pipeline);
|
||||
let vert = FillVertPushConstants {
|
||||
pos: r.rect.to_vk(width, height),
|
||||
pos: r.rect.to_points(),
|
||||
};
|
||||
let frag = FillFragPushConstants {
|
||||
color: r.color.to_array_srgb(),
|
||||
|
|
@ -437,8 +427,8 @@ impl VulkanRenderer {
|
|||
let tex = c.tex.as_vk(&self.device.device);
|
||||
bind(&self.tex_pipeline);
|
||||
let vert = TexVertPushConstants {
|
||||
pos: c.target.to_vk(width, height),
|
||||
tex_pos: c.source.to_vk(),
|
||||
pos: c.target.to_points(),
|
||||
tex_pos: c.source.to_points(),
|
||||
};
|
||||
let image_info = DescriptorImageInfo::builder()
|
||||
.image_view(tex.texture_view)
|
||||
|
|
@ -879,7 +869,7 @@ impl VulkanRenderer {
|
|||
self.secondary_barriers(buf.buffer);
|
||||
self.begin_rendering(buf.buffer, fb, clear);
|
||||
self.set_viewport(buf.buffer, fb);
|
||||
self.record_draws(buf.buffer, fb, opts)?;
|
||||
self.record_draws(buf.buffer, opts)?;
|
||||
self.end_rendering(buf.buffer);
|
||||
self.final_barriers(buf.buffer, fb);
|
||||
self.end_command_buffer(buf.buffer)?;
|
||||
|
|
@ -952,33 +942,6 @@ impl dyn GfxTexture {
|
|||
}
|
||||
}
|
||||
|
||||
impl AbsoluteRect {
|
||||
fn to_vk(&self, width: f32, height: f32) -> [[f32; 2]; 4] {
|
||||
let x1 = 2.0 * self.x1 / width - 1.0;
|
||||
let x2 = 2.0 * self.x2 / width - 1.0;
|
||||
let y1 = 2.0 * self.y1 / height - 1.0;
|
||||
let y2 = 2.0 * self.y2 / height - 1.0;
|
||||
[[x2, y1], [x1, y1], [x2, y2], [x1, y2]]
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferPoint {
|
||||
fn to_vk(&self) -> [f32; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferPoints {
|
||||
fn to_vk(&self) -> [[f32; 2]; 4] {
|
||||
[
|
||||
self.top_right.to_vk(),
|
||||
self.top_left.to_vk(),
|
||||
self.bottom_right.to_vk(),
|
||||
self.bottom_left.to_vk(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn image_barrier() -> ImageMemoryBarrier2Builder<'static> {
|
||||
ImageMemoryBarrier2::builder().subresource_range(
|
||||
ImageSubresourceRange::builder()
|
||||
|
|
|
|||
|
|
@ -176,12 +176,12 @@ impl JayScreencast {
|
|||
self.client.state.perform_screencopy(
|
||||
texture,
|
||||
&buffer.fb,
|
||||
on.global.preferred_scale.get(),
|
||||
on.global.pos.get(),
|
||||
render_hardware_cursors,
|
||||
x_off,
|
||||
y_off,
|
||||
size,
|
||||
on.global.transform.get(),
|
||||
);
|
||||
self.client.event(Ready {
|
||||
self_id: self.id,
|
||||
|
|
@ -218,7 +218,7 @@ impl JayScreencast {
|
|||
_ => return Err(JayScreencastError::XRGB8888),
|
||||
};
|
||||
if let Some(output) = self.output.get() {
|
||||
let mode = output.global.mode.get();
|
||||
let (width, height) = output.global.pixel_size();
|
||||
let num = 3;
|
||||
for _ in 0..num {
|
||||
let mut usage = GBM_BO_USE_RENDERING;
|
||||
|
|
@ -242,8 +242,8 @@ impl JayScreencast {
|
|||
};
|
||||
let buffer = ctx.gbm().create_bo(
|
||||
&self.client.state.dma_buf_ids,
|
||||
mode.width,
|
||||
mode.height,
|
||||
width,
|
||||
height,
|
||||
XRGB8888,
|
||||
modifiers,
|
||||
usage,
|
||||
|
|
@ -493,10 +493,7 @@ efrom!(JayScreencastError, ClientError);
|
|||
|
||||
fn output_size(output: &Option<Rc<OutputNode>>) -> (i32, i32) {
|
||||
match output {
|
||||
Some(o) => {
|
||||
let mode = o.global.mode.get();
|
||||
(mode.width, mode.height)
|
||||
}
|
||||
Some(o) => o.global.pixel_size(),
|
||||
_ => (0, 0),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ use {
|
|||
crate::{
|
||||
backend,
|
||||
client::{Client, ClientError, ClientId},
|
||||
format::XRGB8888,
|
||||
gfx_api::{GfxFramebuffer, GfxTexture},
|
||||
gfx_api::GfxTexture,
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
wl_buffer::WlBufferStorage, wl_surface::WlSurface,
|
||||
|
|
@ -19,12 +18,13 @@ use {
|
|||
buffd::{MsgParser, MsgParserError},
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
linkedlist::LinkedList,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::Transform,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::hash_map::Entry,
|
||||
|
|
@ -75,9 +75,10 @@ pub struct WlOutputGlobal {
|
|||
pub destroyed: Cell<bool>,
|
||||
pub legacy_scale: Cell<u32>,
|
||||
pub preferred_scale: Cell<crate::scale::Scale>,
|
||||
pub transform: Cell<Transform>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
pub struct OutputId {
|
||||
pub manufacturer: String,
|
||||
pub model: String,
|
||||
|
|
@ -102,16 +103,24 @@ impl WlOutputGlobal {
|
|||
width_mm: i32,
|
||||
height_mm: i32,
|
||||
) -> Self {
|
||||
let output_id = Rc::new(OutputId {
|
||||
manufacturer: manufacturer.to_string(),
|
||||
model: product.to_string(),
|
||||
serial_number: serial_number.to_string(),
|
||||
});
|
||||
let transform = state
|
||||
.output_transforms
|
||||
.borrow()
|
||||
.get(&output_id)
|
||||
.copied()
|
||||
.unwrap_or(Transform::None);
|
||||
let (width, height) = transform.maybe_swap((mode.width, mode.height));
|
||||
Self {
|
||||
name,
|
||||
state: state.clone(),
|
||||
connector: connector.clone(),
|
||||
pos: Cell::new(Rect::new_sized(x1, 0, mode.width, mode.height).unwrap()),
|
||||
output_id: Rc::new(OutputId {
|
||||
manufacturer: manufacturer.to_string(),
|
||||
model: product.to_string(),
|
||||
serial_number: serial_number.to_string(),
|
||||
}),
|
||||
pos: Cell::new(Rect::new_sized(x1, 0, width, height).unwrap()),
|
||||
output_id,
|
||||
mode: Cell::new(*mode),
|
||||
node: Default::default(),
|
||||
width_mm,
|
||||
|
|
@ -122,6 +131,7 @@ impl WlOutputGlobal {
|
|||
destroyed: Cell::new(false),
|
||||
legacy_scale: Cell::new(1),
|
||||
preferred_scale: Cell::new(crate::scale::Scale::from_int(1)),
|
||||
transform: Cell::new(transform),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,13 +212,8 @@ impl WlOutputGlobal {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn have_shm_screencopies(&self) -> bool {
|
||||
self.pending_captures.iter().any(|c| c.is_shm.get())
|
||||
}
|
||||
|
||||
pub fn perform_screencopies(
|
||||
&self,
|
||||
fb: Option<&dyn GfxFramebuffer>,
|
||||
tex: &Rc<dyn GfxTexture>,
|
||||
render_hardware_cursors: bool,
|
||||
x_off: i32,
|
||||
|
|
@ -234,55 +239,21 @@ impl WlOutputGlobal {
|
|||
capture.send_failed();
|
||||
continue;
|
||||
}
|
||||
let rect = capture.rect;
|
||||
if let Some(WlBufferStorage::Shm { mem, stride }) =
|
||||
wl_buffer.storage.borrow_mut().deref()
|
||||
{
|
||||
let acc = mem.access(|mem| {
|
||||
tex.clone().read_pixels(
|
||||
capture.rect.x1(),
|
||||
capture.rect.y1(),
|
||||
capture.rect.width(),
|
||||
capture.rect.height(),
|
||||
*stride,
|
||||
wl_buffer.format,
|
||||
mem,
|
||||
)
|
||||
});
|
||||
let mut res = match acc {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
capture.client.error(e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if res.is_err() {
|
||||
if let Some(fb) = fb {
|
||||
let acc = mem.access(|mem| {
|
||||
fb.copy_to_shm(
|
||||
rect.x1(),
|
||||
rect.y1(),
|
||||
rect.width(),
|
||||
rect.height(),
|
||||
XRGB8888,
|
||||
mem,
|
||||
)
|
||||
});
|
||||
res = match acc {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
capture.client.error(e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Err(e) = res {
|
||||
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
|
||||
capture.send_failed();
|
||||
continue;
|
||||
}
|
||||
// capture.send_flags(FLAGS_Y_INVERT);
|
||||
self.state.perform_shm_screencopy(
|
||||
tex,
|
||||
self.pos.get(),
|
||||
x_off,
|
||||
y_off,
|
||||
size,
|
||||
&capture,
|
||||
mem,
|
||||
*stride,
|
||||
wl_buffer.format,
|
||||
Transform::None,
|
||||
);
|
||||
} else {
|
||||
let fb = match wl_buffer.famebuffer.get() {
|
||||
Some(fb) => fb,
|
||||
|
|
@ -295,12 +266,12 @@ impl WlOutputGlobal {
|
|||
self.state.perform_screencopy(
|
||||
tex,
|
||||
&fb,
|
||||
self.preferred_scale.get(),
|
||||
self.pos.get(),
|
||||
render_hardware_cursors,
|
||||
x_off - capture.rect.x1(),
|
||||
y_off - capture.rect.y1(),
|
||||
size,
|
||||
Transform::None,
|
||||
);
|
||||
}
|
||||
if capture.with_damage.get() {
|
||||
|
|
@ -312,6 +283,11 @@ impl WlOutputGlobal {
|
|||
capture.output_link.take();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pixel_size(&self) -> (i32, i32) {
|
||||
let mode = self.mode.get();
|
||||
self.transform.get().maybe_swap((mode.width, mode.height))
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(WlOutputGlobal, WlOutput, WlOutputError);
|
||||
|
|
@ -357,7 +333,7 @@ impl WlOutput {
|
|||
subpixel: SP_UNKNOWN,
|
||||
make: &self.global.output_id.manufacturer,
|
||||
model: &self.global.output_id.model,
|
||||
transform: TF_NORMAL,
|
||||
transform: self.global.transform.get().to_wl(),
|
||||
};
|
||||
self.client.event(event);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ use {
|
|||
linkedlist::LinkedNode,
|
||||
numcell::NumCell,
|
||||
rc_eq::rc_eq,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
wire::{
|
||||
wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId,
|
||||
|
|
@ -264,11 +265,13 @@ impl WlSeatGlobal {
|
|||
let (x, y) = self.get_position();
|
||||
for output in self.state.root.outputs.lock().values() {
|
||||
if let Some(hc) = output.hardware_cursor.get() {
|
||||
let transform = output.global.transform.get();
|
||||
let render = render | output.hardware_cursor_needs_render.take();
|
||||
let scale = output.global.preferred_scale.get();
|
||||
let extents = cursor.extents_at_scale(scale);
|
||||
let (hc_width, hc_height) = hc.size();
|
||||
if render {
|
||||
let (max_width, max_height) = hc.max_size();
|
||||
let (max_width, max_height) = transform.maybe_swap((hc_width, hc_height));
|
||||
if extents.width() > max_width || extents.height() > max_height {
|
||||
hc.set_enabled(false);
|
||||
hc.commit();
|
||||
|
|
@ -285,17 +288,25 @@ impl WlSeatGlobal {
|
|||
x_rel = ((x - Fixed::from_int(opos.x1())).to_f64() * scalef).round() as i32;
|
||||
y_rel = ((y - Fixed::from_int(opos.y1())).to_f64() * scalef).round() as i32;
|
||||
}
|
||||
let mode = output.global.mode.get();
|
||||
if extents
|
||||
.intersects(&Rect::new_sized(-x_rel, -y_rel, mode.width, mode.height).unwrap())
|
||||
{
|
||||
let (width, height) = output.global.pixel_size();
|
||||
if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) {
|
||||
if render {
|
||||
let buffer = hc.get_buffer();
|
||||
buffer.render_hardware_cursor(cursor.deref(), &self.state, scale);
|
||||
buffer.render_hardware_cursor(
|
||||
cursor.deref(),
|
||||
&self.state,
|
||||
scale,
|
||||
transform,
|
||||
);
|
||||
hc.swap_buffer();
|
||||
}
|
||||
hc.set_enabled(true);
|
||||
hc.set_position(x_rel + extents.x1(), y_rel + extents.y1());
|
||||
let mode = output.global.mode.get();
|
||||
let (x_rel, y_rel) =
|
||||
transform.apply_point(mode.width, mode.height, (x_rel, y_rel));
|
||||
let (hot_x, hot_y) =
|
||||
transform.apply_point(hc_width, hc_height, (-extents.x1(), -extents.y1()));
|
||||
hc.set_position(x_rel - hot_x, y_rel - hot_y);
|
||||
} else {
|
||||
if render {
|
||||
output.hardware_cursor_needs_render.set(true);
|
||||
|
|
|
|||
|
|
@ -16,14 +16,10 @@ use {
|
|||
client::{Client, ClientError, RequestParser},
|
||||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
gfx_api::{BufferPoint, BufferPoints},
|
||||
gfx_api::SampleRect,
|
||||
ifs::{
|
||||
wl_buffer::WlBuffer,
|
||||
wl_callback::WlCallback,
|
||||
wl_output::{
|
||||
TF_180, TF_270, TF_90, TF_FLIPPED, TF_FLIPPED_180, TF_FLIPPED_270, TF_FLIPPED_90,
|
||||
TF_NORMAL,
|
||||
},
|
||||
wl_seat::{
|
||||
wl_pointer::PendingScroll, zwp_pointer_constraints_v1::SeatConstraint, Dnd,
|
||||
NodeSeatState, SeatId, WlSeatGlobal,
|
||||
|
|
@ -54,6 +50,7 @@ use {
|
|||
linkedlist::LinkedList,
|
||||
numcell::NumCell,
|
||||
smallmap::SmallMap,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
wire::{
|
||||
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
||||
|
|
@ -63,6 +60,7 @@ use {
|
|||
xwayland::XWaylandEvent,
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::Transform,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Formatter},
|
||||
|
|
@ -83,110 +81,7 @@ const INVALID_SIZE: u32 = 2;
|
|||
|
||||
const OFFSET_SINCE: u32 = 5;
|
||||
const BUFFER_SCALE_SINCE: u32 = 6;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
enum Transform {
|
||||
Normal,
|
||||
Rotate90,
|
||||
Rotate180,
|
||||
Rotate270,
|
||||
Flipped,
|
||||
Flipped90,
|
||||
Flipped180,
|
||||
Flipped270,
|
||||
}
|
||||
|
||||
impl Transform {
|
||||
fn swaps_dimensions(self) -> bool {
|
||||
match self {
|
||||
Transform::Normal => false,
|
||||
Transform::Rotate90 => true,
|
||||
Transform::Rotate180 => false,
|
||||
Transform::Rotate270 => true,
|
||||
Transform::Flipped => false,
|
||||
Transform::Flipped90 => true,
|
||||
Transform::Flipped180 => false,
|
||||
Transform::Flipped270 => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Transform {
|
||||
fn apply_inv_sized(self, x1: f32, y1: f32, width: f32, height: f32) -> BufferPoints {
|
||||
let x2 = x1 + width;
|
||||
let y2 = y1 + height;
|
||||
self.apply_inv(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
fn apply_inv(self, x1: f32, y1: f32, x2: f32, y2: f32) -> BufferPoints {
|
||||
macro_rules! bp {
|
||||
(
|
||||
$tl_x:expr, $tl_y:expr,
|
||||
$tr_x:expr, $tr_y:expr,
|
||||
$br_x:expr, $br_y:expr,
|
||||
$bl_x:expr, $bl_y:expr,
|
||||
) => {
|
||||
BufferPoints {
|
||||
top_left: BufferPoint { x: $tl_x, y: $tl_y },
|
||||
top_right: BufferPoint { x: $tr_x, y: $tr_y },
|
||||
bottom_right: BufferPoint { x: $br_x, y: $br_y },
|
||||
bottom_left: BufferPoint { x: $bl_x, y: $bl_y },
|
||||
}
|
||||
};
|
||||
}
|
||||
use Transform::*;
|
||||
match self {
|
||||
Normal => bp! {
|
||||
x1, y1,
|
||||
x2, y1,
|
||||
x2, y2,
|
||||
x1, y2,
|
||||
},
|
||||
Rotate90 => bp! {
|
||||
y1, x2,
|
||||
y1, x1,
|
||||
y2, x1,
|
||||
y2, x2,
|
||||
},
|
||||
Rotate180 => bp! {
|
||||
x2, y2,
|
||||
x1, y2,
|
||||
x1, y1,
|
||||
x2, y1,
|
||||
},
|
||||
Rotate270 => bp! {
|
||||
y2, x1,
|
||||
y2, x2,
|
||||
y1, x2,
|
||||
y1, x1,
|
||||
},
|
||||
Flipped => bp! {
|
||||
x2, y1,
|
||||
x1, y1,
|
||||
x1, y2,
|
||||
x2, y2,
|
||||
},
|
||||
Flipped90 => bp! {
|
||||
y1, x1,
|
||||
y1, x2,
|
||||
y2, x2,
|
||||
y2, x1,
|
||||
},
|
||||
Flipped180 => bp! {
|
||||
x1, y2,
|
||||
x2, y2,
|
||||
x2, y1,
|
||||
x1, y1,
|
||||
},
|
||||
Flipped270 => bp! {
|
||||
y2, x2,
|
||||
y2, x1,
|
||||
y1, x1,
|
||||
y1, x2,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
const TRANSFORM_SINCE: u32 = 6;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SurfaceRole {
|
||||
|
|
@ -223,6 +118,14 @@ impl NodeVisitorBase for SurfaceSendPreferredScaleVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SurfaceSendPreferredTransformVisitor;
|
||||
impl NodeVisitorBase for SurfaceSendPreferredTransformVisitor {
|
||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||
node.send_preferred_buffer_transform();
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WlSurface {
|
||||
pub id: WlSurfaceId,
|
||||
pub node_id: SurfaceNodeId,
|
||||
|
|
@ -233,7 +136,7 @@ pub struct WlSurface {
|
|||
input_region: Cell<Option<Rc<Region>>>,
|
||||
opaque_region: Cell<Option<Rc<Region>>>,
|
||||
buffer_points: RefCell<BufferPoints>,
|
||||
pub buffer_points_norm: RefCell<BufferPoints>,
|
||||
pub buffer_points_norm: RefCell<SampleRect>,
|
||||
buffer_transform: Cell<Transform>,
|
||||
buffer_scale: Cell<i32>,
|
||||
src_rect: Cell<Option<[Fixed; 4]>>,
|
||||
|
|
@ -273,6 +176,14 @@ impl Debug for WlSurface {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct BufferPoints {
|
||||
x1: f32,
|
||||
x2: f32,
|
||||
y1: f32,
|
||||
y2: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum CommitContext {
|
||||
RootCommit,
|
||||
|
|
@ -387,7 +298,7 @@ impl WlSurface {
|
|||
opaque_region: Default::default(),
|
||||
buffer_points: Default::default(),
|
||||
buffer_points_norm: Default::default(),
|
||||
buffer_transform: Cell::new(Transform::Normal),
|
||||
buffer_transform: Cell::new(Transform::None),
|
||||
buffer_scale: Cell::new(1),
|
||||
src_rect: Cell::new(None),
|
||||
dst_size: Cell::new(None),
|
||||
|
|
@ -448,6 +359,9 @@ impl WlSurface {
|
|||
if old.global.preferred_scale.get() != output.global.preferred_scale.get() {
|
||||
self.on_scale_change();
|
||||
}
|
||||
if old.global.transform.get() != output.global.transform.get() {
|
||||
self.send_preferred_buffer_transform();
|
||||
}
|
||||
let children = self.children.borrow_mut();
|
||||
if let Some(children) = &*children {
|
||||
for ss in children.subsurfaces.values() {
|
||||
|
|
@ -540,6 +454,15 @@ impl WlSurface {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn send_preferred_buffer_transform(&self) {
|
||||
if self.version >= TRANSFORM_SINCE {
|
||||
self.client.event(PreferredBufferTransform {
|
||||
self_id: self.id,
|
||||
transform: self.output.get().global.transform.get().to_wl() as _,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn set_toplevel(&self, tl: Option<Rc<dyn ToplevelNode>>) {
|
||||
let ch = self.children.borrow();
|
||||
if let Some(ch) = &*ch {
|
||||
|
|
@ -823,10 +746,12 @@ impl WlSurface {
|
|||
width *= scale;
|
||||
height *= scale;
|
||||
}
|
||||
*buffer_points = self
|
||||
.buffer_transform
|
||||
.get()
|
||||
.apply_inv_sized(x1, y1, width, height);
|
||||
*buffer_points = BufferPoints {
|
||||
x1,
|
||||
y1,
|
||||
x2: x1 + width,
|
||||
y2: y1 + height,
|
||||
};
|
||||
}
|
||||
let size = match self.dst_size.get() {
|
||||
Some(ds) => ds,
|
||||
|
|
@ -838,10 +763,8 @@ impl WlSurface {
|
|||
}
|
||||
if let Some(buffer) = self.buffer.get() {
|
||||
if new_size.is_none() {
|
||||
let (mut width, mut height) = buffer.rect.size();
|
||||
if self.buffer_transform.get().swaps_dimensions() {
|
||||
mem::swap(&mut width, &mut height);
|
||||
}
|
||||
let (mut width, mut height) =
|
||||
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
||||
let scale = self.buffer_scale.get();
|
||||
if scale != 1 {
|
||||
width = (width + scale - 1) / scale;
|
||||
|
|
@ -850,19 +773,29 @@ impl WlSurface {
|
|||
new_size = Some((width, height));
|
||||
}
|
||||
if transform_changed || Some(buffer.rect) != old_raw_size {
|
||||
if self.src_rect.get().is_none() {
|
||||
*buffer_points = self
|
||||
.buffer_transform
|
||||
.get()
|
||||
.apply_inv_sized(0.0, 0.0, 1.0, 1.0);
|
||||
*buffer_points_norm = *buffer_points;
|
||||
let (x1, y1, x2, y2) = if self.src_rect.get().is_none() {
|
||||
(0.0, 0.0, 1.0, 1.0)
|
||||
} else {
|
||||
*buffer_points_norm = buffer_points
|
||||
.norm(buffer.rect.width() as f32, buffer.rect.height() as f32);
|
||||
if !buffer_points_norm.is_leq_1() {
|
||||
let (width, height) =
|
||||
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
||||
let width = width as f32;
|
||||
let height = height as f32;
|
||||
let x1 = buffer_points.x1 / width;
|
||||
let x2 = buffer_points.x2 / width;
|
||||
let y1 = buffer_points.y1 / height;
|
||||
let y2 = buffer_points.y2 / height;
|
||||
if x1 > 1.0 || x2 > 1.0 || y1 > 1.0 || y2 > 1.0 {
|
||||
return Err(WlSurfaceError::ViewportOutsideBuffer);
|
||||
}
|
||||
}
|
||||
(x1, y1, x2, y2)
|
||||
};
|
||||
*buffer_points_norm = SampleRect {
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
buffer_transform: self.buffer_transform.get(),
|
||||
};
|
||||
}
|
||||
}
|
||||
let (width, height) = new_size.unwrap_or_default();
|
||||
|
|
@ -932,17 +865,8 @@ impl WlSurface {
|
|||
|
||||
fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let req: SetBufferTransform = self.parse(parser)?;
|
||||
use Transform::*;
|
||||
let tf = match req.transform {
|
||||
TF_NORMAL => Normal,
|
||||
TF_90 => Rotate90,
|
||||
TF_180 => Rotate180,
|
||||
TF_270 => Rotate270,
|
||||
TF_FLIPPED => Flipped,
|
||||
TF_FLIPPED_90 => Flipped90,
|
||||
TF_FLIPPED_180 => Flipped180,
|
||||
TF_FLIPPED_270 => Flipped270,
|
||||
_ => return Err(WlSurfaceError::UnknownBufferTransform(req.transform)),
|
||||
let Some(tf) = Transform::from_wl(req.transform) else {
|
||||
return Err(WlSurfaceError::UnknownBufferTransform(req.transform));
|
||||
};
|
||||
self.pending.transform.set(Some(tf));
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ pub struct ZwlrScreencopyFrameV1 {
|
|||
pub with_damage: Cell<bool>,
|
||||
pub output_link: Cell<Option<LinkedNode<Rc<Self>>>>,
|
||||
pub buffer: Cell<Option<Rc<WlBuffer>>>,
|
||||
pub is_shm: Cell<bool>,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
|
|
@ -121,14 +120,6 @@ impl ZwlrScreencopyFrameV1 {
|
|||
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride);
|
||||
}
|
||||
}
|
||||
let is_shm = match &*buffer.storage.borrow() {
|
||||
None => false,
|
||||
Some(s) => match s {
|
||||
WlBufferStorage::Shm { .. } => true,
|
||||
WlBufferStorage::Dmabuf(_) => false,
|
||||
},
|
||||
};
|
||||
self.is_shm.set(is_shm);
|
||||
self.buffer.set(Some(buffer));
|
||||
if !with_damage {
|
||||
self.output.connector.connector.damage();
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@ impl ZwlrScreencopyManagerV1 {
|
|||
with_damage: Cell::new(false),
|
||||
output_link: Cell::new(None),
|
||||
buffer: Cell::new(None),
|
||||
is_shm: Cell::new(false),
|
||||
version: self.version,
|
||||
});
|
||||
track!(self.client, frame);
|
||||
|
|
|
|||
|
|
@ -25,8 +25,9 @@ use {
|
|||
SPA_PARAM_Meta, SpaDataFlags, SpaDataType, SpaDirection, SpaIoType,
|
||||
SpaMediaSubtype, SpaMediaType, SpaMetaType, SpaNodeBuffersFlags, SpaNodeCommand,
|
||||
SpaParamType, SpaVideoFormat, SPA_DATA_FLAG_READABLE, SPA_DIRECTION_INPUT,
|
||||
SPA_DIRECTION_OUTPUT, SPA_NODE_BUFFERS_FLAG_ALLOC, SPA_PARAM_INFO_READ,
|
||||
SPA_PORT_FLAG, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS,
|
||||
SPA_DIRECTION_OUTPUT, SPA_NODE_BUFFERS_FLAG_ALLOC, SPA_PARAM_INFO,
|
||||
SPA_PARAM_INFO_READ, SPA_PARAM_INFO_SERIAL, SPA_PORT_FLAG,
|
||||
SPA_PORT_FLAG_CAN_ALLOC_BUFFERS,
|
||||
},
|
||||
},
|
||||
utils::{
|
||||
|
|
@ -120,6 +121,8 @@ pub struct PwClientNodePort {
|
|||
pub buffer_config: Cell<Option<PwClientNodeBufferConfig>>,
|
||||
|
||||
pub io_buffers: CopyHashMap<u32, Rc<PwMemTyped<spa_io_buffers>>>,
|
||||
|
||||
pub serial: Cell<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
|
|
@ -255,6 +258,7 @@ impl PwClientNode {
|
|||
buffers: RefCell::new(vec![]),
|
||||
buffer_config: Cell::new(None),
|
||||
io_buffers: Default::default(),
|
||||
serial: Cell::new(false),
|
||||
});
|
||||
self.ports.set((direction, port.id), port.clone());
|
||||
port
|
||||
|
|
@ -291,7 +295,14 @@ impl PwClientNode {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn send_port_update(&self, port: &PwClientNodePort) {
|
||||
pub fn send_port_update(&self, port: &PwClientNodePort, re_init: bool) {
|
||||
if re_init {
|
||||
port.serial.set(!port.serial.get());
|
||||
}
|
||||
let serial = match port.serial.get() {
|
||||
true => SPA_PARAM_INFO_SERIAL,
|
||||
false => SPA_PARAM_INFO::none(),
|
||||
};
|
||||
self.con.send(self, PwClientNodeMethods::PortUpdate, |f| {
|
||||
f.write_struct(|f| {
|
||||
// direction
|
||||
|
|
@ -437,11 +448,11 @@ impl PwClientNode {
|
|||
f.write_uint(num_params);
|
||||
if sf.is_some() {
|
||||
f.write_id(SPA_PARAM_EnumFormat.0);
|
||||
f.write_uint(SPA_PARAM_INFO_READ.0);
|
||||
f.write_uint((SPA_PARAM_INFO_READ | serial).0);
|
||||
}
|
||||
if bc.is_some() {
|
||||
f.write_id(SPA_PARAM_Buffers.0);
|
||||
f.write_uint(SPA_PARAM_INFO_READ.0);
|
||||
f.write_uint((SPA_PARAM_INFO_READ | serial).0);
|
||||
}
|
||||
f.write_id(SPA_PARAM_Meta.0);
|
||||
f.write_uint(SPA_PARAM_INFO_READ.0);
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ impl PwClientNodeOwner for StartingScreencast {
|
|||
|
||||
impl PwClientNodeOwner for StartedScreencast {
|
||||
fn port_format_changed(&self, port: &Rc<PwClientNodePort>) {
|
||||
self.node.send_port_update(port);
|
||||
self.node.send_port_update(port, false);
|
||||
}
|
||||
|
||||
fn use_buffers(&self, port: &Rc<PwClientNodePort>) {
|
||||
|
|
@ -300,7 +300,7 @@ impl UsrJayScreencastOwner for StartedScreencast {
|
|||
data_type: SPA_DATA_DmaBuf,
|
||||
};
|
||||
self.port.buffer_config.set(Some(bc));
|
||||
self.node.send_port_update(&self.port);
|
||||
self.node.send_port_update(&self.port, true);
|
||||
self.node.send_active(true);
|
||||
*self.buffers.borrow_mut() = buffers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_api::{BufferPoints, GfxApiOpt},
|
||||
gfx_api::{GfxApiOpt, SampleRect},
|
||||
ifs::{
|
||||
wl_buffer::WlBuffer,
|
||||
wl_callback::WlCallback,
|
||||
|
|
@ -55,7 +55,7 @@ pub struct Renderer<'a> {
|
|||
pub state: &'a State,
|
||||
pub result: Option<&'a mut RenderResult>,
|
||||
pub logical_extents: Rect,
|
||||
pub physical_extents: Rect,
|
||||
pub pixel_extents: Rect,
|
||||
}
|
||||
|
||||
impl Renderer<'_> {
|
||||
|
|
@ -63,8 +63,8 @@ impl Renderer<'_> {
|
|||
self.base.scale
|
||||
}
|
||||
|
||||
pub fn physical_extents(&self) -> Rect {
|
||||
self.physical_extents
|
||||
pub fn pixel_extents(&self) -> Rect {
|
||||
self.pixel_extents
|
||||
}
|
||||
|
||||
pub fn logical_extents(&self) -> Rect {
|
||||
|
|
@ -350,7 +350,7 @@ impl Renderer<'_> {
|
|||
buffer: &WlBuffer,
|
||||
x: i32,
|
||||
y: i32,
|
||||
tpoints: BufferPoints,
|
||||
tpoints: SampleRect,
|
||||
tsize: (i32, i32),
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_api::{
|
||||
AbsoluteRect, BufferPoint, BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxTexture,
|
||||
},
|
||||
gfx_api::{CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, SampleRect},
|
||||
rect::Rect,
|
||||
scale::Scale,
|
||||
theme::Color,
|
||||
utils::transform_ext::TransformExt,
|
||||
},
|
||||
jay_config::video::Transform,
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
|
|
@ -15,6 +15,9 @@ pub struct RendererBase<'a> {
|
|||
pub scaled: bool,
|
||||
pub scale: Scale,
|
||||
pub scalef: f64,
|
||||
pub transform: Transform,
|
||||
pub fb_width: f32,
|
||||
pub fb_height: f32,
|
||||
}
|
||||
|
||||
impl RendererBase<'_> {
|
||||
|
|
@ -72,12 +75,15 @@ impl RendererBase<'_> {
|
|||
for bx in boxes {
|
||||
let bx = self.scale_rect(*bx);
|
||||
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
||||
rect: AbsoluteRect {
|
||||
x1: (bx.x1() + dx) as f32,
|
||||
y1: (bx.y1() + dy) as f32,
|
||||
x2: (bx.x2() + dx) as f32,
|
||||
y2: (bx.y2() + dy) as f32,
|
||||
},
|
||||
rect: FramebufferRect::new(
|
||||
(bx.x1() + dx) as f32,
|
||||
(bx.y1() + dy) as f32,
|
||||
(bx.x2() + dx) as f32,
|
||||
(bx.y2() + dy) as f32,
|
||||
self.transform,
|
||||
self.fb_width,
|
||||
self.fb_height,
|
||||
),
|
||||
color: *color,
|
||||
}));
|
||||
}
|
||||
|
|
@ -101,12 +107,15 @@ impl RendererBase<'_> {
|
|||
for bx in boxes {
|
||||
let (x1, y1, x2, y2) = self.scale_rect_f(*bx);
|
||||
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
||||
rect: AbsoluteRect {
|
||||
x1: x1 + dx,
|
||||
y1: y1 + dy,
|
||||
x2: x2 + dx,
|
||||
y2: y2 + dy,
|
||||
},
|
||||
rect: FramebufferRect::new(
|
||||
x1 + dx,
|
||||
y1 + dy,
|
||||
x2 + dx,
|
||||
y2 + dy,
|
||||
self.transform,
|
||||
self.fb_width,
|
||||
self.fb_height,
|
||||
),
|
||||
color: *color,
|
||||
}));
|
||||
}
|
||||
|
|
@ -117,22 +126,17 @@ impl RendererBase<'_> {
|
|||
texture: &Rc<dyn GfxTexture>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
tpoints: Option<BufferPoints>,
|
||||
tpoints: Option<SampleRect>,
|
||||
tsize: Option<(i32, i32)>,
|
||||
tscale: Scale,
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
let mut texcoord = tpoints.unwrap_or(BufferPoints {
|
||||
top_left: BufferPoint { x: 0.0, y: 0.0 },
|
||||
top_right: BufferPoint { x: 1.0, y: 0.0 },
|
||||
bottom_left: BufferPoint { x: 0.0, y: 1.0 },
|
||||
bottom_right: BufferPoint { x: 1.0, y: 1.0 },
|
||||
});
|
||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||
|
||||
let (twidth, theight) = if let Some(size) = tsize {
|
||||
size
|
||||
} else {
|
||||
let (mut w, mut h) = texture.size();
|
||||
let (mut w, mut h) = texcoord.buffer_transform.maybe_swap(texture.size());
|
||||
if tscale != self.scale {
|
||||
let tscale = tscale.to_f64();
|
||||
w = (w as f64 * self.scalef / tscale).round() as _;
|
||||
|
|
@ -145,86 +149,80 @@ impl RendererBase<'_> {
|
|||
let mut target_y = [y, y + theight];
|
||||
|
||||
if let Some(bounds) = bounds {
|
||||
#[cold]
|
||||
fn cold() {}
|
||||
|
||||
let bounds_x = [bounds.x1(), bounds.x2()];
|
||||
let bounds_y = [bounds.y1(), bounds.y2()];
|
||||
|
||||
macro_rules! clamp {
|
||||
($desired:ident, $bounds:ident, $test_idx:expr, $test_cmp:ident, $test_cmp_eq:ident, $([$modify:ident, $keep:ident],)*) => {{
|
||||
let desired_test = $desired[$test_idx];
|
||||
let desired_other = $desired[1 - $test_idx];
|
||||
let bound = $bounds[$test_idx];
|
||||
if desired_test.$test_cmp(&bound) {
|
||||
cold();
|
||||
if desired_other.$test_cmp_eq(&bound) {
|
||||
return;
|
||||
}
|
||||
let max = (desired_other - bound) as f32;
|
||||
let desired = ($desired[1] - $desired[0]) as f32;
|
||||
let factor = max.abs() / desired;
|
||||
$(
|
||||
let dx = (texcoord.$modify.x - texcoord.$keep.x) * factor;
|
||||
texcoord.$modify.x = texcoord.$keep.x + dx;
|
||||
let dy = (texcoord.$modify.y - texcoord.$keep.y) * factor;
|
||||
texcoord.$modify.y = texcoord.$keep.y + dy;
|
||||
)*
|
||||
$desired[$test_idx] = bound;
|
||||
}
|
||||
}};
|
||||
if bound_target(&mut target_x, &mut target_y, &mut texcoord, bounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
clamp!(
|
||||
target_x,
|
||||
bounds_x,
|
||||
0,
|
||||
lt,
|
||||
le,
|
||||
[top_left, top_right],
|
||||
[bottom_left, bottom_right],
|
||||
);
|
||||
|
||||
clamp!(
|
||||
target_x,
|
||||
bounds_x,
|
||||
1,
|
||||
gt,
|
||||
ge,
|
||||
[top_right, top_left],
|
||||
[bottom_right, bottom_left],
|
||||
);
|
||||
|
||||
clamp!(
|
||||
target_y,
|
||||
bounds_y,
|
||||
0,
|
||||
lt,
|
||||
le,
|
||||
[top_left, bottom_left],
|
||||
[top_right, bottom_right],
|
||||
);
|
||||
|
||||
clamp!(
|
||||
target_y,
|
||||
bounds_y,
|
||||
1,
|
||||
gt,
|
||||
ge,
|
||||
[bottom_left, top_left],
|
||||
[bottom_right, top_right],
|
||||
);
|
||||
}
|
||||
|
||||
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
|
||||
tex: texture.clone(),
|
||||
source: texcoord,
|
||||
target: AbsoluteRect {
|
||||
x1: target_x[0] as f32,
|
||||
y1: target_y[0] as f32,
|
||||
x2: target_x[1] as f32,
|
||||
y2: target_y[1] as f32,
|
||||
},
|
||||
target: FramebufferRect::new(
|
||||
target_x[0] as f32,
|
||||
target_y[0] as f32,
|
||||
target_x[1] as f32,
|
||||
target_y[1] as f32,
|
||||
self.transform,
|
||||
self.fb_width,
|
||||
self.fb_height,
|
||||
),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bound_target(
|
||||
target_x: &mut [i32; 2],
|
||||
target_y: &mut [i32; 2],
|
||||
texcoord: &mut SampleRect,
|
||||
bounds: &Rect,
|
||||
) -> bool {
|
||||
let bounds_x = [bounds.x1(), bounds.x2()];
|
||||
let bounds_y = [bounds.y1(), bounds.y2()];
|
||||
|
||||
if target_x[0] >= bounds_x[0]
|
||||
&& target_x[1] <= bounds_x[1]
|
||||
&& target_y[0] >= bounds_y[0]
|
||||
&& target_y[1] <= bounds_y[1]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn cold() {}
|
||||
cold();
|
||||
|
||||
let SampleRect {
|
||||
x1: ref mut t_x1,
|
||||
x2: ref mut t_x2,
|
||||
y1: ref mut t_y1,
|
||||
y2: ref mut t_y2,
|
||||
..
|
||||
} = texcoord;
|
||||
|
||||
macro_rules! clamp {
|
||||
($desired:ident, $bounds:ident, $test_idx:expr, $test_cmp:ident, $test_cmp_eq:ident, $modify:ident, $keep:ident) => {{
|
||||
let desired_test = $desired[$test_idx];
|
||||
let desired_other = $desired[1 - $test_idx];
|
||||
let bound = $bounds[$test_idx];
|
||||
if desired_test.$test_cmp(&bound) {
|
||||
cold();
|
||||
if desired_other.$test_cmp_eq(&bound) {
|
||||
return true;
|
||||
}
|
||||
let max = (desired_other - bound) as f32;
|
||||
let desired = ($desired[1] - $desired[0]) as f32;
|
||||
let factor = max.abs() / desired;
|
||||
*$modify = *$keep + (*$modify - *$keep) * factor;
|
||||
$desired[$test_idx] = bound;
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
clamp!(target_x, bounds_x, 0, lt, le, t_x1, t_x2);
|
||||
clamp!(target_x, bounds_x, 1, gt, ge, t_x2, t_x1);
|
||||
clamp!(target_y, bounds_y, 0, lt, le, t_y1, t_y2);
|
||||
clamp!(target_y, bounds_y, 1, gt, ge, t_y2, t_y1);
|
||||
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use {
|
|||
INVALID_MODIFIER, LINEAR_MODIFIER,
|
||||
},
|
||||
},
|
||||
jay_config::video::Transform,
|
||||
std::{ops::Deref, rc::Rc},
|
||||
thiserror::Error,
|
||||
uapi::OwnedFd,
|
||||
|
|
@ -76,6 +77,7 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
|
|||
Scale::from_int(1),
|
||||
true,
|
||||
false,
|
||||
Transform::None,
|
||||
);
|
||||
let drm = gbm.drm.dup_render()?.fd().clone();
|
||||
Ok(Screenshot { drm, bo })
|
||||
|
|
|
|||
144
src/state.rs
144
src/state.rs
|
|
@ -9,13 +9,15 @@ use {
|
|||
backends::dummy::DummyBackend,
|
||||
cli::RunArgs,
|
||||
client::{Client, ClientId, Clients, SerialRange, NUM_CACHED_SERIAL_RANGES},
|
||||
clientmem::ClientMemOffset,
|
||||
config::ConfigProxy,
|
||||
cursor::{Cursor, ServerCursors},
|
||||
dbus::Dbus,
|
||||
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
||||
fixed::Fixed,
|
||||
forker::ForkerProxy,
|
||||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
||||
format::Format,
|
||||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture, SampleRect},
|
||||
gfx_apis::create_gfx_context,
|
||||
globals::{Globals, GlobalsError, WaylandGlobal},
|
||||
ifs::{
|
||||
|
|
@ -25,11 +27,13 @@ use {
|
|||
jay_seat_events::JaySeatEvents,
|
||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||
wl_drm::WlDrmGlobal,
|
||||
wl_output::OutputId,
|
||||
wl_seat::{SeatIds, WlSeatGlobal},
|
||||
wl_surface::{
|
||||
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
|
||||
NoneSurfaceExt, WlSurface,
|
||||
},
|
||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
|
||||
},
|
||||
|
|
@ -37,7 +41,7 @@ use {
|
|||
leaks::Tracker,
|
||||
logger::Logger,
|
||||
rect::Rect,
|
||||
renderer::{renderer_base::RendererBase, RenderResult, Renderer},
|
||||
renderer::{RenderResult, Renderer},
|
||||
scale::Scale,
|
||||
theme::{Color, Theme},
|
||||
tree::{
|
||||
|
|
@ -62,7 +66,10 @@ use {
|
|||
},
|
||||
ahash::AHashMap,
|
||||
bstr::ByteSlice,
|
||||
jay_config::{video::GfxApi, PciId},
|
||||
jay_config::{
|
||||
video::{GfxApi, Transform},
|
||||
PciId,
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Formatter},
|
||||
|
|
@ -146,6 +153,7 @@ pub struct State {
|
|||
pub dma_buf_ids: DmaBufIds,
|
||||
pub drm_feedback_ids: DrmFeedbackIds,
|
||||
pub direct_scanout_enabled: Cell<bool>,
|
||||
pub output_transforms: RefCell<AHashMap<Rc<OutputId>, Transform>>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -751,7 +759,7 @@ impl State {
|
|||
output.global.preferred_scale.get(),
|
||||
render_hw_cursor,
|
||||
);
|
||||
output.perform_screencopies(Some(&**fb), tex, !render_hw_cursor, 0, 0, None);
|
||||
output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None);
|
||||
rr.dispatch_frame_requests();
|
||||
}
|
||||
|
||||
|
|
@ -759,30 +767,35 @@ impl State {
|
|||
&self,
|
||||
src: &Rc<dyn GfxTexture>,
|
||||
target: &Rc<dyn GfxFramebuffer>,
|
||||
scale: Scale,
|
||||
position: Rect,
|
||||
render_hardware_cursors: bool,
|
||||
x_off: i32,
|
||||
y_off: i32,
|
||||
size: Option<(i32, i32)>,
|
||||
transform: Transform,
|
||||
) {
|
||||
let mut ops = target.take_render_ops();
|
||||
let (width, height) = target.size();
|
||||
let mut renderer = Renderer {
|
||||
base: RendererBase {
|
||||
ops: &mut ops,
|
||||
scaled: scale != 1,
|
||||
scale,
|
||||
scalef: scale.to_f64(),
|
||||
},
|
||||
base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None),
|
||||
state: self,
|
||||
result: None,
|
||||
logical_extents: position.at_point(0, 0),
|
||||
physical_extents: Rect::new_sized(0, 0, width, height).unwrap(),
|
||||
pixel_extents: {
|
||||
let (width, height) = target.logical_size(Transform::None);
|
||||
Rect::new_sized(0, 0, width, height).unwrap()
|
||||
},
|
||||
};
|
||||
renderer
|
||||
.base
|
||||
.render_texture(src, x_off, y_off, None, size, scale, None);
|
||||
let mut sample_rect = SampleRect::identity();
|
||||
sample_rect.buffer_transform = transform;
|
||||
renderer.base.render_texture(
|
||||
src,
|
||||
x_off,
|
||||
y_off,
|
||||
Some(sample_rect),
|
||||
size,
|
||||
Scale::from_int(1),
|
||||
None,
|
||||
);
|
||||
if render_hardware_cursors {
|
||||
for seat in self.globals.lock_seats().values() {
|
||||
if let Some(cursor) = seat.get_cursor() {
|
||||
|
|
@ -797,4 +810,103 @@ impl State {
|
|||
}
|
||||
target.render(ops, Some(&Color::SOLID_BLACK));
|
||||
}
|
||||
|
||||
fn have_hardware_cursor(&self) -> bool {
|
||||
for seat in self.globals.lock_seats().values() {
|
||||
if seat.get_cursor().is_some() {
|
||||
if seat.hardware_cursor() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn perform_shm_screencopy(
|
||||
&self,
|
||||
src: &Rc<dyn GfxTexture>,
|
||||
position: Rect,
|
||||
x_off: i32,
|
||||
y_off: i32,
|
||||
size: Option<(i32, i32)>,
|
||||
capture: &ZwlrScreencopyFrameV1,
|
||||
mem: &ClientMemOffset,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
transform: Transform,
|
||||
) {
|
||||
let (src_width, src_height) = src.size();
|
||||
let mut needs_copy = capture.rect.x1() < x_off
|
||||
|| capture.rect.x2() > x_off + src_width
|
||||
|| capture.rect.y1() < y_off
|
||||
|| capture.rect.y2() > y_off + src_height
|
||||
|| self.have_hardware_cursor()
|
||||
|| transform != Transform::None;
|
||||
if let Some((target_width, target_height)) = size {
|
||||
if (target_width, target_height) != (src_width, src_height) {
|
||||
needs_copy = true;
|
||||
}
|
||||
}
|
||||
let acc = if needs_copy {
|
||||
let Some(ctx) = self.render_ctx.get() else {
|
||||
log::warn!("Cannot perform shm screencopy because there is no render context");
|
||||
return;
|
||||
};
|
||||
let fb =
|
||||
match ctx.create_fb(capture.rect.width(), capture.rect.height(), stride, format) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Could not create temporary fb for screencopy: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.perform_screencopy(
|
||||
src,
|
||||
&fb,
|
||||
position,
|
||||
true,
|
||||
x_off - capture.rect.x1(),
|
||||
y_off - capture.rect.y1(),
|
||||
size,
|
||||
transform,
|
||||
);
|
||||
mem.access(|mem| {
|
||||
fb.copy_to_shm(
|
||||
0,
|
||||
0,
|
||||
capture.rect.width(),
|
||||
capture.rect.height(),
|
||||
stride,
|
||||
format,
|
||||
mem,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
mem.access(|mem| {
|
||||
src.clone().read_pixels(
|
||||
capture.rect.x1() - x_off,
|
||||
capture.rect.y1() - y_off,
|
||||
capture.rect.width(),
|
||||
capture.rect.height(),
|
||||
stride,
|
||||
format,
|
||||
mem,
|
||||
)
|
||||
})
|
||||
};
|
||||
let res = match acc {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
capture.client.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(e) = res {
|
||||
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
|
||||
capture.send_failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,8 +269,7 @@ impl ConnectorHandler {
|
|||
for seat in seats.values() {
|
||||
if seat.get_output().id == on.id {
|
||||
let tpos = target.global.pos.get();
|
||||
let tmode = target.global.mode.get();
|
||||
seat.set_position(tpos.x1() + tmode.width / 2, tpos.y1() + tmode.height / 2);
|
||||
seat.set_position((tpos.x1() + tpos.x2()) / 2, (tpos.y1() + tpos.y2()) / 2);
|
||||
}
|
||||
}
|
||||
if let Some(dev) = &self.data.drm_dev {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
client::ClientId,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
gfx_api::{GfxFramebuffer, GfxTexture},
|
||||
gfx_api::GfxTexture,
|
||||
ifs::{
|
||||
jay_output::JayOutput,
|
||||
jay_screencast::JayScreencast,
|
||||
|
|
@ -16,6 +16,7 @@ use {
|
|||
wl_surface::{
|
||||
ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
|
||||
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, SurfaceSendPreferredScaleVisitor,
|
||||
SurfaceSendPreferredTransformVisitor,
|
||||
},
|
||||
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
||||
},
|
||||
|
|
@ -34,6 +35,7 @@ use {
|
|||
wire::{JayOutputId, JayScreencastId},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::Transform,
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -80,7 +82,6 @@ pub async fn output_render_data(state: Rc<State>) {
|
|||
impl OutputNode {
|
||||
pub fn perform_screencopies(
|
||||
&self,
|
||||
fb: Option<&dyn GfxFramebuffer>,
|
||||
tex: &Rc<dyn GfxTexture>,
|
||||
render_hardware_cursor: bool,
|
||||
x_off: i32,
|
||||
|
|
@ -93,7 +94,7 @@ impl OutputNode {
|
|||
}
|
||||
}
|
||||
self.global
|
||||
.perform_screencopies(fb, tex, render_hardware_cursor, x_off, y_off, size);
|
||||
.perform_screencopies(tex, render_hardware_cursor, x_off, y_off, size);
|
||||
for sc in self.screencasts.lock().values() {
|
||||
sc.copy_texture(self, tex, render_hardware_cursor, x_off, y_off, size);
|
||||
}
|
||||
|
|
@ -387,15 +388,30 @@ impl OutputNode {
|
|||
}
|
||||
|
||||
pub fn update_mode(self: &Rc<Self>, mode: Mode) {
|
||||
self.update_mode_and_transform(mode, self.global.transform.get());
|
||||
}
|
||||
|
||||
pub fn update_transform(self: &Rc<Self>, transform: Transform) {
|
||||
self.update_mode_and_transform(self.global.mode.get(), transform);
|
||||
}
|
||||
|
||||
pub fn update_mode_and_transform(self: &Rc<Self>, mode: Mode, transform: Transform) {
|
||||
let old_mode = self.global.mode.get();
|
||||
if old_mode == mode {
|
||||
let old_transform = self.global.transform.get();
|
||||
if (old_mode, old_transform) == (mode, transform) {
|
||||
return;
|
||||
}
|
||||
let (old_width, old_height) = self.global.pixel_size();
|
||||
self.global.mode.set(mode);
|
||||
let rect = self.calculate_extents();
|
||||
self.change_extents_(&rect);
|
||||
self.state
|
||||
.output_transforms
|
||||
.borrow_mut()
|
||||
.insert(self.global.output_id.clone(), transform);
|
||||
self.global.transform.set(transform);
|
||||
let (new_width, new_height) = self.global.pixel_size();
|
||||
self.change_extents_(&self.calculate_extents());
|
||||
|
||||
if (old_mode.width, old_mode.height) != (mode.width, mode.height) {
|
||||
if (old_width, old_height) != (new_width, new_height) {
|
||||
let mut to_destroy = vec![];
|
||||
if let Some(ctx) = self.state.render_ctx.get() {
|
||||
for sc in self.screencasts.lock().values() {
|
||||
|
|
@ -412,12 +428,15 @@ impl OutputNode {
|
|||
sc.do_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
if transform != old_transform {
|
||||
self.state.refresh_hardware_cursors();
|
||||
self.node_visit_children(&mut SurfaceSendPreferredTransformVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_extents(&self) -> Rect {
|
||||
let mode = self.global.mode.get();
|
||||
let mut width = mode.width;
|
||||
let mut height = mode.height;
|
||||
let (mut width, mut height) = self.global.pixel_size();
|
||||
let scale = self.global.preferred_scale.get();
|
||||
if scale != 1 {
|
||||
let scale = scale.to_f64();
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ pub mod syncqueue;
|
|||
pub mod threshold_counter;
|
||||
pub mod timer;
|
||||
pub mod toplevel_identifier;
|
||||
pub mod transform_ext;
|
||||
pub mod tri;
|
||||
pub mod trim;
|
||||
pub mod unlink_on_drop;
|
||||
|
|
|
|||
71
src/utils/transform_ext.rs
Normal file
71
src/utils/transform_ext.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
use {
|
||||
crate::ifs::wl_output::{
|
||||
TF_180, TF_270, TF_90, TF_FLIPPED, TF_FLIPPED_180, TF_FLIPPED_270, TF_FLIPPED_90, TF_NORMAL,
|
||||
},
|
||||
jay_config::video::{
|
||||
Transform,
|
||||
Transform::{
|
||||
Flip, FlipRotate180, FlipRotate270, FlipRotate90, None, Rotate180, Rotate270, Rotate90,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub trait TransformExt: Sized {
|
||||
fn maybe_swap<T>(self, args: (T, T)) -> (T, T);
|
||||
|
||||
fn to_wl(self) -> i32;
|
||||
|
||||
fn from_wl(wl: i32) -> Option<Self>;
|
||||
|
||||
fn apply_point(self, width: i32, height: i32, point: (i32, i32)) -> (i32, i32);
|
||||
}
|
||||
|
||||
impl TransformExt for Transform {
|
||||
fn maybe_swap<T>(self, (left, right): (T, T)) -> (T, T) {
|
||||
match self {
|
||||
None | Rotate180 | Flip | FlipRotate180 => (left, right),
|
||||
Rotate90 | Rotate270 | FlipRotate90 | FlipRotate270 => (right, left),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_wl(self) -> i32 {
|
||||
match self {
|
||||
None => TF_NORMAL,
|
||||
Rotate90 => TF_90,
|
||||
Rotate180 => TF_180,
|
||||
Rotate270 => TF_270,
|
||||
Flip => TF_FLIPPED,
|
||||
FlipRotate90 => TF_FLIPPED_90,
|
||||
FlipRotate180 => TF_FLIPPED_180,
|
||||
FlipRotate270 => TF_FLIPPED_270,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_wl(wl: i32) -> Option<Self> {
|
||||
let tf = match wl {
|
||||
TF_NORMAL => None,
|
||||
TF_90 => Rotate90,
|
||||
TF_180 => Rotate180,
|
||||
TF_270 => Rotate270,
|
||||
TF_FLIPPED => Flip,
|
||||
TF_FLIPPED_90 => FlipRotate90,
|
||||
TF_FLIPPED_180 => FlipRotate180,
|
||||
TF_FLIPPED_270 => FlipRotate270,
|
||||
_ => return Option::None,
|
||||
};
|
||||
Some(tf)
|
||||
}
|
||||
|
||||
fn apply_point(self, width: i32, height: i32, (x, y): (i32, i32)) -> (i32, i32) {
|
||||
match self {
|
||||
None => (x, y),
|
||||
Rotate90 => (y, height - x),
|
||||
Rotate180 => (width - x, height - y),
|
||||
Rotate270 => (width - y, x),
|
||||
Flip => (width - x, y),
|
||||
FlipRotate90 => (y, x),
|
||||
FlipRotate180 => (x, height - y),
|
||||
FlipRotate270 => (width - y, height - x),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue