グラフィックの状態とコンポーネントを管理していますか?


11

グラフィックスを扱うとき、私はしばしば多くの時期尚早な最適化を行う傾向があります。私が常に従おうとするいくつかの原則があります。

  • D3Dコンポーネントの数を最小限に抑えます。(レンダリング状態、バッファー、シェーダーなど)
  • どうしても必要な場合にのみコンポーネントをバインドします。(まだバインドされていないなど)
  • コンポーネントをできる限り専門化します。(必要なBindFlagsのみを設定するなど)

これにより、作成されたコンポーネントと現在のパイプライン状態を管理するための非常に手の込んだラッパーを構築することになりました。これは私の貴重な開発時間の多くを消費するだけでなく、複雑さをさらに大きくします。

そして最悪の事態: 場合、それがすべてトラブルに値するかどうかさえ知りません。

私の最適化の考慮事項のいくつかは、すでに下位レベルで実装されている可能性があり、それらを複製しているだけで、CPUの時間をさらに浪費しています。パフォーマンスへの影響は無視できるため、他の考慮事項は完全に不要な場合があります。

だから私の質問は:

  1. 上記のガイドラインのうちどれが有効で、どの程度遵守する必要がありますか?
  2. GPUは状態の変化をどのように処理しますか?
  3. 使用されていない状態を変更するとどうなりますか?(アクティブな間は、描画呼び出しは行われません。)
  4. さまざまな異なるコンポーネントをバインドするための実際のパフォーマンスペナルティは何ですか?
  5. 他にどのようなパフォーマンスの考慮事項を行う必要がありますか?

実際の制限に達するまでは、パフォーマンスについて気にしないでください。それは明らかに実用的な見地からは真実ですが、私は主に理論に興味があります。どういうわけか、最適なグラフィックスフレームワークを構築するという衝動と闘う必要がありますが、通常の「時期尚早の最適化講義」ではそれを実現できないと思います。

コンポーネントの管理

現在、SlimDXをマネージラッパーとして使用して、DirectX 11アプリケーションをC#で作成しています。これは非常に低レベルのラッパーであり、私の現在の抽象化はその上に構築されています。

Direct3D抽象化を使用する場合、いくつかの明らかな利点があります。環境の設定、シェーダーの読み込み、定数の設定、メッシュの描画は、はるかに簡単で、使用するコードが大幅に少なくなります。また、ほとんどのコンポーネントの作成と破棄を管理するため、どこでも自動的に再利用でき、メモリリークをほぼ完全に回避できます。

  1. 通常、すべてのグラフィックコンポーネントとリソースをどのように管理しますか?
  2. 以下の私の例と同様のことを行うマネージラッパーを推奨できますか?

これが私の現在の実装例です。インターフェースにとても満足しています。私のニーズに対して十分な柔軟性があり、使用および理解が非常に簡単です。

// Init D3D environment
var window = new RenderForm();
var d3d = new Direct3D(window, GraphicsSettings.Default);
var graphics = new GraphicsManager(d3d.Device);

// Load assets
var mesh = GeometryPackage.FromFile(d3d, "teapot.gp");
var texture = Texture.FromFile(d3d, "bricks.dds");

// Render states
graphics.SetViewports(new Viewport(0, 0, 800, 600);
graphics.SetRasterizer(wireFrame: false, culling: CullMode.Back);
graphics.SetDepthState(depthEnabled: true, depthWriteEnabled: true);
graphics.SetBlendState(BlendMethod.Transparency);

// Input layout
graphics.SetLayout("effect.fx", "VS", "vs_4_0",
    new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
    new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0)
);

// Vertex shader
graphics.SetShader(Shader.Vertex, "effect.fx", "VS", "vs_4_0");
graphics.SetConstants(Shader.Vertex, 0, 4, stream => stream.Write(wvpMatrix));

// Pixel shader
graphics.SetShader(Shader.Pixel, "effect.fx", "PS", "ps_4_0");
graphics.SetTexture(Shader.Pixel, 0, texture);
graphics.SetSampler(Shader.Pixel, 0, Sampler.AnisotropicWrap);
graphics.SetConstants(Shader.Pixel, 0, 1, stream => stream.Write(new Color4(1, 0, 1, 0);

d3d.Run(() =>
{
    // Draw and present
    d3d.BackBuffer.Clear(new Color4(1, 0, 0.5f, 1));
    graphics.SetOutput(d3d.BackBuffer);
    graphics.Draw(mesh);
    d3d.Present();
}

8
この種の質問については、「時期尚早の最適化」の講義はしませんが、「自分自身で確認できるようにプロファイルの変更」の講義を行います。
テトラッド2013年

@テトラッド私はこれがかなりまともなアドバイスであることを認めるのはほとんど恥ずかしいです。私は間違いなくもっとプロファイリングをするべきです。
Lucius

1
プロファイリング番号は「写真またはそれが起こらなかった」のgamedevバージョンです==
Patrick Hughes

回答:


3

私は、gamed.netのこれらのスレッドでHodgmanによって概説されている抽象化アプローチが好きです。

彼は3層レンダリングシステムについて説明しています。

  1. 「コマンド」を受け入れる低レベルのレンダリングAPI。Direct3D9、Direct3D 11、OpenGLなどのさまざまなグラフィックAPIの違いだけを抽象化します。各「コマンド」は、頂点ストリームやテクスチャのバインド、プリミティブの描画など、異なる状態または描画呼び出しにマップされます。
  2. すべての状態をグループ化し、特定のオブジェクトをレンダリングするために必要な単一の描画呼び出しをグループ化し、それらを並べ替えて、最初のレベルに送信されるコマンドに変換する「レンダリング項目」を受け入れるAPI。レンダリング状態には、ドローコールと「状態グループ」のスタックが含まれ、論理的に状態変化をグループ化します。たとえば、レンダーパスの状態グループ、マテリアルの状態グループ、ジオメトリの状態グループ、インスタンスの状態グループなどがあります。このレベルは、これらのレンダーアイテムを並べ替えて冗長な状態変化を減らし、実際には冗長な状態変化を選別する役割を果たします。
  3. 2番目のレベルにレンダーアイテムを送信するシーングラフやGUIレンダラーなどの高レベルシステム。注意すべき重要な点は、これらのシステムは状態分類アルゴリズムまたは特定のレンダリングAPIのいずれも認識していないため、完全にプラットフォームに依存しないことです。また、下位レベルのAPIが実装されていれば、簡単に使用できます。

結論として、このモデルは両方の問題を一度に解決します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.