2013-03-22 3 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 1600 обращается к кадрам высоко, но не , что значительным, поэтому должно быть возможно получить достойную производительность как есть.

Несколько вещей, чтобы попробовать бы добавить следующие флаги в вашем mesh.Clone вызова:

  1. WriteOnly
  2. OptimizeVertexCache
  3. VbShare

Также убедитесь, что альфа-смешивание выключено (это, вероятно, стоит попробовать).

Идеально, чтобы ваши сферы в обратном порядке оптимизировали переполнение (количество пиков, которые пишутся, должно быть как можно меньше), но это часто может использовать больше CPU, чем вы сохраняете время GPU.

Другие вещи, о которых следует помнить, являются сложными вашими сферами. Могут ли они сократить количество трис?

Помимо этого, используя какой-то отладчик, предложенный Цезарем (хотя я бы не набрал это;)) - это хороший путь вперед. Возможно, Managed DirectX недостаточно эффективен, чтобы дать вам результаты, которые вы после ...

2

Я бы рекомендовал запустить ваш код через профилировщик и посмотреть, где находится шея бутылки в вашем коде, и оптимизировать ее.

Отметьте this question, для какого профайлера там есть C#.

+0

VS 2012 фактически имеет один встроенный в настоящее время (профессиональный и выше, по крайней мере). –

0

В этом примере ваше количество вызовов Draw - это узкое место, а не производительность вашего GPU. Вы должны определенно сократить количество обратных вызовов. Для полноценной полноценной игры на ПК nubmer призывов к обращению редко превышает 2000, и это очень оптимизированный конвейер. На ноутбуках это все еще очень много. Нажимайте на максимум 1000 вызовов при использовании C#.

Для решения возникших проблем есть несколько вариантов.

Первый вариант - поместить все ваши данные в один буфер. Вы должны видеть ваши вершинные буферы как куски данных, которые вы отправляете в gpu, и вы хотите отправить как можно меньше вещей в свой gpu, так как накладные расходы здесь массивные. Вы должны поместить все сферы в один буфер или несколько буферов. Это решит ваши проблемы с производительностью. Слияние статических объектов - обычная практика в игровых двигателях.

Второй вариант, если вам нужны подвижные сферы, вы можете использовать instancing. Instancing - это метод, при котором вы можете многократно отображать одни и те же данные с некоторыми дополнительными данными на один экземпляр. Для этого требуется только один вызов вызова. В настоящее время Instancing обычно поддерживается всеми GPU, поэтому используйте это, если вам нужны движущиеся объекты или параметризованные объекты. Быстрый google, несомненно, предоставит больше информации.

Последнее примечание, как уже упоминалось, управляемый directx был мертв годами. Он медленный и только dx9. Вы должны переключиться на SlimDX при использовании C# (XNA тоже мертв).

Смежные вопросы