{"id":2502,"date":"2020-01-12T11:47:43","date_gmt":"2020-01-12T08:47:43","guid":{"rendered":"http:\/\/demensdeum.com\/blog\/?p=2502"},"modified":"2024-12-16T22:32:30","modified_gmt":"2024-12-16T19:32:30","slug":"porting-cpp-sdl-app-on-android","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/hi\/2020\/01\/12\/porting-cpp-sdl-app-on-android\/","title":{"rendered":"Porting a C++ SDL application to Android"},"content":{"rendered":"<p>In this note I will describe my experience of porting the prototype of the 3D editor <a href=\"https:\/\/gitlab.com\/demensdeum\/cube-art-project-bootstrap\" target=\"_blank\" rel=\"noopener noreferrer\">Cube Art Project<\/a> to Android.<br \/>First, let&#8217;s look at the result, the emulator runs an editor with a red 3D cube cursor:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2509\" src=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2020\/01\/emulator.png\" alt=\"\" width=\"460\" height=\"265\" srcset=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2020\/01\/emulator.png 959w, https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2020\/01\/emulator-300x173.png 300w, https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2020\/01\/emulator-768x442.png 768w\" sizes=\"auto, (max-width: 460px) 100vw, 460px\" \/><\/p>\n<p>For a successful build, you had to do the following:<\/p>\n<ol>\n<li>Install the latest Android SDK and NDK (the newer the NDK version, the better).<\/li>\n<li>Download the SDL2 source code, take from there a template for building an android application.<\/li>\n<li>Add SDL Image, SDL Mixer to the build.<\/li>\n<li>Add my game engine and toolkit libraries, their dependencies (GLM, JSON for Modern C++)<\/li>\n<li>Adapt build files for Gradle.<\/li>\n<li>Adapt C++ code for compatibility with Android, changes affected platform-dependent components (OpenGL ES, initialization of the graphics context)<\/li>\n<li>Build and test the project on the emulator.<\/li>\n<\/ol>\n<h3>Project template<\/h3>\n<p>Download SDL, SDL Image, SDL Mixer sources:<br \/><a href=\"https:\/\/www.libsdl.org\/download-2.0.php\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/www.libsdl.org\/download-2.0.php<\/a><br \/>\nThe docs folder contains detailed instructions on working with the android project template; copy the android-project directory to a separate folder, make a symlink or copy the SDL folder to android-project\/app\/jni.<br \/>We substitute the correct identifier for the avd flag, launch the android emulator from the Sdk directory:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>cd ~\/Android\/Sdk\/emulator\n.\/emulator -avd Pixel_2_API_24\n\n<\/code><\/pre>\n<\/div>\n<p>We specify the paths in the script, build the project:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>rm -rf app\/build || true\nexport ANDROID_HOME=\/home\/demensdeum\/Android\/Sdk\/\nexport ANDROID_NDK_HOME=\/home\/demensdeum\/Android\/android-ndk-r21-beta2\/\n.\/gradlew clean build\n.\/gradlew installDebug\n\n<\/code><\/pre>\n<\/div>\n<p>The SDL project template should build with the C code from the file<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>android-sdl-test-app\/cube-art-project-android\/app\/jni\/src\/YourSourceHere.c\n\n<\/code><\/pre>\n<\/div>\n<h3>Dependencies<\/h3>\n<p>Download the source code in the archives for SDL_image, SDL_mixer:<br \/><a href=\"https:\/\/www.libsdl.org\/projects\/SDL_image\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/www.libsdl.org\/projects\/SDL_image\/<\/a><br \/>\n<a href=\"https:\/\/www.libsdl.org\/projects\/SDL_mixer\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/www.libsdl.org\/projects\/SDL_mixer\/<\/a><\/p>\n<p>Load your project&#8217;s dependencies, for example my shared libraries:<br \/><a href=\"https:\/\/gitlab.com\/demensdeum\/FlameSteelCore\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/FlameSteelCore\/<\/a><br \/>\n<a href=\"https:\/\/gitlab.com\/demensdeum\/FlameSteelCommonTraits\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/FlameSteelCommonTraits<\/a><br \/>\n<a href=\"https:\/\/gitlab.com\/demensdeum\/FlameSteelBattleHorn\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/FlameSteelBattleHorn<\/a><br \/>\n<a href=\"https:\/\/gitlab.com\/demensdeum\/FlameSteelEngineGameToolkit\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/FlameSteelEngineGameToolkit\/<\/a><br \/>\n<a href=\"https:\/\/gitlab.com\/demensdeum\/FlameSteelEngineGameToolkitFSGL\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/FlameSteelEngineGameToolkitFSGL<\/a><br \/>\n<a href=\"https:\/\/gitlab.com\/demensdeum\/FSGL\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/FSGL<\/a><br \/>\n<a href=\"https:\/\/gitlab.com\/demensdeum\/cube-art-project\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/cube-art-project<\/a><\/p>\n<p>We unload all this into app\/jni, each \u201cmodule\u201d into a separate folder, for example app\/jni\/FSGL. Next, you have the option of finding working generators for the Application.mk and Android.mk files, I did not find any, but perhaps there is a simple solution based on CMake. Follow the links and begin to get acquainted with the build file format for Android NDK:<br \/><a href=\"https:\/\/developer.android.com\/ndk\/guides\/application_mk\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/developer.android.com\/ndk\/guides\/application_mk<\/a><br \/>\n<a href=\"https:\/\/developer.android.com\/ndk\/guides\/android_mk\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/developer.android.com\/ndk\/guides\/android_mk<\/a><\/p>\n<p>You should also read about the different APP_STL implementations in the NDK:<br \/><a href=\"https:\/\/developer.android.com\/ndk\/guides\/cpp-support.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/developer.android.com\/ndk\/guides\/cpp-support.html<\/a><\/p>\n<p>After reviewing, we create an Android.mk file for each \u201cmodule\u201d, then an example of a shared library assembly file Cube-Art-Project:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>LOCAL_PATH := $(call my-dir)\ninclude $(CLEAR_VARS)\n\nAPP_STL := c++_static\nAPP_CPPFLAGS := -fexceptions\nLOCAL_MODULE := CubeArtProject\nLOCAL_C_INCLUDES := $(LOCAL_PATH)\/src $(LOCAL_PATH)\/..\/include $(LOCAL_PATH)\/..\/include\/FlameSteelCommonTraits\/src\/FlameSteelCommonTraits\nLOCAL_EXPORT_C_INCLUDES = $(LOCAL_PATH)\/src\/\n\ndefine walk\n$(wildcard $(1)) $(foreach e, $(wildcard $(1)\/*), $(call walk, $(e)))\nendef\n\nALLFILES = $(call walk, $(LOCAL_PATH)\/src)\nFILE_LIST := $(filter %.cpp, $(ALLFILES))\n$(info CubeArtProject source code files list)\n$(info $(FILE_LIST))\nLOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)\/%=%)\n\nLOCAL_SHARED_LIBRARIES += FlameSteelCore\nLOCAL_SHARED_LIBRARIES += FlameSteelBattleHorn\nLOCAL_SHARED_LIBRARIES += FlameSteelCommonTraits\nLOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkit\nLOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkitFSGL\nLOCAL_SHARED_LIBRARIES += FSGL\nLOCAL_SHARED_LIBRARIES += SDL2\nLOCAL_SHARED_LIBRARIES += SDL2_image\n\nLOCAL_LDFLAGS := -static-libstdc++\ninclude $(BUILD_SHARED_LIBRARY)\n\n<\/code><\/pre>\n<\/div>\n<p>Any experienced CMake user will understand this config from the first lines, the formats are very similar, Android.mk lacks GLOB_RECURSIVE, so you have to recursively search for source files using the walk function.<\/p>\n<p>Change Application.mk, Android.mk so-but for building C++ and not C code:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>APP_ABI := armeabi-v7a arm64-v8a x86 x86_64\nAPP_PLATFORM=android-16\nAPP_STL := c++_static\nAPP_CPPFLAGS := -fexceptions\n\n<\/code><\/pre>\n<\/div>\n<p>Rename YourSourceHere.c -> YourSourceHere.cpp, grep for occurrences, change the path in the assembly, for example:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>app\/jni\/src\/Android.mk:LOCAL_SRC_FILES := YourSourceHere.cpp\n\n<\/code><\/pre>\n<\/div>\n<p>Then try to build the project, if you see errors from the compiler about the absence of headers, then check the correctness of the paths in Android.mk; if there are errors from the linker of the type \u201cundefined reference\u201d, then check the correctness of the source code files in the assemblies, you can trace the lists by specifying $(info $(FILE_LIST)) in the Android.mk file. Do not forget about the dual linking mechanism, using modules in the LOCAL_SHARED_LIBRARIES key and correct linking via LD, for example for FSGL:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>LOCAL_LDLIBS := -lEGL -lGLESv2\n\n<\/code><\/pre>\n<\/div>\n<h3>Adaptation and launch<\/h3>\n<p>I had to change some things, for example remove GLEW from the iOS and Android builds, rename some OpenGL calls, adding the EOS postfix (glGenVertexArrays -> glGenVertexArraysOES), include a macro for missing modern debug functions, the cherry on the cake is the implicit inclusion of GLES2 headers with the GL_GLEXT_PROTOTYPES 1: macro.<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>#define GL_GLEXT_PROTOTYPES 1\n#include \"SDL_opengles2.h\"\n\n<\/code><\/pre>\n<\/div>\n<p>I also observed a black screen on the first launches with an error like \u201cE\/libEGL: validate_display:255 error 3008 (EGL_BAD_DISPLAY)\u201d, I changed the initialization of the SDL window, the OpenGL profile and everything worked:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>SDL_DisplayMode mode;\nSDL_GetDisplayMode(0,0,&mode);\nint width = mode.w;\nint height = mode.h;\n\nwindow = SDL_CreateWindow(\n            title,\n            0,\n            0,\n            width,\n            height,\n            SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE\n        );\n\nSDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES );\n<\/code><\/pre>\n<\/div>\n<p>On the emulator, the application is installed by default with the SDL icon and the name &#8220;Game&#8221;.<\/p>\n<p>I&#8217;m left to explore the possibility of automatically generating build files based on CMake, or migrating builds for all platforms to Gradle; however, CMake remains the de facto choice for current C++ development.<\/p>\n<h3>Source code<\/h3>\n<p><a href=\"https:\/\/gitlab.com\/demensdeum\/android-sdl-test-app\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/android- sdl-test-app<\/a><br \/><a href=\"https:\/\/gitlab.com\/demensdeum\/android-sdl-test-app\/tree\/master\/cube-art-project-android\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/android-sdl-test-app\/tree\/master\/cube-art-project-android<\/a><\/p>\n<h3>Sources<\/h3>\n<p><a href=\"https:\/\/developer.android.com\/ndk\/guides\/cpp-support.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/developer.android.com\/ ndk\/guides\/cpp-support.html<\/a><br \/><a href=\"https:\/\/developer.android.com\/ndk\/guides\/application_mk\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/developer.android.com\/ndk\/guides\/application_mk<\/a><br \/>\n<a href=\"https:\/\/developer.android.com\/ndk\/guides\/android_mk\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/developer.android.com\/ndk\/guides\/android_mk<\/a><br \/>\n<a href=\"https:\/\/lazyfoo.net\/tutorials\/SDL\/52_hello_mobile\/android_windows\/index.php\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/lazyfoo.net\/tutorials\/SDL\/52_hello_mobile\/android_windows\/index.php<\/a><br \/>\n<a href=\"https:\/\/medium.com\/androiddevelopers\/getting-started-with-c-and-android-native-activities-2213b402ffff\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/medium.com\/androiddevelopers\/getting-started-with-c-and-android-native-activities-2213b402ffff<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this note I will describe my experience of porting the prototype of the 3D editor Cube Art Project to Android.First, let&#8217;s look at the result, the emulator runs an editor with a red 3D cube cursor: For a successful build, you had to do the following: Install the latest Android SDK and NDK (the<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/hi\/2020\/01\/12\/porting-cpp-sdl-app-on-android\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Porting a C++ SDL application to Android&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[61,52],"tags":[37,124,145,146,77,147,148],"class_list":["post-2502","post","type-post","status-publish","format-standard","hentry","category-techie","category-tutorials","tag-android","tag-c","tag-cpp","tag-opengl-es","tag-sdl","tag-sdl-image","tag-sdl-mixer","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"hi","enabled_languages":["en","ru","zh","de","fr","ja","pt","hi"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false},"hi":{"title":false,"content":false,"excerpt":false}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/posts\/2502","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/comments?post=2502"}],"version-history":[{"count":31,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/posts\/2502\/revisions"}],"predecessor-version":[{"id":3923,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/posts\/2502\/revisions\/3923"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/media?parent=2502"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/categories?post=2502"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/tags?post=2502"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}