From 10521bd9a7e132f506475e3092f317bfb7017eed Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 4 Jul 2022 18:22:34 +0800 Subject: [PATCH 1/9] bump release notes to include FSCB updates --- RELEASE-v0.9.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE-v0.9.md b/RELEASE-v0.9.md index 7a3e95350..300e5bd60 100644 --- a/RELEASE-v0.9.md +++ b/RELEASE-v0.9.md @@ -174,6 +174,10 @@ 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. ## Roadmap to 1.0 From 900e3e6ae2d49df98a2294d929ced485236e137f Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 4 Jul 2022 18:35:18 +0800 Subject: [PATCH 2/9] fix limit on manual FSCB test so we're triggering basis gen once it's pretty tedious to do repeated basis regens manually. --- services/shellchat/src/cmds/pddb_cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/shellchat/src/cmds/pddb_cmd.rs b/services/shellchat/src/cmds/pddb_cmd.rs index 2e3ff4d9e..0920774bc 100644 --- a/services/shellchat/src/cmds/pddb_cmd.rs +++ b/services/shellchat/src/cmds/pddb_cmd.rs @@ -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::::new(); // 128k chunk of junk for i in 0..JUNK_CHUNK { From ae717f78af872abf3c2875789181abe3ea803862 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 5 Jul 2022 02:35:38 +0800 Subject: [PATCH 3/9] add code to make the ball app full-screen redraw affirmative on context switch otherwise, you'll get some stale artifacts because the ball app relies on the prior state of the screen for clean rendering --- apps/ball/src/main.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/ball/src/main.rs b/apps/ball/src/main.rs index 44f6bb775..9a189bc7f 100644 --- a/apps/ball/src/main.rs +++ b/apps/ball/src/main.rs @@ -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, From 32a3bb5fa4a861fee4a5ba9b94e31f150d2dea30 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 5 Jul 2022 02:37:47 +0800 Subject: [PATCH 4/9] change foreground/background behavior 1. add foreground/background tracking, such that if we return to a context instead of redrawing it, we stash the framebuffer and restore it. 2. make foreground/background context swap messages mandatory previously, we were "light fingered" about sending the message -- we'd only send them to apps. the app was not considered to be "backgrounded" if a menu came up in front of it. This turns out to be a mistake. Now, we background apps when a menu or dialog comes in front of it, and then foreground it when the app goes away. --- services/gam/src/contexts.rs | 52 ++++++++++++++++++++++++++++-------- services/gam/src/main.rs | 2 -- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/services/gam/src/contexts.rs b/services/gam/src/contexts.rs index ac2ebe8f5..6b13d8e00 100644 --- a/services/gam/src/contexts.rs +++ b/services/gam/src/contexts.rs @@ -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, diff --git a/services/gam/src/main.rs b/services/gam/src/main.rs index f6c62e635..a887c027c 100644 --- a/services/gam/src/main.rs +++ b/services/gam/src/main.rs @@ -567,8 +567,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()), From 3ad1ae73252ba4a95a71fbbaf7b42fb3345661b9 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 5 Jul 2022 02:39:09 +0800 Subject: [PATCH 5/9] fix typo --- services/shellchat/src/cmds/pddb_cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/shellchat/src/cmds/pddb_cmd.rs b/services/shellchat/src/cmds/pddb_cmd.rs index 0920774bc..ff3f4a720 100644 --- a/services/shellchat/src/cmds/pddb_cmd.rs +++ b/services/shellchat/src/cmds/pddb_cmd.rs @@ -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 { From 147ec8594c49aa55cb156ae7cc28f642c667cece Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 5 Jul 2022 02:44:13 +0800 Subject: [PATCH 6/9] 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". --- services/graphics-server/src/api.rs | 5 +++ .../graphics-server/src/backend/betrusted.rs | 37 ++++++++++++++----- .../graphics-server/src/backend/minifb.rs | 11 ++++++ services/graphics-server/src/lib.rs | 32 ++++++++++++++++ services/graphics-server/src/main.rs | 14 +++++++ 5 files changed, 90 insertions(+), 9 deletions(-) diff --git a/services/graphics-server/src/api.rs b/services/graphics-server/src/api.rs index 0167fd0bb..b83abe479 100644 --- a/services/graphics-server/src/api.rs +++ b/services/graphics-server/src/api.rs @@ -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, diff --git a/services/graphics-server/src/backend/betrusted.rs b/services/graphics-server/src/backend/betrusted.rs index 2b8c22613..0382bdcf2 100644 --- a/services/graphics-server/src/backend/betrusted.rs +++ b/services/graphics-server/src/backend/betrusted.rs @@ -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::() }>, + srfb: [u32; FB_SIZE], csr: utralib::CSR, 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,36 @@ 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(); + fb[..FB_SIZE].copy_from_slice(&self.srfb); + 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 +172,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(); } diff --git a/services/graphics-server/src/backend/minifb.rs b/services/graphics-server/src/backend/minifb.rs index 56f045b71..3b90d37b5 100644 --- a/services/graphics-server/src/backend/minifb.rs +++ b/services/graphics-server/src/backend/minifb.rs @@ -20,6 +20,7 @@ const LIGHT_COLOUR: u32 = 0x1B1B19; pub struct XousDisplay { native_buffer: Vec, //[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.copy_from_slice(&self.srfb); + self.redraw(); + self.update(); + } + pub fn screen_size(&self) -> Point { Point::new(WIDTH as i16, HEIGHT as i16) } diff --git a/services/graphics-server/src/lib.rs b/services/graphics-server/src/lib.rs index 825950ca5..f895b230c 100644 --- a/services/graphics-server/src/lib.rs +++ b/services/graphics-server/src/lib.rs @@ -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}; diff --git a/services/graphics-server/src/main.rs b/services/graphics-server/src/main.rs index db0b6cfd0..9b35f4e17 100644 --- a/services/graphics-server/src/main.rs +++ b/services/graphics-server/src/main.rs @@ -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"); From 761cffc35ea786b8756985264ae4f0cdf997dc3c Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 5 Jul 2022 03:00:31 +0800 Subject: [PATCH 7/9] strip some debug spew --- apps/vault/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vault/src/main.rs b/apps/vault/src/main.rs index 82118d9f3..36e4a15e7 100644 --- a/apps/vault/src/main.rs +++ b/apps/vault/src/main.rs @@ -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; From db569dd6e0f497cae2232a4989e611222666f2a6 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 5 Jul 2022 03:04:04 +0800 Subject: [PATCH 8/9] update release notes --- RELEASE-v0.9.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASE-v0.9.md b/RELEASE-v0.9.md index 300e5bd60..70d4d9f15 100644 --- a/RELEASE-v0.9.md +++ b/RELEASE-v0.9.md @@ -178,6 +178,11 @@ perform the Xous firmware upgrade. This requires running manual update commands, - 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 can cause + a slight glitch on the status bar where the wrong time/status is displayed for up to a second after the context swap reverts, but + it is a minor trade-off for the main purpose, which is to prevent 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 From 148bf71a940223bbcb66c1c343365bd5ca907218 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 5 Jul 2022 03:24:15 +0800 Subject: [PATCH 9/9] modify the fb pop routine so it doesn't overwrite the status bar this eliminates a small glitch in the time display/security infos whenever transitioning between foreground/background contexts --- RELEASE-v0.9.md | 7 +++---- services/gam/src/main.rs | 1 + services/graphics-server/src/backend/betrusted.rs | 4 +++- services/graphics-server/src/backend/minifb.rs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/RELEASE-v0.9.md b/RELEASE-v0.9.md index 70d4d9f15..c82cd2ca7 100644 --- a/RELEASE-v0.9.md +++ b/RELEASE-v0.9.md @@ -179,10 +179,9 @@ perform the Xous firmware upgrade. This requires running manual update commands, - 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 can cause - a slight glitch on the status bar where the wrong time/status is displayed for up to a second after the context swap reverts, but - it is a minor trade-off for the main purpose, which is to prevent 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) + - 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 diff --git a/services/gam/src/main.rs b/services/gam/src/main.rs index a887c027c..20ebf1d64 100644 --- a/services/gam/src/main.rs +++ b/services/gam/src/main.rs @@ -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"); diff --git a/services/graphics-server/src/backend/betrusted.rs b/services/graphics-server/src/backend/betrusted.rs index 0382bdcf2..fe0125510 100644 --- a/services/graphics-server/src/backend/betrusted.rs +++ b/services/graphics-server/src/backend/betrusted.rs @@ -114,7 +114,9 @@ impl XousDisplay { } pub fn pop(&mut self) { let fb: &mut [u32] = self.fb.as_slice_mut(); - fb[..FB_SIZE].copy_from_slice(&self.srfb); + // 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(); } diff --git a/services/graphics-server/src/backend/minifb.rs b/services/graphics-server/src/backend/minifb.rs index 3b90d37b5..86e42fbe1 100644 --- a/services/graphics-server/src/backend/minifb.rs +++ b/services/graphics-server/src/backend/minifb.rs @@ -88,7 +88,7 @@ impl XousDisplay { self.srfb.copy_from_slice(&self.emulated_buffer); } pub fn pop(&mut self) { - self.emulated_buffer.copy_from_slice(&self.srfb); + self.emulated_buffer[FB_WIDTH_WORDS*32..].copy_from_slice(&self.srfb[FB_WIDTH_WORDS*32..]); self.redraw(); self.update(); }