7_DoubleBuffer 游戏编程中的双缓存模式

### double buffer 双缓存

简单说: 当一个缓存被读取的时候,往另一个缓存里写入, 如此交替

#### the pattern
有两个缓存实例,一个是 current buffer, 一个是next buffer

从current buffer读取信息, 往next buffer里写入信息.
swap操作,进行两种buff的身份切换

#### 何时使用
1 需要增量修改的状态
2 在修改的过程中需要进行访问
3 当访问数据的时候,不会发现数据正在被写入
4 读操作不用等待写的操作完成

#### 注意事项

1 swap操作比较耗时
2 必须有两个buffer, 内存消耗变大

#### Sample Code

####一个帧缓存的例子

```
class Framebuffer
{
public:
  Framebuffer() { clear(); }

  void clear()
  {
    for (int i = 0; i < WIDTH * HEIGHT; i++)
    {
      pixels_[i] = WHITE;
    }
  }

  void draw(int x, int y)
  {
    pixels_[(WIDTH * y) + x] = BLACK;
  }

  const char* getPixels()
  {
    return pixels_;
  }

private:
  static const int WIDTH = 160;
  static const int HEIGHT = 120;

  char pixels_[WIDTH * HEIGHT];
};

// 问题版本
class Scene
{
public:

  // 每一帧执行

  void draw()
  {
    buffer_.clear();

    buffer_.draw(1, 1);
    buffer_.draw(4, 1);
    // 问题! video driver 可以在任何时候读取pixels, 可能会读到非法值
    buffer_.draw(1, 3);
    buffer_.draw(2, 4);
    buffer_.draw(3, 4);
    buffer_.draw(4, 3);
  }     

  Framebuffer& getBuffer() { return buffer_; }

private:
  Framebuffer buffer_;

   void swap()
  {
    // Just switch the pointers.
    Framebuffer* temp = current_;   

    current_ = next_;
    next_ = temp;
  }

};

// 使用double buffer
class Scene
{
public:
  Scene()
  : current_(&buffers_[0]),
    next_(&buffers_[1])
  {}

  // video driver 只会从 current里获取pixel
  void draw()
  {
    next_->clear();

    next_->draw(1, 1);
    // ...
    next_->draw(4, 3);

    swap();
  }

  Framebuffer& getBuffer() { return *current_; }

private:
  void swap()
  {
    // Just switch the pointers.
    Framebuffer* temp = current_;
    current_ = next_;
    next_ = temp;
  }

  Framebuffer  buffers_[2];
  Framebuffer* current_;
  Framebuffer* next_;
};

```

##### Artificial unintelligence

```
class Actor
{
public:
  Actor() : slapped_(false) {}

  virtual ~Actor() {}
  virtual void update() = 0;

  void reset()      { slapped_ = false; }
  void slap()       { slapped_ = true; }
  bool wasSlapped() { return slapped_; }

private:
  bool slapped_;
};

// 每一帧都会调用actor的update, ,所有的actor需要同时进行更新
// actor之间可以交互, 例如击打

class Stage
{
public:
  void add(Actor* actor, int index)
  {
    actors_[index] = actor;
  }

  void update()
  {
    for (int i = 0; i < NUM_ACTORS; i++)
    {
      actors_[i]->update();
      actors_[i]->reset();
    }
  }

private:
  static const int NUM_ACTORS = 3;

  Actor* actors_[NUM_ACTORS];
};

// 同一时间, 只会有一个actor执行update

class Comedian : public Actor
{
public:
  void face(Actor* actor) { facing_ = actor; }

  virtual void update()
  {
    if (wasSlapped()) facing_->slap();
  }

private:
  Actor* facing_;
};

// 面对某人
 harry ------> balay ------> chump
 ^                              |
 |
 -------------------------------v

Stage stage;

Comedian* harry = new Comedian();
Comedian* baldy = new Comedian();
Comedian* chump = new Comedian();

harry->face(baldy);
baldy->face(chump);
chump->face(harry);

stage.add(harry, 0);
stage.add(baldy, 1);
stage.add(chump, 2);

harry->slap();

stage.update();

// slap
 harry ---slap---> balay --slap----> chump
 ^                                   |
 |
 ----------------slap---------------v
 正确的结果

 #如果把三人的执行顺序换一下, 结果将不正确#
stage.add(harry, 2);
stage.add(baldy, 1);
stage.add(chump, 0);

只有harry slap 了baldy

buffered slaps

// 把slap用buffer记录下来

class Actor
{
public:
  Actor() : currentSlapped_(false) {}

  virtual ~Actor() {}
  virtual void update() = 0;

  void swap()
  {
    // Swap the buffer.
    currentSlapped_ = nextSlapped_;

    // Clear the new "next" buffer.
    nextSlapped_ = false;
  }

  void slap()       { nextSlapped_ = true; }
  bool wasSlapped() { return currentSlapped_; }

private:
  bool currentSlapped_;
  bool nextSlapped_;
};

void Stage::update()
{
  for (int i = 0; i < NUM_ACTORS; i++)
  {
    actors_[i]->update();
  }

  for (int i = 0; i < NUM_ACTORS; i++)
  {
    actors_[i]->swap();
  }
}

```

#### buffer swap
swap需要锁住两个buffer, 所需需要尽量的轻量快速
1 交换指针 引用
    1.快速
    2.外部代码不能保存buffer指针
    3 当前的数据,是两帧之前的数据(读current的同时,数据写入了next)

2 buffer之间拷贝数据
    如果无法进行swap,可以把next的数据copy到current
    如果数量小则没什么问题,如果大则会耗时

```
当很多对象都有需要swap操作时, 会很慢
下面这个例子,不用swap,而是slap的时候修改了next的值

class Actor
{
public:
  static void init() { current_ = 0; }
  static void swap() { current_ = next(); }

  void slap()        { slapped_[next()] = true; }
  bool wasSlapped()  { return slapped_[current_]; }

private:
  static int current_;
  static int next()  { return 1 - current_; }

  bool slapped_[2];
};
```

#### See also
double buffer 模式在图形编程上应用广泛

You can find the Double Buffer pattern in use in almost every graphics API out there. For example, OpenGL has swapBuffers(), Direct3D has “swap chains”, and Microsoft’s XNA framework swaps the framebuffers within its endDraw() method.
时间: 2024-10-11 11:53:10

7_DoubleBuffer 游戏编程中的双缓存模式的相关文章

【转】游戏编程中的人工智能技术--神经网络

原文:http://blog.csdn.net/ecitnet/article/details/1799444 游戏编程中的人工智能技术. > .  (连载之一) 用平常语言介绍神经网络(Neural Networks in Plain English) 因为我们没有很好了解大脑,我们经常试图用最新的技术作为一种模型来解释它.在我童年的时候,我们都坚信大脑是一部电话交换机.(否 则它还能是什么呢?)我当时还看到英国著名神经学家谢林顿把大脑的工作挺有趣地比作一部电报机.更早些时候,弗罗伊德经常把大

向量几何在游戏编程中的使用2

<2>2-D物体任意角度的反弹 一.求与某个向量a正交的向量b 根据向量内积的性质以及正交向量之间的关系,有: 设a=(xa,ya),b=(xb,yb) a.b = 0=> xa*xb + ya*yb = 0=> xa*xb = -ya*yb=> xa/-ya = yb/xb=> xb = -ya , yb = xa 或 xb = ya , yb = -xa 则向量(xa,ya)的正交向量为(xb,yb)=(-ya,xa)比如上图中,向量(2,3)的逆时针旋转90度的正

游戏编程系列[1]--游戏编程中RPC协议的使用

RPC(Remote Procedure Call Protocol)--远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据.在OSI网络通信模型中,RPC跨越了传输层和应用层.RPC使得开发包括网络分布式多程序在内的应用程序更加容易.RPC采用客户机/服务器模式.请求程序就是一个客户机,而服务提供程序就是一个服务器. 首先,客户机调用进程发送一个有进程参数的调用信息到服务进

向量几何在游戏编程中的使用3

<3>2-D边界碰撞检测 一.使用向量进行障碍检测的原理 在游戏中进行障碍碰撞检测,基本思路是这样的:给定一个障碍范围,判断物体在这次移动后会不会进入这个范围,如果会,就发生碰撞,否则不发生碰撞.在实际操作中,是用物体的边界来判断还是其他部位判断完全取决于编程者.这时候,就可以从这个部位沿着速度的方向引出一条速度向量线,判断一下这条线段(从检测部位到速度向量终点)和障碍边界线有没有交点,如果有,这个交点就是碰撞点. 上面物体A,在通过速度向量移动之后将到达B位置.但是,这次移动将不会顺利进行,

向量几何在游戏编程中的使用4

<4>2-D物体间的碰撞响应 这次我要分析两个球体之间的碰撞响应,这样我们就可以结合以前的知识来编写一款最基本的2-D台球游戏了,虽然粗糙了点,但却是个很好的开始,对吗? 一.初步分析 中学时候上物理课能够认真听讲的人(我?哦,不包括我)应该很熟悉的记得:当两个球体在一个理想环境下相撞之后,它们的总动量保持不变,它们的总机械能也守恒.但这个理想环境是什么样的呢?理想环境会不会影响游戏的真实性?对于前者我们做出在碰撞过程中理想环境的假设: 1)首先我们要排除两个碰撞球相互作用之外的力,也就是假设

wxWidgets第十二课 wxBufferedPaintDC OnPaint函数中的双缓存DC

说明 当使用DC进行图形渲染的过程中,会出现闪烁的问题,主要是图形是实时渲染的,如果先渲染在一块内存中,然后渲染完毕之后,才一次性贴图到屏幕上,就可以避免闪烁的问题.因此引入wxBufferedPaintDC.wxBufferedPaintDC继承自wxBufferedDC,wxPaintDC是其私有成员变量,因此该类只能够在wxPaintEvent事件中使用,也就是只能在OnPaint( wxPaintEvent& event )函数中使用. 为了使用该类,需要包含头文件#include &qu

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记6——Direct3D中的顶点缓存和索引缓存

第12章 Direct3D绘制基础 1. 顶点缓存 计算机所描绘的3D图形是通过多边形网格来构成的,网网格勾勒出轮廓,然后在网格轮廓的表面上贴上相应的图片,这样就构成了一个3D模型.三角形网格是构建物体模型的基本单元,而一个三角形有3个顶点,为了能够使用大量三角形组成三角形网格来描述物体,需要首先定义号三角形的顶点(Vertex),3个顶点确定一个三角形,顶点除了定义每个顶点的坐标位置外,还还含有颜色等其他属性. 在Direct3D中,顶点的具体表现形式是顶点缓存,顶点缓存保存了顶点数据的内存空

【游戏设计模式】之四 《游戏编程模式》读书笔记:全书内容梗概总结

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/53240330 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文的Github版本:QianMo/Reading-Notes/<游戏编程模式>读书笔记 这是一篇超过万字读书笔记,总结了<游戏编程模式>一书中所有章节与内容的知识梗概. 我们知道,游戏行业其实一直很缺一本系

《游戏编程模式》记录

写在前面 这本书长这样 我还没有看过“GOF”,我所读到的设计模式都是这本书(游戏角度)给出的定义,害怕GOF中的定义过于抽象. 没有在项目代码晃来晃去经历的,或者没有工作至少半年的,不用着急买这本书,因为估计看不懂. 本文用来重点记录“我觉得XX设计模式是什么”,以及“当我在看XX设计模式时,我在想什么” 这本书的写作方式,属于我喜欢的“谈话口吻” 命令模式 书面定义:“将一个请求封装成一个对象,从而允许你使用不同的请求.队列或日志将客户端参数化,同时支持请求操作的撤销和恢复” 他的举例:按键