2013-03-22 107 views
1

在我的項目中,我顯示了很多球體。在DirectX中渲染時的性能問題

The device

要顯示球體予加載一個文件的一些值。 因此它可能是1600個球體。現在,我得到一個性能問題,而渲染... :(

在這一部分,我初始化我的設備對象:

 try 
     { 
      meshList = new List<Sphere>(); 

      // Erstellt die PresentParameters für weitere Einstellungen des Device 
      PresentParameters presParams = new PresentParameters() 
      { 
       Windowed = true,       // Device nur innerhalbe des Fensterhandels benutzen 
       SwapEffect = SwapEffect.Discard,   // Grafikkarte entscheidet selbst wie sie den Backbuffer zur anzeige bringt 
       EnableAutoDepthStencil = true,    // Boolean zum Merken der Tiefe 
       AutoDepthStencilFormat = DepthFormat.D16 // Format der Tiefe 
      }; 

      // Erzeugt eine Instanz von dem Device 
      device = new Device(0,          // Nummer fuer den Grafikadapter der verwendet wird     
           DeviceType.Hardware,     // Parameter über die Garfikkarte oder CPU ausführen 
           panel1,     // Fensterhadel für das Device 
           CreateFlags.HardwareVertexProcessing, // Einstellung des Device. Gibt an, dass die Vertices nur per Software verarbeitet werden 
           presParams);       // Gibt die weiteren Einstellungen mit 

      // Wenn das Device neupositioniert wird 
      device.DeviceReset += new System.EventHandler(this.OnResetDevice); 
      // Führt das Reset aus 
      OnResetDevice(device, null); 

      // Definiert keine Vor und Rückseite 
      device.RenderState.CullMode = Cull.Clockwise; 
      // Direct3D-Beleuchtung deaktivieren 
      device.RenderState.Lighting = false; 
      // Beschreibt einen festen Füllmodus 
      device.RenderState.FillMode = FillMode.Solid; 

      // Erstellt den Buffer für die Vertices (Lab Koordinatensystem) 
      vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored), // Typ der Vertices 
              18,          // Anzahl der Vertices 
              device,         // Gerätekontext unser device 
              0,          // Anzahl der Flags zur Verarbeitung der Vertice 
              CustomVertex.PositionColored.Format, // Typ der Vertices (Weil man auch eigene Strukturen definieren kann) 
              Pool.Default);       // Speicherung der Vertices 

      // Event welches aufgerufen wird wenn der Vertexbuffer erstellt wurde 
      vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer); 
      // Event wird von Hand aufgerufen 
      this.OnCreateVertexBuffer(vertexBuffer, null); 

      return true; // Device wurde erstellt 
     } 
     catch { return false; } // Device konnte nicht erstellt werden 

在這一部分,我渲染所有頂點:

public void Render() 
    { 
     // Fragt ob das Device erstellt wurde und noch gültig ist 
     if (device == null) 
      return; 

     // Inhalt des Backbuffers löschen und das ganze mit einer Farbe einfärben 
     device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, // Die entsprechende Oberfläche 
        System.Drawing.Color.Black,    // Die Farbe 
        1.0f,          // Abstand vom Betrachter, an dem die Oberfläche gelöscht wird und einen Wert, ... 
        0);          // ...der in jedem Stencil-Buffer-Eintrag gespeichert wird. 

     // Anfang der Szene 
     device.BeginScene(); 
     // Matrizen aufsetzen 
     SetupMatrices(); 

     // Bindet den Buffer an das Device 
     device.SetStreamSource(0,   // Nummer des Streams 
           vertexBuffer,// Der Buffer 
           0);   // StartOffset in dem Buffer 

     // Teilt dem Device das Format der Vertices mit 
     device.VertexFormat = CustomVertex.PositionColored.Format; 
     // Zeichnet die Dreiecke 
     device.DrawPrimitives(PrimitiveType.LineList, // Typ der Primitive 
           0,      // Eintrag des ersten Vertex 
           3);      // Anzahl der Primetive 

     // Zeichnet jedes einzelne Sphere 
     foreach (Sphere mesh in meshList) 
     { 
      mesh.labMesh.DrawSubset(0); 
     } 

     // Ende der Szene 
     device.EndScene(); 
     // Bringt die Zeichnung auf das Fensterhandle 
     device.Present(); 
    } 

這是創建每個球體的類:

/// <summary> 
/// Die Klasse Sphere 
/// </summary> 
public class Sphere 
{ 
    // Radius der Kugel 
    private const float radius = 4f; 
    // Die Anzahl der Ebenen einer Kugel 
    private const int slices = 40; 
    // Die Anzalh der Flächen einer Ebene 
    private const int stacks = 40; 

    // Das Mesh zum Darstellen der Kugel 
    private Mesh mesh = null; 
    private Vector3 vec; 
    public Vector3 min; 
    public Vector3 max; 


    /// <summary> 
    /// Gibt den Mesh zurück 
    /// </summary> 
    public Mesh labMesh 
    { 
     get { return mesh; } 
    } 

    public Vector3 labVector 
    { 
     get { return vec; } 
    } 

