DXEffect FrameWork und RenderJobs...

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
Stephan Theisgen
Beiträge: 94
Registriert: 29.07.2003, 11:13

DXEffect FrameWork und RenderJobs...

Beitrag von Stephan Theisgen »

Guten Abend!

Ich habe mal ne Frage an die Experten hier im Forum. Bisher habe ich meine Graphik-Engine immer so organisiert, dass eine Art Render-Manager aus dem Scene-Graph Render-Jobs erstellet, diese dann entsprechend optimiert sortiert werden und an den Renderer übergeben werden, welcher die Jobs dann einfach stur runter rendert. Dieses Konzept schießt mir jetzt aber langsam quer, da ich begonnen habe aufwendigere Rendermethoden zu verwenden. Ich habe also langsam so etwas Eigenes wie das Effect-FrameWork von DirectX aufgebaut, basierend auf Vererbung in C++, komme aber nach und nach zu dem Schluss, dass ich vielleicht besser das Effect-FrameWork von DirectX benutzen sollte. Es bietet doch mehr Möglichkeiten und auch diese Skripting-Variante wo Shader leicht mit eingebunden werden kommt mir entgegen. Natürlich könnte ich das alles mit viel Aufwand selbst implementieren, aber wozu (da ich eh nur DirectX benutze).
Trotzdem, egal ob ich es selber mache, oder das von DirectX benutze, beißt es sich mit meinem Konzept der Render-Jobs, da hier ja eher pro Objekt gedacht wird und erst mal das Objekt fertig gerendert wird (zumindest in den DXSamples).

Jetzt stellt sich mir die Frage: Wie verbinde ich das ganze, oder ist es vielleicht gar nicht mehr sinnvoll bei diesen aufwendigen Render-Methoden das als Jobs zu organisieren und sie unabhängig vom Objekt zu sortieren und zu rendern? Ist der Performance-Gewinn so niedrig, dass man das heute bei State-Of-The-Art-Projekten gar nicht mehr macht?

Wie löst/lösen Ihr/Sie das Problem? Was könnt/können Ihr/Sie mir raten? Tipps?

Viele Grüße
Stephan Theisgen
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: DXEffect FrameWork und RenderJobs...

Beitrag von Schrompf »

Bei D3D10 kann ich es nicht beurteilen, aber bei D3D9 ist es sehr wichtig, nach minimalen State Changes zu sortieren. Die schlichte Version wäre, das D3DX-Effektsystem zu benutzen und nach Effekten und Passes zu sortieren. Auf die Art gruppierst Du wenigstens identische DrawCalls zusammen. Mir war das allerdings zu eingeschränkt - ich wollte direkteren Zugriff auf Konstanten, Texturen und States, und ich musste sowieso ein eigenes kleines Skript-System bauen. Ich persönlich würde daher keine Effect Files benutzen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: DXEffect FrameWork und RenderJobs...

Beitrag von kimmi »

Da ich zur zeit OpenGL benutze und dort kein fertiges und mir bekanntes Effect-Framework zur Verfügung steht, haue ich in der ZFXCE gerade selber in die Tasten. Und ich kann nur sagen: es ist recht viel Arbeit! Allerdings baue ich die Effektfiles aus XML zusammen, was die Verarbeitung einfacher macht.
Allerdings bin ich durch diesen Umbau ebenfalls in Probleme mit dem ürsprünglichen Konzept der Renderjobs geraten. Um hier flexibler zu sein, habe ich im Scenegraphen einfach einen Rendernode eingeführt, der das Umschalten der States übernimmt. Der Renderjob an sich kümmert sich nun wirklich nur noch um das Zeichnen von Geometrie. Und zur zeit funktioniert das Konzept recht zuverlässig.
Summa-Summarum kann ich also sagen: Durch das Entwickeln eines eigenen Frameworks hat man viel Arbeit, viele Quellen für Fehler, aber auch einen Haufen Möglichkeiten, da die Einschränkung durch ein anderes Framwork wegfallen. Willst du also schnell Ergebnisse erzielen: mit einem eigenen Effekt-Framework wirst du von den tatsächlichen Aufgaben definitiv abgehalten :).

Gruß Kimmi
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: DXEffect FrameWork und RenderJobs...

Beitrag von Schrompf »

