3 Commits
Author SHA1 Message Date
valknarandClaude Sonnet 5 c7f7cb6afa Bump version to 0.3.1 for the focus-loss freeze fix
CI / Build wasm engine (push) Successful in 10m50s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 15:17:21 +02:00
valknarandClaude Sonnet 5 99ae5b9eda Fix PinMAME busy-wait freeze on browser/window focus loss
core.vbs auto-wires Controller.Pause = True into every table's Paused
event, which Player::OnFocusChanged() fires on focus loss. PinMAME's
own pause handling in usrintrf.c is a "while (g_fPause) { ...;
draw_screen(); ... }" busy-wait written for a real-OS-thread host,
where a separate thread later flips g_fPause back to 0. Under our
single-threaded Emscripten cooperative-scheduling model there is no
second thread to clear it, so entering that loop spins forever at
100% CPU with the tab completely unresponsive.

cpu_run_emscripten_step() now checks g_fPause up front and skips
cpu_timeslice() (and therefore that loop) entirely while paused;
emulation simply doesn't advance until Controller.Pause is set back
to False and the step function is called again next frame.

Confirmed fixed by hands-on testing: unfocusing the browser during a
PinMAME ROM session no longer freezes the tab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 15:15:48 +02:00
valknarandClaude Sonnet 5 5570955bc5 Fix patches/pinmame/0004: drop duplicate hunk conflicting with 0002
CI / Build wasm engine (push) Successful in 10m51s
0004 was generated via a plain `git diff` against pinmame's pristine
commit, which captured the *cumulative* diff for cpuexec.c - including
patch 0002's time_fence stub hunk, not just 0004's own new cpu_run
splitting code. Applying 0002 then 0004 in sequence (exactly what
setup.sh does) failed: 0004's duplicate hunk expected pre-0002 context
that no longer matched.

Regenerated by diffing the current tree against a baseline with 0001-0003
already applied (matching the vpinball/0006 patch's existing approach)
instead of against pristine directly. Verified: all 4 pinmame patches now
apply cleanly in sequence against a fresh pristine checkout, and the
result is byte-identical to the working tree that was actually built and
tested.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoSarxLgY33Kax5UNXcafZ
2026-08-23 12:47:51 +02:00
3 changed files with 18 additions and 40 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@valknar/vpinball-wasm",
"version": "0.2.0",
"version": "0.3.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@valknar/vpinball-wasm",
"version": "0.2.0",
"version": "0.3.1",
"license": "SEE LICENSE IN LICENSE",
"devDependencies": {
"typescript": "^5.6.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@valknar/vpinball-wasm",
"version": "0.3.0",
"version": "0.3.1",
"description": "Visual Pinball's engine compiled to WebAssembly - real .vpx tables, real VBScript, WebGL2 rendering, in the browser",
"license": "SEE LICENSE IN LICENSE",
"type": "module",
@@ -1,8 +1,8 @@
diff --git a/src/cpuexec.c b/src/cpuexec.c
index ca7401f..0bfcbe7 100644
index 07db6a1..19da31a 100644
--- a/src/cpuexec.c
+++ b/src/cpuexec.c
@@ -466,6 +466,65 @@ void cpu_run(void)
@@ -466,6 +466,78 @@ void cpu_run(void)
#endif
}
@@ -29,6 +29,19 @@ index ca7401f..0bfcbe7 100644
+ once it has fully quit (cpu_post_run() has already been invoked). */
+int cpu_run_emscripten_step(void)
+{
+ /* usrintrf.c's per-frame video update contains a "while (g_fPause) {
+ ...; draw_screen(); ... }" busy-wait (see the VPINMAME/LIBPINMAME
+ branch around updatescreen()) that assumes a real OS thread will
+ later flip g_fPause back to 0 from outside it. We're single-threaded
+ here, so entering that loop at all would spin forever burning 100%
+ CPU with no way out - nothing else can ever run to clear the flag.
+ Skip cpu_timeslice() (and therefore that loop) entirely while paused;
+ the emulation simply doesn't advance until Controller.Pause is set
+ back to False and this function is called again next frame. */
+ extern int g_fPause;
+ if (g_fPause)
+ return 1;
+
+ const double frame_target = timer_get_time() + (1.0 / 60.0);
+
+ while (!time_to_quit && !time_to_reset && timer_get_time() < frame_target)
@@ -68,41 +81,6 @@ index ca7401f..0bfcbe7 100644
/*************************************
@@ -943,6 +1002,34 @@ void time_fence_exit()
}
}
+#elif defined(__EMSCRIPTEN__)
+
+// No cross-thread wait primitive is used here: this build is single-threaded
+// (see vpinball-wasm's README on why pthreads/SharedArrayBuffer are out of
+// scope), and time_fence is purely an *optional* external-clock-sync feature
+// (mirrored by Controller.TimeFence in controller.vbs) - reporting it as
+// unsupported just means the emulator paces itself on its own internal
+// timing instead of syncing to the host's clock, which is what every other
+// platform this library runs on outside of this fence do anyway.
+int time_fence_is_supported()
+{
+ return 0;
+}
+
+void time_fence_post()
+{
+}
+
+int time_fence_wait(double secs)
+{
+ (void)secs;
+ return 0;
+}
+
+void time_fence_exit()
+{
+}
+
#else
#include <semaphore.h>
diff --git a/src/libpinmame/libpinmame.cpp b/src/libpinmame/libpinmame.cpp
index 6966d77..755d762 100644
--- a/src/libpinmame/libpinmame.cpp