From 7d3b8b6278fdf14286d5eece4cc44d4dc93a9026 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 27 Feb 2024 23:44:05 +0100 Subject: [PATCH] render: simplify buffer coordinates --- jay-config/src/video.rs | 22 ++++ src/backends/metal/video.rs | 49 ++++----- src/gfx_api.rs | 128 +++++++++++----------- src/gfx_apis/gl.rs | 62 +++-------- src/gfx_apis/vulkan/renderer.rs | 49 +-------- src/ifs/wl_surface.rs | 183 ++++++++----------------------- src/renderer.rs | 4 +- src/renderer/renderer_base.rs | 187 +++++++++++++++----------------- src/utils.rs | 1 + src/utils/transform_ext.rs | 71 ++++++++++++ 10 files changed, 337 insertions(+), 419 deletions(-) create mode 100644 src/utils/transform_ext.rs diff --git a/jay-config/src/video.rs b/jay-config/src/video.rs index 9152cd5d..9ba2a96b 100644 --- a/jay-config/src/video.rs +++ b/jay-config/src/video.rs @@ -403,3 +403,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, +} diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 863b4f07..0a799a2f 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -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, @@ -40,7 +37,7 @@ use { ahash::{AHashMap, AHashSet}, bstr::{BString, ByteSlice}, indexmap::{indexset, IndexSet}, - jay_config::video::GfxApi, + jay_config::video::{GfxApi, Transform}, std::{ cell::{Cell, RefCell}, ffi::CString, @@ -428,8 +425,6 @@ impl MetalConnector { pass: &GfxRenderPass, plane: &Rc, ) -> Option { - 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 +440,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 +450,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 +473,30 @@ impl MetalConnector { } ct }; - if ct.source != BufferPoints::identity() { - // Non-trivial transforms are not supported. + if ct.source.buffer_transform != Transform::None { + // 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; + ( + (ct.target.x1 + 1.0) * plane_w / 2.0, + (ct.target.x2 + 1.0) * plane_w / 2.0, + (ct.target.y1 + 1.0) * plane_h / 2.0, + (ct.target.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 +512,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 _, }; diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 85a283de..cc7e07ec 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -14,7 +14,7 @@ use { }, ahash::AHashMap, indexmap::IndexSet, - jay_config::video::GfxApi, + jay_config::video::{GfxApi, Transform}, std::{ any::Any, cell::Cell, @@ -38,98 +38,89 @@ 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, } +impl FramebufferRect { + pub fn new(x1: f32, y1: f32, x2: f32, y2: f32, 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, + } + } + + pub fn to_points(&self) -> [[f32; 2]; 4] { + let x1 = self.x1; + let x2 = self.x2; + let y1 = self.y1; + let y2 = self.y2; + [[x2, y1], [x1, y1], [x2, y2], [x1, y2]] + } + + 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, - pub source: BufferPoints, - pub target: AbsoluteRect, + pub source: SampleRect, + pub target: FramebufferRect, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -174,11 +165,14 @@ impl dyn GfxFramebuffer { } pub fn renderer_base<'a>(&self, ops: &'a mut Vec, scale: Scale) -> RendererBase<'a> { + let (width, height) = self.size(); RendererBase { ops, scaled: scale != 1, scale, scalef: scale.to_f64(), + fb_width: width as _, + fb_height: height as _, } } diff --git a/src/gfx_apis/gl.rs b/src/gfx_apis/gl.rs index 43922354..ecfa58fc 100644 --- a/src/gfx_apis/gl.rs +++ b/src/gfx_apis/gl.rs @@ -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, @@ -184,7 +185,7 @@ enum RenderError { #[derive(Default)] struct GfxGlState { - triangles: RefCell>, + triangles: RefCell>, 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 _, diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index bd5d418e..8c2f6c2c 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -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() diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 710d2fa2..b951f5c3 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -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}, @@ -84,110 +82,6 @@ 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, - }, - } - } -} - #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum SurfaceRole { None, @@ -233,7 +127,7 @@ pub struct WlSurface { input_region: Cell>>, opaque_region: Cell>>, buffer_points: RefCell, - pub buffer_points_norm: RefCell, + pub buffer_points_norm: RefCell, buffer_transform: Cell, buffer_scale: Cell, src_rect: Cell>, @@ -273,6 +167,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 +289,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), @@ -823,10 +725,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 +742,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 +752,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 +844,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(()) diff --git a/src/renderer.rs b/src/renderer.rs index 965c7dc5..e0901707 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,6 +1,6 @@ use { crate::{ - gfx_api::{BufferPoints, GfxApiOpt}, + gfx_api::{GfxApiOpt, SampleRect}, ifs::{ wl_buffer::WlBuffer, wl_callback::WlCallback, @@ -350,7 +350,7 @@ impl Renderer<'_> { buffer: &WlBuffer, x: i32, y: i32, - tpoints: BufferPoints, + tpoints: SampleRect, tsize: (i32, i32), bounds: Option<&Rect>, ) { diff --git a/src/renderer/renderer_base.rs b/src/renderer/renderer_base.rs index e3f294a2..1c1b6f8e 100644 --- a/src/renderer/renderer_base.rs +++ b/src/renderer/renderer_base.rs @@ -1,11 +1,10 @@ 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, }, std::rc::Rc, }; @@ -15,6 +14,8 @@ pub struct RendererBase<'a> { pub scaled: bool, pub scale: Scale, pub scalef: f64, + pub fb_width: f32, + pub fb_height: f32, } impl RendererBase<'_> { @@ -72,12 +73,14 @@ 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.fb_width, + self.fb_height, + ), color: *color, })); } @@ -101,12 +104,14 @@ 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.fb_width, + self.fb_height, + ), color: *color, })); } @@ -117,22 +122,17 @@ impl RendererBase<'_> { texture: &Rc, x: i32, y: i32, - tpoints: Option, + tpoints: Option, 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 +145,79 @@ 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.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 +} diff --git a/src/utils.rs b/src/utils.rs index 13a84df6..d30fd686 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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; diff --git a/src/utils/transform_ext.rs b/src/utils/transform_ext.rs new file mode 100644 index 00000000..63f5a975 --- /dev/null +++ b/src/utils/transform_ext.rs @@ -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(self, args: (T, T)) -> (T, T); + + fn to_wl(self) -> i32; + + fn from_wl(wl: i32) -> Option; + + fn apply_point(self, width: i32, height: i32, point: (i32, i32)) -> (i32, i32); +} + +impl TransformExt for Transform { + fn maybe_swap(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 { + 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), + } + } +}