Kimmi, eine Warnung: wenn Du versuchst, das Rendern mit in den Szenegraphen zu packen, rennst Du in eine Sackgasse. Das wird Dir auf Dauer einen weiteren gewaltigen, aber notwendigen Umbau bescheren. Halte die Renderjobs unbedingt außerhalb des Szenegraphen, und mach dafür lieber einen eigenen Graphen. Wobei dann ein Graph nicht wirklich notwendig ist, ein flacher Container tut's auch. Den kann man dann viel einfacher sortieren nach all den Kriterien, die für's Rendern wichtig sind.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: DXEffect FrameWork und RenderJobs...

Beitrag von kimmi »

@Schrompf: Danke für die Warnung, das habe ich auch so gemacht. Das Einspielen der Renderjobs in den Scenegraphen wird von RenderNodes geregelt, der Scenegraph und der Renderer sind weiterhin voneinander getrennt. Die Renderjobs werden im Renderer separat gehaltn und ggfs. optimiert.
Der Rendergraph im Scenegraph spiegelt eher die Render-Hierarchie mit Shadern etc. wieder.

Aber mal aus Interesse: Wie würdest / bist du da herangegangen? In welche Sackgasse siehst du mich einbiegen? Was habe ich übersehen?

Gruß Kimmi
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: DXEffect FrameWork und RenderJobs...

Beitrag von Schrompf »

Wenn Du schreibst, dass Du in den Szenegraphen einen Rendernode eingefügt hast, der das Umschalten der Renderstates übernimmt, dann klingt das für mich, als hättest Du Szenegraph (Transformation, Parent/Child-Relations und so) und Rendering (Renderstates, Texturen, Shader) vermischt. Ganz gefährlich, sowas. Spätestens, wenn man nach minimierten Statewechseln zu sortieren versucht.

Bei mir sieht das momentan so aus (schon oft beschrieben, aber ich tu es gern immer wieder :-))

Es gibt einen Szenegraphen. Der enthält Objekte, renderbare wie nicht-renderbare. Objekte haben jeweils eine Trafo, potentielle Unterobjekte und eine AABB ihres Teilbaumes. Bei jedem Zeichenprozess wird nun der Baum anhand des Frustums selektiv abgeklappert und alle Objekte zum Zeichnen() aufgerufen. Das ist eine virtuelle Funktion, die 0 bis x Renderjobs erzeugt (ja, jedes Frame neu. Tut nicht weh) und zurückgibt, ob die Kinder damit miterledigt wurden oder ob sie noch mit Zeichnen() drankommen sollen.

Seiteninfo: Ich mag an diesem System die Idee, den Zeichenprozess selbst in eine Klasse zu kapseln. Bei uns heißt die Klasse ZeichenSzene. Für jeden Zeichenprozess wird eine ZeichenSzene aufgemacht und dann beim Iterieren über den Szenegraphen rumgereicht. Alle Renderjobs werden dann darin gesammelt. In der Theorie soll die ZeichenSzene dann auf einem separaten Core geschoben werden, wo sie die Renderjobs auflöst, sortiert und ausführt. In der Praxis passiert das momentan im SingleCore-Betrieb, weil es sonst mit all den anderen DirectX-Aufrufen für Resourcenverwaltung, Zeichnen von GUI, HUD und sowas kollidieren würde.

Der Rechenzeitaufwand während der Szenegraph-Iteration liegt primär bei den Frustum-Tests und beträgt vielleicht 20% bis 40% des Gesamtaufwands pro Frame.

Danach wird die ZeichenSzene ausgeführt. Das Ausführen bedeutet, dass die Renderjobs zuerst in DrawCalls zerlegt werden und für jeden DrawCall die Shader aus dem ShaderCache besorgt werden, die für die gegebene Kombination aus Rendermodus (normal, Bones, Instancing), Umgebungslichtmodus (konstant, per Vertex, per SH), Nebelmodus und wirksamen Lichtquellen zuständig sind. Ergebnis ist ein Array aus (bei uns ZeichenAktion genannten) Strukturen, die jeweils alle Parameter für einen DrawCall enthalten. Source auf Wunsch verfügbar. Zu jeder Aktion wird dann ein 64Bit-Index mit allen Kriterien gebildet, nach denen das Sortieren lohnt. Dann werden die ZeichenAktionen nach Index sortiert. Dann kommt optional ein Zusammenfassen aufeinanderfolgender identischer Aktionen zu Instancing-Aktionen. Und am Ende die Ausführung jeder Aktion: Rendertarget, Vertex-Deklaration, Shader, Texturen, Renderstates, Buffer und Parameter an D3D übergeben und an Draw*Primitive() verfüttern. Durch die Vorsortierung ergeben sich dann nur sehr wenige Wechsel.

Seiteninfo: Ein Shader ist bei uns ebenso XML-beschrieben und ist eigentlich ein kleines Skript, dass für oben genannte Parameter (Modus, Umlicht, Lichtquellen-Setup, Nebel) Shader-Quelltexte generiert. Die werden dann kompiliert und anhand des Parameter-Indexes im Cache abgelegt. Nebel wird als Parameter wahrscheinlich bald entfernt, weil ich die Anzahl Permutationen pro Shader senken muss und eine AllesDrin-Nebellösung bei nur minimaler zusätzlicher VertexShader-Rechenzeit problemlos ausreicht. Bei der geplanten Erweiterung der Engine um Deferred Rendering für Teile der Szene würde dann auch das Lichtquellen-Setup als Shadergenerierung-Parameter wegfallen. Blieben als Parameter dann nur noch Rendermodus und Umgebungslicht-Methode übrig - das ist dann eine überschaubare Menge Permutationen, die man notfalls auch mit simplem #ifdef im Shader abdecken kann. Und damit - um mal zum Ursprungsthema zurückzukehren - könnte man in der Tat wieder DirectX-EffectFiles benutzen, solange man deren Passes einzeln behandelt.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Stephan Theisgen
Beiträge: 94
Registriert: 29.07.2003, 11:13

Re: DXEffect FrameWork und RenderJobs...

Beitrag von Stephan Theisgen »

@kimmi: Anscheindend gibt es ja doch Leute, die vor einem ähnlichen Problem stehen wie ich. Allerdings habe ich keine guten Erfahrungen damit gemacht Sortier- und Gruppierinformationen mit in dem Scenegraphen zu packen (außer vielleicht bei Partikelsystemen, Gras oder solcher Art sich wiederholender Geometrie). Mein Problem ist immer wieder (und wahrscheinlich das einzige, was meiner ersten guten Engine wirklich im Wege steht), dass sich die Welt die ich render möchte super in einem Scene-Graph verwalten läßt, die Informationen zum performanten Rendern nicht wirklich sinnvoll in den SceneGraph integrieren lassen. Ich habe es bisher einfach nicht geschafft ein simples und performantes System zu entwickeln in dem ich sowohl die Szenen-Informationen wie auch die Informationen zum Rendern (Resourcen, performanteste Reihenfolge etc., Gruppierung etc.) elegant und performant vereinigt bekomme.

Zuerst habe ich versucht alles in einem SuperScene-Graph zu verwalten, dass schlug gründlich fehl. Dann habe ich mir überlegt, dass ich das gänzlich trennen sollte, also die Szene im Scene-Graph verwalten und parallel einen Graphen (Container) mit den RenderJobs auf Stand halten sollte. Aber auch da gibt es Probleme, nicht nur das Informationen doppelt vorhanden sind, auch das sinnvolle Updaten der RenderJobs bei Veränderung der Szene ist schwierig. Bisher habe ich das einigermaßen gut gelöst. Aber ich habe auch nie komplexere Techniken zum Rendern verwendet. Nun merke ich, dass ich für einen realen Test diese Techniken benutzen muß, wollte aber erstmal möglichst ohne großen Aufwand so ein RenderSystem implementieren, da bot sich das EffectFrameWork an. Allerdings merke ich nun, dass je anspruchsvoller die Techniken werden eine Trennung von RenderJob und Objekt im Scene-Graph nur noch schwer möglich ist. Nebenbei verhindert diese Verquickung auch eine Graphik-Api-Unabhängigkeit (was aber für mich kein Problem darstellt).

@Schrompf: Vielen Dank für die Antwort, wo könnte man das für DX10 rausfinden? Minimale State Changes sind aber jetzt nicht nur die der FFP gemeint, sondern auch für das Rendern mit Shadern? Also Shader-Wechsel, Konstanten setzen, etc.?
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: DXEffect FrameWork und RenderJobs...

