Merge branch 'pddb' into main

This commit is contained in:
bunnie 2022-07-05 03:25:02 +08:00
commit 7ef4e4dc41
11 changed files with 152 additions and 25 deletions

View file

@ -174,6 +174,14 @@ perform the Xous firmware upgrade. This requires running manual update commands,
- Autobacklight feature: selectable feature to automatically raise the backlight when keys are pressed. Thanks to gsora for the feature!
- More importantly, this adds `crossbeam` into the kernel. This is a somewhat heavyweight item and is not recommended for use in core
services, but by including it to drive the auto-backlight feature, we get regular code coverage of the condvar pathway.
- Fix PDDB FSCB generation problems
- some issue were found in free space cache generation, these are now fixed
- fuzzer added to push many more corner cases than before -- disk is filled to ~75% capacity with dozens of Bases,
triggering roughly 4 FSCB ops, and then all Bases and keys are verified to be correct.
- Optimize GAM performance
- context switches that revert to a prior app screen are restored from a stashed bitmap instead of a full redraw. This
prevents old menus, contexts, and defacements from "piling up" when an app is blocked due to an exceptional condition
being raised (such as the PDDB requesting a full basis unlock to generate the FSCB)
## Roadmap to 1.0

View file

@ -55,6 +55,7 @@ fn main() -> ! {
// this is the main event loop for the app.
let mut allow_redraw = true;
let mut into_allow_redraw = false;
loop {
let msg = xous::receive_message(sid).unwrap();
match FromPrimitive::from_usize(msg.body.id()) {
@ -65,6 +66,10 @@ fn main() -> ! {
}
}
Some(AppOp::Pump) => { // this is a blocking scalar so that pump calls don't initiate faster than the app can draw
if into_allow_redraw {
ball.focus();
into_allow_redraw = false;
}
if allow_redraw {
ball.update();
}
@ -81,6 +86,7 @@ fn main() -> ! {
}),
Some(AppOp::FocusChange) => xous::msg_scalar_unpack!(msg, new_state_code, _, _, _, {
let new_state = gam::FocusState::convert_focus_change(new_state_code);
log::info!("focus change: {:?}", new_state);
match new_state {
gam::FocusState::Background => {
allow_redraw = false; // this instantly terminates future updates, even if Pump messages are in our input queue
@ -90,6 +96,7 @@ fn main() -> ! {
).expect("couldn't send stop message to the pump thread");
}
gam::FocusState::Foreground => {
into_allow_redraw = true;
allow_redraw = true;
xous::send_message(
cid_to_pump,

View file

@ -373,7 +373,7 @@ fn main() -> ! {
Some(VaultOp::ChangeFocus) => xous::msg_scalar_unpack!(msg, new_state_code, _, _, _, {
let new_state = gam::FocusState::convert_focus_change(new_state_code);
vaultux.change_focus_to(&new_state);
log::info!("change focus: {:?}", new_state);
log::debug!("change focus: {:?}", new_state);
match new_state {
gam::FocusState::Background => {
allow_redraw = false;

View file

@ -92,6 +92,7 @@ pub(crate) struct ContextManager {
contexts: HashMap::<[u32; 4], UxContext>,
focused_context: Option<[u32; 4]>, // app_token of the app that has I/O focus, if any
last_context: Option<[u32; 4]>, // previously focused context, if any
context_stack: Vec::<[u32; 4]>,
imef: ime_plugin_api::ImeFrontEnd,
imef_active: bool,
kbd: keyboard::Keyboard,
@ -112,6 +113,7 @@ impl ContextManager {
ContextManager {
tm: TokenManager::new(&xns),
contexts: HashMap::new(),
context_stack: Vec::new(),
focused_context: None,
last_context: None,
imef,
@ -325,7 +327,10 @@ impl ContextManager {
token: [u32; 4],
clear: bool,
) -> Result<(), xous::Error> {
self.notify_app_switch(token).ok();
let mut leaving_visibility: bool = false;
let stack_on_entry = self.context_stack.len();
{
// using a temp copy of the old focus, check if we need to update any visibility state
let maybe_leaving_focused_context = if let Some(focused_token) = self.focused_context {
@ -357,22 +362,27 @@ impl ContextManager {
(leaving_focused_context.layout.behavior() == LayoutBehavior::App) {
context.layout.set_visibility_state(true, canvases);
leaving_visibility = false;
self.context_stack.pop();
self.context_stack.push(token);
} else if // alert covering an app
(context.layout.behavior() == LayoutBehavior::Alert) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::App) {
context.layout.set_visibility_state(true, canvases);
leaving_visibility = true;
self.context_stack.push(token);
} else if // app covering an alert
(context.layout.behavior() == LayoutBehavior::App) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::Alert) {
context.layout.set_visibility_state(true, canvases);
leaving_visibility = false;
self.context_stack.pop();
}
}
} else {
// there was no current focus, just make the activation visible
log::debug!("setting first-time visibility to context {:?}", token);
context.layout.set_visibility_state(true, canvases);
self.context_stack.push(token);
}
}
}
@ -448,12 +458,27 @@ impl ContextManager {
self.last_context = self.focused_context;
self.focused_context = Some(last_token);
}
log::trace!("context stack: {:x?}", self.context_stack);
if self.context_stack.len() > 1 { // we've now got a stack of contexts, start stashing copies
log::trace!("stashing");
gfx.stash(true);
}
// run the defacement before we redraw all the canvases
if deface(gfx, &self.trng, canvases) {
log::trace!("activate triggered a defacement");
}
log::trace!("activate redraw");
self.redraw().expect("couldn't redraw the currently focused app");
if self.context_stack.len() > 0 && stack_on_entry > 1 {
if self.context_stack[self.context_stack.len() - 1] == token {
// if we're returning to the previous context, just pop the image
log::trace!("popping");
gfx.pop(true);
} else {
self.redraw().expect("couldn't redraw the currently focused app");
}
} else {
self.redraw().expect("couldn't redraw the currently focused app");
}
}
Ok(())
}
@ -477,19 +502,24 @@ impl ContextManager {
}
}
pub(crate) fn notify_app_switch(&self, new_app_token: [u32; 4]) -> Result<(), xous::Error> {
if let Some(old_context) = self.get_context_by_token(self.focused_context.unwrap()) {
if let Some(focuschange_id) = old_context.focuschange_id {
log::trace!("Background focus change to {}, id {}", old_context.listener, old_context.redraw_id);
xous::send_message(old_context.listener,
xous::Message::new_scalar(focuschange_id as usize, gam::FocusState::Background as usize, 0, 0, 0)
).map(|_| ())?;
} else {
return Err(xous::Error::ServerNotFound);
log::trace!("notify app switch to {:?}", new_app_token);
if let Some(current_focus) = self.focused_context {
if let Some(old_context) = self.get_context_by_token(current_focus) {
if let Some(focuschange_id) = old_context.focuschange_id {
log::trace!("Background focus change to {}, id {} / {:?}", old_context.listener, old_context.redraw_id, current_focus);
xous::send_message(old_context.listener,
xous::Message::new_scalar(focuschange_id as usize, gam::FocusState::Background as usize, 0, 0, 0)
).map(|_| ())?;
} else {
// don't return an error -- this just means that the listener doesn't recognize focus changes. This should not
// deprive the later app of a notification that it is coming into focus!
}
}
}
if let Some(new_context) = self.get_context_by_token(new_app_token) {
if let Some(focuschange_id) = new_context.focuschange_id {
log::trace!("Foreground focus change to {}, id {}", new_context.listener, new_context.redraw_id);
log::trace!("Foreground focus change to {}, id {} / {:?}", new_context.listener, new_context.redraw_id, new_app_token);
xous::send_message(new_context.listener,
xous::Message::new_scalar(focuschange_id as usize, gam::FocusState::Foreground as usize, 0, 0, 0)
).map(|_| ())?;
@ -622,7 +652,7 @@ impl ContextManager {
pub(crate) fn toggle_menu_mode(&mut self, token: [u32; 4]) {
if let Some(context) = self.contexts.get_mut(&token) {
context.imef_menu_mode = !context.imef_menu_mode;
log::info!("menu mode for token {:?} is now {}", token, context.imef_menu_mode);
log::debug!("menu mode for token {:?} is now {}", token, context.imef_menu_mode);
}
}
pub(crate) fn raise_menu(&mut self,

View file

@ -75,6 +75,7 @@ fn wrapped_main() -> ! {
let status_canvas = Canvas::new(
Rectangle::new_coords(
0, 0, screensize.x,
// note: if this gets modified, the "pop" routine in gfx/backend/betrusted.rs also needs to be updated
gfx.glyph_height_hint(GlyphStyle::Cjk).expect("couldn't get glyph height") as i16 * 2),
255, &trng, None, crate::api::CanvasType::Status
).expect("couldn't create status canvas");
@ -567,8 +568,6 @@ fn wrapped_main() -> ! {
for switchers in authorized_switchers {
if let Some(auth_token) = context_mgr.find_app_token_by_name(switchers) {
if auth_token == switchapp.token {
context_mgr.notify_app_switch(new_app_token)
.unwrap_or_else(|_| {log::warn!("Application does not recognize focus changes")});
match context_mgr.activate(&gfx, &mut canvases, new_app_token, false) {
Ok(_) => (),
Err(_) => log::warn!("failed to switch to {}, silent error!", switchapp.app_name.as_str().unwrap()),

View file

@ -108,6 +108,11 @@ pub(crate) enum Opcode {
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,

View file

@ -1,6 +1,6 @@
use crate::api::Point;
use crate::api::{LINES, WIDTH};
use susres::{ManagedMem, RegManager, RegOrField, SuspendResume};
use susres::{RegManager, RegOrField, SuspendResume};
use utralib::generated::*;
use xous::MemoryRange;
@ -13,7 +13,7 @@ const CONFIG_CLOCK_FREQUENCY: u32 = 100_000_000;
pub struct XousDisplay {
fb: MemoryRange,
hwfb: MemoryRange,
srfb: ManagedMem<{ utralib::generated::HW_MEMLCD_MEM_LEN / core::mem::size_of::<u32>() }>,
srfb: [u32; FB_SIZE],
csr: utralib::CSR<u32>,
susres: RegManager<{ utra::memlcd::MEMLCD_NUMREGS }>,
}
@ -61,7 +61,7 @@ impl XousDisplay {
hwfb,
csr: CSR::new(control.as_mut_ptr() as *mut u32),
susres: RegManager::new(control.as_mut_ptr() as *mut u32),
srfb: ManagedMem::new(fb),
srfb: [0u32; FB_SIZE],
};
display.set_clock(CONFIG_CLOCK_FREQUENCY);
@ -101,18 +101,38 @@ impl XousDisplay {
)
}
pub fn stash(&mut self) {
let fb = unsafe{core::slice::from_raw_parts_mut(self.fb.as_mut_ptr() as *mut u32, FB_SIZE)};
for lines in 0..FB_LINES {
// set the dirty bits prior to stashing the frame buffer
fb[lines * FB_WIDTH_WORDS + (FB_WIDTH_WORDS - 1)] |= 0x1_0000;
}
let srfb_ptr = self.srfb.as_mut_ptr() as *mut u32;
for (index, &src) in fb.iter().enumerate() {
unsafe{srfb_ptr.add(index).write_volatile(src)};
}
}
pub fn pop(&mut self) {
let fb: &mut [u32] = self.fb.as_slice_mut();
// skip copying the status bar, so that the status info is not overwritten by the pop.
// this is "fixed" at 32 pixels high (2 * Cjk glyph height hint) per line 79 in gam/src/main.rs
fb[FB_WIDTH_WORDS * 32..FB_SIZE].copy_from_slice(&self.srfb[FB_WIDTH_WORDS * 32..FB_SIZE]);
self.redraw();
}
pub fn suspend(&mut self) {
while self.busy() {
// just wait until any pending FB operations are done
}
let fb: *mut [u32; FB_SIZE] = self.fb.as_mut_ptr() as *mut [u32; FB_SIZE];
let fb = unsafe{core::slice::from_raw_parts_mut(self.fb.as_mut_ptr() as *mut u32, FB_SIZE)};
for lines in 0..FB_LINES {
// set the dirty bits prior to stashing the frame buffer
unsafe {
(*fb)[lines * FB_WIDTH_WORDS + (FB_WIDTH_WORDS - 1)] |= 0x1_0000;
}
fb[lines * FB_WIDTH_WORDS + (FB_WIDTH_WORDS - 1)] |= 0x1_0000;
}
let srfb_ptr = self.srfb.as_mut_ptr() as *mut u32;
for (index, &src) in fb.iter().enumerate() {
unsafe{srfb_ptr.add(index).write_volatile(src)};
}
self.srfb.suspend();
self.susres.suspend();
let note = crate::sleep_note::LOGO_MAP;
@ -154,7 +174,8 @@ impl XousDisplay {
}
pub fn resume(&mut self) {
self.susres.resume();
self.srfb.resume();
let fb: &mut [u32] = self.fb.as_slice_mut();
fb[..FB_SIZE].copy_from_slice(&self.srfb);
self.redraw();
}

View file

@ -20,6 +20,7 @@ const LIGHT_COLOUR: u32 = 0x1B1B19;
pub struct XousDisplay {
native_buffer: Vec<u32>, //[u32; WIDTH * HEIGHT],
emulated_buffer: [u32; FB_SIZE],
srfb: [u32; FB_SIZE],
window: Window,
devboot: bool,
}
@ -70,6 +71,7 @@ impl XousDisplay {
native_buffer,
window,
emulated_buffer: [0u32; FB_SIZE],
srfb: [0u32; FB_SIZE],
devboot: true,
}
}
@ -82,6 +84,15 @@ impl XousDisplay {
pub fn suspend(&self) {}
pub fn resume(&self) {}
pub fn stash(&mut self) {
self.srfb.copy_from_slice(&self.emulated_buffer);
}
pub fn pop(&mut self) {
self.emulated_buffer[FB_WIDTH_WORDS*32..].copy_from_slice(&self.srfb[FB_WIDTH_WORDS*32..]);
self.redraw();
self.update();
}
pub fn screen_size(&self) -> Point {
Point::new(WIDTH as i16, HEIGHT as i16)
}

View file

@ -268,6 +268,38 @@ impl Gfx {
)
.expect("couldn't self test");
}
pub fn stash(&self, blocking: bool) {
if blocking {
send_message(
self.conn,
Message::new_blocking_scalar(Opcode::Stash.to_usize().unwrap(), 0, 0, 0, 0)
)
.expect("couldn't stash");
} else {
send_message(
self.conn,
Message::new_scalar(Opcode::Stash.to_usize().unwrap(), 0, 0, 0, 0)
)
.expect("couldn't stash");
}
}
pub fn pop(&self, blocking: bool) {
if blocking {
send_message(
self.conn,
Message::new_blocking_scalar(Opcode::Pop.to_usize().unwrap(), 0, 0, 0, 0)
)
.expect("couldn't stash");
} else {
send_message(
self.conn,
Message::new_scalar(Opcode::Pop.to_usize().unwrap(), 0, 0, 0, 0)
)
.expect("couldn't stash");
}
}
}
use core::sync::atomic::{AtomicU32, Ordering};

View file

@ -606,6 +606,20 @@ fn wrapped_main() -> ! {
xous::return_scalar(msg.sender, duration).expect("couldn't ack test pattern");
}),
Some(Opcode::Stash) => {
display.stash();
match msg.body { // ack the message if it's a blocking scalar
xous::Message::BlockingScalar(_) => xous::return_scalar(msg.sender, 1).unwrap(),
_ => ()
}
}
Some(Opcode::Pop) => {
display.pop();
match msg.body { // ack the message if it's a blocking scalar
xous::Message::BlockingScalar(_) => xous::return_scalar(msg.sender, 1).unwrap(),
_ => ()
}
}
Some(Opcode::Quit) => break,
None => {
log::error!("received opcode scalar that is not handled");

View file

@ -195,7 +195,7 @@ impl<'a> ShellCmdApi<'a> for PddbCmd {
"dictlist" => {
match self.pddb.list_dict(None) {
Ok(list) => {
let checked_len = if list.len() > 6 {
let checked_len = if list.len() > 10 {
write!(ret, "First 10 dicts of {}:", list.len()).unwrap();
10
} else {
@ -594,7 +594,7 @@ impl<'a> ShellCmdApi<'a> for PddbCmd {
// 128k chunks of junk
const JUNK_CHUNK: usize = 131072;
log::info!("fill junk");
for index in 0..28 { // write ~3 megs of junk, should trigger FSCB unlock at least once...
for index in 0..17 { // write ~2 megs of junk, should trigger FSCB unlock at least once...
let mut junk = Vec::<u8>::new();
// 128k chunk of junk
for i in 0..JUNK_CHUNK {