2

一連の頂点をジオメトリ シェーダーに送信し、その情報からスプライトをレンダリングする単純なレンダリング プロセスがあります。

小さなアプリのメモリ使用量は屋根を通り抜け、継続的に増加します。_CrtDumpMemoryLeaks()Visual Leak Detector で簡単なテストを実行しましたが、どちらも漏れはないと主張しています。

頂点情報でいっぱいのデバイスとコンテナーがあります。

ID3D10Device* pD3DDevice;
std::vector<SpriteVertex>* sprites;

そして、私のRenderSprites()方法では、リークが止まるまでほとんどすべてをコメントアウトしました(レンダリングと同様に;)

物事がうまくいかないポイントは次のとおりです。

void DirectX10Renderer::RenderSprites()
{
    D3D10_SUBRESOURCE_DATA initData;
    initData.pSysMem = &((*sprites)[0]);

    D3D10_BUFFER_DESC bd;
    bd.Usage = D3D10_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(SpriteVertex)*(numSprites);
    bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    bd.MiscFlags = 0;

    // As soon as the following line is uncommented, memory starts leaking
    pD3DDevice->CreateBuffer(&bd, &initData, &m_SpriteBuffer)); // <-- this is leaking

    // the rest of the rendering code is for now commented out
}

これを停止する各フレームで何を解放または削除すればよいかわかりません。

4

3 に答える 3

4

他の回答が迅速な方法で解決策を仮定しており、コメントが少し短すぎるため、ここに行きます-別の急ごしらえです。実行時に作成されたバッファを更新するために他に何が必要かを尋ねました:

バッファーの使用をD3D10_USAGE_DEFAULT(GPU 読み取り/書き込みのみ) から、 およびD3D10_USAGE_DYNAMICのような機能を有効にするように変更できます。バッファの一部または全体などを更新できます。これにはパフォーマンスへの影響がありますが、動的は本質的に静的 on-initialization-constant バッファよりもコストがかかるため、これは予想されることです。専門的なことを言いすぎたり、調査の楽しみをあなたに任せたりするのは避けます。CopyResource()CopySubresourceRegion()

冒頭の質問に対する長い答え

Yarrr、心のこもった、新しいデータ。ラム酒の補完的な樽。

方法の背後にある理由についてさらに情報を提供しようとしますが、それでも楽しく読むことができます。ウィキペディアを含むさまざまな Web サイトは、コンピューター サイエンスに関連するすべてのものから面白みを奪っています。重要なポイントは、プログラマーに直観的な理由、つまり理由を理解させ、その理由に対応する方法を分析させることです。合同パラレトープのような数学の単純な概念と同じように、初心者にはせいぜい不可解に聞こえ、単位時間あたりの悪口が大量に発生します。私が何百万回も証明してきたように、モチベーションを理解することは、適切な理解に大いに役立ちます。

メモリリークの問題は、他の人がこれまでに言ったように、メモリを適切に管理していないという事実によって引き起こされます。通常、メモリを気にする必要があるかどうか (従来の C/C++ インジケーターが new、delete、malloc などのように実行されない場合) の良いインジケーターは、 Microsoftの起源 (通常は COM インターフェイス)へのポインターがある場合です。基になる COM コンポーネント インスタンスまたは必要に応じて COM オブジェクトとの間のネゴシエーションを仲介します)。これには、あなたの知らない人によって開発された、あなたが完全には理解していない他の API も含まれます。しかし、最初に COM とその癖の 1 つであるメモリ管理について話しましょう。実際には非常に単純で、従来の C++ オブジェクトとあまり違いはありません。

バッファーの手動割り当て自体はありませんが(当面の場合)、少なくとも古典的なC/C++ の方法では、COM 準拠のインターフェイスで動作するものはすべて (ちょっと待ってください。後で説明します)、これらのインターフェイスへの最初の null ポインターは、作業が完了したときに関数を呼び出すことに依存している可能性が非常に高くなります。Release()

Release()は、私たちが大切にし、愛することを学んだキーワードの直訳です。したがって、実際に初期化された COM コンポーネント インスタンスまたはオブジェクトへのアドレスをインターフェイス ポインターに入力するさまざまな作成関数delete(コンストラクターに相当するもの) をクエリすることによって取得する COM オブジェクトの管理に関する Microsoft の指示に従ってください。将来の参照と好奇心の満足のために、すべての COM インターフェイスは、継承元コンポーネントの新しい依存関係を説明するためにオーバーライドする必要があるものから派生し、参照カウントなどの他の機能を提供します。IUnknownRelease()

なぜ COM で大騒ぎするのですか?

多くの Microsoft API は、COM (コンポーネント オブジェクト モデル) に依存/基づいています。これは、純粋な C++ で抽象クラスによって行われる通常のインターフェイス駆動型コードを実装する方法であり、vtables と継承の概念を推進する楽しい時間です。ポリモーフィズム。しかし、なぜ単純に C++ を使用しないのか、不思議に思うに違いありません。おわかりのように、生まれてからかなり古い DirectX (1994./1995 と考えてください) は、COM が最初に開始された頃に、基本的な COM のアイデアを将来繰り返すための自然な選択でした。

