zoukankan      html  css  js  c++  java
  • DirectX怪象之二, 程序很吃CPU

    学习DirectX编程的兄弟们可能经常遇到的一个情况是,程序经常莫名奇妙的占用大量的CPU资源,其实吃CPU的问题并不是DirectX程序所特有的,几乎任何程序都可能,只不过DirectX程序更加容易产生而已,总结了一下,主要有以下几个方面

    没有及时释放资源

    这种情况的现象多发生在程序运行的时候,也就是窗口处于active状态时,大家都知道,DX是基于COM的,这也就意味着,你需要手动释放COM对象,如果没有及时释放的话,CPU就会吃紧,尤其是当在Render函数中创建对象的时候更是如此。至于哪些需要释放,哪些不需要释放,需要视具体情况而定,这里有一个简单的方法,如果创建该对象的函数形如CreateXXX,那么基本都需要释放,这种对象一般都有一个Release方法,直接调用这个方法即可。给大家分享一个宏定义,来自于DX SDK,这个宏可以很方便的释放COM对象

    #define SAFE_RELEASE(P) if(P){ P->Release(); P = NULL;}

    我们在编程时应该遵循一些好的编程习惯,这样就会避免错误发生,比如

    1 对于那些只需要一次创建的对象,通通放到Render函数之外,所以我们通常设置一个函数名叫InitD3D,可以把一次性初始化的代码都放到这里。切记不可把非渲染操作放到Render函数里面,因为Render函数就是用来做渲染的,而且调用频率极高,几乎是时时刻刻都在调用,如果把其他操作资源的过程也放到这里,势必会影响效率。

    2 在开发的时候尽量使用Debug版本的库,这样可以及时发现资源泄露。

    没有处理窗口inactive的情况

    这种情况多发生在窗口处于inactive状态时,比如被遮挡或者最小化到任务栏。先看一段Windows下的渲染框架,下面的代码逻辑很简单,有消息则处理消息,无消息则渲染,如果你使用了这段代码,那么你可以试着将窗口最小化,你将会看到CPU使用率上升一大截,为什么呢?这是由于PeekMessage的机制造成的,说到PeekMessage就不得不说GetMessage,这二者都是用于从消息队列中取得消息,不同的是,PeekMessage如果没取到消息,会立即返回0,而GetMessage如果没取到消息,则会一直等待有消息可取。也就是说PeekMessage是异步的,而GetMessage是同步的。下面的代码就是利用没有消息处理的时间进行渲染,而当窗口最小化时,PeekMessage一直取不到消息,所以程序一直在渲染,这样就占用了大量的CPU时间。

    MSG msg ;
    ZeroMemory( &msg, sizeof(msg) );
    PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
    
    // Get latest time
    static DWORD lastTime = timeGetTime();
    
    while (msg.message != WM_QUIT)
    {
        if( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) !=0)
        {
            TranslateMessage (&msg) ;
            DispatchMessage (&msg) ;
        }
        else// Render the scene if there is no message to handle
        {
            // Get current time
            DWORD currTime = timeGetTime();
    
            // Calculate time elapsed
            float timeDelta = (currTime - lastTime) *0.001f;
    
            // Render
            Render(timeDelta) ;
    
            // Update lastTime
            lastTime = currTime;
        }
    }

    有了上面的分析,我们可以分两种情况来处理,一是程序处于active状态时,我们按照上面的方法渲染。二是程序处于inactive状态时,我们可以使用GetMessage来等待一个消息,因为此时程序不需要渲染(已经最小化了,还渲染啥呀?)。对应的代码如下

    BOOL bGotMsg;
    MSG msg;
    PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
    
    while( WM_QUIT != msg.message )
    {
        // Use PeekMessage() if the app is active, so we can use idle time to
        // render the scene. Else, use GetMessage() to avoid eating CPU time.
        if( m_bActive )
            bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );
        else
            bGotMsg = GetMessage( &msg, NULL, 0U, 0U );
    
        if( bGotMsg )
        {
            // Translate and dispatch the message
            if( 0== TranslateAccelerator( m_hWnd, hAccel, &msg ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
        }
        else
        {
            // Render a frame during idle time (no messages are waiting)
            if( m_bActive )
            {
                if( FAILED( Render3DEnvironment() ) )
                    SendMessage( m_hWnd, WM_CLOSE, 0, 0 );
            }
        }
    }

    下面的问题就是如何处理m_bActive这个变量了,可以响应WM_SIZE消息,SIZE_MAXHIDE表示有其他窗口最大化了,这样就意味着本窗口被完全遮挡了,SIZE_MINIMIZED表示窗口最小化了。这两种情况我们将窗口视为inactive状态。

    case WM_SIZE:
    // Check to see if we are losing our window...
    if( SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam )
         m_bActive
    = FALSE;
    else
         m_bActive
    = TRUE;
    break;

    没有做场景管理

    由于我对场景管理不是很熟练,所以只能简单说说,场景管理应该属于中高层次的技术,在刚学DirectX的时候,根本不会考虑这些,但是随着知识的深入,程序代码的增大,逻辑越来越复杂,模型越来越多,难免会涉及到场景管理,什么是场景管理呢,如果从渲染的角度来讲,就是区分哪些应该渲染,哪些不用渲染,就是说要提高渲染的速度,场景管理中最基本的就是Frustum剪裁,四叉树,BSP场景管理,八叉树等。

    Frustum,中文翻译成视锥,视锥剪裁是最基本的场景管理,也就是只渲染在视野内的模型,其他的一律剪裁掉。

    四叉树多用于地形渲染

    BSP多用于室内场景管理,著名的Quake系列使用的就是BSP树

    八叉树比较通用

    如果你的场景中有大量的模型,而又没有做场景管理,也就意味着,一次性将所有的模型都渲染一遍,不管模型是否在视野里,这势必很浪费CPU资源。

    当然,肯定还有许多其他的原因,欢迎大家补充!

     

    作者:zdd
    出处:http://www.cnblogs.com/graphics/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    iOS开发UI篇—在UITableview的应用中使用动态单元格来完成app应用程序管理界面的搭建
    python读文件和写文件
    asp.net中FileUpload得到上传文件的完整路径
    Derek解读Bytom源码-P2P网络 upnp端口映射
    Derek解读Bytom源码-启动与停止
    Derek解读Bytom源码-P2P网络 地址簿
    比原链社区项目一览(持续更新)
    Derek解读Bytom源码-protobuf生成比原核心代码
    Derek解读Bytom源码-Api Server接口服务
    Derek解读Bytom源码-孤块管理
  • 原文地址:https://www.cnblogs.com/graphics/p/2039569.html
Copyright © 2011-2022 走看看