I spent some time studying the work of SDL2 with Emscripten. Although in the end it turned out to be a bad idea(look at the end), I decided to post information that could help me in my time.
Warn:I'm very bad in English. Most of the text is translated by Google, do not be surprised.
All you need:
- nimble install sl2_nim jsbind
- make a working native application
- get sdl.nim and nim.cfg from this repo
- replace import sdl2/sdl to import sdl
- Change the main loop of your application using sdl.mainLoop.
for example:
while done:
to
sdl.mainLoop done:
nim c -d:asmjs -d:release main.nim
That's all. (Your code still has be compilable for the native)
Nothing complicated. Look at the code for the sdl.nim and nim.cfg. It all boils down to applying the necessary compilation settings and replacing the main application loop with the procedure that will be called by Emscrypton.
template mainLoop*(statement, actions: untyped): untyped =
proc emscLoop{.cdecl.} =
if statement:
emscripten_cancel_main_loop()
sdl.quit()
system.quit()
else:
actions
passL %= "-s USE_SDL=2"
I took examples from Vladar4/sdl2_nim. Not everything was made working.
- ex101_init - everything is working. Nothing interesting to see there
- ex102_logs - same as the previous one
- ex104_timers - it seems the timer is triggered once. Perhaps this is because of the single-threadedness of js
- ex105_syswm - getWindowWMInfo are not defined. But this can easily be fixed
- ex201_textures - works. But look below how to use external files and sdl2-image
- ex202_transformations
- ex203_blending
- ex204_drawing
- ex205_sdl_gfx_primitives - for better performance, I moved the code of the drawing from the main loop. Also look below about gfx primitives
- ex206_bitmap_fonts - works. But look below how to use sdl2_ttf
- ex207_ttf_fonts
- ex208_framerate
- ex209_viewports_and_scaling
- ex210_pixels
- ex211_opengl - Emscripten does not support OpenGL. Only OpenGL ES and WebGL
- ex301_keyboard
- ex302_mouse
- ex303_joystick - I don't have a joystick to test this, but looks working
- ex401_mixer - no music. See below about sdl-mixer
- ex402_panning - works, but without SetDistance
Simply run nim e build.nims
to build all examples.
If you experience problems with local launching of examples on the chromium-based browser, then start your browser with the allow-file-access-from-files key. Ex: chromium --allow-file-access-from-files
I was not interested in sdl-net. But below you can find information about it. See "Ports"
Although WebAssembly(wasm) is more efficient, but not supported by many browsers. Therefore by default (see nim.cfg) it compiles into asmjs. Just add -d:wasm to compiler for wasm.
ex: nim c -d:release -d:wasm ex208_framerate.nim
Live demo: wasm_ex208_framerate
If you're interested, the forum has a post with the benchmarks: Nim in the browser: some benchmarks
The module gfxPrimitives is not included in sdl2. Also it does not exist in other ports. I had to compile it myself. Just download the libSDL2_gfx.o and add the linker option(Already there in a nim.cfg):
passL %= "./libSDL2_gfx.o"
Use preload-file. Then put the files in the assets directory. After compilation, all the files will be assembled into one, but accessible by the same path.
For example:
add to config file passL %= "--preload-file assets"
to your code image1.load(app.renderer, "assets/img1.png")
All available ports are located here: https://github.com/emscripten-ports
Also in $EMSCRIPTEN/tools/ports/ you can find scripts that download and build ports.
All ports are downloaded and unpacked in: $HOME/.emscripten_ports/
In $HOME/.emscripten_cache/asmjs/ the cache and compiled ports are stored.
You can easily connect the desired port by specifying a flag for the linker.
ex: passL %= "-s USE_TTF=2"
This port requires additionally indicate for linker which formats need to be included:
ex: passL %= "-s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS=\'[\"png\"]\'"
Also you can already use the version compiled by me(supports png and bmp). Download and add:
passL %= "./libSDL2_image-png.o"
You can see the script code from Emscripten, but they are not always effective. Also some information can be found in the wiki or on the github.
Example of how to build SDL2:
git clone https://github.com/emscripten-ports/SDL2
cd SDL2-master
mkdir build
cd build
emconfigure ../configure --host=asmjs-unknown-emscripten --disable-assembly --disable-threads --disable-mmx --disable-sdltest --disable-shared --enable-cpuinfo=false CFLAGS="-O3"
emmake make
Similarly, a ttf and a mixer can be built with:
EMCONFIGURE_JS=1 emconfigure ../configure --disable-shared --disable-sdltest --disable-mmx
emmake make
Then you can collect the necessary name.o files using emcc.
ex: emcc ./freetype.bc ./*.o -o libSDL2_ttf.bc
tip: llvm-nm file.o
Will show what names the file exports
So you can build a library with only the required functionality.
There is no separate port for the mixer. The built-in functions of sdl seem to play only wav. There also lacks some functions. For example: SetDistance
I didn't build the mixer with the support of mp3.
Some tips and scripts you can find here: link
Because it has a large size(3 mb) and terrible architecture(port, not sdl).
At first I thought about dynamic linking. But this is an experimental feature(article) and I was not able to use SDL2 as side module.
In general, I spent a lot of time trying to reduce the size. But neither a myself builds with the necessary functionality, nor dynamic linking will not help. Just take a look at how the SDL-image works:
It uses native functions to load the file and the libpng library. This adds about 1 megabyte and a loss in performance. The correct way would be to use ready-made browser functions for working with images. The functions of SDL2 should have been just stubs that caused the corresponding functions on the js.
And a quick review of the code, shows that this is used everywhere. It's horrible. My perfectionism did not allow me to continue to use this option for cross-platform applications.
You can use sdl1. For emscripten there is a simple library that uses the correct method. Just take a look at her code: library_sdl.js