mosra / magnum

Lightweight and modular C++11 graphics middleware for games and data visualization
https://magnum.graphics/
Other
4.74k stars 439 forks source link

EmscriptenApplication doesn't play well with asyncify #455

Open mosra opened 4 years ago

mosra commented 4 years ago

Reported by @ShaddyDC. Current workaround is using Sdl2Application instead.

Repro code -- after pressing the button, the value is printed, but Frame isn't anymore after that. WindowFlag::AlwaysRequestAnimationFrame doesn't help.

#include <emscripten.h>

EM_JS(int, get_digest_size, (const char* str), {
  // Note how we return the output of handleSleep() here.
  return Asyncify.handleSleep(function(wakeUp) {
    const text = UTF8ToString(str);
    const encoder = new TextEncoder();
    const data = encoder.encode(text);
    out("ask for digest for " + text);
    window.crypto.subtle.digest("SHA-256", data).then(digestValue => {
      out("got digest of length " + digestValue.byteLength);
      // Return the value by sending it to wakeUp(). It will then be returned
      // from handleSleep() on the outside.
      wakeUp(digestValue.byteLength);
    });
  });
});

void ImGuiExample::drawEvent() {
    GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);

    _imgui.newFrame();

    Debug{} << "Frame";

    /* Enable text input, if needed */
    if(ImGui::GetIO().WantTextInput && !isTextInputActive())
        startTextInput();
    else if(!ImGui::GetIO().WantTextInput && isTextInputActive())
        stopTextInput();

    {
        const char* silly = "some silly text";
        static int value = 0;
        ImGui::Text("%s's digest size is: %d\n", silly, value);
        if(ImGui::Button("load")){
            value = get_digest_size(silly);
            Debug{} << value;
        }
    }

    /* Update application cursor */
    _imgui.updateApplicationCursor(*this);

    _imgui.drawFrame();

    swapBuffers();
    redraw();
}

@Squareys since you were originally implementing the idle loop, do you have any idea what could cause this?

ShaddyDC commented 4 years ago

It also affects functions like emscripten_wget_data. After an asincify operation, future frames aren't requested anymore and the application freezes. Manually requesting the next frame draw seems to work around it. Code copied from here

if(ImGui::Button("load")){
    value = get_digest_size(silly);
    Magnum::Debug() << value;

    int (*_callback)(void*);
    _callback = [](void* userData) -> int {
        auto& app = *static_cast<ImGuiExample*>(userData);

        app.drawEvent();

        return true;
    };

    EM_ASM({
        /* Animation frame callback */
        var drawEvent = function() {
            var id = window.requestAnimationFrame(drawEvent);

            /* Call our callback via function pointer returning int with two
            int params */
            if(!dynCall('ii', $0, [$1])) {
                window.cancelAnimationFrame(id);
            }
        };

        window.requestAnimationFrame(drawEvent);
    }, _callback, this);
}