### 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