    /// <summary> 
    /// Erstellt das Mesh 
    /// </summary> 
    /// <param name="device">Das 3D Device</param> 
    /// <param name="color">Die Farbe der Kugel</param> 
    /// <param name="labValues">Die Lab Werte der Kugel</param> 
    public void createMesh(Device device, Color color, params float[] labValues) 
    { 
     // Erstellt die Kugel mit der Anbindung an das Device 
     mesh = Mesh.Sphere(device, radius, slices, stacks); 
     // Kopiert das Mesh zum Erstellen des VertexArrays 
     Mesh tempMesh = mesh.Clone(mesh.Options.Value, Vertex.FVF_Flags, device); 
     // Erstellt den VertexArray 
     Vertex[] vertData = (Vertex[])tempMesh.VertexBuffer.Lock(0, typeof(Vertex), LockFlags.None, tempMesh.NumberVertices); 

     // Weist jedem Vertex die Farbe und die Position zu 
     for (int i = 0; i < vertData.Length; ++i) 
     { 
      vertData[i].color = color.ToArgb(); 
      vertData[i].x += labValues[1]; 
      vertData[i].y += labValues[0] - 50f; 
      vertData[i].z += labValues[2]; 
     } 
     min = new Vector3(labValues[1], labValues[0] + 100f, labValues[2]); 
     max = new Vector3(labValues[1], labValues[0] - 100f, labValues[2]); 

     // Gibt den VertexBuffer in der Kopie frei 
     tempMesh.VertexBuffer.Unlock(); 
     // Löscht den Mesh aus dem Speicher 
     mesh.Dispose(); 
     // Legt die Kopie in der Meshinstanz ab 
     mesh = tempMesh; 

     Vector3 v = new Vector3(labValues[1], labValues[0], labValues[2]); 
     vec = v; 
    } 
} 

/// <summary> 
/// Vertex für die Kugel 
/// </summary> 
struct Vertex 
{ 
    public float x, y, z; // Position of vertex in 3D space 
    public int color;  // Diffuse color of vertex 

    /// <summary> 
    /// Konstruktor der Vertex 
    /// </summary> 
    /// <param name="_x">X(A) - Position</param> 
    /// <param name="_y">Y(L) - Position</param> 
    /// <param name="_z">Z(B) - Position</param> 
    /// <param name="_color">Die Farbe</param> 
    public Vertex(float _x, float _y, float _z, int _color) 
    { 
     x = _x; y = _y; z = _z; 
     color = _color; 
    } 

    // Das Format des Vertex 
    public static readonly VertexFormats FVF_Flags = VertexFormats.Position | VertexFormats.Diffuse; 
} 

我不知道如何提高性能渲染1600個球體! 我認爲在遊戲中也必須是一個解決方案。

我希望你有一個想法,幫我!

回答

2

首先,我必須說,受管理的DirectX不受Microsoft的支持。你使用XNA之類的東西要好得多,或者更好的是SlimDX

一種方法是隻使用一個球體,然後建立第二個包含矩陣數據的頂點流。然後,您可以使用一個單一的繪圖調用渲染球體實例。這應該會顯着提高性能。

另一種方法是用盡可能多的球體構建一個巨型頂點緩衝區,因此您可以調用DrawSubset更少。這會提高性能。

也就是說1600繪製要求幀高但不是顯着,所以應該可以得到像樣的表現。

有幾件事情來嘗試在mesh.Clone通話將被添加以下標誌:

  1. 只寫
  2. OptimizeVertexCache
  3. VbShare

同時確保alpha混合關閉(它可能是值得一試)。

理想情況下,從前到後依次渲染球體將優化溢出(像素寫入的次數應該儘可能低),但這通常會比GPU中保存的CPU時間多。

其他要記住的事情是你的球體有多複雜。他們能減少三分之一的數量嗎?除了使用凱撒建議的某種調試器之外(從來沒有,我會輸入那個;))是一個很好的前進方向。這可能只是託管DirectX的性能不足以給你後面的結果......

2

我會建議通過探查器運行您的代碼,並查看瓶頸在代碼中的位置並對其進行優化。

檢查this question瞭解哪些profiler存在C#。

+0

VS 2012實際上有一個內置的(專業和以上,至少)。 – 2013-03-22 18:50:39

0

在這個例子中,你的Draw Calls數量是瓶頸,而不是你的GPU性能。 你應該明確地減少你的抽獎數量。對於一個完整的高端遊戲來說,在PC上,抽獎調用的數量很少超過2000,這是一個非常優化的流程。在筆記本電腦上,這仍然是一個非常高的數字。在使用C#時最大限度地利用1000個繪圖調用。

爲了解決您的問題,有一些選項。

第一種選擇是將所有數據放入單個緩衝區。你應該看到你的頂點緩衝區是發送給GPU的數據塊,並且你希望儘可能少地發送你的GPU,因爲這裏的開銷很大。您應該將所有球體放入單個緩衝區或幾個緩衝區。這將解決您的性能問題。 合併靜態對象是遊戲引擎中的常見做法。

第二種選擇是,如果你需要可移動的球體,你可以使用實例化。實例化是一種技術,您可以多次渲染相同的數據,併爲每個實例額外添加一些數據。這隻需要一次平局。實例現在通常由所有GPU支持,因此如果需要移動對象或參數化對象,請使用此選項。快速谷歌肯定會提供更多的信息。

最後一點,就像已經提到的那樣,託管directx已經死了多年。它很慢,只有DX9。你應該在使用c#時切換到SlimDX(XNA也死了)。