Was ist der effizienteste Weg, um auf Ressourcen zuzugreifen?

7

Ich habe einen Ressourcenmanager, der Ressourcen lädt und zurückgibt. Derzeit kehre ich nur shared_ptrsfür die von mir erstellten Ressourcen zurück, denke aber darüber nach, so etwas wie einfache Handles zu verwenden, wobei Sie eine ID erhalten, mit der Sie auf die Ressource zugreifen. Die Gründe sind expliziteres Eigentum und auch weniger Overhead als Verwendung smart_ptrs.

Sind Ressourcenhandles der effizienteste Weg, um auf Ressourcen zuzugreifen, sollte ich mich an shared_ptrs halten oder gibt es andere Alternativen?

Der Grund , den ich frage , ist, dass ich diesen Artikel gelesen habe , was mich gefragt hat, ob ich stattdessen so etwas implementieren soll.

KaiserJohaan
quelle
Ich würde definitiv sagen, dass ganzzahlige Handles der richtige Weg sind. Wenn Sie möchten, dass es auffälliger / intuitiver wird, können Sie auch einfach Aufzählungen verwenden. Auf diese Weise können Sie die verschiedenen Assets besser auflisten.
Krythic

Antworten:

4

Ich bin mit Vittorio nicht einverstanden, dass kein gemeinsames Eigentum an Ressourcen erforderlich ist, und obwohl ich die Implementierung von nicht mag shared_ptr, kann sie gut funktionieren.

Sie möchten eine gemeinsame Eigentümerschaft, da natürlich möglicherweise mehrere Spielobjekte dieselbe Ressource verwenden. Noch wichtiger ist, dass Sie, wenn Sie mehrere Ebenen haben und die Ebenen mindestens laden möchten, eine Möglichkeit benötigen, die Vereinigung und Schnittmenge der von beiden benötigten Ressourcen zu berechnen.

Eine Möglichkeit, dies zu tun, die mehr als nur ein erfordert shared_ptr, besteht darin, das "Laden" eines Levels ohne Ressourcen zu ermöglichen und es das gemeinsame Eigentum an den benötigten Ressourcen übernehmen zu lassen. Entladen Sie dann das alte Level und entfernen Sie dessen Besitz. Laden Sie dann für jede Ressource, deren Eigentümer noch aktiv ist, die jedoch nicht geladen ist, die Ressource.

Für eine Ressource A, die von Level 1 und 2 benötigt wird, lautet der Referenzzähler ungefähr wie folgt:

load level 1
ref=1
load A
load level 2
ref=2
unload level 1
ref=1

Für eine Ressource B, die nur von Stufe 1 verwendet wird, gilt Folgendes:

load level 1
ref=1
load B
load level 2
ref=1
unload level 1
ref=0 (removed)

Und eine Ressource C, die nur von Stufe 2 verwendet wird, sieht folgendermaßen aus:

load level 1
ref=0 (implicit, as no resource C exists)
load level 2
ref=1
unload level 1
load C

Eine Methode könnte darin bestehen, eine Indirektionsebene zu haben. Sie speichern shared_ptrin einem ResourceHolder. Diese Inhaber sind auch in einer Liste irgendeiner Art. Beim Laden eines Levels erstellen Sie diese ResourceHolderObjekte und halten sie fest . Sie enthalten a unique_ptrzur eigentlichen Ressource (anfangs nullptr) und den Pfad oder andere Informationen, die zum eventuellen Laden des Elements erforderlich sind.

Nachdem Sie die neue Ebene geladen und die alte entladen haben, können Sie die Liste der Live- ResourceHolderObjekte durchlaufen, nach Objekten suchen, die ein nullptrHandle für die eigentliche Ressource haben, und diese dann laden. Wenn das shared_ptrto a ResourceHoldereine Ref-Anzahl von 0 erreicht und es entlädt, gibt es natürlich auch die Ressource frei, und Sie sollten sicherstellen, dass es sich selbst aus der Liste der Live-Ressourcen entfernt.

Sie können es viel besser machen als, shared_ptraber es ist ein perfekter Ort, um loszulegen. Optimieren Sie es später zu etwas Besserem, sobald Sie ein funktionierendes Spiel haben.

Sean Middleditch
quelle
1
Was sind die Vorteile des gemeinsamen Ressourcenbesitzes? Selbst wenn viele Objekte dieselbe Ressource benötigen, können sie einfach eine Referenz oder einen nicht besitzenden Zeiger darauf speichern.
Vittorio Romeo
2
Ich habe es in diesem Beitrag anhand von Beispielen für Referenzzählsequenzen erklärt. Wenn Sie keinen gemeinsamen Besitz haben, wissen Sie nicht, wann Sie Ressourcen beim Übergang von Ebenen entladen oder neu laden müssen, es sei denn, Sie machen die Ebenen selbst zu Besitzern (was beschissen ist, weil ich nicht möchte, welche Ressourcen manuell zu kennzeichnen sind Jede Ebene benötigt (ich möchte nur Objekte zu einer Ebenenressource hinzufügen und alles automatisch funktionieren lassen).
Sean Middleditch
Ihr Anwendungsfall ist wirklich interessant und möglicherweise ein sehr vernünftiger Fall für echtes gemeinsames Eigentum. Ich habe oft eine Phobie von shared_ptrund sogar von GC im Allgemeinen entwickelt, obwohl ich in der Vergangenheit in Teams gearbeitet habe, in denen beide als Ausreden angesehen wurden, um nicht über Ressourcenmanagement und Eigenverantwortung nachzudenken. Das Ergebnis war sehr logisch Lecks, bei denen der analoge Absturz des baumelnden Zeigers zu einem stillen Speicherverlust wurde, der unter dem Radar des Testens flog, da eine Ressource einen shared_ptr für einen anderen speichern würde, dessen Lebensdauer lange hätte beendet werden müssen [...]
[...] vor der äußeren Ressource, die das speichert shared_ptr. Seitdem zögere ich nicht mehr mit shared_ptrs und sogar mit GC, mit all den schwer zu erkennenden Lecks, auf die ich gestoßen bin.
In unserem Fall war es so, als würde der Renderer eine matte Ausschlussliste mit starken Netzreferenzen speichern, um sie vom Rendern auszuschließen, ein Licht könnte eine Lichtausschlussliste mit starken Refs speichern, ein Shader-Plugin könnte den Besitz für eine Textur teilen, die es als Eingabe verwendet, usw. . etc. etc. Alles wird letztendlich im Besitz aller möglichen anderen Dinge geteilt, und oft wird eine sperrige Ressource wie ein Netz von 20 Stellen im System im Besitz geteilt ... und 19 dieser Orte werden möglicherweise null die Referenz oder entfernen Sie es aus einer Liste als Antwort auf das richtige Ereignis, aber man würde scheitern ...
0

Nein, sie sind nicht die effizienteste Möglichkeit, auf Ressourcen zuzugreifen. Der gleiche Sean Middleditch, der Ihnen eine Antwort gegeben hat, schrieb einen Artikel " Gefahren von std :: shared_ptr " gegen die Verwendung shared_ptretwa ein Jahr später. Möglicherweise möchten Sie erneut Handles verwenden. Wie er selbst schrieb:

Game Engines unterstützen diese Art von geliehener Referenz seit langem mit eindeutigen ID-Handles. Anstatt einen Zeiger (intelligent oder anderweitig) auf ein Objekt zu speichern, speichern Sie stattdessen eine numerische ID (möglicherweise in einen Schablonentyp eingeschlossen, um die Typensicherheit zu gewährleisten).

Weitere Informationen dazu, wie Sie bei der Verwendung von Griffen im Gegensatz zu viel effizienter arbeiten können, finden Sie im Abschnitt Effizienz in seinem Artikel shared_ptr.

Weitere Vorteile von IDs finden Sie im kurzen Artikel zum Entity Component System Pattern .

VisorZ
quelle