当初、COM はツール間の連携 (Microsoft Office スイート)、またはより専門的にはプロセス間通信を支援するために開発されました。そのバスケットに、OLE をドロップすることもできます。そこから、Microsoft のソフトウェア エンジニアは、これをもう少し拡張する可能性を見出しました。その論拠は、COM コンポーネントが問題を解決するためにデビューする言語を介してアクセス可能な標準化されたインターフェースに対応する COM コンポーネントの形式でさまざまなリソースを作成できるようにすることを主張しました。そうすれば、Microsoft は多くの作業をスケーラブルなフォームファクタにカプセル化し、それにさまざまな一意の識別子 (__uuidof()) を割り当てることができます。これは、COM が登録/追跡する方法です。すべてのクラス)、COM 規則に従って別の場所でコンパイルされたものをまったく異なる言語で動作させるクロスランゲージ ソリューションを作成します。

それは実際に、クロスプロセスの協力を提供し、その逆にアクセスするために成長しました。また、OS の開発や共有機能で発生するあらゆる種類の開発上の問題に関連するさまざまな問題も取り上げています (再利用可能な対話、ファイルを開くダイアログが有名な例です)。当然、DirectX (および ActiveX など) は、この状況に完全に適合します。ゲームやシミュレーションなどのインタラクティブ性の高いソフトウェアに関して、OS に共通のソリューションを提供します。

そのため、Interface を表す "I" 接頭辞が表示さID3D10Deviceれます。具体的には、実際の実装に対応する COM コンポーネントの接頭辞です。GUID 識別、インターフェイス ポインターへのポインターを必要とする統合関数呼び出しを介して COM コンポーネントのインスタンスを接続し、指定した説明に基づいて動作中の D3D10 デバイスを取得します。そのため、実際のポインターを LPVOID として提供することもできます (または基本的に、ここにバイトがあると言って、必要なことを行います)。使用法が正しければ、基になる実装によって適切に解決され、インターフェイス ポインターが機能するようになります。基本的に、機能している COM インスタンスを取得するためのクエリは、参照を関数AddRef()で上下させRelease()ます。保持解放の概念に似ています。

そして通常、これらのコンポーネントはすべて、問題なく適切なバージョン管理のために「レンダリング」された一連の DLL に押し込まれます。そして、まともな API 実装が得られますが、正直なところ、それほど悪くはありません。それは実際にはちょっときれいです。

.NETとCOMの「違い」

現在、その主な機能のいくつかは、.NET を垣間見ている人にとっては混乱を招く可能性がありますが、COM は .NET の前身であることに注意する必要があります (つまり、ほとんど単なる一時的な前身であり、驚くほどほとんど共有していません)。 (2000年からここにあります)。また、.NET フレームワークは、Windows で作業するあらゆる種類の開発者にクロス言語の標準プラットフォームを提供するために意図的に開発されているのに対し、 COM の今日の機能セットは多少偶然のものであることに注意してください。COM の可能性は、最初に実装された後に見られました。.NET は、可能性と必要性​​によって推進されました。

このような決定は、さまざまな言語に精通した多くの開発者がいる Windows のように広く普及しているプラ​​ットフォームにとって論理的でした。CLS 準拠のコンポーネントを作成することで、多くの異なる .NET 言語でコンパイルされた多くのソリューションを楽しむことができます。開発時間が短縮され、実際にパフォーマンスが向上します。たとえば、C# (.NET 言語) の一部の数学関数は、実際には高速な C/C++ で記述された外部関数に解決されます (pow() はこの例です)。

さらに言えば、.NET はCOM を凌駕するだけでなく(「競合」しなければならない場合、ほとんど無意味です)、物事のやり方が根本的に異なります。そのため、Microsoft は COM を .NET に接続するためのさまざまな方法を作成し、.NET フレームワークのコア アーキテクチャを損なうことなくラップアラウンドしました。

これは、ご存じない場合の簡単な紹介にすぎません。COM のトピックについては、本全体が書かれています。それを使用するには、より単純で直感的な理解が必要です。それが役に立ち、幸せなコーディングを願っています。また、特にメモリ管理を担当している場合は、render 関数で何かを作成しないようにしてください。

于 2012-04-08T04:43:20.693 に答える
2

ID3D10Device::CreateBuffer の呼び出しが成功するたびに、Release() の呼び出しと一致させる必要があります。

于 2012-04-06T14:42:51.187 に答える
1

トムがすでに言ったように、作成後にバッファを解放する必要があります。ただし、レンダリング関数内にバッファーを作成するのは良いことではないと思います。これは、何度も何度もバッファを作成していることを意味します。これはメモリ リークを引き起こすだけでなく、非常に遅く、必要ではありません。

したがって、バッファを作成する初期化関数を作成するだけです。また、バッファが不要になったとき (シーンの終わりなど) にバッファを解放する関数を作成します。次に、レンダリング関数には、バッファーの描画への呼び出しのみが含まれている必要があります。

これが途中で役立つことを願っています。

幸運を!

于 2012-04-06T14:56:54.037 に答える