feat(k-ui): motion: ring springs to focus, toggle knob slides, screens slide+fade in (theme motion tokens, 0 = off); 16ms frame timer only while moving; motion-only frames drawn+presented in part (whole elements, pixel-snapped rects, == full draw); virtio fb flushes only damage; TICKS_PER_MS in types
This commit is contained in:
@@ -32,6 +32,9 @@ pub struct VirtioFramebuffer {
|
||||
width: Dimension,
|
||||
height: Dimension,
|
||||
stride: usize,
|
||||
/// What changed since the last flush, as corners: only that goes to
|
||||
/// the host.
|
||||
dirty: Option<(usize, usize, usize, usize)>,
|
||||
}
|
||||
|
||||
impl VirtioFramebuffer {
|
||||
@@ -79,6 +82,7 @@ impl VirtioFramebuffer {
|
||||
width,
|
||||
height,
|
||||
stride: w as usize * FB_BPP,
|
||||
dirty: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -87,6 +91,17 @@ impl VirtioFramebuffer {
|
||||
fn pixel(&self, x: usize, y: usize) -> ByteOffset {
|
||||
ByteOffset::new(y * self.stride + x * FB_BPP)
|
||||
}
|
||||
|
||||
/// Adds the rectangle from (x0, y0) up to (x1, y1) to what changed.
|
||||
fn touch(&mut self, x0: usize, y0: usize, x1: usize, y1: usize) {
|
||||
if x0 >= x1 || y0 >= y1 {
|
||||
return;
|
||||
}
|
||||
self.dirty = Some(match self.dirty {
|
||||
None => (x0, y0, x1, y1),
|
||||
Some((a, b, c, d)) => (a.min(x0), b.min(y0), c.max(x1), d.max(y1)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Framebuffer for VirtioFramebuffer {
|
||||
@@ -108,9 +123,11 @@ impl Framebuffer for VirtioFramebuffer {
|
||||
if x >= w || y >= h {
|
||||
return;
|
||||
}
|
||||
let n = pixels.len().min(w - x);
|
||||
self.touch(x, y, x + n, y + 1);
|
||||
let mut run = [0u32; RUN];
|
||||
let mut at = self.pixel(x, y);
|
||||
for chunk in pixels[..pixels.len().min(w - x)].chunks(RUN) {
|
||||
for chunk in pixels[..n].chunks(RUN) {
|
||||
for (slot, c) in run.iter_mut().zip(chunk) {
|
||||
*slot = c.raw();
|
||||
}
|
||||
@@ -124,14 +141,17 @@ impl Framebuffer for VirtioFramebuffer {
|
||||
let (fw, fh) = (self.width.raw() as usize, self.height.raw() as usize);
|
||||
let (x, y) = (x.raw() as usize, y.raw() as usize);
|
||||
let w = (w.raw() as usize).min(fw.saturating_sub(x));
|
||||
for row in y..(y + h.raw() as usize).min(fh) {
|
||||
let bottom = (y + h.raw() as usize).min(fh);
|
||||
self.touch(x, y, x + w, bottom);
|
||||
for row in y..bottom {
|
||||
self.pixels.fill_u32(self.pixel(x, row), w, color.raw());
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self, color: Color) {
|
||||
let total = self.width.raw() as usize * self.height.raw() as usize;
|
||||
self.pixels.fill_u32(ByteOffset::ZERO, total, color.raw());
|
||||
let (w, h) = (self.width.raw() as usize, self.height.raw() as usize);
|
||||
self.touch(0, 0, w, h);
|
||||
self.pixels.fill_u32(ByteOffset::ZERO, w * h, color.raw());
|
||||
}
|
||||
|
||||
fn scroll_up(&mut self, top: ScreenCoord, height: Dimension, by: Dimension, fill: Color) {
|
||||
@@ -140,16 +160,18 @@ impl Framebuffer for VirtioFramebuffer {
|
||||
let bottom = (top + height.raw() as usize).min(fh);
|
||||
let by = (by.raw() as usize).min(bottom.saturating_sub(top));
|
||||
let moved = bottom - top - by;
|
||||
self.touch(0, top, self.width.raw() as usize, bottom);
|
||||
self.pixels.copy_within(self.pixel(0, top + by), self.pixel(0, top), moved * self.stride);
|
||||
let width = self.width.raw() as usize;
|
||||
self.pixels.fill_u32(self.pixel(0, bottom - by), by * width, fill.raw());
|
||||
}
|
||||
|
||||
/// Sends the host only what changed since the last flush.
|
||||
fn flush(&mut self) {
|
||||
let w = self.width.raw() as u32;
|
||||
let h = self.height.raw() as u32;
|
||||
let whole = gpu::Rect { x: 0, y: 0, w, h };
|
||||
gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, whole);
|
||||
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, whole);
|
||||
let Some((x0, y0, x1, y1)) = self.dirty.take() else { return };
|
||||
let r = gpu::Rect { x: x0 as u32, y: y0 as u32, w: (x1 - x0) as u32, h: (y1 - y0) as u32 };
|
||||
let offset = self.pixel(x0, y0).raw() as u64;
|
||||
gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, r, offset);
|
||||
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, r);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user