xous-core/services/graphics-server/src/api.rs
bunnie 147ec8594c add stash/pop of FB; fix suspend/resume buffering
The `ManagedMem` thing for the suspend/resume crate is simply
a disaster. It tries to be fancy and Rusty and really it just fails
in non-obvious ways. This is one of the places where it simply
does not pay to make things seem so simple.

Why? because in reality, memory is complicated. It comes in pages.
There's initialization. There's caching. There's volatility.
I haven't encountered a single situation where the assumptions
of page-alignment and memory layout have really lined up well enough
for this primitive to make sense. The "idea" was hey, since
you allocate a peripheral MemoryRegion, why not just use its attributes
as a marker to a suspend/resume thing that automatically handled
stashing that MemoryRegion to a backing store inside RAM? Seems
simple right? wrong. Every time I've tried to use it, it's failed
in non-obvious ways.

In this most recent incarnation, it turns out that for whatever
reason, it seems to be just stashing about the first 4-8k of the
framebuffer to the SR backup region, and then somehow grabbing
a future(?) copy of the memory? or maybe just a wrong copy? I don't
know. The code seems simple enough but it goes through some
traits and some type transformations and then there's some unsafe
code to make it volatile and it doesn't work.

Instead, we just use a damn [u32; FB_SIZE] slice to store the
frame buffer. This is so obvious and easy. The only price is
you have an "ugly" `fb[..FB_SIZE].copy_from_slice(&self.srfb)`
line instead of an "elegant" `srfb.suspend()` invocation.
Stashing into it is a bit uglier, because we have to cast the
slice to an unsafe pointer in order to access the `write_volatile()`
method. But, it works! It. Just. Works. And you can see what's going
on. Really, I should stop trying to be fancy and just be readable.

OK anyways, that was that rant. This commit also adds some
calls that allow us to re-use the suspend/resume frame buffer during
"normal" times as a stash/pop for the application image. This is
nice because the memory is normally idle/overhead when not
in suspend, so now we're repurposing some already-allocated memory
to accelerate some drawing operations, and it also makes, crucially,
a multi-step blocking modal dialog interaction less cosmetically
jarring, because the application thread can be fully blocked and
not responding to redraw commands but the stash/pop mechanism
creates the illusion of "everything is moving along".
2022-07-05 02:44:13 +08:00

192 lines
4.8 KiB
Rust

#![allow(dead_code)]
/*
Primitives needed:
- clipping rectangles (for drawing)
- rounded rectangles
- inverse text
- icons/sprites
- width of a text string with a given font (to compute alignments)
- untrusted backgrounds
- bitmaps
*/
//////////////// primitives
pub mod points;
pub use points::Point;
pub mod styles;
pub use styles::*;
pub mod shapes;
pub use shapes::*;
pub mod text;
pub use text::*;
pub mod glyphstyle;
pub use glyphstyle::*;
pub mod blitstr2;
pub use blitstr2::*;
#[cfg(feature="ditherpunk")]
pub mod tile;
#[cfg(feature="ditherpunk")]
pub use tile::*;
use std::hash::{Hash, Hasher};
pub const LINES: i16 = 536;
pub const WIDTH: i16 = 336;
//////////////// IPC APIs
#[derive(Debug, Copy, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct Gid {
/// a 128-bit random identifier for graphical objects
gid: [u32; 4],
}
impl Gid {
pub fn new(id: [u32; 4]) -> Self {
Gid { gid: id }
}
pub fn gid(&self) -> [u32; 4] {
self.gid
}
}
impl Hash for Gid {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
Hash::hash(&self.gid[..], state);
}
}
pub const SERVER_NAME_GFX: &str = "_Graphics_";
#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)]
pub(crate) enum Opcode {
/// Flush the buffer to the screen
Flush,
/// Clear the buffer to "light" colored pixels
Clear,
/// Draw a line at the specified area
Line, //(Line),
/// Draw a rectangle or square at the specified coordinates
Rectangle, //(Rectangle),
/// Draw a rounded rectangle
RoundedRectangle, //(RoundedRectangle),
/// Paint a Bitmap Tile
#[cfg(feature="ditherpunk")]
Tile,
/// Draw a circle with a specified radius
Circle, //(Circle),
/// Retrieve the X and Y dimensions of the screen
ScreenSize,
/// gets info about the current glyph to assist with layout
QueryGlyphProps, //(GlyphStyle),
/// draws a textview
DrawTextView, //(TextView),
/// draws an object that requires clipping
DrawClipObject, //(ClipObject),
DrawClipObjectList,
/// draws the sleep screen; assumes requests are vetted by GAM/xous-names
DrawSleepScreen,
/// permanently turns on the Devboot mark
Devboot,
/// bulk read for signature verifications
BulkReadFonts,
RestartBulkRead,
/// sling the framebuffer into and out of the suspend/resume area, abusing this
/// to help accelerate redraws between modal swaps.
Stash,
Pop,
/// generates a test pattern
TestPattern,
/// SuspendResume callback
SuspendResume,
Quit,
}
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)]
pub enum ClipObjectType {
Line(Line),
Circ(Circle),
Rect(Rectangle),
RoundRect(RoundedRectangle),
XorLine(Line),
#[cfg(feature="ditherpunk")]
Tile(Tile),
}
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)]
pub struct ClipObject {
pub clip: Rectangle,
pub obj: ClipObjectType,
}
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)]
pub struct ClipObjectList {
// ClipObject is 28 bytes, so 32 of these takes 896 bytes, which is less than a 4k page (the minimum amount that gets remapped)
// we limit the length to 32 so we can use the Default initializer to set the None's on the array, otherwise it gets a bit painful.
pub list: [Option::<ClipObject>; 32],
free: usize,
}
impl ClipObjectList {
pub fn default() -> ClipObjectList {
ClipObjectList {
list: Default::default(),
free: 0,
}
}
pub fn push(&mut self, item: ClipObjectType, clip: Rectangle) -> Result<(), ClipObjectType> {
if self.free < self.list.len() {
self.list[self.free] = Some(ClipObject {clip, obj: item});
self.free += 1;
Ok(())
} else {
Err(item)
}
}
}
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)]
pub struct TokenClaim {
pub token: Option<[u32; 4]>,
pub name: xous_ipc::String<128>,
}
/// the buffer length of this equal to the internal length passed by the
/// engine-sha512 implementation times 2 (a small amount of overhead is required
/// out of an even 4096 page for bookkeeping). We could make this a neat power of 2,
/// but then you'd end up doing an extra memory message for the overhead bits that
/// are left over.
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)]
pub struct BulkRead {
pub buf: [u8; 7936],
pub from_offset: u32,
pub len: u32, // used to return the length read out of the font map
}
impl BulkRead {
pub fn default() -> BulkRead {
BulkRead {
buf: [0; 7936],
from_offset: 0,
len: 7936,
}
}
}