1. Введение

Если есть что-то, что мне нравится больше, чем компьютерная графика, так это быстрая компьютерная графика…

К концу этого поста вы узнаете, как создать и связать Google ANGLE с Raylib, что даст как минимум 3-кратное увеличение исходной частоты кадров на компьютерах Mac с чипами M1/M2. По сути, это та же низкоуровневая графическая библиотека, на которой работает Google Chrome! (И любые браузеры на основе хрома)

Этот учебник предполагает некоторые базовые знания о компиляции программного обеспечения C/C++ и некоторый опыт работы с командной строкой.

Предпосылки

Вам также понадобятся следующие предварительные условия:

  1. Инструменты командной строки Apple Developer (обязательно)
  2. Код VS (рекомендуется)
  3. Домашний пивовар (рекомендуется)
  4. Ниндзя brew install ninja
  5. CMake brew install cmake
  6. Python 3 (обязательно)
  7. Гит (обязательно)

Нетерпеливые могут сразу перейти к Краткой версии в разделе 3, где показано, как украсть libEGL.dylib и libGLESv2.dylib из Chrome.

Если вам нужно более полное руководство и вы хотите узнать, как создать raylib и ANGLE с нуля и связать их вместе в хороший проект cmake, ознакомьтесь с разделом 4.

2. Зачем вообще использовать ANGLE?

Кажется, в наши дни у каждого есть свой собственный графический API. Microsoft видит будущее в Direct3D. Kronos (разработчик спецификаций OpenGL и Vulkan) считает Vulkan решением следующего поколения. С другой стороны, Apple, верная своей природе создания полнофункциональных, аппаратных и программных продуктов, выбрала свободу и гибкость и решила, что им тоже нужен собственный блестящий ускоренный графический API — Metal

NVidia поддерживает столько, сколько может, они просто хотят продавать оборудование.

Как правило, OpenGL имеет довольно хорошую кроссплатформенную поддержку. И это обычно подходит для чего-либо кроссплатформенного. Но поскольку Apple сосредоточилась на Metal и фактически отказалась от поддержки OpenGL в 2018 году, производительность драйверов OpenGL на Mac не получила большой популярности и, вероятно, не будет в будущем. Хорошая новость заключается в том, что Metal может получить примерно в 10 раз больше вызовов отрисовки, чем OpenGL, особенно на современных чипах M1/M2.

Со всеми этими различными API начинают появляться уровни абстракции. На самом деле, Google разрабатывает такой API, который присутствует во всех браузерах на основе Chromium, и называется ANGLE. Он предоставляет внешний интерфейс, совместимый с OpenGL ES 2.0, с внутренними интерфейсами для всех основных собственных графических API.

Короче говоря, вы можете получить ОГРОМНОЕ увеличение скорости при использовании Metal через ANGLE на платформах MacOS. Частота кадров обычно удваивается, уменьшается или даже больше, чем у решений на основе OpenGL.

3. Быстрый старт (короткая версия с использованием ANGLE из браузера)

Приветствуйте Peter0x44 на Github и сервере Discord Raylib за указание на это, но на самом деле вы можете просто получить ANGLE в виде libEGL.dylib и libGLESv2.dylib из браузера на основе Chromium, который у вас, вероятно, уже есть!

Итак, поехали.

Шаг 1. Загрузите libEGL.dylib и libGLESv2.dylib из Chrome

Например, используя Chrome, давайте покопаемся в .app и возьмем эти файлы. Мы будем использовать команду find:

cd /Applications/Google\ Chrome.app
find ./ -name 'libEGL.dylib'

Ваш вывод должен выглядеть примерно так. Сладкий.

.//Contents/Frameworks/Google Chrome Framework.framework/Versions/113.0.5672.126/Libraries/libEGL.dylib
.//Contents/Frameworks/Google Chrome Framework.framework/Versions/114.0.5735.90/Libraries/libEGL.dylib

Теперь вы можете найти libGLESv2.dylib:

find ./ -name 'libGLESv2.dylib'

Бум:

.//Contents/Frameworks/Google Chrome Framework.framework/Versions/113.0.5672.126/Libraries/libGLESv2.dylib
.//Contents/Frameworks/Google Chrome Framework.framework/Versions/114.0.5735.90/Libraries/libGLESv2.dylib

Шаг 2. Скопируйте libEGL.dylib и libGLESv2.dylib raylib в папку проекта

cd <your project>
cp "/Applications/Google Chrome.app/Frameworks/Google Chrome Framework.framework/Versions/114.0.5735.90/Libraries/libGLESv2.dylib" ./
cp "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/114.0.5735.90/Libraries/libEGL.dylib" ./

Шаг 3. Установите связь с raylib

Вот однострочный вариант, если вы используете статическую сборку raylib:

g++ -std=c++14 -I/path/to/raylib/include -o main main.cpp /path/to/raylib.a /path/to/libEGL.dylib /path/to/libGLESv2.dylib

4. Немного более длинная версия

Одна команда g++ или gcc подходит для небольших проектов, но для большинства проектов вам понадобится система сборки. Мы будем использовать CMake, так как он кажется стандартом де-факто для проектов C/C++.

