Target Windows dla tangram-es

Tangram to jedna z lepszych alternatyw google/bing maps, do tego otwarta i działająca nie tylko w przeglądarce (JS + WebGL), ale tekże w wersji pod OpenGL-ES dla Linuksa, OSX, Androida i iOS. Dodatkowo projekt jest otwarty, więc można go rozszerzać pod własne potrzeby. Niestety, wersji na Windows brak. Jako, że używałem tych map w aplikacji androidowej i sprawdzała się tam nieźle, stwierdziłem, że w imię jednolitości opracuję target dla Windows. Żeby cały wysiłek miał jakiś walor dydaktyczny, pisałem ten artykuł równolegle z kolejno wykonywanymi rzeczami.

Rozeznanie

Pierwsza rzecz, to pobieżne przepatrzenie kodu i opracowanie strategii dalszego działania. Na daną chwilę repozytorium posiada katalog platforms, w którym to są przykładowe aplikacje dla danych systemów. Widać też, że aplikacja linuksowa korzysta z GLFW, który również dostępny jest dla Windows. No, może nie będzie źle.

Druga rzecz rzucająca się w oczy to sposób budowania. W instrukcjach dla platformy linuksowej używa się instrukcji make linux . Drążąc temat dalej widać, że makefile generowany jest przy użyciu CMake. Trzeba będzie go trochę zmodyfikować, by przyjmował target windowsowy.

Przygotowanie platformy windows

Skopiowałem sobie platformę linux i nazwałem windows, zmieniłem nazwy linuxPlatform na windowsPlatform. I tu pojawił się pierwszy commit, by móc śledzić diffa.

Wywaliłem odniesienia do sys/resource.h i sys/syscall.h. Ifdefy linux/raspi też nie są potrzebne, z tym, że zostawiłem kod zawarty w ifdefie linuksowym w initGLExtensions – Windows raczej obsłuży to samo. Dodatkowo pozbyłem się funkcji setCurrentThreadPriority(). Raz, że nie była używana, dwa że jest mocno związana z linuksem, trzy że raczej na początek nie będzie potrzebna.

Pozbyłem się też obsługi sygnału CTRL+C z pliku głównego. Być może później będę musiał to dodać ponownie, zatem tę pojedynczą zmianę zrobiłem jako oddzielny commit. Będzie to wyraźniej widać w historii.

Poprawki CMake

Jeśli chodzi o platformę, główny plik CMake ogranicza się do sprawdzenia aktualnej platformy i upewnienia się, że jest ona na liście obsługiwanych platform. Dodając  windows  do listy platform spowodowałem próbę dołączenia pliku toolchains/windows.cmake , który oczywiście nie istnieje. Stworzyłem go kopiując linux.cmake i robiąc znajdź-zamień na wyrazie “linux”. Moją uwagę jednak skupiła linia add_definitions(-DTANGRAM_LINUX) . Szukając TANGRAM_LINUX  natknąłem się na wystąpienia tylko w platforms/linux (więc niegroźnie) i platform_gl.h w platforms/common. Brzmi jak obietnica zabawy. Tak czy inaczej, zmieniłem TANGRAM_LINUX  na TANGRAM_WINDOWS . Zabawa z CMake prawodpodobnie tu się kończy.

Poprawki platform_gl

definicja TANGRAM_LINUX  używana jest tylko w jednym miejscu:

Jako, że target windowsowy również ma korzystać z GLFW, zamieniłem to na coś takiego:

Odpalając CMake . -G "MinGW Makefiles"  któryś z kolejnych cmake’ów podprojektów nakrzyczał na mnie, że nie wolno budować w katalogu głównym. Założyłem zatem katalog build i z jego poziomu zrobiłem CMake .. -G "MinGW Makefiles" . Ruszyło, zaczęło budować…

Błędy przy kompilacji

… I wysypało się w połowie drogi.

Chwila guglania i rzeczywiście – dany plik nie inkluduje string.h ani cstring, w którym leży funkcja strdup. To jednak nie rozwiązało problemu, zatem zacząłem szukać głębiej i to nie inklud był przyczyną – kompilator uruchomiony był z flagą -std=c++1y  i o dziwo w takiej konfiguracji nie ma funkcji strdup – zarówno w formie zwykłej jak i z prefiksem std:: . Zmiana flagi kompilacji na -std=gnu++1y  w windows.cmake zrobiła swoje. Kolejny przystanek w 83%:

No i faktycznie, pliku nie ma. Patrzę w log kompilacji a tam coś takiego:

No i widać, że ścieżka się nie zgadza. Zapuściłem make’a tym razem z katalogu build/windows, tak by dwa piętra wyżej leżał katalog core. Druga sprawa – podczas kompilacji uruchamiany był program incbin.sh, który nie działał, gdyż nie mam interpretera .sh. Okazuje się, że incbin to wspaniały kawałek kodu, który jest jednocześnie poprawnym skryptem bat i sh domyślnie występujący z rozszerzeniem .bat i nie wiedzieć czemu ekipa od tangram-es zmieniła mu rozszerzenie na .sh łamiąc kompatybilność z Windowsem. Pierwsze co, to przywróciłem rozszerzenie .bat i oryginalną treść. Druga rzecz, to drobna modyfikacja CMakeLists.txt tangram-es/core by zamiast polecenia incbin  używał ${INCBIN}  zdefiniowanego jako:

No i ruszyło i zatrzymało się na 98%:

Naprawiłem to doinstalowując port windowsowy zlib i dodając do CMake’a szukanie i dołączenie jego plików. Poszło dalej, aż nie zaczął się wywalać platforms/common/platform_gl.cpp krzycząc różne dziwne rzeczy o redefinicjach symboli OpenGLa i braku niektórych funkcji. Zwłaszcza ten drugi problem to gigantyczna słabość Windowsa – w sposób podstawowy nagłówki OpenGLa zapewniają wyłącznie stare API do wersji 1.4, jeszcze sprzed rewolucji shaderowej, gdzie dzisiaj już jest wersja 4.5. Pozostałe funkcje trzeba sobie załadować we własnym zakresie.

Przypomniało mi się, że GLFW na swojej stronie zalecał jakiegoś loadera dodatkowych funkcji OGL. Chwila sprawdzania i wyszło na to, że był to GLAD – wraz z praktycznym generatorem plików do dołączenia do projektu. Wygenerowałem sobie goły GLES2.0 z loaderem. Przy kompilacji zaczął krzyczeć z powodu redeklaracji, bo GLFW jakimś cudem dalej ładował nagłówki systemowe. Zgodnie z informacjami w FAQ projektu, GLFW powinien sam wykryć loadera, lecz jeśli tego nie zrobi, należy zdefiniować GLFW_INCLUDE_NONE . Tak też zrobiłem i błędów ubyło, lecz jeszcze nie wszystkie – brakuje m.in. funkcji glMapBuffer, która nie jest częścią standardu. Zaznaczam wszystkie możliwe rozszerzenia i dalej nic – coś jest nie tak.

Okazuje się, że dla innych targetów niektóre niestandardowe rozszerzenia miały zdefiniowane aliasy. Przykładowo dla OSX:

Wykonałem podobne definicje dla brakujących funkcji, gdyż GLAD deklarował je z końcówką OES. Pomogło. Następny przystanek – cURL. Doinstalowanie pakietu poszło łatwo, ciężej żeby zmusić CMake’a do jego znalezienia, gdyż skrypt szukania cURLa nie wykorzystuje ścieżki instalacji. Z pomocą przychodzi zmienna środowiskowa CMAKE_PREFIX_PATH, która mówi CMake’owi gdzie ma szukać inkludów i libek. I to w sumie na tyle błędów kompilacji. Pozostało coś gorszego…

Błędy linkowania

Chwila guglania i okazuje się, że funkcje poprzedzone _imp_ są odwołaniami do funkcji w DLL. Poszukałem jeszcze trochę i okazuje się, że może być to spowodowane korzystaniem przez MinGW z bibliotek budowanych w Visual C++ . Ściągnąłem źródła cURLa, skompilowałem komendą make mingw32 i ruszyło.

Odezwała się funkcja setCurrentThreadPriority, którą wyrzuciłem, więc ponownie ją dodałem z pustym ciałem funkcji. Co do reszty: sqlite było kompilowane z flaga -fstack-protector zaś reszta nie. Dodanie tej flagi do windowsowego cmake’a załatwiło sprawę. Exe gotowe, ale to nie koniec.

Problemy po uruchomieniu

Aplikacja się nie chciała uruchomić z braku paru .dll od zliba i cURLa. Po zaciągnięciu ich aplikacja ruszyła, jednak w oknie konsoli przywitała mnie salwa errorów typu:

O ile te drugie związane mogą być z tym, że same w sobie linki nie działają, gdyż nie kompilowałem z podaniem klucza API, o tyle te od gstatic pokazują, że cURL ma sam w sobie problemy z HTTPSem. Stwierdziłem, że to zapewne dlatego, że korzystam z gotowca curla. Skompilowałem go sobie ręcznie i otrzymałem tego typu errory:

Okazało się, że makefile pod mingw w ogóle nie ma opcji ustawienia ssl. Po dohackowaniu sobie obsługi ssl i zlib wróciłem do tych samych problemów z certyfikatami SSL, zatem to nie było to. Wygląda na to, że nie ma wkompilowanego certyfikatu, więc żeby się z tym w tej chwili dłużej nie bawić dodałem omijanie sprawdzania certyfikatu po stronie tangrama:

No i ruszyło, ale to nie koniec. Pojawiły się crashe. Po skompilowaniu tangram-es w trybie debug, GDB mi podpowiedział co nieco:

Jak widać kłopoty sprawia funkcja glMapBuffer – jedna z tych, które musiałem ręcznie dodefiniować. Chwila guglania i ktoś na stacku wypowiada się, że GLES2.0 jest w sumie kompatybilny z GL3.4. Wygenerowałem GLADa dla tej wersji i zrekompilowałem. Teraz tangram tylko krzyczy, że nie zna dwóch funkcji będących w GLES2 a dodanych dopiero w GL4.0. Jako, że funkcje te po prostu są kopiami innych funkcji przyjmującymi argumenty float zamiast double, zdefiniowałem je makrami aby nie zwiększać zbytnio wymagań aplikacji:

I na tym historia się kończy.

Epilog

Z tego, co naczytałem się issues projektu, target windowsowy był dość długo oczekiwaną funkcjonalnością. Dziwi mnie, że nikt tego wcześniej nie zrobił. Ba, dziwi mnie, że projekt od samego początku nie supportuje windowsa, zwłaszcza że korzysta z GLFW, które z automatu załatwia wszelką kompatybilność. W międzyczasie wyszła kolejna wersja tangrama, więc będę musiał dosynchronizować się przed pull requestem. No i skompilować OpenSSL z certyfikatem, by móc sprawdzać wiarygodność źródeł.

Następny przystanek: kontrolka tangram-es dla wxWidgets.

This entry was posted in tangram-es and tagged , , , , , , , , , . Bookmark the permalink.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *