Performance et débogage

Modèle de performance

Structure des coûts par frame et par instance :

ÉtapeCoût
Marshalling de l’instantané (hôte → module)l’unique traversée Java imposée : l’hôte écrit l’instantané POD dans les pages partagées / un tampon direct à l’aide de constructeurs réutilisés
Lecture des données (Vehicle / Pids / …)simples lectures mémoire, sans coût
Écriture des enregistrements de dessinallocation par incrément (bump) dans la zone, zéro allocation, zéro boxing
Rastérisation des pixels (Gfx2D)boucles C++ pures ; chemin rapide src-over pour les couleurs opaques
Rejeu par l’hôtele même chemin de rendu que JS ; le téléversement de textures ne déplace que les rectangles modifiés
Appel Apialler-retour encodage JSON + réflexion + décodage JSON — coûteux, à réserver aux canaux basse fréquence

Règles du chemin chaud

  1. Pas d’Api dans render. Les données à haute fréquence sont toutes dans l’instantané ; le dessin à haute fréquence passe par le flux d’enregistrements. Les à-coups GC périodiques de l’ère JS venaient précisément d’« un appel réflexif par frame et par véhicule ».
  2. Créez les ressources uniquement dans create : modèles, textures, handles d’objets Java, callbacks — construisez-les, stockez-les dans State, et render se contente de les utiliser.
  3. Évitez les lectures de ressources / HTTP à chaque frame : le réseau va dans BackgroundWorker, les résultats dans votre propre cache synchronisé, que render lit.
  4. Téléversez une texture seulement quand son contenu change : le fond statique se dessine une fois ; pour le contenu dynamique, restreignez la zone redessinée avec push_clip afin que le rectangle modifié ne couvre que les bandes réellement touchées.
  5. Déphasez les objets : carrousels / clignotements de plusieurs instances ne doivent pas bouger tous dans la même frame — un ordonnancement à phases décalées répartit les zones modifiées sur des frames différentes (utilisez in_game_time() ou game_time_millis() comme horloge).
  6. Mesurez les objets chaînes temporaires : les comparaisons StrView évitent toute copie ; en cas de concaténation réellement nécessaire, attention aux allocations de std::string (la SSO dispense d’allocation pour les chaînes courtes).

Mémoire et récupération différée

La discipline mémoire côté hôte (à titre d’information pour les auteurs de scripts ; le SDK en bénéficie automatiquement) :

  • Les cinq zones de sortie sont une par module et réutilisées entre frames ; la zone de pixels démarre à 1 Mo, croît à la demande (16 Mo au maximum) et se rétracte de façon déterministe vers son plancher après les pics — fini la résidence fixe de 16 Mo par module.
  • Une instance qui sort du frustum n’est pas détruite (elle reste chaude, ré-entrée en vue sans reconstruction) ; elle n’est retirée qu’environ 5 secondes après avoir réellement quitté la liste des véhicules du client.
  • Les gros blocs libérés passent par defer_free (ABI 10) : les tampons de plusieurs méga-octets morts dans render / dispose sont confiés au thread de récupération en arrière-plan de l’hôte, évitant d’imputer tout le coût du free à la frame courante et d’amplifier l’attente des points sûrs du GC. Contrat : le bloc doit être mort, sans destructeur, et aucun thread ne doit plus y toucher ensuite (rejeu de l’hôte compris). La rétraction de la zone de pixels du SDK s’y conforme déjà automatiquement ; pour vos propres gros tampons, appelez host->defer_free selon les mêmes règles — si l’hôte est trop ancien (emplacement null), une libération synchrone suffit.

Observation et journaux

Quoi regarderOùContenu
census des modulessection de démarrage de logs/latest.lognombre et types de modules chargés ; ligne récapitulative du canal de transport (mentionnant transport: : shared-memory / panama-ffm, etc.) — pour confirmer que shm / FFM est réellement actif et détecter une éventuelle rétrogradation silencieuse
raison d’un échec de chargementligne census + mode debugun côté manquant ou en échec (JS ou C++) est simplement ignoré avec un message, l’autre continue de fonctionner
garde-fou anti-plantagelatest.lognative script faulted: code=… module disabled — la raison précise de la désactivation du module
trafic de nettoyagelatest.log (une ligne par minute)journal agrégé du rythme de destruction des instances, pour observer directement le comportement de durée de vie
octets téléversésGraphicsTexture::last_upload_bytes()instrumentation dans votre code : l’efficacité de la stratégie de rectangles modifiés saute aux yeux

Après modification d’une bibliothèque, F3+T (ou le raccourci du mod « Recharger tous les scripts MTR ») recharge les scripts C++ — déchargement et redécouverte suivent le chemin complet, sans redémarrer le jeu.

Paramètres JVM

ParamètreDéfautEffet
-Djcm.nativeIdleMillis90000lorsqu’une instance existe mais n’a plus été rendue depuis longtemps, la libération n’intervient qu’au-delà de ce nombre de millisecondes ; 0 = pas de libération sur critère temporel
-Djcm.nativeIdleMax32plafond d’instances chaudes ; au-delà, on libère en priorité celles restées le plus longtemps sans rendu ; 0 = pas de plafond
-Djcm.ffm.disabledésactivécommutateur de débogage : désactive le chemin de chargement Panama FFM

Le retrait et la destruction d’instances obéissent en outre à un budget par tick (2 par défaut, la plus ancienne d’abord) et à un vidage ponctuel lors du déchargement du monde / changement de dimension / déconnexion.

Problèmes courants

Le module ne se charge pas du tout ?

  1. Vérifiez que vous utilisez un jar Release ou Complete Build — les artefacts de la CI rapide ne contiennent pas les ponts natifs et le journal affiche native bridge unavailable (ce n’est pas un problème de votre module).
  2. Vérifiez le répertoire de plateforme : le système courant ne charge que les bibliothèques de sa plateforme ; une déclaration par racine (stem) sans extension se développe automatiquement en chemins pour chaque plateforme (voir Intégration dans un pack de ressources).
  3. Vérifiez que ID est identique, caractère pour caractère, au champ id de l’entrée déclarée.
  4. Vérifiez l’ABI : l’hôte accepte 6–11 ; une bibliothèque ABI 8+ exige un hôte 2.3.0-beta-2.yanyang.7 ou supérieur.

Des carrés de substitution (tofu) à l’écran ?

Les glyphes CJK se dégradent quand la police de l’hôte ne couvre pas le point de code — transmettez un id de police de ressource à Text::font() / au constructeur de Gfx2D (par ex. jsblock:shanghai), ou vérifiez que le jeu de polices utilisé contient bien ces caractères.

Chutes de framerate ponctuelles ?

  • Vérifiez d’abord s’il s’agit d’un « redessin complet de l’écran à chaque frame » : dessinez le contenu statique une fois, redessinez le contenu dynamique par bandes clippées, et validez avec last_upload_bytes().
  • Vérifiez ensuite si plusieurs instances bougent dans la même frame : déphasez par objet les carrousels / clignotements / animations d’ouverture des portes.
  • Enfin, cherchez tout appel Api dans render (y compris indirect : quelques méthodes de contexte telles que ctx.set_auto_z_ordering provoquent un aller-retour en ABI 8+).

Comportement différent sous Windows ?

Les versions historiques antérieures à l’ABI 6 reposaient sur « une mémoire remplie de zéros directement utilisable comme objet » — cela ne survécut que par hasard à la disposition MSVC. Le contrat mtrInit() du SDK actuel a éradiqué ce problème ; si vous écrivez un hôte personnalisé, appelez impérativement mtrInit() après l’allocation du bloc d’état.


Retour à : l’accueil de la documentation.