Skip to content

Emscripten core switching#18174

Merged
LibretroAdmin merged 4 commits intolibretro:masterfrom
BinBashBanana:master
Sep 13, 2025
Merged

Emscripten core switching#18174
LibretroAdmin merged 4 commits intolibretro:masterfrom
BinBashBanana:master

Conversation

@BinBashBanana
Copy link
Copy Markdown
Contributor

This enables core switching in the web player. Currently it creates dummy .core files in the cores directory so that RetroArch can see them, and the core name is parsed and loaded on exitspawn through Module.retroArchExit.

In the future, if cores are downloadable from the buildbot server instead of always being fetched from the server hosting the web player, the .core file could contain the core javascript and wasm together. (Since these aren't actual executables, it's up to the embedding javascript to decide what these files contain and how they should be loaded.)

The core list is now in core_list.js, rather than in index.html. This enables the core selector list to be dynamically created, and I have it ordered alphabetically by display name to match the in-app behavior.

Comment thread frontend/drivers/platform_emscripten.c
Comment thread pkg/emscripten/libretro/libretro.js
@JoeOsborn
Copy link
Copy Markdown
Contributor

I only have superficial remarks about the code, overall it looks good but there's a lot of code motion presumably due to the need to reinitialize stuff on loading the core and booting the program again? Or an unrelated refactoring?

I guess the idea here is that when RA quits, it execs back into itself but sometimes the wasm has changed? It feels tricky since we're loading two unrelated programs at the same path, with different emscripten modules---the (old?) emscripten runtime must still exist to the extent that it can exec into the new program, but it feels weird.

In particular, doesn't using exec replace the running program and thus skip the atexit/end-of-main code that compiling with -s EXIT_RUNTIME=1 adds?

I think a comment somewhere or document explaining the control flow here would be really helpful.

@BinBashBanana
Copy link
Copy Markdown
Contributor Author

I'm not entirely sure if EXIT_RUNTIME=1 is necessary, but emscripten_force_exit warns on an assertion without it. The refactoring in platform_emscripten (and its JS library) was necessary to remove all event listeners from the canvas/document/window so that RetroArch can safely run again without reloading the page.

Sorry for the convoluted exec stuff-- essentially, right before the program exits, it uses setTimeout to call Module.retroArchExit deferred, so that emscripten has time to do its cleanup, then the retroArchExit function is called. I have the web players set up so that on retroArchExit, it loads a new Module that replaces the old one, which is then garbage collected (a huge benefit of MODULARIZE). So it's not true exec, afaik nothing like that exists in emscripten, but close enough.

Also note that since the new web player downloads the script into a blob, it can reuse the old blob if the core is just restarting.

I plan to rewrite the documentation for emscripten too, including extra functions that the embedder can define on Module (fullscreenEnter, fullscreenExit, retroArchExit), but that will come in a later PR.

Copy link
Copy Markdown
Contributor

@JoeOsborn JoeOsborn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. I think this change is really positive and opens up a much nicer UX for web.libretro!

@BinBashBanana
Copy link
Copy Markdown
Contributor Author

@LibretroAdmin ready to merge!

@JoeOsborn
Copy link
Copy Markdown
Contributor

It occurs to me you could make the HTML dropdown work in the same way as switching cores within RA using a patch like this one:

diff --git a/pkg/emscripten/libretro/libretro.js b/pkg/emscripten/libretro/libretro.js
index b8a541410f..e8b5601f53 100644
--- a/pkg/emscripten/libretro/libretro.js
+++ b/pkg/emscripten/libretro/libretro.js
@@ -252,7 +252,7 @@ $(function() {
    var coreSelector = document.getElementById("core-selector");
    for (let name of coreNames) {
       let a = document.createElement("a");
-      a.href = ".";
+      a.href = "#";
       a.dataset.core = coreArray.find(i => i[1] == name)[0];
       a.textContent = name;
       a.classList.add("dropdown-item");
@@ -314,6 +314,7 @@ $(function() {
    $('#core-selector a').click(function() {
       var coreChoice = $(this).data('core');
       localStorage.setItem("core", coreChoice);
+      loadCore(coreChoice);
    });
 
    // Find which core to load.

I've tested it a bit and this seems to be enough. Could you add this to libretro and libretro-thread, modulo any changes you'd want to see?

@BinBashBanana
Copy link
Copy Markdown
Contributor Author

I added a LOAD_CORE command so that we can ask RetroArch to unload/load a new core from JS.

@BinBashBanana
Copy link
Copy Markdown
Contributor Author

Not sure why the windows CI broke... it shouldn't be due to this change?

@JoeOsborn
Copy link
Copy Markdown
Contributor

This is a nice change, I imagined you would update the core local storage and force RA to restart with a command--having it use the existing load core task is pretty cool.

This is a really neat PR that seems to bring emscripten RA closer in behavior to other static platforms that restart RA automatically on a core change. Very nice!

@LibretroAdmin
Copy link
Copy Markdown
Contributor

Alright, I'll merge this. If there are issues with it we'll have to revert it and you'll have to revisit it

@LibretroAdmin LibretroAdmin merged commit a650525 into libretro:master Sep 13, 2025
28 of 34 checks passed
@BinBashBanana
Copy link
Copy Markdown
Contributor Author

Looks like the CI works now. Weird!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants