今天想通一个问题,如何异步地加载3D资源,进而实现整个地图块的后台异步加载机制,进而实现无缝大地图加载.
3D资源(纹理,顶点缓冲等)的加载分两个阶段,首先是数据加载,而后是根据读入的内存进行3D资源构造.
发现,IO操作的耗时远比3D资源构造多得多,3D资源构造,如glTexImage,glBufferData,Lock,Unlock速度相对还是很快的,而如果把整个加载放入一个线程导致跨线程3D资源的构造和使用上的问题.
理论上在一个线程创建的3D资源不能在另一个线程内使用(如OpenGL),即使可以做到(如DirectX的CREATE_MULTITHREADED),也会出现很多需要同步的3D资源句柄.
所以将纯IO操作加载阶段放入一个线程,将分析内存进行3D资源构造的阶段放到主线程的空闲时间处,能最简单地实现异步加载方案.
进而,在该逻辑运行下,出现这样的结果:所有的节点对象被建立出来,所有的3D资源类对象被建立出来,但类对象里的3D资源是空的,所以上层的逻辑仍在流畅地运行,但作图都是空的.
IO线程,从数据读取请求队列取出请求,进行IO操作,将请求放入完成队列.
主线程在渲染完成后,判断IO完成对列中是否有完成的请求,计算当前空闲时间是多少,如要保持流畅25帧,每帧40毫秒,当前花了25毫秒,还有15毫秒可以消费且可以保持流畅.
在15毫秒内处理完成队列里的每个请求,使用请求里的内存进行3D资源构造,如glTexImage,Lock,Unlock直到超过15毫秒或队列被处理完.即最小可控制粒度为当前这个资源的构造时间.假设有一张很大的纹理的显卡构造花了20毫秒(一般不可能),超出15毫秒,那游戏就会出现稍微的卡.
系统内每个对象都可能处在一种Shell状态,光有抽象逻辑上的意义,而没有本体数据.
唯心主义上而言,没有实体,只有意志,游戏一样跑下去,只是看不到图像.
进而带来的好处是,如果一个建筑的数据还没完成IO,其3D对应资源还没建立,那建筑内的Model也不会被加载.建筑的3D资源在主线程被构造时,需要加载其关联/依赖的Model资源,这个加载也是异步的,所以不会卡住,从而形成嵌套循环的异步加载过程,最终的显示结果对象逐个显示出来,相当地有趣.
所以,当异步加载地图,引发异步加载模型,异步加载世界物件,又引发异步加载纹理对象.
假设卡住模型加载时的IO线程,那么游戏还是会运行,只是看不到模型,模型的碰撞检测也失效,如果用多线程IO呢?
OpenGL的name机制很适合做空对象,glGenXXX只是生成一个空对象,在没加载数据前,用其调用渲染也不会出错,只是白色或没图像而已,等数据从异步线程出来了,图像就出来的.
然而,对于更复杂的流式逻辑,空对象模式也难以应用多线程加载,比如人物换装纹理的构建一定要求即时加载纹理,武器绑定要求人体的模型已经建立,才知道绑定到哪个socket,让我觉得复杂游戏玩家的外观难以异步加载.进入wow中时,玩家人物总是最后一步被加载显示出来的,有服务器的原因,估计也有客户端的同步加载原因.当有新人进入视野时,机器有时还是会卡一下的.
在Opengl.org上看到一个很新奇的技术,跟wow有关,
World of Warcraft update adds mult-threaded OpenGL on Intel iMacs running OS X 10.4.8 or higher. Depending on hardware, scene and graphical settings, this can raise frame rates up to a factor of 2X.
技术文档出处:
http://developer.apple.com/technotes/tn2006/tn2085.html