21

I have been working on a PhoneGap plugin to enable WebGL, intended for publishing HTML5 games to mobile. It's called WebGLGap. However, PhoneGap's way of calling plugin code (via exec) typically involves stringifying all the parameters to JSON, then parsing it on the other side again. According to this question, this has not changed even in PhoneGap 2.2 which was advertised as having a faster bridge. For something like WebGL, this is absolutely untenable and kills performance (<10 FPS) even for simple demos. This is because in many cases, especially 2D games, every frame must transmit a large block of JSON data representing all the WebGL commands to run. This includes all vertex data - imagine a huge string of "0.959455, 0.959595, 0.588575, 0.585858..." etc every frame.

Obviously stringifying and parsing is an unnecessary and inefficient step, but I'm struggling to find a way to pass the JSON data from JS to native which avoids that. Ideally this should work on both Android and iOS, but I'm happy to stick to an Android-only solution for now. Does anyone have any ideas about the most efficient way to do this?

Community
  • 1
  • 1
AshleysBrain
  • 22,335
  • 15
  • 88
  • 124

6 Answers6

12

Linkedin use Web Sockets for their iPad app. Might be worth looking into: http://engineering.linkedin.com/mobile/linkedin-ipad-nativeweb-messaging-bridge-and-websockets

Some of the benefits that you're looking for

  • WebSockets can communicate asynchronously from JavaScript to native.
  • WebSockets don't have a payload limit
  • WebSockets don't require us to encode our JSON strings as URL parameters
  • WebSockets should be faster than URL scheme navigation
sciritai
  • 3,688
  • 1
  • 17
  • 22
  • Very promising for iOS, but not supported at all on Android still (at least in the stock browser, which you basically get with PhoneGap). Any alternative ideas for Android? – AshleysBrain Nov 28 '12 at 00:22
  • Does PhoneGap even support web sockets? – NT3RP Nov 28 '12 at 20:26
  • @NT3RP The question is more: does Safari or the Android stock browser support web sockets. Looks like Android stock doesn't unfortunately. – sciritai Nov 29 '12 at 19:41
  • This looks like the best solution for iOS - unfortunately it doesn't look like it will work on Android, but hopefully Google get their act together and replace the stock browser with Chrome (complete with WebSockets) soon... congrats, you win the bounty! – AshleysBrain Nov 30 '12 at 13:45
3

Addressing performance

Looking at CordovaPlugin.java, as you mentioned, everything is a String:

public boolean execute(String action, String rawArgs, CallbackContext callbackContext) throws JSONException {
    JSONArray args = new JSONArray(rawArgs);
    return execute(action, args, callbackContext);
}

If, for example, the conversion from String to JSONArray is the only bottleneck, then you could override this method in your plugin and perform your own deserialization. It's a small performance improvement, but it might be worth investigating.

Creating an alternate bridge

As for creating an alternative bridge, that's trickier. I don't know much about Cordova / PhoneGap, but from what research I've gathered, Cordova exposes a specific Javascript interface via addJavascriptInterface. If you could implement your own NativetoJSMessageQueue, you might be able to wire it all together.

EDIT
After conducting a bit more research, I can provide a bit more direction. The really relevant part of the NativetoJSMessageQueue is the various BridgeModes it implements (see line 92). You could look at the other bridge modes as an example.

Unfortunately, the NativetoJSMessageQueue has exactly four bridge modes registered; assuming that you could implement your own bridge mode, you would still need to some how register it as a new mode for the NativetoJSMessageQueue.

NT3RP
  • 15,262
  • 9
  • 61
  • 97
  • Thanks, but I think even a custom way via converting to string could be too slow... unless there's a fast way to share a string like a binary buffer? Creating an alternative bridge sounds promising though... – AshleysBrain Nov 25 '12 at 05:01
2

I'm not sure what exactly you want to do but I notice that in your project you are converting the JSON to String and then you will pass it through the PhoneGap plugin , convert it to JSON and then convert it to Matrix!

what if you keep your data in a string and convert the string straight to Matrix? this way you can skip the converting to and from JSON part

Ardavan Kalhori
  • 1,664
  • 1
  • 18
  • 32
  • Thanks, but matrices aren't a performance sensitive part of the project, mainly the buffer data arrays. I need a way to avoid stringifying at all. – AshleysBrain Nov 28 '12 at 21:07
2

On android, you can try to use the addJavascriptInterface (link to WebView's documentation) method of the WebView to "inject Java objects into the WebView", which is the approach PhoneGap uses to add the apis for geolocation, filesystem, etc.

I guess this will be faster than using the plugin approach (didn't test this yet).

Check the code of the PhoneGapView, which extends WebView: https://github.com/phonegap/phonegap/blob/3c7324ea8b3944b6e5d3d91e9e328c9c3939464b/android/framework/src/com/phonegap/PhoneGapView.java#L42

UPDATE

After all, this only works for simple types such as int or String, like you said in a comment below.

Passing a JavaScript object using addJavascriptInterface() on Android

Trying to do otherwise will result in exceptions from within the android.webkit.WebViewCore and android.webkit.JWebCoreJavaBridge classes.

UPDATE 2

Well, the best code you'll achieve using this approach will be something like this (from https://github.com/commonsguy/cw-advandroid/blob/master/WebView/GeoWeb1/src/com/commonsware/android/geoweb/GeoWebOne.java#L80):

public String getLocation() throws JSONException {
  Location loc=myLocationManager.getLastKnownLocation(PROVIDER);

  if (loc==null) {
    return(null);
  }

  JSONObject json=new JSONObject();

  json.put("lat", loc.getLatitude());
  json.put("lon", loc.getLongitude());

  return(json.toString());
}

and most likely, all parameters should be string'fied (or JSON'fied...)

This might help you if you have a performance hit when triying to create strings from javascript side: http://www.javascripture.com/ArrayBuffer

Community
  • 1
  • 1
fableal
  • 1,577
  • 10
  • 24
  • Thanks, sounds promising, but according to here: http://blog.johnmckerrell.com/2011/01/21/working-with-android-addjavascriptinterface/ "After some testing I found that addJavascriptInterface only allows basic data types such as boolean, int and String." They then go on to describe a workaround... involving stringifying. I at least need an array of floats to be passed - is this supported by addJavascriptInterface? – AshleysBrain Nov 28 '12 at 21:09
  • If you want asynchronous callbacks, I guess you're in trouble (https://github.com/phonegap/phonegap/blob/3c7324ea8b3944b6e5d3d91e9e328c9c3939464b/android/framework/src/com/phonegap/AccelListener.java#L72); I'll try to see for synchronous calling of Java methods – fableal Nov 28 '12 at 22:26
  • It's a good find, but I'm afraid stringifying JSON is exactly what I need to avoid... it really does kill performance! – AshleysBrain Nov 29 '12 at 11:00
  • I'm starting to get the feel that you either have to 1) compile html+js to native apps (idea growing, might start a proof-of-concept in the meanwhile) or 2) improve the android.webkit.* classes – fableal Nov 29 '12 at 12:28
1

I've read about encoding data in png. Here is the original article: http://cg.alexandra.dk/2012/11/26/webgl-tutorial-optimizing-data-transfer-for-webgl-applications/

Might be useful.

Andrey Kuzmin
  • 4,479
  • 2
  • 23
  • 28
1

Given that json is very inefficient, you may gain a lot of performance if you take this route:

server side: data model -> protobuf (build() to binary) -> encode base64 (binary to string) -> add as one json field or payload (best is to pass binary blob as a payload, but I'm not sure if PhoneGap allow this)

client side: decode from base64 (string to binary) -> protobuf (parse from binary) -> use the protobuf objects directly as deep as you can in your code - it's extremely efficient data model

Amir Uval
  • 14,425
  • 4
  • 50
  • 74