Clayman的专栏

It's all about XNA & GPU Programming

用户操作
[即时聊天] [发私信] [加为好友]
claymanID:soilwork
192751次访问,排名382好友0人,关注者13
soilwork的文章
原创 85 篇
翻译 15 篇
转载 0 篇
评论 330 篇
clayman的公告
嘿嘿 ^o^....
最近评论
ffffk:研究到这的都是高手
jym5596337:我也不知道我怎么就走到了你的路上来了呵呵...
太晕了,我们专业就学的C#... 那我就凑合着用它学习MDX喽,但到了2008你的这个时期,感觉形式有点尴尬,以前的人说 在中国搞软件是 前有微软,后有盗版。
现在是 前有XNA后有C++ ... MDX 学习资料太太太难找了。。太太太少了。 师兄给介绍下你学历路途中的资料目录咯。。。我好找来学习咯。。 感谢哦感谢。。
jym5596337:好象没有继续哇 ...
shapin:ATI的网站有个支持HLSL语法高亮的vs插件,可以支持其他版本,只要修改相应的那个注册表就行
flip:To linxv :
貼圖座標有用投影嗎?
文章分类
收藏
    相册
    blogs
    David Weller
    nVidia Developer blog
    Rico Mariani
    Shawn Hargreaves
    XNA Team blog
    XNA资源
    XNA Creators Club
    ZBuffer
    Ziggyware XNA Resources
    中国XNA开发网
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 使用Managed DirectX编写游戏----理解sample framework 之事件处理收藏

    新一篇: 使用Managed DirectX创建三维地形 | 旧一篇: 【翻译】Managed DirectX(第十二章上 )

    作者:clayman
    clayman_joe@yahoo.com.cn
    转载请注明作者,商业使用请联系我^_^

    事件处理机制

          

             Framework类是sample framework中最重要的类,完成了创建窗体,初始化设备,创建命令行,事件处理(render loop)以及调节各种参数的任务。Framework类包含在dxmut.cs文件中。其中,比较特别的就是事件处理模型(或render loop)。

             为了获得高性能的渲染以及事件处理机制,framework类在初始化的方法中使用Device.IsUsingEventHandlers = false;关闭了事件处理模型。我们先来看看为什么默认的事件处理机制会导致性能的损失。默认情况下,Managed DirectX中的类在每创建一个资源时,都会为device订阅一些必然的事件。在最简单的情况下,每个资源(比如纹理或顶点缓冲)将会订阅Disposing事件,以及其他一些诸如DeviceLostDeviceReset的事件。这个步骤在整个对象的生存期都会发生。但是为什么我们在程序中不需要这种行为呢?

             主要原因就是需要对这种行为付出一定代价,有些情况下,代价还会很大。我们举个例子来说明这一点。看看下面这段简单的伪代码:

    SomeResource res = new SomeResource(device);

    device.Render(res);

             这段代码看起来几乎是“无害”的。只是创建了一个资源,并且渲染它。当这个对象不再使用时,垃圾回收器应该会智能的清除它。但是,这个想法是完全错误的。当创建新资源时,至少需要对device订阅一个事件,允许device正确的清除它。这种订阅实际上是一把“双刃剑”。

    首先,订阅事件时需要分配(allocationEventHander对象完成实际的订阅工作。虽然这种分配的代价很小,但是,我们稍候就会看到,就算是很小的分配也会迅速膨胀。第二,订阅事件之后,资源和设备之间就有了一个硬连接(hard link)。因此,在垃圾回收器的眼里,这个对象在device的生存期里仍然处于使用状态,并且没有取消事件的订约。设想一下,这代码如果在渲染每帧的时候都运行一次;再设想一下,你的程序每分钟需要进行上千次渲染,并且程序已经运行了两分钟。结果,你创建了120000个在device生存期间不会被回收的对象,以及120000个事件句柄。所创建的这些对象不但会迅速消耗内存,而且会导致额外的垃圾回收,严重影响程序性能。如果你的资源都位于显存中,那么很快就会耗尽显存。

             这里,我们还没有考虑当最后释放设备时可能发生的情况。在前面的例子中,当释放device时,首先触发Disposing事件,此时,有120000个监听者订约了这个事件。你是否已经考虑到,调用这个巨大的事件句柄列表会将花费很多时间?实际上这将会花去几分钟时间,并且让用户认为程序已经处于死锁状态。

             因此,最好只在最简单的Direct3D程序中使用Managed Direct3D内建的事件处理机制。在任何需要考虑内存容量和性能的应用中(比如游戏),都必须避免这些处理过程。

             接下来,我们来看看framework中是如何实现事件处理模型的。实际上,SDK中的事件处理模型也是几经修改,现在使用的方法最早由Tom Mille 在他的bolg上贴出了具体的实现:

    public void MainLoop()

    {

            // Hook the application's idle event

            System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);

            System.Windows.Forms.Application.Run(myForm);

    }

     

    private void OnApplicationIdle(object sender, EventArgs e)

    {

        while (AppStillIdle)

        {

             // Render a frame during idle time (no messages are waiting)

             UpdateEnvironment();

             Render3DEnvironment();

        }

    }

     

    private bool AppStillIdle

    {

         get

        {

            NativeMethods.Message msg;

            return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);

         }

    }

     

    And the declarations for those two native methods members:

     

    [StructLayout(LayoutKind.Sequential)]

    public struct Message

    {

        public IntPtr hWnd;

        public WindowMessage msg;

        public IntPtr wParam;

        public IntPtr lParam;

        public uint time;

        public System.Drawing.Point p;

    }

     

    [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously

    [DllImport("User32.dll", CharSet=CharSet.Auto)]

    public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

     

             这里,通过平台调用,使用了一些Win32 API。首先,在main方法中订阅了Application.Idle事件。在程序处理完了所有消息(如果不熟悉消息,那么可以把消息理解为系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。)之后,将会触发Application.Idle事件。我们的目标是让程序尽可能快,尽可能多的处理消息,同时不打断wendows消息的输入。

             OnApplicationIdle事件处理程序中,使用的了简单的Win32 API PeekMessage来检查程序是否有任何未处理的消息。这里使用while循环的原因是保证在处理完所有消息,同时消息队列还为空时,只触发一次Application.Idle事件。所以,我没一直循环,直到有新的消息,然后,跳出循环。普通的.Net WinForm窗体消息句柄将会选择未处理的消息。

             接下来,我们将使用框架,来显示一些物体了(源码请参考SDK中的empty project)。由于框架已经为我们完成了以上工作。我们只需要选择订阅那些事件就可以了。Sample framework通过这些事件通知应用程序关于改变设备、用户输入以及各种窗口消息。这些事件是可选的,但是,如果你没有设置,那么框架就不会为你处理相应的事件。在main方法中,创建了GameEngine对象之后,添加代码:

    sampleFramework.Disposing += new EventHandler(blockerEngine.OnDestroyDevice);

    sampleFramework.DeviceLost += new EventHandler(blockerEngine.OnLoseDevice);

    sampleFramework.DeviceCreated += new DeviceEventHandler(blockerEngine.CreateDevice);

    sampleFramework.DeviceReset += new DeviceEventHandler(blockerEngine.OnResetDevice);

    sampleFramework.SetWndProcCallback(new WndProcCallback(blockerEngine.OnMsgProc));

    sampleFramework.SetCallbackInterface(blockerEngine);

    (注意,虽然在SDK October 2005的文档中还可以查到framework对象的SetKeyboardCallback方法,但实际上这个方法已经被删除了,老版本的SDK示例中使用了整个方法。)

             这一段代码作了很多工作,首先,为四个事件订阅了事件处理程序,分别是创建设备,失去设备,重置设备,销毁设备。我们将在后面实现这些事件处理程序。SetWndProcCallback方法订阅了处理windows消息的方法。随后,使用当前game engine实例作为参数,调用SetCallbackInterface方法。之后,编写事件处理程序

    private void OnCreateDevice(object sender, DeviceEventArgs e)

    {

        SurfaceDescription desc = e.BackBufferDescription;

    }

    private void OnResetDevice(object sender, DeviceEventArgs e)

    {

        SurfaceDescription desc = e.BackBufferDescription;

    }

    private void OnLostDevice(object sender, EventArgs e)

    {

    }

    private void OnDestroyDevice(object sender, EventArgs e)

    {

    }

    public IntPtr OnMsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool noFurtherProcessing)

    {

    }

     

             由于之前的SetCallbackInterface需要接收一个IframeworkCallback的变量作为参数,但是我们的game engine类并没有实现这个类,所以添加以下代码:

    public class EmptyProject : IFrameworkCallback, IdeviceCreation

             实现这个接口所定义的方法

    public void OnFrameMove(Device device, double appTime, float elapsedTime)

    {

    }

    public void OnFrameRender(Device device, double appTime, float elapsedTime)

    {

    }

             哦,框架性的东西总算是弄的差不多了。在SetCallbackInterface之后加上以下代码

    try

    {

     

             sampleFramework.SetCursorSettings(true, true);

             sampleFramework.Initialize( false, false, true );

             sampleFramework.CreateWindow("haha");

             sampleFramework.Window.KeyDown += new System.Windows.Forms.KeyEventHandler(blockerEngine.OnKeyEvent);

             sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth, Framework.DefaultSizeHeight, blockerEngine);

             sampleFramework.MainLoop();

     

    }

    #if(DEBUG)

             catch (Exception e)

             {

                       sampleFramework.DisplayErrorMessage(e);

    #else

         catch

         {

              // In release mode fail silently

    #endif

                       // Ignore any exceptions here, they would have been handled by other areas

                       return (sampleFramework.ExitCode == 0) ? 1 : sampleFramework.ExitCode; // Return an error code here

                       }

     

                       return sampleFramework.ExitCode;

             }

    }

             现在运行程序看看,虽然只是一个蓝色的窗口,但是我们背后所搭建的框架已经可以实际应用到一个游戏之中了。为了让程序开起来有一点点交互,我们还订阅了键盘事件,通过空格键可以改变程序的颜色。

     

     源码:http://bbs.gameres.com/upload/sf_2005103102041.rar

    发表于 @ 2005年10月31日 00:20:00|评论(loading...)|编辑

    新一篇: 使用Managed DirectX创建三维地形 | 旧一篇: 【翻译】Managed DirectX(第十二章上 )

    评论:没有评论。

    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © clayman