diff --git a/configure.ac b/configure.ac index 02e97817..5c96c3f0 100644 --- a/configure.ac +++ b/configure.ac @@ -95,7 +95,7 @@ message_string="no" osname="unspecified" runtime_mode="procs" compositing_schema="yes" -nr_update_threads=0 +nr_concurrent_tasks=0 virtual_window="no" use_shmopen="no" mgslice_use_fallback="no" @@ -861,9 +861,9 @@ if test "x$runtime_mode" == "xprocs"; then [ --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) + AC_ARG_WITH(concurrent_tasks, + [ --with-concurrent-tasks=[0/1/3/7]], + nr_concurrent_tasks=$withval) fi if test "x$runtime_mode" != "xths"; then @@ -1783,33 +1783,29 @@ 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 + case "$nr_concurrent_tasks" in 1) - AC_DEFINE(_MGNR_UPDATE_THREADS, 1, - [The number of concurrent update threads.]) + AC_DEFINE(_MGNR_CONCURRENT_TASKS, 1, + [The number of concurrent threads.]) ;; - 2) - AC_DEFINE(_MGNR_UPDATE_THREADS, 2, - [The number of concurrent update threads.]) + 3) + AC_DEFINE(_MGNR_CONCURRENT_TASKS, 3, + [The number of concurrent 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.]) + 7) + AC_DEFINE(_MGNR_CONCURRENT_TASKS, 7, + [The number of concurrent threads.]) ;; *) - AC_DEFINE(_MGNR_UPDATE_THREADS, 0, - [The number of concurrent update threads.]) + AC_DEFINE(_MGNR_CONCURRENT_TASKS, 0, + [The number of concurrent 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.]) + AC_DEFINE(_MGNR_CONCURRENT_TASKS, 0, + [The number of concurrent threads.]) fi if test "x$virtual_window" = "xyes"; then @@ -2824,7 +2820,7 @@ AC_MSG_NOTICE([ * Runtime mode: ${runtime_mode} * Virtual Window: ${virtual_window} * Surface schema: ${surface_schema} - * Update Threads: ${nr_update_threads} + * Concurrent Tasks: ${nr_concurrent_tasks} * Use shm_open: ${use_shmopen} * Incore resource: ${incore_res} * Fallback mgslice: ${mgslice_use_fallback} diff --git a/include/window.h b/include/window.h index 2e053015..b79b8c61 100644 --- a/include/window.h +++ b/include/window.h @@ -6455,7 +6455,7 @@ MG_EXPORT RES_KEY Str2Key (const char* str); * \fn int GUIAPI GetWindowZOrder(HWND hWnd); * \brief Get The Control ZOrder in the parent window * - * \param hWnd Handler of a control + * \param hWnd Handle of a control * * \return return index of ZOrder or 0 if hWnd is a MainWindow or -1 if error * diff --git a/src/newgal/Makefile.am b/src/newgal/Makefile.am index 04430c8a..b5259b61 100644 --- a/src/newgal/Makefile.am +++ b/src/newgal/Makefile.am @@ -42,6 +42,8 @@ COMMON_SRCS = \ surface-shm.c \ shadow-screen.h \ shadow-screen.c \ + concurrent-tasks.h \ + concurrent-tasks.c \ $(NULL_FILE) libnewgal_la_SOURCES = $(COMMON_SRCS) diff --git a/src/newgal/concurrent-tasks.c b/src/newgal/concurrent-tasks.c new file mode 100644 index 00000000..382deae8 --- /dev/null +++ b/src/newgal/concurrent-tasks.c @@ -0,0 +1,304 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT NOTICE +// +// The following open source license statement does not apply to any +// entity in the Exception List published by FMSoft. +// +// For more information, please visit: +// +// https://www.fmsoft.cn/exception-list +// +////////////////////////////////////////////////////////////////////////////// +/* + * This file is part of MiniGUI, a mature cross-platform windowing + * and Graphics User Interface (GUI) support system for embedded systems + * and smart IoT devices. + * + * Copyright (C) 2021, Beijing FMSoft Technologies Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Or, + * + * As this program is a library, any link to this program must follow + * GNU General Public License version 3 (GPLv3). If you cannot accept + * GPLv3, you need to be licensed from FMSoft. + * + * If you have got a commercial license of this program, please use it + * under the terms and conditions of the commercial license. + * + * For more information about the commercial license, please refer to + * . + */ +/* +** concurrent-tasks.c: +** This file implements the concurrent tasks by using POSIX threads. +** +** Current maintainer: Wei Yongming. +** +** Create date: 2021/05/21 +*/ + +#include +#include +#include + +#include "common.h" +#include "minigui.h" +#include "constants.h" +#include "newgal.h" + +#include +#include + +typedef void (*task_proc)(void* context, int loop_idx); + +typedef struct _ConcurrentTasksInfo { + pthread_t pths[_MGNR_CONCURRENT_TASKS]; + sem_t sem_loop; + sem_t sem_lock; + sem_t sem_sync; + + int nr_loops; + task_proc proc; + void* ctxt; +} ConcurrentTasksInfo; + +static ConcurrentTasksInfo *cctasks_info; + +static inline int cc_sem_wait (sem_t *sem) +{ +try_again: + if (sem_wait (sem) < 0) { + if (errno == EINTR) + goto try_again; + else { + _WRN_PRINTF ("Failed sem_wait: %s\n", strerror (errno)); + return -1; + } + } + + return 0; +} + +static void* cc_task_entry (void* data) +{ + int task_idx = (int)(intptr_t)data; + + if (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL)) + return NULL; + + do { + int loop_idx; + + if (cc_sem_wait (&cctasks_info->sem_loop)) { + _ERR_PRINTF ("CCTasks: thread %d failed on sem_wait\n", task_idx); + return NULL; + } + + cc_sem_wait (&cctasks_info->sem_lock); + loop_idx = cctasks_info->nr_loops++; + sem_post (&cctasks_info->sem_lock); + + pthread_testcancel (); + + cctasks_info->proc (cctasks_info->ctxt, loop_idx); + + if (sem_post (&cctasks_info->sem_sync)) { + _ERR_PRINTF ("CCTasks: thread %d failed on sem_post\n", task_idx); + return NULL; + } + + pthread_testcancel (); + + } while (1); + + return NULL; +} + +static void split_rect (GAL_Rect* rcs, const GAL_Rect* rc, int count) +{ + if (count == 1) { + rcs[0] = *rc; + } + else if (count == 2) { + rcs[0].x = rc->x; + rcs[0].y = rc->y; + rcs[0].w = rc->w; + rcs[0].h = rc->h >> 1; + + rcs[1].x = rc->x; + rcs[1].y = rcs[0].y + rcs[0].h; + rcs[1].w = rc->w; + rcs[1].h = rc->h - rcs[0].h; + } + else if (count == 4) { + rcs[0].x = rc->x; + rcs[0].y = rc->y; + rcs[0].w = rc->w; + rcs[0].h = rc->h >> 2; + + rcs[1].x = rc->x; + rcs[1].y = rcs[0].y+ rcs[0].h; + rcs[1].w = rc->w; + rcs[1].h = rc->h >> 2; + + rcs[2].x = rc->x; + rcs[2].y = rcs[1].y+ rcs[1].h; + rcs[2].w = rc->w; + rcs[2].h = rc->h >> 2; + + rcs[3].x = rc->x; + rcs[3].y = rcs[2].y+ rcs[2].h; + rcs[3].w = rc->w; + rcs[3].h = rc->h - rcs[0].h - rcs[1].h - rcs[2].h; + } + else { + assert (0); + } +} + +typedef struct _ContextBlit { + GAL_Surface *src; + GAL_Surface *dst; + + GAL_blit real_blit; + + GAL_Rect src_rects [_MGNR_CONCURRENT_TASKS + 1]; + GAL_Rect dst_rects [_MGNR_CONCURRENT_TASKS + 1]; +} ContextBlit; + +static void blit_proc (void* context, int loop_idx) +{ + ContextBlit *ctxt = context; + + if (ctxt->src_rects[loop_idx].h > 0) { + ctxt->real_blit (ctxt->src, ctxt->src_rects + loop_idx, + ctxt->dst, ctxt->dst_rects + loop_idx); + } +} + +int concurrentTasks_Blit (GAL_blit real_blit, + struct GAL_Surface *src, GAL_Rect *srcrect, + struct GAL_Surface *dst, GAL_Rect *dstrect) +{ + int i; + ContextBlit ctxt; + + ctxt.src = src; + ctxt.dst = dst; + ctxt.real_blit = real_blit; + + split_rect (ctxt.src_rects, srcrect, _MGNR_CONCURRENT_TASKS + 1); + split_rect (ctxt.dst_rects, dstrect, _MGNR_CONCURRENT_TASKS + 1); + + if (_MGNR_CONCURRENT_TASKS > 0) { + cctasks_info->nr_loops = 1; // reserve 0 for the current thread + cctasks_info->proc = blit_proc; + cctasks_info->ctxt = &ctxt; + } + + // wake up the concurrent task threads + for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) { + sem_post (&cctasks_info->sem_loop); + } + + blit_proc (&ctxt, 0); + + // wait for finish of concurrent task threads + for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) { + if (cc_sem_wait (&cctasks_info->sem_sync) < 0) + return -1; + } + + return 0; +} + +int concurrentTasks_Init (void) +{ + int i; + + if (_MGNR_CONCURRENT_TASKS <= 0) + return 0; + + if ((cctasks_info = calloc (1, sizeof (ConcurrentTasksInfo))) == NULL) { + _ERR_PRINTF ("CCTasks: failed to allocate memory\n"); + goto failed_sem_loop; + } + + if (sem_init (&cctasks_info->sem_loop, 0, 0)) { + _ERR_PRINTF ("CCTasks: failed to create loop semaphore: %s\n", + strerror (errno)); + goto failed_sem_loop; + } + + if (sem_init (&cctasks_info->sem_sync, 0, 0)) { + _ERR_PRINTF ("CCTasks: failed to create sync semaphore: %s\n", + strerror (errno)); + goto failed_sem_sync; + } + + if (sem_init (&cctasks_info->sem_lock, 0, 1)) { + _ERR_PRINTF ("CCTasks: failed to create lock semaphore: %s\n", + strerror (errno)); + goto failed_sem_lock; + } + + for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) { + if (pthread_create (cctasks_info->pths + i, NULL, cc_task_entry, + (void*)(intptr_t)(i + 1))) { + _ERR_PRINTF ("CCTasks: failed to create update thread (%d): %s\n", + i, strerror (errno)); + goto failed_threads; + } + } + + return 0; + +failed_threads: + sem_destroy (&cctasks_info->sem_lock); + +failed_sem_lock: + sem_destroy (&cctasks_info->sem_sync); + +failed_sem_sync: + sem_destroy (&cctasks_info->sem_loop); + +failed_sem_loop: + return -1; +} + +int concurrentTasks_Term (void) +{ + int i; + + if (_MGNR_CONCURRENT_TASKS <= 0) + return 0; + + for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) { + _WRN_PRINTF ("Cancelling concurrent task thread: %d\n", i); + /* send cancel request */ + pthread_cancel (cctasks_info->pths [i]); + pthread_join (cctasks_info->pths [i], NULL); + } + + sem_destroy (&cctasks_info->sem_sync); + sem_destroy (&cctasks_info->sem_loop); + sem_destroy (&cctasks_info->sem_lock); + + free (cctasks_info); + return 0; +} + diff --git a/src/newgal/concurrent-tasks.h b/src/newgal/concurrent-tasks.h new file mode 100644 index 00000000..b3fa190c --- /dev/null +++ b/src/newgal/concurrent-tasks.h @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT NOTICE +// +// The following open source license statement does not apply to any +// entity in the Exception List published by FMSoft. +// +// For more information, please visit: +// +// https://www.fmsoft.cn/exception-list +// +////////////////////////////////////////////////////////////////////////////// +/* + * This file is part of MiniGUI, a mature cross-platform windowing + * and Graphics User Interface (GUI) support system for embedded systems + * and smart IoT devices. + * + * Copyright (C) 2002~2020, Beijing FMSoft Technologies Co., Ltd. + * Copyright (C) 1998~2002, WEI Yongming + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Or, + * + * As this program is a library, any link to this program must follow + * GNU General Public License version 3 (GPLv3). If you cannot accept + * GPLv3, you need to be licensed from FMSoft. + * + * If you have got a commercial license of this program, please use it + * under the terms and conditions of the commercial license. + * + * For more information about the commercial license, please refer to + * . + */ +/* +** The helpers for concurrent tasks. +** +** Current maintainer: Wei Yongming. +** +** Create date: 2021/05/21 +*/ + +#ifndef _GAL_concurrent_tasks_h +#define _GAL_concurrent_tasks_h + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int concurrentTasks_Init (void); +int concurrentTasks_Term (void); +int concurrentTasks_Blit (GAL_blit real_blit, + struct GAL_Surface *src, GAL_Rect *srcrect, + struct GAL_Surface *dst, GAL_Rect *dstrect); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _GAL_concurrent_tasks_h */ diff --git a/src/newgal/shadow-screen.c b/src/newgal/shadow-screen.c index 983bb1d4..93f31cf7 100644 --- a/src/newgal/shadow-screen.c +++ b/src/newgal/shadow-screen.c @@ -304,10 +304,12 @@ int shadowScreen_BlitToReal (_THIS) static struct UpdateThreadsInfo { GAL_VideoDevice *device; - sem_t sem_tasks[_MGNR_UPDATE_THREADS]; + sem_t sem_loop; + sem_t sem_lock; sem_t sem_sync; pthread_t pths[_MGNR_UPDATE_THREADS]; + int nr_loops; RECT bound; } cuth_info; @@ -353,25 +355,31 @@ try_again: static void* task_do_update (void* data) { - int idx = (int)(intptr_t)data; + int task_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); + int loop_idx; + + if (my_sem_wait (&cuth_info.sem_loop)) { + _ERR_PRINTF ("Update thread %d failed on sem_wait\n", task_idx); return NULL; } + my_sem_wait (&cuth_info.sem_lock); + loop_idx = cuth_info.nr_loops++; + sem_post (&cuth_info.sem_lock); + pthread_testcancel (); copy_lines (cuth_info.device->hidden->shadow_screen, cuth_info.device->hidden->real_screen, - &cuth_info.bound, idx); + &cuth_info.bound, loop_idx); if (sem_post (&cuth_info.sem_sync)) { - _ERR_PRINTF ("Update thread %d failed on sem_post\n", idx); + _ERR_PRINTF ("Update thread %d failed on sem_post\n", task_idx); return NULL; } @@ -390,12 +398,10 @@ int shadowScreen_InitUpdateThreads (_THIS) 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_loop, 0, 0)) { + _ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create loop semaphore: %s\n", + strerror (errno)); + goto failed_sem_update; } if (sem_init (&cuth_info.sem_sync, 0, 0)) { @@ -404,6 +410,12 @@ int shadowScreen_InitUpdateThreads (_THIS) goto failed_sem_sync; } + if (sem_init (&cuth_info.sem_lock, 0, 1)) { + _ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create lock semaphore: %s\n", + strerror (errno)); + goto failed_sem_lock; + } + for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { if (pthread_create (cuth_info.pths + i, NULL, task_do_update, (void*)(intptr_t)(i + 1))) { @@ -417,12 +429,13 @@ int shadowScreen_InitUpdateThreads (_THIS) return 0; failed_threads: + sem_destroy (&cuth_info.sem_lock); + +failed_sem_lock: sem_destroy (&cuth_info.sem_sync); failed_sem_sync: - for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { - sem_destroy (cuth_info.sem_tasks + i); - } + sem_destroy (&cuth_info.sem_loop); failed_sem_update: return -1; @@ -440,9 +453,8 @@ int shadowScreen_TermUpdateThreads (_THIS) } sem_destroy (&cuth_info.sem_sync); - for (i = 0; i < _MGNR_UPDATE_THREADS; i++) { - sem_destroy (cuth_info.sem_tasks + i); - } + sem_destroy (&cuth_info.sem_loop); + sem_destroy (&cuth_info.sem_lock); return 0; } @@ -491,11 +503,12 @@ int shadowScreen_BlitToReal (_THIS) CHECK_VERSION_RETVAL (this, -1); + cuth_info.nr_loops = 1; // reserve 0 for the current thread 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); + sem_post (&cuth_info.sem_loop); } copy_lines (cuth_info.device->hidden->shadow_screen, diff --git a/src/newgal/surface.c b/src/newgal/surface.c index fcdf9b49..a5f05f07 100644 --- a/src/newgal/surface.c +++ b/src/newgal/surface.c @@ -59,6 +59,7 @@ #ifdef _MGRM_PROCESSES #include /* for munmap */ +#include "concurrent-tasks.h" #endif /* _MGRM_PROCESSES */ /* Public routines */ @@ -538,38 +539,20 @@ int GAL_LowerBlit (GAL_Surface *src, GAL_Rect *srcrect, } #endif -#ifdef MG_CONFIG_USE_OWN_OVERLAPPED_BITBLIT - ret = own_overlapped_bitblit(do_blit, src, srcrect, dst, dstrect); +#ifdef _MGRM_PROCESSES + // we use concurrent tasks only under MiniGUI-Processes. + concurrentTasks_Blit (do_blit, src, srcrect, dst, dstrect); #else +# ifdef MG_CONFIG_USE_OWN_OVERLAPPED_BITBLIT + ret = own_overlapped_bitblit(do_blit, src, srcrect, dst, dstrect); +# else ret = do_blit(src, srcrect, dst, dstrect); -#endif - -#if 0 - { - static int n_sw=0, n_hw=0; - if ((src->flags & GAL_HWACCEL) == GAL_HWACCEL) - { - n_hw ++; - } - else - { - n_sw ++; - } - printf("[%06u] hw/blit=%d/%d %c size=%dX%d src=(%d %d),%p dst=(%d %d),%p\n", - times(NULL) % 1000000, - n_hw, n_hw+n_sw, - do_blit == src->map->sw_blit ? 'S':'H', - srcrect->w, srcrect->h, - srcrect->x, srcrect->y, src, - dstrect->x, dstrect->y, dst - ); - } +# endif #endif return ret; } - int GAL_UpperBlit (GAL_Surface *src, GAL_Rect *srcrect, GAL_Surface *dst, GAL_Rect *dstrect, DWORD op) {