Hintergrund:
Ich entwerfe ein einfaches 3D-Render-System für eine Architektur vom Typ eines Entitätskomponentensystems unter Verwendung von C ++ und OpenGL. Das System besteht aus einem Renderer und einem Szenendiagramm. Wenn ich die erste Iteration des Renderers abgeschlossen habe, verteile ich möglicherweise das Szenendiagramm in der ECS-Architektur. Im Moment ist es auf die eine oder andere Weise ein Platzhalter. Wenn möglich, sind meine Ziele für den Renderer:
- Einfachheit . Dies ist für ein Forschungsprojekt und ich möchte meine Systeme leicht ändern und erweitern können (daher der ECS-Ansatz).
- Leistung . Meine Szene könnte viele kleine Modelle und auch große Volumen mit viel Geometrie haben. Es ist nicht akzeptabel, Objekte aus dem OGL-Kontext zu erfassen und die Geometrie in jedem Renderrahmen zu puffern. Ich strebe eine Datenlokalität an, um Cache-Fehler zu vermeiden.
- Flexibilität . Es muss in der Lage sein, Sprites, Modelle und Volumes (Voxel) zu rendern.
- Entkoppelt . Das Szenendiagramm kann nach dem Schreiben meines Renderers in die ECS-Kernarchitektur umgestaltet werden.
- Modular . Es wäre schön, verschiedene Renderer austauschen zu können, ohne mein Szenendiagramm zu ändern.
- Referenzielle Transparenz , dh ich kann ihr zu jedem Zeitpunkt eine gültige Szene geben und es wird immer das gleiche Bild für diese Szene gerendert. Insbesondere dieses Ziel ist nicht unbedingt erforderlich. Ich dachte, dies würde die Serialisierung von Szenen vereinfachen (ich muss in der Lage sein, Szenen zu speichern und zu laden) und mir die Flexibilität geben, zur Laufzeit verschiedene Szenen zu Test- / Experimentierzwecken auszutauschen.
Problem und Ideen:
Ich habe mir verschiedene Ansätze ausgedacht, um zu versuchen, aber ich habe Probleme damit, die OGL-Ressourcen (VAO, VBOs, Shader usw.) für jeden Renderknoten zwischenzuspeichern. Im Folgenden sind die verschiedenen Caching-Konzepte aufgeführt, die ich mir bisher ausgedacht habe:
- Zentraler Cache. Jeder Szenenknoten hat eine ID und der Renderer verfügt über einen Cache, der IDs den Renderknoten zuordnet. Jeder Renderknoten enthält die VAO und VBOs, die der Geometrie zugeordnet sind. Ein Cache-Fehler erfasst Ressourcen und ordnet die Geometrie einem Renderknoten im Cache zu. Wenn die Geometrie geändert wird, wird ein schmutziges Flag gesetzt. Wenn der Renderer beim Durchlaufen der Szenenknoten ein fehlerhaftes Geometrie-Flag sieht, werden die Daten mithilfe des Renderknotens zurückgewiesen. Wenn ein Szenenknoten entfernt wird, wird ein Ereignis gesendet und der Renderer entfernt den zugeordneten Renderknoten aus dem Cache, während Ressourcen freigegeben werden. Alternativ wird der Knoten zum Entfernen markiert und der Renderer ist für das Entfernen verantwortlich. Ich denke, dieser Ansatz erreicht das Ziel 6 am ehesten, während auch 4 und 5 berücksichtigt werden. 2 leidet unter der zusätzlichen Komplexität und dem Verlust der Datenlokalität bei Kartensuchen anstelle von Array-Zugriff.
- Verteilter Cache . Ähnlich wie oben, außer dass jeder Szenenknoten einen Renderknoten hat. Dies umgeht die Kartensuche. Um die Datenlokalität zu adressieren, könnten die Renderknoten im Renderer gespeichert werden. Dann könnten die Szenenknoten stattdessen Zeiger zum Rendern von Knoten haben, und der Renderer setzt den Zeiger auf einen Cache-Fehler. Ich denke, diese Art ahmt einen Entity-Component-Ansatz nach, sodass er mit dem Rest der Architektur übereinstimmt. Das Problem hierbei ist, dass jetzt Szenenknoten rendererimplementierungsspezifische Daten enthalten. Wenn ich ändere, wie Dinge im Renderer gerendert werden (wie das Rendern von Sprites oder Volumes), muss ich jetzt den Renderknoten ändern oder dem Szenenknoten weitere "Komponenten" hinzufügen (was bedeutet, dass auch das Szenendiagramm geändert wird). Auf der positiven Seite scheint dies der einfachste Weg zu sein, um meinen Renderer für die erste Iteration zum Laufen zu bringen.
- Verteilte Metadaten . In jedem Szenenknoten wird eine Renderer-Cache-Metadatenkomponente gespeichert. Diese Daten sind nicht implementierungsspezifisch, sondern enthalten eine ID, einen Typ und alle anderen relevanten Daten, die vom Cache benötigt werden. Anschließend kann die Cache-Suche mithilfe der ID direkt in einem Array durchgeführt werden, und der Typ kann angeben, welche Art von Rendering-Ansatz verwendet werden soll (z. B. Sprites oder Volumes).
- Besucher + verteiltes Mapping . Der Renderer ist ein Besucher und Szenenknoten sind Elemente im Besuchermuster. Jeder Szenenknoten enthält einen Cache-Schlüssel (wie die Metadaten, aber nur eine ID), den nur der Renderer bearbeitet. Die ID kann für das Array anstelle der allgemeinen Kartensuche verwendet werden. Der Renderer kann dem Szenenknoten erlauben, eine andere Renderfunktion basierend auf dem Typ des Szenenknotens auszulösen, und die ID kann von jedem Cache verwendet werden. Eine Standard-ID oder eine ID außerhalb des Bereichs würde einen Cache-Fehler anzeigen.
Wie würden Sie dieses Problem lösen? Oder haben Sie Vorschläge? Danke, dass du meine Textwand gelesen hast!