1
0
Fork 0
forked from len0rd/rockbox

Optimized the gui list code performance, including automatic frame dropping and cpu boosting when button events are getting queued. Improved scrollwheel acceleration code is needed to notice a real change.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12721 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Miika Pekkarinen 2007-03-11 10:52:36 +00:00
parent 408dfd65ad
commit 2eefb5acb8
5 changed files with 222 additions and 82 deletions

View file

@ -40,11 +40,17 @@
#define SCROLL_LIMIT 2 #define SCROLL_LIMIT 2
#endif #endif
/* The minimum number of pending button events in queue before starting
* to limit list drawing interval.
*/
#define FRAMEDROP_TRIGGER 6
#ifdef HAVE_LCD_BITMAP #ifdef HAVE_LCD_BITMAP
static int offset_step = 16; /* pixels per screen scroll step */ static int offset_step = 16; /* pixels per screen scroll step */
/* should lines scroll out of the screen */ /* should lines scroll out of the screen */
static bool offset_out_of_view = false; static bool offset_out_of_view = false;
#endif #endif
static struct gui_list* last_list_displayed[NB_SCREENS];
#define SHOW_LIST_TITLE ((gui_list->title != NULL) && \ #define SHOW_LIST_TITLE ((gui_list->title != NULL) && \
(gui_list->display->nb_lines > 1)) (gui_list->display->nb_lines > 1))
@ -86,6 +92,9 @@ static void gui_list_init(struct gui_list * gui_list,
gui_list->title = NULL; gui_list->title = NULL;
gui_list->title_width = 0; gui_list->title_width = 0;
gui_list->title_icon = NOICON; gui_list->title_icon = NOICON;
gui_list->last_displayed_selected_item = -1 ;
gui_list->last_displayed_start_item = -1 ;
} }
/* /*
@ -217,7 +226,7 @@ static int gui_list_get_item_offset(struct gui_list * gui_list, int item_width,
* Draws the list on the attached screen * Draws the list on the attached screen
* - gui_list : the list structure * - gui_list : the list structure
*/ */
static void gui_list_draw(struct gui_list * gui_list) static void gui_list_draw_smart(struct gui_list *gui_list)
{ {
struct screen * display=gui_list->display; struct screen * display=gui_list->display;
int cursor_pos = 0; int cursor_pos = 0;
@ -231,15 +240,51 @@ static void gui_list_draw(struct gui_list * gui_list)
int item_offset; int item_offset;
int old_margin = display->getxmargin(); int old_margin = display->getxmargin();
#endif #endif
int start, end;
bool partial_draw = false;
/* Speed up UI by drawing the changed contents only. */
if (gui_list == last_list_displayed[gui_list->display->screen_type]
&& gui_list->last_displayed_start_item == gui_list->start_item
&& gui_list->selected_size == 1)
{
partial_draw = true;
}
if (SHOW_LIST_TITLE)
lines = display->nb_lines - 1;
else
lines = display->nb_lines;
if (partial_draw)
{
end = gui_list->last_displayed_selected_item - gui_list->start_item;
i = gui_list->selected_item - gui_list->start_item;
if (i < end )
{
start = i;
end++;
}
else
{
start = end;
end = i + 1;
}
}
else
{
gui_textarea_clear(display); gui_textarea_clear(display);
start = 0;
end = display->nb_lines;
gui_list->last_displayed_start_item = gui_list->start_item;
last_list_displayed[gui_list->display->screen_type] = gui_list;
}
gui_list->last_displayed_selected_item = gui_list->selected_item;
/* position and draw the list title & icon */ /* position and draw the list title & icon */
if (SHOW_LIST_TITLE) if (SHOW_LIST_TITLE && !partial_draw)
{ {
i = 1;
lines = display->nb_lines - 1;
if (gui_list->title_icon != NOICON && draw_icons) if (gui_list->title_icon != NOICON && draw_icons)
{ {
screen_put_iconxy(display, 0, 0, gui_list->title_icon); screen_put_iconxy(display, 0, 0, gui_list->title_icon);
@ -266,11 +311,6 @@ static void gui_list_draw(struct gui_list * gui_list)
display->puts_scroll(text_pos, 0, gui_list->title); display->puts_scroll(text_pos, 0, gui_list->title);
#endif #endif
} }
else
{
i = 0;
lines = display->nb_lines;
}
/* Adjust the position of icon, cursor, text for the list */ /* Adjust the position of icon, cursor, text for the list */
#ifdef HAVE_LCD_BITMAP #ifdef HAVE_LCD_BITMAP
@ -309,7 +349,14 @@ static void gui_list_draw(struct gui_list * gui_list)
screen_set_xmargin(display, text_pos); /* margin for list */ screen_set_xmargin(display, text_pos); /* margin for list */
#endif #endif
while (i < display->nb_lines) if (SHOW_LIST_TITLE)
{
start++;
if (end < display->nb_lines)
end++;
}
for (i = start; i < end; i++)
{ {
unsigned char *s; unsigned char *s;
char entry_buffer[MAX_PATH]; char entry_buffer[MAX_PATH];
@ -338,21 +385,28 @@ static void gui_list_draw(struct gui_list * gui_list)
{/* The selected item must be displayed scrolling */ {/* The selected item must be displayed scrolling */
#ifdef HAVE_LCD_BITMAP #ifdef HAVE_LCD_BITMAP
if (global_settings.invert_cursor)/* Display inverted-line-style*/ if (global_settings.invert_cursor)/* Display inverted-line-style*/
{
/* if text got out of view */ /* if text got out of view */
if (item_offset > item_width - (display->width - text_pos)) if (item_offset > item_width - (display->width - text_pos))
{
/* don't scroll */ /* don't scroll */
display->puts_style_offset(0, i, entry_name, display->puts_style_offset(0, i, entry_name,
STYLE_INVERT,item_offset); STYLE_INVERT,item_offset);
}
else else
{
display->puts_scroll_style_offset(0, i, entry_name, display->puts_scroll_style_offset(0, i, entry_name,
STYLE_INVERT, STYLE_INVERT,
item_offset); item_offset);
}
}
else /* if (!global_settings.invert_cursor) */ else /* if (!global_settings.invert_cursor) */
{
if (item_offset > item_width - (display->width - text_pos)) if (item_offset > item_width - (display->width - text_pos))
display->puts_offset(0, i, entry_name,item_offset); display->puts_offset(0, i, entry_name,item_offset);
else else
display->puts_scroll_offset(0, i, entry_name,item_offset); display->puts_scroll_offset(0, i, entry_name,item_offset);
}
#else #else
display->puts_scroll(text_pos, i, entry_name); display->puts_scroll(text_pos, i, entry_name);
#endif #endif
@ -389,7 +443,6 @@ static void gui_list_draw(struct gui_list * gui_list)
if(icon) if(icon)
screen_put_iconxy(display, icon_pos, i, icon); screen_put_iconxy(display, icon_pos, i, icon);
} }
i++;
} }
#ifdef HAVE_LCD_BITMAP #ifdef HAVE_LCD_BITMAP
@ -413,6 +466,15 @@ static void gui_list_draw(struct gui_list * gui_list)
gui_textarea_update(display); gui_textarea_update(display);
} }
/*
* Force a full screen update.
*/
static void gui_list_draw(struct gui_list *gui_list)
{
last_list_displayed[gui_list->display->screen_type] = NULL;
return gui_list_draw_smart(gui_list);
}
/* /*
* Selects an item in the list * Selects an item in the list
* - gui_list : the list structure * - gui_list : the list structure
@ -426,6 +488,64 @@ static void gui_list_select_item(struct gui_list * gui_list, int item_number)
gui_list_put_selection_in_screen(gui_list, false); gui_list_put_selection_in_screen(gui_list, false);
} }
static void scroll_down(struct gui_list *gui_list, bool paginate)
{
int nb_lines = gui_list->display->nb_lines;
if (SHOW_LIST_TITLE)
nb_lines--;
int item_pos = gui_list->selected_item - gui_list->start_item;
int end_item = gui_list->start_item + nb_lines;
if (paginate)
{
/* When we reach the bottom of the list
* we jump to a new page if there are more items*/
if ((item_pos > nb_lines-gui_list->selected_size) &&
(end_item < gui_list->nb_items) )
{
gui_list->start_item = gui_list->selected_item;
if ( gui_list->start_item > gui_list->nb_items-nb_lines )
gui_list->start_item = gui_list->nb_items-nb_lines;
}
}
else
{
/* we start scrolling vertically when reaching the line
* (nb_lines-SCROLL_LIMIT)
* and when we are not in the last part of the list*/
if( (item_pos > nb_lines-SCROLL_LIMIT) &&
(end_item < gui_list->nb_items) )
{
gui_list->start_item+=gui_list->selected_size;
}
}
}
static void scroll_up(struct gui_list *gui_list, bool paginate)
{
int item_pos = gui_list->selected_item - gui_list->start_item;
int nb_lines = gui_list->display->nb_lines;
if (paginate)
{
/* When we reach the top of the list
* we jump to a new page if there are more items*/
if( item_pos < 0)
gui_list->start_item = gui_list->selected_item - nb_lines +
gui_list->selected_size;
}
else
{
/* we start scrolling vertically when reaching the line
* (nb_lines-SCROLL_LIMIT)
* and when we are not in the last part of the list*/
if( item_pos < SCROLL_LIMIT-1)
gui_list->start_item-=gui_list->selected_size;
}
if( gui_list->start_item < 0 )
gui_list->start_item = 0;
}
/* /*
* Selects the next item in the list * Selects the next item in the list
* (Item 0 gets selected if the end of the list is reached) * (Item 0 gets selected if the end of the list is reached)
@ -444,33 +564,7 @@ static void gui_list_select_next(struct gui_list * gui_list)
else else
{ {
gui_list->selected_item+=gui_list->selected_size; gui_list->selected_item+=gui_list->selected_size;
int nb_lines = gui_list->display->nb_lines; scroll_down(gui_list, global_settings.scroll_paginated);
if (SHOW_LIST_TITLE)
nb_lines--;
int item_pos = gui_list->selected_item - gui_list->start_item;
int end_item = gui_list->start_item + nb_lines;
if (global_settings.scroll_paginated)
{
/* When we reach the bottom of the list
* we jump to a new page if there are more items*/
if( (item_pos > nb_lines-gui_list->selected_size) &&
(end_item < gui_list->nb_items) )
{
gui_list->start_item = gui_list->selected_item;
if ( gui_list->start_item > gui_list->nb_items-nb_lines )
gui_list->start_item = gui_list->nb_items-nb_lines;
}
}
else
{
/* we start scrolling vertically when reaching the line
* (nb_lines-SCROLL_LIMIT)
* and when we are not in the last part of the list*/
if( (item_pos > nb_lines-SCROLL_LIMIT) &&
(end_item < gui_list->nb_items) )
gui_list->start_item+=gui_list->selected_size;
}
} }
} }
@ -499,27 +593,8 @@ static void gui_list_select_previous(struct gui_list * gui_list)
} }
else else
{ {
int item_pos;
gui_list->selected_item -= gui_list->selected_size; gui_list->selected_item -= gui_list->selected_size;
item_pos = gui_list->selected_item - gui_list->start_item; scroll_up(gui_list, global_settings.scroll_paginated);
if (global_settings.scroll_paginated)
{
/* When we reach the top of the list
* we jump to a new page if there are more items*/
if( item_pos < 0)
gui_list->start_item = gui_list->selected_item - nb_lines +
gui_list->selected_size;
}
else
{
/* we start scrolling vertically when reaching the line
* (nb_lines-SCROLL_LIMIT)
* and when we are not in the last part of the list*/
if( item_pos < SCROLL_LIMIT-1)
gui_list->start_item-=gui_list->selected_size;
}
if( gui_list->start_item < 0 )
gui_list->start_item = 0;
} }
} }
@ -850,6 +925,9 @@ unsigned gui_synclist_do_button(struct gui_synclist * lists,
case ACTION_STD_PREV: case ACTION_STD_PREV:
case ACTION_STD_PREVREPEAT: case ACTION_STD_PREVREPEAT:
gui_synclist_select_previous(lists); gui_synclist_select_previous(lists);
#ifndef SIMULATOR
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
gui_synclist_draw(lists); gui_synclist_draw(lists);
yield(); yield();
return ACTION_STD_PREV; return ACTION_STD_PREV;
@ -857,6 +935,9 @@ unsigned gui_synclist_do_button(struct gui_synclist * lists,
case ACTION_STD_NEXT: case ACTION_STD_NEXT:
case ACTION_STD_NEXTREPEAT: case ACTION_STD_NEXTREPEAT:
gui_synclist_select_next(lists); gui_synclist_select_next(lists);
#ifndef SIMULATOR
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
gui_synclist_draw(lists); gui_synclist_draw(lists);
yield(); yield();
return ACTION_STD_NEXT; return ACTION_STD_NEXT;

View file

@ -91,6 +91,10 @@ struct gui_list
int selected_size; int selected_size;
/* The data that will be passed to the callback function YOU implement */ /* The data that will be passed to the callback function YOU implement */
void * data; void * data;
/* These are used to calculate how much of the screen content we need
to redraw. */
int last_displayed_selected_item;
int last_displayed_start_item;
/* The optional title, set to NULL for none */ /* The optional title, set to NULL for none */
char *title; char *title;
/* Cache the width of the title string in pixels/characters */ /* Cache the width of the title string in pixels/characters */
@ -162,6 +166,7 @@ extern void gui_list_screen_scroll_out_of_view(bool enable);
struct gui_synclist struct gui_synclist
{ {
struct gui_list gui_list[NB_SCREENS]; struct gui_list gui_list[NB_SCREENS];
struct gui_list *last_displayed[NB_SCREENS];
}; };
extern void gui_synclist_init( extern void gui_synclist_init(

View file

@ -26,6 +26,7 @@
#include "system.h" #include "system.h"
#include "button.h" #include "button.h"
#include "kernel.h" #include "kernel.h"
#include "thread.h"
#include "backlight.h" #include "backlight.h"
#include "serial.h" #include "serial.h"
#include "power.h" #include "power.h"
@ -255,21 +256,52 @@ static void button_tick(void)
lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT); lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
} }
void button_boost(bool state)
{
static bool boosted = false;
if (state && !boosted)
{
cpu_boost(true);
boosted = true;
}
else if (!state && boosted)
{
cpu_boost(false);
boosted = false;
}
}
long button_get(bool block) long button_get(bool block)
{ {
struct event ev; struct event ev;
int pending_count = queue_count(&button_queue);
if ( block || !queue_empty(&button_queue) ) /* Control the CPU boost trying to keep queue empty. */
if (pending_count == 0)
button_boost(false);
else if (pending_count > 2)
button_boost(true);
if ( block || pending_count )
{ {
queue_wait(&button_queue, &ev); queue_wait(&button_queue, &ev);
return ev.id; return ev.id;
} }
return BUTTON_NONE; return BUTTON_NONE;
} }
long button_get_w_tmo(int ticks) long button_get_w_tmo(int ticks)
{ {
struct event ev; struct event ev;
/* Be sure to keep boosted state. */
if (!queue_empty(&button_queue))
return button_get(true);
button_boost(false);
queue_wait_w_tmo(&button_queue, &ev, ticks); queue_wait_w_tmo(&button_queue, &ev, ticks);
return (ev.id != SYS_TIMEOUT)? ev.id: BUTTON_NONE; return (ev.id != SYS_TIMEOUT)? ev.id: BUTTON_NONE;
} }

View file

@ -123,6 +123,7 @@ extern bool queue_in_queue_send(struct event_queue *q);
extern bool queue_empty(const struct event_queue* q); extern bool queue_empty(const struct event_queue* q);
extern void queue_clear(struct event_queue* q); extern void queue_clear(struct event_queue* q);
extern void queue_remove_from_head(struct event_queue *q, long id); extern void queue_remove_from_head(struct event_queue *q, long id);
extern int queue_count(const struct event_queue *q);
extern int queue_broadcast(long id, intptr_t data); extern int queue_broadcast(long id, intptr_t data);
extern void mutex_init(struct mutex *m); extern void mutex_init(struct mutex *m);

View file

@ -374,6 +374,27 @@ void queue_remove_from_head(struct event_queue *q, long id)
set_irq_level(oldlevel); set_irq_level(oldlevel);
} }
/**
* The number of events waiting in the queue.
*
* @param struct of event_queue
* @return number of events in the queue
*/
int queue_count(const struct event_queue *q)
{
int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
int result = 0;
if (q->read <= q->write)
result = q->write - q->read;
else
result = QUEUE_LENGTH - (q->read - q->write);
set_irq_level(oldlevel);
return result;
}
int queue_broadcast(long id, intptr_t data) int queue_broadcast(long id, intptr_t data)
{ {
int i; int i;