How to use requestAnimationFrame in place of wgpuInstanceProcessEvents
In library_webgpu.js, the wgpuInstanceProcessEvents method suggests to use requestAnimationFrame.
In a desktop/web application which uses webgpu C++ api, how can I use the emscripten_set_main_loop or emscripten_push_main_loop_blocker or emscripten_request_animation_frame APIs to emulate wgpuInstanceProcessEvents? Here's what I think can be done, but it doesn't yield to the browser thread. I must also say that I don't have a proper understanding of how to use these raf and main loop methods. Can someone provide an example or suggestions?
void MyApplication::ProcessEvents()
{
#ifndef __EMSCRIPTEN__
instance.ProcessEvents();
#else
long id = emscripten_request_animation_frame([](double time, void *userData){ std::cout << "wait\n"; }, nullptr);
emscripten_cancel_animation_frame(id);
#endif
}
@kainino0x @beaufortfrancois
Not sure whether your use case is similar to mine, but I came across this post trying to figure out why I didn't see my rendered result.
You don't actually need to use emscripten_request_animation_frame, if you just want to see the result of a WebGPU frame.
Instead, use emscripten_set_main_loop(FrameFunc, 0, true);
Notice the 'true' at the end, it sets up an infinite loop for you.
Now FrameFunc gets called for you and it automatically displays the result afterwards. Just make sure that your FrameFunc actually returns (mine did a loop internally as well, and thus never finished, which is why I only saw black).
FWIW, I use emscripten_set_main_loop(Render, 0, false); in https://github.com/beaufortfrancois/webgpu-cross-platform-app/blob/main/main.cpp#L151C3-L152C1 and it seems to work "fine". I'd be happy to change this if we think it's a best practise as I use it for https://developer.chrome.com/docs/web-platform/webgpu/build-app#update_the_code
https://github.com/emscripten-core/emscripten/blob/main/test/webgpu_basic_rendering.cpp shows an example how to use emscripten_set_main_loop() with WebGPU:
- A callback is registered with emscripten_set_main_loop()
- Then the application "falls out of main" to yield execution back to the browser's event loop.
- The browser will then begin to pump the provided frame() callback function, in which you can do the per-frame render event.
Note that in the webgpu_basic_rendering.cpp example only one frame is rendered for testing purposes, which is why exit(0) is immediately called at the end of frame(), but in an animation loop case, that would be omitted.
If refactoring the code to run in an event-based callback approach is not feasible, then it is also possible to refactor the code to run a synchronous main loop by using the upcoming JSPI feature. This is currently not available in Emscripten's WebGPU bindings, although one example can be found in this sample.
There the function wgpu_present_all_rendering_and_wait_for_next_animation_frame(); serves in place of the wgpuInstanceProcessEvents(); function in Dawn.
Thank you for this wgpu_present_all_rendering_and_wait_for_next_animation_frame tip, I was having difficulty finding methods of synchronous framerate=Hz with WebGPU.
As I port TestUFO to WebGPU, I'm looking for workflows that allows me to synchronize to refresh rate in a perfect framerate=Hz manner.
Part of the reason I'm abstracting this way is because WebGPU HDR is now supported in Chrome/Edge/Brave/Safari (Tech Preview 215) without needing a chrome://flag unlike Canvas2D HDR. WebGL will be supported too because I need to run some of my own shadertoy ports (but only for the few shader-required TestUFO tests)
However, to allow accurate detection of refresh rate (and heuristics on missed refresh cycles) in TestUFO, I have to use methods that is synchronous. I must always have access to a synchronouz framerate=Hz as TestUFO is capable of 1000fps 1000Hz on prototype 1000Hz monitors (most of the 50 selectable TestUFO animations, at upper right corner menu, render in just 0.1ms on modern GPUs whenever power management is disabled)
(At this level, Meltdown/Specture timer fuzz of 0.1ms in Chromium and 1ms in Safari/FireFox starts to become a major error margin, but 1000fps 1000Hz works on most high end gaming rigs on internal prototype 1000Hz displays, and consumer 480Hz displays)
@mdrejhon I'm just curious, why do you want to use WebGPU via Emscripten instead of using the browser's JS API directly? That seems more suitable for TestUFO. Do you specifically want to test Emscripten as well, not just the browser?
BTW, please note we're working on replacing Emscripten's built-in WebGPU bindings with external ones. See #23432.
I'm in my research stage for TestUFO 3.0, plus also being able to render TestUFO thumbnails of the 50 tests on the server via its own TestUFO tests too -- e.g. for opengraph style sharing etc -- This is where potential solutions based on emscripten-core comes in..
Doing it manually otherwise becomes unmaintinable if it expands (e.g. imagine 100 tests/demos/animations/presentations/etc), and generating a static motionblurred PNG from simulated motion blur out of multiple consecutive offscreen rendered TestUFO frames and whatnot. Some tests get replaced with a new animation, requiring replacement of the thumbnail, and what not. Testing multiple routes of making it happen, which just so happens to involve emscripten-core and want to avoid wrecking the gametime clocks (etc), but I'm also working around that too.
No matter; I've come up with multiple viable workarounds for the opengraph-style offline/server rendered testufo images, so I'm good now. It will be a few months before that's deployed, but thanks.
Meanwhile, venting something else (maybe one of you can tell me which standards group to reach out to) See more details about my www.xkcd.com/1172 style hack:
I'm trying to use multiple selectable different methods of framepacing (selectable by user in future for mission-critical use cases).
The multiplatform TestUFO consists of 50 different tests selectable at upper right corner -- some scientific, some demos, some classroom teaching, some convention kiosks -- etc. Some display manufacturers use some tests with scientific equipment (I'm in a NIST researcher paper about equipment measurement method on a TestUFO test, -- requires framepacing precision but it also suddenly distorts if it stutters, so it's got a magical ruin-the-results-on-a-framedrop, which notifies the tester/researcher to just conveniently try again. But the better the framepacing the merrier)
But will expand to future GPU stuff too (like 3D/shaders). I have to prep for multiple methods of synchronization APIs, all with different pros/cons. Some platforms sync better with different sync APIs.
For example I had to add a very XKCD #1172 style busyloop workaround for Safari stutters (TestUFO -> Settings -> High Performance Mode for MacBooks) to make the requestAnimationFrame stutters disappear.
Some tests flag themselves as scientific-quality required (will auto-turn-on the busyloops) and others will flag themselves as relaxed (etc), but different sync APIs have different QoS on different platforms...
Sync weirdnesses is my pain point and sometimes different vsync APIs behave differently, especially if certain things are done differently (e.g. onscreen vs offscreen), and depending on whichever platforms. And the lack of VRR in browsers too (e.g. setting custom requestAnimationFrame() frequency is not possible in browers. (Some of my users are asking for VRR support in browsers)
VRR displays synchronizes to the frame Present() or gl.finish() rate, but browser engine master compositor frequencies don't pass through their underlying dominant-element (fullscreen element rate, whether video or canvas). Not even in fullscreen mode) despite VRR being easier than most developers sync since it's only the display synchronizing to the software rate of frame presentations. Instead, the browser compositor blasts at full rate, despite a 2d/webgl/webgpu/video element running at a different rate, without the option for the browser compositor to sync to that underlying element's rate (which means VRR would automatically work)
Still, for enabling variable refresh rate displays (which is surprisingly easy to implement in games) -- future engines should permit JS-control of the browser compositor rate via the rate of frame-completions (which means VRR works when the rate is within VRR range, it just automatically switches to VSYNC when it hits MaxHz).
I push the sync limits of browsers! So I am trying to discover multiple sync workarounds.
That being said -- Maybe I need to join the web rendering standards committees but my time is starved. I had some collab with W3C, but that WIP got lost when things moved over to WHATWG...and then I moved on;
Anyway, I realize this is probably the wrong venue (e.g. WHATWG might be more proper)
Thanks for the info!
For frame pacing, the HTML group at WHATWG is the appropriate one. I see you've already posted on https://github.com/whatwg/html/issues/5025. Another relevant issue might be https://github.com/whatwg/html/issues/8031.
We're moving the WebGPU bindings to be maintained in Dawn instead of Emscripten, so I'm migrating bugs to Dawn. I don't think there is anything to do here though, I think the questions more or less got answered. So closing. Feel free to file bugs on Dawn as needed!