Beitrag von Schrompf »

Wie Du das rauskriegst, ob ein Test auf redundante State-Zuweisungen nötig ist, weiß ich auch nicht... außer halt durch Ausprobieren :-) Die FFP benutzt ja keiner mehr, ich habe hier mit "States" die komplette Breite von Graka-Einstellungen gemeint. Wir sortieren nach folgenden Kriterien (in absteigender Wichtigkeit)

- optionale Sequenz-ID (Manchmal muss man doch eine Reihenfolge garantieren)
- Rendertarget
- Vertex-Deklaration (damit verbunden auch VertexBuffer-Setup, also mit/ohne Instancing-Buffer und so)
- Shader (Vertex und Pixel behandel ich immer zusammen, ebenso Renderstates wie AlphaBlending, DepthTesting, Culling und so)
- Texturen (ich behandle das komplette Set aus Diffuse, Normal, Specular usw. immer zusammen, ShadowMaps zwangsweise weniger streng)
- Mesh (also VertexBuffer)
- Parameter

Das Sortieren nach Mesh und Parametern bringt praktisch nichts - anscheinend ist das Anbinden von Vertex- und IndexBuffern nahezu kostenlos. Und Parameter sind sowieso nur Shaderkonstanten, die sind also auch quasi kostenlos. Texturwechsel zu vermeiden ist leidlich wichtig, Shaderwechsel ziemlich wichtig, Vertexdeklaration erstaunlich wichtig und Rendertarget-Wechsel sowieso. Unbedingt DepthTest-Methode stabil halten, der Wechsel davon ist so teuer wie ein RenderTarget-Wechsel! Also DepthWrite oder DepthTest an/aus tut keinem weh, aber wenn Du z.B. von LessEqual zu GreaterThan wechselst, läuft das intern auf ein Fullscreen-Update hinaus. Aber das nur nebenbei. Ganz oben auf der Liste der Sortierkriterien steht aber eine Sequenz-ID, weil ich festgestellt habe, dass ich für manche Zwecke unbedingt garantieren muss, dass eine gewisse Reihenfolge zwischen manchen Renderjobs eingehalten wird. Bei uns ist das konkret das Terrain, bei dem über AlphaSplatting verschiedene Materialschichten kombiniert werden. Die müssen unbedingt in der Reihenfolge ankommen, in der sie beauftragt werden. Innerhalb einer SequenzID darf aber frei sortiert werden, und der Großteil der DrawCall hat die selbe ID, weswegen das Sortiersystem trotzdem sehr gut funktioniert.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: DXEffect FrameWork und RenderJobs...

Beitrag von kimmi »

Ah, ich seh das Problem: meine Namen :). Der "SceneGraph" hält einen Graphen, der die Scenehierarchie mit allen Transformationen sowie den AABB beinhaltet. Hier wird auch das Culling ausgeführt. Dann gibt es einen weiteren Graphen, der komplett unabhängig von der Scenen-Hierarchie ist und den RenderGraph abbildet. Beide sind komplett voneinander getrennt, unabhängig und somit nicht durchmischt ( also komplett orthogonal, ich habe mich bisher erfolgrecih vom Durchmischen dieser beiden Graphen abhalten können ).

Das, was ich bisher als "SceneGraph" bezeichnet habe, ist aber eigentlich ein Kompositum, um die unterschiedlichen Graphen zu verwalten. Da habe ich einen Kandidaten, den ich dringendst umbenennen sollte. Unser Renderer kapselt aus historischen Gründen ( wir haben unter Windows und Linux funktionierende Renderer, daher müssen wir mindest OpenGL anbieten, DX ist unter Windows aber nun mal schicker ) die Render-API. Dort liegt wenig bis keine Logik, um die Drawcalls zu optimieren bzw. umzusortieren.
Zur Zeit ist die trennung bei uns etwas ungeschickt gewählt, scheint mir. Aber das brachte die Restrukturierung von mir leider mit sich. Da muß ich ( wie an noch so vielen anderen Stellen ) noch Gehirnschmalz investieren.

Danke auf jeden Fall für das Feedback :).

Gruß Kimmi
Antworten