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

Описание проблемы
Рассмотрим пример: Вы хотите написать кроссплатформенный игровой движок. Допустим вы знаете С/С++, 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, а приложение. Прикладной код приложения (игровая логика, физика, ИИ, GUI, и пр.) — оформляется в виде отдельной библиотеки с определенным интерфейсом. Пример объявления библиотеки:
template class IGame < public: virtual ~IGame()<>//функция обработки одного такта игры virtual OutputData Tick(const InputData& input) = 0; >;
#include "IGame.h" // Коды клавиш для внутриигровой обработки enum KeyCode < KEY_UNKNOWN, KEY_ESCAPE, KEY_W, KEY_A, KEY_S, KEY_D, //. KEYS_TOTAL >; //Возможные коды ошибок игры enum ErrorCode < ERROR_UNKNOWN, //.. ERRORS_TOTAL >; //Управляющие команды игры enum CommandCode < COMMAND_UNKNOWN, COMMAND_CLOSE_GAME, //. COMMANDS_TOTAL >; //Входные данные по которым игра обрабатывает один свой такт struct InputData < float ProcessTimeInSec; float CursorPos_x; float CursorPos_y; bool PressedKeys[KeyCode::KEYS_TOTAL]; //. >; //Выходные данные такта игры struct OutputData < std::vectorСommands;//команды такта игры std::vector Уrrors;//ошибки такта игры std::vector VertexBuffer; //вершинный буфер, который заполняется //при просчете такта игры int VerticesCount; //кол-во вершин //. >; // реализация интерфейса прикладной библиотеки class Game : public IGame < public: //Инициализация игры Game(/*Сюда можно вставить параметры игры*/); //Деструктор освободит ресурсы игры virtual ~Game(); //функция обработки одного такта игры virtual OutputData Tick(const InputData& input) override; >;
Итак вы реализуете класс Game, пишите игровую логику, делаете это максимально кроссплатформенно и не привязываясь к определенным низкоуровневым API. Через структуру OutputData возвращаете данные геометрии, данные оцифрованного звука, желательно управляющие команды и ошибки.
UDP: Код представленный в статье лишь пример и модель, его цель объяснить идею, а не эффективно выполнится, поэтому я опустил различные оптимизации.
Дальше надо написать функцию main. Именно функция main и вызывает конкретные функции OpenGL/DirectX/Vulkan попутно вызывая код вашего приложения через функцию Tick. Из функции Tick возвращаются данные, которые конкретное API может использовать для рендеринга, воспроизведения звука и т.д. Общий код функции main может быть таким:
//#include ///. //#include ////. #include "Game.h" int main() < //код по открытию окна Window wnd = openWindow(. ); //назначить обработчики событий нажатия клавиш и движения мыши в wnd wnd.SetCallback(. ); //инициализация графического API InitializeGraphicAPI(. ); //также нужно сделать отображение кодов клавиш низкоуровневого API //в коды клавиш вашей прикладной библиотеки. Game::KeyCode KeyMap[Api.KeysCount()]; KeyMap[Key_Escape] = Game::KEY_ESCAPE; //. //создание игры Game game; //входные данные для просчета одного такта игры Game::InputData input;//не забудьте их заполнить начальными значениями //главный цикл приложения while(wnd.IsOpen()) < //вызываем оконные события wnd.pollEvents(); //заполняем вводные данные для такта игры input.Time = . ; input.CursorPos = . ; input.PressedKeys = . ; //просчитываем один такт игры //можно поиграть с многопоточностью и запускать просчет такта в новом потоке Game::OutputData output = game.tick(input) //вывод renderingWithApi(output); PlaySoundsWithApi(output); //. >return 0; >
Под каждую конфигурацию нужно написать свой main, который связывается с кодом вашей прикладной библиотеки.
Важно не забыть сделать отображения кодов нажатых клавиш, потому что у стороннего API и у вашей прикладной библиотеки коды клавиш могут быть разные. (Вы же определяете коды клавиш в прикладной библиотеке не привязываясь к конкретным API для работы с клавиатурой)
Заключение
Что это все дает?
- Переносимость кода под любые API с минимальным переписыванием кода,
- Ускорение темпа разработки, потому что можно очень много времени потратить на проектирование интерфейса IRenderer, а затем на дописывание/переписывание,
- Спорный плюс: если оформить вашу прикладную библиотеку в виде dll, то можно подключить эту dll в собственный main и выполнить портирование программы на API, которые использует ваша система. Но к сожалению формат dll не стандартизирован и не переносим с одной платформы на другую.
И это все можно применять не только к играм, потому что на сегодняшний день почти любое приложение использует графику, звуки и т.д. В статье я сделал упор именно на сферу графики и геймдева, потому что в данный момент занимаюсь этим.
Важно: данный подход стоит применять только в случае несовместимых API. Когда API совместимы (сокеты, потоки, менеджеры памяти и все что работает примерно одинаково), то лучше сделать интерфейс для API, а не приложения.
Честно, я не знаю каким решением пользуются крупные компании вроде EpicGames. Возможно с их количеством экспертов по всем областям программирования они могут продумать и спроектировать интерфейс IRenderer. Но я не думаю что это под силу небольшой группе программистов, которые далеко не эксперты в применении низкоуровневых API. Если у кого-то есть информация о том, как это делается в крупных и не очень компаниях, то я буду рад, если вы скинете это в комментариях.
Мне известно, что Dear ImGui и Nuklear используют аналогичный подход — основная логика выделена в отдельный модуль и есть ряд бэкендов (под каждую конфигурацию и без единого интерфейса), которые отвечают за вызов функций графического API или оконного API. Но их код не такой прозрачный и понятный, как я показал выше.
P.S.
Дабы продемонстрировать работоспособность решения, я собрал небольшой проект на GitHub — приложение которое создает окно и выводит разноцветный треугольник. Опять же цель проекта — объяснить идею, а не создать эффективную программу, поэтому код не везде оптимален.
Пользователь обнаружил, что приложения Google больше не удаётся вручную установить на смартфоны с SoC Kirin
Судя по всему, компания Google перешла к решительным действиям и перекрыла возможность установки фирменных приложений на устройства Huawei.

