-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Emscripten crossbuild support #359
base: master
Are you sure you want to change the base?
Changes from 18 commits
d3412f5
1a119a5
25ac49b
2262741
90f9d90
2cd7697
f406747
7c6b4e2
3ee4a2f
350e96e
1cc7431
94a8574
4824eda
89f73fe
02b391e
a66b276
44e9372
6c856e4
e185068
257cf67
45d0f93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -13,6 +13,39 @@ option(ENABLE_AUDIO "Enable Audio" ON) | |||
option(ENABLE_MICROPROFILE "Enable microprofile" OFF) | ||||
option(ENABLE_ANGELSCRIPT "Enable AngelScript" ON) | ||||
option(ENABLE_MOFILEREADER "Enable MofileReader" ON) | ||||
option(CROSSBUILD_EMSCRIPTEN "Build for the EmScripten Platform" OFF) | ||||
|
||||
if (CROSSBUILD_EMSCRIPTEN) | ||||
# Turn off not features that are not compatible with emscripten | ||||
|
||||
include(~/.conan/data/emsdk_installer/1.39.6/bincrafters/stable/package/1492a59deb6efdf29776a5734de63fc5f5c58650/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake ) | ||||
endif() | ||||
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten") | ||||
find_package(OpenGL REQUIRED) | ||||
include_directories(${OPENGL_INCLUDE_DIR}) | ||||
|
||||
list( APPEND conan_cmake_run_params SETTINGS os.threads=true ) | ||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --no-check-features") | ||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --no-check-features") | ||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --no-check-features") | ||||
|
||||
#Debug information | ||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --profiling-funcs -Oz -g4 -s PTHREAD_POOL_SIZE=8 -s DEMANGLE_SUPPORT=1 -pthread -s DISABLE_EXCEPTION_CATCHING=0 -s ASSERTIONS=1 -s USE_PTHREADS=1 -s USE_SDL_IMAGE=2 -s USE_SDL_TTF=2 -s USE_ZLIB=1" ) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should probably remove this comment
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I left this in because those are the flags that can be used to debug emscripten. Instead of removing the command, we should check for CMAKE_BUILD_TYPE=Debug and use the aproprate flags instead. |
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --profiling-funcs -O3 -g0 --llvm-lto 1 --llvm-opts 3 -s PTHREAD_POOL_SIZE=8 -pthread -s DISABLE_EXCEPTION_CATCHING=1 -s USE_PTHREADS=1 -s USE_SDL_IMAGE=2 -s USE_SDL_TTF=2 -s USE_ZLIB=1" ) | ||||
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -pthread -s USE_PTHREADS=1 -s USE_SDL_IMAGE=2 -s USE_SDL_TTF=2 -s USE_ZLIB=1" ) | ||||
set(SDL2_LIBRARIES "-s USE_SDL=2") | ||||
set(SDL2_IMAGE_LIBRARIES "-s USE_PTHREADS=1 -s USE_SDL_IMAGE=2") | ||||
#set(SDL2_MIXER_LIBRARIES "-s USE_SDL_MIXER=2") | ||||
set(SDL2_TTF_LIBRARIES "--no-check-features -s USE_SDL_TTF=2") | ||||
set(SDL2_ZLIB_LIBRARIES "-s USE_ZLIB=1") | ||||
|
||||
endif() | ||||
|
||||
|
||||
# Comment-out uneeded libs | ||||
if (NOT ENABLE_AUDIO) | ||||
|
@@ -72,14 +105,27 @@ if (USE_PACKAGE_MANAGER) | |||
|
||||
include(pmm) | ||||
|
||||
pmm(CONAN | ||||
REMOTES | ||||
AFG https://api.bintray.com/conan/anotherfoxguy/conan-packages | ||||
catchorg https://api.bintray.com/conan/catchorg/Catch2 | ||||
ror-dependencies https://api.bintray.com/conan/anotherfoxguy/ror-dependencies | ||||
BINCRAFTERS | ||||
CMakeCM ROLLING | ||||
) | ||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten") | ||||
|
||||
pmm(CONAN | ||||
PROFILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/conan_profiles/emscripten_profile" | ||||
REMOTES | ||||
AFG https://api.bintray.com/conan/anotherfoxguy/conan-packages | ||||
catchorg https://api.bintray.com/conan/catchorg/Catch2 | ||||
ror-dependencies https://api.bintray.com/conan/anotherfoxguy/ror-dependencies | ||||
BINCRAFTERS | ||||
CMakeCM ROLLING | ||||
) | ||||
else() | ||||
pmm(CONAN | ||||
REMOTES | ||||
AFG https://api.bintray.com/conan/anotherfoxguy/conan-packages | ||||
catchorg https://api.bintray.com/conan/catchorg/Catch2 | ||||
ror-dependencies https://api.bintray.com/conan/anotherfoxguy/ror-dependencies | ||||
BINCRAFTERS | ||||
CMakeCM ROLLING | ||||
) | ||||
endif() | ||||
|
||||
if (DEFINED ENV{CI}) | ||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) | ||||
|
@@ -95,6 +141,32 @@ if (USE_PACKAGE_MANAGER) | |||
list(APPEND CMAKE_MODULE_PATH "${CONAN_LIB_DIRS_CATCH2}/cmake/Catch2") | ||||
endif () | ||||
|
||||
# these compiler options are mostly for debugging and trying to get it to work | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these should go in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed |
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten") | ||||
list(APPEND _link_libraries | ||||
#"-s ERROR_ON_UNDEFINED_SYMBOLS=0" | ||||
"--preload-file ${CMAKE_CURRENT_BINARY_DIR}/resources@/resources" #include resources dir in .data file | ||||
"-s USE_PTHREADS=1" | ||||
"--use-preload-plugins" | ||||
# "--source-map-base" | ||||
"-s WASM=1" | ||||
"-s WASM_MEM_MAX=512MB" # ALLOW_MEMORY_GROWTH demands MEM_MAX to be set | ||||
"-s ALLOW_MEMORY_GROWTH=1" #seems necessary for pthreads and dynamic memory allocation | ||||
"-o index.html" | ||||
# "-s DISABLE_EXCEPTION_CATCHING=0" # 0 -> catch exceptions! 1-> is disabled | ||||
"-s TOTAL_MEMORY=33554432" | ||||
# "-s SAFE_HEAP=1" | ||||
#demangle C++ functions | ||||
# "-s ASSERTIONS=0" # use assertions | ||||
"-O3" # no optimization | ||||
"-g0" # most debug info | ||||
CONAN_PKG::libnoise | ||||
${SDL2_LIBRARIES} | ||||
${SDL2_IMAGE_LIBRARIES} | ||||
${SDL2TTF_LIBRARIES} | ||||
${SDL2_ZLIB_LIBRARIES} | ||||
) | ||||
else() | ||||
list(APPEND _link_libraries | ||||
CONAN_PKG::sdl2 | ||||
CONAN_PKG::sdl2_image | ||||
|
@@ -103,6 +175,7 @@ if (USE_PACKAGE_MANAGER) | |||
CONAN_PKG::libnoise | ||||
CONAN_PKG::zlib | ||||
) | ||||
endif() | ||||
|
||||
if (ENABLE_AUDIO) | ||||
list(APPEND _link_libraries CONAN_PKG::sdl2_mixer) | ||||
|
@@ -197,7 +270,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") | |||
link_libraries(dbghelp.lib) | ||||
else() | ||||
# Needed for filesystem library | ||||
if(NOT APPLE) | ||||
if(NOT APPLE AND NOT EMSCRIPTEN) | ||||
# TODO: Link with stdc++fs on Apple with XCode11 | ||||
list(APPEND _link_libraries "stdc++fs") | ||||
endif() | ||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[settings] | ||
os=Emscripten | ||
arch=x86 | ||
#arch=wasm | ||
os_build=Linux | ||
arch_build=x86_64 | ||
compiler=clang | ||
compiler.version=10 | ||
#compiler.version=6.0 | ||
compiler.libcxx=libc++ | ||
[options] | ||
threads=true | ||
[build_requires] | ||
emsdk_installer/1.39.6@bincrafters/stable | ||
[options] | ||
libjpeg-turbo:SIMD = False | ||
sdl2_image:tif = False |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,12 @@ | |
#include <SDL.h> | ||
#include <SDL_ttf.h> | ||
|
||
#ifdef __EMSCRIPTEN__ | ||
#include <emscripten.h> | ||
#endif | ||
|
||
void gameLoop(void* arg_); | ||
|
||
#ifdef USE_ANGELSCRIPT | ||
#include "Scripting/ScriptEngine.hxx" | ||
#endif | ||
|
@@ -26,6 +32,17 @@ | |
template void Game::LoopMain<GameLoopMQ, Game::GameVisitor>(Game::GameContext &, Game::GameVisitor); | ||
template void Game::LoopMain<UILoopMQ, Game::UIVisitor>(Game::GameContext &, Game::UIVisitor); | ||
|
||
typedef struct loop_arg { | ||
Engine *engine; | ||
EventManager *evManager; | ||
UIManager *uiManager; | ||
} loop_arg; | ||
|
||
// FPS Counter variables | ||
const float fpsIntervall = 1.0; // interval the fps counter is refreshed in seconds. | ||
Uint32 fpsLastTime = SDL_GetTicks(); | ||
Uint32 fpsFrames = 0; | ||
|
||
Game::Game() | ||
: m_GameContext(&m_UILoopMQ, &m_GameLoopMQ, | ||
#ifdef USE_AUDIO | ||
|
@@ -240,24 +257,32 @@ void Game::mainMenu() | |
|
||
void Game::run(bool SkipMenu) | ||
{ | ||
loop_arg* arg = new loop_arg; | ||
Timer benchmarkTimer; | ||
LOG(LOG_INFO) << VERSION; | ||
|
||
Engine &engine = Engine::instance(); | ||
EventManager &evManager = EventManager::instance(); | ||
UIManager &uiManager = UIManager::instance(); | ||
|
||
|
||
if (SkipMenu) | ||
{ | ||
Engine::instance().newGame(); | ||
} | ||
|
||
benchmarkTimer.start(); | ||
Engine &engine = Engine::instance(); | ||
|
||
LOG(LOG_DEBUG) << "Map initialized in " << benchmarkTimer.getElapsedTime() << "ms"; | ||
// SDL_Event event; | ||
|
||
arg->engine = &engine; | ||
arg->evManager = &evManager; | ||
arg->uiManager = &uiManager; | ||
|
||
LOG(LOG_DEBUG) << "Map initialized in " << benchmarkTimer.getElapsedTime() << "ms"; | ||
Camera::centerScreenOnMapCenter(); | ||
|
||
SDL_Event event; | ||
EventManager &evManager = EventManager::instance(); | ||
|
||
UIManager &uiManager = UIManager::instance(); | ||
uiManager.init(); | ||
|
||
#ifdef USE_ANGELSCRIPT | ||
|
@@ -278,55 +303,18 @@ void Game::run(bool SkipMenu) | |
} | ||
#endif // USE_AUDIO | ||
|
||
// FPS Counter variables | ||
const float fpsIntervall = 1.0; // interval the fps counter is refreshed in seconds. | ||
Uint32 fpsLastTime = SDL_GetTicks(); | ||
Uint32 fpsFrames = 0; | ||
#ifdef __EMSCRIPTEN__ | ||
emscripten_set_main_loop_arg(&gameLoop, arg, 0, 1); | ||
#else | ||
|
||
// GameLoop | ||
while (engine.isGameRunning()) | ||
{ | ||
#ifdef MICROPROFILE_ENABLED | ||
MICROPROFILE_SCOPEI("Map", "Gameloop", MP_GREEN); | ||
#endif | ||
SDL_RenderClear(WindowManager::instance().getRenderer()); | ||
|
||
evManager.checkEvents(event, engine); | ||
|
||
// render the tileMap | ||
if (engine.map != nullptr) | ||
{ | ||
engine.map->renderMap(); | ||
} | ||
|
||
// render the ui | ||
// TODO: This is only temporary until the new UI is ready. Remove this afterwards | ||
if (Settings::instance().drawUI) | ||
{ | ||
uiManager.drawUI(); | ||
} | ||
|
||
// reset renderer color back to black | ||
SDL_SetRenderDrawColor(WindowManager::instance().getRenderer(), 0, 0, 0, SDL_ALPHA_OPAQUE); | ||
|
||
// Render the Frame | ||
SDL_RenderPresent(WindowManager::instance().getRenderer()); | ||
|
||
fpsFrames++; | ||
|
||
if (fpsLastTime < SDL_GetTicks() - fpsIntervall * 1000) | ||
{ | ||
fpsLastTime = SDL_GetTicks(); | ||
uiManager.setFPSCounterText(std::to_string(fpsFrames) + " FPS"); | ||
fpsFrames = 0; | ||
} | ||
|
||
SDL_Delay(1); | ||
|
||
#ifdef MICROPROFILE_ENABLED | ||
MicroProfileFlip(nullptr); | ||
#endif | ||
gameLoop( arg); | ||
} | ||
#endif | ||
|
||
delete arg; | ||
} | ||
|
||
void Game::shutdown() | ||
|
@@ -370,3 +358,46 @@ template <typename MQType, typename Visitor> void Game::LoopMain(GameContext &co | |
// @todo: Call shutdown() here in a safe way | ||
} | ||
} | ||
|
||
void gameLoop(void* arg_) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's good that you were able to get it to work this way. However I don't think this is the right solution. If Emscripten truly doesn't support multithreading then we can try using coroutines instead. For now, this is a good starting point There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i was sure you wouldn't be happy with this solution. (Because i'm not too) According to the documentation: https://emscripten.org/docs/porting/emscripten-runtime-environment.html#browser-main-loop it seems to be the only way to do this. |
||
// void gameLoop(Engine &engine, EventManager &evManager, UIManager &uiManager) | ||
{ | ||
|
||
loop_arg* arg = (loop_arg*)arg_; | ||
#ifdef MICROPROFILE_ENABLED | ||
MICROPROFILE_SCOPEI("Map", "Gameloop", MP_GREEN); | ||
#endif | ||
SDL_RenderClear(WindowManager::instance().getRenderer()); | ||
SDL_Event event; | ||
arg->evManager->checkEvents(event, *arg->engine); | ||
|
||
// render the tileMap | ||
if (arg->engine->map != nullptr) | ||
arg->engine->map->renderMap(); | ||
|
||
// render the ui | ||
arg->uiManager->drawUI(); | ||
|
||
// reset renderer color back to black | ||
SDL_SetRenderDrawColor(WindowManager::instance().getRenderer(), 0, 0, 0, SDL_ALPHA_OPAQUE); | ||
|
||
// Render the Frame | ||
SDL_RenderPresent(WindowManager::instance().getRenderer()); | ||
|
||
fpsFrames++; | ||
|
||
if (fpsLastTime < SDL_GetTicks() - fpsIntervall * 1000) | ||
{ | ||
fpsLastTime = SDL_GetTicks(); | ||
arg->uiManager->setFPSCounterText(std::to_string(fpsFrames) + " FPS"); | ||
fpsFrames = 0; | ||
} | ||
|
||
#ifndef __EMSCRIPTEN__ | ||
SDL_Delay(1); | ||
#endif | ||
|
||
#ifdef MICROPROFILE_ENABLED | ||
MicroProfileFlip(nullptr); | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -42,12 +42,14 @@ void SIG_handler(int signal) | |||
exit(1); | ||||
} | ||||
|
||||
#else | ||||
// #else | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should remove this comment
Suggested change
|
||||
#elif __unix__ && !__EMSCRIPTEN__ | ||||
|
||||
#include <unistd.h> | ||||
#include <execinfo.h> | ||||
#include <signal.h> | ||||
|
||||
#include <execinfo.h> | ||||
|
||||
void SIG_handler(int signal) | ||||
{ | ||||
switch (signal) | ||||
|
@@ -93,7 +95,7 @@ SDL_AssertState AssertionHandler(const SDL_AssertData *data, void *) | |||
SYMBOL_INFO symbol; | ||||
for (int i = 0; i < size; ++i) | ||||
std::cout << "\tat " << symbol.Name << "\n"; | ||||
#else | ||||
#elif __unix__ && !__EMSCRIPTEN__ | ||||
/* We print the last 10 calls */ | ||||
void *buffer[10]; | ||||
size_t size; | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is good practice...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hash is likely to change... We should use the proper conan way to do this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right about both, but i don't know how to do this with conan. Maybe @AnotherFoxGuy could help us out here?