1

私のプロジェクトでは、たくさんの球を表示しています。

デバイス

球を表示するために、いくつかの値を含むファイルをロードします。したがって、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個の球をレンダリングしてパフォーマンスを向上させる方法がわかりません。ゲームでも解決策に違いないと思います。

あなたが私を喜ばせるアイデアを持っていることを願っています!

4

3 に答える 3

2

プロファイラーを介してコードを実行し、コードのボトルネックがどこにあるかを確認して最適化することをお勧めします。

C#用にどのプロファイラーが存在するかについては、この質問を確認してください。

于 2013-03-22T18:30:47.890 に答える
2

まず、Managed DirectX は Microsoft によってサポートされていないと言わざるを得ません。XNA のようなものを使用する方がはるかに優れているか、さらにSlimDXの方が優れています。

1 つの方法は、1 つの球体のみを使用してから、行列データを含む 2 つ目の頂点ストリームを設定することです。その後、1 回の描画呼び出しでインスタンス化された球体をレンダリングできます。これにより、パフォーマンスが大幅に向上するはずです。

もう 1 つの方法は、1 つの巨大な頂点バッファーを構築し、そこに収まる限り多くの球体を作成して、呼び出す回数をDrawSubset減らすことです。これにより、パフォーマンスが向上します。

とは言っても、1 フレームあたり 1600 回のドロー コールは高いですが、それほど重要ではないため、このままで十分なパフォーマンスを得ることができるはずです。

mesh.Clone試してみるべきいくつかのことは、呼び出しに次のフラグを追加することです。

  1. 書き込み専用
  2. 頂点キャッシュの最適化
  3. Vbシェア

また、アルファブレンディングがオフになっていることを確認してください (オフになっている可能性がありますが、試してみる価値があります)。

理想的には、球体を前後の順序でレンダリングすると、オーバーフィルが最適化されます (ピクセルが書き込まれる回数もできるだけ少なくする必要があります) が、多くの場合、GPU 時間の節約よりも多くの CPU を使用する可能性があります。

他に心に留めておくべきことは、球体がどれほど複雑かということです。トリスの数を減らすことはできますか?

それを超えて、シーザーによって提案されているようなある種のデバッガーを使用することは(私は決してそれをタイプしませんでした;))、前進する良い方法です。マネージド DirectX のパフォーマンスが不十分で、求めている結果が得られない可能性があります...

于 2013-03-22T18:47:42.020 に答える
0

この例では、描画呼び出しの数がボトルネックであり、GPU のパフォーマンスではありません。ドローコールの数を確実に減らす必要があります。完全なハイエンド ゲームの場合、PC ではドロー コールの数が 2000 を超えることはめったになく、これは非常に最適化されたパイプラインにあります。ラップトップでは、これは依然として非常に高い数値です。C# を使用する場合は、最大 1000 回の描画呼び出しを目指します。

問題を解決するには、いくつかのオプションがあります。

最初のオプションは、すべてのデータを単一のバッファーに入れることです。GPU に送信するデータのチャンクとして頂点バッファーを確認する必要があります。ここでのオーバーヘッドが大きいため、GPU に送信するデータはできるだけ少なくする必要があります。すべての球体を 1 つまたはいくつかのバッファーに配置する必要があります。これにより、パフォーマンスの問題が解決されます。静的オブジェクトのマージは、ゲーム エンジンでは一般的な方法です。

2 番目のオプションは、可動球が必要な場合は、インスタンス化を使用することです。インスタンス化は、インスタンスごとに追加のデータを使用して、同じデータを複数回レンダリングできる手法です。これには、ドローコールが 1 つだけ必要です。インスタンス化は現在、すべての GPU で一般的にサポートされているため、移動オブジェクトまたはパラメータ化されたオブジェクトが必要な場合はこれを使用してください。簡単なグーグルは確かにより多くの情報を提供します。

最後に、既に述べたように、管理された directx は何年も前から死んでいます。遅くてdx9のみです。c# を使用する場合は、SlimDX に切り替える必要があります (XNA も使用できません)。

于 2013-03-23T14:53:28.497 に答える