4.1 Структура папок и настройка

Шаг 1. Настройка папки

Идите вперед и выберите корневой проект на своем компьютере. Я предлагаю что-то вроде ~/code/raylib-angle или что-нибудь попроще.

Поместите в папку следующее:

.
├── CMakeLists.txt
├── src
│   └── main.cpp
└── vendor

После того, как мы настроим подмодули git и добавим исходный код, он должен выглядеть примерно так:

.
├── CMakeLists.txt
├── build
├── src
│   └── main.cpp
└── vendor
    ├── angle
    ├── raygui
    └── raylib

4.2 Подмодули Git

Хотя вы, безусловно, можете связываться со статическими сборками raylib, мне нравится хранить все в комплекте в виде исходного кода, который вы можете скомпилировать без суеты. Итак, давайте добавим raylib и raygui, если вам хочется приключений.

Добавить raylib в качестве подмодуля в vender/raylib

git submodule add https://github.com/raysan5/raylib.git vendor/raylib

Добавить raylib в качестве подмодуля в vendor/raygui

git submodule add https://github.com/raysan5/raygui.git vendor/raygui

Добавить ANGLE в качестве подмодуля к vendor/angle

git submodule add https://chromium.googlesource.com/angle/angle vendor/angle

Инициализировать подмодули Это нужно делать только в том случае, если вы клонируете корневой репозиторий, но в качестве напоминания на будущее:

git submodule update --init --recursive

Круто, теперь давайте построим ANGLE

4.2 Угол построения

Я еще не совсем привязал это к своему CMakeLists.txt, так как Angle использует отдельную систему сборки, ninja (кстати, она безумно быстрая), поэтому пока мы будем собирать ANGLE отдельно.

Шаг 1. УГОЛ начальной загрузки

cd vendor/angle
python3 scripts/bootstrap.py
gclient sync

Шаг 2. Создание файлов сборки Ninja

gn gen out/Release --args='is_debug=false'

