2

自分の状況でRAIIを最大限に活用する方法がわかりません。これは状況です:

基本的なレンダラーを作成しています。Geometryは、頂点が追加されている可能性のあるGeometryクラスによって記述されます。Geometryオブジェクトをレンダリングに使用するには、最初にコンパイルする必要があります(つまり、Geometry用にVBOが作成されます)。ジオメトリオブジェクトのコンパイル(および逆コンパイル)は、レンダラーオブジェクトを介して実行されます。逆コンパイルは、ジオメトリオブジェクトの処理が完了したら実行する必要があります。逆コンパイルを行わないと、メモリリークが発生します。

これが私が説明していることの例です:

Renderer renderer; // the renderer object

{
    Geometry geometry;

    // add vertices
    // and play around with material, etc.

    if(!renderer.compile(geometery); // compile the geometery, so we can use it
    {
        cerr << "Failed to compile geometry!\n";
    }

    // now we can use it...
    renderer.render(geometry, RenderType::TriangleStrips);

} // if I don't call renderer.decompile(geometry) here, I will get a leak

私がやろうとしているのは、レンダラーに逆コンパイルするように明示的に指示せずに、ジオメトリを逆コンパイルすることです。これは、単にメモリリークを減らすためです。私が最初に考えたのはRAIIを使用することでしたが、そうすると、GeometryクラスにはRendererクラスが必要になり、かなり面倒に見えます。ジオメトリをコンパイルしたRendererオブジェクトへの参照が必要になるためです。

私が考えたもう1つの方法は、レンダラーにジオメトリを作成させることでしたが、これにより、ジオメトリオブジェクトが動的に(つまり、newで)割り当てられ、非常に面倒に見えます。

また、一意のハンドルオブジェクトへのunique_ptrなど、ジオメトリ内にハンドルオブジェクトを配置することも考えました。

例えば

class GeometeryHandle
{
   virtual ~GeometeryHandle() = 0; 
};

GLuintハンドル内に格納するためにも使用される可能性があるため、実際に機能する可能性があります。ただし、レンダラー参照を介してジオメトリを直接逆コンパイルできるため、これが適切かどうかはわかりません。つまり、デストラクタを介して直接呼び出す場合も同じことを行います。

誤ってジオメトリを逆コンパイルしないように、これを適切に設計するにはどうすればよいですか?

4

3 に答える 3

3

方法1:

ヘルパークラスを使用できます。

class CompileHandler
{
    private:
        Geometry& g;
        Renderer& r;
    public:
        CompileHandler(Geometry& _g, Renderer& _r) : g(_g), r(_r) 
        {
        }
        ~CompileHandler()
        {
            r.decompile(g);
        }
};

そして、あなたはそれを次のように使うことができます:

{
    Geometry geometry;
    CompileHandler ch(geometry,renderer);

    // add vertices
    // and play around with material, etc.

    if(!renderer.compile(geometery); // compile the geometery, so we can use it
    {
        cerr << "Failed to compile geometry!\n";
    }

    // now we can use it...
    renderer.render(geometry, RenderType::TriangleStrips);
// decompilation is automatic on destruction of the CompileHandler object
}

方法2:

より堅牢な階層を作成します。

GeometryCompiler
   ^
   |
   | inherits
   |
Renderer

ジオメトリのコンパイル時に、ジオメトリコンパイラ(ここではレンダラー)は、ジオメトリがコンパイルされたことをジオメトリに通知します(そしてGeometryCompiler、ジオメトリ内にポインタを設定します)。次に、ジオメトリの破棄時に、ポインタがnullでない場合は、GeometryCompilerを逆コンパイルする必要があります。

于 2013-02-24T13:00:30.960 に答える
3

誰があなたのデザインの何に責任があるのか​​は明確ではありません。これは、あらゆる形式のリソース管理を採用する方法を理解するためのステップ1です。つまり、誰が何に対して責任を負うかを決定します。

たとえば、「GeometryはGeometryクラスによって記述され、頂点が追加されている可能性があります」と言います。OK、しかしこれはコンパイル後のデータとどのように関連していますか?コンパイル後にユーザーが頂点を追加した場合、Geometryそれらの頂点はコンパイルされたデータに自動的に配置されますか?または、コンパイルされたデータがコンパイル後にクラスから完全に分離されているGeometryため、クラスを変更してGeometryもコンパイルされたデータは更新されませんか?

あなたが2つの非常に異なるアイデアを混同しているように私には聞こえます:GeometryBuilderRenderableGeometryBuilder頂点データを入れるものです。次に、それを取得して作成しますRenderable。はRenderable、に格納されているデータの最適化された形式ですGeometryBuilder。オブジェクトは、それを作成したオブジェクトから完全に独立しGeometryBuilderています。これRenderableが実際にレンダリングできるものです。

だからあなたはこれをするでしょう:

GeometryBuilder meshBuilder;

meshBuilder.AddVertex(...);
...

Renderable renderMesh = render.CreateRenderable(meshBuilder);

この時点以降、meshBuilderはに依存しませんrenderMesh。あなたは一方と他方の罰金を削除することができます。複数回「コンパイル」renderMeshして、同じデータの同一のコピーを取得できます。などなど。

于 2013-02-24T13:12:38.510 に答える
0

RAIIでは、「元に戻す」操作をインストラクターが実行する必要があります。

この場合、ジオメトリは破棄されますが、レンダリングは存続します。あなたが持っている唯一のトリガーは、Geometryデストラクタです。これは、コンパイルされたレンダリングを知っている必要があり、彼を呼び出して逆コンパイルします。

ただし、ジオメトリは再定義について知ることを目的としていないためCompile_guard、ジオメトリの完了直後にインスタンス化する必要があるヘルパークラス(これを呼び出します)が必要になる可能性があります。このクラスは、ジオメトリとレンダーをパラメーターとして取り、Render::compileを呼び出します。構築時およびRender::破棄時に逆コンパイル:

Renderer renderer; // the renderer object

{
    Geometry geometry;

    // add vertices
    // and play around with material, etc.

    Compile_guard guard(render, geometry);

    if(!guard); // Invalid compilation ....
    { 
        cerr << "Failed to compile geometry!\n";
        return; // this is exception safe!
    }

    // now we can use it...
    renderer.render(geometry, RenderType::TriangleStrips);

} //here guard will decompile

Compile_guardについては、次のようになります。

class Compile_guard
{
public:
   Compile_guard(Render& r, Geometry& g) :render(&r), geometry(&g), good(false)
   { good = render->compile(*geometry); }

   ~Compile_guard()
   { if(good) render->decompile(*geometry); }

   explicit operator bool() const { return good; }

   Compile_guard(const Compile_guard&) =delete;  //just avoid copy and assign.
   Compile_guard& operator=(const Compile_guard&) =delete;
private:
   Render* render;
   Geometry* geometry;
   bool good;
};
于 2013-02-24T13:08:46.250 に答える