Huawei уже больше года вынуждена выпускать флагманы и более простые новые смартфоны без установленных приложений и сервисов Google из-за санкций со стороны США. Тем не менее, пользователи находили обходные пути и устанавливали приложения Google вручную. Теперь с установкой вручную появились сложности.
На проблему обратил внимание один из пользователей социального новостного ресурса Reddit. Он сообщил, что годами вручную загружал установочные файлы APK для Google Maps (Google Карты), а также других приложений, и вручную устанавливал их. Тем не менее, недавно этот трюк впервые потерпел неудачу.

При попытке установить свежую версию Google Maps из APK на смартфоне появилось сообщение об ошибке установки, а в качестве причины указывалось следующее: «Это приложение не совместимо с CPU вашего устройства», хотя прошлая версия этого же приложения на этом же смартфоне не имела такой проблемы.
Пользователь протестировал обновлённые APK свежих версий приложений Google на «гораздо более старом» смартфоне Samsung и установка прошла «идеально». Он также не поленился и попробовал установить эти же обновлённые приложения Google на другие устройства Huawei, включая Huawei P30, P20, P40, и так далее. И во всех случаях установка на смартфоны Huawei терпела крах из-за «несовместимого CPU».
Huawei и Google пока не давали официальных комментариев по этому поводу.
Дополнено: российский офис Huawei опровергает проблемы с установкой приложений Google на смартфоны с процессорами Kirin, уточняя, что может комментировать только ситуацию в России.
Android Studio 2.2.2 Ошибка «No CPU/ABI system image available for this target»
Создаю программу впервые. При помощи AVD manager хочу создать AVD но поле CPU/ABI остаётся пустым, не высвечивается кнопка ОК и пишет:
No CPU/ABI system image available for this target
Отслеживать
6,488 6 6 золотых знаков 24 24 серебряных знака 31 31 бронзовый знак
задан 19 ноя 2016 в 0:51
1 1 1 бронзовый знак
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Вам нужно скачать системный образ Android. Делается это, насколько я помню, в SDK Manager’e.
Отслеживать
ответ дан 19 ноя 2016 в 0:53
FullyRetarded FullyRetarded
121 1 1 серебряный знак 11 11 бронзовых знаков
если это комментарий, отправьте тревогу модераторам, чтобы перенесли ответ в комментарий. если это решение, то все хорошо(только лишний текст удалите).
19 ноя 2016 в 13:29
На мой взгляд, это вполне ответ. Что тут ещё можно добавить-то, скриншот кнопки «Download»?
19 ноя 2016 в 15:18
Впрочем, можно добавить пару слов про CPU/ABI system image, что это такое и почему нужно качать отдельно. И если нужно будет выбрать из нескольких — то как выбрать.
19 ноя 2016 в 15:18
Если речь о eclipse, то нужно зайти Window-> Android SDK manager и установить для нужних платформ «ARM EABI v7a System Image». Тут есть то, что Вам нужно https://stackoverflow.com/questions/13488419/unable-to-create-android-virtual-device.
Отслеживать
ответ дан 4 мая 2017 в 6:50
Уточните ваш вопрос — «для нужних платформ «ARM EABI v7a System Image»» — чем это поможет?
4 мая 2017 в 7:20
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.12.7.2316
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Как установить несовместимые с Android-устройством приложения в обход ограничений Google Play
Порой случаются ситуации, когда мы заходим на Google Play, находим какое-либо приложение, пытаемся его скачать, а система выдает ответ – «Приложение не совместимо с Вашим устройством».
Такое может произойти по двум причинам:
1. Ваше устройство находится в том регионе, в котором запрещено использование этого приложения. Иногда сложно сказать по какой причине это происходит, но создатели софта ставят на его эксплуатацию региональные ограничения.
2. Вы используете несовместимую с приложением версию Android. К примеру, на Вашем гаджете версия 4.0, а необходима 5.0 или выше.
Но не расстраивайтесь, прочитав данную инструкцию, Вы узнаете, как обойти ограничение Google Play и установить приложения, которые без использования метода, описанного ниже, не установились бы.
Приложение ZenMate VPN Security & Privacy
В основе способа — использование простейшей программы ZenMate VPN Security & Privacy, предназначенной для обеспечения анонимности и безопасности интернет-серфигнга. Данное приложение дает возможность имитировать выход в сеть из США, Великобритании, Германии, Гонконга или Швейцарии с соответствующим изменением ip-адреса и отменой блокировки Google Play.
Порядок действий:
1. Скачиваете на смартфон или планшет бесплатную программу ZenMate по ссылке:
2. Производите установку и запуск программы.
3. Регистрируетесь — указываете любой e-mail и пароль (подтверждения или активации аккаунта с почты не требуется).

4. Указываете любую из предлагаемых локаций.

5. Подтверждаем полученный от запрос на подключение (нажать «ОК»).

6. Заходите в Google Play и спокойно скачиваете или обновляете все, что вам необходимо.
Загрузите .apk файл, используя специальные сайты
Еще более простым и, возможно, более надежным решением является использование веб-сайтов, которые анализируют содержимое Google Play позволяют загружать файлы с расширением .apk напрямую.


Вы просто должны будете скопировать адрес страницы с приложением в Google Play и скачать .apk файл на ваш смартфон или компьютер. Если Вы загрузили .apk файл на компьютер, просто переместите его в память Вашего устройства и, используя файловый менеджер, откройте и установите его.
Используйте другие магазины приложений
Если Вы столкнулись с тем, что не можете загрузить какое-либо приложение, используйте для его поиска и загрузки другие магазины, например Amazon App Store. Кроме магазина Amazon, в Интернете сети есть целый ряд сторонних магазинов приложений, таких как GetJar , SlideME, F-Droid, Mobogenie, Samsung Galaxy Apps store, и множество других, где могут содержаться приложения, которые Вы ищите. Помните правило – кто ищет, тот обязательно найдет!