Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Porting Native code with Emscripten or: Not Reinventing the Wheel in JS

Porting Native code with Emscripten or: Not Reinventing the Wheel in JS

Presented at MelbJS
See also: https://github.com/jsdf/pce

James Friend

April 08, 2015
Tweet

More Decks by James Friend

Other Decks in Programming

Transcript

  1. • lots of good existing code out there • many

    niche libs will never be ported to JS • JS libs not as full featured, well tested Joel on Software: Things You Should Never Do, Part I
  2. Things you probably shouldn't rewrite from scratch • drawing/rendering (eg.

    complex graph/plot types) • media synthesis+processing • data formats: media, 3D, geospatial, archive, domain specific, legacy • computer vision • machine learning • crypto

  3. • clang compiles c/c++ to LLVM bytecode • Emscripten compiles

    LLVM bytecode to ASM.js JS • runs fast in modern browsers • uses shims for IO (SDL, OpenGL, etc) • JS interop mechanisms (expose functions, classes)
  4. Choosing an existing codebase to port • should target a

    POSIX environment • only C or C++ • single threaded • ideally built using make (directly or indirectly)
  5. Porting an existing codebase • Make minimal changes to existing

    code • #ifdef EMSCRIPTEN • keep both native and Emscripten pathways working • some issues can be debugged with native tools • also easier integration of upstream changes
  6. Build system • modify existing Makefile, or drive it with

    scripts/environment variables • for meta build systems (autotools, cmake, etc), plumb your changes through to make, don’t modify output files (obvious, but…) • automate everything so you can keep iterations tight Image credit: James Halliday (@substack)
  7. Development and debugging • take advantage of make incremental rebuilds:


    build out-of-tree, never clean, or,
 clean when switching between native and JS targets, but not when rebuilding • avoid packing data files – requires a rebuild every time data files change • have example program or tests for continuous feedback • optimisation level (-O flag) makes a big difference to perf • debug level (-g flag) disable some optimisations, -g4 creates source maps
  8. Using Emscripten with CommonJS modules/browserify • by default Emscripten creates

    lots of globals • you can wrap Emscripten's output in a function
 - with a prologue defining var Module,
 - and an epilogue to expose APIs you need (FS, PATH, etc)
  9. Module wrapper example module.exports = function(config) { // emscripten expects

    an object called Module to be defined var Module = opts || {}; // hide node/commonjs globals so environment is not detected as node var process = null; var require = null; // [emscripten output goes here] // expose stuff which emscripten defines in this scope Module.FS = FS; Module.PATH = PATH; Module.IDBFS = IDBFS; Module.NODEFS = NODEFS; return Module; }
  10. Customising Emscripten library shims • Emscripten provides SDL, OpenGL etc

    shims • Not fully implemented • Some unimplemented functions can just be stubbed