Enabling & Using WebGL on iOS
Background
I’ve become a lot more interested in WebGL lately, and remembered reading on Hacker News a while back that WebGL would be available in iOS 5 as part of the iAd framework.
WebGL will not be publicly available in iOS 5. It will only be available to iAd developers.
When iOS 5.0 went GM, I decided to knock up a quick WebGL demo to see what was involved & how well it performed.
Unfortunately it didn’t run at all — creating a webgl-experimental
context via the canvas element’s getContext()
API would fail.
Breakthrough
I shot through a tweet to Antoine Quint (iAd JS/iOS software engineer at Apple), asking for confirmation of Chris Marrin’s email:
Chris Marrin suggested that WebGL would be available in iAds as of iOS 5.0. Is that actually the case?
To which he replied:
WebGL has been supported for iAds since iOS 4.2.
That’s even better news than I had anticipated! However, it still doesn’t explain why I’m unable to create a WebGL context.
With a little bit more prompting, I got to the bottom of why HTMLCanvasElement
was returning null
when I called getContext("experimental-webgl")
:
You also need to have “uses-webgl” in your ad’s plist. Sorry, forgot about that.
The uses-webgl
setting is completely undocumented, but it certainly works!
Demo iAd WebGL Project
I’ve put together a demo WebGL iAd project that can simply be dragged into the Simulator or synced to your device via iTunes.
Drag the WebGL.ad
directory into the iOS Simulator & the built-in iAd Tester
app will launch. You’ll see a banner at the bottom, which when tapped will bring up the WebGL demo:
If you’re having difficulties getting the iAd running, take a look at the Testing on the iOS Simulator and Testing on the Device sections of the iAd JS Programming Guide.
Do standard UIWebView
s (secretly) support WebGL?
So if the iAd framework is using a UIWebView
, perhaps we can get the same behaviour in our own web views?
Looking at the output of ps
while running iAd Tester
in the iOS Simulator, you’ll notice an app called AdSheet
running. Using otool
, we can see what private frameworks it’s linked against:
otool -L /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk//Applications/AdSheet.app/AdSheet | grep PrivateFrameworks
/System/Library/PrivateFrameworks/iAdCore.framework/iAdCore (compatibility version 1.0.0, current version 1.0.0)
/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices (compatibility version 1.0.0, current version 1.0.0)
iAdCore.framework
looks interesting. Let’s take a look for WebGL-related functionality using class-dump
:
class-dump /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/PrivateFrameworks/iAdCore.framework/iAdCore
Searching through the output, it’s pretty clear that we’ve found what we’re after:
1 2 3 4 5 |
|
Turns out we can simply link against the private iAdCore.framework
, and use ADWebView
s instead of UIWebView
s. Here’s an implementation which does just that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Is ADWebView
really doing that much?
Perhaps we can get rid of ADWebView
altogether & just implement whatever -[ADWebView setWebGLEnabled:]
is doing in our own custom UIWebView
subclass. We’re still going to be calling private APIs, but at least we won’t need to link against private frameworks.
To reproduce -[ADWebView setWebGLEnabled:]
, we’ll need to disassemble it using otool -tVv
, or install objdump
via Homebrew & use gobjdump -d
. Here’s the bit we’re interested in:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
I won’t go into detail about how to read the disassembly, but hopefully you’ll be able to see that at 5abf4
we’re running a block of code on what seems to be the UIWebView
’s dedicated web thread (_WebThreadRun$stub
). The block itself seems to be making 3 consecutive Objective-C method calls (_objc_msgSend$stub
) at 5ac20
, 5ac32
& 5ac4c
.
By running the previous snippet where we were linking against the private iAdCore
framework and using ADWebView
, we can set a symbolic breakpoint on ___29-[ADWebView setWebGLEnabled:]_block_invoke_0
and see what the values of the $eax
& $ecx
registers are prior to calling objc_msgSend
. Look at the i386 ABI for objc_msgSend()
, and you’ll note that these registers correspond to the receiver ($eax
) of the message and the selector ($ecx
) being called.
Stepping through, we see:
- At
5ac20
,$eax
is an instance ofADWebView
, and$ecx
is_browserView
. - At
5ac32
,$eax
is an instance ofUIWebDocumentView
and$ecx
iswebView
. - At
5ac4c
,$eax
is an instance ofWebView
and$ecx
is_setWebGLEnabled:
.
From this, we can piece together our re-implementation of -[ADWebView setWebGLEnabled:]
:
1 2 3 4 5 |
|
I’ve put together an Xcode project demoing the results of this, which you can find on Github.
Summing Up
Apple are clearly working towards supporting WebGL in a more general sense, as can be seen in their support for it in iAds.
Instead of whinging about it only being available in iAds, I’ve demonstrated that it’s possible to take advantage of WebGL using standard embedded web views (albeit using private APIs).
This will let developers track the progress of Apple’s commitment to WebGL, and skate to where the puck is going to be.
I’m really looking forward to seeing what you all come up with!
Have something to say? Discuss on Hacker News.