Portieren einer C++-SDL-Anwendung auf Android

In diesem Beitrag beschreibe ich meine Erfahrungen mit der Portierung eines Prototyps eines 3D-Editors Cube Art Projectauf Android.
Schauen wir uns zunächst das Ergebnis an: Im Emulator läuft ein Editor mit einem roten 3D-Würfelcursor:

Für eine erfolgreiche Montage mussten Sie Folgendes tun:

  1. Installieren Sie das neueste Android SDK und NDK (je neuer die NDK-Version, desto besser).
  2. Laden Sie den SDL2-Quellcode herunter und verwenden Sie die Vorlage von dort, um die Android-Anwendung zu erstellen.
  3. SDL Image und SDL Mixer zur Baugruppe hinzufügen.
  4. Fügen Sie die Bibliotheken meiner Spiel-Engine und meines Toolkits sowie deren Abhängigkeiten hinzu (GLM, JSON für Modern C++)
  5. Assembly-Dateien für Gradle anpassen.
  6. C++-Code für Kompatibilität mit Android anpassen, betroffene plattformabhängige Komponenten ändern (OpenGL ES, Grafikkontextinitialisierung)
  7. Erstellen und testen Sie das Projekt auf dem Emulator.

Projektvorlage

Laden der Quellen SDL, SDL Image, SDL Mixer:
https://www.libsdl.org/download-2.0.php
Der Ordner „docs“ enthält detaillierte Anweisungen zum Arbeiten mit der Android-Projektvorlage. Kopieren Sie das Android-Projektverzeichnis in einen separaten Ordner, erstellen Sie einen Symlink oder kopieren Sie den SDL-Ordner nach Android-Projekt/App/jni.
Wir ersetzen das Avd-Flag durch die richtige Kennung und starten den Android-Emulator aus dem Verzeichnis Sdk:

cd ~/Android/Sdk/emulator
./emulator -avd Pixel_2_API_24

Geben Sie die Pfade im Skript an und stellen Sie das Projekt zusammen:

rm -rf app/build || true
export ANDROID_HOME=/home/demensdeum/Android/Sdk/
export ANDROID_NDK_HOME=/home/demensdeum/Android/android-ndk-r21-beta2/
./gradlew clean build
./gradlew installDebug

Die SDL-Projektvorlage mit C-Code aus der Datei sollte zusammengestellt werden

android-sdl-test-app/cube-art-project-android/app/jni/src/YourSourceHere.c

Abhängigkeiten

Laden Sie den Quellcode in den Archiven für SDL_image und SDL_mixer herunter:
https://www.libsdl.org/projects/SDL_image/
https://www.libsdl.org/projects/SDL_mixer/

Laden der Abhängigkeiten Ihres Projekts, zum Beispiel meiner gemeinsam genutzten Bibliotheken:
https://gitlab.com/demensdeum/FlameSteelCore/
https://gitlab.com/demensdeum/FlameSteelCommonTraits
https://gitlab.com/demensdeum/FlameSteelBattleHorn
https://gitlab.com/demensdeum/FlameSteelEngineGameToolkit/
https://gitlab.com/demensdeum/FlameSteelEngineGameToolkitFSGL
https://gitlab.com/demensdeum/FSGL
https://gitlab.com/demensdeum/cube-art-project

Wir laden das alles nach app/jni hoch, jedes „Modul“ in einen separaten Ordner, zum Beispiel app/jni/FSGL. Als nächstes haben Sie die Möglichkeit, funktionierende Generatoren für die Dateien Application.mk und Android.mk zu finden. Ich habe sie nicht gefunden, aber vielleicht gibt es eine einfache Lösung, die auf CMake basiert. Folgen Sie den Links und machen Sie sich mit dem Assembly-Dateiformat für Android NDK vertraut:
https://developer.android.com/ndk/guides/application_mk
https://developer.android.com/ndk/guides/android_mk

Sie sollten auch über verschiedene APP_STL-Implementierungen in NDK lesen:
https://developer.android.com/ndk/guides/cpp-support.html

Nach der Einarbeitung erstellen wir für jedes „Modul“ eine Android.mk-Datei, gefolgt von einer Beispiel-Assembly-Datei der gemeinsam genutzten Bibliothek Cube-Art-Project:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

APP_STL := c++_static
APP_CPPFLAGS := -fexceptions
LOCAL_MODULE := CubeArtProject
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/FlameSteelCommonTraits/src/FlameSteelCommonTraits
LOCAL_EXPORT_C_INCLUDES = $(LOCAL_PATH)/src/

define walk
$(wildcard $(1)) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e)))
endef

