HiWebGL原创翻译整理
作者: Nathan de Vries
原文地址:http://atnan.com/blog/2011/11/03/enabling-and-using-webgl-on-ios/
背景
最近我对WebGL越来越感兴趣,我突然想起来曾经在Hacker News上看过一个新闻说iOS 5中将会在iAD框架中支持WebGL。
iOS 5不会开放对WebGL的普遍支持,而只会对iAD开发者开放。
—— Chris Marrin,WebKit工程师 (来源:Public WebGL Mailing List)
在iOS 5释出GM版本时,我决定做一个简单的WebGL Demo来看看支持的情况和性能如何。
不幸的是这个Demo根本不能运行——当我尝试使用canvas的“getContext()”创建一个“webgl-experimental”的上下文时,失败了!
突破
我马上发了一条微博(其实是Tweet啦!)给 Antoine Quint(iAd JS/iOS软件工程师,就职于Apple),确认Chris Marrin的E-mail内容是否属实:
Chris Marrin暗示说iOS 5将会在iAd中支持WebGL。这是真的吗?
Nathan de Vries —— Twitter
他回复说:
iAd从iOS 4.2开始就支持WebGL啦!
Antoine Quint —— Twitter
这真是个比我预想的还要好的消息!然而,他还是没解释为什么我不能创建一个WebGL上下文。
我一点一点的慢慢尝试,我终于知道为什么当我调用getContext(“experimental-webgl”)的时候canvas总是返回null了。
你还需要把“uses-webgl”添加到你的ad的参数列表里。对不起,忘说了。
Antoine Quint —— Twitter
这个“uses-webgl”的参数设置从来没有被公布过,但是它真的管用!
iAd WebGL Demo
我制作了一个WebGL的iAD的工程,可以拖拽到模拟器中或者使用iTunes同步到设备上。
把WebGL.ad目录拖拽到iOS模拟器中,然后会运行内置的iAd Tester应用。你会在底部看到一个条幅,点击这个条幅就会打开WebGL Demo。
如果你的iAd无法运行,可以去查看一下《iAd JS编程指南》中的《使用iOS模拟器测试》和《使用设备测试》这两个章节。
标准的UIWebView暗中支持WebGL?
如果iAd框架使用了UIWebView,那我们是不是可以在浏览web时也会得到相同的效果?
在iOS模拟器中运行iAd Tester时检查ps的输出,你会发现运行着一个叫做AdSheet的应用。使用otool,我们看一下它连接到了哪个私有框架:
1 2 3 | 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看起来很有趣。让我们用class-dump看看与WebGL相关的函数。
1 | class-dump /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/PrivateFrameworks/iAdCore.framework/iAdCore |
在输出中搜索一下,很容易就可以找到以下内容:
1 2 3 4 5 | @interface ADWebView : UIWebView { BOOL _webGLEnabled; } @property(nonatomic) BOOL webGLEnabled; // @synthesize webGLEnabled=_webGLEnabled; @end |
反过来,我们可以简单的连接到iAdCore.framework,然后使用ADWebView来代替UIWebView。以下是一个实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @implementation WGLAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { Class ADWebView = NSClassFromString(@"ADWebView"); UIWebView* webView = (UIWebView *)[[[ADWebView alloc] initWithFrame:self.window.bounds] autorelease]; [webView setWebGLEnabled:YES]; NSString* htmlPath = [[NSBundle mainBundle] pathForResource:@"WebGL" ofType:@"html"]; NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:htmlPath]]; [webView loadRequest:request]; UIViewController* viewController = [[[UIViewController alloc] init] autorelease]; viewController.view = webView; self.window.rootViewController = viewController; [self.window makeKeyAndVisible]; return YES; } @end |
ADWebView能行吗?
也许我们可以完全摆脱ADWebView,只是在自定义的UIWebView子类中加入“-[ADWebView setWebGLEnabled:]”。我们仍将继续调用私有API,但是至少我们不需要再连接私有框架了。
为了再生“-[ADWebView setWebGLEnabled:]”,我们需要反汇编一下,可以使用“otool -tVv”,或者通过Homebrew安装objdump,然后调用“gobjdump –d”。下面是我们感兴趣的一些东西:
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 | 0005aba3 <-[ADWebView setWebGLEnabled:]>: 5aba3: 55 push %ebp 5aba4: 89 e5 mov %esp,%ebp 5aba6: 56 push %esi 5aba7: 83 ec 24 sub $0x24,%esp 5abaa: e8 00 00 00 00 call 5abaf <-[ADWebView setWebGLEnabled:]+0xc> 5abaf: 59 pop %ecx 5abb0: 8b b1 89 e4 04 00 mov 0x4e489(%ecx),%esi 5abb6: 8a 45 10 mov 0x10(%ebp),%al 5abb9: 8b 55 08 mov 0x8(%ebp),%edx 5abbc: 88 04 32 mov %al,(%edx,%esi,1) 5abbf: 8b b1 d5 65 03 00 mov 0x365d5(%ecx),%esi 5abc5: 89 75 e0 mov %esi,-0x20(%ebp) 5abc8: c7 45 e4 00 00 00 42 movl $0x42000000,-0x1c(%ebp) 5abcf: c7 45 e8 00 00 00 00 movl $0x0,-0x18(%ebp) 5abd6: 8d b1 50 00 00 00 lea 0x50(%ecx),%esi 5abdc: 89 75 ec mov %esi,-0x14(%ebp) 5abdf: 8d 89 c1 d9 04 00 lea 0x4d9c1(%ecx),%ecx 5abe5: 89 4d f0 mov %ecx,-0x10(%ebp) 5abe8: 89 55 f4 mov %edx,-0xc(%ebp) 5abeb: 88 45 f8 mov %al,-0x8(%ebp) 5abee: 8d 45 e0 lea -0x20(%ebp),%eax 5abf1: 89 04 24 mov %eax,(%esp) 5abf4: e8 d7 45 00 00 call 5f1d0 <_WebThreadRun$stub> 5abf9: 83 c4 24 add $0x24,%esp 5abfc: 5e pop %esi 5abfd: 5d pop %ebp 5abfe: c3 ret 0005abff <___29-[ADWebView setWebGLEnabled:]_block_invoke_0>: 5abff: 55 push %ebp 5ac00: 89 e5 mov %esp,%ebp 5ac02: 57 push %edi 5ac03: 56 push %esi 5ac04: 83 ec 10 sub $0x10,%esp 5ac07: e8 00 00 00 00 call 5ac0c <___29-[ADWebView setWebGLEnabled:]_block_invoke_0+0xd> 5ac0c: 5e pop %esi 5ac0d: 8b 7d 08 mov 0x8(%ebp),%edi 5ac10: 8b 47 14 mov 0x14(%edi),%eax 5ac13: 8b 8e 54 66 04 00 mov 0x46654(%esi),%ecx 5ac19: 89 4c 24 04 mov %ecx,0x4(%esp) 5ac1d: 89 04 24 mov %eax,(%esp) 5ac20: e8 a7 46 00 00 call 5f2cc <_objc_msgSend$stub> 5ac25: 8b 8e 50 66 04 00 mov 0x46650(%esi),%ecx 5ac2b: 89 4c 24 04 mov %ecx,0x4(%esp) 5ac2f: 89 04 24 mov %eax,(%esp) 5ac32: e8 95 46 00 00 call 5f2cc <_objc_msgSend$stub> 5ac37: 0f be 57 18 movsbl 0x18(%edi),%edx 5ac3b: 8b 8e 94 6a 04 00 mov 0x46a94(%esi),%ecx 5ac41: 89 54 24 08 mov %edx,0x8(%esp) 5ac45: 89 4c 24 04 mov %ecx,0x4(%esp) 5ac49: 89 04 24 mov %eax,(%esp) 5ac4c: e8 7b 46 00 00 call 5f2cc <_objc_msgSend$stub> 5ac51: 83 c4 10 add $0x10,%esp 5ac54: 5e pop %esi 5ac55: 5f pop %edi 5ac56: 5d pop %ebp 5ac57: c3 ret 5ac58: 0f 1f 84 00 00 00 00 nopl 0x0(%eax,%eax,1) 5ac5f: 00 |
我不会详细解释如何理解这些反汇编内容的细节,但是希望你们能够注意到在5abf4,我们正在运行一些看起来像是使用了UIWebView的web线程(_WebThreadRun$stub)的代码。这些代码好像用连续三次调用了Objective-C方法(_objc_msgSend$stub),分别在5ac20、5ac32和5ac4c。
在前面我们使用ADWebView并连接到私有框架iAdCore的代码中,我们可以在___29-[ADWebView setWebGLEnabled:]_block_invoke_0设置一个断点标识,然后检查暂存器$eax和$ecx的值,看看哪个优先调用了objc_msgSend。在i386 ABI for objc_msgSend()中,你会发现这两个暂存器对应于被调用的信息的接收者($eax)和选择者($ecx)。
继续下去,我们会发现:
- 在5ac20,$eax是ADWebView的实例,$ecx是_browserView
- 在5ac32,$eax是UIWebDocumentView的实例,$ecx是webView
- 在5ac4c,$eax是WebView的实例,$ecx是_setWebGLEnabled:
这样,我们就可以拼凑出出的-[ADWebView setWebGLEnabled:]重实现
1 2 3 4 5 | - (void)setWebGLEnabled:(BOOL)enableWebGL { UIWebDocumentView* webDocumentView = [self _browserView]; WebView* backingWebView = [webDocumentView webView]; [backingWebView _setWebGLEnabled:enableWebGL]; } |
我把Xcode的工程都放到了GitHub。
总结
很明显,从对iAd支持WebGL中可以看出来,Apple正在致力于将WebGL的支持普及到更广泛的应用场合中。
与其抱怨为什么只在iAd中支持WebGL,我的实验证明了在标准嵌入式的web浏览中使用WebGL是可能的(尽管使用了私有API)。
这可以让开发者继续跟进Apple对WebGL的承诺,看看他们走到哪一步了。
欢迎和我一起讨论!
3 trackbacks