From df297ec8fc610edd94b46785f765062dd8d2a7b0 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 17 Apr 2016 16:48:30 -0600 Subject: [PATCH] VNC: Add some rectangle queuing logic --- graphics/vnc/server/Kconfig | 24 ++++++- graphics/vnc/server/vnc_negotiate.c | 2 +- graphics/vnc/server/vnc_server.c | 31 +++++++- graphics/vnc/server/vnc_server.h | 42 +++++++++-- graphics/vnc/server/vnc_updater.c | 105 ++++++++++++++++++++++++++-- 5 files changed, 188 insertions(+), 16 deletions(-) diff --git a/graphics/vnc/server/Kconfig b/graphics/vnc/server/Kconfig index 7af1dccf52..1a5ad3ad71 100644 --- a/graphics/vnc/server/Kconfig +++ b/graphics/vnc/server/Kconfig @@ -71,6 +71,26 @@ config VNCSERVER_SCREENHEIGHT int "Framebuffer height (rows)" default 240 +config VNCSERVER_NUPDATES + int "Number of pre-allocate update structures" + default 48 + ---help--- + This setting provides the number of pre-allocated update structures + that will be used. Dynamic memory allocations are never made. In + the likely event that we run out of update structures, the graphics + subsystem will pause and wait for the next structures to be released. + + Overhead is 12-bytes per update structure. + +config VNCSERVER_UPDATE_BUFSIZE + int "Max update buffer size (bytes)" + default 4096 + ---help--- + A single buffer is pre-allocated for rendering updates. This + setting specifies the maximum in bytes of that update buffer. For + example, an update buffers of 32 pixels at 32-bits per pixel and + 32-rows would yield a buffer size of 4096. + config VNCSERVER_KBDENCODE bool "Encode keyboard input" default n @@ -79,8 +99,8 @@ config VNCSERVER_KBDENCODE Use a special encoding of keyboard characters as defined in include/nuttx/input/kbd_coded.h. -config VNCSERVER_IOBUFFER_SIZE - int "I/O buffer size +config VNCSERVER_INBUFFER_SIZE + int "Input buffer size default 80 endif # VNCSERVER diff --git a/graphics/vnc/server/vnc_negotiate.c b/graphics/vnc/server/vnc_negotiate.c index 39b9681e69..34430cf19f 100644 --- a/graphics/vnc/server/vnc_negotiate.c +++ b/graphics/vnc/server/vnc_negotiate.c @@ -274,7 +274,7 @@ int vnc_negotiate(FAR struct vnc_session_s *session) */ (void)psock_recv(&session->connect, session->inbuf, - CONFIG_VNCSERVER_IOBUFFER_SIZE, 0); + CONFIG_VNCSERVER_INBUFFER_SIZE, 0); session->state = VNCSERVER_CONFIGURED; return OK; diff --git a/graphics/vnc/server/vnc_server.c b/graphics/vnc/server/vnc_server.c index 77ed0f6410..5dffc3606f 100644 --- a/graphics/vnc/server/vnc_server.c +++ b/graphics/vnc/server/vnc_server.c @@ -41,7 +41,9 @@ #include #include +#include #include +#include #include #include #include @@ -50,6 +52,7 @@ #include #include +#include #include #include "vnc_server.h" @@ -91,6 +94,10 @@ static FAR struct vnc_session_s *g_vnc_sessions[RFB_MAX_DISPLAYS]; static void vnc_reset_session(FAR struct vnc_session_s *session, FAR uint8_t *fb) { + FAR struct vnc_fbupdate_s *curr; + FAR struct vnc_fbupdate_s *next; + int i; + /* Close any open sockets */ if (session->state >= VNCSERVER_CONNECTED) @@ -99,12 +106,29 @@ static void vnc_reset_session(FAR struct vnc_session_s *session, psock_close(&session->listen); } - /* [Re-]nitialize the session. Set all values to 0 == NULL == false. */ + /* [Re-]initialize the session. */ + /* Put all of the pre-allocated update structures into the freelist */ - memset(session, 0, sizeof(struct vnc_session_s)); + sq_init(&session->updqueue); - /* Then initialize only non-zero values */ + session->updfree.head = + (FAR sq_entry_t *)&session->updpool[0]; + session->updfree.tail = + (FAR sq_entry_t *)&session->updpool[CONFIG_VNCSERVER_NUPDATES-1]; + next = &session->updpool[0]; + for (i = 1; i < CONFIG_VNCSERVER_NUPDATES-1; i++) + { + curr = next; + next = &session->updpool[i]; + curr->flink = next; + } + + next->flink = NULL; + + /* Set the INITIALIZED state */ + + sem_reset(&session->updsem, CONFIG_VNCSERVER_NUPDATES); session->fb = fb; session->state = VNCSERVER_INITIALIZED; } @@ -242,6 +266,7 @@ int vnc_server(int argc, FAR char *argv[]) } g_vnc_sessions[display] = session; + sem_init(&session->updsem, 0, CONFIG_VNCSERVER_NUPDATES); /* Loop... handling each each VNC client connection to this display. Only * a single client is allowed for each display. diff --git a/graphics/vnc/server/vnc_server.h b/graphics/vnc/server/vnc_server.h index b60ff2a17c..dc85dca47c 100644 --- a/graphics/vnc/server/vnc_server.h +++ b/graphics/vnc/server/vnc_server.h @@ -43,9 +43,12 @@ #include #include +#include #include +#include #include +#include #include #include #include @@ -126,10 +129,21 @@ # define CONFIG_VNCSERVER_UPDATER_STACKSIZE 2048 #endif -#ifndef CONFIG_VNCSERVER_IOBUFFER_SIZE -# define CONFIG_VNCSERVER_IOBUFFER_SIZE 80 +#ifndef CONFIG_VNCSERVER_INBUFFER_SIZE +# define CONFIG_VNCSERVER_INBUFFER_SIZE 80 #endif +#ifndef CONFIG_VNCSERVER_NUPDATES +# define CONFIG_VNCSERVER_NUPDATES 48 +#endif + +#ifndef CONFIG_VNCSERVER_UPDATE_BUFSIZE +# define CONFIG_VNCSERVER_UPDATE_BUFSIZE 4096 +#endif + +#define VNCSERVER_UPDATE_BUFSIZE \ + (CONFIG_VNCSERVER_UPDATE_BUFSIZE + SIZEOF_RFB_FRAMEBUFFERUPDATE_S(0)) + /* Local framebuffer characteristics in bytes */ #define RFB_BYTESPERPIXEL ((RFB_BITSPERPIXEL + 7) >> 8) @@ -168,6 +182,16 @@ enum vnc_server_e VNCSERVER_STOPPING /* The server has been asked to stop */ }; +/* This structure is used to queue FrameBufferUpdate event. It includes a + * pointer to support singly linked list. + */ + +struct vnc_fbupdate_s +{ + FAR struct vnc_fbupdate_s *flink; + struct nxgl_rect_s rect; /* The enqueued update rectangle */ +}; + struct vnc_session_s { /* NX graphics system */ @@ -185,15 +209,23 @@ struct vnc_session_s uint8_t colorfmt; /* Remote color format (See include/nuttx/fb.h) */ uint8_t bpp; /* Remote bits per pixel */ FAR uint8_t *fb; /* Allocated local frame buffer */ - + /* Updater information */ pthread_t updater; /* Updater thread ID */ + /* Update list information */ + + struct vnc_fbupdate_s updpool[CONFIG_VNCSERVER_NUPDATES]; + sq_queue_t updfree; + sq_queue_t updqueue; + sem_t exclsem; + sem_t updsem; + /* I/O buffers for misc network send/receive */ - uint8_t inbuf[CONFIG_VNCSERVER_IOBUFFER_SIZE]; - uint8_t outbuf[CONFIG_VNCSERVER_IOBUFFER_SIZE]; + uint8_t inbuf[CONFIG_VNCSERVER_INBUFFER_SIZE]; + uint8_t outbuf[VNCSERVER_UPDATE_BUFSIZE]; }; /**************************************************************************** diff --git a/graphics/vnc/server/vnc_updater.c b/graphics/vnc/server/vnc_updater.c index 4db204c9b4..1837b60cd3 100644 --- a/graphics/vnc/server/vnc_updater.c +++ b/graphics/vnc/server/vnc_updater.c @@ -39,8 +39,11 @@ #include +#include #include +#include #include +#include #include #include @@ -50,6 +53,81 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: vnc_alloc_update + * + * Description: + * Allocate one update structure by taking it from the freelist. + * + * Input Parameters: + * session - A reference to the VNC session structure. + * + * Returned Value: + * A non-NULL structure pointer should always be returned. This function + * will wait if no structure is available. + * + ****************************************************************************/ + +static FAR struct vnc_fbupdate_s * +vnc_alloc_update(FAR struct vnc_session_s *session) +{ + FAR struct vnc_fbupdate_s *update; + + /* Reserve one element from the free list. Lock the scheduler to assure + * that the sq_remfirst() and the successful return for sem_wait are + * atomic. Of course, the scheduler will be unlocked while we wait. + */ + + sched_lock(); + while (sem_wait(&session->updsem) < 0) + { + DEBUGASSERT(get_errno() == EINTR); + } + + /* It is reserved.. go get it */ + + update = (FAR struct vnc_fbupdate_s *)sq_remfirst(&session->updfree); + sched_unlock(); + + DEBUGASSERT(update != NULL); + return update; +} + +/**************************************************************************** + * Name: vnc_free_update + * + * Description: + * Free one update structure by returning it from the freelist. + * + * Input Parameters: + * Standard pthread arguments. + * + * Returned Value: + * NULL is always returned. + * + ****************************************************************************/ + +static void vnc_free_update(FAR struct vnc_session_s *session, + FAR struct vnc_fbupdate_s *update) +{ + /* Reserve one element from the free list. Lock the scheduler to assure + * that the sq_addlast() and the sem_post() are atomic. + */ + + sched_lock(); + + /* Put the entry into the free list */ + + sq_addlast((FAR sq_entry_t *)update, &session->updfree); + + /* Post the semaphore to indicate the availability of one more update */ + + sem_post(&session->updsem); + DEBUGASSERT(session->updsem.semcount <= CONFIG_VNCSERVER_NUPDATES); + + sched_unlock(); +} + /**************************************************************************** * Name: vnc_updater * @@ -189,12 +267,29 @@ int vnc_stop_updater(FAR struct vnc_session_s *session) int vnc_update_rectangle(FAR struct vnc_session_s *session, FAR const struct nxgl_rect_s *rect) { - /* Make sure that the rectangle has a area */ + FAR struct vnc_fbupdate_s *update; - if (!nxgl_nullrect(rect)) - { -#warning Missing logic - } + /* Make sure that the rectangle has a area */ + + if (!nxgl_nullrect(rect)) + { + /* Allocate an update structure... waiting if necessary */ + + update = vnc_alloc_update(session); + DEBUGASSERT(update != NULL); + + /* Copy the rectangle into the update structure */ + + memcpy(&update->rect, rect, sizeof(struct nxgl_rect_s)); + + /* Add the upate to the end of the update queue. Lock the scheduler + * to assure that the sq_addlast() is atomic. + */ + + sched_lock(); + sq_addlast((FAR sq_entry_t *)update, &session->updqueue); + sched_unlock(); + } return -ENOSYS; }