In 5f969dd#diff-fe83e208ade4a4d8de4dc610010e1271R91, there is an example to illustrate how to set up a synchronously blocking main loop:
"Another possibility is to use the async/await syntax:"
async function animationLoop() {
var promise;
do {
//draw stuff
(...)
promise = ctx.commit()
// do post commit work
(...)
} while (await promise);
}
However this does not actually do what readers expect - and especially - it does not solve what @OleksandrChekhovskyi was asking for in https://discourse.wicg.io/t/offscreencanvas-animations-in-workers/1989/15. That is, the comment
"Okay, I think we've got it. Basically, the syntax for getting a commit that throttles by blocking (as Oleksadr suggests) would simply be "await context.commit();". And of course, this is much better than hard-blocking the entire thread."
unfortunately does not solve the use case for a synchronous main loop.
The subtle issue is that an async function is a function that returns a Promise, and calling await immediately returns from the calling function (chain of async functions) and continues execution from the first non-async function. This effect breaks the computation model that is required for WebAssembly/Emscripten-based applications that set up their own main loops.
Here is a more concrete example based on OffscreenCanvas prototypes, which attempts to set up a blocking main loop, but fails due to the subtlety:
webgl_modal_loop.html:
<html><body><canvas id='canvas'>
<script>
var htmlCanvas = document.getElementById("canvas");
var offscreen = htmlCanvas.transferControlToOffscreen();
var worker = new Worker("webgl_worker.js");
worker.postMessage({canvas: offscreen}, [offscreen]);
</script>
</body></html>
webgl_worker.js:
/*
// This is a simulation of a blocking GL.commit(): (can try as alternative to real GL.commit() if OffscreenCanvas not yet implemented)
function commit() {
var displayRefreshRate = 1; // Simulate a 1Hz display for easy observing
return new Promise(function (resolve, reject) {
setTimeout(function() {
resolve();
}, 1000/displayRefreshRate);
});
}
*/
var gl = null;
async function renderLoop() {
var frameNumber = 0;
// Start our modal rendering loop
for(;;) {
gl.clearColor(performance.now(), performance.now(), performance.now(), performance.now());
gl.clear(gl.COLOR_BUFFER_BIT);
await gl.commit();
// await commit(); // Alternatively to try out simulated gl.commit() in the absence of real thing
console.log('rendered frame ' + frameNumber++);
if (frameNumber > 10) break; // Stop test after 10 frames to not flood the browser
}
}
function init(evt) {
console.log('init');
// Startup initialization for the application
var canvas = evt.data.canvas;
gl = canvas.getContext("webgl");
}
function runGame() {
console.log('runGame');
renderLoop();
}
function deinit() {
console.log('deinit');
gl = null; // tear down
}
onmessage = function(evt) {
// Game "main": init, run loop, and deinit
init(evt);
runGame();
deinit();
};
The expectation from a synchronously blocking execution is that the above application should print out
init
runGame
rendered frame 0
rendered frame 1
rendered frame 2
rendered frame 3
rendered frame 4
rendered frame 5
rendered frame 6
rendered frame 7
rendered frame 8
rendered frame 9
rendered frame 10
deinit
but instead, running the page will print out
init
runGame
deinit
rendered frame 0
<throw since gl is null>
This is because the onmessage function will continue executing immediately after the the await gl.commit(); is called, and deinit() will be called, breaking down the synchronous programming model.
I am currently implementing OffscreenCanvas support to Emscripten, and trying to figure out how to implement vsync synchronization when a Worker is rendering via OffscreenCanvas. In the absence of a GL.commit(blockUntilVsyncFinished=true) type of API or similar help from the OffscreenCanvas spec, my current thinking is to set up a requestAnimationFrame loop in the main browser thread, and use that to ping "vsync finished" events to a Worker, via SharedArrayBuffer. This will work if OffscreenCanvas based rendering is guaranteed to still allow observing "proper" requestAnimationFrame synchronization on the main browser thread, though I am not sure if OffscreenCanvas currently says anything about this?
In 5f969dd#diff-fe83e208ade4a4d8de4dc610010e1271R91, there is an example to illustrate how to set up a synchronously blocking main loop:
"Another possibility is to use the async/await syntax:"However this does not actually do what readers expect - and especially - it does not solve what @OleksandrChekhovskyi was asking for in https://discourse.wicg.io/t/offscreencanvas-animations-in-workers/1989/15. That is, the comment
"Okay, I think we've got it. Basically, the syntax for getting a commit that throttles by blocking (as Oleksadr suggests) would simply be "await context.commit();". And of course, this is much better than hard-blocking the entire thread."unfortunately does not solve the use case for a synchronous main loop.
The subtle issue is that an
async functionis a function that returns a Promise, and callingawaitimmediately returns from the calling function (chain of async functions) and continues execution from the first non-async function. This effect breaks the computation model that is required for WebAssembly/Emscripten-based applications that set up their own main loops.Here is a more concrete example based on OffscreenCanvas prototypes, which attempts to set up a blocking main loop, but fails due to the subtlety:
webgl_modal_loop.html:webgl_worker.js:The expectation from a synchronously blocking execution is that the above application should print out
but instead, running the page will print out
This is because the
onmessagefunction will continue executing immediately after the theawait gl.commit();is called, anddeinit()will be called, breaking down the synchronous programming model.I am currently implementing OffscreenCanvas support to Emscripten, and trying to figure out how to implement vsync synchronization when a Worker is rendering via OffscreenCanvas. In the absence of a
GL.commit(blockUntilVsyncFinished=true)type of API or similar help from the OffscreenCanvas spec, my current thinking is to set up arequestAnimationFrameloop in the main browser thread, and use that to ping "vsync finished" events to a Worker, via SharedArrayBuffer. This will work if OffscreenCanvas based rendering is guaranteed to still allow observing "proper"requestAnimationFramesynchronization on the main browser thread, though I am not sure if OffscreenCanvas currently says anything about this?