Technology

Geschichten aus den Schützengräben der Optimierung: Speicherplatz sparen mit Addressables

PATRICK DEVARNEY / UNITY TECHNOLOGIESContributor
Mar 31, 2021|12 Min.
Geschichten aus den Schützengräben der Optimierung: Speicherplatz sparen mit Addressables
Diese Website wurde aus praktischen Gründen für Sie maschinell übersetzt. Die Richtigkeit und Zuverlässigkeit des übersetzten Inhalts kann von uns nicht gewährleistet werden. Sollten Sie Zweifel an der Richtigkeit des übersetzten Inhalts haben, schauen Sie sich bitte die offizielle englische Version der Website an.

Effizientes Streaming von Assets in den und aus dem Speicher ist ein Schlüsselelement eines jeden hochwertigen Spiels. Als Berater in unserem Professional Services Team habe ich mich bemüht, die Leistung vieler Kundenprojekte zu verbessern. Deshalb möchte ich Ihnen einige Tipps geben, wie Sie das Unity Addressable Asset System nutzen können, um Ihre Strategie zum Laden von Inhalten zu verbessern.

Speicher ist eine knappe Ressource, die Sie sorgfältig verwalten müssen, insbesondere wenn Sie ein Projekt auf eine neue Plattform portieren. Die Verwendung von Addressables kann den Laufzeitspeicher verbessern, indem schwache Referenzen eingeführt werden, die verhindern, dass unnötige Assets geladen werden. Schwache Referenzen bedeuten, dass Sie die Kontrolle darüber haben, wann das referenzierte Asset in den und aus dem Speicher geladen wird; das Addressable System findet alle notwendigen Abhängigkeiten und lädt sie ebenfalls. Dieser Blog befasst sich mit einer Reihe von Szenarien und Problemen, auf die Sie stoßen können, wenn Sie Ihr Projekt für die Verwendung des Unity Addressable Asset Systems einrichten - und erklärt, wie Sie diese erkennen und umgehend beheben können.

Beispiel für eine Bestandsaufnahme
Adressables verwenden

Für diese Reihe von Empfehlungen werden wir mit einem einfachen Beispiel arbeiten, das folgendermaßen aufgebaut ist:

  • Wir haben ein InventoryManager-Skript in der Szene mit Verweisen auf unsere drei Inventarobjekte: Schwert, Boss-Schwert, Schild-Fertigteile.
  • Diese Mittel werden während des Spiels nicht immer benötigt.

Sie können die Projektdateien für dieses Beispiel auf meinem GitHub herunterladen. Wir verwenden das Vorschaupaket Memory Profiler, um den Speicher zur Laufzeit zu betrachten. In Unity 2020 LTS müssen Sie zunächst Vorschaupakete in den Projekteinstellungen aktivieren, bevor Sie dieses Paket über den Paketmanager installieren können.

Wenn Sie Unity 2021.1 verwenden, wählen Sie die Option Paket nach Name hinzufügen aus dem zusätzlichen Menü (+) im Fenster Paketmanager. Verwenden Sie den Namen "com.unity.memoryprofiler".

Stufe 1: Harte Referenzen, keine Addressables

Beginnen wir mit der einfachsten Implementierung und arbeiten uns dann zum besten Ansatz für die Einrichtung unserer Addressables-Inhalte vor. Wir werden einfach harte Referenzen (direkte Zuweisung im Inspektor, verfolgt durch GUID) auf unsere Prefabs in einem MonoBehaviour anwenden, das in unserer Szene existiert.

Inventarisierungssystem

Wenn die Szene geladen wird, werden auch alle Objekte in der Szene und ihre Abhängigkeiten in den Speicher geladen. Das bedeutet, dass jedes in unserem InventorySystem aufgelistete Prefab im Speicher liegt, zusammen mit allen Abhängigkeiten dieser Prefabs (Texturen, Meshes, Audio, etc.).

Wenn wir einen Build erstellen und mit dem Memory Profiler einen Schnappschuss machen, können wir sehen, dass die Texturen für unsere Assets bereits im Speicher gespeichert sind, obwohl keine von ihnen instanziiert sind.

Die Texturen für unsere Objekte werden im Speicher abgelegt, auch wenn sie noch nicht instanziiert sind.
Die Texturen für unsere Objekte werden im Speicher abgelegt, auch wenn sie noch nicht instanziiert sind.

Problem: Es gibt Vermögenswerte im Speicher, die wir derzeit nicht benötigen. Bei einem Projekt mit einer großen Anzahl von Inventarobjekten würde dies zu einer erheblichen Belastung des Laufzeitspeichers führen.

Stufe 2: Addressables implementieren

Um das Laden unerwünschter Assets zu vermeiden, werden wir unser Inventarsystem auf Addressables umstellen. Durch die Verwendung von Asset-Referenzen anstelle von direkten Referenzen wird verhindert, dass diese Objekte zusammen mit unserer Szene geladen werden. Verschieben wir unsere Inventar-Prefabs in eine Addressables-Gruppe und ändern wir InventorySystem so, dass Objekte über die Addressables API instanziiert und freigegeben werden.

Asset-Menü mit den Optionen 'Schwert', 'Bossschwert' und 'Schild'

Erstellen Sie den Player und machen Sie einen Schnappschuss. Beachten Sie, dass sich noch keine der Assets im Speicher befinden, was gut ist, da sie noch nicht instanziiert wurden.

Es erscheinen keine Texturen für Inventarobjekte im Speicher, sondern nur TextMeshPro-Texturen.
Es erscheinen keine Texturen für Inventarobjekte im Speicher, sondern nur TextMeshPro-Texturen.

Instanziieren Sie alle Elemente, damit sie korrekt mit ihren Assets im Speicher erscheinen.

Adressables verwenden

Problem: Wenn wir alle unsere Gegenstände instanziieren und das Boss-Schwert despawnen, sehen wir immer noch die Textur des Boss-Schwertes "BossSword_E" im Speicher, obwohl es nicht in Gebrauch ist. Der Grund dafür ist, dass Sie Asset-Bündel zwar teilweise laden können, es aber nicht möglich ist, sie automatisch teilweise zu entladen. Dieses Verhalten kann vor allem bei Bundles mit vielen Assets problematisch werden, z. B. bei einem einzelnen AssetBundle, das alle unsere Inventar-Prefabs enthält. Keines der Assets im Bundle wird entladen, bis das gesamte AssetBundle nicht mehr benötigt wird, oder bis wir die kostspielige CPU-Operation Resources.UnloadUnusedAssets() aufrufen.

Die Textur BossSword_E bleibt im Speicher, auch nachdem das Bossschwert freigegeben wurde.
Die Textur BossSword_E bleibt im Speicher, auch nachdem das Bossschwert freigegeben wurde.
Data Layout
Stufe 3: Kleinere Bündel

Um dieses Problem zu beheben, müssen wir die Art und Weise ändern, wie wir unsere AssetBundles organisieren. Während wir derzeit eine einzige Addressable Group haben, die alle ihre Assets in ein AssetBundle packt, können wir stattdessen ein AssetBundle für jedes Prefab erstellen. Diese detaillierteren AssetBundles verringern das Problem, dass große Bundles Assets im Speicher halten, die wir nicht mehr benötigen.

Diese Änderung ist einfach. Wählen Sie eine Addressables-Gruppe und dann Content Packaging & Loading > Advanced Options > Bundle Mode, und gehen Sie zum Inspector, um den Bundle Mode von Pack Together auf Pack Separate zu ändern.

Wenn Sie diese Addressables-Gruppe mit Separat verpacken erstellen, können Sie für jedes Asset in der Addressables-Gruppe ein AssetBundle erstellen.

Menü "Adressierbare Anlagengruppen

Die Assets und Bundles sehen dann wie folgt aus:

Vermögenswerte und Vermögensbündel

Kehren wir nun zu unserem ursprünglichen Test zurück: Wenn wir unsere drei Gegenstände spawnen und dann das Bossschwert despawnen, bleiben keine unnötigen Werte mehr im Speicher. Die Bossschwert-Texturen werden nun entladen, da das gesamte Paket nicht mehr benötigt wird.

Problem: Wenn wir alle drei Gegenstände spawnen und eine Speicheraufnahme machen, erscheinen doppelte Gegenstände im Speicher. Genauer gesagt, führt dies zu mehreren Kopien der Texturen "Sword_N" und "Sword_D". Wie kann das passieren, wenn wir nur die Anzahl der Bündel ändern?

Texturen mit Duplikaten
Stufe 4: Duplizierte Assets korrigieren

Um diese Frage zu beantworten, sollten wir uns ansehen, was in den drei von uns erstellten Paketen enthalten ist. Wir haben zwar nur drei vorgefertigte Assets in Bundles platziert, aber es gibt zusätzliche Assets, die implizit als Abhängigkeiten der vorgefertigten Assets in diese Bundles gezogen werden. Das vorgefertigte Schwert-Asset enthält beispielsweise auch Mesh-, Material- und Textur-Assets, die einbezogen werden müssen. Wenn diese Abhängigkeiten nicht ausdrücklich an anderer Stelle in Addressables enthalten sind, werden sie automatisch zu jedem Bundle hinzugefügt, das sie benötigt.

Die Asset-Pakete für das Schwert und das BossSchwert enthalten einige der gleichen Abhängigkeiten.
Unsere Asset-Bundles für das Schwert und das BossSword enthalten einige der gleichen Abhängigkeiten.

