Skip to content

Commit c73154e

Browse files
committed
Add support for preMainLoop/postMainLoop functions
Rather than having code from e.g SDL, etc, in event loop code use a callback registration system.
1 parent 77e24ae commit c73154e

10 files changed

+76
-47
lines changed

src/library_eventloop.js

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,21 @@ LibraryJSEventLoop = {
149149
clearInterval(id);
150150
},
151151

152+
$registerPostMainLoop: (f) => {
153+
typeof MainLoop != 'undefined' && MainLoop.postMainLoop.push(f);
154+
},
155+
156+
$registerPreMainLoop: (f) => {
157+
typeof MainLoop != 'undefined' && MainLoop.preMainLoop.push(f);
158+
},
159+
152160
$MainLoop__internal: true,
153161
$MainLoop__deps: ['$setMainLoop', '$callUserCallback', 'emscripten_set_main_loop_timing'],
154162
$MainLoop__postset: `
155163
Module["requestAnimationFrame"] = MainLoop.requestAnimationFrame;
156164
Module["pauseMainLoop"] = MainLoop.pause;
157-
Module["resumeMainLoop"] = MainLoop.resume;`,
165+
Module["resumeMainLoop"] = MainLoop.resume;
166+
MainLoop.init();`,
158167
$MainLoop: {
159168
running: false,
160169
scheduler: null,
@@ -173,6 +182,8 @@ LibraryJSEventLoop = {
173182
timingValue: 0,
174183
currentFrameNumber: 0,
175184
queue: [],
185+
preMainLoop: [],
186+
postMainLoop: [],
176187

177188
pause() {
178189
MainLoop.scheduler = null;
@@ -211,19 +222,28 @@ LibraryJSEventLoop = {
211222
#endif
212223
},
213224

225+
init() {
226+
#if expectToReceiveOnModule('preMainLoop')
227+
Module['preMainLoop'] && MainLoop.preMainLoop.push(Module['preMainLoop']);
228+
#endif
229+
#if expectToReceiveOnModule('postMainLoop')
230+
Module['postMainLoop'] && MainLoop.postMainLoop.push(Module['postMainLoop']);
231+
#endif
232+
},
233+
214234
runIter(func) {
215235
if (ABORT) return;
216-
#if expectToReceiveOnModule('preMainLoop')
217-
if (Module['preMainLoop']) {
218-
var preRet = Module['preMainLoop']();
219-
if (preRet === false) {
236+
for (var pre of MainLoop.preMainLoop) {
237+
if (pre() === false) {
220238
return; // |return false| skips a frame
221239
}
222240
}
223-
#endif
224241
callUserCallback(func);
225-
#if expectToReceiveOnModule('postMainLoop')
226-
Module['postMainLoop']?.();
242+
for (var post of MainLoop.postMainLoop) {
243+
post();
244+
}
245+
#if STACK_OVERFLOW_CHECK
246+
checkStackCookie();
227247
#endif
228248
},
229249

@@ -424,28 +444,6 @@ LibraryJSEventLoop = {
424444
MainLoop.tickStartTime = _emscripten_get_now();
425445
}
426446

427-
// Signal GL rendering layer that processing of a new frame is about to start. This helps it optimize
428-
// VBO double-buffering and reduce GPU stalls.
429-
#if FULL_ES2 || LEGACY_GL_EMULATION
430-
GL.newRenderingFrameStarted();
431-
#endif
432-
433-
#if PTHREADS && OFFSCREEN_FRAMEBUFFER && GL_SUPPORT_EXPLICIT_SWAP_CONTROL
434-
// If the current GL context is a proxied regular WebGL context, and was initialized with implicit swap mode on the main thread, and we are on the parent thread,
435-
// perform the swap on behalf of the user.
436-
if (typeof GL != 'undefined' && GL.currentContext && GL.currentContextIsProxied) {
437-
var explicitSwapControl = {{{ makeGetValue('GL.currentContext', 0, 'i32') }}};
438-
if (!explicitSwapControl) _emscripten_webgl_commit_frame();
439-
}
440-
#endif
441-
442-
#if OFFSCREENCANVAS_SUPPORT
443-
// If the current GL context is an OffscreenCanvas, but it was initialized with implicit swap mode, perform the swap on behalf of the user.
444-
if (typeof GL != 'undefined' && GL.currentContext && !GL.currentContextIsProxied && !GL.currentContext.attributes.explicitSwapControl && GL.currentContext.GLctx.commit) {
445-
GL.currentContext.GLctx.commit();
446-
}
447-
#endif
448-
449447
#if ASSERTIONS
450448
if (MainLoop.method === 'timeout' && Module.ctx) {
451449
warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!');
@@ -455,19 +453,9 @@ LibraryJSEventLoop = {
455453

456454
MainLoop.runIter(iterFunc);
457455

458-
#if STACK_OVERFLOW_CHECK
459-
checkStackCookie();
460-
#endif
461-
462456
// catch pauses from the main loop itself
463457
if (!checkIsRunning()) return;
464458

465-
// Queue new audio data. This is important to be right after the main loop invocation, so that we will immediately be able
466-
// to queue the newest produced audio samples.
467-
// TODO: Consider adding pre- and post- rAF callbacks so that GL.newRenderingFrameStarted() and SDL.audio.queueNewAudioData()
468-
// do not need to be hardcoded into this function, but can be more generic.
469-
if (typeof SDL == 'object') SDL.audio?.queueNewAudioData?.();
470-
471459
MainLoop.scheduler();
472460
}
473461

src/library_html5_webgl.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ var LibraryHtml5WebGL = {
4545
emscripten_webgl_commit_frame: 'emscripten_webgl_do_commit_frame',
4646
#endif
4747

48+
#if OFFSCREENCANVAS_SUPPORT
49+
emscripten_webgl_do_create_context__deps: ['$registerPreMainLoop'],
50+
emscripten_webgl_do_create_context__postjs: `
51+
registerPreMainLoop(() => {
52+
// If the current GL context is an OffscreenCanvas, but it was initialized
53+
// with implicit swap mode, perform the swap on behalf of the user.
54+
if (GL.currentContext && !GL.currentContextIsProxied && !GL.currentContext.attributes.explicitSwapControl && GL.currentContext.GLctx.commit) {
55+
GL.currentContext.GLctx.commit();
56+
}
57+
});`,
58+
#endif
59+
4860
emscripten_webgl_do_create_context__deps: [
4961
#if OFFSCREENCANVAS_SUPPORT
5062
'malloc',
@@ -184,6 +196,7 @@ var LibraryHtml5WebGL = {
184196
var contextHandle = GL.createContext(canvas, contextAttributes);
185197
return contextHandle;
186198
},
199+
187200
#if PTHREADS && OFFSCREEN_FRAMEBUFFER
188201
// Runs on the calling thread, proxies if needed.
189202
emscripten_webgl_make_context_current_calling_thread__sig: 'ip',
@@ -196,6 +209,19 @@ var LibraryHtml5WebGL = {
196209
// In this scenario, the pthread does not hold a high-level JS object to the GL context, because it lives on the main thread, in which case we record
197210
// an integer pointer as a token value to represent the GL context activation from another thread. (when this function is called, the main browser thread
198211
// has already accepted the GL context activation for our pthread, so that side is good)
212+
#if GL_SUPPORT_EXPLICIT_SWAP_CONTROL
213+
_emscripten_proxied_gl_context_activated_from_main_browser_thread__deps: ['$registerPreMainLoop'],
214+
_emscripten_proxied_gl_context_activated_from_main_browser_thread__postjs: `
215+
// If the current GL context is a proxied regular WebGL context, and was
216+
// initialized with implicit swap mode on the main thread, and we are on the
217+
// parent thread, perform the swap on behalf of the user.
218+
registerPreMainLoop(() => {
219+
if (GL.currentContext && GL.currentContextIsProxied) {
220+
var explicitSwapControl = {{{ makeGetValue('GL.currentContext', 0, 'i32') }}};
221+
if (!explicitSwapControl) _emscripten_webgl_commit_frame();
222+
}
223+
});`,
224+
#endif
199225
_emscripten_proxied_gl_context_activated_from_main_browser_thread: (contextHandle) => {
200226
GLctx = Module.ctx = GL.currentContext = contextHandle;
201227
GL.currentContextIsProxied = true;

src/library_sdl.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2311,8 +2311,13 @@ var LibrarySDL = {
23112311

23122312
// SDL_Audio
23132313

2314-
SDL_OpenAudio__deps: ['$autoResumeAudioContext', '$safeSetTimeout'],
2314+
SDL_OpenAudio__deps: ['$autoResumeAudioContext', '$safeSetTimeout', '$registerPostMainLoop'],
23152315
SDL_OpenAudio__proxy: 'sync',
2316+
SDL_OpenAudio__postjs: `
2317+
// Queue new audio data. This is important to be right after the main loop
2318+
// invocation, so that we will immediately be able to queue the newest
2319+
// produced audio samples.
2320+
registerPostMainLoop(() => SDL.audio?.queueNewAudioData?.());`,
23162321
SDL_OpenAudio: (desired, obtained) => {
23172322
try {
23182323
SDL.audio = {

src/library_webgl.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,17 @@ for (/**@suppress{duplicate}*/var i = 0; i <= {{{ GL_POOL_TEMP_BUFFERS_SIZE }}};
252252
'$webgl_enable_WEBGL_multi_draw',
253253
'$getEmscriptenSupportedExtensions',
254254
#endif // GL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS
255+
#if FULL_ES2 || LEGACY_GL_EMULATION
256+
'$registerPreMainLoop',
257+
#endif
255258
],
259+
#if FULL_ES2 || LEGACY_GL_EMULATION
260+
$GL__postjs: `
261+
// Signal GL rendering layer that processing of a new frame is about to
262+
// start. This helps it optimize VBO double-buffering and reduce GPU stalls.
263+
registerPreMainLoop(() => GL.newRenderingFrameStarted());
264+
`,
265+
#endif
256266
$GL: {
257267
#if GL_DEBUG
258268
debug: true,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7991
1+
8005
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
21372
1+
21413
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6542
1+
6552
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
17522
1+
17563
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
54741
1+
54792
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
53606
1+
53657

0 commit comments

Comments
 (0)