Как я могу хранить идентификатор <MTLBuffer>внутри кода C ++? - PullRequest
0 голосов
/ 27 декабря 2018

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

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

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

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

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

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

Ответы [ 2 ]

0 голосов
/ 27 декабря 2018

Здесь необходимо учесть несколько моментов:

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

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

Содержимое вашего MTLBuffer не всегда может быть доступно из ЦП

Это зависит от того, на какой платформе вы находитесь.Если вы работаете исключительно на 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, вы должны сохранить еще один указатель на него, используя CFBridgingRetain().

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

Удачи!

0 голосов
/ 27 декабря 2018

Мне кажется, что вы используете неправильную абстракцию.Это часто решается путем использования базового класса, который реализует то, что вы хотите сделать на высоком уровне, и наличия подкласса для каждой платформы, которая выполняет определенную работу.Так что в вашем случае у вас может быть что-то вроде базового класса 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-С объекты.

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

...