• 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.
  • 相关阅读:
    Linux如何设置时区/时间/上海时间
    Anaconda Python3.7环境 import _ssl DLL load failed(ImportError:DLL load failed:找不到指定模块)
    一行命令搞定/usr/bin/perl^M: bad interpreter
    Embed MP4 in HTML using flash-player(html5 video player)
    mp4文件转码为m3u8
    Python 下载图片的三种方法
    图解MySQL 内连接、外连接、左连接、右连接、全连接……太多了
    DOS批处理中%~dp0表示什么意思
    Android学习探索之App多渠道打包及动态添加修改资源属性
    Android学习探索之运用MVP设计模式实现项目解耦
  • 原文地址:https://www.cnblogs.com/lightlfyan/p/4230736.html
Copyright © 2020-2023  润新知