Introduction to Native Client



Joel Webber <joel@monetology.com>
dev nexus
atlanta
2012

Why


  • Google is performance-obsessed
  • 3 years ago:
    • What if we could run native code in the browser?
  • Retain good parts of the web:
    • portability, security ease, reach, etc.
  • Leverage bazillions of lines of existing C[++] code
  • Ultimately replace insecure NPAPI plugins
Some stats suggest that, for some games, up to 90% of people who *want* to play abandon when prompted to install a plugin.

What


  • Run untrusted x86 code safely in Chrome
  • Fully sandboxed
    • No system API access
    • May only call blessed "Pepper" APIs

Performance


  • Box2D: NaCl roughly 10x faster than best Javascript
    • Often closer to 20x for hand-written JS
    • Oddly, best JS results are for C++ cross-compiled to JS (!)
  • Flocking Geese benchmark

Security


  • Same model as web
  • Not NPAPI or ActiveX
  • Can only call Pepper APIs
  • Double-sandbox:
    • Proof-carrying code
    • Chrome renderer sandbox

Features


  • Accelerated 3d rendering
  • Real-time stereo audio
  • Sandboxed local file storage
  • Full-screen, mouse-lock
  • Networking
  • URL loading
  • Async communication with Javascript
  • Shared-memory threads
  • Dynamic linking
  • GNU libc

Applications


  • Games
  • 3D modeling, CAD
  • Media editing (photo/video/audio)
  • Scientific computing (any heavy number crunching)

Support


  • Chrome 14+ (Current is 17)
  • Not all machines support 3D rendering (see below)
  • Enabled when:
    • Command-line flag (for developers)
    • Triggered by Chrome Web Store

The reality of cross-platform development


  • Consoles:
    • C++, completely proprietary SDKs
  • Mobile devices:
    • C++, Objective C, Java
    • OpenGL, OpenAL, otherwise proprietary
  • Native desktop machines:
    • C++, Objective C
    • OpenGL, DirectX, OpenAL, platform APIs
  • Web:
    • Actionscript, Flash APIs
    • Javascript, HTML5 APIs
  • Chrome:
    • C++, Pepper APIs
    • Javascript, HTML5 APIs

Games and Apps


  • Bastion (Supergiant)
  • Mini Ninjas (Square Enix)
  • Cordy & Sleepy Jack (SilverTree Media)
  • Pirates of New Horizons & Planet Buster (Exit Strategy Entertainment)
  • Various 3D Training tools (Heartwood Studios)
  • Lots of emulators — MAME, DOSBox, etc.

Middleware



Example: Star Legends


  • 3D MMO for Android 2.0
  • Ported to NaCl in 14 days
    • 1 programmer, 600k lines of code
  • Runs across Android, iOS, & NaCl

Distribution


  • Through Chrome Web Store
  • Monetize with:
    • Ads
    • In-app purchase
    • Checkout
  • Or through Chrome Extension

Developing in NaCl


Application and deployment structure


Application lifecycle


GCC / G++ Compiler


  • Make C++ code ISO/IEC 14882:1998 compliant
    • POSIX style coding / functions
    • OS specific calls not supported
  • Removes / tests any unsafe code
  • Produces safe compiled x86 code

Pepper APIs


Architecture


Rendering


  • OpenGL ES 2.0
  • Remember this is stricter than full OpenGL
    • Not all extensions available
    • But the same as most iOS and Android devices
  • Can use ANGLE to test on Windows

Audio


  • Straightforward low-level audio API
    • Stereo
    • Real-time
  • More info

Communication with Javascript


  • Send messages to NaCl:
     myAppModule.postMessage('w00t?'); 
  • Receive messages from NaCl:
     myAppModule.addEventListener('message', messageFunc, false); 
  • Send messages to Javascript:
    pp::Var var;
    this->PostMessage(var);
                   
  • Handle messages from Javascript:
     void MyAppInstance::HandleMessage(const pp::Var& var_message) { ... } 
  • More info

Loading Resources


  • Just like in Javascript, resource loading is async
  • pp:URLLoader() makes this relatively painless
                  void MyInstance::StartRequest(const std::string& url) {
                    loader_ = pp::URLLoader(this);
                    loader_.Open(request, factory_.NewRequiredCallback(&MyInstance::OnOpenComplete));
                  }
    
                  void MyInstance::OnOpenComplete(int32_t result) {
                    response_ = loader_.GetResponseInfo();
    
                    // etc.
                  }
                  

Threads


Pepper thread


  • Do not call blocking functions on the main thread!

Pepper APIs


  • Pepper APIs calls are Asynchronous
    • ~1 'frame' to get results
  • Must be called from the main thread [will be fixed soon]
  void fopen_mt(void* void_data, int32_t /* unused */) {
    ASSERT_MAIN_THREAD();
    FileIO::OpenParams* params = static_cast<FileIO::OpenParams*>(void_data);
    file_io_->Open(*file_ref_, params->flags, pp::CompletionCallback(fopen_cb,params));
  }

  void fopen_cb(void* void_data, int32_t result) {
    // Yay, do some stuff!
  }
        

Working around thread restrictions


  • This is not how your game engine is designed
  • Best idea?
    • Run game logic on separate thread
    • Wrap platform functions to talk with main thread.

Working around thread restrictions


  static int32_t RequestAndWait(void (*function)(void*, int32_t), void* param) {
    ASSERT_WORKER_THREAD()
    pp::Module::Get()->core()->CallOnMainThread(0,
        pp::CompletionCallback(function, param), PP_OK);
    pthread_cond_wait(&gData_.cond, &gData_.mutex);
    return data_.result;
  }

  static void ReturnFromMainThread(void* void_data, int32_t result) {
    ASSERT_MAIN_THREAD();
    MainThreadData* data = static_cast<MainThreadData*>(void_data);
    data->result = result;
    pthread_cond_signal(&data->cond);
  }
          

Development Tricks


Caching and Dev Tools


  • Disable caching for fast iteration
  • Chrome dev tools
    • Debugging network issues, HTML/JS parts of the game, console

about:tracing


  • Trace of Chrome rendering internals
  • Can emit events from user code!
  • Load/Save traces

Debugging


Deployment Issues


3D on the web


  • 3D APIs can be malicious in the web
  • WebGL has a 'blacklist' of 3d drivers
    • Failure to create a webgl context
  • Detect early and alert user!

Blacklisted drivers: Detect in Javascript


  // Has blacklisted hardware / feature sets?
  function textureSizeTest(size) {
    var canvas = document.createElement('canvas');
    var gl = canvas.getContext('webgl') ||
             canvas.getContext('experimental-webgl');
    if (gl)
      return gl.getParameter(gl.MAX_TEXTURE_SIZE) >= size;
  }
          
Further info

NaCl Whitelisting


  • Nacl not enabled until :
    • Devs - enable via flags
    • Users - Install from Chrome Web Store
  • Origin of Location white listed
    • NMF and Nexe loc must match!

Can the user run NaCl?


  // Is using chrome?
  var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;

  // Is app installed?
  if (window.chrome.app.isInstalled)
    // You're running as an installed app, via the app launcher!
  else
    // You're running via a bookmark/link.
          
Further info

Future Developments


APIs


  • Web sockets
  • Gamepad support
  • Memory-mapped files
  • Track all new web features as they're added to Chrome

pNaCl (Pinnacle)


  • "Portable Native Client"
  • Already OS-neutral. pNaCl makes it instruction-set neutral
  • Uses LLVM bitcode rather than x86
    • Just as portable as Javascript
  • Chrome Web Store restriction lifted

Questions?