emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.86k stars 3.32k forks source link

How to use requestAnimationFrame in place of wgpuInstanceProcessEvents #22343

Open jspanchu opened 3 months ago

jspanchu commented 3 months ago

In library_webgpu.js, the wgpuInstanceProcessEvents method suggests to use requestAnimationFrame.

image

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
}
jspanchu commented 3 months ago

@kainino0x @beaufortfrancois

bss-jan-krassnigg commented 2 months ago

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).

beaufortfrancois commented 2 months ago

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

juj commented 2 months ago

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:

  1. A callback is registered with emscripten_set_main_loop()
  2. Then the application "falls out of main" to yield execution back to the browser's event loop.
  3. 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.