Addressables enthält ein Analysefenster, das bei der Diagnose des Bundle-Layouts hilft. Öffnen Sie Fenster > Asset Management > Addressables > Analyze und führen Sie die Regel Bundle Layout Preview aus. Hier sehen wir, dass das sword-Bundle explizit die sword.prefab enthält, aber es gibt viele implizite Abhängigkeiten, die ebenfalls in dieses Bundle gezogen werden.

Menü Regeln

Führen Sie im selben Fenster den Befehl Duplicate Bundle Dependencies prüfen aus. Diese Regel hebt die Assets hervor, die in mehreren Asset-Bundles enthalten sind, die auf unserem aktuellen Addressables-Layout basieren.

Menü Regeln mit Duplikaten
Die Analyse zeigt, dass wir doppelte Texturen und Meshes zwischen unseren Schwertbündeln haben, und alle drei Bündel duplizieren denselben Shader.

Wir können die Duplizierung dieser Vermögenswerte auf zwei Arten verhindern:

1. Legen Sie die Prefabs Schwert, BossSchwert und Schild in dasselbe Bundle, damit sie gemeinsame Abhängigkeiten haben, oder

2. Nehmen Sie die duplizierten Assets explizit irgendwo in Addressables auf

Wir wollen vermeiden, dass mehrere Inventar-Prefabs im selben Bundle enthalten sind, um zu verhindern, dass unerwünschte Assets im Speicher verbleiben. Daher werden wir die duplizierten Assets ihren eigenen Bündeln (Bündel 4 und Bündel 5) hinzufügen.

Addressables in ihren eigenen Bündeln
Die doppelten Texturen werden ausdrücklich in eigenen Bündeln platziert.

Zusätzlich zur Analyse unserer Pakete können die Analyseregeln die beanstandeten Assets über die Fix Selected Rules automatisch reparieren. Drücken Sie diese Schaltfläche, um eine neue Addressable Group mit dem Namen "Duplicate Asset Isolation" zu erstellen, die die vier duplizierten Assets enthält. Setzen Sie den Bündelmodus dieser Gruppe auf "Separat verpacken", um zu verhindern, dass andere, nicht mehr benötigte Assets im Speicher verbleiben.

Screenshot des Editors - Addressables Gruppen
Stufe 5: Reduzierung der Asset-Bundle-Metadatengröße in großen Projekten

Die Verwendung dieser AssetBundle-Strategie kann im großen Maßstab zu Problemen führen. Für jedes AssetBundle, das zu einem bestimmten Zeitpunkt geladen wird, gibt es einen Speicher-Overhead für AssetBundle-Metadaten. Diese Metadaten werden wahrscheinlich eine unannehmbare Menge an Speicherplatz verbrauchen, wenn wir diese aktuelle Strategie auf Hunderte oder Tausende von Inventarelementen erweitern. Weitere Informationen zu AssetBundle-Metadaten finden Sie in den Addressables-Dokumenten.

Zeigen Sie die aktuellen AssetBundle-Metadatenspeicherkosten im Unity Profiler an. Gehen Sie zum Speichermodul und machen Sie einen Speicher-Snapshot. Suchen Sie in der Kategorie Sonstiges > SerializedFile.

Archiv
Derzeit sind 1.819 Bundles mit einer Gesamtgröße von 263 MB im SerializedFile-Speicher geladen.

Für jedes geladene AssetBundle ist ein SerializedFile-Eintrag im Speicher vorhanden. Bei diesem Speicher handelt es sich um AssetBundle-Metadaten und nicht um die eigentlichen Assets in den Bundles. Diese Metadaten umfassen:

  • Zwei Dateilesepuffer
  • Ein Typenbaum, der alle im Paket enthaltenen eindeutigen Typen auflistet
  • Ein Inhaltsverzeichnis, das auf die Assets verweist

Von diesen drei Elementen beanspruchen die Dateilesepuffer den meisten Platz. Diese Puffer sind jeweils 64 KB auf PS4, Switch und Windows RT und 7 KB auf allen anderen Plattformen. Im obigen Beispiel sind 1.819 Bündel * 64 KB * 2 Puffer = 227 MB nur für Puffer.

Da die Anzahl der Puffer linear mit der Anzahl der AssetBundles skaliert, besteht die einfache Lösung zur Reduzierung des Speichers darin, weniger Bundles zur Laufzeit zu laden. Allerdings haben wir es bisher vermieden, große Pakete zu laden, um zu verhindern, dass unerwünschte Assets im Speicher verbleiben. Wie können wir also die Anzahl der Bündel reduzieren und gleichzeitig die Granularität beibehalten?

Ein solider erster Schritt wäre die Gruppierung von Assets auf der Grundlage ihrer Verwendung in der Anwendung. Wenn Sie auf der Grundlage Ihrer Anwendung intelligente Annahmen treffen können, dann können Sie Assets gruppieren, von denen Sie wissen, dass sie immer zusammen geladen und entladen werden, z. B. solche, die auf der Spielebene basieren, in der sie sich befinden.

