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
#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
static int offset_step = 16; /* pixels per screen scroll step */
/* should lines scroll out of the screen */
static bool offset_out_of_view = false;
#endif
static struct gui_list* last_list_displayed[NB_SCREENS];
#define SHOW_LIST_TITLE ((gui_list->title != NULL) && \
(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_width = 0;
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
* - 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;
int cursor_pos = 0;
@ -231,15 +240,51 @@ static void gui_list_draw(struct gui_list * gui_list)
int item_offset;
int old_margin = display->getxmargin();
#endif
int start, end;
bool partial_draw = false;
gui_textarea_clear(display);
/* 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);
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 */
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)
{
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);
#endif
}
else
{
i = 0;
lines = display->nb_lines;
}
/* Adjust the position of icon, cursor, text for the list */
#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 */
#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;
char entry_buffer[MAX_PATH];
@ -338,26 +385,33 @@ static void gui_list_draw(struct gui_list * gui_list)
{/* The selected item must be displayed scrolling */
#ifdef HAVE_LCD_BITMAP
if (global_settings.invert_cursor)/* Display inverted-line-style*/
{
/* if text got out of view */
if (item_offset > item_width - (display->width - text_pos))
{
/* don't scroll */
display->puts_style_offset(0, i, entry_name,
STYLE_INVERT,item_offset);
}
else
{
display->puts_scroll_style_offset(0, i, entry_name,
STYLE_INVERT,
item_offset);
}
}
else /* if (!global_settings.invert_cursor) */
{
if (item_offset > item_width - (display->width - text_pos))
display->puts_offset(0, i, entry_name,item_offset);
else
display->puts_scroll_offset(0, i, entry_name,item_offset);
else
display->puts_scroll_offset(0, i, entry_name,item_offset);
}
#else
display->puts_scroll(text_pos, i, entry_name);
display->puts_scroll(text_pos, i, entry_name);
#endif
if(draw_cursor)
if (draw_cursor)
screen_put_cursorxy(display, cursor_pos, i, true);
}
else
@ -389,7 +443,6 @@ static void gui_list_draw(struct gui_list * gui_list)
if(icon)
screen_put_iconxy(display, icon_pos, i, icon);
}
i++;
}
#ifdef HAVE_LCD_BITMAP
@ -413,6 +466,15 @@ static void gui_list_draw(struct gui_list * gui_list)
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
* - 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);
}
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
* (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
{
gui_list->selected_item+=gui_list->selected_size;
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 (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;
}
scroll_down(gui_list, global_settings.scroll_paginated);
}
}
@ -499,27 +593,8 @@ static void gui_list_select_previous(struct gui_list * gui_list)
}
else
{
int item_pos;
gui_list->selected_item-=gui_list->selected_size;
item_pos = gui_list->selected_item - gui_list->start_item;
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;
gui_list->selected_item -= gui_list->selected_size;
scroll_up(gui_list, global_settings.scroll_paginated);
}
}
@ -850,14 +925,20 @@ unsigned gui_synclist_do_button(struct gui_synclist * lists,
case ACTION_STD_PREV:
case ACTION_STD_PREVREPEAT:
gui_synclist_select_previous(lists);
gui_synclist_draw(lists);
#ifndef SIMULATOR
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
gui_synclist_draw(lists);
yield();
return ACTION_STD_PREV;
case ACTION_STD_NEXT:
case ACTION_STD_NEXTREPEAT:
gui_synclist_select_next(lists);
gui_synclist_draw(lists);
#ifndef SIMULATOR
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
gui_synclist_draw(lists);
yield();
return ACTION_STD_NEXT;

View file

@ -91,6 +91,10 @@ struct gui_list
int selected_size;
/* The data that will be passed to the callback function YOU implement */
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 */
char *title;
/* 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_list gui_list[NB_SCREENS];
struct gui_list *last_displayed[NB_SCREENS];
};
extern void gui_synclist_init(

View file

@ -26,6 +26,7 @@
#include "system.h"
#include "button.h"
#include "kernel.h"
#include "thread.h"
#include "backlight.h"
#include "serial.h"
#include "power.h"
@ -255,21 +256,52 @@ static void button_tick(void)
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)
{
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);
return ev.id;
}
return BUTTON_NONE;
}
long button_get_w_tmo(int ticks)
{
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);
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 void queue_clear(struct event_queue* q);
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 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);
}
/**
* 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 i;