emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.85k stars 3.31k forks source link

Pthreads + webgl causes exception #8831

Closed sergeyext closed 4 years ago

sergeyext commented 5 years ago

The following code

int main()
{
        if (glewInit() != GLEW_OK) {
                std::cout << "No glew" << std::endl;
                return 1;
        }
        if (glfwInit() != GLFW_TRUE) {
                std::cout << "No glfw" << std::endl;
                return 2;
        }

        auto w = glfwCreateWindow(100, 100, "Foo", 0, 0);
        glfwMakeContextCurrent(w);

        GLuint bufId;
        glGenBuffers(1, &bufId);
        glBindBuffer(GL_ARRAY_BUFFER, bufId);
        glBufferData(GL_ARRAY_BUFFER, 256, nullptr, GL_STATIC_DRAW);

        return 0;
}

being compiled with flags -s USE_PTHREADS=1 -s USE_GLFW=3 -s USE_WEBGL2=1 crashes with Assertion failed: target_thread, at: /path/to/emsdk/emscripten/1.38.31/system/lib/pthread/library_pthread.c,355,emscripten_async_queue_call_on_thread on glBufferData call.

It works correctly if we comment out glBufferData or build the code without threading support. Threading and shared buffers in browser are enabled and a minimal example with ClearColor() and Clear() work correctly even from a worker thread.

Also crashes with webgl1.

juj commented 5 years ago

Trying out

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

int main()
{
        if (glewInit() != GLEW_OK) {
                std::cout << "No glew" << std::endl;
                return 1;
        }
        if (glfwInit() != GLFW_TRUE) {
                std::cout << "No glfw" << std::endl;
                return 2;
        }

        auto w = glfwCreateWindow(100, 100, "Foo", 0, 0);
        glfwMakeContextCurrent(w);

        GLuint bufId;
        glGenBuffers(1, &bufId);
        glBindBuffer(GL_ARRAY_BUFFER, bufId);
        glBufferData(GL_ARRAY_BUFFER, 256, nullptr, GL_STATIC_DRAW);
        glClearColor(1,1,0,1);
        glClear(GL_COLOR_BUFFER_BIT);

        std::cout << "done" << std::endl;
        return 0;
}

with em++ a.cpp -o a.html -s USE_PTHREADS=1 -s USE_GLFW=3 -s USE_WEBGL2=1 -std=c++11 does work for me, tried on incoming branch at 576f6399ef3997606017660912074d88762ce2a1 The above code gives

Screen Shot 2019-06-20 at 3 13 03 PM

Perhaps the STR is missing a detail?

sergeyext commented 5 years ago

Updating to 1.38.36 solved the problem. Previous version was 1.38.31.

sergeyext commented 5 years ago

Problem is still here, adding more detail to the issue.

@juj I started with your example with your command line and it worked. Then I added -s OFFSCREENCANVAS_SUPPORT=1 -s OFFSCREEN_FRAMEBUFFER=1 and got crash:

/home/sergey/code/emsdk/fastcomp/emscripten/system/lib/pthread/library_pthread.c,446,emscripten_async_queue_call_on_thread") at Error
    at jsStackTrace (http://localhost:8003/ems_thread.js:1157:13)
    at stackTrace (http://localhost:8003/ems_thread.js:1174:12)
    at abort (http://localhost:8003/ems_thread.js:10393:44)
    at ___assert_fail (http://localhost:8003/ems_thread.js:1802:7)
    at _emscripten_async_queue_call_on_thread (wasm-function[282]:76)
    at _emscripten_async_queue_on_thread_ (wasm-function[283]:837)
    at _glBufferData (wasm-function[267]:208)
    at _main (wasm-function[95]:252)
    at Object.Module._main (http://localhost:8003/ems_thread.js:9743:33)
    at Object.callMain (http://localhost:8003/ems_thread.js:10225:30)

The whole command line was

em++ a.cpp -o a.html -g -s USE_PTHREADS=1 -s USE_GLFW=3 -s USE_WEBGL2=1 -s OFFSCREENCANVAS_SUPPORT=1 -s OFFSCREEN_FRAMEBUFFER=1 -std=c++11
blockspacer commented 5 years ago

May be related to https://github.com/emscripten-core/emscripten/issues/8638

test_webgl_offscreen_canvas_in_pthread fails in good working browser. Am i starting test correctly? "EMCC_DEBUG=1 python tests/runner.py browser.test_webgl_offscreen_canvas_in_pthread"

...

is it expected -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 to break -s OFFSCREENCANVAS_SUPPORT=1 and -s OFFSCREEN_FRAMEBUFFER=1 ? (with wasm & pthreads, as in provided example)
juj commented 5 years ago

Hmm, I think GLFW may not have been updated to support multithreading. If you use HTML5 context creation API, that is multithreading compatible.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.

major-mayer commented 2 years ago

I am having more or less exactly the same problem as the OP, however i am using EGL instead of GLFW for context creation.

Currently i am creating my context like this:

    // create a GL context...
    EGLint numConfigs;
    EGLint majorVersion;
    EGLint minorVersion;
    EGLConfig config;
    EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE};

    // create an RGBA-mode context
    // specify depth, stencil, accumulator sizes (GLint)
    EGLint attribList[] = 
    {
        EGL_DEPTH_SIZE, rc.GetDepthSize(),
        EGL_ALPHA_SIZE, 8,
        EGL_STENCIL_SIZE, 0,
        //EGL_ACCUMBITS, 0 // doesn't seem to exist TODO is this needed?
    };

    // obtai and egl display
    static Display* x_display = NULL;
    m_display = eglGetDisplay((EGLNativeDisplayType)x_display);
    if ( m_display == EGL_NO_DISPLAY )
    {
        throw std::runtime_error("no display!");
    }

    if (!eglInitialize(m_display, &majorVersion, &minorVersion))
    {
        throw std::runtime_error("init failed!");

    }

    // Get configs
    if (!eglGetConfigs(m_display, NULL, 0, &numConfigs))
    {
        throw std::runtime_error("get configs failed!");
    }

    // Choose config
    if( !eglChooseConfig(m_display, attribList, &config, 1, &numConfigs))
    {
        throw std::runtime_error("choose config failed!");
    }

    // Create a surface
    m_surface = eglCreateWindowSurface(m_display, config, NULL, NULL);

    // Create a GL context
    auto ctx = eglCreateContext(m_display, config, EGL_NO_CONTEXT, contextAttribs);

        // make the context current: i.e. bind the buffer to the context to make it current!
        eglMakeCurrent(m_display, m_surface, m_surface, m_rc->GetContext());

        std::cout << "context creation successful!" << std::endl;

It was possible to call functions some functions like glClearColor(...) that indeed had a effect on the canvas that i created. But calling glBufferData created exactly the same error as @sergeyext described. I am also using the flags -s OFFSCREEN_FRAMEBUFFER=1 (to use OpenGL with pthreads at all) and -sOFFSCREENCANVAS_SUPPORT=1 (the error stayed the same, wether i enabled this or not).

After reading through this PR https://github.com/emscripten-core/emscripten/pull/5580 which was not merged, i think that the combination of EGL and pthreads is currently not working right @juj ? Would it be better to use the HTML5 context creation API in this case?

juj commented 2 years ago

After reading through this PR https://github.com/emscripten-core/emscripten/pull/5580 which was not merged, i think that the combination of EGL and pthreads is currently not working right @juj ?

Even without #5580, EGL should work in pthreads enabled programs, IF the GL context is created on the main browser thread, and all GL and EGL calls are restricted to happen only on the main browser thread.

The idea with #5580 was that it would enable use of EGL in pthreads as well, when paired with the OffscreenCanvas feature (although EGL+OffscreenCanvas was never developed as part of #5580).

If you want to use WebGL in a pthread via OffscreenCanvas, using HTML5 context creation API will enable that.

major-mayer commented 2 years ago

Ah thank you very much, this explains my problems pretty good. I was creating a egl context inside a std::thread (which I think uses pthreads under the hood) and wondering where all those threading errors come from. Activating the offscreen canvas flag on link time didn't change the situation, because #5580 wasn't merged.

I now switched to the HTML5 context creation API and things work pretty well.

One thing I wonder is if the egl solution would be able to allow creating contexts in a pthread via proxying the calls to the main browser thread. That's what I am currently doing with the HTML5 api, because using offscreen canvas (without proxying) with std::thread instead of pthread don't seem to work.