render: simplify buffer coordinates
This commit is contained in:
parent
85c11448fb
commit
7d3b8b6278
10 changed files with 337 additions and 419 deletions
|
|
@ -403,3 +403,25 @@ pub fn set_gfx_api(gfx_api: GfxApi) {
|
||||||
pub fn set_direct_scanout_enabled(enabled: bool) {
|
pub fn set_direct_scanout_enabled(enabled: bool) {
|
||||||
get!().set_direct_scanout_enabled(None, enabled);
|
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,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,7 @@ use {
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
edid::Descriptor,
|
edid::Descriptor,
|
||||||
format::{Format, ARGB8888, XRGB8888},
|
format::{Format, ARGB8888, XRGB8888},
|
||||||
gfx_api::{
|
gfx_api::{GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass, GfxTexture},
|
||||||
AbsoluteRect, BufferPoints, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass,
|
|
||||||
GfxTexture,
|
|
||||||
},
|
|
||||||
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
||||||
renderer::RenderResult,
|
renderer::RenderResult,
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -40,7 +37,7 @@ use {
|
||||||
ahash::{AHashMap, AHashSet},
|
ahash::{AHashMap, AHashSet},
|
||||||
bstr::{BString, ByteSlice},
|
bstr::{BString, ByteSlice},
|
||||||
indexmap::{indexset, IndexSet},
|
indexmap::{indexset, IndexSet},
|
||||||
jay_config::video::GfxApi,
|
jay_config::video::{GfxApi, Transform},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
|
|
@ -428,8 +425,6 @@ impl MetalConnector {
|
||||||
pass: &GfxRenderPass,
|
pass: &GfxRenderPass,
|
||||||
plane: &Rc<MetalPlane>,
|
plane: &Rc<MetalPlane>,
|
||||||
) -> Option<DirectScanoutData> {
|
) -> Option<DirectScanoutData> {
|
||||||
let plane_w = plane.mode_w.get();
|
|
||||||
let plane_h = plane.mode_h.get();
|
|
||||||
let ct = 'ct: {
|
let ct = 'ct: {
|
||||||
let mut ops = pass.ops.iter().rev();
|
let mut ops = pass.ops.iter().rev();
|
||||||
let ct = 'ct2: {
|
let ct = 'ct2: {
|
||||||
|
|
@ -445,13 +440,7 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let plane_rect = AbsoluteRect {
|
if !ct.tex.format().has_alpha && ct.target.is_covering() {
|
||||||
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 {
|
|
||||||
// Texture covers the entire screen and is opaque.
|
// Texture covers the entire screen and is opaque.
|
||||||
break 'ct ct;
|
break 'ct ct;
|
||||||
}
|
}
|
||||||
|
|
@ -461,7 +450,7 @@ impl MetalConnector {
|
||||||
GfxApiOpt::FillRect(fr) => {
|
GfxApiOpt::FillRect(fr) => {
|
||||||
if fr.color == Color::SOLID_BLACK {
|
if fr.color == Color::SOLID_BLACK {
|
||||||
// Black fills can be ignored because this is the CRTC background color.
|
// 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.
|
// If fill covers the entire screen, we don't have to look further.
|
||||||
break 'ct ct;
|
break 'ct ct;
|
||||||
}
|
}
|
||||||
|
|
@ -484,20 +473,30 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
ct
|
ct
|
||||||
};
|
};
|
||||||
if ct.source != BufferPoints::identity() {
|
if ct.source.buffer_transform != Transform::None {
|
||||||
// Non-trivial transforms are not supported.
|
// Rotations and mirroring are not supported.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if ct.target.x1 < 0.0
|
if !ct.source.is_covering() {
|
||||||
|| ct.target.y1 < 0.0
|
// Viewports are not supported.
|
||||||
|| ct.target.x2 > plane_w as f32
|
return None;
|
||||||
|| ct.target.y2 > plane_h as f32
|
}
|
||||||
{
|
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.
|
// Rendering outside the screen is not supported.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (tex_w, tex_h) = ct.tex.size();
|
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 {
|
if crtc_w < 0.0 || crtc_h < 0.0 {
|
||||||
// Flipping x or y axis is not supported.
|
// Flipping x or y axis is not supported.
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -513,8 +512,8 @@ impl MetalConnector {
|
||||||
let position = DirectScanoutPosition {
|
let position = DirectScanoutPosition {
|
||||||
src_width: tex_w,
|
src_width: tex_w,
|
||||||
src_height: tex_h,
|
src_height: tex_h,
|
||||||
crtc_x: ct.target.x1 as _,
|
crtc_x: x1 as _,
|
||||||
crtc_y: ct.target.y1 as _,
|
crtc_y: y1 as _,
|
||||||
crtc_width: crtc_w as _,
|
crtc_width: crtc_w as _,
|
||||||
crtc_height: crtc_h as _,
|
crtc_height: crtc_h as _,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
128
src/gfx_api.rs
128
src/gfx_api.rs
|
|
@ -14,7 +14,7 @@ use {
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
indexmap::IndexSet,
|
indexmap::IndexSet,
|
||||||
jay_config::video::GfxApi,
|
jay_config::video::{GfxApi, Transform},
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
|
|
@ -38,98 +38,89 @@ pub struct GfxRenderPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct BufferPoint {
|
pub struct SampleRect {
|
||||||
pub x: f32,
|
pub x1: f32,
|
||||||
pub y: f32,
|
pub y1: f32,
|
||||||
|
pub x2: f32,
|
||||||
|
pub y2: f32,
|
||||||
|
pub buffer_transform: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferPoint {
|
impl SampleRect {
|
||||||
pub fn is_leq_1(&self) -> bool {
|
pub fn identity() -> Self {
|
||||||
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 {
|
|
||||||
Self {
|
Self {
|
||||||
top_left: BufferPoint {
|
x1: 0.0,
|
||||||
x: self.top_left.x / width,
|
y1: 0.0,
|
||||||
y: self.top_left.y / height,
|
x2: 1.0,
|
||||||
},
|
y2: 1.0,
|
||||||
top_right: BufferPoint {
|
buffer_transform: Transform::None,
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_leq_1(&self) -> bool {
|
pub fn is_covering(&self) -> bool {
|
||||||
self.top_left.is_leq_1()
|
self.x1 == 0.0 && self.y1 == 0.0 && self.x2 == 1.0 && self.y2 == 1.0
|
||||||
&& self.top_right.is_leq_1()
|
|
||||||
&& self.bottom_left.is_leq_1()
|
|
||||||
&& self.bottom_right.is_leq_1()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identity() -> Self {
|
pub fn to_points(&self) -> [[f32; 2]; 4] {
|
||||||
Self {
|
use Transform::*;
|
||||||
top_left: BufferPoint::top_left(),
|
let x1 = self.x1;
|
||||||
top_right: BufferPoint::top_right(),
|
let x2 = self.x2;
|
||||||
bottom_left: BufferPoint::bottom_left(),
|
let y1 = self.y1;
|
||||||
bottom_right: BufferPoint::bottom_right(),
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct AbsoluteRect {
|
pub struct FramebufferRect {
|
||||||
pub x1: f32,
|
pub x1: f32,
|
||||||
pub x2: f32,
|
pub x2: f32,
|
||||||
pub y1: f32,
|
pub y1: f32,
|
||||||
pub y2: 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)]
|
#[derive(Debug)]
|
||||||
pub struct FillRect {
|
pub struct FillRect {
|
||||||
pub rect: AbsoluteRect,
|
pub rect: FramebufferRect,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CopyTexture {
|
pub struct CopyTexture {
|
||||||
pub tex: Rc<dyn GfxTexture>,
|
pub tex: Rc<dyn GfxTexture>,
|
||||||
pub source: BufferPoints,
|
pub source: SampleRect,
|
||||||
pub target: AbsoluteRect,
|
pub target: FramebufferRect,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -174,11 +165,14 @@ impl dyn GfxFramebuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn renderer_base<'a>(&self, ops: &'a mut Vec<GfxApiOpt>, scale: Scale) -> RendererBase<'a> {
|
pub fn renderer_base<'a>(&self, ops: &'a mut Vec<GfxApiOpt>, scale: Scale) -> RendererBase<'a> {
|
||||||
|
let (width, height) = self.size();
|
||||||
RendererBase {
|
RendererBase {
|
||||||
ops,
|
ops,
|
||||||
scaled: scale != 1,
|
scaled: scale != 1,
|
||||||
scale,
|
scale,
|
||||||
scalef: scale.to_f64(),
|
scalef: scale.to_f64(),
|
||||||
|
fb_width: width as _,
|
||||||
|
fb_height: height as _,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,8 @@ macro_rules! dynload {
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||||
|
SampleRect,
|
||||||
},
|
},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
gl::texture::image_target,
|
gl::texture::image_target,
|
||||||
|
|
@ -184,7 +185,7 @@ enum RenderError {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct GfxGlState {
|
struct GfxGlState {
|
||||||
triangles: RefCell<Vec<f32>>,
|
triangles: RefCell<Vec<[f32; 2]>>,
|
||||||
fill_rect: VecStorage<&'static FillRect>,
|
fill_rect: VecStorage<&'static FillRect>,
|
||||||
copy_tex: VecStorage<&'static CopyTexture>,
|
copy_tex: VecStorage<&'static CopyTexture>,
|
||||||
}
|
}
|
||||||
|
|
@ -198,8 +199,6 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
||||||
let copy_tex = &mut *copy_tex;
|
let copy_tex = &mut *copy_tex;
|
||||||
let mut triangles = state.triangles.borrow_mut();
|
let mut triangles = state.triangles.borrow_mut();
|
||||||
let triangles = &mut *triangles;
|
let triangles = &mut *triangles;
|
||||||
let width = fb.gl.width as f32;
|
|
||||||
let height = fb.gl.height as f32;
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < ops.len() {
|
while i < ops.len() {
|
||||||
macro_rules! has_ops {
|
macro_rules! has_ops {
|
||||||
|
|
@ -240,19 +239,14 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
||||||
Some(c) if c == fr.color => {}
|
Some(c) if c == fr.color => {}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
let x1 = 2.0 * (fr.rect.x1 / width) - 1.0;
|
let [top_right, top_left, bottom_right, bottom_left] = fr.rect.to_points();
|
||||||
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;
|
|
||||||
triangles.extend_from_slice(&[
|
triangles.extend_from_slice(&[
|
||||||
// triangle 1
|
top_right,
|
||||||
x2, y1, // top right
|
top_left,
|
||||||
x1, y1, // top left
|
bottom_left,
|
||||||
x1, y2, // bottom left
|
top_right,
|
||||||
// triangle 2
|
bottom_left,
|
||||||
x2, y1, // top right
|
bottom_right,
|
||||||
x1, y2, // bottom left
|
|
||||||
x2, y2, // bottom right
|
|
||||||
]);
|
]);
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -262,16 +256,12 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for tex in &*copy_tex {
|
for tex in &*copy_tex {
|
||||||
let x1 = 2.0 * (tex.target.x1 / width) - 1.0;
|
render_texture(&fb.ctx, &tex.tex.as_gl(), &tex.target, &tex.source)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
let gles = ctx.ctx.dpy.gles;
|
||||||
unsafe {
|
unsafe {
|
||||||
(gles.glUseProgram)(ctx.fill_prog.prog);
|
(gles.glUseProgram)(ctx.fill_prog.prog);
|
||||||
|
|
@ -285,7 +275,7 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
||||||
boxes.as_ptr() as _,
|
boxes.as_ptr() as _,
|
||||||
);
|
);
|
||||||
(gles.glEnableVertexAttribArray)(ctx.fill_prog_pos 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 _);
|
(gles.glDisableVertexAttribArray)(ctx.fill_prog_pos as _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -293,11 +283,8 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
||||||
fn render_texture(
|
fn render_texture(
|
||||||
ctx: &GlRenderContext,
|
ctx: &GlRenderContext,
|
||||||
texture: &Texture,
|
texture: &Texture,
|
||||||
x1: f32,
|
target_rect: &FramebufferRect,
|
||||||
y1: f32,
|
src: &SampleRect,
|
||||||
x2: f32,
|
|
||||||
y2: f32,
|
|
||||||
src: &BufferPoints,
|
|
||||||
) {
|
) {
|
||||||
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
|
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
|
||||||
let gles = ctx.ctx.dpy.gles;
|
let gles = ctx.ctx.dpy.gles;
|
||||||
|
|
@ -334,23 +321,8 @@ fn render_texture(
|
||||||
|
|
||||||
(gles.glUniform1i)(prog.tex, 0);
|
(gles.glUniform1i)(prog.tex, 0);
|
||||||
|
|
||||||
let texcoord = [
|
let texcoord = src.to_points();
|
||||||
src.top_right.x,
|
let pos = target_rect.to_points();
|
||||||
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
|
|
||||||
];
|
|
||||||
|
|
||||||
(gles.glVertexAttribPointer)(
|
(gles.glVertexAttribPointer)(
|
||||||
prog.texcoord as _,
|
prog.texcoord as _,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{
|
gfx_api::{GfxApiOpt, GfxFormat, GfxFramebuffer, GfxTexture},
|
||||||
AbsoluteRect, BufferPoint, BufferPoints, GfxApiOpt, GfxFormat, GfxFramebuffer,
|
|
||||||
GfxTexture,
|
|
||||||
},
|
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
allocator::VulkanAllocator,
|
allocator::VulkanAllocator,
|
||||||
command::{VulkanCommandBuffer, VulkanCommandPool},
|
command::{VulkanCommandBuffer, VulkanCommandPool},
|
||||||
|
|
@ -386,12 +383,7 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_draws(
|
fn record_draws(&self, buf: CommandBuffer, opts: &[GfxApiOpt]) -> Result<(), VulkanError> {
|
||||||
&self,
|
|
||||||
buf: CommandBuffer,
|
|
||||||
fb: &VulkanImage,
|
|
||||||
opts: &[GfxApiOpt],
|
|
||||||
) -> Result<(), VulkanError> {
|
|
||||||
let dev = &self.device.device;
|
let dev = &self.device.device;
|
||||||
let mut current_pipeline = None;
|
let mut current_pipeline = None;
|
||||||
let mut bind = |pipeline: &VulkanPipeline| {
|
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 {
|
for opt in opts {
|
||||||
match opt {
|
match opt {
|
||||||
GfxApiOpt::Sync => {}
|
GfxApiOpt::Sync => {}
|
||||||
GfxApiOpt::FillRect(r) => {
|
GfxApiOpt::FillRect(r) => {
|
||||||
bind(&self.fill_pipeline);
|
bind(&self.fill_pipeline);
|
||||||
let vert = FillVertPushConstants {
|
let vert = FillVertPushConstants {
|
||||||
pos: r.rect.to_vk(width, height),
|
pos: r.rect.to_points(),
|
||||||
};
|
};
|
||||||
let frag = FillFragPushConstants {
|
let frag = FillFragPushConstants {
|
||||||
color: r.color.to_array_srgb(),
|
color: r.color.to_array_srgb(),
|
||||||
|
|
@ -437,8 +427,8 @@ impl VulkanRenderer {
|
||||||
let tex = c.tex.as_vk(&self.device.device);
|
let tex = c.tex.as_vk(&self.device.device);
|
||||||
bind(&self.tex_pipeline);
|
bind(&self.tex_pipeline);
|
||||||
let vert = TexVertPushConstants {
|
let vert = TexVertPushConstants {
|
||||||
pos: c.target.to_vk(width, height),
|
pos: c.target.to_points(),
|
||||||
tex_pos: c.source.to_vk(),
|
tex_pos: c.source.to_points(),
|
||||||
};
|
};
|
||||||
let image_info = DescriptorImageInfo::builder()
|
let image_info = DescriptorImageInfo::builder()
|
||||||
.image_view(tex.texture_view)
|
.image_view(tex.texture_view)
|
||||||
|
|
@ -879,7 +869,7 @@ impl VulkanRenderer {
|
||||||
self.secondary_barriers(buf.buffer);
|
self.secondary_barriers(buf.buffer);
|
||||||
self.begin_rendering(buf.buffer, fb, clear);
|
self.begin_rendering(buf.buffer, fb, clear);
|
||||||
self.set_viewport(buf.buffer, fb);
|
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.end_rendering(buf.buffer);
|
||||||
self.final_barriers(buf.buffer, fb);
|
self.final_barriers(buf.buffer, fb);
|
||||||
self.end_command_buffer(buf.buffer)?;
|
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> {
|
fn image_barrier() -> ImageMemoryBarrier2Builder<'static> {
|
||||||
ImageMemoryBarrier2::builder().subresource_range(
|
ImageMemoryBarrier2::builder().subresource_range(
|
||||||
ImageSubresourceRange::builder()
|
ImageSubresourceRange::builder()
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,10 @@ use {
|
||||||
client::{Client, ClientError, RequestParser},
|
client::{Client, ClientError, RequestParser},
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{BufferPoint, BufferPoints},
|
gfx_api::SampleRect,
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
wl_buffer::WlBuffer,
|
||||||
wl_callback::WlCallback,
|
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_seat::{
|
||||||
wl_pointer::PendingScroll, zwp_pointer_constraints_v1::SeatConstraint, Dnd,
|
wl_pointer::PendingScroll, zwp_pointer_constraints_v1::SeatConstraint, Dnd,
|
||||||
NodeSeatState, SeatId, WlSeatGlobal,
|
NodeSeatState, SeatId, WlSeatGlobal,
|
||||||
|
|
@ -54,6 +50,7 @@ use {
|
||||||
linkedlist::LinkedList,
|
linkedlist::LinkedList,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
|
transform_ext::TransformExt,
|
||||||
},
|
},
|
||||||
wire::{
|
wire::{
|
||||||
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
||||||
|
|
@ -63,6 +60,7 @@ use {
|
||||||
xwayland::XWaylandEvent,
|
xwayland::XWaylandEvent,
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
jay_config::video::Transform,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
|
|
@ -84,110 +82,6 @@ const INVALID_SIZE: u32 = 2;
|
||||||
const OFFSET_SINCE: u32 = 5;
|
const OFFSET_SINCE: u32 = 5;
|
||||||
const BUFFER_SCALE_SINCE: u32 = 6;
|
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)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum SurfaceRole {
|
pub enum SurfaceRole {
|
||||||
None,
|
None,
|
||||||
|
|
@ -233,7 +127,7 @@ pub struct WlSurface {
|
||||||
input_region: Cell<Option<Rc<Region>>>,
|
input_region: Cell<Option<Rc<Region>>>,
|
||||||
opaque_region: Cell<Option<Rc<Region>>>,
|
opaque_region: Cell<Option<Rc<Region>>>,
|
||||||
buffer_points: RefCell<BufferPoints>,
|
buffer_points: RefCell<BufferPoints>,
|
||||||
pub buffer_points_norm: RefCell<BufferPoints>,
|
pub buffer_points_norm: RefCell<SampleRect>,
|
||||||
buffer_transform: Cell<Transform>,
|
buffer_transform: Cell<Transform>,
|
||||||
buffer_scale: Cell<i32>,
|
buffer_scale: Cell<i32>,
|
||||||
src_rect: Cell<Option<[Fixed; 4]>>,
|
src_rect: Cell<Option<[Fixed; 4]>>,
|
||||||
|
|
@ -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)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
enum CommitContext {
|
enum CommitContext {
|
||||||
RootCommit,
|
RootCommit,
|
||||||
|
|
@ -387,7 +289,7 @@ impl WlSurface {
|
||||||
opaque_region: Default::default(),
|
opaque_region: Default::default(),
|
||||||
buffer_points: Default::default(),
|
buffer_points: Default::default(),
|
||||||
buffer_points_norm: Default::default(),
|
buffer_points_norm: Default::default(),
|
||||||
buffer_transform: Cell::new(Transform::Normal),
|
buffer_transform: Cell::new(Transform::None),
|
||||||
buffer_scale: Cell::new(1),
|
buffer_scale: Cell::new(1),
|
||||||
src_rect: Cell::new(None),
|
src_rect: Cell::new(None),
|
||||||
dst_size: Cell::new(None),
|
dst_size: Cell::new(None),
|
||||||
|
|
@ -823,10 +725,12 @@ impl WlSurface {
|
||||||
width *= scale;
|
width *= scale;
|
||||||
height *= scale;
|
height *= scale;
|
||||||
}
|
}
|
||||||
*buffer_points = self
|
*buffer_points = BufferPoints {
|
||||||
.buffer_transform
|
x1,
|
||||||
.get()
|
y1,
|
||||||
.apply_inv_sized(x1, y1, width, height);
|
x2: x1 + width,
|
||||||
|
y2: y1 + height,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
let size = match self.dst_size.get() {
|
let size = match self.dst_size.get() {
|
||||||
Some(ds) => ds,
|
Some(ds) => ds,
|
||||||
|
|
@ -838,10 +742,8 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
if let Some(buffer) = self.buffer.get() {
|
if let Some(buffer) = self.buffer.get() {
|
||||||
if new_size.is_none() {
|
if new_size.is_none() {
|
||||||
let (mut width, mut height) = buffer.rect.size();
|
let (mut width, mut height) =
|
||||||
if self.buffer_transform.get().swaps_dimensions() {
|
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
||||||
mem::swap(&mut width, &mut height);
|
|
||||||
}
|
|
||||||
let scale = self.buffer_scale.get();
|
let scale = self.buffer_scale.get();
|
||||||
if scale != 1 {
|
if scale != 1 {
|
||||||
width = (width + scale - 1) / scale;
|
width = (width + scale - 1) / scale;
|
||||||
|
|
@ -850,19 +752,29 @@ impl WlSurface {
|
||||||
new_size = Some((width, height));
|
new_size = Some((width, height));
|
||||||
}
|
}
|
||||||
if transform_changed || Some(buffer.rect) != old_raw_size {
|
if transform_changed || Some(buffer.rect) != old_raw_size {
|
||||||
if self.src_rect.get().is_none() {
|
let (x1, y1, x2, y2) = if self.src_rect.get().is_none() {
|
||||||
*buffer_points = self
|
(0.0, 0.0, 1.0, 1.0)
|
||||||
.buffer_transform
|
|
||||||
.get()
|
|
||||||
.apply_inv_sized(0.0, 0.0, 1.0, 1.0);
|
|
||||||
*buffer_points_norm = *buffer_points;
|
|
||||||
} else {
|
} else {
|
||||||
*buffer_points_norm = buffer_points
|
let (width, height) =
|
||||||
.norm(buffer.rect.width() as f32, buffer.rect.height() as f32);
|
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
||||||
if !buffer_points_norm.is_leq_1() {
|
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);
|
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();
|
let (width, height) = new_size.unwrap_or_default();
|
||||||
|
|
@ -932,17 +844,8 @@ impl WlSurface {
|
||||||
|
|
||||||
fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let req: SetBufferTransform = self.parse(parser)?;
|
let req: SetBufferTransform = self.parse(parser)?;
|
||||||
use Transform::*;
|
let Some(tf) = Transform::from_wl(req.transform) else {
|
||||||
let tf = match req.transform {
|
return Err(WlSurfaceError::UnknownBufferTransform(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)),
|
|
||||||
};
|
};
|
||||||
self.pending.transform.set(Some(tf));
|
self.pending.transform.set(Some(tf));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::{BufferPoints, GfxApiOpt},
|
gfx_api::{GfxApiOpt, SampleRect},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
wl_buffer::WlBuffer,
|
||||||
wl_callback::WlCallback,
|
wl_callback::WlCallback,
|
||||||
|
|
@ -350,7 +350,7 @@ impl Renderer<'_> {
|
||||||
buffer: &WlBuffer,
|
buffer: &WlBuffer,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
tpoints: BufferPoints,
|
tpoints: SampleRect,
|
||||||
tsize: (i32, i32),
|
tsize: (i32, i32),
|
||||||
bounds: Option<&Rect>,
|
bounds: Option<&Rect>,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::{
|
gfx_api::{CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, SampleRect},
|
||||||
AbsoluteRect, BufferPoint, BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxTexture,
|
|
||||||
},
|
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
|
utils::transform_ext::TransformExt,
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
@ -15,6 +14,8 @@ pub struct RendererBase<'a> {
|
||||||
pub scaled: bool,
|
pub scaled: bool,
|
||||||
pub scale: Scale,
|
pub scale: Scale,
|
||||||
pub scalef: f64,
|
pub scalef: f64,
|
||||||
|
pub fb_width: f32,
|
||||||
|
pub fb_height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RendererBase<'_> {
|
impl RendererBase<'_> {
|
||||||
|
|
@ -72,12 +73,14 @@ impl RendererBase<'_> {
|
||||||
for bx in boxes {
|
for bx in boxes {
|
||||||
let bx = self.scale_rect(*bx);
|
let bx = self.scale_rect(*bx);
|
||||||
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
||||||
rect: AbsoluteRect {
|
rect: FramebufferRect::new(
|
||||||
x1: (bx.x1() + dx) as f32,
|
(bx.x1() + dx) as f32,
|
||||||
y1: (bx.y1() + dy) as f32,
|
(bx.y1() + dy) as f32,
|
||||||
x2: (bx.x2() + dx) as f32,
|
(bx.x2() + dx) as f32,
|
||||||
y2: (bx.y2() + dy) as f32,
|
(bx.y2() + dy) as f32,
|
||||||
},
|
self.fb_width,
|
||||||
|
self.fb_height,
|
||||||
|
),
|
||||||
color: *color,
|
color: *color,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -101,12 +104,14 @@ impl RendererBase<'_> {
|
||||||
for bx in boxes {
|
for bx in boxes {
|
||||||
let (x1, y1, x2, y2) = self.scale_rect_f(*bx);
|
let (x1, y1, x2, y2) = self.scale_rect_f(*bx);
|
||||||
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
||||||
rect: AbsoluteRect {
|
rect: FramebufferRect::new(
|
||||||
x1: x1 + dx,
|
x1 + dx,
|
||||||
y1: y1 + dy,
|
y1 + dy,
|
||||||
x2: x2 + dx,
|
x2 + dx,
|
||||||
y2: y2 + dy,
|
y2 + dy,
|
||||||
},
|
self.fb_width,
|
||||||
|
self.fb_height,
|
||||||
|
),
|
||||||
color: *color,
|
color: *color,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -117,22 +122,17 @@ impl RendererBase<'_> {
|
||||||
texture: &Rc<dyn GfxTexture>,
|
texture: &Rc<dyn GfxTexture>,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
tpoints: Option<BufferPoints>,
|
tpoints: Option<SampleRect>,
|
||||||
tsize: Option<(i32, i32)>,
|
tsize: Option<(i32, i32)>,
|
||||||
tscale: Scale,
|
tscale: Scale,
|
||||||
bounds: Option<&Rect>,
|
bounds: Option<&Rect>,
|
||||||
) {
|
) {
|
||||||
let mut texcoord = tpoints.unwrap_or(BufferPoints {
|
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||||
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 (twidth, theight) = if let Some(size) = tsize {
|
let (twidth, theight) = if let Some(size) = tsize {
|
||||||
size
|
size
|
||||||
} else {
|
} else {
|
||||||
let (mut w, mut h) = texture.size();
|
let (mut w, mut h) = texcoord.buffer_transform.maybe_swap(texture.size());
|
||||||
if tscale != self.scale {
|
if tscale != self.scale {
|
||||||
let tscale = tscale.to_f64();
|
let tscale = tscale.to_f64();
|
||||||
w = (w as f64 * self.scalef / tscale).round() as _;
|
w = (w as f64 * self.scalef / tscale).round() as _;
|
||||||
|
|
@ -145,86 +145,79 @@ impl RendererBase<'_> {
|
||||||
let mut target_y = [y, y + theight];
|
let mut target_y = [y, y + theight];
|
||||||
|
|
||||||
if let Some(bounds) = bounds {
|
if let Some(bounds) = bounds {
|
||||||
#[cold]
|
if bound_target(&mut target_x, &mut target_y, &mut texcoord, bounds) {
|
||||||
fn cold() {}
|
return;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
|
||||||
tex: texture.clone(),
|
tex: texture.clone(),
|
||||||
source: texcoord,
|
source: texcoord,
|
||||||
target: AbsoluteRect {
|
target: FramebufferRect::new(
|
||||||
x1: target_x[0] as f32,
|
target_x[0] as f32,
|
||||||
y1: target_y[0] as f32,
|
target_y[0] as f32,
|
||||||
x2: target_x[1] as f32,
|
target_x[1] as f32,
|
||||||
y2: target_y[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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ pub mod syncqueue;
|
||||||
pub mod threshold_counter;
|
pub mod threshold_counter;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod toplevel_identifier;
|
pub mod toplevel_identifier;
|
||||||
|
pub mod transform_ext;
|
||||||
pub mod tri;
|
pub mod tri;
|
||||||
pub mod trim;
|
pub mod trim;
|
||||||
pub mod unlink_on_drop;
|
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