我的订单|我的收藏|我的商城|帮助中心|返回首页
虚拟现实新闻>VR>行业资讯>行业知识

WinNT下Vega虚拟现实视景仿真软件的分析与设计

文章来源:搜维尔[SouVR.com] 作者:Frank 发布时间:2011年07月07日 点击数: 字号:

  该框架结构能够将程序中的数据和显示部分进行有效的隔离,并能将一个文档与多个视进行对应。这种设计方法在设计模式[3]中被称为观察者模式(Observer),它的结构如图2 所示。它是一种对象行为型模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。 为了便于开发者能较容易的开发出基于 MFC 的 Vega应用程序,Vega 通过继承 MFC 中的 CView 类而派生出一个子类 zsVegaView。这个zsVegaView 类提供了启动一个 Vega线程最基本的功能,还以虚函数的形式定义了特定的应用要进行操作的通用接口,因此用户的应用程序只需从zsVegaView派生出新类并根据需要重载必要的虚函数即可。从设计模式的观点来看,它是采用了模板方法模式(TemplateMethod)。模板方法模式是一种类行为模式,其意图是定义一个操作算法的骨架,而将一些具体步骤延迟到子类中去,这是代码重用的一种基本技术。模板方法导致了一种反向的控制结构,即指的是一个父类调用一个子类的操作,而不是相反,它的实现依赖于对象特性中的多态性。

  3.1 基于 MFC 的 Vega 应用的线程分析

  通过采用模板方法将用户开发一个基于 MFC 应用的工作量减少到最低,这是 Vega 系统提供的方法,其初衷是好的,但由于 MFC 类库并不是一个支持多线程访问的类库。从图 3 可以看出,用户派生的子类通过在 OnInitialUpdate()函数中调用基类的 runVega()来启动 Vega 线程,并将派生类的指针作为参数传给新启动的子线程,这恰是问题的所在。

  由于源代码中的类空间和线程运行时的线程空间是可以相互交迭的,也就是说不同的线程在运行时可以访问相同的类的实例——对象,而 MFC 类库本身设计时并未考虑到多线程访问,因此在线程间传递视类CView(根据“ISA”规则[4],它的子类对象也是一个 CView 对象)将是危险的,实践也证明了这一点。在单文档多视中在 Vega 线程中改变文档数据后通过调用函数 UpdateAllViews()来更新所有相应的视图时旋即出现了访问保护异常的错误。

WinNT下Vega虚拟现实视景仿真软件的分析与设计

  回头看一下前面的设计,用类 CView 的派生类来嵌入与 Vega 功能有关的功能代码,而这些功能代码中主要是启动 Vega 的三个主要步骤,模型渲染主循环和便于用户扩展的几个虚函数接口,其对于 CView 的关联仅仅是使用了CView 类所拥有的视窗口的句柄而已。这种设计其实违反了软件设计的基本准则:高内聚,低耦合[5-6]。为了既能保持由模板方法设计带来的好处,又能达到高内聚、低耦合的目标,最好的方法就是进行切割分离。将所有与 Vega 系统有关的数据和操作单独作为一个基类 CVega 来实现,并保持用户的扩展借口。这样用户只需从 CVega 类派生出自己的类并将视口的句柄作为参数进行传递就可以了。那么当 Vega 线程改变数据后如何通知各个视口及时的进行更新呢?好在Windows 平台的消息驱动机制,使得可以利用视口的句柄发送消息来实现。

  3.2 基于 MFC 的 Vega 线程中的问题

  前面已经提到,Vega 应用程序可以通过应用定义文件(.ADF)来载入三维场景模型。在实际开发的应用中,当用户打开应用程序时,在不退出该应用的前提下进行三维场景切换是十分必要的。从 Vega 的应用程序框架中我们可以看出,框架主要由它的配置三步曲和渲染主循环构成。当用户已经打开一个场景后需要切换到另一个场景时,程序正处于渲染主循环中,本应只需用配置三步曲中的第二步导入另一个.ADF 文件再进行第三步设置或三步曲皆重新作过再进入渲染主循环即可,遗憾的是由于某些未知的具体原因(并非程序结构的实现技术问题)而行不通。由于 Vega 系统提供的是链接库形式的函数调用,没有源码,从设计软件系统的角度来看,这种为了保证整个系统的完整性,系统外部接口的一致性,系统内部更新的独立性以及系统内部实现技术的保密性等而进行的封装是完全有必要的。然而在保证以上特性的前提下,给开发用户提供必要的诊错手段也是必不可少的。在这方面 Windows 的 API 调用以及 MFC 类库等做的十分好,它们在许多操作的结果通常都返回一个值,以告诉用户是否成功或是失败还是出现异常,更完善的是 Windows系统提供了一个全局的函数调用 GetLastError()操作来获取每一步操作后的结果是否成功。从设计模式的观点来看,这是使用了单件模式(Singleton),它使得在程序的任何位置都能直接访问到该函数。而在 Vega 系统里却没有提供如此完善的诊错手段,它的大部分操作没有返回结果,因此用户只能认为此次操作成功了。然而一旦程序出现了问题,用户将会不知所措。而且,由于 Vega 将类的接口定义都“封装”起来了,用户所能获取的仅仅是一个可用来充当句柄的类的指针而已,一些与面向对象有关的继承、多态、消息操作[4]等皆无明显体现,这大概与 Vega 旨在最大限度的兼容现有程序的移植目标有关吧。

  若在 Vega 线程未退出时进行重新配置将导致失败,那么在切换场景时让正在运行的线程自然终止而重新开启另一个线程应该不会有错了。然而结果是令人沮丧的,出现一个地址访问保护的错误。导致这种错误的主要原因是引用的无效的指针,该指针所指的空间要么是分配空间后已被释放,要么是指向其它线程的局部地址空间。为什么会出现这种错误呢?在第二次初始化 Vega 系统时,前一个 Vega 线程不是已经终止了吗?所以一定存在遗留问题。经调试跟踪显示,问题就出在 Vega 配置三步曲的第一步 vgInitWinSys()上,该函数的作用主要是初始化 Vega 系统并创建共享内存以及信号量等,除此之外它还在后台做了一件重要的事情——就是又开启了一个 Vega 窗口子线程,该子线程根据传送的窗口句柄参数创建一个与该句柄对应窗口一相同大小的窗口二,并将它蒙(覆盖)在窗口一上,这是 Vega 开发指南上未有提及的,这样 Vega 系统的渲染窗口就可以嵌入到基于 MFC 的视口上了。该子线程的创建是必不可少的,但却没有终止它的办法——它是 Vega 函数内部创建的。又因为线程隶属于进程,只有进程结束了,所有的线程才全部退出。所以即使创建它的父线程已死,它却还“活”着,不过因此而保留了一些无用的参数如无效的指针等。而更糟的是当第二个线程启动时,函数 vgInitWinSys()却不再启动新的子线程而是继续保留原来没死的窗口线程,因此错误的发生是注定的。

  3.3 基于 MFC 的 Vega 应用的进程解决方案

  由 Windows 线程的一些特性可以得出解决问题的有效方法就是进一步隔离——用一个单独的 Vega 进程来实现。从模式的观点来看,这可以作为一个新的设计模式应用于软件的设计当中去,不妨称之为栅栏模式(Fence)。它通过将同一个应用中的不同部分进行相互黑箱化,彼此之间仅通过定义好的接口进行访问,这使得各部分之间的相互影响减少到最低限度。从操作系统平台考虑,Windows 不是一个实时操作系统,但却是一个较稳定的系统。由于 FVSS 系统是一个实时的虚拟仿真系统,在它的视景驱动模块中将处理大量的实时仿真结果数据,如何保证该模块中的两个进程之间能快速有效地进行通信将成为问题。

共3页 您在第2页 首页 上一页 1 2 3 下一页 尾页 跳转到页 本页共有3442个字符
  • 暂无资料
  • 暂无资料
  • 暂无资料
  • 暂无资料
  • 暂无资料