Merge pull request #372 from mahkoh/jorth/vulkan-partial-draw
metal: track per-framebuffer damage
This commit is contained in:
commit
3056c9af71
30 changed files with 661 additions and 311 deletions
|
|
@ -11,6 +11,7 @@ use {
|
||||||
create_render_pass, AcquireSync, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture,
|
create_render_pass, AcquireSync, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture,
|
||||||
ReleaseSync, SyncFile,
|
ReleaseSync, SyncFile,
|
||||||
},
|
},
|
||||||
|
rect::Region,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
time::Time,
|
time::Time,
|
||||||
tracy::FrameName,
|
tracy::FrameName,
|
||||||
|
|
@ -30,7 +31,8 @@ use {
|
||||||
|
|
||||||
struct Latched {
|
struct Latched {
|
||||||
pass: GfxRenderPass,
|
pass: GfxRenderPass,
|
||||||
damage: u64,
|
damage_count: u64,
|
||||||
|
damage: Region,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -172,24 +174,24 @@ impl MetalConnector {
|
||||||
Some(b) => b,
|
Some(b) => b,
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
|
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
||||||
|
|
||||||
if self.has_damage.get() > 0 || self.cursor_damage.get() {
|
if self.has_damage.get() > 0 || self.cursor_damage.get() {
|
||||||
node.schedule.commit_cursor();
|
node.schedule.commit_cursor();
|
||||||
}
|
}
|
||||||
self.latch_cursor(&node)?;
|
self.latch_cursor(&node)?;
|
||||||
let cursor_programming = self.compute_cursor_programming();
|
let cursor_programming = self.compute_cursor_programming();
|
||||||
let latched = self.latch(&node);
|
let latched = self.latch(&node, buffer);
|
||||||
node.latched(self.try_async_flip());
|
node.latched(self.try_async_flip());
|
||||||
|
|
||||||
if cursor_programming.is_none() && latched.is_none() {
|
if cursor_programming.is_none() && latched.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
|
||||||
let mut present_fb = None;
|
let mut present_fb = None;
|
||||||
let mut direct_scanout_id = None;
|
let mut direct_scanout_id = None;
|
||||||
if let Some(latched) = &latched {
|
if let Some(latched) = &latched {
|
||||||
let fb = self.prepare_present_fb(buffer, &plane, &latched.pass, true)?;
|
let fb = self.prepare_present_fb(buffer, &plane, latched, true)?;
|
||||||
direct_scanout_id = fb.direct_scanout_data.as_ref().map(|d| d.dma_buf_id);
|
direct_scanout_id = fb.direct_scanout_data.as_ref().map(|d| d.dma_buf_id);
|
||||||
present_fb = Some(fb);
|
present_fb = Some(fb);
|
||||||
}
|
}
|
||||||
|
|
@ -212,12 +214,8 @@ impl MetalConnector {
|
||||||
);
|
);
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
if let Some(dsd_id) = direct_scanout_id {
|
if let Some(dsd_id) = direct_scanout_id {
|
||||||
let fb = self.prepare_present_fb(
|
let fb =
|
||||||
buffer,
|
self.prepare_present_fb(buffer, &plane, latched.as_ref().unwrap(), false)?;
|
||||||
&plane,
|
|
||||||
&latched.as_ref().unwrap().pass,
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
present_fb = Some(fb);
|
present_fb = Some(fb);
|
||||||
self.await_present_fb(present_fb.as_mut()).await;
|
self.await_present_fb(present_fb.as_mut()).await;
|
||||||
res = self.program_connector(
|
res = self.program_connector(
|
||||||
|
|
@ -241,7 +239,14 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let reset_damage = || {
|
||||||
|
for buffer in &*buffers {
|
||||||
|
buffer.damage_queue.clear();
|
||||||
|
}
|
||||||
|
buffers[0].damage_full();
|
||||||
|
};
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
reset_damage();
|
||||||
if let MetalError::Commit(DrmError::Atomic(OsError(c::EACCES))) = e {
|
if let MetalError::Commit(DrmError::Atomic(OsError(c::EACCES))) = e {
|
||||||
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
@ -265,7 +270,10 @@ impl MetalConnector {
|
||||||
self.presentation_is_zero_copy
|
self.presentation_is_zero_copy
|
||||||
.set(fb.direct_scanout_data.is_some());
|
.set(fb.direct_scanout_data.is_some());
|
||||||
if fb.direct_scanout_data.is_none() {
|
if fb.direct_scanout_data.is_none() {
|
||||||
|
buffer.damage_queue.clear();
|
||||||
self.next_buffer.fetch_add(1);
|
self.next_buffer.fetch_add(1);
|
||||||
|
} else {
|
||||||
|
reset_damage();
|
||||||
}
|
}
|
||||||
self.next_framebuffer.set(Some(fb));
|
self.next_framebuffer.set(Some(fb));
|
||||||
}
|
}
|
||||||
|
|
@ -275,7 +283,7 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
self.can_present.set(false);
|
self.can_present.set(false);
|
||||||
if let Some(latched) = latched {
|
if let Some(latched) = latched {
|
||||||
self.has_damage.fetch_sub(latched.damage);
|
self.has_damage.fetch_sub(latched.damage_count);
|
||||||
}
|
}
|
||||||
self.cursor_changed.set(false);
|
self.cursor_changed.set(false);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -487,12 +495,19 @@ impl MetalConnector {
|
||||||
Some(programming)
|
Some(programming)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn latch(&self, node: &Rc<OutputNode>) -> Option<Latched> {
|
fn latch(&self, node: &Rc<OutputNode>, buffer: &RenderBuffer) -> Option<Latched> {
|
||||||
let damage = self.has_damage.get();
|
let damage_count = self.has_damage.get();
|
||||||
if damage == 0 {
|
if damage_count == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
node.global.connector.damaged.set(false);
|
node.global.connector.damaged.set(false);
|
||||||
|
let damage = {
|
||||||
|
node.global.add_visualizer_damage();
|
||||||
|
let damage = &mut *node.global.connector.damage.borrow_mut();
|
||||||
|
buffer.damage_queue.damage(damage);
|
||||||
|
damage.clear();
|
||||||
|
buffer.damage_queue.get()
|
||||||
|
};
|
||||||
let render_hw_cursor = !self.cursor_enabled.get();
|
let render_hw_cursor = !self.cursor_enabled.get();
|
||||||
let mode = node.global.mode.get();
|
let mode = node.global.mode.get();
|
||||||
let pass = create_render_pass(
|
let pass = create_render_pass(
|
||||||
|
|
@ -508,7 +523,11 @@ impl MetalConnector {
|
||||||
node.global.persistent.transform.get(),
|
node.global.persistent.transform.get(),
|
||||||
Some(&self.state.damage_visualizer),
|
Some(&self.state.damage_visualizer),
|
||||||
);
|
);
|
||||||
Some(Latched { pass, damage })
|
Some(Latched {
|
||||||
|
pass,
|
||||||
|
damage_count,
|
||||||
|
damage,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trim_scanout_cache(&self) {
|
fn trim_scanout_cache(&self) {
|
||||||
|
|
@ -692,7 +711,7 @@ impl MetalConnector {
|
||||||
&self,
|
&self,
|
||||||
buffer: &RenderBuffer,
|
buffer: &RenderBuffer,
|
||||||
plane: &Rc<MetalPlane>,
|
plane: &Rc<MetalPlane>,
|
||||||
pass: &GfxRenderPass,
|
latched: &Latched,
|
||||||
try_direct_scanout: bool,
|
try_direct_scanout: bool,
|
||||||
) -> Result<PresentFb, MetalError> {
|
) -> Result<PresentFb, MetalError> {
|
||||||
self.trim_scanout_cache();
|
self.trim_scanout_cache();
|
||||||
|
|
@ -706,7 +725,7 @@ impl MetalConnector {
|
||||||
&& self.dev.is_render_device();
|
&& self.dev.is_render_device();
|
||||||
let mut direct_scanout_data = None;
|
let mut direct_scanout_data = None;
|
||||||
if try_direct_scanout {
|
if try_direct_scanout {
|
||||||
direct_scanout_data = self.prepare_direct_scanout(&pass, plane);
|
direct_scanout_data = self.prepare_direct_scanout(&latched.pass, plane);
|
||||||
}
|
}
|
||||||
let direct_scanout_active = direct_scanout_data.is_some();
|
let direct_scanout_active = direct_scanout_data.is_some();
|
||||||
if self.direct_scanout_active.replace(direct_scanout_active) != direct_scanout_active {
|
if self.direct_scanout_active.replace(direct_scanout_active) != direct_scanout_active {
|
||||||
|
|
@ -723,7 +742,12 @@ impl MetalConnector {
|
||||||
None => {
|
None => {
|
||||||
let sf = buffer
|
let sf = buffer
|
||||||
.render_fb()
|
.render_fb()
|
||||||
.perform_render_pass(AcquireSync::Unnecessary, ReleaseSync::Explicit, pass)
|
.perform_render_pass(
|
||||||
|
AcquireSync::Unnecessary,
|
||||||
|
ReleaseSync::Explicit,
|
||||||
|
&latched.pass,
|
||||||
|
&latched.damage,
|
||||||
|
)
|
||||||
.map_err(MetalError::RenderFrame)?;
|
.map_err(MetalError::RenderFrame)?;
|
||||||
sync_file = buffer.copy_to_dev(sf)?;
|
sync_file = buffer.copy_to_dev(sf)?;
|
||||||
fb = buffer.drm.clone();
|
fb = buffer.drm.clone();
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use {
|
||||||
wl_output::OutputId,
|
wl_output::OutputId,
|
||||||
wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
|
wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
|
||||||
},
|
},
|
||||||
|
rect::{DamageQueue, Rect},
|
||||||
state::State,
|
state::State,
|
||||||
tree::OutputNode,
|
tree::OutputNode,
|
||||||
udev::UdevDevice,
|
udev::UdevDevice,
|
||||||
|
|
@ -2600,12 +2601,26 @@ impl MetalBackend {
|
||||||
ctx: &MetalRenderContext,
|
ctx: &MetalRenderContext,
|
||||||
cursor: bool,
|
cursor: bool,
|
||||||
) -> Result<[RenderBuffer; N], MetalError> {
|
) -> Result<[RenderBuffer; N], MetalError> {
|
||||||
let create =
|
let mut damage_queue = ArrayVec::from(DamageQueue::new::<N>());
|
||||||
|| self.create_scanout_buffer(dev, format, plane_modifiers, width, height, ctx, cursor);
|
let mut create = || {
|
||||||
|
self.create_scanout_buffer(
|
||||||
|
dev,
|
||||||
|
format,
|
||||||
|
plane_modifiers,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
ctx,
|
||||||
|
cursor,
|
||||||
|
damage_queue.pop().unwrap(),
|
||||||
|
)
|
||||||
|
};
|
||||||
let mut array = ArrayVec::<_, N>::new();
|
let mut array = ArrayVec::<_, N>::new();
|
||||||
for _ in 0..N {
|
for _ in 0..N {
|
||||||
array.push(create()?);
|
array.push(create()?);
|
||||||
}
|
}
|
||||||
|
if let Some(buffer) = array.first() {
|
||||||
|
buffer.damage_full();
|
||||||
|
}
|
||||||
Ok(array.into_inner().unwrap())
|
Ok(array.into_inner().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2618,6 +2633,7 @@ impl MetalBackend {
|
||||||
height: i32,
|
height: i32,
|
||||||
render_ctx: &MetalRenderContext,
|
render_ctx: &MetalRenderContext,
|
||||||
cursor: bool,
|
cursor: bool,
|
||||||
|
damage_queue: DamageQueue,
|
||||||
) -> Result<RenderBuffer, MetalError> {
|
) -> Result<RenderBuffer, MetalError> {
|
||||||
let ctx = dev.ctx.get();
|
let ctx = dev.ctx.get();
|
||||||
let dev_gfx_formats = ctx.gfx.formats();
|
let dev_gfx_formats = ctx.gfx.formats();
|
||||||
|
|
@ -2746,7 +2762,8 @@ impl MetalBackend {
|
||||||
};
|
};
|
||||||
Ok(RenderBuffer {
|
Ok(RenderBuffer {
|
||||||
drm: drm_fb,
|
drm: drm_fb,
|
||||||
_dev_bo: dev_bo,
|
damage_queue,
|
||||||
|
dev_bo,
|
||||||
_render_bo: render_bo,
|
_render_bo: render_bo,
|
||||||
dev_fb,
|
dev_fb,
|
||||||
dev_tex,
|
dev_tex,
|
||||||
|
|
@ -2970,7 +2987,8 @@ impl MetalBackend {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RenderBuffer {
|
pub struct RenderBuffer {
|
||||||
pub drm: Rc<DrmFramebuffer>,
|
pub drm: Rc<DrmFramebuffer>,
|
||||||
pub _dev_bo: GbmBo,
|
pub damage_queue: DamageQueue,
|
||||||
|
pub dev_bo: GbmBo,
|
||||||
pub _render_bo: Option<GbmBo>,
|
pub _render_bo: Option<GbmBo>,
|
||||||
// ctx = dev
|
// ctx = dev
|
||||||
// buffer location = dev
|
// buffer location = dev
|
||||||
|
|
@ -3010,6 +3028,12 @@ impl RenderBuffer {
|
||||||
)
|
)
|
||||||
.map_err(MetalError::CopyToOutput)
|
.map_err(MetalError::CopyToOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn damage_full(&self) {
|
||||||
|
let dmabuf = self.dev_bo.dmabuf();
|
||||||
|
let rect = Rect::new_sized_unchecked(0, 0, dmabuf.width, dmabuf.height);
|
||||||
|
self.damage_queue.damage(&[rect]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool {
|
fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -544,7 +544,9 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
drm_dev: None,
|
drm_dev: None,
|
||||||
async_event: Default::default(),
|
async_event: Default::default(),
|
||||||
damaged: Cell::new(false),
|
damaged: Cell::new(false),
|
||||||
|
damage: Default::default(),
|
||||||
needs_vblank_emulation: Cell::new(false),
|
needs_vblank_emulation: Cell::new(false),
|
||||||
|
damage_intersect: Default::default(),
|
||||||
});
|
});
|
||||||
let schedule = Rc::new(OutputSchedule::new(
|
let schedule = Rc::new(OutputSchedule::new(
|
||||||
&state.ring,
|
&state.ring,
|
||||||
|
|
|
||||||
150
src/damage.rs
150
src/damage.rs
|
|
@ -1,14 +1,19 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::AsyncEngine,
|
async_engine::AsyncEngine,
|
||||||
|
fixed::Fixed,
|
||||||
|
ifs::wl_output::WlOutputGlobal,
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
renderer::renderer_base::RendererBase,
|
renderer::renderer_base::RendererBase,
|
||||||
state::State,
|
state::State,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
time::Time,
|
time::Time,
|
||||||
utils::{asyncevent::AsyncEvent, errorfmt::ErrorFmt, timer::TimerFd},
|
utils::{
|
||||||
|
asyncevent::AsyncEvent, errorfmt::ErrorFmt, timer::TimerFd, transform_ext::TransformExt,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
isnt::std_1::primitive::IsntSliceExt,
|
isnt::std_1::primitive::IsntSliceExt,
|
||||||
|
jay_config::video::Transform,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
|
@ -56,6 +61,9 @@ pub async fn visualize_damage(state: Rc<State>) {
|
||||||
fn damage_all(state: &State) {
|
fn damage_all(state: &State) {
|
||||||
for connector in state.connectors.lock().values() {
|
for connector in state.connectors.lock().values() {
|
||||||
if connector.connected.get() {
|
if connector.connected.get() {
|
||||||
|
let damage = &mut *connector.damage.borrow_mut();
|
||||||
|
damage.clear();
|
||||||
|
damage.push(connector.damage_intersect.get());
|
||||||
connector.damage();
|
connector.damage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,10 +134,7 @@ impl DamageVisualizer {
|
||||||
self.color.set(color);
|
self.color.set(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, cursor_rect: &Rect, renderer: &mut RendererBase<'_>) {
|
fn trim(&self) {
|
||||||
if !self.enabled.get() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let now = self.eng.now();
|
let now = self.eng.now();
|
||||||
let entries = &mut *self.entries.borrow_mut();
|
let entries = &mut *self.entries.borrow_mut();
|
||||||
let decay = self.decay.get();
|
let decay = self.decay.get();
|
||||||
|
|
@ -140,6 +145,16 @@ impl DamageVisualizer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, cursor_rect: &Rect, renderer: &mut RendererBase<'_>) {
|
||||||
|
if !self.enabled.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.trim();
|
||||||
|
let now = self.eng.now();
|
||||||
|
let entries = &*self.entries.borrow();
|
||||||
|
let decay = self.decay.get();
|
||||||
let base_color = self.color.get();
|
let base_color = self.color.get();
|
||||||
let mut used = Region::empty();
|
let mut used = Region::empty();
|
||||||
let dx = -cursor_rect.x1();
|
let dx = -cursor_rect.x1();
|
||||||
|
|
@ -156,4 +171,129 @@ impl DamageVisualizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn copy_damage(&self, output: &WlOutputGlobal) {
|
||||||
|
if !self.enabled.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.trim();
|
||||||
|
let entries = &*self.entries.borrow();
|
||||||
|
let pos = output.pos.get();
|
||||||
|
for entry in entries {
|
||||||
|
if entry.rect.intersects(&pos) {
|
||||||
|
output.add_damage_area(&entry.rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct DamageMatrix {
|
||||||
|
transform: Transform,
|
||||||
|
mx: f64,
|
||||||
|
my: f64,
|
||||||
|
dx: f64,
|
||||||
|
dy: f64,
|
||||||
|
smear: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DamageMatrix {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
transform: Default::default(),
|
||||||
|
mx: 1.0,
|
||||||
|
my: 1.0,
|
||||||
|
dx: 0.0,
|
||||||
|
dy: 0.0,
|
||||||
|
smear: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DamageMatrix {
|
||||||
|
pub fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
|
||||||
|
let x1 = rect.x1() - self.smear;
|
||||||
|
let x2 = rect.x2() + self.smear;
|
||||||
|
let y1 = rect.y1() - self.smear;
|
||||||
|
let y2 = rect.y2() + self.smear;
|
||||||
|
let [x1, y1, x2, y2] = match self.transform {
|
||||||
|
Transform::None => [x1, y1, x2, y2],
|
||||||
|
Transform::Rotate90 => [-y2, x1, -y1, x2],
|
||||||
|
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
|
||||||
|
Transform::Rotate270 => [y1, -x2, y2, -x1],
|
||||||
|
Transform::Flip => [-x2, y1, -x1, y2],
|
||||||
|
Transform::FlipRotate90 => [y1, x1, y2, x2],
|
||||||
|
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
|
||||||
|
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
|
||||||
|
};
|
||||||
|
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
|
||||||
|
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
|
||||||
|
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
|
||||||
|
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
|
||||||
|
Rect::new(x1, y1, x2, y2).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
transform: Transform,
|
||||||
|
legacy_scale: i32,
|
||||||
|
buffer_width: i32,
|
||||||
|
buffer_height: i32,
|
||||||
|
viewport: Option<[Fixed; 4]>,
|
||||||
|
dst_width: i32,
|
||||||
|
dst_height: i32,
|
||||||
|
) -> DamageMatrix {
|
||||||
|
let mut buffer_width = buffer_width as f64;
|
||||||
|
let mut buffer_height = buffer_height as f64;
|
||||||
|
let dst_width = dst_width as f64;
|
||||||
|
let dst_height = dst_height as f64;
|
||||||
|
|
||||||
|
let mut mx = 1.0;
|
||||||
|
let mut my = 1.0;
|
||||||
|
if legacy_scale != 1 {
|
||||||
|
let scale_inv = 1.0 / (legacy_scale as f64);
|
||||||
|
mx = scale_inv;
|
||||||
|
my = scale_inv;
|
||||||
|
buffer_width *= scale_inv;
|
||||||
|
buffer_height *= scale_inv;
|
||||||
|
}
|
||||||
|
let (mut buffer_width, mut buffer_height) =
|
||||||
|
transform.maybe_swap((buffer_width, buffer_height));
|
||||||
|
let (mut dx, mut dy) = match transform {
|
||||||
|
Transform::None => (0.0, 0.0),
|
||||||
|
Transform::Rotate90 => (buffer_width, 0.0),
|
||||||
|
Transform::Rotate180 => (buffer_width, buffer_height),
|
||||||
|
Transform::Rotate270 => (0.0, buffer_height),
|
||||||
|
Transform::Flip => (buffer_width, 0.0),
|
||||||
|
Transform::FlipRotate90 => (0.0, 0.0),
|
||||||
|
Transform::FlipRotate180 => (0.0, buffer_height),
|
||||||
|
Transform::FlipRotate270 => (buffer_width, buffer_height),
|
||||||
|
};
|
||||||
|
if let Some([x, y, w, h]) = viewport {
|
||||||
|
dx -= x.to_f64();
|
||||||
|
dy -= y.to_f64();
|
||||||
|
buffer_width = w.to_f64();
|
||||||
|
buffer_height = h.to_f64();
|
||||||
|
}
|
||||||
|
let mut smear = false;
|
||||||
|
if dst_width != buffer_width {
|
||||||
|
let scale = dst_width / buffer_width;
|
||||||
|
mx *= scale;
|
||||||
|
dx *= scale;
|
||||||
|
smear |= dst_width > buffer_width;
|
||||||
|
}
|
||||||
|
if dst_height != buffer_height {
|
||||||
|
let scale = dst_height / buffer_height;
|
||||||
|
my *= scale;
|
||||||
|
dy *= scale;
|
||||||
|
smear |= dst_height > buffer_height;
|
||||||
|
}
|
||||||
|
DamageMatrix {
|
||||||
|
transform,
|
||||||
|
mx,
|
||||||
|
my,
|
||||||
|
dx,
|
||||||
|
dy,
|
||||||
|
smear: smear as _,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -257,12 +257,28 @@ pub enum ResetStatus {
|
||||||
pub trait GfxFramebuffer: Debug {
|
pub trait GfxFramebuffer: Debug {
|
||||||
fn physical_size(&self) -> (i32, i32);
|
fn physical_size(&self) -> (i32, i32);
|
||||||
|
|
||||||
|
fn full_region(&self) -> Region {
|
||||||
|
let (width, height) = self.physical_size();
|
||||||
|
Region::new2(Rect::new_sized_unchecked(0, 0, width, height))
|
||||||
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
&self,
|
&self,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
|
self.render_with_region(acquire_sync, release_sync, ops, clear, &self.full_region())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_with_region(
|
||||||
|
&self,
|
||||||
|
acquire_sync: AcquireSync,
|
||||||
|
release_sync: ReleaseSync,
|
||||||
|
ops: &[GfxApiOpt],
|
||||||
|
clear: Option<&Color>,
|
||||||
|
region: &Region,
|
||||||
) -> Result<Option<SyncFile>, GfxError>;
|
) -> Result<Option<SyncFile>, GfxError>;
|
||||||
|
|
||||||
fn format(&self) -> &'static Format;
|
fn format(&self) -> &'static Format;
|
||||||
|
|
@ -395,8 +411,15 @@ impl dyn GfxFramebuffer {
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
pass: &GfxRenderPass,
|
pass: &GfxRenderPass,
|
||||||
|
region: &Region,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.render(acquire_sync, release_sync, &pass.ops, pass.clear.as_ref())
|
self.render_with_region(
|
||||||
|
acquire_sync,
|
||||||
|
release_sync,
|
||||||
|
&pass.ops,
|
||||||
|
pass.clear.as_ref(),
|
||||||
|
region,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_output(
|
pub fn render_output(
|
||||||
|
|
@ -451,7 +474,7 @@ impl dyn GfxFramebuffer {
|
||||||
transform,
|
transform,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
self.perform_render_pass(acquire_sync, release_sync, &pass)
|
self.perform_render_pass(acquire_sync, release_sync, &pass, &self.full_region())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_hardware_cursor(
|
pub fn render_hardware_cursor(
|
||||||
|
|
|
||||||
|
|
@ -99,12 +99,13 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
(self.gl.width, self.gl.height)
|
(self.gl.width, self.gl.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render_with_region(
|
||||||
&self,
|
&self,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
_release_sync: ReleaseSync,
|
_release_sync: ReleaseSync,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
_region: &Region,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.render(acquire_sync, ops, clear).map_err(|e| e.into())
|
self.render(acquire_sync, ops, clear).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -530,15 +530,16 @@ impl GfxFramebuffer for VulkanImage {
|
||||||
(self.width as _, self.height as _)
|
(self.width as _, self.height as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render_with_region(
|
||||||
&self,
|
&self,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
region: &Region,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.renderer
|
self.renderer
|
||||||
.execute(self, acquire_sync, release_sync, ops, clear)
|
.execute(self, acquire_sync, release_sync, ops, clear, region)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,23 +27,27 @@ use {
|
||||||
VulkanError,
|
VulkanError,
|
||||||
},
|
},
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
|
rect::Region,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
utils::{
|
||||||
|
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, once::Once,
|
||||||
|
stack::Stack,
|
||||||
|
},
|
||||||
video::dmabuf::{dma_buf_export_sync_file, DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE},
|
video::dmabuf::{dma_buf_export_sync_file, DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
ash::{
|
ash::{
|
||||||
vk,
|
vk,
|
||||||
vk::{
|
vk::{
|
||||||
AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, ClearColorValue, ClearValue,
|
AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, ClearAttachment, ClearColorValue,
|
||||||
CommandBuffer, CommandBufferBeginInfo, CommandBufferSubmitInfo,
|
ClearRect, ClearValue, CommandBuffer, CommandBufferBeginInfo, CommandBufferSubmitInfo,
|
||||||
CommandBufferUsageFlags, CopyImageInfo2, DependencyInfoKHR,
|
CommandBufferUsageFlags, CopyImageInfo2, DependencyInfoKHR,
|
||||||
DescriptorBufferBindingInfoEXT, DescriptorImageInfo, DescriptorType, DeviceSize,
|
DescriptorBufferBindingInfoEXT, DescriptorImageInfo, DescriptorType, DeviceSize,
|
||||||
Extent2D, Extent3D, ImageAspectFlags, ImageCopy2, ImageLayout, ImageMemoryBarrier2,
|
Extent2D, Extent3D, ImageAspectFlags, ImageCopy2, ImageLayout, ImageMemoryBarrier2,
|
||||||
ImageSubresourceLayers, ImageSubresourceRange, PipelineBindPoint, PipelineStageFlags2,
|
ImageSubresourceLayers, ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint,
|
||||||
Rect2D, RenderingAttachmentInfo, RenderingInfo, SemaphoreSubmitInfo,
|
PipelineStageFlags2, Rect2D, RenderingAttachmentInfo, RenderingInfo,
|
||||||
SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport, WriteDescriptorSet,
|
SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport,
|
||||||
QUEUE_FAMILY_FOREIGN_EXT,
|
WriteDescriptorSet, QUEUE_FAMILY_FOREIGN_EXT,
|
||||||
},
|
},
|
||||||
Device,
|
Device,
|
||||||
},
|
},
|
||||||
|
|
@ -138,6 +142,17 @@ pub(super) struct Memory {
|
||||||
release_fence: Option<Rc<VulkanFence>>,
|
release_fence: Option<Rc<VulkanFence>>,
|
||||||
release_sync_file: Option<SyncFile>,
|
release_sync_file: Option<SyncFile>,
|
||||||
descriptor_buffer: Option<VulkanDescriptorBuffer>,
|
descriptor_buffer: Option<VulkanDescriptorBuffer>,
|
||||||
|
paint_regions: Vec<PaintRegion>,
|
||||||
|
clear_rects: Vec<ClearRect>,
|
||||||
|
image_copy_regions: Vec<ImageCopy2<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaintRegion {
|
||||||
|
rect: Rect2D,
|
||||||
|
x1: f32,
|
||||||
|
y1: f32,
|
||||||
|
x2: f32,
|
||||||
|
y2: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct PendingFrame {
|
pub(super) struct PendingFrame {
|
||||||
|
|
@ -485,20 +500,31 @@ impl VulkanRenderer {
|
||||||
|
|
||||||
fn begin_rendering(&self, buf: CommandBuffer, fb: &VulkanImage, clear: Option<&Color>) {
|
fn begin_rendering(&self, buf: CommandBuffer, fb: &VulkanImage, clear: Option<&Color>) {
|
||||||
zone!("begin_rendering");
|
zone!("begin_rendering");
|
||||||
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
|
let clear_value = clear.map(|clear| ClearValue {
|
||||||
|
color: ClearColorValue {
|
||||||
|
float32: clear.to_array_srgb(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let load_clear = memory.paint_regions.len() == 1 && {
|
||||||
|
let rect = &memory.paint_regions[0].rect;
|
||||||
|
rect.offset.x == 0
|
||||||
|
&& rect.offset.y == 0
|
||||||
|
&& rect.extent.width == fb.width
|
||||||
|
&& rect.extent.height == fb.height
|
||||||
|
};
|
||||||
|
let (load_clear, manual_clear) = match load_clear {
|
||||||
|
false => (None, clear_value),
|
||||||
|
true => (clear_value, None),
|
||||||
|
};
|
||||||
let rendering_attachment_info = {
|
let rendering_attachment_info = {
|
||||||
let mut rai = RenderingAttachmentInfo::default()
|
let mut rai = RenderingAttachmentInfo::default()
|
||||||
.image_view(fb.render_view.unwrap_or(fb.texture_view))
|
.image_view(fb.render_view.unwrap_or(fb.texture_view))
|
||||||
.image_layout(ImageLayout::GENERAL)
|
.image_layout(ImageLayout::GENERAL)
|
||||||
.load_op(AttachmentLoadOp::LOAD)
|
.load_op(AttachmentLoadOp::LOAD)
|
||||||
.store_op(AttachmentStoreOp::STORE);
|
.store_op(AttachmentStoreOp::STORE);
|
||||||
if let Some(clear) = clear {
|
if let Some(clear) = load_clear {
|
||||||
rai = rai
|
rai = rai.clear_value(clear).load_op(AttachmentLoadOp::CLEAR);
|
||||||
.clear_value(ClearValue {
|
|
||||||
color: ClearColorValue {
|
|
||||||
float32: clear.to_array_srgb(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.load_op(AttachmentLoadOp::CLEAR);
|
|
||||||
}
|
}
|
||||||
rai
|
rai
|
||||||
};
|
};
|
||||||
|
|
@ -515,6 +541,27 @@ impl VulkanRenderer {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device.device.cmd_begin_rendering(buf, &rendering_info);
|
self.device.device.cmd_begin_rendering(buf, &rendering_info);
|
||||||
}
|
}
|
||||||
|
if let Some(clear) = manual_clear {
|
||||||
|
let clear_attachment = ClearAttachment::default()
|
||||||
|
.color_attachment(0)
|
||||||
|
.clear_value(clear)
|
||||||
|
.aspect_mask(ImageAspectFlags::COLOR);
|
||||||
|
memory.clear_rects.clear();
|
||||||
|
for region in &memory.paint_regions {
|
||||||
|
memory.clear_rects.push(ClearRect {
|
||||||
|
rect: region.rect,
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
self.device.device.cmd_clear_attachments(
|
||||||
|
buf,
|
||||||
|
&[clear_attachment],
|
||||||
|
&memory.clear_rects,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_viewport(&self, buf: CommandBuffer, fb: &VulkanImage) {
|
fn set_viewport(&self, buf: CommandBuffer, fb: &VulkanImage) {
|
||||||
|
|
@ -551,6 +598,7 @@ impl VulkanRenderer {
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
zone!("record_draws");
|
zone!("record_draws");
|
||||||
|
let memory = &*self.memory.borrow();
|
||||||
let pipelines = self.get_or_create_pipelines(fb.format.vk_format)?;
|
let pipelines = self.get_or_create_pipelines(fb.format.vk_format)?;
|
||||||
let dev = &self.device.device;
|
let dev = &self.device.device;
|
||||||
let mut current_pipeline = None;
|
let mut current_pipeline = None;
|
||||||
|
|
@ -566,20 +614,27 @@ impl VulkanRenderer {
|
||||||
match opt {
|
match opt {
|
||||||
GfxApiOpt::Sync => {}
|
GfxApiOpt::Sync => {}
|
||||||
GfxApiOpt::FillRect(r) => {
|
GfxApiOpt::FillRect(r) => {
|
||||||
bind(&pipelines.fill);
|
|
||||||
let push = FillPushConstants {
|
let push = FillPushConstants {
|
||||||
pos: r.rect.to_points(),
|
pos: r.rect.to_points(),
|
||||||
color: r.color.to_array_srgb(),
|
color: r.color.to_array_srgb(),
|
||||||
};
|
};
|
||||||
unsafe {
|
for region in &memory.paint_regions {
|
||||||
dev.cmd_push_constants(
|
let mut push = push;
|
||||||
buf,
|
let draw = region.constrain(&mut push.pos, None);
|
||||||
pipelines.fill.pipeline_layout,
|
if !draw {
|
||||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
continue;
|
||||||
0,
|
}
|
||||||
uapi::as_bytes(&push),
|
bind(&pipelines.fill);
|
||||||
);
|
unsafe {
|
||||||
dev.cmd_draw(buf, 4, 1, 0, 0);
|
dev.cmd_push_constants(
|
||||||
|
buf,
|
||||||
|
pipelines.fill.pipeline_layout,
|
||||||
|
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||||
|
0,
|
||||||
|
uapi::as_bytes(&push),
|
||||||
|
);
|
||||||
|
dev.cmd_draw(buf, 4, 1, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GfxApiOpt::CopyTexture(c) => {
|
GfxApiOpt::CopyTexture(c) => {
|
||||||
|
|
@ -602,7 +657,6 @@ impl VulkanRenderer {
|
||||||
false => TexSourceType::Opaque,
|
false => TexSourceType::Opaque,
|
||||||
};
|
};
|
||||||
let pipeline = &pipelines.tex[copy_type][source_type];
|
let pipeline = &pipelines.tex[copy_type][source_type];
|
||||||
bind(pipeline);
|
|
||||||
let push = TexPushConstants {
|
let push = TexPushConstants {
|
||||||
pos: c.target.to_points(),
|
pos: c.target.to_points(),
|
||||||
tex_pos: c.source.to_points(),
|
tex_pos: c.source.to_points(),
|
||||||
|
|
@ -611,36 +665,47 @@ impl VulkanRenderer {
|
||||||
let image_info = DescriptorImageInfo::default()
|
let image_info = DescriptorImageInfo::default()
|
||||||
.image_view(tex.texture_view)
|
.image_view(tex.texture_view)
|
||||||
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
||||||
unsafe {
|
let init = Once::default();
|
||||||
if let Some(db) = &self.device.descriptor_buffer {
|
for region in &memory.paint_regions {
|
||||||
db.cmd_set_descriptor_buffer_offsets(
|
let mut push = push;
|
||||||
buf,
|
let draw = region.constrain(&mut push.pos, Some(&mut push.tex_pos));
|
||||||
PipelineBindPoint::GRAPHICS,
|
if !draw {
|
||||||
pipeline.pipeline_layout,
|
continue;
|
||||||
0,
|
}
|
||||||
&[0],
|
init.exec(|| unsafe {
|
||||||
&[tex.descriptor_buffer_offset.get()],
|
bind(pipeline);
|
||||||
);
|
if let Some(db) = &self.device.descriptor_buffer {
|
||||||
} else {
|
db.cmd_set_descriptor_buffer_offsets(
|
||||||
let write_descriptor_set = WriteDescriptorSet::default()
|
buf,
|
||||||
.descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER)
|
PipelineBindPoint::GRAPHICS,
|
||||||
.image_info(slice::from_ref(&image_info));
|
pipeline.pipeline_layout,
|
||||||
self.device.push_descriptor.cmd_push_descriptor_set(
|
0,
|
||||||
buf,
|
&[0],
|
||||||
PipelineBindPoint::GRAPHICS,
|
&[tex.descriptor_buffer_offset.get()],
|
||||||
pipeline.pipeline_layout,
|
);
|
||||||
0,
|
} else {
|
||||||
slice::from_ref(&write_descriptor_set),
|
let write_descriptor_set = WriteDescriptorSet::default()
|
||||||
);
|
.descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER)
|
||||||
|
.image_info(slice::from_ref(&image_info));
|
||||||
|
self.device.push_descriptor.cmd_push_descriptor_set(
|
||||||
|
buf,
|
||||||
|
PipelineBindPoint::GRAPHICS,
|
||||||
|
pipeline.pipeline_layout,
|
||||||
|
0,
|
||||||
|
slice::from_ref(&write_descriptor_set),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unsafe {
|
||||||
|
dev.cmd_push_constants(
|
||||||
|
buf,
|
||||||
|
pipeline.pipeline_layout,
|
||||||
|
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||||
|
0,
|
||||||
|
uapi::as_bytes(&push),
|
||||||
|
);
|
||||||
|
dev.cmd_draw(buf, 4, 1, 0, 0);
|
||||||
}
|
}
|
||||||
dev.cmd_push_constants(
|
|
||||||
buf,
|
|
||||||
pipeline.pipeline_layout,
|
|
||||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
|
||||||
0,
|
|
||||||
uapi::as_bytes(&push),
|
|
||||||
);
|
|
||||||
dev.cmd_draw(buf, 4, 1, 0, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -696,20 +761,33 @@ impl VulkanRenderer {
|
||||||
.layer_count(1)
|
.layer_count(1)
|
||||||
.base_array_layer(0)
|
.base_array_layer(0)
|
||||||
.mip_level(0);
|
.mip_level(0);
|
||||||
let image_copy = ImageCopy2::default()
|
memory.image_copy_regions.clear();
|
||||||
.src_subresource(image_subresource_layers)
|
for region in &memory.paint_regions {
|
||||||
.dst_subresource(image_subresource_layers)
|
let offset = Offset3D {
|
||||||
.extent(Extent3D {
|
x: region.rect.offset.x,
|
||||||
width: fb.width,
|
y: region.rect.offset.y,
|
||||||
height: fb.height,
|
z: 0,
|
||||||
|
};
|
||||||
|
let extent = Extent3D {
|
||||||
|
width: region.rect.extent.width,
|
||||||
|
height: region.rect.extent.height,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
});
|
};
|
||||||
|
memory.image_copy_regions.push(
|
||||||
|
ImageCopy2::default()
|
||||||
|
.src_subresource(image_subresource_layers)
|
||||||
|
.dst_subresource(image_subresource_layers)
|
||||||
|
.src_offset(offset)
|
||||||
|
.dst_offset(offset)
|
||||||
|
.extent(extent),
|
||||||
|
);
|
||||||
|
}
|
||||||
let copy_image_info = CopyImageInfo2::default()
|
let copy_image_info = CopyImageInfo2::default()
|
||||||
.src_image(fb.image)
|
.src_image(fb.image)
|
||||||
.src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
|
.src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
|
||||||
.dst_image(bridge.dmabuf_image)
|
.dst_image(bridge.dmabuf_image)
|
||||||
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||||
.regions(slice::from_ref(&image_copy));
|
.regions(&memory.image_copy_regions);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device.device.cmd_copy_image2(buf, ©_image_info);
|
self.device.device.cmd_copy_image2(buf, ©_image_info);
|
||||||
}
|
}
|
||||||
|
|
@ -965,9 +1043,10 @@ impl VulkanRenderer {
|
||||||
fb_release_sync: ReleaseSync,
|
fb_release_sync: ReleaseSync,
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
region: &Region,
|
||||||
) -> Result<Option<SyncFile>, VulkanError> {
|
) -> Result<Option<SyncFile>, VulkanError> {
|
||||||
zone!("execute");
|
zone!("execute");
|
||||||
let res = self.try_execute(fb, fb_acquire_sync, fb_release_sync, opts, clear);
|
let res = self.try_execute(fb, fb_acquire_sync, fb_release_sync, opts, clear, region);
|
||||||
let sync_file = {
|
let sync_file = {
|
||||||
let mut memory = self.memory.borrow_mut();
|
let mut memory = self.memory.borrow_mut();
|
||||||
memory.textures.clear();
|
memory.textures.clear();
|
||||||
|
|
@ -989,6 +1068,39 @@ impl VulkanRenderer {
|
||||||
Ok(semaphore)
|
Ok(semaphore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_paint_regions(&self, fb: &VulkanImage, region: &Region) {
|
||||||
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
|
memory.paint_regions.clear();
|
||||||
|
for rect in region.rects() {
|
||||||
|
let x1 = rect.x1().max(0);
|
||||||
|
let y1 = rect.y1().max(0);
|
||||||
|
let x2 = rect.x2();
|
||||||
|
let y2 = rect.y2();
|
||||||
|
if x1 as u32 > fb.width || y1 as u32 > fb.height || x2 <= 0 || y2 <= 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let x2 = x2.min(fb.width as i32);
|
||||||
|
let y2 = y2.min(fb.height as i32);
|
||||||
|
let to_fb = |c: i32, max: u32| 2.0 * (c as f32 / max as f32) - 1.0;
|
||||||
|
memory.paint_regions.push(PaintRegion {
|
||||||
|
rect: Rect2D {
|
||||||
|
offset: Offset2D {
|
||||||
|
x: x1 as _,
|
||||||
|
y: y1 as _,
|
||||||
|
},
|
||||||
|
extent: Extent2D {
|
||||||
|
width: (x2 - x1) as u32,
|
||||||
|
height: (y2 - y1) as u32,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x1: to_fb(x1, fb.width),
|
||||||
|
x2: to_fb(x2, fb.width),
|
||||||
|
y1: to_fb(y1, fb.height),
|
||||||
|
y2: to_fb(y2, fb.height),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn try_execute(
|
fn try_execute(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
fb: &VulkanImage,
|
fb: &VulkanImage,
|
||||||
|
|
@ -996,8 +1108,10 @@ impl VulkanRenderer {
|
||||||
fb_release_sync: ReleaseSync,
|
fb_release_sync: ReleaseSync,
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
region: &Region,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
self.check_defunct()?;
|
self.check_defunct()?;
|
||||||
|
self.create_paint_regions(fb, region);
|
||||||
let buf = self.gfx_command_buffers.allocate()?;
|
let buf = self.gfx_command_buffers.allocate()?;
|
||||||
self.collect_memory(opts);
|
self.collect_memory(opts);
|
||||||
self.begin_command_buffer(buf.buffer)?;
|
self.begin_command_buffer(buf.buffer)?;
|
||||||
|
|
@ -1123,3 +1237,51 @@ async fn await_release(
|
||||||
}
|
}
|
||||||
renderer.pending_frames.remove(&frame.point);
|
renderer.pending_frames.remove(&frame.point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaintRegion {
|
||||||
|
fn constrain(&self, pos: &mut [[f32; 2]; 4], tex_pos: Option<&mut [[f32; 2]; 4]>) -> bool {
|
||||||
|
zone!("constrain");
|
||||||
|
let mut npos = *pos;
|
||||||
|
for [x, y] in &mut npos {
|
||||||
|
*x = x.clamp(self.x1, self.x2);
|
||||||
|
*y = y.clamp(self.y1, self.y2);
|
||||||
|
}
|
||||||
|
if npos == *pos {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if npos[0] == npos[1] && npos[2] == npos[3] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if npos[0] == npos[2] && npos[1] == npos[3] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Some(tp) = tex_pos {
|
||||||
|
let mut ntp = *tp;
|
||||||
|
for i in 0..4 {
|
||||||
|
if npos[i] == pos[i] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
macro_rules! sub {
|
||||||
|
($l:expr, $r:expr) => {
|
||||||
|
[$l[0] - $r[0], $l[1] - $r[1]]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let dx = sub!(npos[i], pos[i]);
|
||||||
|
let dy = sub!(pos[(i + 1) & 3], pos[i]);
|
||||||
|
let dz = sub!(pos[(i + 2) & 3], pos[i]);
|
||||||
|
let det = 1.0 / (dy[0] * dz[1] - dy[1] * dz[0]);
|
||||||
|
let alpha = [
|
||||||
|
(dx[0] * dz[1] - dx[1] * dz[0]) * det,
|
||||||
|
(dx[1] * dy[0] - dx[0] * dy[1]) * det,
|
||||||
|
];
|
||||||
|
let dy = sub!(tp[(i + 1) & 3], tp[i]);
|
||||||
|
let dz = sub!(tp[(i + 2) & 3], tp[i]);
|
||||||
|
ntp[i][0] += alpha[0] * dy[0] + alpha[1] * dz[0];
|
||||||
|
ntp[i][1] += alpha[0] * dy[1] + alpha[1] * dz[1];
|
||||||
|
}
|
||||||
|
*tp = ntp;
|
||||||
|
}
|
||||||
|
*pos = npos;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
backend,
|
backend,
|
||||||
client::{Client, ClientError, ClientId},
|
client::{Client, ClientError, ClientId},
|
||||||
|
damage::DamageMatrix,
|
||||||
format::{Format, XRGB8888},
|
format::{Format, XRGB8888},
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1},
|
ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1},
|
||||||
|
|
@ -55,7 +56,7 @@ const MODE_PREFERRED: u32 = 2;
|
||||||
|
|
||||||
pub struct WlOutputGlobal {
|
pub struct WlOutputGlobal {
|
||||||
pub name: GlobalName,
|
pub name: GlobalName,
|
||||||
pub _state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
pub connector: Rc<ConnectorData>,
|
pub connector: Rc<ConnectorData>,
|
||||||
pub pos: Cell<Rect>,
|
pub pos: Cell<Rect>,
|
||||||
pub output_id: Rc<OutputId>,
|
pub output_id: Rc<OutputId>,
|
||||||
|
|
@ -71,6 +72,7 @@ pub struct WlOutputGlobal {
|
||||||
pub legacy_scale: Cell<u32>,
|
pub legacy_scale: Cell<u32>,
|
||||||
pub persistent: Rc<PersistentOutputState>,
|
pub persistent: Rc<PersistentOutputState>,
|
||||||
pub opt: Rc<OutputGlobalOpt>,
|
pub opt: Rc<OutputGlobalOpt>,
|
||||||
|
pub damage_matrix: Cell<DamageMatrix>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -151,9 +153,9 @@ impl WlOutputGlobal {
|
||||||
persistent_state.transform.get(),
|
persistent_state.transform.get(),
|
||||||
scale,
|
scale,
|
||||||
);
|
);
|
||||||
Self {
|
let global = Self {
|
||||||
name,
|
name,
|
||||||
_state: state.clone(),
|
state: state.clone(),
|
||||||
connector: connector.clone(),
|
connector: connector.clone(),
|
||||||
pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
|
pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
|
||||||
output_id: output_id.clone(),
|
output_id: output_id.clone(),
|
||||||
|
|
@ -169,7 +171,10 @@ impl WlOutputGlobal {
|
||||||
legacy_scale: Cell::new(scale.round_up()),
|
legacy_scale: Cell::new(scale.round_up()),
|
||||||
persistent: persistent_state.clone(),
|
persistent: persistent_state.clone(),
|
||||||
opt: Default::default(),
|
opt: Default::default(),
|
||||||
}
|
damage_matrix: Default::default(),
|
||||||
|
};
|
||||||
|
global.update_damage_matrix();
|
||||||
|
global
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> Rect {
|
pub fn position(&self) -> Rect {
|
||||||
|
|
@ -253,6 +258,40 @@ impl WlOutputGlobal {
|
||||||
.get()
|
.get()
|
||||||
.maybe_swap((mode.width, mode.height))
|
.maybe_swap((mode.width, mode.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_damage_matrix(&self) {
|
||||||
|
let pos = self.pos.get();
|
||||||
|
let mode = self.mode.get();
|
||||||
|
let matrix = DamageMatrix::new(
|
||||||
|
self.persistent.transform.get().inverse(),
|
||||||
|
1,
|
||||||
|
pos.width(),
|
||||||
|
pos.height(),
|
||||||
|
None,
|
||||||
|
mode.width,
|
||||||
|
mode.height,
|
||||||
|
);
|
||||||
|
self.damage_matrix.set(matrix);
|
||||||
|
self.connector
|
||||||
|
.damage_intersect
|
||||||
|
.set(Rect::new_sized_unchecked(0, 0, mode.width, mode.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_damage_area(&self, area: &Rect) {
|
||||||
|
let pos = self.pos.get();
|
||||||
|
let rect = area.move_(-pos.x1(), -pos.y1());
|
||||||
|
let mut rect = self.damage_matrix.get().apply(0, 0, rect);
|
||||||
|
let damage = &mut *self.connector.damage.borrow_mut();
|
||||||
|
const MAX_CONNECTOR_DAMAGE: usize = 32;
|
||||||
|
if damage.len() >= MAX_CONNECTOR_DAMAGE {
|
||||||
|
rect = rect.union(damage.pop().unwrap());
|
||||||
|
}
|
||||||
|
damage.push(rect.intersect(self.connector.damage_intersect.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_visualizer_damage(&self) {
|
||||||
|
self.state.damage_visualizer.copy_damage(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global_base!(WlOutputGlobal, WlOutput, WlOutputError);
|
global_base!(WlOutputGlobal, WlOutput, WlOutputError);
|
||||||
|
|
|
||||||
|
|
@ -923,7 +923,7 @@ impl WlSeatGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus_toplevel(self: &Rc<Self>, n: Rc<dyn ToplevelNode>) {
|
pub fn focus_toplevel(self: &Rc<Self>, n: Rc<dyn ToplevelNode>) {
|
||||||
let node = match n.tl_focus_child(self.id) {
|
let node = match n.tl_focus_child() {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
_ => n.tl_into_node(),
|
_ => n.tl_into_node(),
|
||||||
};
|
};
|
||||||
|
|
@ -1153,7 +1153,7 @@ impl WlSeatGlobal {
|
||||||
});
|
});
|
||||||
self.surface_pointer_frame(surface);
|
self.surface_pointer_frame(surface);
|
||||||
if pressed {
|
if pressed {
|
||||||
if let Some(node) = surface.get_focus_node(self.id) {
|
if let Some(node) = surface.get_focus_node() {
|
||||||
self.focus_node_with_serial(node, serial);
|
self.focus_node_with_serial(node, serial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1372,7 +1372,7 @@ impl WlSeatGlobal {
|
||||||
self.surface_touch_event(Version::ALL, surface, |t| {
|
self.surface_touch_event(Version::ALL, surface, |t| {
|
||||||
t.send_down(serial, time, surface.id, id, x, y)
|
t.send_down(serial, time, surface.id, id, x, y)
|
||||||
});
|
});
|
||||||
if let Some(node) = surface.get_focus_node(self.id) {
|
if let Some(node) = surface.get_focus_node() {
|
||||||
self.focus_node_with_serial(node, serial);
|
self.focus_node_with_serial(node, serial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ impl TabletTool {
|
||||||
});
|
});
|
||||||
if state == ToolButtonState::Pressed {
|
if state == ToolButtonState::Pressed {
|
||||||
n.client.focus_stealing_serial.set(Some(serial.get()));
|
n.client.focus_stealing_serial.set(Some(serial.get()));
|
||||||
if let Some(node) = n.get_focus_node(self.tablet.seat.id) {
|
if let Some(node) = n.get_focus_node() {
|
||||||
self.tablet.seat.focus_node_with_serial(node, serial.get());
|
self.tablet.seat.focus_node_with_serial(node, serial.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +261,7 @@ impl TabletTool {
|
||||||
if let Some(changes) = changes {
|
if let Some(changes) = changes {
|
||||||
if changes.down == Some(true) {
|
if changes.down == Some(true) {
|
||||||
n.client.focus_stealing_serial.set(Some(serial.get()));
|
n.client.focus_stealing_serial.set(Some(serial.get()));
|
||||||
if let Some(node) = n.get_focus_node(self.tablet.seat.id) {
|
if let Some(node) = n.get_focus_node() {
|
||||||
self.tablet.seat.focus_node_with_serial(node, serial.get());
|
self.tablet.seat.focus_node_with_serial(node, serial.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ impl ZwpInputMethodV2RequestHandler for ZwpInputMethodV2 {
|
||||||
version: self.version,
|
version: self.version,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
positioning_scheduled: Cell::new(false),
|
positioning_scheduled: Cell::new(false),
|
||||||
|
was_on_screen: Default::default(),
|
||||||
});
|
});
|
||||||
track!(self.client, popup);
|
track!(self.client, popup);
|
||||||
self.client.add_client_obj(&popup)?;
|
self.client.add_client_obj(&popup)?;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use {
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
cursor_user::{CursorUser, CursorUserId},
|
cursor_user::{CursorUser, CursorUserId},
|
||||||
|
damage::DamageMatrix,
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
|
|
@ -777,9 +778,9 @@ impl WlSurface {
|
||||||
Ok(cursor)
|
Ok(cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_focus_node(&self, seat: SeatId) -> Option<Rc<dyn Node>> {
|
pub fn get_focus_node(&self) -> Option<Rc<dyn Node>> {
|
||||||
match self.toplevel.get() {
|
match self.toplevel.get() {
|
||||||
Some(tl) if tl.tl_accepts_keyboard_focus() => tl.tl_focus_child(seat),
|
Some(tl) if tl.tl_accepts_keyboard_focus() => tl.tl_focus_child(),
|
||||||
Some(_) => None,
|
Some(_) => None,
|
||||||
_ => self.ext.get().focus_node(),
|
_ => self.ext.get().focus_node(),
|
||||||
}
|
}
|
||||||
|
|
@ -1591,18 +1592,6 @@ impl WlSurface {
|
||||||
ss.surface.detach_node(set_invisible);
|
ss.surface.detach_node(set_invisible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(tl) = self.toplevel.get() {
|
|
||||||
let data = tl.tl_data();
|
|
||||||
let mut remove = vec![];
|
|
||||||
for (seat, s) in data.focus_node.iter() {
|
|
||||||
if s.node_id() == self.node_id() {
|
|
||||||
remove.push(seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for seat in remove {
|
|
||||||
data.focus_node.remove(&seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.seat_state.destroy_node(self);
|
self.seat_state.destroy_node(self);
|
||||||
if self.visible.get() && self.toplevel.is_none() {
|
if self.visible.get() && self.toplevel.is_none() {
|
||||||
self.client.state.damage(self.buffer_abs_pos.get());
|
self.client.state.damage(self.buffer_abs_pos.get());
|
||||||
|
|
@ -1801,9 +1790,14 @@ impl Node for WlSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_on_focus(self: Rc<Self>, seat: &WlSeatGlobal) {
|
fn node_on_focus(self: Rc<Self>, seat: &WlSeatGlobal) {
|
||||||
if let Some(tl) = self.toplevel.get() {
|
if let Some(xsurface) = self.ext.get().into_xsurface() {
|
||||||
tl.tl_data().focus_node.insert(seat.id(), self.clone());
|
if let Some(window) = xsurface.xwindow.get() {
|
||||||
tl.tl_on_activate();
|
self.client
|
||||||
|
.state
|
||||||
|
.xwayland
|
||||||
|
.queue
|
||||||
|
.push(XWaylandEvent::Activate(window.data.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
seat.focus_surface(&self);
|
seat.focus_surface(&self);
|
||||||
}
|
}
|
||||||
|
|
@ -2034,117 +2028,6 @@ efrom!(WlSurfaceError, XdgSurfaceError);
|
||||||
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
||||||
efrom!(WlSurfaceError, CommitTimelineError);
|
efrom!(WlSurfaceError, CommitTimelineError);
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
struct DamageMatrix {
|
|
||||||
transform: Transform,
|
|
||||||
mx: f64,
|
|
||||||
my: f64,
|
|
||||||
dx: f64,
|
|
||||||
dy: f64,
|
|
||||||
smear: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DamageMatrix {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
transform: Default::default(),
|
|
||||||
mx: 1.0,
|
|
||||||
my: 1.0,
|
|
||||||
dx: 0.0,
|
|
||||||
dy: 0.0,
|
|
||||||
smear: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DamageMatrix {
|
|
||||||
fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
|
|
||||||
let x1 = rect.x1() - self.smear;
|
|
||||||
let x2 = rect.x2() + self.smear;
|
|
||||||
let y1 = rect.y1() - self.smear;
|
|
||||||
let y2 = rect.y2() + self.smear;
|
|
||||||
let [x1, y1, x2, y2] = match self.transform {
|
|
||||||
Transform::None => [x1, y1, x2, y2],
|
|
||||||
Transform::Rotate90 => [-y2, x1, -y1, x2],
|
|
||||||
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
|
|
||||||
Transform::Rotate270 => [y1, -x2, y2, -x1],
|
|
||||||
Transform::Flip => [-x2, y1, -x1, y2],
|
|
||||||
Transform::FlipRotate90 => [y1, x1, y2, x2],
|
|
||||||
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
|
|
||||||
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
|
|
||||||
};
|
|
||||||
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
|
|
||||||
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
|
|
||||||
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
|
|
||||||
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
|
|
||||||
Rect::new(x1, y1, x2, y2).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
|
||||||
transform: Transform,
|
|
||||||
legacy_scale: i32,
|
|
||||||
buffer_width: i32,
|
|
||||||
buffer_height: i32,
|
|
||||||
viewport: Option<[Fixed; 4]>,
|
|
||||||
dst_width: i32,
|
|
||||||
dst_height: i32,
|
|
||||||
) -> DamageMatrix {
|
|
||||||
let mut buffer_width = buffer_width as f64;
|
|
||||||
let mut buffer_height = buffer_height as f64;
|
|
||||||
let dst_width = dst_width as f64;
|
|
||||||
let dst_height = dst_height as f64;
|
|
||||||
|
|
||||||
let mut mx = 1.0;
|
|
||||||
let mut my = 1.0;
|
|
||||||
if legacy_scale != 1 {
|
|
||||||
let scale_inv = 1.0 / (legacy_scale as f64);
|
|
||||||
mx = scale_inv;
|
|
||||||
my = scale_inv;
|
|
||||||
buffer_width *= scale_inv;
|
|
||||||
buffer_height *= scale_inv;
|
|
||||||
}
|
|
||||||
let (mut buffer_width, mut buffer_height) =
|
|
||||||
transform.maybe_swap((buffer_width, buffer_height));
|
|
||||||
let (mut dx, mut dy) = match transform {
|
|
||||||
Transform::None => (0.0, 0.0),
|
|
||||||
Transform::Rotate90 => (buffer_width, 0.0),
|
|
||||||
Transform::Rotate180 => (buffer_width, buffer_height),
|
|
||||||
Transform::Rotate270 => (0.0, buffer_height),
|
|
||||||
Transform::Flip => (buffer_width, 0.0),
|
|
||||||
Transform::FlipRotate90 => (0.0, 0.0),
|
|
||||||
Transform::FlipRotate180 => (0.0, buffer_height),
|
|
||||||
Transform::FlipRotate270 => (buffer_width, buffer_height),
|
|
||||||
};
|
|
||||||
if let Some([x, y, w, h]) = viewport {
|
|
||||||
dx -= x.to_f64();
|
|
||||||
dy -= y.to_f64();
|
|
||||||
buffer_width = w.to_f64();
|
|
||||||
buffer_height = h.to_f64();
|
|
||||||
}
|
|
||||||
let mut smear = false;
|
|
||||||
if dst_width != buffer_width {
|
|
||||||
let scale = dst_width / buffer_width;
|
|
||||||
mx *= scale;
|
|
||||||
dx *= scale;
|
|
||||||
smear |= dst_width > buffer_width;
|
|
||||||
}
|
|
||||||
if dst_height != buffer_height {
|
|
||||||
let scale = dst_height / buffer_height;
|
|
||||||
my *= scale;
|
|
||||||
dy *= scale;
|
|
||||||
smear |= dst_height > buffer_height;
|
|
||||||
}
|
|
||||||
DamageMatrix {
|
|
||||||
transform,
|
|
||||||
mx,
|
|
||||||
my,
|
|
||||||
dx,
|
|
||||||
dy,
|
|
||||||
smear: smear as _,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VblankListener for WlSurface {
|
impl VblankListener for WlSurface {
|
||||||
fn after_vblank(self: Rc<Self>) {
|
fn after_vblank(self: Rc<Self>) {
|
||||||
if self.visible.get() {
|
if self.visible.get() {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use {
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
tree::Node,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
linkedlist::{LinkedNode, NodeRef},
|
linkedlist::{LinkedNode, NodeRef},
|
||||||
|
|
@ -257,6 +258,18 @@ impl WlSubsurface {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn damage(&self) {
|
||||||
|
if !self.surface.visible.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (x, y) = self.surface.buffer_abs_pos.get().position();
|
||||||
|
let mut rect = self.surface.extents.get().move_(x, y);
|
||||||
|
if let Some(tl) = self.surface.toplevel.get() {
|
||||||
|
rect = rect.intersect(tl.node_absolute_position());
|
||||||
|
}
|
||||||
|
self.surface.client.state.damage(rect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WlSubsurfaceRequestHandler for WlSubsurface {
|
impl WlSubsurfaceRequestHandler for WlSubsurface {
|
||||||
|
|
@ -352,12 +365,13 @@ impl SurfaceExt for WlSubsurface {
|
||||||
if self.had_buffer.replace(has_buffer) != has_buffer {
|
if self.had_buffer.replace(has_buffer) != has_buffer {
|
||||||
if has_buffer {
|
if has_buffer {
|
||||||
if self.parent.visible.get() {
|
if self.parent.visible.get() {
|
||||||
let (x, y) = self.surface.buffer_abs_pos.get().position();
|
|
||||||
let extents = self.surface.extents.get();
|
|
||||||
self.surface.client.state.damage(extents.move_(x, y));
|
|
||||||
self.surface.set_visible(true);
|
self.surface.set_visible(true);
|
||||||
|
self.damage();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if self.surface.toplevel.is_some() {
|
||||||
|
self.damage();
|
||||||
|
}
|
||||||
self.surface.destroy_node();
|
self.surface.destroy_node();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -375,6 +389,10 @@ impl SurfaceExt for WlSubsurface {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn focus_node(&self) -> Option<Rc<dyn Node>> {
|
||||||
|
self.parent.ext.get().focus_node()
|
||||||
|
}
|
||||||
|
|
||||||
fn consume_pending_child(
|
fn consume_pending_child(
|
||||||
&self,
|
&self,
|
||||||
surface: &WlSurface,
|
surface: &WlSurface,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use {
|
||||||
SurfaceExt, WlSurface, WlSurfaceError,
|
SurfaceExt, WlSurface, WlSurfaceError,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
tree::ToplevelNode,
|
tree::{Node, ToplevelNode, ToplevelNodeBase},
|
||||||
utils::clonecell::CloneCell,
|
utils::clonecell::CloneCell,
|
||||||
xwayland::XWaylandEvent,
|
xwayland::XWaylandEvent,
|
||||||
},
|
},
|
||||||
|
|
@ -58,6 +58,15 @@ impl SurfaceExt for XSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn focus_node(&self) -> Option<Rc<dyn Node>> {
|
||||||
|
if let Some(xwindow) = self.xwindow.get() {
|
||||||
|
if xwindow.tl_accepts_keyboard_focus() {
|
||||||
|
return Some(xwindow.x.surface.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn into_xsurface(self: Rc<Self>) -> Option<Rc<XSurface>> {
|
fn into_xsurface(self: Rc<Self>) -> Option<Rc<XSurface>> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use {
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal},
|
wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
|
||||||
wl_surface::{x_surface::XSurface, WlSurface, WlSurfaceError},
|
wl_surface::{x_surface::XSurface, WlSurface, WlSurfaceError},
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
|
|
@ -222,7 +222,7 @@ impl Xwindow {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
slf.x.xwindow.set(Some(slf.clone()));
|
slf.x.xwindow.set(Some(slf.clone()));
|
||||||
slf.x.surface.set_toplevel(Some(slf.clone()));
|
slf.update_toplevel();
|
||||||
Ok(slf)
|
Ok(slf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,9 +296,25 @@ impl Xwindow {
|
||||||
Change::None => {}
|
Change::None => {}
|
||||||
}
|
}
|
||||||
self.data.state.tree_changed();
|
self.data.state.tree_changed();
|
||||||
if override_redirect {
|
self.damage_override_redirect();
|
||||||
self.data.state.damage(self.data.info.pending_extents.get());
|
}
|
||||||
|
|
||||||
|
fn damage_override_redirect(&self) {
|
||||||
|
if !self.data.info.override_redirect.get() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
let extents = self.x.surface.extents.get();
|
||||||
|
let (x, y) = self.x.surface.buffer_abs_pos.get().position();
|
||||||
|
let extents = extents.move_(x, y);
|
||||||
|
self.data.state.damage(extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_toplevel(self: &Rc<Self>) {
|
||||||
|
let mut toplevel = None;
|
||||||
|
if !self.data.info.override_redirect.get() {
|
||||||
|
toplevel = Some(self.clone() as _);
|
||||||
|
}
|
||||||
|
self.x.surface.set_toplevel(toplevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -398,15 +414,7 @@ impl ToplevelNodeBase for Xwindow {
|
||||||
&& self.data.info.input_model.get() != XInputModel::None
|
&& self.data.info.input_model.get() != XInputModel::None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_on_activate(&self) {
|
fn tl_focus_child(&self) -> Option<Rc<dyn Node>> {
|
||||||
self.data
|
|
||||||
.state
|
|
||||||
.xwayland
|
|
||||||
.queue
|
|
||||||
.push(XWaylandEvent::Activate(self.data.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tl_focus_child(&self, _seat: SeatId) -> Option<Rc<dyn Node>> {
|
|
||||||
Some(self.x.surface.clone())
|
Some(self.x.surface.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -419,8 +427,6 @@ impl ToplevelNodeBase for Xwindow {
|
||||||
let old = self.data.info.extents.replace(*rect);
|
let old = self.data.info.extents.replace(*rect);
|
||||||
if old != *rect {
|
if old != *rect {
|
||||||
if self.data.info.override_redirect.get() {
|
if self.data.info.override_redirect.get() {
|
||||||
self.data.state.damage(old);
|
|
||||||
self.data.state.damage(*rect);
|
|
||||||
let (x, y) = rect.center();
|
let (x, y) = rect.center();
|
||||||
let output = self.data.state.find_closest_output(x, y).0;
|
let output = self.data.state.find_closest_output(x, y).0;
|
||||||
self.x.surface.set_output(&output);
|
self.x.surface.set_output(&output);
|
||||||
|
|
@ -482,9 +488,7 @@ impl StackedNode for Xwindow {
|
||||||
stacked_node_impl!();
|
stacked_node_impl!();
|
||||||
|
|
||||||
fn stacked_set_visible(&self, visible: bool) {
|
fn stacked_set_visible(&self, visible: bool) {
|
||||||
let extents = self.x.surface.extents.get();
|
self.damage_override_redirect();
|
||||||
let (x, y) = self.x.surface.buffer_abs_pos.get().position();
|
|
||||||
self.data.state.damage(extents.move_(x, y));
|
|
||||||
self.tl_set_visible(visible);
|
self.tl_set_visible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use {
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
ifs::{
|
ifs::{
|
||||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||||
wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal},
|
wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
|
||||||
wl_surface::{
|
wl_surface::{
|
||||||
xdg_surface::{
|
xdg_surface::{
|
||||||
xdg_toplevel::xdg_dialog_v1::XdgDialogV1, XdgSurface, XdgSurfaceError,
|
xdg_toplevel::xdg_dialog_v1::XdgDialogV1, XdgSurface, XdgSurfaceError,
|
||||||
|
|
@ -582,7 +582,7 @@ impl ToplevelNodeBase for XdgToplevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_focus_child(&self, _seat: SeatId) -> Option<Rc<dyn Node>> {
|
fn tl_focus_child(&self) -> Option<Rc<dyn Node>> {
|
||||||
Some(self.xdg.surface.clone())
|
Some(self.xdg.surface.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ pub struct ZwpInputPopupSurfaceV2 {
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
pub positioning_scheduled: Cell<bool>,
|
pub positioning_scheduled: Cell<bool>,
|
||||||
|
pub was_on_screen: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceExt for ZwpInputPopupSurfaceV2 {
|
impl SurfaceExt for ZwpInputPopupSurfaceV2 {
|
||||||
|
|
@ -57,6 +58,7 @@ impl ZwpInputPopupSurfaceV2 {
|
||||||
&& self.client.state.root_visible();
|
&& self.client.state.root_visible();
|
||||||
self.surface.set_visible(is_visible);
|
self.surface.set_visible(is_visible);
|
||||||
if was_visible != is_visible {
|
if was_visible != is_visible {
|
||||||
|
self.was_on_screen.set(false);
|
||||||
if is_visible {
|
if is_visible {
|
||||||
self.schedule_positioning();
|
self.schedule_positioning();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -107,12 +109,16 @@ impl ZwpInputPopupSurfaceV2 {
|
||||||
rect = rect2;
|
rect = rect2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.surface.buffer_abs_pos.set(
|
let old = self.surface.buffer_abs_pos.get();
|
||||||
self.surface
|
let new = old.at_point(rect.x1() - extents.x1(), rect.y1() - extents.y1());
|
||||||
.buffer_abs_pos
|
if self.was_on_screen.get() && new != old {
|
||||||
.get()
|
self.damage();
|
||||||
.at_point(rect.x1() - extents.x1(), rect.y1() - extents.y1()),
|
}
|
||||||
);
|
self.surface.buffer_abs_pos.set(new);
|
||||||
|
if !self.was_on_screen.get() || new != old {
|
||||||
|
self.damage();
|
||||||
|
}
|
||||||
|
self.was_on_screen.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install(self: &Rc<Self>) -> Result<(), ZwpInputPopupSurfaceV2Error> {
|
pub fn install(self: &Rc<Self>) -> Result<(), ZwpInputPopupSurfaceV2Error> {
|
||||||
|
|
|
||||||
|
|
@ -376,12 +376,13 @@ impl GfxFramebuffer for TestGfxFb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render_with_region(
|
||||||
&self,
|
&self,
|
||||||
_acquire_sync: AcquireSync,
|
_acquire_sync: AcquireSync,
|
||||||
_release_sync: ReleaseSync,
|
_release_sync: ReleaseSync,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
_region: &Region,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
||||||
let points = rect.to_points();
|
let points = rect.to_points();
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,11 @@ impl Rect {
|
||||||
self.raw.x1 == self.raw.x2 || self.raw.y1 == self.raw.y2
|
self.raw.x1 == self.raw.x2 || self.raw.y1 == self.raw.y2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn is_not_empty(&self) -> bool {
|
||||||
|
!self.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub fn to_origin(&self) -> Self {
|
pub fn to_origin(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,13 @@ use {
|
||||||
RectRaw,
|
RectRaw,
|
||||||
},
|
},
|
||||||
smallvec::SmallVec,
|
smallvec::SmallVec,
|
||||||
std::{cell::UnsafeCell, mem, ops::Deref, rc::Rc},
|
std::{
|
||||||
|
cell::UnsafeCell,
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
mem,
|
||||||
|
ops::Deref,
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
|
|
@ -196,6 +202,12 @@ pub struct DamageQueue {
|
||||||
datas: Rc<UnsafeCell<Vec<Vec<Rect>>>>,
|
datas: Rc<UnsafeCell<Vec<Vec<Rect>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for DamageQueue {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("DamageQueue").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DamageQueue {
|
impl DamageQueue {
|
||||||
pub fn new<const N: usize>() -> [DamageQueue; N] {
|
pub fn new<const N: usize>() -> [DamageQueue; N] {
|
||||||
let datas = Rc::new(UnsafeCell::new(vec![vec!(); N]));
|
let datas = Rc::new(UnsafeCell::new(vec![vec!(); N]));
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,9 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
if let Some(titles) = rd.titles.get(&self.base.scale) {
|
if let Some(titles) = rd.titles.get(&self.base.scale) {
|
||||||
for title in titles {
|
for title in titles {
|
||||||
let (x, y) = self.base.scale_point(x + title.x, y + title.y);
|
let rect = title.rect.move_(x, y);
|
||||||
|
let bounds = self.base.scale_rect(rect);
|
||||||
|
let (x, y) = self.base.scale_point(rect.x1(), rect.y1());
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&title.tex,
|
&title.tex,
|
||||||
None,
|
None,
|
||||||
|
|
@ -262,7 +264,7 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
None,
|
Some(&bounds),
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
|
@ -486,7 +488,9 @@ impl Renderer<'_> {
|
||||||
self.base.fill_boxes(&title_underline, &uc);
|
self.base.fill_boxes(&title_underline, &uc);
|
||||||
if let Some(title) = floating.title_textures.borrow().get(&self.base.scale) {
|
if let Some(title) = floating.title_textures.borrow().get(&self.base.scale) {
|
||||||
if let Some(texture) = title.texture() {
|
if let Some(texture) = title.texture() {
|
||||||
let (x, y) = self.base.scale_point(x + bw, y + bw);
|
let rect = floating.title_rect.get().move_(x, y);
|
||||||
|
let bounds = self.base.scale_rect(rect);
|
||||||
|
let (x, y) = self.base.scale_point(rect.x1(), rect.y1());
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&texture,
|
&texture,
|
||||||
None,
|
None,
|
||||||
|
|
@ -495,7 +499,7 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
None,
|
Some(&bounds),
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,9 @@ pub struct ConnectorData {
|
||||||
pub drm_dev: Option<Rc<DrmDevData>>,
|
pub drm_dev: Option<Rc<DrmDevData>>,
|
||||||
pub async_event: Rc<AsyncEvent>,
|
pub async_event: Rc<AsyncEvent>,
|
||||||
pub damaged: Cell<bool>,
|
pub damaged: Cell<bool>,
|
||||||
|
pub damage: RefCell<Vec<Rect>>,
|
||||||
pub needs_vblank_emulation: Cell<bool>,
|
pub needs_vblank_emulation: Cell<bool>,
|
||||||
|
pub damage_intersect: Cell<Rect>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OutputData {
|
pub struct OutputData {
|
||||||
|
|
@ -831,6 +833,7 @@ impl State {
|
||||||
self.damage_visualizer.add(rect);
|
self.damage_visualizer.add(rect);
|
||||||
for output in self.root.outputs.lock().values() {
|
for output in self.root.outputs.lock().values() {
|
||||||
if output.global.pos.get().intersects(&rect) {
|
if output.global.pos.get().intersects(&rect) {
|
||||||
|
output.global.add_damage_area(&rect);
|
||||||
if cursor && output.schedule.defer_cursor_updates() {
|
if cursor && output.schedule.defer_cursor_updates() {
|
||||||
output.schedule.software_cursor_changed();
|
output.schedule.software_cursor_changed();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
||||||
drm_dev: drm_dev.clone(),
|
drm_dev: drm_dev.clone(),
|
||||||
async_event: Rc::new(AsyncEvent::default()),
|
async_event: Rc::new(AsyncEvent::default()),
|
||||||
damaged: Cell::new(false),
|
damaged: Cell::new(false),
|
||||||
|
damage: Default::default(),
|
||||||
needs_vblank_emulation: Cell::new(false),
|
needs_vblank_emulation: Cell::new(false),
|
||||||
|
damage_intersect: Default::default(),
|
||||||
});
|
});
|
||||||
if let Some(dev) = drm_dev {
|
if let Some(dev) = drm_dev {
|
||||||
dev.connectors.set(id, data.clone());
|
dev.connectors.set(id, data.clone());
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,7 @@ pub enum ContainerFocus {
|
||||||
tree_id!(ContainerNodeId);
|
tree_id!(ContainerNodeId);
|
||||||
|
|
||||||
pub struct ContainerTitle {
|
pub struct ContainerTitle {
|
||||||
pub x: i32,
|
pub rect: Rect,
|
||||||
pub y: i32,
|
|
||||||
pub tex: Rc<dyn GfxTexture>,
|
pub tex: Rc<dyn GfxTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,11 +764,7 @@ impl ContainerNode {
|
||||||
}
|
}
|
||||||
if let Some(tex) = tex.texture() {
|
if let Some(tex) = tex.texture() {
|
||||||
let titles = rd.titles.get_or_default_mut(*scale);
|
let titles = rd.titles.get_or_default_mut(*scale);
|
||||||
titles.push(ContainerTitle {
|
titles.push(ContainerTitle { rect, tex })
|
||||||
x: rect.x1(),
|
|
||||||
y: rect.y1(),
|
|
||||||
tex,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -840,11 +835,7 @@ impl ContainerNode {
|
||||||
for (scale, tex) in tt {
|
for (scale, tex) in tt {
|
||||||
if let Some(tex) = tex.texture() {
|
if let Some(tex) = tex.texture() {
|
||||||
let titles = rd.titles.get_or_default_mut(*scale);
|
let titles = rd.titles.get_or_default_mut(*scale);
|
||||||
titles.push(ContainerTitle {
|
titles.push(ContainerTitle { rect, tex })
|
||||||
x: rect.x1(),
|
|
||||||
y: rect.y1(),
|
|
||||||
tex,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2059,14 +2050,6 @@ impl ToplevelNodeBase for ContainerNode {
|
||||||
&self.toplevel_data
|
&self.toplevel_data
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_default_focus_child(&self) -> Option<Rc<dyn Node>> {
|
|
||||||
self.focus_history
|
|
||||||
.last()
|
|
||||||
.map(|v| v.node.clone())
|
|
||||||
.or_else(|| self.children.first().map(|c| c.node.clone()))
|
|
||||||
.map(|tl| tl.tl_into_node())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
||||||
for child in self.children.iter() {
|
for child in self.children.iter() {
|
||||||
child.node.clone().tl_set_workspace(ws);
|
child.node.clone().tl_set_workspace(ws);
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ pub struct FloatNode {
|
||||||
pub seat_state: NodeSeatState,
|
pub seat_state: NodeSeatState,
|
||||||
pub layout_scheduled: Cell<bool>,
|
pub layout_scheduled: Cell<bool>,
|
||||||
pub render_titles_scheduled: Cell<bool>,
|
pub render_titles_scheduled: Cell<bool>,
|
||||||
|
pub title_rect: Cell<Rect>,
|
||||||
pub title: RefCell<String>,
|
pub title: RefCell<String>,
|
||||||
pub title_textures: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
|
pub title_textures: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
|
||||||
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
||||||
|
|
@ -124,6 +125,7 @@ impl FloatNode {
|
||||||
seat_state: Default::default(),
|
seat_state: Default::default(),
|
||||||
layout_scheduled: Cell::new(false),
|
layout_scheduled: Cell::new(false),
|
||||||
render_titles_scheduled: Cell::new(false),
|
render_titles_scheduled: Cell::new(false),
|
||||||
|
title_rect: Default::default(),
|
||||||
title: Default::default(),
|
title: Default::default(),
|
||||||
title_textures: Default::default(),
|
title_textures: Default::default(),
|
||||||
cursors: Default::default(),
|
cursors: Default::default(),
|
||||||
|
|
@ -174,7 +176,9 @@ impl FloatNode {
|
||||||
(pos.height() - 2 * bw - th - 1).max(0),
|
(pos.height() - 2 * bw - th - 1).max(0),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let tr = Rect::new_sized(bw, bw, (pos.width() - 2 * bw).max(0), th).unwrap();
|
||||||
child.clone().tl_change_extents(&cpos);
|
child.clone().tl_change_extents(&cpos);
|
||||||
|
self.title_rect.set(tr);
|
||||||
self.layout_scheduled.set(false);
|
self.layout_scheduled.set(false);
|
||||||
self.schedule_render_titles();
|
self.schedule_render_titles();
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +192,6 @@ impl FloatNode {
|
||||||
fn render_title_phase1(&self) -> Rc<AsyncEvent> {
|
fn render_title_phase1(&self) -> Rc<AsyncEvent> {
|
||||||
let on_completed = Rc::new(OnDropEvent::default());
|
let on_completed = Rc::new(OnDropEvent::default());
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let th = theme.sizes.title_height.get();
|
|
||||||
let tc = match self.active.get() {
|
let tc = match self.active.get() {
|
||||||
true => theme.colors.focused_title_text.get(),
|
true => theme.colors.focused_title_text.get(),
|
||||||
false => theme.colors.unfocused_title_text.get(),
|
false => theme.colors.unfocused_title_text.get(),
|
||||||
|
|
@ -205,7 +208,7 @@ impl FloatNode {
|
||||||
_ => return on_completed.event(),
|
_ => return on_completed.event(),
|
||||||
};
|
};
|
||||||
let scales = self.state.scales.lock();
|
let scales = self.state.scales.lock();
|
||||||
let tr = Rect::new_sized(pos.x1() + bw, pos.y1() + bw, pos.width() - 2 * bw, th).unwrap();
|
let tr = self.title_rect.get();
|
||||||
let tt = &mut *self.title_textures.borrow_mut();
|
let tt = &mut *self.title_textures.borrow_mut();
|
||||||
for (scale, _) in scales.iter() {
|
for (scale, _) in scales.iter() {
|
||||||
let tex =
|
let tex =
|
||||||
|
|
|
||||||
|
|
@ -794,13 +794,17 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_extents_(self: &Rc<Self>, rect: &Rect) {
|
fn change_extents_(self: &Rc<Self>, rect: &Rect) {
|
||||||
if self.node_visible() {
|
let visible = self.node_visible();
|
||||||
|
if visible {
|
||||||
let old_pos = self.global.pos.get();
|
let old_pos = self.global.pos.get();
|
||||||
self.state.damage(old_pos);
|
self.state.damage(old_pos);
|
||||||
self.state.damage(*rect);
|
|
||||||
}
|
}
|
||||||
self.global.persistent.pos.set((rect.x1(), rect.y1()));
|
self.global.persistent.pos.set((rect.x1(), rect.y1()));
|
||||||
self.global.pos.set(*rect);
|
self.global.pos.set(*rect);
|
||||||
|
self.global.update_damage_matrix();
|
||||||
|
if visible {
|
||||||
|
self.state.damage(*rect);
|
||||||
|
}
|
||||||
self.state.output_extents_changed();
|
self.state.output_extents_changed();
|
||||||
self.update_rects();
|
self.update_rects();
|
||||||
if let Some(ls) = self.lock_surface.get() {
|
if let Some(ls) = self.lock_surface.get() {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use {
|
||||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||||
jay_screencast::JayScreencast,
|
jay_screencast::JayScreencast,
|
||||||
jay_toplevel::JayToplevel,
|
jay_toplevel::JayToplevel,
|
||||||
wl_seat::{collect_kb_foci, collect_kb_foci2, NodeSeatState, SeatId},
|
wl_seat::{collect_kb_foci, collect_kb_foci2, NodeSeatState},
|
||||||
wl_surface::WlSurface,
|
wl_surface::WlSurface,
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
|
|
@ -22,7 +22,6 @@ use {
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
hash_map_ext::HashMapExt,
|
hash_map_ext::HashMapExt,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
smallmap::SmallMap,
|
|
||||||
threshold_counter::ThresholdCounter,
|
threshold_counter::ThresholdCounter,
|
||||||
toplevel_identifier::{toplevel_identifier, ToplevelIdentifier},
|
toplevel_identifier::{toplevel_identifier, ToplevelIdentifier},
|
||||||
},
|
},
|
||||||
|
|
@ -172,10 +171,6 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
||||||
pub trait ToplevelNodeBase: Node {
|
pub trait ToplevelNodeBase: Node {
|
||||||
fn tl_data(&self) -> &ToplevelData;
|
fn tl_data(&self) -> &ToplevelData;
|
||||||
|
|
||||||
fn tl_default_focus_child(&self) -> Option<Rc<dyn Node>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tl_accepts_keyboard_focus(&self) -> bool {
|
fn tl_accepts_keyboard_focus(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -184,15 +179,8 @@ pub trait ToplevelNodeBase: Node {
|
||||||
let _ = active;
|
let _ = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_on_activate(&self) {
|
fn tl_focus_child(&self) -> Option<Rc<dyn Node>> {
|
||||||
// nothing
|
None
|
||||||
}
|
|
||||||
|
|
||||||
fn tl_focus_child(&self, seat: SeatId) -> Option<Rc<dyn Node>> {
|
|
||||||
self.tl_data()
|
|
||||||
.focus_node
|
|
||||||
.get(&seat)
|
|
||||||
.or_else(|| self.tl_default_focus_child())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
||||||
|
|
@ -259,7 +247,6 @@ pub struct ToplevelData {
|
||||||
pub client: Option<Rc<Client>>,
|
pub client: Option<Rc<Client>>,
|
||||||
pub state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
pub active_surfaces: ThresholdCounter,
|
pub active_surfaces: ThresholdCounter,
|
||||||
pub focus_node: SmallMap<SeatId, Rc<dyn Node>, 1>,
|
|
||||||
pub visible: Cell<bool>,
|
pub visible: Cell<bool>,
|
||||||
pub is_floating: Cell<bool>,
|
pub is_floating: Cell<bool>,
|
||||||
pub float_width: Cell<i32>,
|
pub float_width: Cell<i32>,
|
||||||
|
|
@ -300,7 +287,6 @@ impl ToplevelData {
|
||||||
client,
|
client,
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
active_surfaces: Default::default(),
|
active_surfaces: Default::default(),
|
||||||
focus_node: Default::default(),
|
|
||||||
visible: Cell::new(false),
|
visible: Cell::new(false),
|
||||||
is_floating: Default::default(),
|
is_floating: Default::default(),
|
||||||
float_width: Default::default(),
|
float_width: Default::default(),
|
||||||
|
|
@ -393,7 +379,6 @@ impl ToplevelData {
|
||||||
}
|
}
|
||||||
self.workspace.take();
|
self.workspace.take();
|
||||||
self.seat_state.destroy_node(node);
|
self.seat_state.destroy_node(node);
|
||||||
self.focus_node.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn broadcast(&self, toplevel: Rc<dyn ToplevelNode>) {
|
pub fn broadcast(&self, toplevel: Rc<dyn ToplevelNode>) {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ pub trait TransformExt: Sized {
|
||||||
fn from_wl(wl: i32) -> Option<Self>;
|
fn from_wl(wl: i32) -> Option<Self>;
|
||||||
|
|
||||||
fn apply_point(self, width: i32, height: i32, point: (i32, i32)) -> (i32, i32);
|
fn apply_point(self, width: i32, height: i32, point: (i32, i32)) -> (i32, i32);
|
||||||
|
|
||||||
|
fn inverse(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformExt for Transform {
|
impl TransformExt for Transform {
|
||||||
|
|
@ -68,4 +70,12 @@ impl TransformExt for Transform {
|
||||||
FlipRotate270 => (width - y, height - x),
|
FlipRotate270 => (width - y, height - x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn inverse(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Rotate90 => Rotate270,
|
||||||
|
Rotate270 => Rotate90,
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2115,6 +2115,7 @@ impl Wm {
|
||||||
// log::info!("xwin {} or {}", data.window_id, or);
|
// log::info!("xwin {} or {}", data.window_id, or);
|
||||||
if let Some(window) = data.window.get() {
|
if let Some(window) = data.window.get() {
|
||||||
window.tl_destroy();
|
window.tl_destroy();
|
||||||
|
window.update_toplevel();
|
||||||
window.map_status_changed();
|
window.map_status_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue