Skip to content

Commit e0645d5

Browse files
committed
Emscripten core switching
1 parent f0370a1 commit e0645d5

13 files changed

Lines changed: 615 additions & 352 deletions

File tree

Makefile.emscripten

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ ifeq ($(HAVE_SDL2), 1)
161161
endif
162162

163163
LDFLAGS := -L. --no-heap-copy -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(INITIAL_HEAP) \
164-
-s EXPORTED_RUNTIME_METHODS=$(EXPORTS) \
164+
-s EXPORTED_RUNTIME_METHODS=$(EXPORTS) -s EXIT_RUNTIME=1 \
165165
-s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="$(EXPORTED_FUNCTIONS)" \
166166
-s MODULARIZE=1 -s EXPORT_ES6=1 -s EXPORT_NAME="libretro_$(subst -,_,$(LIBRETRO))" \
167167
-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \

audio/drivers/audioworklet.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,19 @@ bool audioworklet_external_block(void)
431431
}
432432
#endif
433433

434+
/* called on program exit */
435+
void audioworklet_close(void)
436+
{
437+
audioworklet_data_t *audioworklet = audioworklet_static_data;
438+
439+
if (!audioworklet)
440+
return;
441+
442+
MAIN_THREAD_EM_ASM({
443+
emscriptenGetAudioObject($0).close();
444+
}, audioworklet->context);
445+
}
446+
434447
static bool audioworklet_stop(void *data)
435448
{
436449
audioworklet_data_t *audioworklet = (audioworklet_data_t*)data;

emscripten/library_platform_emscripten.js

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ var LibraryPlatformEmscripten = {
44
$RPE: {
55
canvasWidth: 0,
66
canvasHeight: 0,
7+
battery: null,
8+
observer: null,
9+
memoryUsageTimeout: null,
710
sentinelPromise: null,
811
command_queue: [],
912
command_reply_queue: []
1013
},
1114

12-
PlatformEmscriptenWatchCanvasSizeAndDpr__deps: ["platform_emscripten_update_canvas_dimensions_cb"],
15+
$PlatformEmscriptenOnWindowResize: function() {
16+
RPE.observer.unobserve(Module.canvas);
17+
RPE.observer.observe(Module.canvas);
18+
},
19+
20+
PlatformEmscriptenWatchCanvasSizeAndDpr__deps: ["platform_emscripten_update_canvas_dimensions_cb", "$PlatformEmscriptenOnWindowResize"],
21+
PlatformEmscriptenWatchCanvasSizeAndDpr__proxy: "sync",
1322
PlatformEmscriptenWatchCanvasSizeAndDpr: function(dpr) {
1423
if (RPE.observer) {
1524
RPE.observer.unobserve(Module.canvas);
@@ -34,39 +43,57 @@ var LibraryPlatformEmscripten = {
3443
_platform_emscripten_update_canvas_dimensions_cb(width, height, dpr);
3544
});
3645
RPE.observer.observe(Module.canvas);
37-
window.addEventListener("resize", function() {
38-
RPE.observer.unobserve(Module.canvas);
39-
RPE.observer.observe(Module.canvas);
40-
}, false);
46+
window.addEventListener("resize", PlatformEmscriptenOnWindowResize, false);
47+
},
48+
49+
$PlatformEmscriptenOnCanvasPointerDown: function() {
50+
Module.canvas.focus();
4151
},
4252

43-
PlatformEmscriptenWatchWindowVisibility__deps: ["platform_emscripten_update_window_hidden_cb"],
53+
$PlatformEmscriptenOnCanvasContextMenu: function(e) {
54+
e.preventDefault();
55+
},
56+
57+
PlatformEmscriptenCanvasListenersInit__deps: ["$PlatformEmscriptenOnCanvasPointerDown", "$PlatformEmscriptenOnCanvasContextMenu"],
58+
PlatformEmscriptenCanvasListenersInit__proxy: "sync",
59+
PlatformEmscriptenCanvasListenersInit: function() {
60+
Module.canvas.addEventListener("pointerdown", PlatformEmscriptenOnCanvasPointerDown, false);
61+
Module.canvas.addEventListener("contextmenu", PlatformEmscriptenOnCanvasContextMenu, false);
62+
},
63+
64+
$PlatformEmscriptenOnVisibilityChange__deps: ["platform_emscripten_update_window_hidden_cb"],
65+
$PlatformEmscriptenOnVisibilityChange: function() {
66+
_platform_emscripten_update_window_hidden_cb(document.visibilityState == "hidden");
67+
},
68+
69+
PlatformEmscriptenWatchWindowVisibility__deps: ["$PlatformEmscriptenOnVisibilityChange"],
70+
PlatformEmscriptenWatchWindowVisibility__proxy: "sync",
4471
PlatformEmscriptenWatchWindowVisibility: function() {
45-
document.addEventListener("visibilitychange", function() {
46-
_platform_emscripten_update_window_hidden_cb(document.visibilityState == "hidden");
47-
}, false);
72+
document.addEventListener("visibilitychange", PlatformEmscriptenOnVisibilityChange, false);
4873
},
4974

50-
$PlatformEmscriptenPowerStateChange__deps: ["platform_emscripten_update_power_state_cb"],
51-
$PlatformEmscriptenPowerStateChange: function(e) {
75+
$PlatformEmscriptenOnPowerStateChange__deps: ["platform_emscripten_update_power_state_cb"],
76+
$PlatformEmscriptenOnPowerStateChange: function(e) {
5277
_platform_emscripten_update_power_state_cb(true, Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF, e.target.level, e.target.charging);
5378
},
5479

55-
PlatformEmscriptenPowerStateInit__deps: ["$PlatformEmscriptenPowerStateChange"],
80+
PlatformEmscriptenPowerStateInit__deps: ["$PlatformEmscriptenOnPowerStateChange"],
81+
PlatformEmscriptenPowerStateInit__proxy: "sync",
5682
PlatformEmscriptenPowerStateInit: function() {
5783
if (!navigator.getBattery) return;
5884
navigator.getBattery().then(function(battery) {
59-
battery.addEventListener("chargingchange", PlatformEmscriptenPowerStateChange);
60-
battery.addEventListener("levelchange", PlatformEmscriptenPowerStateChange);
61-
PlatformEmscriptenPowerStateChange({target: battery});
85+
RPE.battery = battery;
86+
battery.addEventListener("chargingchange", PlatformEmscriptenOnPowerStateChange);
87+
battery.addEventListener("levelchange", PlatformEmscriptenOnPowerStateChange);
88+
PlatformEmscriptenOnPowerStateChange({target: battery});
6289
});
6390
},
6491

6592
$PlatformEmscriptenUpdateMemoryUsage__deps: ["platform_emscripten_update_memory_usage_cb"],
6693
$PlatformEmscriptenUpdateMemoryUsage: function() {
6794
// unfortunately this will be inaccurate in threaded (worker) builds
6895
_platform_emscripten_update_memory_usage_cb(BigInt(performance.memory.usedJSHeapSize || 0), BigInt(performance.memory.jsHeapSizeLimit || 0));
69-
setTimeout(PlatformEmscriptenUpdateMemoryUsage, 5000);
96+
RPE.memoryUsageTimeout = setTimeout(PlatformEmscriptenUpdateMemoryUsage, 5000);
7097
},
7198

7299
PlatformEmscriptenMemoryUsageInit__deps: ["$PlatformEmscriptenUpdateMemoryUsage"],
@@ -75,22 +102,33 @@ var LibraryPlatformEmscripten = {
75102
PlatformEmscriptenUpdateMemoryUsage();
76103
},
77104

78-
PlatformEmscriptenWatchFullscreen__deps: ["platform_emscripten_update_fullscreen_state_cb"],
105+
$PlatformEmscriptenOnFullscreenChange__deps: ["platform_emscripten_update_fullscreen_state_cb"],
106+
$PlatformEmscriptenOnFullscreenChange: function() {
107+
_platform_emscripten_update_fullscreen_state_cb(!!document.fullscreenElement);
108+
},
109+
110+
PlatformEmscriptenWatchFullscreen__deps: ["$PlatformEmscriptenOnFullscreenChange"],
111+
PlatformEmscriptenWatchFullscreen__proxy: "sync",
79112
PlatformEmscriptenWatchFullscreen: function() {
80-
document.addEventListener("fullscreenchange", function() {
81-
_platform_emscripten_update_fullscreen_state_cb(!!document.fullscreenElement);
82-
}, false);
113+
document.addEventListener("fullscreenchange", PlatformEmscriptenOnFullscreenChange, false);
83114
},
84115

85-
PlatformEmscriptenGLContextEventInit__deps: ["platform_emscripten_gl_context_lost_cb", "platform_emscripten_gl_context_restored_cb"],
116+
$PlatformEmscriptenOnGLContextLost__deps: ["platform_emscripten_gl_context_lost_cb"],
117+
$PlatformEmscriptenOnGLContextLost: function(e) {
118+
e.preventDefault();
119+
_platform_emscripten_gl_context_lost_cb();
120+
},
121+
122+
$PlatformEmscriptenOnGLContextRestored__deps: ["platform_emscripten_gl_context_restored_cb"],
123+
$PlatformEmscriptenOnGLContextRestored: function() {
124+
_platform_emscripten_gl_context_restored_cb();
125+
},
126+
127+
PlatformEmscriptenGLContextEventInit__deps: ["$PlatformEmscriptenOnGLContextLost", "$PlatformEmscriptenOnGLContextRestored"],
128+
PlatformEmscriptenGLContextEventInit__proxy: "sync",
86129
PlatformEmscriptenGLContextEventInit: function() {
87-
Module.canvas.addEventListener("webglcontextlost", function(e) {
88-
e.preventDefault();
89-
_platform_emscripten_gl_context_lost_cb();
90-
});
91-
Module.canvas.addEventListener("webglcontextrestored", function() {
92-
_platform_emscripten_gl_context_restored_cb();
93-
});
130+
Module.canvas.addEventListener("webglcontextlost", PlatformEmscriptenOnGLContextLost);
131+
Module.canvas.addEventListener("webglcontextrestored", PlatformEmscriptenOnGLContextRestored);
94132
},
95133

96134
$PlatformEmscriptenDoSetCanvasSize: async function(width, height) {
@@ -156,6 +194,34 @@ var LibraryPlatformEmscripten = {
156194

157195
$EmscriptenReceiveCommandReply: function() {
158196
return RPE.command_reply_queue.shift();
197+
},
198+
199+
$PlatformEmscriptenFreeBrowser__proxy: "sync",
200+
$PlatformEmscriptenFreeBrowser__deps: ["$PlatformEmscriptenDoSetWakeLock", "$PlatformEmscriptenOnCanvasPointerDown", "$PlatformEmscriptenOnCanvasContextMenu", "$PlatformEmscriptenOnWindowResize", "$PlatformEmscriptenOnVisibilityChange", "$PlatformEmscriptenOnFullscreenChange", "$PlatformEmscriptenOnPowerStateChange"],
201+
$PlatformEmscriptenFreeBrowser: function() {
202+
if (RPE.memoryUsageTimeout) clearTimeout(RPE.memoryUsageTimeout);
203+
PlatformEmscriptenDoSetWakeLock(false);
204+
if (RPE.observer) {
205+
RPE.observer.unobserve(Module.canvas);
206+
RPE.observer = null;
207+
}
208+
Module.canvas.removeEventListener("pointerdown", PlatformEmscriptenOnCanvasPointerDown);
209+
Module.canvas.removeEventListener("contextmenu", PlatformEmscriptenOnCanvasContextMenu);
210+
window.removeEventListener("resize", PlatformEmscriptenOnWindowResize);
211+
document.removeEventListener("visibilitychange", PlatformEmscriptenOnVisibilityChange);
212+
document.removeEventListener("fullscreenchange", PlatformEmscriptenOnFullscreenChange);
213+
if (RPE.battery) {
214+
RPE.battery.removeEventListener("chargingchange", PlatformEmscriptenOnPowerStateChange);
215+
RPE.battery.removeEventListener("levelchange", PlatformEmscriptenOnPowerStateChange);
216+
RPE.battery = null;
217+
}
218+
},
219+
220+
PlatformEmscriptenFree__deps: ["$PlatformEmscriptenFreeBrowser"],
221+
PlatformEmscriptenFree: function() {
222+
Module.canvas.removeEventListener("webglcontextlost", PlatformEmscriptenOnGLContextLost);
223+
Module.canvas.removeEventListener("webglcontextrestored", PlatformEmscriptenOnGLContextRestored);
224+
PlatformEmscriptenFreeBrowser();
159225
}
160226
};
161227

frontend/drivers/platform_emscripten.c

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ void emscripten_mainloop(void);
7676

7777
/* javascript library functions */
7878
void PlatformEmscriptenWatchCanvasSizeAndDpr(double *dpr);
79+
void PlatformEmscriptenCanvasListenersInit(void);
7980
void PlatformEmscriptenWatchWindowVisibility(void);
8081
void PlatformEmscriptenPowerStateInit(void);
8182
void PlatformEmscriptenMemoryUsageInit(void);
@@ -84,6 +85,7 @@ void PlatformEmscriptenGLContextEventInit(void);
8485
void PlatformEmscriptenSetCanvasSize(int width, int height);
8586
void PlatformEmscriptenSetWakeLock(bool state);
8687
uint32_t PlatformEmscriptenGetSystemInfo(void);
88+
void PlatformEmscriptenFree(void);
8789

8890
typedef struct
8991
{
@@ -95,8 +97,10 @@ typedef struct
9597
uint64_t memory_used;
9698
uint64_t memory_limit;
9799
double device_pixel_ratio;
100+
double device_pixel_ratio_temp;
98101
enum platform_emscripten_browser browser;
99102
enum platform_emscripten_os os;
103+
enum frontend_fork fork_mode;
100104
int raf_interval;
101105
int canvas_width;
102106
int canvas_height;
@@ -383,7 +387,7 @@ size_t platform_emscripten_command_read(char **into, size_t max_len)
383387
var next_command = RPE.command_queue.shift();
384388
var length = lengthBytesUTF8(next_command);
385389
if (length > $2) {
386-
console.error("[CMD] Command too long, skipping", next_command);
390+
err("[CMD] Command too long, skipping", next_command);
387391
return 0;
388392
}
389393
stringToUTF8(next_command, $1, $2);
@@ -552,6 +556,8 @@ static void frontend_emscripten_get_env(int *argc, char *argv[],
552556
char bundle_path[PATH_MAX];
553557
const char *home = getenv("HOME");
554558

559+
path_set(RARCH_PATH_CORE, getenv("LIBRARY_PATH"));
560+
555561
if (home)
556562
{
557563
size_t _len = strlcpy(base_path, home, sizeof(base_path));
@@ -686,6 +692,51 @@ static uint64_t frontend_emscripten_get_free_mem(void)
686692
return (PLATFORM_GETVAL(u64, &emscripten_platform_data->memory_limit) - used);
687693
}
688694

695+
#ifdef HAVE_AUDIOWORKLET
696+
void audioworklet_close(void);
697+
#endif
698+
699+
static void frontend_emscripten_exec_browser(void *path)
700+
{
701+
const char *core = emscripten_platform_data->fork_mode == FRONTEND_FORK_NONE ? 0 : path;
702+
const char *content = emscripten_platform_data->fork_mode == FRONTEND_FORK_CORE_WITH_ARGS ? path_get(RARCH_PATH_CONTENT) : 0;
703+
704+
#ifdef HAVE_AUDIOWORKLET
705+
audioworklet_close();
706+
#endif
707+
708+
EM_ASM({
709+
#ifdef PROXY_TO_PTHREAD
710+
/* undo OffscreenCanvas */
711+
let newCanvas = Module.canvas.cloneNode();
712+
Module.canvas.replaceWith(newCanvas);
713+
Module.canvas = newCanvas;
714+
#endif
715+
if (typeof Module.retroArchExit == "function")
716+
setTimeout(Module.retroArchExit, 0, $0 && UTF8ToString($0), $1 && UTF8ToString($1));
717+
else
718+
out("[INFO] Exiting, but Module.retroArchExit was not provided");
719+
}, core, content);
720+
emscripten_force_exit(0);
721+
}
722+
723+
static void frontend_emscripten_exec(const char *path, bool should_load_content)
724+
{
725+
PlatformEmscriptenFree();
726+
platform_emscripten_run_on_browser_thread_sync(frontend_emscripten_exec_browser, (void *)path);
727+
}
728+
729+
static void frontend_emscripten_exitspawn(char *s, size_t len, char *args)
730+
{
731+
frontend_emscripten_exec(s, false);
732+
}
733+
734+
static bool frontend_emscripten_set_fork(enum frontend_fork fork_mode)
735+
{
736+
emscripten_platform_data->fork_mode = fork_mode;
737+
return true;
738+
}
739+
689740
/* program entry and startup */
690741

691742
#ifdef HAVE_EXTRA_WASMFS
@@ -890,14 +941,6 @@ int main(int argc, char *argv[])
890941
if (!Module.canvas.getAttribute("tabindex"))
891942
Module.canvas.setAttribute("tabindex", "-1");
892943
Module.canvas.focus();
893-
Module.canvas.addEventListener("pointerdown", function() {
894-
Module.canvas.focus();
895-
}, false);
896-
897-
/* disable browser right click menu */
898-
Module.canvas.addEventListener("contextmenu", function(e) {
899-
e.preventDefault();
900-
}, false);
901944

902945
/* background should be black */
903946
Module.canvas.style.backgroundColor = "#000000";
@@ -908,21 +951,22 @@ int main(int argc, char *argv[])
908951

909952
/* ensure canvas size is constrained by CSS, otherwise infinite resizing may occur */
910953
if (window.getComputedStyle(Module.canvas).display == "inline") {
911-
console.warn("[WARN] Canvas should not use display: inline!");
954+
err("[WARN] Canvas should not use display: inline!");
912955
Module.canvas.style.display = "inline-block";
913956
}
914957
var oldWidth = Module.canvas.clientWidth;
915958
var oldHeight = Module.canvas.clientHeight;
916959
Module.canvas.width = 64;
917960
Module.canvas.height = 64;
918961
if (oldWidth != Module.canvas.clientWidth || oldHeight != Module.canvas.clientHeight) {
919-
console.warn("[WARN] Canvas size should be set using CSS properties!");
962+
err("[WARN] Canvas size should be set using CSS properties!");
920963
Module.canvas.style.width = oldWidth + "px";
921964
Module.canvas.style.height = oldHeight + "px";
922965
}
923966
});
924967

925-
PlatformEmscriptenWatchCanvasSizeAndDpr(malloc(sizeof(double)));
968+
PlatformEmscriptenWatchCanvasSizeAndDpr(&emscripten_platform_data->device_pixel_ratio_temp);
969+
PlatformEmscriptenCanvasListenersInit();
926970
PlatformEmscriptenWatchWindowVisibility();
927971
PlatformEmscriptenPowerStateInit();
928972
PlatformEmscriptenMemoryUsageInit();
@@ -956,10 +1000,10 @@ frontend_ctx_driver_t frontend_ctx_emscripten = {
9561000
frontend_emscripten_get_env, /* environment_get */
9571001
NULL, /* init */
9581002
NULL, /* deinit */
959-
NULL, /* exitspawn */
1003+
frontend_emscripten_exitspawn, /* exitspawn */
9601004
NULL, /* process_args */
961-
NULL, /* exec */
962-
NULL, /* set_fork */
1005+
frontend_emscripten_exec, /* exec */
1006+
frontend_emscripten_set_fork, /* set_fork */
9631007
NULL, /* shutdown */
9641008
NULL, /* get_name */
9651009
NULL, /* get_os */

frontend/frontend_driver.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ size_t frontend_driver_get_core_extension(char *s, size_t len)
212212
if (envIsHomebrew())
213213
return strlcpy(s, "3dsx", len);
214214
return strlcpy(s, "cia", len);
215+
#elif defined(EMSCRIPTEN)
216+
/* may not contain the core */
217+
return strlcpy(s, "core", len);
215218
#else
216219
return 0;
217220
#endif

0 commit comments

Comments
 (0)