2010-08-21 76 views
3

我正在寫一個基於2D的引擎。目前,我的繪圖例程使用C#繪圖庫在每次刷新屏幕時重新繪製每個可見的圖塊。我滾動並縮放工作,一切看起來都是我想要的。但例程很慢。現在我正在努力改進它。我有幾個問題:繪製2D雪碧(位圖)有效地寫入

首先,我認爲在每次刷新時重繪瓦片是不必要的(因爲它們從不改變)。現在我試圖改變算法,以便在初始化時將整個地圖寫入單個位圖,然後在繪製時切割正確的位圖部分。你認爲這是正確的路嗎?我也考慮過把圖片留在背景中,然後滾動它,但後來我決定不想繪製視野之外的東西,但也許這比剪切/粘貼更便宜?內存vs時間問題?)

其次,據我所知,C#繪圖程序不使用GPU的全部功能。我認爲我應該嘗試在OpenGL(或DirectX,但我更喜歡前者,因爲它是多平臺)繪圖。這會有幫助嗎?你知道任何用於OpenGL的平鋪(或一般像素繪圖)教程嗎?圖書參考也可以提供幫助。

我現在也不做多線程(實際上我只是對這是什麼有一個模糊的概念)。我應該嘗試多線程抽屜嗎?或者OpenGL會使圖形冗餘的多線程?

謝謝。

+0

XNA4.0在我問我的問題2周後問世。我試了一下,喜歡它,並轉向它。 XNA隱式管理DirectX,所以人們可以讓自己的精靈在四處浮動,而不用擔心任何事情。非常好。我的一個朋友使用WPF。這也可以正常工作,但我認爲你需要了解一些DirectX(沒有太大的問題)。再次感謝偉大的答案,夥計們。 – JackKane 2011-02-21 18:33:45

回答

2

您打算使用哪種應用程序框架?在WinForms(Win32)和WPF之間進行高效繪圖的技巧是非常不同的。

.NET繪圖例程沒有充分利用GPU是正確的。使用DirectX或OpenGL,一個即時優化將使用圖像列表或顯示列表將所有圖像切片(或者至少所有用於立即視圖區域的圖塊再加上一點點)預加載到GPU內存中。然後,您將通過索引在表面上繪製瓦片 - 在x,y處繪製瓦片N.這通常比使用存儲在主系統內存中的位圖在GPU表面上繪製要快得多,因爲位圖像素必須複製到繪製每個圖塊的GPU並耗盡大量時間。只要您可以在輸出中的多個位置使用相同的圖像切片,按索引繪製也會使用更少的GPU內存。

OpenGL與DirectX是您的選擇,但IMO DirectX一直在以更快的速度發展,提供比OpenGL更多的硬件加速功能。 Windows上的OpenGL驅動程序也因硬件供應商忽視而聞名。他們主要關注的是他們的DirectX驅動程序。

給你一些想法,你是否真的需要OpenGL或DirectX爲你的二維瓷磚應用程序。要求OpenGL或DirectX將減少可以運行你的應用程序的機器數量,尤其是舊機器數量。 OpenGL和DirectX可能會矯枉過正。如果你聰明的話,你可以用普通的舊GDI做很多事情。

遠離多線程,直到你有一個很好的理由去那裏,你有一些線程的經驗。多線程提供了一些的性能提升一些計算情況的獎勵,但也帶來了新的頭痛和新的性能問題。在註冊所有這些令人頭疼的事情之前,確保好處是顯着的。

一般來說,在屏幕上移動像素通常不會很好地匹配多線程。你只有一個顯示設備(在大多數情況下),因此在多個線程嘗試同時更改像素的情況下擊中它並不會很好。從項目管理中借用一個表達方式:如果一個女性可以在9個月內創建一個嬰兒,您可以在1個月內使用9個女性創建1個嬰兒嗎? ;>

確定系統中不需要訪問相同資源或設備的部分。那些更適合在並行線程中運行,而不是在屏幕上運行磁貼。

最好的優化是發現不需要完成的工作 - 例如減少tile重繪次數,或者更改爲索引模型,以便可以從GPU內存而不是系統內存中繪製瓦片。

+0

我正在使用Windows窗體。直到你提到它,我才知道WPF存在。除了使用DirectX之外,我發現它與Forms類似。也許我會嘗試將我的應用程序移植到WPF以查看會發生什麼。 *** 我用GDI將位圖加載到主內存中 - 我會讀取並嘗試將它們加載到GPU中。在你和Blam的回覆之間,我想我會使用DirectX,除非在GDI中可能加載內存。現在我將避免多線程。 *** 謝謝你的反應如此之好,這麼快! – JackKane 2010-08-21 01:57:23

+0

由於WinForms應用程序僅僅是Win32應用程序,因此當窗口區域被遮擋然後再次被發現時,系統會提示他們重新繪製窗口區域。大多數窗口畫圖代碼都採用簡單的方法,只是重繪窗口中的所有內容。爲了獲得更好的性能,尤其是對於大型繪圖負載,您應該注意剪切矩形,只重繪實際上無效的窗口部分。這也可以很好地用於滾動,因爲向上滾動10個像素只會使窗口底部的10個像素無效,而不是整個窗口。 – dthorpe 2010-08-21 17:07:27

+0

WPF使用完全不同的繪圖範例,並需要非常不同的優化技術。 WPF會緩存繪製的圖像,以便在發現問題時迅速進行重新繪製,而無需調用您的代碼。在WPF應用程序中沒有WM_PAINT循環(您參與其中)。 WPF可能會在底層使用DirectX進行繪製,因此與WinForms相比,您可能更有可能使用普通的WPF控件和對象來利用GPU資源。 – dthorpe 2010-08-21 17:09:53

2

如果你想使用OpenGL,你最好的選擇2d將是SDL。使用OpenGL與C#將永遠不會是可移植的,因爲它會使用.NET包裝器。

XNA是用C#編寫遊戲的好工具,它應該提供更多的速度和靈活性,然後SDL(特別是.net端口)加上更多的功能(但更多的功能)。

對於你的裁剪或滾動問題,最好的路線是滾動。 當您使用GDI +(System.Drawing使用什麼)繪圖時,內存與CPU相比要少得多。您可以隨時將地圖分成多個部分,然後在需要時將其滾動,然後在需要時加載。

+0

你是對的!我設法讓裁剪算法起作用,並且比前一個更慢。然後我得到了一個滾動的工作(這比我想象的要容易),並且它看起來更快(儘管它縮小了縮放 - 現在我重新繪製縮放的大位圖,導致動作暫停一秒鐘)。 感謝您的SDL鏈接,我會研究它。 – JackKane 2010-08-21 01:36:13

+0

我聽說過XNA。我幾天前試圖安裝它,安裝失敗,我忘了它。是不是真的值得嗎?看起來像另一個'gamemaker'類型的程序給我。 – JackKane 2010-08-21 01:59:49

+0

Nononono,XNA *更多的是一種用戶友好的框架類型,但並不意味着它和gamemaker一樣。 查看一些教程,以便了解它是如何工作的,然後自己決定。正如我所說,它應該比SDL更快,並提供更多功能。加XNA是爲C#製作的:) – Blam 2010-08-21 11:24:53

2

我對OpenGL並不熟悉,但我在ManagedDX(稍後移植到XNA)中編寫了基於圖塊的引擎。 ManagedDX被扣除,但仍有積極發展的SlimDX項目。

使用DX,您可以將每個單獨的圖塊加載到Texture。 (例如使用Texture.FromFile()Texture.FromStream()),並且有一個單獨的Sprite實例繪製它們。這表現相當不錯。我將這些紋理用一個簡單的類進行分組,其位置爲Point或Vector2,只在位置發生變化時才設置它們,而不是每次調用繪製方法。我將內存中的圖塊僅存儲在內存中,僅用於立即屏幕和超出一個或兩個圖塊,因爲文件IO足夠快以便在滾動時獲取新圖塊,所以沒有必要超過此限制。

struct Tile 
{ 
    public Point Location; 
    public Texture Texture; 
} 

Device device; 
Sprite sprite; 
List<Tile> tiles = new List<Tile>(); 

.ctor() { 
    this.device = new Device(...) 
    this.sprite = new Sprite(this.device); 
} 

void Draw() { 
    this.device.Clear(ClearFlags.Target, Color.CornflowerBlue, 1.0f, 0); 
    this.device.BeginScene(); 
    this.sprite.Begin(SpriteFlags.AlphaBlend); 
    foreach (Tile tile in this.tiles) { 
     this.sprite.Draw2D(tile.Texture, 
      new Point(0, 0), 0.0f, tile.Location, Color.White); 
    } 
    this.sprite.End(); 
    this.device.EndScene(); 
    this.device.Present(); 
}