在DirectX中呈现时的性能问题

时间:2013-03-22 13:49:20

标签: c# directx rendering render direct3d

在我的项目中,我展示了很多领域。

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个球体来提高性能! 我认为在游戏中也必须是一个解决方案。

我希望你有一个想法来帮助我!

3 个答案:

答案 0 :(得分:2)

我建议您通过分析器运行代码并查看代码中瓶颈的位置并进行优化。

检查this question以查找C#的配置文件。

答案 1 :(得分:2)

首先,我必须说Managed DirectX不受微软的支持。你最好还是使用像XNA这样的东西或更好的SlimDX

一种方法是仅使用一个球体,然后设置包含矩阵数据的第二个顶点流。然后,您可以使用一个单一的绘制调用渲染实例化的球体。这应该会显着提高性能。

另一种方法是构建一个包含尽可能多的球体的巨型顶点缓冲区,以便减少DrawSubset。这将提高性能。

那说1600画面调用一帧很高但 显着,所以应该可以获得不错的性能。

要尝试的一些方法是在mesh.Clone电话中添加以下标记:

  1. 只写
  2. OptimizeVertexCache
  3. VbShare
  4. 还要确保关闭alpha混合(这可能值得一试)。

    理想情况下,从前到后渲染球体将优化溢出(写入像素的次数也应尽可能低),但这通常会占用比GPU时间更多的CPU。

    要记住的其他事项是你的球体有多复杂。他们可以减少tris的数量吗?

    除了使用Caesar建议的某种调试器(从来没有我输入;))是一个很好的前进方法。可能只是Managed DirectX的性能不足以为您提供您所追求的结果......

答案 2 :(得分:0)

在此示例中,绘制调用的数量是瓶颈,而不是GPU性能。 您应该明确减少绘制调用的次数。对于一个完整的高端游戏,在PC上,绘图调用的nubmer很少超过2000,这是一个非常优化的管道。在笔记本电脑上,这仍然是一个非常高的数字。使用C#时最多可以进行1000次绘制调用。

为了解决您的问题,有一些选择。

第一个选项是将所有数据放入单个缓冲区。您应该将顶点缓冲区视为发送到gpu的数据块,并且您希望尽可能少地向gpu发送数据,因为此处的开销很大。您应该将所有球体放在一个缓冲区或几个缓冲区中。这将解决您的性能问题。 合并静态对象是游戏引擎中的常见做法。

第二个选项是,如果你需要可移动的球体,你可以使用实例化。实例化是一种技术,您可以在其中多次渲染相同的数据,每个实例都有一些额外的数据。这只需要一次平局调用。现在,所有GPU都支持实例化,因此如果需要移动对象或参数化对象,请使用此实例。快速谷歌肯定会提供更多信息。

最后注意,就像已经提到的那样,托管的directx多年来已经死了。它很慢,只有dx9。使用c#时应切换到SlimDX(XNA也已死)。