Здесь мы сгенерировали файлы сборки для релизной (оптимизированной) сборки, но вы можете создать отладочную сборку, удалив --args='is_debug=false' and runninggn gen out/Debug`

Шаг 2.5. (Необязательно) сборка только с металлической серверной частью

Если вы хотите уменьшить размер dylib примерно с 11 МБ до 8 МБ, вы можете отключить серверные части, отличные от Mac, для более компактной сборки.

Отредактируйте out/Release/args.gn и добавьте следующее содержимое:

angle_enable_d3d9 = false
angle_enable_d3d11 = false
angle_enable_gl = false
angle_enable_null = false
angle_enable_metal = true
angle_enable_vulkan = false
angle_enable_essl = false
angle_enable_glsl = true
is_debug = false
is_component_build = false

Шаг 3. Скомпилируйте угол

Этот шаг займет горячую минуту. На моем Macbook M1 Pro 2021 года это заняло около 10 минут. Отладочная сборка заняла примерно половину этого времени.

ninja -j 10 -k1 -C out/Release

Здесь я использовал все 10 процессоров. Используйте что-то подходящее для вашей системы.

По завершении нужные общие библиотеки (dylib) libEGL.dylib и libGLESv2.dylib находятся в vendor/angle/out/Release/ копии корневого каталога вашего проекта: ~/code/raylib-angle (или любого другого)

4.3 Создание проекта

Теперь давайте быстро настроим CMakeLists.txt и скомпилируем raylib и связанный с ним код, например glfw.

Шаг 1. Создайте файл CMakeLists.txt

Добавьте это к <project-root>/CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(raylib-starter)
set(CMAKE_CXX_STANDARD 14)
# Add raylib subdir
add_subdirectory(vendor/raylib)
# Add my sources
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable(${PROJECT_NAME} ${SOURCES})
# Define path to ANGLE libraries
set(ANGLE_LIBRARY_DIR vendor/angle/out/Release)
# Find ANGLE libraries
find_library(ANGLE_GLESv2_LIBRARY libGLESv2.dylib PATHS ${ANGLE_LIBRARY_DIR})
find_library(ANGLE_EGL_LIBRARY libEGL.dylib PATHS ${ANGLE_LIBRARY_DIR})
# Add raylib include directory
target_include_directories(${PROJECT_NAME} PRIVATE vendor/raylib/src)
# Add raygui include directory
target_include_directories(${PROJECT_NAME} PRIVATE vendor/raygui/src)
# Add ANGLE include directory
target_include_directories(${PROJECT_NAME} PRIVATE vendor/angle/include)
# Link against raylib and ANGLE libraries
target_link_libraries(${PROJECT_NAME} raylib ${ANGLE_EGL_LIBRARY} ${ANGLE_GLESv2_LIBRARY})

Шаг 2. Создайте <project-root>src/main.cpp

Вот немного кода Hello World raylib:

#include "raylib.h"
int main(void)
{
    const int screenWidth = 800;
    const int screenHeight = 450;
    InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
    SetTargetFPS(60); 
    while (!WindowShouldClose()) 
    {
        BeginDrawing();
            ClearBackground(RAYWHITE);
            DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
            DrawFPS(10, 10);
        EndDrawing();
    }
    CloseWindow();
    return 0;
}

Шаг 3. Создание файлов сборки CMake

cmake -DCUSTOMIZE_BUILD=ON -DOPENGL_VERSION="ES 2.0" -B build
cmake --build build

Флаги -DCUSTOMIZE_BUILD=ON -DOPENGL_VERSION="ES 2.0" являются ключевыми. Они говорят Tell raylib искать реализацию OpenGL ES, которую предоставит ANGLE. Вы можете узнать больше о флагах cmake на Raylib Wiki

Шаг 3. Проверка

Здесь вы можете видеть, что наш проект raylib использует реализацию Google OpenGL ES, поддерживаемую Metal.

λ ~/code/projects/raylib-angle-test/ master* ./r
INFO: Initializing raylib 4.6-dev
INFO: Supported raylib modules:
INFO:     > rcore:..... loaded (mandatory)
INFO:     > rlgl:...... loaded (mandatory)
INFO:     > rshapes:... loaded (optional)
INFO:     > rtextures:. loaded (optional)
INFO:     > rtext:..... loaded (optional)
INFO:     > rmodels:... loaded (optional)
INFO:     > raudio:.... loaded (optional)
INFO: DISPLAY: Device initialized successfully
INFO:     > Display size: 1920 x 1080
INFO:     > Screen size:  800 x 450
INFO:     > Render size:  800 x 450
INFO:     > Viewport offsets: 0, 0
INFO: GLAD: OpenGL ES2.0 loaded successfully
INFO: GL: Supported extensions count: 104
INFO: GL: OpenGL device information:
INFO:     > Vendor:   Google Inc. (Apple)
INFO:     > Renderer: ANGLE (Apple, Apple M1 Pro, OpenGL 4.1 Metal - 76.3)
INFO:     > Version:  OpenGL ES 3.0.0 (ANGLE 2.1.21199 git hash: b0e9bbd79fb6)
INFO:     > GLSL:     OpenGL ES GLSL ES 3.00 (ANGLE 2.1.21199 git hash: b0e9bbd79fb6)
INFO: GL: VAO extension detected, VAO functions loaded successfully
INFO: GL: NPOT textures extension detected, full NPOT textures supported
INFO: TEXTURE: [ID 1] Texture loaded successfully (1x1 | R8G8B8A8 | 1 mipmaps)
INFO: TEXTURE: [ID 1] Default texture loaded successfully
INFO: SHADER: [ID 1] Vertex shader compiled successfully
INFO: SHADER: [ID 2] Fragment shader compiled successfully
INFO: SHADER: [ID 3] Program shader loaded successfully
INFO: SHADER: [ID 3] Default shader loaded successfully
INFO: RLGL: Render batch vertex buffers loaded successfully in RAM (CPU)
INFO: RLGL: Render batch vertex buffers loaded successfully in VRAM (GPU)
INFO: RLGL: Default OpenGL state initialized successfully
INFO: TEXTURE: [ID 2] Texture loaded successfully (128x128 | GRAY_ALPHA | 1 mipmaps)
INFO: FONT: Default font loaded successfully (224 glyphs)
INFO: TIMER: Target time per frame: 16.667 milliseconds
INFO: SYSTEM: Decompress data: Comp. size: 1059 -> Original size: 131072
INFO: TEXTURE: [ID 3] Texture loaded successfully (256x256 | GRAY_ALPHA | 1 mipmaps)

4.4 Производительность

С УГЛОМ

Да… Это 11 тысяч + FPS, вы правильно видите. У меня даже есть браузер Chrome с Spotify и VS Code Running. Конечно, raylib здесь мало что делает, но все же!

Без УГЛА

Да… сказал Наф. Это почти в 4 раза быстрее. ANGLE определенно стоит того на MacOS и чипах M1/M2.

6. Устранение неполадок

Если вы получаете ошибку, например, при компиляции следующим образом:

error: incompatible pointer types assigning to 'int *' from 'bool *'
    if (active == NULL) active = &temp;

Просто отредактируйте эту строку в vendor/raygui/src/raygui.h и измените эти строки:

bool temp = false;
    if (active == NULL) active = &temp;

to

bool temp = false;
    if (active == NULL) active = (int *)&temp;

Это приводит temp, который является bool * к int *

Вы также можете изменить bool temp на int temp

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

Заключение

Это пока все! ANGLE может не дать такого же улучшения скорости на других платформах, таких как Windows и Linux, но я могу ошибаться. Поддержка ANGLE в Raylib все еще экспериментальная, но это в значительной степени подтверждает, что она работает!

Я надеюсь вернуться с еще несколькими рецензиями на Raylib. На данный момент потрясающая экосистема Go checkout ray демонстрирует любовь к проекту по адресу:

Если вам нужен код стартового пакета, который мы установили сегодня, вы можете найти его на https://github.com/grplyler/raylib-cmake-starter.

До скорого! Продолжай программировать!

-Райан «Вортигон» Плайлер