Double buffering for Smartstate
ProfFan opened this issue · 1 comments
Hi, thank you for this great library. I wonder if the smartstate can be applied to double buffering as well? I think the difference is that all draw commands has to be cached and applied to the previous buffer as well.
Hi! I thought about this a bit.
Caching the draw commands is also possible of course, but that would potentially take a lot of RAM I think. Instead, I propose using two sets of smartstates, one for each buffer. That should basically be all that's necessary, I think. This would imply that both buffers are updated if required, one after the other. This is still extremely CPU efficient because while it's twice the computational load, this is still only necessary once per frame.
The problem would be redraw detection. I'm unsure what the best way for that would be. The major speedup of Kolibri is that screens only need to update exactly the pixels needed. If you already have a high FPS display link, that is of course not important anymore, but for low data rate links like SPI, I think direct display updates would be more advantageous for that exact reason.
Here's how I think double buffering could work:
fn main() {
// ... setup code
let mut i = 0;
// buffer to choose
let mut buffer = 0;
// init two smartstate providers, one for each buffer
let smp = [SmartstateProvider::<10>::new(), SmartstateProvider::<10>::new()];
// ... clear background ...
loop {
// get the display buffer
let mut display = get_buffer(buffer);
// create the UI each frame
let mut ui = Ui::new_fullscreen(&mut display, medsize_rgb565_style());
// ... input code
// restart the counter at the start (or end) of the loop FOR THE CURRENT SMARTSTATE PROVIDER
smp[buffer].restart_counter();
// add a smartstate to each widget
ui.add(Label::new("Basic Example").with_font(ascii::FONT_10X20).smartstate(smp[buffer].next()));
// [...]
if ui.add_horizontal(Button::new("-").smartstate(smp[buffer].next())).clicked() {
i = i.saturating_sub(1);
// force update for both smartstate providers
smp[buffer].peek().force_redraw()
smp[(buffer + 1) % 2].get(smp[buffer].get_pos() + 1).force_redraw()
}
ui.add_horizontal(Label::new(format!("Clicked {} times", i).as_ref()).smartstate(smp[buffer].next()));
// flip the buffer
buffer = (buffer + 1 % 2)
}
}
The best way to do this would be to make a special SmartstateProvider
for multiple buffers, or to add support for multiple providers in the current one I think.