3DO und Android NDK und wie man sich nicht einmischt ...

Bild

Es wird wahrscheinlich eine ganze Reihe von Anwendungen geben, die in Java aufgrund der großen C ++ - Quellcodebasis oder der Leistungsanforderungen fast unmöglich sind. Und so stellte sich heraus, dass ich eine dieser Anwendungen entwickelte, nämlich den 3DO-Spielkonsolenemulator Real3DOPlayer . In meinem Fall spielten sowohl die Codebasis als auch die Leistungsanforderungen eine Rolle. Der Code basierte auf meinem Phoenix- Desktop-Projekt und verlangsamte sich sogar auf mittleren Desktops, nicht wie bei eingebetteten Prozessoren. Ich kann mich nicht einmal daran erinnern, wie viele Flüche bei Gooogle aufgetreten sind, aber die Erfahrungen, die ich gemacht habe, waren von unschätzbarem Wert, die ich hier mitteilen möchte.


Ein Neuling schlägt mit der Stirn gegen eine Wand ...


Gex

Trotz der Tatsache, dass ich ein erfahrener Programmierer bin (mehr als 15 Jahre), fühlte ich mich in den Schuhen eines Anfängers, sobald ich die NDK aufnahm.

Die erste Beule. Nachdem ich Android Studio unter Windows installiert hatte, entschloss ich mich, mit einem JNI-Aufruf das einfachste "Hallo, Welt!" Alles ist sehr einfach:

JNIEXPORT jstring JNICALL Java_ru_arts_union_real3doplayer_NativeCore_stringFromJNI(JNIEnv* env, jclass clazz)
{
    return env->NewStringUTF("Hello, World!");
}


Kompilierungsfehler. Nur ein Kompilierungsfehler. Sie sehen, der Code wird nicht kompiliert, Punkt. An dem Tag, an dem ich versuchte, den Code zu kompilieren und das Problem aktiv zu googeln, konnte der Code nach einem Tag nicht mehr erfasst werden. Am nächsten Tag nahm ich einen Laptop mit Linux und beschloss, diese beiden Zeilen weiter zu quälen. Zu meiner Überraschung sammelte sich alles auf Anhieb. Ich habe angefangen, Foren zu lesen. Warum kann ich unter Windows keinen Code kompilieren? Es stellte sich heraus, dass eine C / CPP-Datei nicht ausreicht, um ein Projekt zu erstellen. Sie benötigt mindestens 2!

Die Beule ist die zweite.Ich starte die Anwendung und sie stürzt ab. Dabei wird gemeldet, dass die stringFromJNI-Methode nicht gefunden wurde. Wie so nicht gefunden?! Hier ist es, genau hier und im Disassembler ist es sichtbar! Keine Panik, ich schaue auf den gesamten Code von und bis, Gott sei Dank, gibt es nicht so viele davon. Ich schaue mir das Präfix im Namen der Funktion ru_arts_union_real3doplayer an und etwas sagt mir, dass hier etwas nicht stimmt ... Immerhin sah der Name meines Pakets so aus: ru.arts-union.real3doplayer, und der Methodenname codiert den Punkt und strich das gleiche, das ist nicht gut , und es ist seltsam, dass die Umgebung selbst einen solchen Namen für das Paket zugelassen hat. Ändern Sie den Namen des Pakets und der Methode, indem Sie den Bindestrich entfernen. Hurra! Die Methode ist zu sehen, und zwei Tage später: "Hallo, Welt!"

Die Beule ist die dritte.Es spielt keine Rolle, es wird eine Menge Dateien geben, der Name wurde korrigiert, wir fahren fort und denken, dass das Schlimmste dahinter steckt. Wir fügen unsere Quellen hinzu, die unter Linux / Windows für verschiedene Prozessoren (einschließlich ARM) getestet wurden. Alles perfekt kompiliert, ohne einen einzigen Fehler. Die Anwendung stürzt beim Start ohne Warnung ab. Wenn ich weiter über den Code meditiere, beobachte ich eine vorbeiziehende Zeile mit einem langen Double. Ich erinnere mich, dass NEON ein Double Maximum hat, aber dieser Code wird nicht einmal aufgerufen, er existiert nur und in der Typüberschrift habe ich real80 gelöscht. Und dann wurde der Code fehlerfrei kompiliert! Hmm .. als auf ein Tamburin zu klopfen, würde ich lieber diesen Code trinken ... Starten und Bingo! Der Code funktioniert, die Spiele beginnen, wenn auch mit wilden Bremsen!

Erwähnenswert ist die Arbeit mit scheinbar alltäglichen Dingen - Standard-C-Bibliotheken. Seien Sie vorsichtig, es kommt häufig vor, dass einige Methoden, die in einer Version vorhanden sind, in einer anderen nicht vorhanden sind, und Ihre Anwendung aufgrund der Unfähigkeit, die gewünschte Methode zu laden, abstürzt. Hier helfen nur umfangreiche Tests oder Ablehnungen zugunsten von STL mit statischer Verknüpfung. So stürzt TinyXML beispielsweise auf einer relativ großen Anzahl von Geräten ab, da Funktionen zum Konvertieren von Zahlen in einen String fehlen und umgekehrt.

Je weniger Erfahrung Sie haben, desto mehr Zapfen werden Sie haben - seien Sie mental vorbereitet, wahrscheinlich habe ich mich nicht an alles erinnert ...

Auf der Suche nach Leistung oder einmal gestürzt ...


BC Rennfahrer

Wie man die Leistung des Emulators erhöht, der an einigen Stellen sogar das i7 belastete? Ganz einfach: Sie müssen sich auf eine Computerressource beschränken, die versucht wurde, sie auf Android zu portieren.

In solch einer schwierigen Situation mussten wir Textur-Caching mit vorab oder vollständig verarbeiteten Pixeln durch den Grafikprozessor der Konsole implementieren. Triangulation von quadratischen Polygonen (3DO hat eine nichtlineare Texturabbildung); statische Code-Neukompilierung, häufig verwendete Bibliotheken des Konsolenbetriebssystems; Parallelisierung der Emulation von Subsystemen der Konsole. All dies ermöglichte es, die Hardwareanforderungen um mehr als das Dreifache zu reduzieren. Wo sonst können Sie die Uhr retten, ohne die klassischen Techniken, die Montageoptimierung und das lange Sitzen im Profiler? Offensichtlich wird auf was die meisten Benutzer den Unterschied nicht bemerken. So habe ich zum Beispiel beim Ausfüllen eines Frames viel auf einem 16-Bit-Raster gespeichert, anstatt auf einem 24-Bit-Raster.

Der Optimierungsprozess selbst ist sehr unterhaltsam und basiert auf einem separaten Artikel, aber all das gilt nicht für Android als solches. Aber was können uns die Plattform selbst und ihre Funktionen bieten? Offensichtlich ein direktes Schreiben in den Texturspeicher, da die überwiegende Mehrheit der Geräte CPU und GPU auf mobilen Plattformen gemeinsam nutzt. Leider ist dieser als GraphicBuffer bekannte Mechanismus nicht für den allgemeinen Gebrauch gedacht! Nichts, du kannst es durch dlopen bekommen. Dies muss jedoch optional erfolgen, da dieser Hack möglicherweise nicht überall funktioniert. Die Implementierung kann hier angesehen werden (es ist ziemlich schwierig, sie über die Suche zu finden): android.googlesource.com/platform/external/deqp/+/deqp-dev/framework/platform/android/tcuAndroidInternals.cpp .

Und was will Android uns wirklich geben, braucht es aber wirklich? Natürlich die Kontrolle über den Applikationszyklus! Aber wenn Sie es wirklich brauchen, hilft Ihnen NativeActivity oder dieses kleine Beispiel, das durch seine Einfachheit und Bequemlichkeit besticht: github.com/tsaarni/android-native-egl-example . Im Gegensatz zu NativeActivity, bei dem die Funktionsweise einer regulären Aktivität unklar ist, können Sie mit diesem Ansatz den Fensterkontext erfassen und in einem separaten Stream zeichnen, wenn wir ihn benötigen, und nicht, wenn Java sich dafür entscheidet. Und hier war ich in einem Durcheinander, ohne es selbst zu wissen.

Tatsache ist, dass dieser Mechanismus nicht auf allen Android-Versionen funktioniert (zumindest in meinen Tests unterstützen ihn die Versionen 4.1-4.3 nicht), und dies sind laut Google-Statistiken etwa 40% der Zielgeräte für meine Anwendung, d. H. 40% Gewinn aus. Und Sie können dies sehr spät herausfinden, da der Benutzer die Anwendung normalerweise ablegt und feststellt, dass sie nicht funktioniert, sie sofort zerstört und keine Bewertung abgibt. Dies ist verständlich, da Sie das Geld schneller zurückgeben müssen. Das Korrigieren des falschen Zyklus der Anwendung ist keine angenehme Sache, da es unmöglich ist zu bestimmen, auf welchem ​​Gerät es funktioniert und auf welchem ​​Gerät es nicht ist. Es ist möglich, eine langsamere, aber kompatiblere Lösung festzulegen, aber Sie können Ihr Karma verderben, weil nicht jeder es bekommen wird überprüfen Sie in den Einstellungen: "Ist es möglich, etwas anzuziehen,

Schneiden von Objekten oder Herumspielen von zwei ...


Bust-a-Move

Sehr oft verfügt das SDK bereits über Funktionen, die Sie nicht im systemeigenen Code duplizieren möchten, z. B. zum Arbeiten mit Schriftarten oder Bildern und vielen anderen Dingen, da Java über eine große Bibliothek verfügt. Und es besteht ein natürlicher Wunsch, die Java-Methode von C ++ aus aufzurufen. Ich hatte einfach so einen Wunsch und ich habe eine coole Sache gemacht - Schriften auf Anfrage auf Textur aus dem nativen Code zu rendern, ich habe alles auf meinen Geräten und eine Reihe von virtuellen Geräten getestet. Alles hat gut funktioniert und ist wieder durcheinander geraten - hat eine Veröffentlichung gemacht ... Für ungefähr 1% der Benutzer (die die Anwendung bereits gekauft haben) begann das Programm abzustürzen. Um es milde auszudrücken - sehr schlechte, negative Bewertungen und die Situation selbst - die Person, die bezahlt hat und dann bam - es funktioniert nicht für ihn. Es ist schwer zu sagen, was der Grund ist. Ich vermute, dass einige Hersteller eine modifizierte Achse oder etwas anderes verwenden.

java.lang.NoSuchMethodError: no method with name='loadConfig' signature='(Ljava/lang/String;)Ljava/lang/String;' in class Lru/vastness/altmer/real3doplayer/MainActivity;
at dalvik.system.NativeStart.run(Native Method)


Ich musste diese Lösung sehr schnell herausschneiden und durch eine andere ersetzen. Es hat ein bisschen Blut gekostet. Die Lösung selbst ist sehr gut (Sie können 1% der Inkompatibilitäten tolerieren, sie vergehen einfach und kaufen sie nicht), aber nur bis zum Start der Anwendung. Wenn dann bereits viele Exemplare verkauft wurden, würde ich dringend davon abraten.

Aktualisieren des Arsenals oder in Schwierigkeiten geraten drei ...


Poed

Beim Umstellen des Systems habe ich das SDK niedriger installiert und dementsprechend auch targetSdkVersion geändert. Es gab genau dort ein Ärgernis. SD-Laufwerke werden nicht mehr angezeigt, was auf den seltsamen Betrug von Google mit Zugriffsrechten zurückzuführen ist. Gleichzeitig war das Herunterladen der APK mit einer älteren Version des SDK möglich! Es ist unmöglich! Aber ich bin mit einem leichten Schrecken davongekommen, das Problem wurde gelöst, indem ich um Erlaubnis bat. Und wenn das Problem nicht so einfach zu lösen wäre? Was ist dann zu tun? Die Frage ist sehr interessant ...

Aber das Unangenehmste, was mit Updates kam, die ich immer noch nicht loswerden kann, ist die Zurückhaltung der Umgebung, die APK zu aktualisieren, wenn Änderungen in der nativen Bibliothek vorgenommen werden. Ich beobachte das Problem unter Linux und Windows. Dies ist ärgerlich, sodass auf dem Gerät, auf dem das letzte frisch gesammelte APK getestet wird, Folgendes ausgeführt werden muss (Android Studio 2.1.1):

1. Erstellen Sie das Projekt neu.
2. Führen Sie das Projekt aus (gleichzeitig wird es wieder zusammengesetzt, wenn Sie den ersten Schritt überspringen, wird es nicht neu zugewiesen).
3. Beenden Sie die Anwendung.
4. Erstellen Sie die APK.
5. Führen Sie die Anwendung aus.

Und Sie dachten, wenn Sie den Neuaufbau durchgeführt haben, erhalten Sie beim Starten der Anwendung die neuesten, frisch gesammelten APKs auf dem Gerät? Es war nicht dort, überprüfen Sie es, sondern erstellen Sie selbst ein Popup-Fenster oder eine Meldung im Protokoll mit der Version der Änderungen. Andernfalls riskieren Sie viel Zeit bei der Überprüfung Ihres Arbeitscodes. Und hoffen Sie nicht, dass es ab dem zweiten oder dritten Start klappt.

Für diejenigen, die ähnliche Probleme haben, empfehle ich, um die Zeit zu kompensieren, falls dies noch nicht geschehen ist, Jobs N für die parallele Assemblierung des nativen Codes anzugeben.

Das Endergebnis ...


Robinsons Requiem

Im Allgemeinen wurde ich von Entwicklungstools für diese Plattform sedimentiert, ich habe seit zehn Jahren keine solche Krümmung mehr gesehen, aber ich habe etwas zu vergleichen. Natürlich verstehe ich, dass Java eine Priorität ist, aber dennoch ... Nur ein Fehler, den ich vor vielen Jahren im Intel-Compiler beim Umschalten auf Null in einer Variablen gefunden habe, kann darauf stoßen. Seitdem schreibe ich:

	if(shift)return x>>shift;
	return x;


Trotzdem lohnt es sich, Google Tribut zu zollen, sie haben alles getan, damit die Gehälter der einheimischen Entwickler für Android hoch waren!

Für mich als C ++ - Entwickler sieht die Situation mit Anwendungen für Android paradox aus. Einerseits gibt es notwendige Mechanismen, aber die Gesamtmenge der Probleme auf der Plattform ist so groß, dass ich mich entscheiden muss, ob ich meinen Ruf verlieren oder mich weiterentwickeln möchte anwendungen. Keine einzige Plattform hat mich vor eine solche Wahl gestellt.

Unwillkürlich denken Sie, dass es besser ist, nichts für diese Plattform zu verbessern, sonst ist es so, als wäre etwas passiert ...

PS. Denken Sie nicht - der Emulator wird sich weiterentwickeln, er brodelt nur.

Jetzt auch beliebt: