Как правильно использовать RAII / уменьшить утечки памяти в этой ситуации

Я не уверен, как я могу использовать RAII в полной мере в моей ситуации. Это ситуация:

Я создаю базовый рендер. Геометрия описывается классом Geometry, к которому могут быть добавлены вершины. Для того чтобы объекты Geometry использовались для рендеринга, его сначала нужно скомпилировать (т. Е. Для геометрии будет создан VBO). Компиляция (и декомпиляция) объектов геометрии выполняется с помощью объекта Renderer, декомпиляция должна быть выполнена после того, как вы закончите работу с объектами геометрии. Если декомпиляция не будет выполнена, будут утечки памяти.

Вот пример того, что я описываю:

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, который скомпилировал геометрию.

Еще одна альтернатива, о которой я подумал, — заставить рендерер создавать геометрию, но это приведет к тому, что геометрические объекты будут размещаться динамически (то есть с новым), и это также выглядит довольно грязно.

Я также подумал о том, чтобы поместить объект дескриптора в геометрию, например unique_ptr, в абстрактный объект дескриптора.

например

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

Что на самом деле может работать, так как оно также может быть использовано для хранения GLuint внутри ручки. Хотя я не уверен, что это уместно, так как я мог бы просто декомпилировать геометрию напрямую через ссылку Renderer. то есть он будет делать то же самое, если я вызову его напрямую через деструктор.

Как мне правильно спроектировать это, чтобы случайно не декомпилировать геометрию?

2

Решение

Не ясно, кто должен нести ответственность за что в вашем дизайне. Это первый шаг в выяснении того, как использовать любую форму управления ресурсами: решить, кто за что отвечает.

Например, вы говорите, что «Геометрия описывается классом Geometry, к которому могут быть добавлены вершины». Хорошо, но как это относится к данным после компиляции? Если пользователь добавляет вершины в Geometry после компиляции эти вершины автоматически помещаются в скомпилированные данные? Или скомпилированные данные полностью отделены от Geometry класс после компиляции, так что меняется на Geometry класс не обновляет скомпилированные данные?

Для меня это звучит так, как будто вы объединяете две совершенно разные идеи: GeometryBuilder а также Renderable, GeometryBuilder это то, что вы кладете данные вершины. Затем вы берете это и создаете Renderable из этого. Renderable это оптимизированная форма данных, хранящихся в GeometryBuilder, Объект полностью независим от GeometryBuilder который создал это. Renderable это то, что вы можете сделать с

Итак, вы бы сделали это:

GeometryBuilder meshBuilder;

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

Renderable renderMesh = render.CreateRenderable(meshBuilder);

После этого meshBuilder не зависит от renderMesh, Вы можете удалить один и другой в порядке. Вы можете «скомпилировать» renderMesh несколько раз и получите идентичные копии одних и тех же данных. И так далее.

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 указатель внутри геометрии на него, компилятор).
Затем, при разрушении геометрии, если указатель не равен нулю, может потребоваться GeometryCompiler декомпилировать это.

3

RAII требует, чтобы деструкторы выполняли операции «отменить».

В вашем случае геометрия разрушена, но рендер выживет. Единственный триггер, который у вас есть, — это деструктор Geometry, который должен знать, что откомпилировать, чтобы вызвать его для декомпиляции.

Но поскольку геометрия не предназначена для того, чтобы узнать о редерсе, вам, скорее всего, нужен вспомогательный класс (назовем его Compile_guard) который должен быть создан сразу после завершения геометрии, принимая Geometry и Render в качестве параметров и вызывая Render :: compile при построении и Render :: decompile при уничтожении:

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;
};
0
По вопросам рекламы [email protected]