From 87b9facc8438425dd6d01c93aaf83d7134fa6be3 Mon Sep 17 00:00:00 2001 From: Vincent Wei Date: Thu, 20 May 2021 22:14:28 +0800 Subject: [PATCH] initial implementation of concurrent blitting --- configure.ac | 41 +++++- src/kernel/compsor-fallback.c | 2 +- src/newgal/blit.c | 4 +- src/newgal/pcxvfb/pcxvfb.c | 5 + src/newgal/shadow-screen.c | 257 ++++++++++++++++++++++++++++++++++ src/newgal/shadow-screen.h | 2 + 6 files changed, 303 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 8462d715..02e97817 100644 --- a/configure.ac +++ b/configure.ac @@ -95,6 +95,7 @@ message_string="no" osname="unspecified" runtime_mode="procs" compositing_schema="yes" +nr_update_threads=0 virtual_window="no" use_shmopen="no" mgslice_use_fallback="no" @@ -853,12 +854,16 @@ build_ctrl_animation=$enableval) if test "x$runtime_mode" == "xprocs"; then AC_ARG_ENABLE(compositing, -[ --enable-compositing enable compositing schema (MiniGUI-Processes runmode only) ], - compositing_schema=$enableval) + [ --enable-compositing enable compositing schema (MiniGUI-Processes runmode only) ], + compositing_schema=$enableval) AC_ARG_ENABLE(shmopen, -[ --enable-shmopen use shm_open ], - use_shmopen=$enableval) + [ --enable-shmopen use shm_open ], + use_shmopen=$enableval) + + AC_ARG_WITH(update_threads, + [ --with-update-threads=[0/1/2/4/8]], + nr_update_threads=$withval) fi if test "x$runtime_mode" != "xths"; then @@ -1755,7 +1760,7 @@ case "$runtime_mode" in [Define if build MiniGUI-Processes]) MINIGUI_RUNMODE="procs" if test "x$compositing_schema" = "xyes"; then - surface_schema="compositing" + surface_schema="compositing" fi if test "x$use_shmopen" = "xyes"; then @@ -1777,9 +1782,34 @@ esac if test "x$surface_schema" = "xcompositing"; then AC_DEFINE(_MGSCHEMA_COMPOSITING, 1, [Define if use compositing schema for the current runmode]) + + case "$nr_update_threads" in + 1) + AC_DEFINE(_MGNR_UPDATE_THREADS, 1, + [The number of concurrent update threads.]) + ;; + 2) + AC_DEFINE(_MGNR_UPDATE_THREADS, 2, + [The number of concurrent update threads.]) + ;; + 4) + AC_DEFINE(_MGNR_UPDATE_THREADS, 4, + [The number of concurrent update threads.]) + ;; + 8) + AC_DEFINE(_MGNR_UPDATE_THREADS, 8, + [The number of concurrent update threads.]) + ;; + *) + AC_DEFINE(_MGNR_UPDATE_THREADS, 0, + [The number of concurrent update threads.]) + ;; + esac else AC_DEFINE(_MGSCHEMA_SHAREDFB, 1, [Define if use legacy schema (shared frame buffer) for the current runmode]) + AC_DEFINE(_MGNR_UPDATE_THREADS, 0, + [The number of asynchronous update threads.]) fi if test "x$virtual_window" = "xyes"; then @@ -2794,6 +2824,7 @@ AC_MSG_NOTICE([ * Runtime mode: ${runtime_mode} * Virtual Window: ${virtual_window} * Surface schema: ${surface_schema} + * Update Threads: ${nr_update_threads} * Use shm_open: ${use_shmopen} * Incore resource: ${incore_res} * Fallback mgslice: ${mgslice_use_fallback} diff --git a/src/kernel/compsor-fallback.c b/src/kernel/compsor-fallback.c index f6b74ec3..2c2b079b 100644 --- a/src/kernel/compsor-fallback.c +++ b/src/kernel/compsor-fallback.c @@ -1303,7 +1303,7 @@ static void transit_to_layer (CompositorCtxt* ctxt, MG_Layer* to_layer) } } - _DBG_PRINTF ("Average time to composite the layers: %u (times: %u)\n", total_time_ms / total_times, total_times); + _WRN_PRINTF ("Average time to composite the layers: %u (times: %u)\n", total_time_ms / total_times, total_times); } CompositorOps __mg_fallback_compositor = { diff --git a/src/newgal/blit.c b/src/newgal/blit.c index 2211d87e..901c1b37 100644 --- a/src/newgal/blit.c +++ b/src/newgal/blit.c @@ -77,13 +77,13 @@ static int GAL_SoftBlit(GAL_Surface *src, GAL_Rect *srcrect, GAL_loblit RunBlit; /* Set up the blit information */ - info.s_pixels = (Uint8 *)src->pixels + + info.s_pixels = (Uint8 *)src->pixels + src->pixels_off + (Uint16)srcrect->y*src->pitch + (Uint16)srcrect->x*src->format->BytesPerPixel; info.s_width = srcrect->w; info.s_height = srcrect->h; info.s_skip=src->pitch-info.s_width*src->format->BytesPerPixel; - info.d_pixels = (Uint8 *)dst->pixels + + info.d_pixels = (Uint8 *)dst->pixels + dst->pixels_off + (Uint16)dstrect->y*dst->pitch + (Uint16)dstrect->x*dst->format->BytesPerPixel; info.d_width = dstrect->w; diff --git a/src/newgal/pcxvfb/pcxvfb.c b/src/newgal/pcxvfb/pcxvfb.c index e9e34faf..77692e51 100644 --- a/src/newgal/pcxvfb/pcxvfb.c +++ b/src/newgal/pcxvfb/pcxvfb.c @@ -765,6 +765,10 @@ static GAL_Surface *PCXVFB_SetVideoMode (_THIS, GAL_Surface *current, this->hidden->shadow_screen = NULL; } + if (this->hidden->real_screen) { + shadowScreen_InitUpdateThreads(this); + } + /* We're done */ return current; } @@ -828,6 +832,7 @@ static void PCXVFB_VideoQuit (_THIS) /* Since 5.0.0 */ if (this->hidden->real_screen) { GAL_FreeSurface (this->hidden->real_screen); + shadowScreen_TermUpdateThreads(this); } #ifdef WIN32 // windows diff --git a/src/newgal/shadow-screen.c b/src/newgal/shadow-screen.c index 58a2da27..983bb1d4 100644 --- a/src/newgal/shadow-screen.c +++ b/src/newgal/shadow-screen.c @@ -230,6 +230,18 @@ void shadowScreen_UpdateRects (_THIS, int numrects, GAL_Rect *rects) this->hidden->dirty_rc = bound; } +#if _MGNR_UPDATE_THREADS <= 0 + +int shadowScreen_InitUpdateThreads (_THIS) +{ + return 0; +} + +int shadowScreen_TermUpdateThreads (_THIS) +{ + return 0; +} + /* Blit dirty content from shadow surface to ultimate surface */ int shadowScreen_BlitToReal (_THIS) { @@ -285,3 +297,248 @@ int shadowScreen_BlitToReal (_THIS) return 0; } +#else /* _MGNR_UPDATE_THREADS <= 0 */ + +#include +#include + +static struct UpdateThreadsInfo { + GAL_VideoDevice *device; + sem_t sem_tasks[_MGNR_UPDATE_THREADS]; + sem_t sem_sync; + pthread_t pths[_MGNR_UPDATE_THREADS]; + + RECT bound; +} cuth_info; + +static inline void copy_lines (GAL_Surface* src_surf, GAL_Surface* dst_surf, + const RECT *rc, int idx) +{ + int y, h = RECTHP (rc); + uint8_t *src, *dst; + size_t count = src_surf->format->BytesPerPixel * RECTWP (rc); + + if (h <= 0 || count == 0) + return; + + src = src_surf->pixels + src_surf->pixels_off; + src += src_surf->pitch * (rc->top + idx); + src += src_surf->format->BytesPerPixel * rc->left; + + dst = dst_surf->pixels + dst_surf->pixels_off; + dst += dst_surf->pitch * (rc->top + idx); + dst += dst_surf->format->BytesPerPixel * rc->left; + + for (y = idx; y < h; y += (_MGNR_UPDATE_THREADS + 1)) { + memcpy (dst, src, count); + src += src_surf->pitch * (_MGNR_UPDATE_THREADS + 1); + dst += dst_surf->pitch * (_MGNR_UPDATE_THREADS + 1); + } +} + +static inline int my_sem_wait (sem_t *sem) +{ +try_again: + if (sem_wait (sem) < 0) { + if (errno == EINTR) + goto try_again; + else { + _WRN_PRINTF ("NEWGAL>DBLBUFF: failed sem_wait: %s\n", strerror (errno)); + return -1; + } + } + + return 0; +} + +static void* task_do_update (void* data) +{ + int idx = (int)(intptr_t)data; + + if (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL)) + return NULL; + + do { + if (my_sem_wait (cuth_info.sem_tasks + idx - 1)) { + _ERR_PRINTF ("Update thread %d failed on sem_wait\n", idx); + return NULL; + } + + pthread_testcancel (); + + copy_lines (cuth_info.device->hidden->shadow_screen, + cuth_info.device->hidden->real_screen, + &cuth_info.bound, idx); + + if (sem_post (&cuth_info.sem_sync)) { + _ERR_PRINTF ("Update thread %d failed on sem_post\n", idx); + return NULL; + } + + pthread_testcancel (); + + } while (1); + + return NULL; +} + +int shadowScreen_InitUpdateThreads (_THIS) +{ + int i; + + CHECK_VERSION_RETVAL (this, -1); + + assert (this->real_screen && this->shadow_screen); + + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { + if (sem_init (cuth_info.sem_tasks + i, 0, 0)) { + _ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create task semaphore: %s\n", + strerror (errno)); + goto failed_sem_update; + } + } + + if (sem_init (&cuth_info.sem_sync, 0, 0)) { + _ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create sync semaphore: %s\n", + strerror (errno)); + goto failed_sem_sync; + } + + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { + if (pthread_create (cuth_info.pths + i, NULL, task_do_update, + (void*)(intptr_t)(i + 1))) { + _ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create update thread (%d): %s\n", + i, strerror (errno)); + goto failed_threads; + } + } + + cuth_info.device = this; + return 0; + +failed_threads: + sem_destroy (&cuth_info.sem_sync); + +failed_sem_sync: + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { + sem_destroy (cuth_info.sem_tasks + i); + } + +failed_sem_update: + return -1; +} + +int shadowScreen_TermUpdateThreads (_THIS) +{ + int i; + + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { + _WRN_PRINTF ("Cancelling update thread: %d\n", i); + /* send cancel request */ + pthread_cancel (cuth_info.pths [i]); + pthread_join (cuth_info.pths [i], NULL); + } + + sem_destroy (&cuth_info.sem_sync); + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { + sem_destroy (cuth_info.sem_tasks + i); + } + return 0; +} + +#if 0 +static void split_rect (RECT* rcs, const RECT* rc) +{ + if (_MGNR_UPDATE_THREADS == 1) { + rcs[0] = *rc; + } + else if (_MGNR_UPDATE_THREADS == 2) { + rcs[0] = *rc; + rcs[0].bottom -= RECTHP (rc) >> 1; + + rcs[1] = *rc; + rcs[1].top = rcs[0].bottom; + } + else if (_MGNR_UPDATE_THREADS == 4) { + rcs[0] = *rc; + rcs[0].right -= RECTWP (rc) >> 1; + rcs[0].bottom -= RECTHP (rc) >> 1; + + rcs[1].top = rcs[0].top; + rcs[1].left = rcs[0].right; + rcs[1].right = rc->right; + rcs[1].bottom = rcs[0].bottom; + + rcs[2].top = rcs[0].bottom; + rcs[2].left = rcs[0].left; + rcs[2].right = rcs[0].right; + rcs[2].bottom = rc->bottom; + + rcs[3].top = rcs[0].bottom; + rcs[3].left = rcs[2].right; + rcs[3].right = rc->right; + rcs[3].bottom = rc->bottom; + } + else { + assert (0); + } +} +#endif + +int shadowScreen_BlitToReal (_THIS) +{ + int i, value; + + CHECK_VERSION_RETVAL (this, -1); + + cuth_info.bound = this->hidden->dirty_rc; + + // wake up the concurrent update threads + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { + sem_post (cuth_info.sem_tasks + i); + } + + copy_lines (cuth_info.device->hidden->shadow_screen, + cuth_info.device->hidden->real_screen, + &cuth_info.bound, 0); + + // wait for finish of concurrent update threads + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { + if (my_sem_wait (&cuth_info.sem_sync) < 0) + return -1; + } + +#ifdef _MGSCHEMA_COMPOSITING + if (this->hidden->cursor) { + RECT csr_rc, eff_rc; + csr_rc.left = boxleft (this); + csr_rc.top = boxtop (this); + csr_rc.right = csr_rc.left + CURSORWIDTH; + csr_rc.bottom = csr_rc.top + CURSORHEIGHT; + + if (IntersectRect (&eff_rc, &csr_rc, &cuth_info.bound)) { + GAL_Rect src_rect, dst_rect; + + src_rect.x = eff_rc.left - csr_rc.left; + src_rect.y = eff_rc.top - csr_rc.top; + src_rect.w = RECTW (eff_rc); + src_rect.h = RECTH (eff_rc); + + dst_rect.x = eff_rc.left; + dst_rect.y = eff_rc.top; + dst_rect.w = src_rect.w; + dst_rect.h = src_rect.h; + GAL_SetupBlitting (this->hidden->cursor, + this->hidden->real_screen, 0); + GAL_BlitSurface (this->hidden->cursor, &src_rect, + this->hidden->real_screen, &dst_rect); + GAL_CleanupBlitting (this->hidden->cursor, + this->hidden->real_screen); + } + } +#endif /* _MGSCHEMA_COMPOSITING */ + + return 0; +} + +#endif /* _MGNR_UPDATE_THREADS > 0 */ diff --git a/src/newgal/shadow-screen.h b/src/newgal/shadow-screen.h index 5de54c69..40b1d6f1 100644 --- a/src/newgal/shadow-screen.h +++ b/src/newgal/shadow-screen.h @@ -64,6 +64,8 @@ extern "C" { # define _THIS GAL_VideoDevice *this #endif +int shadowScreen_InitUpdateThreads (_THIS); +int shadowScreen_TermUpdateThreads (_THIS); int shadowScreen_SetCursor (_THIS, GAL_Surface *surface, int hot_x, int hot_y); int shadowScreen_MoveCursor (_THIS, int x, int y); void shadowScreen_UpdateRects (_THIS, int numrects, GAL_Rect *rects);