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.