Проблема Несовместимых Api Или Как Легко Поддерживать Совместимость С Opengl, Directx И Vulkan

В программировании очень популярна техника создания программных интерфейсов — API. Этот прием очень полезен, чтобы скрыть все тонкости реализации и не обременять ими обывателя.

Но бывают случаи, когда вам нужно поддерживать в своем коде несколько API, выполняющих одну и ту же задачу, с минимальным переписыванием кода.

Например: поддержка работы игры (движка) на различных графических API: DirectX, OpenGL, Vulkan. В этой статье представлены мысли о том, как это сделать.



Проблема несовместимых API или как легко поддерживать совместимость с OpenGL, DirectX и Vulkan



Описание проблемы

Давайте рассмотрим пример: вы хотите написать кроссплатформенный игровой движок.

Допустим, вы знаете C/C++, OpenGL, DirectX, Vulkan. Поскольку движок кроссплатформенный, вы сначала думаете «а как насчет того, чтобы сделать его на OpenGL», и у вас все получается хорошо, пока вам не закрадывается мысль, что, может быть, OpenGL не идеален для Windows? Почему крупные компании поддерживают сразу все API, а UnrealEngine для Windows компилируется с использованием DirectX (по умолчанию), а не OpenGL/Vulkan. И теперь перед вами стоит задача как-то обобщить все API. Вы пытаетесь написать интерфейс — IRenderer и классы-потомки, которые бы сами инициализировали необходимый API и отвечали за отрисовку.

Но проблема не в этом, OpenGL не может работать без созданного окна (скрытое окно тоже окно), а DirectX и vulkan могут. И тут два решения: либо сделать IRenderer так, чтобы он отвечал еще и за создание окна (классы несут дополнительную ответственность), либо привязать IRenderer к какому-то уже созданному окну (но можно рендерить и без окон!!! Не универсально!!).

В общем, об IRenderer можно и не думать сразу, API слишком отличаются друг от друга, хотя казалось бы, они решают одну и ту же задачу — доступ к видеокарте и обеспечение отрисовки.

Но я все равно хочу написать игру и хочу, чтобы она работала под разными платформами, с разными несовместимыми API, а еще я хочу, чтобы код был читаемым, минимальным и не переписывался по 10 раз.

Я постараюсь ответить на этот вопрос.

Таким образом, несовместимые API — это API, которые решают одну и ту же задачу, но имеют совершенно разные подходы к решению и, следовательно, разный набор функций.

Подходы настолько разные, что обобщить работу под один интерфейс невозможно.

А совместимые API — это API, состоящие из похожих функций (сигнатуры похожи).

Примеры совместимых API: сокеты, потоки, кучи, файлы.

Их работу легко свести в один интерфейс, поэтому для работы с ними написано так много библиотек.



Предлагаемое решение

А решение простое: обобщать не API, а приложение.

Код прикладного приложения (игровая логика, физика, AI, GUI и т.д.) оформлен в виде отдельной библиотеки со специфическим интерфейсом.

Пример объявления библиотеки:

   

template<typename InputData, typename OutputData> class IGame { public:

Теги: #Разработка игр #vulkan #api #Системный анализ и проектирование #дизайн #дизайн и рефакторинг #DirectX #gamedev #opengl
Вместе с данным постом часто просматривают: