pthom / hello_imgui

Hello, Dear ImGui: unleash your creativity in app development and prototyping

Home Page:https://pthom.github.io/hello_imgui

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Emscripten multithread compile options

Guillaume227 opened this issue · comments

I would like to start experimenting with an emscripten build of my app (that was part of the original draw to hello_imgui as I was able to bring up the minimal example hello_imgui_app on a localhost server so quickly).
My imgui application uses std::thread and my understanding is I need to turn on a number of additional compilation flags like -pthread and -sASYNCIFY.
I am struggling to find where these should go in the current hello_imgui setup.

I see the following comment in hello_imgui_emscripten.cmake:

# hello_imgui_emscripten_target_compile_options needs fix!
# For the moment, these options are global via hello_imgui_emscripten_global_options.cmake
# function(hello_imgui_emscripten_target_compile_options target_name)
#     target_compile_options(
#         ${target_name} 
#         PUBLIC
#         "SHELL:-s USE_SDL=2"
#         "SHELL:-s USE_WEBGL2=1"
#         "SHELL:-s WASM=1" 
#         "SHELL:-s FULL_ES3=1"
#         "SHELL:-s ALLOW_MEMORY_GROWTH=1"
#     )
# endfunction()

Is the idea to add something like

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -sASYNCIFY")

in hello_imgui_emscripten_global_options.cmake ?

I can't seem to marry the settings working on e.g. this sample emscripten with threads project :
https://github.com/jiulongw/emscripten-mutex-test/blob/main/run
to the hello_imgui cmake framework.

The minimal example I am working with is:

#include "hello_imgui/hello_imgui.h"
#include <thread>

int main(int , char *[]) {

  std::thread thread([]{}); // should run, do nothing and exit.

  HelloImGui::Run(
          [] { ImGui::Text("%s", "Hello World!"); }, // Gui code
          "Hello!",                            // Window title
          true                                 // Window size auto
  );

}

It compiles but doesn't display anything in the browser (if I comment out the thread line, it's basically the hello_imgui_app and it works).

My imgui application uses std::thread and my understanding is I need to turn on a number of additional compilation flags like -pthread and -sASYNCIFY

You are entering a complex territory, since the support for threads in Webasm/emscripten is rather new and a bit experimental. I managed to get it working once, but I remember several steps had to be taken; and I do not remember all of them.

This note from https://emscripten.org/docs/porting/pthreads.html is especially important:

Browsers that have implemented and enabled SharedArrayBuffer, are gating it behind Cross Origin Opener Policy (COOP) and Cross Origin Embedder Policy (COEP) headers. Pthreads code will not work in deployed environment unless these headers are correctly set. For more information click this

Is the idea to add something like set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -sASYNCIFY") in hello_imgui_emscripten_global_options.cmake ?

This file is rather old, and I think you might be able to do it on a target level instead, using

if (EMSCRIPTEN)
   target_compile_options(...)

Hi,

I made a test on my side.

Several things are required to be able to use threads with emscripten: see https://emscripten.org/docs/porting/pthreads.html

1/ The flag '-pthread` needs to be added to all files and at link time:

For example

add_compile_options(-pthread)
add_link_options(-pthread)

This can be done either with hello_imgui_emscripten_global_options.cmake or at the top of your main CMakeLists (before add_subdirectory(hello_imgui))

2/ In hello_imgui_cmake/emscripten/hello_imgui_emscripten.cmake , you may want to remove -s ALLOW_MEMORY_GROWTH=1 in order to suppress warnings about the fact the -s ALLOW_MEMORY_GROWTH combined with shared memory may be slow.

3/ When testing you need a server that sends COOP and COEP headers: see https://web.dev/coop-coep/

This simple python program can do it for you

#!/usr/bin/python3

# From https://gist.github.com/Faless/1e228325ced0662aee59dc92fa69efd7

from http.server import HTTPServer, SimpleHTTPRequestHandler, test
import os, sys, logging
import argparse

