尽管计算机的处理能力相比过去有了极大的进步。但对于实时渲染的游戏程序,仍旧不能在一瞬间将同屏的所有物件全部加载出来。即使可以,用户的硬件条件参差不齐,若运行在性能较低的机器,用户将看到破碎断续加载的画面。
从更底层形象地看断续加载的原因,就要了解画面是如何绘制的。计算机维护着一个帧缓冲区,游戏想要显示画面,就需要将像素颜色信息填写到帧缓冲区中。而显示设备就从帧缓冲区中读取像素信息并会知道屏幕上。这时候就会出现一个同步问题——当显示设备从帧缓冲区读取到计算机未填充的信息时,就会照成原本想要绘制的画面像素缺失,导致画面撕裂,从而表现为断续加载。
如何解决计算填充像素能力跟不上显示器读取像素的频率的问题,双缓冲给出了一个比较好的解决方案。
两个人赛跑,已知其中一个速度快,一个速度慢。那么如何让那个速度跑的慢的长时间能够保证领先于速度快的人?很简单,就是让速度慢的人提前跑很长一段时间。双缓冲的原理就是,计算机维护两个帧缓冲区,当计算机填充好一个帧缓冲区后,就交给显示设备进行读取,再显示设备读取此帧缓冲区的过程中,计算机已经开始填充另一个缓冲区。这样的错开使得每一次显示设备都能获取到数据完整的帧缓冲区,也不会出现断续加载、画面撕裂的情况。
双缓冲模式抽象出来,可以总结出它的使用范围:
//帧缓冲区 class FrameBuffer { public: static const int screen_width=1920; static const int screen_heigh=1080; void SetPixel(int x,int y,Color c) { if(x<screen_width&&y<screen_heigh) pixels[x][y]=c; } Color GetPixel(int x,int y) { if(x<screen_width&&y<screen_heigh) return pixels[x][y]; else return Color.Default; } void Clear() { for(int i=0;i<screen_width;i++) for(int j=0;j<screen_heigh;j++) pixels[i][j]=Color.White; } Color* GetPixels() { return pixels; } private: Color pixels[screen_width][screen_heigh]; } //渲染类 class Render { public: void Draw() { FrameBuffer buffer=frames[drawCount%(FRAME_COUNT-1)]; //填充数据 buffer.SetPixel(0,0,Color.red); //... Swap(); } void Swap() { currentFrame=drawCount%(FRAME_COUNT-1); } FrameBuffer GetDisplayBuffer() { return frames[currentFrame]; } private: static const int FRAME_COUNT=2; FrameBuffer frames[FRAME_COUNT]; int currentFrame=0; int drawCount=0; } //显示设备类 class DisplayAdaptor { public: void Display() { FrameBuffer buffer=GetDisplayBuffer(); //... } }