我們先看乙個典型的例子,每個遊戲引擎都要處理的問題——渲染。當引擎渲染出使用者看到的世界時,在同一時間它只渲染一塊:遠處山峰、欺負的丘陵、樹木,這些輪流渲染;假如使用者也逐步的觀察視窗的渲染過程,那麼看到的將是破碎的世界。這是我們不能接受的,場景比如平滑快速的更新,每一幀必須被完整的顯示。如何解決這個問題?這就要用到本節講述的雙緩衝模式。
首先我們回顧一下計算機是如何渲染圖形的:
計算機顯示圖形的裝置就是我們常見的顯示器,顯示器從最早的crt發展到現在的液晶顯示器,硬體技術不斷的發展,但顯示的原理一直沒變。顯示器的螢幕其實由很多的小單元組成,乙個單元我們可以稱之為乙個畫素,根據螢幕的尺寸和解析度,可以簡單的計算出每個畫素的物理尺寸。假設我們現在要顯示一幅影象,很簡單的把影象覆蓋的點顯示成對應的顏色即可。但這個過程並不是乙個瞬間完成的,而是乙個逐行掃瞄的過程,即從左到右,從上到下乙個畫素乙個畫素的著色。當掃瞄到螢幕右下角的時候,將重新定位到左上角重複這個過程。這個過程非常的快,在現代的顯示器上可以達到60甚至是120次每秒,我們的肉眼是分辨不出這個過程的,所以我們所見的就是一張一張的靜態。
但我們又是如何知道哪些畫素需要繪製成什麼顏色了?答案就是快取區,也就是計算機中儲存這乙個陣列(它是ram中的乙個塊,其中兩個位元組表示乙個畫素顏色),這個陣列與螢幕畫素對應(當然緩衝區大小不一定與螢幕解析度一致,如何對映有相應的策略和演算法),所以通過遍歷緩衝區給對應的畫素著色即可。但這裡就可能存在乙個問題,就是計算和渲染是同時進行的,即我們一邊在更新緩衝區,一邊再讀取緩衝區,如果讀取和更新的速度不一致,那麼不管更新比讀取快還是慢,都會導致當前螢幕顯示的內容**於兩幀影象,造成一種撕裂的效果。那麼對這個問題的處理就很簡單了,等讀取完成之後再更新緩衝區就行了。但很明顯,這種做法會極大的降低幀率,這就誕生了雙緩衝的做法。
雙緩衝,其實就是兩個緩衝區,乙個用於讀,乙個用於寫(也就是更新),當更新完成後,把兩個緩衝區交換(即更新好的緩衝用於讀取,之前用於讀取的緩衝用於儲存新的更新資料);這就避免的上述情況的出現。這裡有個很有趣的問題,就是何時交換緩衝區了?很明顯的乙個做法就是掃瞄重新定位到左上角的過程中。這裡我們不過多的講解顯示器的問題,回到雙緩衝模式。
從雙緩衝模式的定義上,我們就可以推斷出它的使用場景:
我們需要維護一些被逐步改變著的變數;
同個狀態可能在其被修改的同時被訪問到;
我們希望避免訪問狀態的**能看到具體的工作過程;
我們希望避免能夠讀取狀態但不希望等待寫入操作完成;
需要注意的是:
1.雙緩衝模式需要在狀態寫入完成後進行一次交換操作,操作必須是原子性的,也就是說任何**都無法在這個交換期間對緩衝區內的任何狀態進行訪問。通常這個交換和分配乙個指標的速度差不多,但如果交換用去了比修改初始狀態更長的時間,那這個模式就毫無益處了;
2.這個模式使用了兩個緩衝區,所以乙個直接的後果就是增加了記憶體的占用,所以如果你的記憶體受限,你就只能想其它的辦法了。
我們先看看沒有使用雙緩衝的**
classframebuffer
~framebuffer() {}
void
clear()
}void draw(int x, int
y)
const
char*getpixels()
private
:
static
const
int kwidth = 160
;
static
const
int kheight = 120
;
char pixels_[kwidth *kheight];
};class
scene
framebuffer&getbuffer()
private
: framebuffer buffer_;
};
很明顯,scene向外提供了獲取buffer的介面,外部**就很容易在scene繪製的同時修改buffer,這個時候buffer中的資料就不是我們預期的資料了。接下來我們使用雙緩衝修正它:
classscene
framebuffer&getbuffer()
void
swap()
private
: framebuffer buffers_[2];
framebuffer *current_;
framebuffer *next_;
};
1.交換緩衝區指標或引用
這個是處理圖形緩衝區的最通用的解決方案。優點很明顯:速度快,交換一對指標,其速度和簡單性基本上不會被超越了;但約束就是外部**無法儲存乙個持久化的指標指向緩衝區,這對於那些顯示卡希望緩衝區在記憶體衝固定位址的系統來說尤其會造成麻煩。
2.在兩個緩衝區之間進行資料拷貝
這個與交換指標或引用相比,速度就要慢很多。但優點就是後台緩衝區的資料與當前資料就差一幀的時間,如果我們要訪問上一幀的資料,這將帶來極大的方便。
也就是緩衝區如何組織?通常你所快取的內容將會告訴你答案,當然我們也可以調整它們,比如每乙個物件都有乙個狀態,把這個狀態存在物件中是一種做法,也可以把所有物件的狀態一起存在乙個陣列中,物件中只儲存索引即可。
雙緩衝模式
定義緩衝類封裝緩衝 一段可改變的狀態。緩衝被增量的修改,外部 將修改視為單一的原子操作。類儲存下乙個緩衝和當前緩衝。從當前緩衝區讀取資料,將資料寫入下乙個緩衝區,當改變完成後,將當前緩衝區和下乙個緩衝區交換。使用場景 1.需要維護一些增量修改的狀態 2.修改到一半時,狀態可能會被外部請求 3.防止請...
儲存系統雙緩衝設計模式
在儲存系統寫資料的過程中,出於效能上的考慮,新寫的資料並不是每次都flush到目標儲存中的,而是先放入到乙個buffer空間裡,等到buffer空間滿了,再做一次flush出去的動作。這種情況和人們等車的例子極為類似,一輛車等人都上滿了再開,才能保證更高的效率。但是這種緩衝設計模式還是存有乙個主要弊...
遊戲程式設計模式 命令模式
最近深感 設計對於軟體開發過程中的重要性,所以重新拾起了設計模式,以前學的比較鬆散,理解不夠,這一次本著learning,try,teaching的精神,重新認識和學習設計模式。這一次參考robert nystrom 著的 遊戲程式設計模式 一書,與原先的gof所著的24種設計模式不同,但思想是相通...