Andererseits könnten Sie sich in einer Situation befinden, in der Sie keine sicheren Annahmen darüber treffen können, wann Ihr Vermögen benötigt wird bzw. nicht benötigt wird. Wenn Sie z. B. ein Open-World-Spiel entwickeln, können Sie nicht einfach alles aus dem Waldbiom in einem einzigen Asset-Bündel zusammenfassen, weil Ihre Spieler einen Gegenstand aus dem Wald nehmen und ihn zwischen den Biomen tragen könnten. Das gesamte Waldbündel verbleibt im Speicher, da der Spieler noch einen Gegenstand aus dem Wald benötigt.

Glücklicherweise gibt es eine Möglichkeit, die Anzahl der Pakete zu reduzieren und gleichzeitig den gewünschten Grad an Granularität beizubehalten. Wir sollten intelligenter mit der Deduplizierung unserer Pakete umgehen.

Die integrierte Deduplizierungs-Analyseregel, die wir ausgeführt haben, erkennt alle Assets, die in mehreren Bündeln enthalten sind, und verschiebt sie effizient in eine einzige Addressable Group. Wenn Sie diese Gruppe auf "Separat verpacken" setzen, erhalten Sie ein Asset pro Bundle. Es gibt jedoch einige duplizierte Assets, die wir sicher zusammenpacken können, ohne dass es zu Speicherproblemen kommt. Betrachten Sie das folgende Diagramm:

Ein Vermögenswert pro Bündel

Wir wissen, dass die Texturen "Sword_N" und "Sword_D" von denselben Bundles (Bundle 1 und Bundle 2) abhängig sind. Da diese Texturen dieselben Eltern haben, können wir sie sicher zusammenpacken, ohne dass es zu Gedächtnisproblemen kommt. Beide Schwerttexturen müssen immer geladen oder entladen werden. Es besteht nie die Sorge, dass eine der Texturen im Speicher verbleiben könnte, da es nie einen Fall gibt, in dem wir speziell eine Textur und nicht die andere verwenden.

Wir können diese verbesserte Deduplizierungslogik in unserer eigenen Addressables Analyze Rule implementieren. Wir werden von der bestehenden Regel CheckForDupeDependencies.cs ausgehen. Den vollständigen Implementierungscode können Sie im Beispiel für das Inventory System sehen. Bei diesem einfachen Projekt haben wir lediglich die Gesamtzahl der Bündel von sieben auf fünf reduziert. Aber stellen Sie sich ein Szenario vor, in dem Ihre Anwendung Hunderte, Tausende oder noch mehr doppelte Assets in Addressables hat. Bei der Zusammenarbeit mit Unknown Worlds Entertainment im Rahmen eines Professional Services-Auftrags für das Spiel Subnautica umfasste das Projekt nach Anwendung der integrierten Deduplizierungs-Analyseregel zunächst insgesamt 8.718 Pakete. Nach Anwendung der benutzerdefinierten Regel zur Gruppierung deduplizierter Assets auf der Grundlage ihrer Bundle-Eltern konnten wir diese Zahl auf 5.199 Bundles reduzieren. Mehr über unsere Arbeit mit dem Team erfahren Sie in diesem Fallbericht.

Das bedeutet eine Verringerung der Anzahl der Pakete um 40 % bei gleichem Inhalt und gleichem Granularitätsgrad. Durch die Reduzierung der Anzahl der Bundles um 40 % konnte auch die Größe der SerializedFile zur Laufzeit um 40 % reduziert werden (von 311 MB auf 184 MB).

Fazit

Die Verwendung von Addressables kann den Speicherverbrauch erheblich reduzieren. Sie können den Speicherplatz weiter reduzieren, indem Sie Ihre AssetBundles so organisieren, dass sie Ihrem Anwendungsfall entsprechen. Schließlich sind die eingebauten Analyseregeln konservativ, um allen Anwendungen gerecht zu werden. Durch das Schreiben eigener Analyseregeln können Sie das Layout von Paketen automatisieren und für Ihre Anwendung optimieren. Um Speicherprobleme zu erkennen, sollten Sie weiterhin häufig Profile erstellen und im Fenster Analysieren nachsehen, welche Assets explizit und implizit in Ihren Bundles enthalten sind. In der Addressable Asset System-Dokumentation finden Sie weitere Best Practices, einen Leitfaden für die ersten Schritte und eine erweiterte API-Dokumentation.

Wenn Sie mehr praktische Hilfe benötigen, um zu lernen, wie Sie Ihr Content Management mit dem Addressable Asset System verbessern können, wenden Sie sich an den Vertrieb, um eine professionelle Schulung zu erhalten.