Bug经典回放(一)

来源:互联网 发布:java for循环实现阶乘 编辑:程序博客网 时间:2024/04/29 18:01
写程序有些年头了,Bug是从来都没有荒废过, 唯一不同的是刚开始写程序的时候,bug都比较简单.而伴随着年龄的增大,岁月的磋跎.Bug也越来越狡猾了. 当然bug分很多种,有程序设计上的bug,算法设计上的bug,还有架构设计上的bug.后两种bug不是一时半会讲的清楚的. 这里就讲讲程序设计上因为"不慎"而碰到的bt事情吧.

一): 本月经典bug.
       做游戏做的累了,就跑出来做应用软件了. 在这里的任务就是和同事完成一个GUI,这个GUI基本就是一个for Game的GUI ,所以我用了OpenGL.  大家都知道绘制GUI有个根本的函数就是drawRectangle, 这个函数正常心里的都会定义个drawRectangle(Image, x , y , w , h )或者类似的版本,.而正常的调用者都会使用w,h > 0的参数来调用. 所以正常的运行情况下,w,h为0的时候显然是出现问题了(废话).而且这是一个视频播放器软件.我需要一个可以lockable的texture,交给视频的decoder,让它来给我创建纹理大小,并往里填充数据.现在问题出现了:
    先来看看drawRectangle函数 ,它的逻辑是这样的.
    drawRect(ITexture* pImage, int x , int y ,int w , int h)
   {
            pImage->bind(0);  // texture , so we must call such a function(glBindTexture eg.)
           glBegin();
                ....................
           glEnd();
   }
  非常正常的一段函数.
   接着我们来看看LockAble的texture是如何工作的.
   CLockableTexture::onCreate(int pixelFormat , int w , int h)
   {
           onDestory();//先删除前面的数据
           m_Pixels = new char [w * h * 4];
           m_GLTexID = 0;// 纹理ID为0
           m_W = w ;
           m_H  = h;         
   }
   这个函数在decoder启动的时候,才会调用. 因为只有这个时候才会知道需要多大的纹理.
  
bool CLockableTexture::getTextureDim(int& w , int& h)
   {     
           w = m_W; ....
           if(glIsTexture(m_GLTexID) == false) return false;
           .....
   }

  bool
CLockableTexture::bind(int texStage)
  {
              //纹理数据存在了.但是OpenGL的texture ID不存在
              if(
glIsTexture(m_GLTexID) == false  && m_Pixels !=NULL)
                    __createGLTexture();
              ................
  }
     大家看最后那个函数,似乎有点蹊跷. 为什么不在onCreate的时候就把textureID创建好呢?原因是这样的. openGL是一个线程相关的东西.如果函数不在create OpenGL Context的同一个thread里调用. 会出现不可预测的错误的(其实无非也是没效果而已). 而onCreate函数是由视频解码器调用.视频解码器通常十一个独立的线程.所以这个函数的目的就是在bind的时候,判断一下是不是刚才有人调用过onCreate.因为调用过的话,就会把TexID清除掉.而bind函数肯定是在创建OpenGL的那个线程里调用的.
     现在这三个函数是非常正常的: 应用程序创建一LockableTexture.并把指针给decoder.decoder开始播放后,就onCreate这个texture . ...   程序不停的调用这个texture的bind.当它知道onCreate函数调用过后就__createGLTexture(); 并把数据upload到显卡中.
    前天晚上, 天才的我认为性能是一点点的省出来的. bug是一行行的if堵上的.于是我就在drawRect上下毒手.,把它改成这样了:
  
drawRect(ITexture* pImage, int x , int y ,int w , int h)
   {
            if(w == 0 || h == 0)
            {
                   log<<"warning .... w/h==0"<<endl;
                   return ;
            }
            pImage->bind(0); 
            glBegin();
                ....................
            glEnd();
   }

 前面说过w = 0 或者 h = 0 的时候,是不正常的. 这个时候,就应该警告和return . 非OpenGL渲染的时候,有时候是会挂的. 似乎这一切也天经地义.而且改完后我在debian下编译后也能正常运行. 非常的正常.但是把这个程序放到笔记本上的时候,经常放视频的时候,是黑的. log出来的drawRect的wh经常是w=1024 h = 0 . 非常的神奇.
   经过逐步排查.我最终还是把目光定位到了这个曾经被我认为是绝对不会产生问题的改动上: 我在bind函数里下了log. 竟然从来没有输出过. 而下在getTextureDim的函数里的log永远都log出警告: 我的纹理没有创建. ...... 这个纹理竟然一直没有纹理对象!!!!!!
    我的老天!!!
    郁闷了我好久. 比较了一下两台机器. 我的机器是Debian/Testing 台式机.笔记本
是Fedora Core 5.Intel T2300CPU. 貌似比台式机要先进. 但是它的CDROM的DMA没有开. 我的DMA是打开的. 所以当我调用decoder的play的时候,decoder很快就创建了我的lockable的纹理.我的应用程序第一次调用drawRect来绘制这个视频的时候,就知道了它的正确大小.所以传递到drawRect里的w h都是正常的.. 于是.bind函数就开始创建OpenGL texture ID .  而当DMA没有打开的时候, lockAble的纹理在第一次drawRect的时候还是个无效的纹理.所以得到的w , h  都是0........
    天啊, 这个纹理成了没人要的孩子了.......

    问题到这里就比较的明显了. w == 0 h == 0的这个判断,永久的阻止了__createGLTexture的调用. 这个显然是会黑屏的.....
  
反思:
   一:  多线程+OpenGL是可恶的
   二:  并非一台机器能正常运行你的程序就意味着你的程序是正常的.
   三:  看似正常的代码 往往不一定是正常的!!!
   四: 一定要注意分析代码的流程,下断点或者log

to be continue

2006-6-26
xheartblue

  
原创粉丝点击