Skip to content

Commit 066f033

Browse files
authored
fix: preserve screencopy transforms for rotated outputs (#530)
1 parent 1591466 commit 066f033

5 files changed

Lines changed: 66 additions & 43 deletions

File tree

wayvr/src/overlays/screen/backend.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,16 +238,20 @@ impl OverlayBackend for ScreenBackend {
238238
}
239239

240240
if let Some(pipeline) = self.pipeline.as_mut() {
241-
if self.meta.is_some_and(|old| old.extent != meta.extent) {
242-
pipeline.set_extent(
241+
if self.meta.is_some_and(|old| old.extent != meta.extent)
242+
|| frame.format.transform != pipeline.transform()
243+
{
244+
pipeline.set_layout(
243245
app,
244246
[meta.extent[0] as _, meta.extent[1] as _],
245247
[0., 0.],
248+
frame.format.transform,
246249
)?;
247250
self.interaction_transform = Some(ui_transform(meta.extent));
248251
}
249252
} else {
250-
let pipeline = ScreenPipeline::new(&meta, app, stereo, [0., 0.])?;
253+
let pipeline =
254+
ScreenPipeline::new(&meta, app, stereo, [0., 0.], frame.format.transform)?;
251255
self.pipeline = Some(pipeline);
252256
self.interaction_transform = Some(ui_transform(meta.extent));
253257
}

wayvr/src/overlays/screen/capture.rs

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use std::{
2-
f32::consts::PI,
32
os::fd::AsRawFd,
43
sync::{Arc, OnceLock},
54
};
65

7-
use glam::{Affine3A, Vec3};
6+
use glam::Affine3A;
87
use smallvec::{SmallVec, smallvec};
98
use vulkano::{
109
buffer::{BufferUsage, Subbuffer},
@@ -55,6 +54,7 @@ pub struct ScreenPipeline {
5554
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
5655
extentf: [f32; 2],
5756
offsetf: [f32; 2],
57+
transform: wlx_frame::Transform,
5858
stereo: StereoMode,
5959
}
6060

@@ -64,6 +64,7 @@ impl ScreenPipeline {
6464
app: &mut AppState,
6565
stereo: StereoMode,
6666
offsetf: [f32; 2],
67+
transform: wlx_frame::Transform,
6768
) -> anyhow::Result<Self> {
6869
let extentf = [meta.extent[0] as f32, meta.extent[1] as f32];
6970

@@ -81,6 +82,7 @@ impl ScreenPipeline {
8182
pipeline,
8283
extentf,
8384
offsetf,
85+
transform,
8486
stereo,
8587
};
8688
me.ensure_stereo(stereo);
@@ -96,6 +98,10 @@ impl ScreenPipeline {
9698
self.pass.clear(); // ensure_depth will repopulate
9799
}
98100

101+
pub const fn transform(&self) -> wlx_frame::Transform {
102+
self.transform
103+
}
104+
99105
fn ensure_depth(&mut self, app: &mut AppState, depth: usize) -> anyhow::Result<()> {
100106
while self.pass.len() < depth {
101107
self.pass.push(Self::create_pass(
@@ -111,20 +117,22 @@ impl ScreenPipeline {
111117
}
112118

113119
for (eye, current) in self.pass.iter_mut().enumerate() {
114-
let verts = stereo_mode_to_verts(self.stereo, eye);
120+
let verts = stereo_mode_to_verts(self.stereo, eye, self.transform);
115121
current.buf_vert.write()?.copy_from_slice(&verts);
116122
}
117123
Ok(())
118124
}
119125

120-
pub fn set_extent(
126+
pub fn set_layout(
121127
&mut self,
122128
app: &mut AppState,
123129
extentf: [f32; 2],
124130
offsetf: [f32; 2],
131+
transform: wlx_frame::Transform,
125132
) -> anyhow::Result<()> {
126133
self.extentf = extentf;
127134
self.offsetf = offsetf;
135+
self.transform = transform;
128136
self.pass.clear();
129137

130138
self.mouse = Self::create_mouse_pass(app, self.pipeline.clone(), extentf, offsetf)?;
@@ -241,13 +249,31 @@ impl ScreenPipeline {
241249
}
242250
}
243251

244-
fn stereo_mode_to_verts(stereo: StereoMode, array_index: usize) -> [Vert2Uv; 4] {
252+
fn transform_uv(uv: [f32; 2], transform: wlx_frame::Transform) -> [f32; 2] {
253+
let [u, v] = uv;
254+
match transform {
255+
wlx_frame::Transform::Normal | wlx_frame::Transform::Undefined => [u, v],
256+
wlx_frame::Transform::Rotated90 => [v, 1.0 - u],
257+
wlx_frame::Transform::Rotated180 => [1.0 - u, 1.0 - v],
258+
wlx_frame::Transform::Rotated270 => [1.0 - v, u],
259+
wlx_frame::Transform::Flipped => [1.0 - u, v],
260+
wlx_frame::Transform::Flipped90 => [v, u],
261+
wlx_frame::Transform::Flipped180 => [u, 1.0 - v],
262+
wlx_frame::Transform::Flipped270 => [1.0 - v, 1.0 - u],
263+
}
264+
}
265+
266+
fn stereo_mode_to_verts(
267+
stereo: StereoMode,
268+
array_index: usize,
269+
transform: wlx_frame::Transform,
270+
) -> [Vert2Uv; 4] {
245271
let eye = match stereo {
246272
StereoMode::RightLeft | StereoMode::BottomTop => (1 - array_index) as f32,
247273
_ => array_index as f32,
248274
};
249275

250-
match stereo {
276+
let mut verts = match stereo {
251277
StereoMode::None => [
252278
Vert2Uv {
253279
in_pos: [0., 0.],
@@ -302,7 +328,13 @@ fn stereo_mode_to_verts(stereo: StereoMode, array_index: usize) -> [Vert2Uv; 4]
302328
in_uv: [1., 0.5 + eye * 0.5],
303329
},
304330
],
331+
};
332+
333+
for vert in &mut verts {
334+
vert.in_uv = transform_uv(vert.in_uv, transform);
305335
}
336+
337+
verts
306338
}
307339

308340
static DMA_ALLOCATOR: OnceLock<Arc<dyn MemoryAllocator>> = OnceLock::new();
@@ -460,7 +492,7 @@ impl WlxCaptureOut {
460492
FrameMeta {
461493
clear: WGfxClearMode::DontCare,
462494
extent: extent_from_format(self.format, config),
463-
transform: affine_from_format(&self.format),
495+
transform: Affine3A::IDENTITY,
464496
format: self.image.format(),
465497
stereo,
466498
}
@@ -585,13 +617,14 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
585617
mouse: frame.mouse,
586618
})
587619
}
588-
WlxFrame::Implicit => {
620+
WlxFrame::Implicit(transform) => {
589621
log::trace!("{}: New Implicit frame", me.name);
590622

591-
let Some((image, format)) = me.dma_exporter.as_ref().unwrap().get_current() else {
623+
let Some((image, mut format)) = me.dma_exporter.as_ref().unwrap().get_current() else {
592624
log::error!("{}: Implicit frame is missing!", me.name);
593625
return None;
594626
};
627+
format.transform = transform;
595628

596629
Some(WlxCaptureOut {
597630
image,
@@ -676,43 +709,26 @@ const fn receive_callback_dummy(_: &DummyDrmExporter, frame: WlxFrame) -> Option
676709
}
677710

678711
fn extent_from_format(fmt: FrameFormat, config: &GeneralConfig) -> [u32; 2] {
712+
let (width, height) = match fmt.transform {
713+
wlx_frame::Transform::Rotated90
714+
| wlx_frame::Transform::Rotated270
715+
| wlx_frame::Transform::Flipped90
716+
| wlx_frame::Transform::Flipped270 => (fmt.height, fmt.width),
717+
_ => (fmt.width, fmt.height),
718+
};
719+
679720
// screens above a certain resolution will have severe aliasing
680721
let height_limit = if config.screen_render_down {
681722
u32::from(config.screen_max_height.min(2560))
682723
} else {
683724
2560
684725
};
685726

686-
let h = fmt.height.min(height_limit);
687-
let w = (fmt.width as f32 / fmt.height as f32 * h as f32) as u32;
727+
let h = height.min(height_limit);
728+
let w = (width as f32 / height as f32 * h as f32) as u32;
688729
[w, h]
689730
}
690731

691-
fn affine_from_format(format: &FrameFormat) -> Affine3A {
692-
const FLIP_X: Vec3 = Vec3 {
693-
x: -1.0,
694-
y: 1.0,
695-
z: 1.0,
696-
};
697-
698-
match format.transform {
699-
wlx_frame::Transform::Rotated90 => Affine3A::from_rotation_z(-PI / 2.0),
700-
wlx_frame::Transform::Rotated180 => Affine3A::from_rotation_z(PI),
701-
wlx_frame::Transform::Rotated270 => Affine3A::from_rotation_z(PI / 2.0),
702-
wlx_frame::Transform::Flipped => Affine3A::from_scale(FLIP_X),
703-
wlx_frame::Transform::Flipped90 => {
704-
Affine3A::from_scale(FLIP_X) * Affine3A::from_rotation_z(-PI / 2.0)
705-
}
706-
wlx_frame::Transform::Flipped180 => {
707-
Affine3A::from_scale(FLIP_X) * Affine3A::from_rotation_z(PI)
708-
}
709-
wlx_frame::Transform::Flipped270 => {
710-
Affine3A::from_scale(FLIP_X) * Affine3A::from_rotation_z(PI / 2.0)
711-
}
712-
_ => Affine3A::IDENTITY,
713-
}
714-
}
715-
716732
macro_rules! new_wlx_capture {
717733
($capture_queue:expr, $capture:expr) => {
718734
if $capture_queue.is_none() {

wayvr/src/overlays/wayvr.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use wgui::{
1919
parser::Fetchable,
2020
widget::{EventResult, label::WidgetLabel},
2121
};
22-
use wlx_capture::frame::MouseMeta;
22+
use wlx_capture::frame::{MouseMeta, Transform};
2323
use wlx_common::{
2424
overlays::{BackendAttrib, BackendAttribValue, StereoMode},
2525
windowing::{OverlayWindowState, Positioning},
@@ -348,10 +348,11 @@ impl OverlayBackend for WvrWindowBackend {
348348

349349
if let Some(pipeline) = self.pipeline.as_mut() {
350350
if self.inner_extent != inner_extent {
351-
pipeline.set_extent(
351+
pipeline.set_layout(
352352
app,
353353
[inner_extent[0] as _, inner_extent[1] as _],
354354
[BORDER_SIZE as _, (BAR_SIZE + BORDER_SIZE) as _],
355+
Transform::Normal,
355356
)?;
356357
self.apply_extent(app, &meta)?;
357358
self.inner_extent = inner_extent;
@@ -362,6 +363,7 @@ impl OverlayBackend for WvrWindowBackend {
362363
app,
363364
self.stereo.unwrap_or(StereoMode::None),
364365
[BORDER_SIZE as _, (BAR_SIZE + BORDER_SIZE) as _],
366+
Transform::Normal,
365367
)?;
366368
self.apply_extent(app, &meta)?;
367369
self.pipeline = Some(pipeline);

wlx-capture/src/frame.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub enum WlxFrame {
1616
Dmabuf(DmabufFrame),
1717
MemFd(MemFdFrame),
1818
MemPtr(MemPtrFrame),
19-
Implicit,
19+
Implicit(Transform),
2020
}
2121

2222
#[derive(Debug, Clone, Copy, Default, PartialEq)]

wlx-capture/src/wlr_screencopy.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ where
267267
// copy_with_damage seems to not work here
268268
proxy.copy(&wl_buffer);
269269

270-
frame_buffer = Some((WlxFrame::Implicit, BufData::Dma { wl_buffer }));
270+
frame_buffer =
271+
Some((WlxFrame::Implicit(transform), BufData::Dma { wl_buffer }));
271272
} else if let Some(ScreenCopyEvent::Buffer {
272273
shm_format,
273274
width,

0 commit comments

Comments
 (0)