5

いくつかのグラフィックス API (DirectX9 および DirectX11) の上に抽象化レイヤーを作成しています。ご意見をお聞かせください。

従来、抽象化したい概念ごとに基本クラスを作成していました。
したがって、典型的な OO のやり方では、たとえばクラス Shader と 2 つのサブクラス DX9Shader および DX11Shader を使用します。

テクスチャなどのプロセスを繰り返します...それらをインスタンス化する必要がある場合、現在のグラフィックス API に応じて適切なサブクラスを返す抽象ファクトリがあります。
RAII に従って、返されるポインターは std::shared_ptr にカプセル化されます。

これまでのところうまくいっていますが、私の場合、このアプローチにはいくつかの問題があります。

  1. 両方の API (および将来的には他の API) の機能をカプセル化するパブリック インターフェイスを考え出す必要があります。
  2. 派生クラスは別々の DLL (DX9 用に 1 つ、DX11 用に 1 つなど) に格納されており、クライアントでそれらに shared_ptr を設定するのは呪いです: 終了時にグラフィック DLL がアンロードされ、クライアントにまだ shared_ptr がある場合アンロードされた DLL からのコードの呼び出しが原因で、グラフィックス オブジェクトの 1 つがブーム、クラッシュします。

これにより、私は自分のやり方を再設計するようになりました.リソースへの生のポインターを返すだけで、グラフィックス API をきれいにすることができると思っていましたが、クライアント側にぶら下がっているポインターの問題とインターフェイスの問題がまだあります. COM のような手動参照カウントも考えましたが、それは後退だと思いました (shared_ptr の世界から来て、手動参照カウントは原始的なようです)。

次に、すべてのグラフィックス クラスが整数 ID で表される Humus の作業を見ました (OpenGL と同じように)。新しいオブジェクトを作成すると、その整数 ID のみが返され、ポインターが内部に保存されます。それはすべて完全に不透明です!

抽象化を表すクラス (DX9Shader など) はすべて、唯一のインターフェイスであるデバイス API の背後に隠されています。
テクスチャを設定したい場合は、device->SetTexture(ID) を呼び出すだけで、残りは舞台裏で行われます。

残念なことに、API の隠れた部分が肥大化していること、それを機能させるために必要なボイラー プレート コードがたくさんあること、そして私はすべてを行うクラスのファンではないことです。

アイデア/考えはありますか?

4

3 に答える 3

3

あなたは、主な問題は、DLLがその内部へのポインタを持っている間にアンロードされることであると言います。まあ...それをしないでください。そのDLLにメンバーが実装されているクラスインスタンスがあります。それらのクラスインスタンスが存在する限り、そのDLLがアンロードされることは基本的にエラーです。

したがって、この抽象化をどのように使用するかについて責任を負う必要があります。DLLからロードするコードを担当する必要があるのと同じように、DLLからのコードは、DLLをアンロードする前にクリーンアップする必要があります。それをどのように行うかはあなた次第です。DLLが返すオブジェクトごとに増分される内部参照カウントを設定し、参照されているすべてのオブジェクトがなくなった後にのみDLLをアンロードすることができます。または何でも、本当に。

結局のところ、これらの不透明な番号などを使用している場合でも、DLLがアンロードされたときに、その番号でこれらのAPI関数の1つを呼び出すとどうなりますか?おっと...だから、それは本当にあなたに何の保護も買わない。あなたはどちらの方法でも責任を負わなければなりません。

あなたが考えていないかもしれない数の方法の欠点は次のとおりです。

  • オブジェクトが実際に何であるかを知る能力の低下。実際にはオブジェクトではない番号を渡したため、API呼び出しが失敗する可能性があります。さらに悪いことに、テクスチャを受け取る関数にシェーダーオブジェクトを渡すとどうなりますか?シェーダーとテクスチャを使用する関数について話しているのかもしれませんが、誤って引数の順序を忘れてしまいましたか?C ++のルールでは、それらがオブジェクトポインターである場合でも、そのコードをコンパイルすることはできません。しかし、整数では?大丈夫だよー; 実行時エラーのみが発生します。

  • パフォーマンス。すべてのAPI呼び出しは、実際のポインターを使用するために、ハッシュテーブルなどでこの番号を検索する必要があります。ハッシュテーブル(つまり、配列)の場合、おそらくかなりマイナーです。しかし、それはまだ間接的です。また、抽象化は非常に低レベルのように見えるため、このレベルでのパフォーマンスの低下は、パフォーマンスが重要な状況で実際に悪影響を与える可能性があります。

  • RAIIおよびその他のスコープメカニズムの欠如。もちろん、shared_ptrそれらを作成および削除する-esqueオブジェクトを作成できます。ただし、実際のポインタを使用している場合は、これを行う必要はありません。

それは価値がないようです。

于 2011-11-30T10:06:35.773 に答える
0

あなたのpについて。2: クライアントは常にライブラリの前にアンロードされます。

すべてのプロセスにはライブラリ依存関係ツリーがあり、.exe がツリー ルート、ユーザー Dll が中間レベル、システム ライブラリが低レベルです。プロセスは低レベルから高レベルにロードされ、ツリー ルート (exe) は最後にロードされます。プロセスはルートからアンロードされ、低レベルのライブラリは最後にアンロードされます。これは、あなたが話している状況を防ぐために行われます。

もちろん、ライブラリを手動でロード/アンロードすると、この順序が変更され、ポインタを有効に保つ責任があります。

于 2011-11-30T09:59:10.773 に答える