diff --git a/Makefile.emscripten b/Makefile.emscripten index c2e8280803..fde53d7a23 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -56,6 +56,7 @@ HAVE_BSV_MOVIE = 1 HAVE_AL = 1 HAVE_CHD ?= 0 HAVE_NETPLAYDISCOVERY ?= 0 +HAVE_THREADS ?= 0 # WARNING -- READ BEFORE ENABLING # The rwebaudio driver is known to have several audio bugs, such as @@ -79,7 +80,6 @@ PROXY_TO_PTHREAD ?= 0 ASYNC ?= 0 LTO ?= 0 -PTHREAD ?= 0 PTHREAD_POOL_SIZE ?= 4 STACK_SIZE ?= 4194304 @@ -121,7 +121,8 @@ endif ifeq ($(PROXY_TO_PTHREAD), 1) LIBS += -s OFFSCREENCANVAS_SUPPORT DEFINES += -DPROXY_TO_PTHREAD -DEMSCRIPTEN_STACK_SIZE=$(STACK_SIZE) - override PTHREAD = 1 + override HAVE_THREADS = 1 + # use the default stack size for the browser thread; the RetroArch thread will be created with the specified stack size override STACK_SIZE = 4194304 else ifeq ($(HAVE_AL), 1) override ASYNC = 1 @@ -169,19 +170,16 @@ ifeq ($(HAVE_AL), 1) DEFINES += -DHAVE_AL endif -ifeq ($(PTHREAD), 1) +ifeq ($(HAVE_THREADS), 1) LDFLAGS += -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD_POOL_SIZE) CFLAGS += -pthread -s SHARED_MEMORY - HAVE_THREADS = 1 -else - HAVE_THREADS = 0 endif ifeq ($(ASYNC), 1) DEFINES += -DEMSCRIPTEN_ASYNCIFY LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=8192 ifeq ($(DEBUG), 1) - # LDFLAGS += -s ASYNCIFY_DEBUG=1 # -s ASYNCIFY_ADVISE + LDFLAGS += -s ASYNCIFY_DEBUG=1 # -s ASYNCIFY_ADVISE endif endif @@ -205,7 +203,8 @@ endif ifeq ($(DEBUG), 1) LDFLAGS += -O0 -g -gsource-map -s SAFE_HEAP=1 -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=1 - CFLAGS += -O0 -g -gsource-map + # -O0 in cflags gives "too many locals" errors + CFLAGS += -O1 -g -gsource-map else LDFLAGS += -O3 # WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake) diff --git a/dist-scripts/dist-cores.sh b/dist-scripts/dist-cores.sh index 5c91e70860..cc4a13143c 100755 --- a/dist-scripts/dist-cores.sh +++ b/dist-scripts/dist-cores.sh @@ -214,7 +214,7 @@ for f in `ls -v *_${platform}.${EXT}`; do fi if [ $PLATFORM = "emscripten" ]; then async=${ASYNC:-0} - pthread=${PTHREAD:-0} + have_threads=${HAVE_THREADS:-0} proxy_to_pthread=${PROXY_TO_PTHREAD:-0} gles3=${HAVE_OPENGLES3:-0} stack_mem=4194304 @@ -249,7 +249,7 @@ for f in `ls -v *_${platform}.${EXT}`; do echo LTO: $lto if [ $PLATFORM = "emscripten" ]; then echo ASYNC: $async - echo PTHREAD: $pthread + echo HAVE_THREADS: $have_threads echo PROXY_TO_PTHREAD: $proxy_to_pthread echo GLES3: $gles3 echo STACK_MEMORY: $stack_mem @@ -261,7 +261,7 @@ for f in `ls -v *_${platform}.${EXT}`; do if [ $MAKEFILE_GRIFFIN = "yes" ]; then make -C ../ -f Makefile.griffin platform=${platform} clean || exit 1 elif [ $PLATFORM = "emscripten" ]; then - make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 -j7 clean || exit 1 + make -C ../ -f Makefile.emscripten HAVE_THREADS=$have_threads ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 -j7 clean || exit 1 elif [ $PLATFORM = "unix" ]; then make -C ../ -f Makefile LINK=g++ LTO=$lto -j7 clean || exit 1 else @@ -273,8 +273,8 @@ for f in `ls -v *_${platform}.${EXT}`; do if [ $MAKEFILE_GRIFFIN = "yes" ]; then make -C ../ -f Makefile.griffin $OPTS platform=${platform} $whole_archive $big_stack -j3 || exit 1 elif [ $PLATFORM = "emscripten" ]; then - echo "BUILD COMMAND: make -C ../ -f Makefile.emscripten $OPTS LTO=$lto ASYNC=$async PTHREAD=$pthread PROXY_TO_PTHREAD=$proxy_to_pthread HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js" - make -C ../ -f Makefile.emscripten $OPTS LTO=$lto ASYNC=$async PTHREAD=$pthread PROXY_TO_PTHREAD=$proxy_to_pthread HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js || exit 1 + echo "BUILD COMMAND: make -C ../ -f Makefile.emscripten $OPTS LTO=$lto ASYNC=$async HAVE_THREADS=$have_threads PROXY_TO_PTHREAD=$proxy_to_pthread HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js" + make -C ../ -f Makefile.emscripten $OPTS LTO=$lto ASYNC=$async HAVE_THREADS=$have_threads PROXY_TO_PTHREAD=$proxy_to_pthread HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js || exit 1 elif [ $PLATFORM = "unix" ]; then make -C ../ -f Makefile LINK=g++ $whole_archive $big_stack -j3 || exit 1 elif [ $PLATFORM = "ctr" ]; then @@ -393,7 +393,7 @@ for f in `ls -v *_${platform}.${EXT}`; do if [ $MAKEFILE_GRIFFIN = "yes" ]; then make -C ../ -f Makefile.griffin platform=${platform} clean || exit 1 elif [ $PLATFORM = "emscripten" ]; then - make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 clean || exit 1 + make -C ../ -f Makefile.emscripten HAVE_THREADS=$have_threads ASYNC=$async LTO=$lto -j7 clean || exit 1 elif [ $PLATFORM = "unix" ]; then make -C ../ -f Makefile LTO=$lto -j7 clean || exit 1 else diff --git a/emscripten/library_platform_emscripten.js b/emscripten/library_platform_emscripten.js index 305cd69f98..fb86595c3c 100644 --- a/emscripten/library_platform_emscripten.js +++ b/emscripten/library_platform_emscripten.js @@ -3,7 +3,7 @@ var LibraryPlatformEmscripten = { $RPE: { powerStateChange: function(e) { - Module._update_power_state(true, Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF, e.target.level, e.target.charging); + _update_power_state(true, Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF, e.target.level, e.target.charging); }, updateMemoryUsage: function() { @@ -11,7 +11,7 @@ var LibraryPlatformEmscripten = { var used = BigInt(performance.memory.usedJSHeapSize || 0); var limit = BigInt(performance.memory.jsHeapSizeLimit || 0); // emscripten currently only supports passing 32 bit ints, so pack it - Module._update_memory_usage(Number(used & 0xFFFFFFFFn), Number(used >> 32n), Number(limit & 0xFFFFFFFFn), Number(limit >> 32n)); + _update_memory_usage(Number(used & 0xFFFFFFFFn), Number(used >> 32n), Number(limit & 0xFFFFFFFFn), Number(limit >> 32n)); setTimeout(RPE.updateMemoryUsage, 5000); }, command_queue: [], @@ -37,7 +37,7 @@ var LibraryPlatformEmscripten = { } // doubles are too big to pass as an argument to exported functions {{{ makeSetValue("dpr", "0", "window.devicePixelRatio", "double") }}}; - Module._update_canvas_dimensions(width, height, dpr); + _update_canvas_dimensions(width, height, dpr); }); RPE.observer.observe(Module.canvas); window.addEventListener("resize", function() { @@ -48,7 +48,7 @@ var LibraryPlatformEmscripten = { PlatformEmscriptenWatchWindowVisibility: function() { document.addEventListener("visibilitychange", function() { - Module._update_window_hidden(document.visibilityState == "hidden"); + _update_window_hidden(document.visibilityState == "hidden"); }, false); }, diff --git a/frontend/drivers/platform_emscripten.c b/frontend/drivers/platform_emscripten.c index ac27c0f9a6..6e1eafb539 100644 --- a/frontend/drivers/platform_emscripten.c +++ b/frontend/drivers/platform_emscripten.c @@ -63,8 +63,10 @@ #include #include #define PLATFORM_SETVAL(type, addr, val) emscripten_atomic_store_##type(addr, val) +#define PLATFORM_GETVAL(type, addr) emscripten_atomic_load_##type(addr) #else #define PLATFORM_SETVAL(type, addr, val) *addr = val +#define PLATFORM_GETVAL(type, addr) *addr #endif void emscripten_mainloop(void); @@ -73,39 +75,6 @@ void PlatformEmscriptenWatchWindowVisibility(void); void PlatformEmscriptenPowerStateInit(void); void PlatformEmscriptenMemoryUsageInit(void); -static bool command_flag = false; - -void PlatformEmscriptenCommandReply(const char *msg, size_t len) -{ - MAIN_THREAD_EM_ASM({ - var message = UTF8ToString($0, $1); - RPE.command_reply_queue.push(message); - }, msg, len); -} - -size_t PlatformEmscriptenCommandRead(char **into, size_t max_len) -{ - if (!command_flag) { return 0; } - return MAIN_THREAD_EM_ASM_INT({ - var next_command = RPE.command_queue.shift(); - var length = lengthBytesUTF8(next_command); - if (length > $2) { - console.error("[CMD] Command too long, skipping", next_command); - return 0; - } - stringToUTF8(next_command, $1, $2); - if (RPE.command_queue.length == 0) { - setValue($0, 0, 'i8'); - } - return length; - }, &command_flag, into, max_len); -} - -void PlatformEmscriptenCommandRaiseFlag() -{ - command_flag = true; -} - typedef struct { uint64_t memory_used; @@ -118,6 +87,7 @@ typedef struct volatile bool power_state_charging; volatile bool power_state_supported; volatile bool window_hidden; + volatile bool command_flag; } emscripten_platform_data_t; static emscripten_platform_data_t *emscripten_platform_data = NULL; @@ -288,27 +258,62 @@ void update_memory_usage(uint32_t used1, uint32_t used2, uint32_t limit1, uint32 PLATFORM_SETVAL(u64, &emscripten_platform_data->memory_limit, limit1 | ((uint64_t)limit2 << 32)); } +void PlatformEmscriptenCommandRaiseFlag() +{ + if (!emscripten_platform_data) + return; + emscripten_platform_data->command_flag = true; +} + /* platform specific c helpers */ +void PlatformEmscriptenCommandReply(const char *msg, size_t len) +{ + MAIN_THREAD_EM_ASM({ + var message = UTF8ToString($0, $1); + RPE.command_reply_queue.push(message); + }, msg, len); +} + +size_t PlatformEmscriptenCommandRead(char **into, size_t max_len) +{ + if (!emscripten_platform_data || !emscripten_platform_data->command_flag) + return 0; + return MAIN_THREAD_EM_ASM_INT({ + var next_command = RPE.command_queue.shift(); + var length = lengthBytesUTF8(next_command); + if (length > $2) { + console.error("[CMD] Command too long, skipping", next_command); + return 0; + } + stringToUTF8(next_command, $1, $2); + if (RPE.command_queue.length == 0) { + setValue($0, 0, 'i8'); + } + return length; + }, &emscripten_platform_data->command_flag, into, max_len); +} + void platform_emscripten_get_canvas_size(int *width, int *height) { - if (!emscripten_platform_data || - (emscripten_platform_data->canvas_width == 0 && emscripten_platform_data->canvas_height == 0)) - { - *width = 800; - *height = 600; - RARCH_ERR("[EMSCRIPTEN]: Could not get screen dimensions!\n"); - } - else - { - *width = emscripten_platform_data->canvas_width; - *height = emscripten_platform_data->canvas_height; - } + if (!emscripten_platform_data) + goto error; + + *width = PLATFORM_GETVAL(u32, &emscripten_platform_data->canvas_width); + *height = PLATFORM_GETVAL(u32, &emscripten_platform_data->canvas_height); + + if (*width != 0 || *height != 0) + return; + +error: + *width = 800; + *height = 600; + RARCH_ERR("[EMSCRIPTEN]: Could not get screen dimensions!\n"); } double platform_emscripten_get_dpr(void) { - return emscripten_platform_data->device_pixel_ratio; + return PLATFORM_GETVAL(f64, &emscripten_platform_data->device_pixel_ratio); } bool platform_emscripten_is_window_hidden(void) @@ -440,19 +445,22 @@ static void frontend_emscripten_get_env(int *argc, char *argv[], static enum frontend_powerstate frontend_emscripten_get_powerstate(int *seconds, int *percent) { enum frontend_powerstate ret = FRONTEND_POWERSTATE_NONE; + int level; if (!emscripten_platform_data || !emscripten_platform_data->power_state_supported) return ret; + level = PLATFORM_GETVAL(f32, &emscripten_platform_data->power_state_level); + if (!emscripten_platform_data->power_state_charging) ret = FRONTEND_POWERSTATE_ON_POWER_SOURCE; - else if (emscripten_platform_data->power_state_level == 1) + else if (level == 1) ret = FRONTEND_POWERSTATE_CHARGED; else ret = FRONTEND_POWERSTATE_CHARGING; - *seconds = emscripten_platform_data->power_state_discharge_time; - *percent = (int)(emscripten_platform_data->power_state_level * 100); + *seconds = PLATFORM_GETVAL(u32, &emscripten_platform_data->power_state_discharge_time); + *percent = (int)(level * 100); return ret; } @@ -461,7 +469,7 @@ static uint64_t frontend_emscripten_get_total_mem(void) { if (!emscripten_platform_data) return 0; - return emscripten_platform_data->memory_limit; + return PLATFORM_GETVAL(u64, &emscripten_platform_data->memory_limit); } static uint64_t frontend_emscripten_get_free_mem(void) @@ -469,11 +477,11 @@ static uint64_t frontend_emscripten_get_free_mem(void) if (!emscripten_platform_data) return 0; #ifndef PROXY_TO_PTHREAD - uint64_t used = emscripten_platform_data->memory_used; + uint64_t used = PLATFORM_GETVAL(u64, &emscripten_platform_data->memory_used); #else uint64_t used = mallinfo().uordblks; #endif - return (emscripten_platform_data->memory_limit - used); + return (PLATFORM_GETVAL(u64, &emscripten_platform_data->memory_limit) - used); } /* program entry and startup */ @@ -510,7 +518,7 @@ void platform_emscripten_mount_filesystems(void) abort(); } } -#if false + if (fetch_manifest) { /* fetch_manifest should be a path to a manifest file. @@ -575,7 +583,6 @@ void platform_emscripten_mount_filesystems(void) fclose(file); free(line); } -#endif } #endif /* HAVE_WASMFS */ @@ -608,6 +615,10 @@ static void *main_pthread(void* arg) int main(int argc, char *argv[]) { int ret = 0; +#ifdef PROXY_TO_PTHREAD + pthread_attr_t attr; + pthread_t thread; +#endif // this never gets freed emscripten_platform_data = (emscripten_platform_data_t *)calloc(1, sizeof(emscripten_platform_data_t)); @@ -619,8 +630,6 @@ int main(int argc, char *argv[]) #ifdef PROXY_TO_PTHREAD _main_argc = argc; _main_argv = argv; - pthread_attr_t attr; - pthread_t thread; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&attr, EMSCRIPTEN_STACK_SIZE); diff --git a/gfx/drivers_context/emscriptenwebgl_ctx.c b/gfx/drivers_context/emscriptenwebgl_ctx.c index 43cf6ba200..171abd804f 100644 --- a/gfx/drivers_context/emscriptenwebgl_ctx.c +++ b/gfx/drivers_context/emscriptenwebgl_ctx.c @@ -62,13 +62,10 @@ static void gfx_ctx_emscripten_webgl_check_window(void *data, bool *quit, *quit = false; } +/* https://github.com/emscripten-core/emscripten/issues/17816#issuecomment-1249719343 */ static void gfx_ctx_emscripten_webgl_swap_buffers(void *data) { -#ifdef PROXY_TO_PTHREAD - emscripten_webgl_commit_frame(); -#else (void)data; -#endif } static void gfx_ctx_emscripten_webgl_get_video_size(void *data, @@ -134,11 +131,7 @@ static void *gfx_ctx_emscripten_webgl_init(void *video_driver) #endif attrs.minorVersion = 0; attrs.enableExtensionsByDefault = true; -#ifdef PROXY_TO_PTHREAD - attrs.explicitSwapControl = true; -#else attrs.explicitSwapControl = false; -#endif attrs.renderViaOffscreenBackBuffer = false; attrs.proxyContextToMainThread = EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW; diff --git a/pkg/emscripten/README.md b/pkg/emscripten/README.md index b9a78a4c47..57c7b20e38 100644 --- a/pkg/emscripten/README.md +++ b/pkg/emscripten/README.md @@ -83,11 +83,11 @@ emmake make -f Makefile platform=emscripten cp melonds_libretro_emscripten.bc ~/retroarch/RetroArch/libretro_emscripten.bc ``` -Now build the frontend with the pthreads env variable: (2 is the number of workers; this can be any integer, but many browsers limit the number of workers) +Now build the frontend with the pthreads env variable: ``` cd ~/retroarch/RetroArch -emmake make -f Makefile.emscripten LIBRETRO=melonds PTHREAD=2 && cp melonds_libretro.* pkg/emscripten/libretro +emmake make -f Makefile.emscripten LIBRETRO=melonds HAVE_THREADS=1 && cp melonds_libretro.* pkg/emscripten/libretro ``` Your resulting output will be located in: @@ -153,7 +153,7 @@ git clone https://github.com/libretro/RetroArch.git ~/retroarch/RetroArch cp ~/retroarch/libretro-fceumm/fceumm_libretro_emscripten.bc ~/retroarch/RetroArch/libretro_emscripten.bc cd ~/retroarch -emmake make -f Makefile.emscripten LIBRETRO=fceumm PROXY_TO_PTHREAD=1 PTHREAD=4 HAVE_WASMFS=1 ASYNC=0 HAVE_EGL=0 -j all +emmake make -f Makefile.emscripten LIBRETRO=fceumm PROXY_TO_PTHREAD=1 HAVE_WASMFS=1 -j all cp fceumm_libretro.{js,wasm} pkg/emscripten/libretro-thread ```