转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2125

Metro是Win8的一个重要组成部分。对用户来说,Metro给移动和桌面(尤其是触摸设备)带来了全新的体验;对开发者来说,可以把Metro当作一个基于WinRT的新平台。传统程序在Win8上只能以desktop方式执行,可以访问所有传统Win32 API;Metro程序则只能访问一部分通过WinRT暴露出来的API。从开发体验来看,Metro更类似于移动平台的方法,程序需要经过deploy、签名、打包,才能分发。

API的差异

有很多Win32 API在新的WinRT中已经消失了,需要绕开或者用WinRT的函数重写。和当年移植到Android的过程一样,首先需要解决Boost的移植问题。这里借用的是Ogre的Metro补丁,它原先是针对boost 1.50的,需要手动一个个修改才能应用于boost 1.51。大部分修改都是函数名的替换而已,大的修改是thread部分。WinRT已经不再提供诸如CreateThread之类的API,而是提供了Windows::System::Threading来支持thread。那个补丁用了稍微修改过的ThreadEmulation库,通过WinRT来实现原先Win32 API的thread接口,使得原先使用Win32 API的thread可以几乎不修改。Python也有类似的问题,但在这个版本里我没去解决,只是用#ifdef把python绕开了。这个问题会在下个版本的脚本抽象层中彻底得到解决。

窗口

核心部分,少数用到了Win32 API的地方,都找到了WinRT中相应的函数,除了窗口部分。桌面程序的窗口是自己建立,自己设置属性;而Metro的窗口是系统建立好,通过SetWindow设置给app的,这一点和Android很像。在自己的程序里需要写一个派生自Windows::ApplicationModel::Core::IFrameworkView的类,负责和系统打交道,还需要一个派生自Windows::ApplicationModel::Core::IFrameworkViewSource的类,负责建立view。好在,这两个类的样例实现都可以在VS2012的例子中找到。经过对App3DFramework的一系列修改,所有跟Metro窗口交互的代码都可封装到那两个类中,外部接口保持不变。D3D11建立的部分也只需要改几行即可。

main

Metro的入口函数必须声明为

[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^)

而不是int main(),所以这个地方得用#ifdef来区分。这也是唯一一个需要区分的上层代码。当年通过修改android_native_app_glue能做到让Android的入口也是int main(),但这次我还没想到好办法来处理这点。

CMake

以上一切都准备就绪后,KlayGE Core、D3D11、Octree都可以编译通过,生成dll了。本以为exe也同样轻松,没想到遭遇了一个恶心的问题:CMake不完全支持Metro。正如这个bug report所说的,目前的最新版本CMake 2.8.10.1漏掉了<AppContainerApplication>true</AppContainerApplication>和<AppxManifest Include=”Package.appxmanifest” />,使得exe无法deploy。更遗憾的是,CMake的作者认为Metro的用户量太小,暂时不值得他们去修改这个bug,除非开发者自己提交patch。所以目前KlayGE的Sample只能通过提供手动创建的vc11工程文件来解决这个问题。

结果

目前测试了Text和DeferredRendering两个例子,都能顺利地在Metro下安装和启动。

Metro Start

这个版本基本就到这个程度了,下个版本开始要支持Metro和Android触屏操作,更深入地应用新平台。

在现在的版本里,还有一些需要注意的问题。因为Metro下不支持D3DCompile之类的函数,effect都得offline先编译成.kfx才能载入。所以先要用非Metro的执行一遍,生成kfx。这个问题会在下个版本的离线编译器中解决。同时,meshml也需要编译成model_bin。脚本和网络因为时间问题来不及移植,只能以后慢慢解决。另外,需要注意的是,所有被exe依赖的dll和资源文件都得添加到工程里,并把content属性设置为Yes,才会被deploy。