ALLFILES = $(call walk, $(LOCAL_PATH)/src)
FILE_LIST := $(filter %.cpp, $(ALLFILES))
$(info CubeArtProject source code files list)
$(info $(FILE_LIST))
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)

LOCAL_SHARED_LIBRARIES += FlameSteelCore
LOCAL_SHARED_LIBRARIES += FlameSteelBattleHorn
LOCAL_SHARED_LIBRARIES += FlameSteelCommonTraits
LOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkit
LOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkitFSGL
LOCAL_SHARED_LIBRARIES += FSGL
LOCAL_SHARED_LIBRARIES += SDL2
LOCAL_SHARED_LIBRARIES += SDL2_image

LOCAL_LDFLAGS := -static-libstdc++
include $(BUILD_SHARED_LIBRARY)

Jeder erfahrene CMake-Benutzer wird diese Konfiguration von den ersten Zeilen an verstehen, die Formate sind sehr ähnlich, Android.mk fehlt GLOB_RECURSIVE, sodass Sie mit der Walk-Funktion rekursiv nach Quelldateien suchen müssen.

Wir ändern Application.mk und Android.mk, um C++ und nicht C-Code zu erstellen:

APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
APP_PLATFORM=android-16
APP_STL := c++_static
APP_CPPFLAGS := -fexceptions

Benennen Sie YourSourceHere.c -> YourSourceHere.cpp um, überprüfen Sie die Einträge, ändern Sie den Pfad in der Assembly, zum Beispiel:

app/jni/src/Android.mk:LOCAL_SRC_FILES := YourSourceHere.cpp

Versuchen Sie als Nächstes, das Projekt zu erstellen. Wenn Sie vom Compiler Fehlermeldungen über das Fehlen von Headern sehen, überprüfen Sie die Richtigkeit der Pfade in Android.mk. Wenn vom Linker Fehler wie „undefinierte Referenz“ auftreten, überprüfen Sie, ob die Quellcodedateien in den Assemblys korrekt angegeben sind, indem Sie $(info $(FILE_LIST)) in der Datei Android.mk angeben. Vergessen Sie nicht den doppelten Verknüpfungsmechanismus, die Verwendung von Modulen im Schlüssel LOCAL_SHARED_LIBRARIES und die korrekte Verknüpfung über LD, zum Beispiel für FSGL:

LOCAL_LDLIBS := -lEGL -lGLESv2

Anpassung und Einführung

Ich musste einige Dinge ändern, zum Beispiel GLEW aus den Builds für iOS und Android entfernen, einige der OpenGL-Aufrufe umbenennen, das EOS-Postfix hinzufügen (glGenVertexArrays -> glGenVertexArraysOES) und ein Makro für die fehlenden modernen Debugging-Funktionen einfügen , das Sahnehäubchen ist die implizite Einbindung von GLES2-Headern, die auf Makros hinweisen GL_GLEXT_PROTOTYPES 1:

#define GL_GLEXT_PROTOTYPES 1
#include "SDL_opengles2.h"

Ich habe bei den ersten Starts auch einen schwarzen Bildschirm mit einem Fehler wie „E/libEGL: validate_display:255 error 3008 (EGL_BAD_DISPLAY)“ beobachtet, die Initialisierung des SDL-Fensters und des OpenGL-Profils geändert und alles hat funktioniert:

SDL_DisplayMode mode;
SDL_GetDisplayMode(0,0,&mode);
int width = mode.w;
int height = mode.h;

window = SDL_CreateWindow(
            title,
            0,
            0,
            width,
            height,
            SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE
        );

SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES );

Auf dem Emulator wird die Anwendung standardmäßig mit dem SDL-Symbol und dem Namen „Spiel“ installiert.

Ich muss nur die Möglichkeit erkunden, Assembly-Dateien automatisch auf Basis von CMake zu generieren oder Assemblys für alle Plattformen nach Gradle zu migrieren; CMake bleibt jedoch de facto die Wahl für die fortlaufende C++-Entwicklung.

Quellcode

https://gitlab.com/demensdeum/android- SDL-Test-App
https://gitlab.com/demensdeum/android-sdl-test-app/tree/master/cube-art-project-android

Quellen

https://developer.android.com/ ndk/guides/cpp-support.html
https://developer.android.com/ndk/guides/application_mk
https://developer.android.com/ndk/guides/android_mk
https://lazyfoo.net/tutorials/SDL/52_hello_mobile/android_windows/index.php
https://medium.com/androiddevelopers/getting-started-with-c-and-android-native-activities-2213b402ffff

Leave a Comment

Your email address will not be published. Required fields are marked *