class CORSRequestHandler (SimpleHTTPRequestHandler):
    def end_headers (self):
        self.send_header('Cross-Origin-Opener-Policy', 'same-origin')
        self.send_header('Cross-Origin-Embedder-Policy', 'require-corp')
        SimpleHTTPRequestHandler.end_headers(self)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--port', default=8060, type=int)
    parser.add_argument('-r', '--root', default='', type=str)
    args = parser.parse_args()
    if args.root:
        os.chdir(args.root)
    test(CORSRequestHandler, HTTPServer, port=args.port)

Thanks, I am going to take a look.
Let me point out the simplicity of the example project I referred to above.
The compile step gave me great hopes in its simplicity. It is literaly just:

    subprocess.check_call(
        [
            "em++",
            "-std=c++17",
            "-pthread",
            "-s",
            "PTHREAD_POOL_SIZE=2",
            "-s",
            "ENVIRONMENT=web,worker",
            "main.cc",
            "-o",
            "index.html",
        ]

It works for me in the same exact setup (trying both up-to-date vanilla Chrome and Firefox) that I am playing with hello_imgui in, so it feels tantalizingly close.
From what I can tell that example also does not make use of COEP / COOP. EDIT: Never mind, it does use COEP/COOP.
Finding where to plug that in my hello_imgui dummy std::thread example is proving harder than I thought.
I am going to make another attempt with the pointers you gave me.

Thanks so much, pointing out that COOP/COEP saved the day for me. I had tunnel vision on the Cmake / C++ / em++ settings and overlooked that initially!
Do you want a PR for this so other people get it right the first time? or is that use case out of scope for a minimal hello_imgui use case?

Regarding ALLOW_MEMORY_GROWTH: are you saying that in your experience that warning is a false positive and should be turned off or is it warning about a real performance impact, in the context of emscripten-wrapped multi-threaded cpp ?

Actually I don’t know

Do you want a PR for this so other people get it right the first time? or is that use case out of scope for a minimal hello_imgui use case?

This would be an interesting addition, which I am considering to work on.

However, it would need to be well documented and sufficiently customizable, i.e.

  • have an option HELLOIMGUI_EMSCRIPTEN_PTHREAD
  • maybe add an option regarding ALLOW_MEMORY_GROWTH
  • provide some doc in the readme
  • provide a python script that runs a server with the COOP and COEP headers and mention it in the readme

If you feel like working on it, tell me ;-)

I am just looking for an opportunity to give back. In that case it looks like you have fleshed it out pretty well in your mind already and my added value would be limited.

In that case it looks like you have fleshed it out pretty well in your mind already and my added value would be limited.

Agreed 👍


I added support for multi-threading in b9e8198.

I also added some doc about the usage in the main CMakeLists.txt:

hello_imgui/CMakeLists.txt

Lines 85 to 101 in b9e8198

#------------------------------------------------------------------------------
# Esmcripten build options
#------------------------------------------------------------------------------
if (EMSCRIPTEN)
# You can enable support for multithreading for emscripten via HELLOIMGUI_EMSCRIPTEN_PTHREAD
# Warning:
# You will need a server that sends specific http headers (Cross Origin Opener Policy (COOP) and Cross Origin Embedder Policy (COEP))
# HelloImGui provides a demo web server which provides that sends those headers. You can run it like this:
# python tools/emscripten/webserver_multithread_policy.py
option(HELLOIMGUI_EMSCRIPTEN_PTHREAD "Build emscripten with multithreading support" OFF)
# With multithreading support, automatic memory growth can be slow with emscripten, and is disabled by default
# In that case, you can call
# hello_imgui_set_emscripten_target_initial_memory_megabytes(your_app_name nb_megabytes)
# to set the initial memory for a given target
option(HELLOIMGUI_EMSCRIPTEN_PTHREAD_ALLOW_MEMORY_GROWTH "Allow memory growth with emscripten even if multithreading support is on" OFF)
endif()