цель c — Как я могу сохранить идентификатор & lt; MTLBuffer & gt; внутри кода C ++?

Я пишу кроссплатформенный движок для рендеринга. Я пытаюсь создать кроссплатформенную структуру, которая по существу управляет типом геометрии, которая рисуется.

Цель состоит в том, чтобы существовал класс c ++, который содержит void * для блока памяти, выделенного для буфера, а затем этот указатель передается в MTLBuffer или Vulcan Buffer для использования при рендеринге. Таким образом, одно из полей этого класса должно быть буфером, но в кроссплатформенном смысле.

Для части моего рисунка код должен выглядеть так

func draw() {
CrossPlatformBuffers* buffs = // preset list
for (buffer in buffs {
PlatformSpecificEngine.drawWith((PlatformSpecificBuffer)buffs->buffer)
}
}

По сути, мне нужно иметь возможность хранить свой MTLBuffer как void * в классе c ++. Это сбивает с толку меня, так как я не совсем уверен, как c ++ играет с target-c ARC или что именно должен означать id.

Буду ли я сталкиваться с какими-либо проблемами, если я просто добавлю идентификатор в void * и передам его в класс c ++, как позже назвал delete для него?

2

Решение

Здесь нужно учесть несколько моментов:

Ваш указатель на объект MTLBuffer делает не указать на содержимое объекта

MTLBuffer это металлический каркасный объект, который управляет буфером памяти на GPU. Адрес, который у вас есть, это просто адрес этого объекта. Для некоторых буферов Metal предоставляет способ доступа к содержимому буфера из CPU, используя [MTLBuffer contents] метод. contents возвращает void * что вы можете напрямую использовать для чтения и записи из вашего буфера со следующими оговорками:

Содержимое вашего MTLBuffer не всегда доступно из CPU

Это зависит от того, на какой платформе вы находитесь. Если вы работаете исключительно на iOS / tvOS, просто создайте свой MTLBuffer с MTLStorageModeShared режим хранения, и вы должны быть в порядке — Metal обеспечит синхронизацию данных, которые вы видите на процессоре, с видом графического процессора. В MacOS это зависит от того, какой графический процессор вы используете, поэтому здесь есть некоторые дополнительные тонкости. Сейчас я предполагаю, что мы говорим только о iOS / tvOS.

Есть несколько способов объединить Objective-C с кодом C ++

Один из вариантов — создать файлы Objective-C ++ (файлы .mm) и поместить весь свой специфичный для Metal код в подкласс внутри этих файлов .mm. Это позволило бы вам воспользоваться преимуществами ARC (автоматического подсчета ссылок) Objective-C и по-прежнему создавать красивые, обертки C ++. В файле заголовка для вашего класса Objective-C ++ вы должны сделать что-то вроде этого:

class MetalBuffer : GenericBuffer
{
private:
#ifdef __OBJC__
id <MTLBuffer> metalBuffer;
#else
void *internalMetalBuffer;
#endif
}

Это позволит вашим обычным (не Objective-C ++) классам включать заголовок Objective-C ++. Я сделал «специальный» управляемый указатель private чтобы никто не пытался получить к нему доступ из-за пределов класса Objective-C ++, поскольку это, очевидно, было бы плохой идеей.

Если этот подход не подходит, вы можете просто делать все на C ++, но тогда вам придется вручную отслеживать ссылки на ваши объекты Objective-C:

Если вы должны хранить ваши объекты в коде C ++ как пустые указатели, вам потребуется ручной подсчет ссылок

Objective-C использует ARC для отслеживания использования объекта и автоматического освобождения объектов по мере необходимости. Если вы пытаетесь управлять всем этим в C ++, вам нужно будет вручную управлять ссылками на ваши объекты (например, ваши MTLBuffer и не только) Это делается сообщением ARC, что вы хотите набрать управляемый Objective-C id возражает против регулярных указателей Си.

После создания вашего экземпляра MTLBuffer, ты можешь использовать CFBridgingRetain() на вашем объекте, который теперь позволяет хранить его как void * (не путать с void * вы схватили это указывает на содержание вашего буфера!) в вашем классе C ++. Когда вы закончите, используя MTLBuffer, ты можешь позвонить CFRelease() на ваше void * чтобы освободить это. Вам не нужно беспокоиться об освобождении contents буфер — основная память будет освобождена автоматически, как только MTLBuffer объект освобожден (например, когда вы звоните CFRelease()).

Обратите внимание, что вы можете использовать CFBridgingRelease() когда вам нужно вызвать функции Objective-C, которые используют ваш MTLBuffer объект (такой как setFragmentBufferи т. д.) Подумайте о CFBridgingRelease() как конвертер, который возвращает ваш объект обратно в ARC, но учтите, что он включает ручное освобождение, а это означает, что как только Metal завершит работу с вашим объектом, он будет автоматически освобожден.

Если вы хотите, чтобы ваш объект жил за пределами текущего запроса Metal, вам следует сохранить другой указатель на него, используя CFBridgingRetain(),

Опять же, это последнее средство — я бы не рекомендовал этот маршрут.

Удачи!

4

Другие решения

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

class Renderer {
public:
Renderer();
~Renderer();

virtual void* allocateBuffer(const size_t numBytes) = 0;
virtual void renderWorld() = 0;
... etc.
};

Тогда у вас будет 2 платформо-зависимых класса: VulkanRenderer а также MetalRenderer:

class VulkanRenderer: public Renderer {
public:
VulkanRenderer();
~VulkanRenderer();

virtual void* allocateBuffer(const size_t numBytes);
virtual void renderWorld();
... etc.
};

а также

class MetalRenderer: public Renderer {
public:
MetalRenderer();
~MetalRenderer();

virtual void* allocateBuffer(const size_t numBytes);
virtual void renderWorld();
... etc.
};

Ваш файл реализации для MetalRenderer классом будет файл .mm вместо файла .cpp, указывающий, что это файл Objective-C ++, и позволяющий объекту C ++ содержать объекты Objective-C.

Ни один из вашего другого кода не должен иметь дело ни с MetalRenderer или же VulkanRenderer но вместо этого просто Renderer,

1

По вопросам рекламы [email protected]