Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Mär 28, 2024 22:51

Foren-Übersicht » Programmierung » OpenGL
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 16 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Autor Nachricht
 Betreff des Beitrags: Data driven rendering
BeitragVerfasst: Di Sep 08, 2015 23:25 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hatte erst überlegt es in mein Projekt zu posten aber ich erhoffe mir eigentlich ein Gedankenaustausch und das ist in der Projektecke eher nicht gegeben.

Ich hab die Tage wieder mal Zeit gefunden an mein OpenGL Code zu arbeiten.
In meinem Radon Framework Thread hab ich ja angefangen meine OpenGL Implementierung zu abstrahieren, damit mein UI, Mesh und Shader nicht an bestimmte OpenGL Funktionen gekoppelt sind.

In der Vergangenheit hatte ich diverse Klassen um Models, Shader, Texturen und so weiter zu verwalten und die waren z.B. nicht mit OpenGL 4 Kompatibel, da hab ich dann durch Interface und Ableitung mehrere Varianten für die einzelnen OpenGL APIs gebaut und das wurde immer mehr zum Stress es zu pflegen.
Dann kam ich darauf, dass jede Engine diese Klassen anders in OpenGL anbindet und so hab ich mich entschieden das zu abstrahieren.
Zur gleichen Zeit hatte ich mich auch wegen Glew geärgert, weil 2/3 meiner finalen Binary ungenutze OpenGL API war und entschieden das ganze zur Laufzeit zu laden und auch zu generieren.

Ich hatte darauf hin libSpec geschrieben und ein kleines Konsolen Tool zum erzeugen von eigenen Binding Code.
Aus den Gedankenprozess kam dann die Idee eine VM zu bauen, wobei es neben jeden OpenGL Befehl noch Opcode für Register Operationen gibt.
Ich kann also OpenGL Befehle in Bytecode verpacken und ausführen und in profiling war das auch nicht langsamer.

Die Tage bin ich dann darauf gekommen, dass dies per Definition "data driven renderer" ist und dies ziemlich mächtig sein kann.
Man kann auch einige praktische dinge auf Basis der Daten machen, z.B. kann man die OpenGL aufrufe auf Fehler prüfen(kein end auf begin, gen auf destroy, bind, Konstante parameter), typische optimierungen auf Funktion/Pass/Frame und bestimmt gibt es noch viel mehr, woran ich einfach noch garnicht gedacht hab.
Man kann z.B. mit einem Tool sein renderer designen und mit Code generierung dann die VM auf jede beliebige Sprache nutzen.
Weiter in die Zukunft gesponnen kann ich ein Tiled Renderer zusammen klicken und den dann mit anderen Teilen, die garnicht meine Engine oder gar Sprache nutzen.

Folgendes ist zum Teil mein Klassen Design in Radon Framework.
Bild
NativeShape ist enthällt mehrere States, wobei man jeden State VM Bytecode und einer ID zuweisen kann.
Typischen States die man anlegen würde sind z.B. Init, Shutdown, ZPass, Color und dann den jeweiligen Bytecode.

CommandBuffer ist ein Util Klasse um aktuell VM Code zu bauen aber ich hab schon überlegt ein Visuelles Tool zu basteln.
Code:
  1. CommandBuffer cmdBuffer;
  2. CommandBuffer::HandleList buffer = cmdBuffer.ReserveHandleList();
  3. CommandBuffer::DataStream dataStream = cmdBuffer.ReserveDataStream();
  4. cmdBuffer.Call<GLFunctions::CreateBuffers>(1, buffer);
  5. cmdBuffer.Call<GLFunctions::BufferData>(RF_GL::GL_ARRAY_BUFFER,
  6.             triangles.Count() * sizeof(RF_Geo::Vec3f), dataStream,
  7.             RF_GL::GL_STATIC_DRAW);
  8. cmdBuffer.Finalize();
  9.  
  10. result = RF_Mem::AutoPointer<NativeShape>(new NativeShape);
  11. result->AddState(NativeShape::PredefinedIDs::Startup, cmdBuffer.ReleaseData());


DataStream sind Binary Blobs z.B. DDS, Shader Text, Meshes, ..., da der VM Code fest legt was mit den Daten passiert brauch ich keine Klassen mehr.
Mein RadonConverter Tool erzeugt aus bildern, 3d modellen und so weiter bereits native Daten(mit lua, assimp, magick, texconv und diversen anderen libs), dass sind dann die BinaryBlobs.

NativeShapeOptimizer ist aktuell z.B. StripeOutUnchangedRegisterMoves, welcher gerade bei begin end API recht viel gebracht hat.

Für meine Techdemo hab ich folgende Pipeline überlegt.
Bild
Ich hab 2x VM Code, einmal für Assets, um Buffer zu erzeugen und einmal für den Renderer selber für z.B. Initialisierung, Pass Start/Ende.

Was denkt ihr was man damit noch so machen könnte ?
Ich hab z.B. überlegt ob es nützliche Anwendungsfälle für Kontrollfluss Befehle gibt.


Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Mi Sep 09, 2015 09:53 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Ich seh gerade bei meiner eigenen Engine dass die sich in diese Richtung entwickelt. Allerdings entsteht die VM eher implizit durch aufgenommene ("aufgenommen" wie in recorded) Rendercalls und virtuelle Methoden.

Finde das Konzept auf jeden Fall interessant und werde mal überlegen, ob und wie ich die Idee des "black box" BLOB-Input den du da betreibst irgendwie für mich nutzen kann.

viele Grüße,
Horazont

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Do Sep 10, 2015 09:11 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Eine Sache ist mir noch eingefallen: Hast du überlegt, ob und wie eine Anbindung zu dem neuen JIT-Interface des GCC aussehen könnte? Ich hab über die Sache gestern nochmal nachgedacht. Wenn sowas wie Materialien z.B. einiges an Setup haben (so fünf, sechs gl* calls), dann ist das von sich aus schon ein Hotspot, den man durch einen JIT jagen könnte. Ich weiß nicht, wieviel optimierung das JIT-Interface noch anbietet und ob das dann realistisch ist, für jeden Frame oder jede Konfiguration binärcode zu erzeugen.

Hast du Benchmarkergebnisse dazu, wieviel Speedup man braucht damit man noch was rausholen kann? Oder ist das schon recht schnell einfach GPU-bound und der Overhead der VM verschwindet in der Rechenzeit der GPU für drawcalls?

viele Grüße,
Horazont

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Do Sep 10, 2015 15:11 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Nov 08, 2010 18:41
Beiträge: 769
Programmiersprache: Gestern
Mhh nett, funktioniert aber wahrscheinlich nicht gut bei Forward-Rendering. Dort müsstest du am besten noch so etwas wie Reaktionen auf Future(X) einbauen
sonst blockiert die VM dir ja alle Backend Threads oder deine Optimierungen greifen nicht.

_________________
Meine Homepage


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Do Sep 10, 2015 18:16 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich glaube nicht das die Jit hier hilfreich ist, ausser man hat Kontrollfluss und variablen, berechnungen mit in der VM.
In meiner VM z.B. hab ich sowas nicht geplant, weil ich kein Grund für Kontrollfluss sehe.

Die VM selber hat beim Profiling kein einfluss gezeigt, die Schwankungen von außen waren größer als die paar cycles die in der VM dazu kommen.
Es wird ein Array mit 14 8byte variablen erzeugt, die als Register dienen, 8Byte weil in den Specs die größte Variable 8Byte ist und die längste Funktion mit 14 Parameter kommt.
Da die meiste zeit auf ~4 Parametern gearbeitet wird, liegen die eigentlich permanent in den registern der jeweiligen kerne und sehe nie den Arbeitspeicher(liegen nochmal im Code Cache der CPU).
Die Funktionsaufrufe sind normale Aufrufe also gibt es keine extra kosten und das einzige was an overhead dazu kommt ist ne jumptable, was ne pointer addition und ein cache miss zufolge hat.
Ich wollte nochmal gucken die Jumptables Kontext bezogen zu machen, also mit einem Frame Optimizer oder Native Shape Optimizer die Calls zu erfassen und eine eigene Jumptable für die zu bauen oder für Frames die top N Funktionen zusammen zu fassen, damit die im Cache liegen und kein cache miss für die meisten aufrufe passiert.
Prinzipiell reden wir hier aber von ner hand voll von cycles performance verlust, ein Sync zwischen CPU und GPU ist schlimmer.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Do Sep 10, 2015 18:45 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich glaube nicht, dass man performance beim aufrufen von den opengl calls raus holen kann aber ich sehe potenzial beim raus reduzieren von aufrufen.
im prinzip kann man statische code analyze machen. Ein beispiel wäre ich stelle fest, dass opengl4 verfügbar ist und finde bind befehle auf ein buffer nach dem generate befehl, dann kann ich ne info werfen, dass dies durch ein create befehl ersetzt werden kann.
Ein weiterer vorteil wäre code injection. Man ist im debug mode und tut auf jeden opengl befehl ein geterror check injizieren.
Das spart unmengen an code und man läuft nicht gefahr langsame error checks im release zu haben.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Mo Sep 14, 2015 19:54 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Was ich noch nicht erwähnt hab aber nenutze ist das kommunizieren und parallelisieren vom rendering.
Ich arbeite mit einem entity component system, wo ich immer alle komponenten eines typs parallel auf allen threads verarbeite.
Die render komponenten auch und die erzeugen/duplizieren opengl vm bytecode und schieben es in eine lockfree queue, wo dann sich der mainthread bedient und abarbeitet.
Es ist also nicht wirklich parallelisiert, kann opengl ja noch nicht aber die finalen opengl befehle sind gefunden.
Eigentlich muss man für diese alle objekte durchlaufen und mit vielen state checks, cache misses und in der mainloop teurer machen.
So kann ich mit der kernanzahl skalieren, cachemisses reduzieren und auf mehrere kerne verteilen, sowie das data driven design für render komponenten verwenden.

Ich hab gestern wieder mal weiter dran arbeiten können.
Es gibt nun opengl variablen, also storage für pointer, buffer objekt ids und variable parameter.
Das ganze hab ich recht low level implementiert und das war keine gute idee.
Ich kann über ein api call ein variable objekt anlegen und dafür erzeugt er platz im bytestream.
Jede nutzung des variablen Objektes wird erkannt und passender opcode erzeugt.
Im nachhinau bin ich der meinung dass ich lieber variablen im bytecode einbauen sollte und im nativem code die richtigen schritte unternehme, um die daten an die richtige stelle zu bringen.
Ich hab nun neben den move befehl ein copy und addr befehl zum realisieren des features.
Wenn icj das konzept einer variable einbaue brauch ich die opcodes nicht und kann weiter move verwenden.
Ich muss dann aber auch die metadaten für die variablen speichern.
Jede variable besteht aus type, anzahl, bytegröße, index, extern und einem hash vom namen.
Der name als string liegt in den seperaten debugdaten.
Extern macht das ganze kniffelig, weil man nun ein pointer auf die eigentlichen daten hat und ein bisschen anders als der pointer für ein array funktioniert.
Daher der addr operator, welcher speicheraddresse-variablenoffset macht und dann dort den index als offset nimmt, während arrays noch den index auf den offset rechnen.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Fr Sep 18, 2015 15:03 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab mich die Woche mal mit meinem Kollegen unterhalten, weil er eine eigene Sprache(PSHDL) für FPGA entwickelt und entsprechen auch mit VM's Opcode, Bytecode und so weiter zu tun hat.
Computed goto/Jumptables und Execution Pipeline
Eines der Themen war computed goto, welche es für so ziemlich jeden großen Compiler, ausser VSC++ gibt.
Im Rahmen dessen hab ich mal geguckt wie es LuaJit regelt und Projekte die auf computed goto setzen aber mit visual studio bauen.
Die Antwort war zerstörend, die schreiben den Teil komplett in Assembler.

Ich benutze aktuell eine Switch-Case Variante mit 3400Zeilen Code für alle OpCodes/OpenGL Funktionen.
Das Switch-Case wird, wenn die Identifier keine größeren Lücken aufweisen jumptables.
Im Debug erzeugt VC++ ein Array mit Sprungaddressen, die einzelnen Cases werden zu Codeblöcken mit labels und switch wird zu einer variable prüfung ob default Label angesprungen wird oder aus der Sprungtabelle die passende Labeladdresse kopiert und zu dieser gesprungen wird.
Also wie im oberen Artikel auch beschrieben.
Der wesentliche Vorteil von Computed Goto ist die bessere nutzung der Branch-Prediction aber in der Praxis liegen die Performanceunterschiede im Durchsatz so bei 5-10%, was ich so an Zahlen finden konnte.

Das kann man auch noch weiter Treiben, in dem man mehrere Cases/Labels in weitere Cases/Labels zusammen fasst.
Z.b.
Code:
  1. switch(OpCodeGruppe(bytes))
  2. {
  3. case OGL:
  4. break;
  5. case WGL:
  6. break;
  7. case XGL:
  8. break;
  9. case WRITE:
  10.   switch(WriteOpCode(bytes))
  11.   {
  12.   case Bytes1:
  13.   break;
  14.   case Bytes2:
  15.   break;
  16.   case Bytes4:
  17.   break;
  18.   case Bytse8:
  19.     switch(Write8OpCode(bytes))
  20.     {
  21.       case Register0:
  22.         break;
  23.       case Register1:
  24.       ...
  25.     }
  26.   break;
  27.   }
  28. break;
  29. case READ:
  30. break;
  31. default:
  32. }

Was hier passiert ist, dass man die Branchprediction unter die Arme greift, in dem man so wenig branches wie möglich hat und da sich die CPU die Entscheidung vom vorigen mal merkt nimmt sie die gleiche Entscheidung nochmal.
Hab ich also 5 aufeinander folgende Write Opcodes, dann hab ich 5x den fall, dass die Pipeline nicht reseten muss und schon einige weitere Operationen ausgeführt hat, bevor überhautpt klar ist ob der sprung eigentlich der richtige war.
Intel I7 macht das schon mit 2 Leveln, also geht im obigen Beispiel in die Write Gruppe und dort in 4Byte, weil am häufigsten der 4Byte Branch getroffen hat.
Ab dort, ist dann der übliche Fall(true branch oder wenn der compiler schon infos mit gibt, welcher der häufigere ist).
Am meisten Sinn wird es machen noch custom Jumptables ein zu bauen und die Große Jumptable möglichst leserlich zu lassen.
Man kann zur Laufzeit neue passende Jumptables bauen und diese der großen vorziehen.

Kontrollfluss/Rückgabewerte
Ich hab gestern bytecode geschrieben, damit ich Shader laden und bauen kann.
Da bin ich auf ein Problem gestoßen, dass ich erstmal keine Results in der VM drin hatte aber viel schlimmer ich war kurz davor Conditions einzuführen.
Wenn man Shader bauen will, dann trifft man auf was unübliches, Rückgabewerte, die einem das Objekt geben, statt ein pointer als Parameter zu übergeben.
Um ein Shader zu bauen muss man erst die Shader kompilieren und dann ein programm linken aber was wenn einer von beiden ein Fehler hat, dann sollte ich die doch nicht Linken und was ist wenn das Linken schief geht oder ich erst ein shader baue und kompiliere und dann den 2. und die dann linke.
In den Fällen dachte ich brauch ich ein If aber zum glück hab ich nach einer geistiger Umnachtung fest gestellt, dass das ganze eher ne Statemachine ist und ich sie auch als solche verwende.
Wenn ich also nun meine Shader Factory in States zerlege komme ich aktuell auf folgende.
-CompileShaders
-LinkProgram
-DeleteShaders
-DeleteProgram
-ObtainShaderLog
-ObtainProgramLog
Wenn ich ein State abgearbeitet hab, kann ich ja in den Variablen lesen ob der Shader funktioniert oder nicht und darauf hin dann in den nächsten State wechseln aber das passiert dann im Nativen Code und nicht in der VM.
Code:
  1. auto isCompiled = cmdBuffer.AddVariable<RF_Type::Int32>(2,true, "isCompiled");// nach aussen bekannt als hash von isCompiled
  2. auto isLinked = cmdBuffer.AddVariable<RF_Type::Int32>(1, true, "isLinked");
  3. cmdBuffer.State("compileshaders");
  4. cmdBuffer.Call<GLFunctions::CreateShader>(RF_GL::GL_FRAGMENT_SHADER);
  5. cmdBuffer.CopyResult(fragmentShader);
  6. ...
  7. cmdBuffer.Call<GLFunctions::GetShaderiv>(fragmentShader, RF_GL::GL_COMPILE_STATUS, isCompiled[1].Ptr());


Mögliche Funktionalität
-Code Injection(Features ohne neu zu Kompilieren/Starten und kein Performanceeinbuße vor injection und nach rückgängigmachung)
--OGL Error Prüfung
--Performance Counter
-Code Replacement
--OpenGL 1.2 Code auf 4.5 mappen(mappe glbegin/glend auf vbo und vao, eventuell sogar ubo)
--Laufzeitoptimierung z.B. nutzlosen Code rauswerfen wie z.B. Statechanges die nix Ändern.
-Statische Codeanalyse
--Fehlerhafte Parameter und Funktionsaufrufe
--Optimierung von nutzlosen Code
--erkennung von Fehlerhafter Resourcen erzeugung/zerstörung
--Syncpoint Analyse
--Cache Effizienz(für die VM)
--Pipeline Effizienz(vertexformat, größe, offset, resourcen erzeugung/zerstörung)
--nötige OpenGL Version
--custom Jumptable generierung
--custom LoadLibrary code generierung(dynamisches linken mit nur den Funktionen die man brauchen wird)
-Code Generierung für diverse Sprachen
-serialisierung/deserialisierung
-parallelisierung von renderlogik(nicht das rendern selbst)

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Fr Okt 09, 2015 11:33 
Offline
DGL Member

Registriert: Fr Mai 11, 2012 13:25
Beiträge: 229
Programmiersprache: c++, c, JavaScript
Hi, ich möchte gern diesen Thread auffrischen, da ich grad nochmal drübergestolpert bin und ein paar Fragen dazu habe.
1. Wie kann man sich grob den Vorgang vorstellen, der aus Rendercalls Bytecode macht?
2. Hab ich es richtig verstanden, dass performancetechnisch hier der größte Vorteil ist, dass man Rendercalls quasi sehr effizient (dynamisch) umsortieren bzw. weglassen kann? Und wie sinnvoll wäre das ganze unter Verwendung der Vulkan-Api, die ja vorraussichtlich diesbezüglich von Haus aus optimierter arbeiten wird.
3. Wäre es denkbar, mit der präsentierten Methode auch Calls von Displaylisten durch Drawcalls von VBOs etc. zu ersetzen.
4. Ein bisschen bin ich verwirrt über die Bedeutung von CommandBuffer.
Was hat der hier genau bzw. bei Vulkan zu bedeuten?

Grüße,
Vinz

_________________
"Pixel, ich bin dein Vater."
-Darf Shader


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Fr Okt 09, 2015 14:08 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Nov 08, 2010 18:41
Beiträge: 769
Programmiersprache: Gestern
Nu im Prinzip ist es nicht viel anders als eine Displayliste. Nur halt etwas komplexer, aber letzten Endes packst du halt einfach
nur deine Funktionsaufrufe/Name + Daten in einen Speicherblock und arbeitest diese dann mit deiner konkreten Implementierung ab. Der
Vorteil dabei ist halt:
1.) Die Implementierung ist sehr einfach da du nur stupide die Funktionen abarbeitest und kann ohne weiteres mit zusätzlichen Features
fuer Debugging und co. aufpoliert werden.
2.) Du kannst Problemlos Bottlenecks in der API umgehen da du ja zwischenzeitlich neue Buffer in einen Background-Thread bauen kannst.

Von dort ausgehend kannst du jetzt natürlich beliebig komplex werden. Ob du da jetzt nun eine ganze VM drumherumbaust oder nicht
ist eigentlich egal. Die meisten Engines machen es einfach von Hand da man davon ausgeht das alles richtig ist und es ohnehin nur sehr
wenig Befehle gibt.

_________________
Meine Homepage


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Fr Okt 09, 2015 14:42 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Vinz hat geschrieben:
1. Wie kann man sich grob den Vorgang vorstellen, der aus Rendercalls Bytecode macht?

Die einfachste Form, um aus Rendercalls bytecode zu machen ist jedem OpenGL Befehl eine Zahl zu zuweisen.
Da Khronos für die Funktionen und Extensions keine IDs, wie bei den Konstanten verteilt hat, kann man hier nicht auf was einheitliches zurück greifen.
Das einfachste aber nicht mit anderen Kompatible Verfahren wäre einfach alle Funktionen in eine Liste/Enum zu packen und dann den Index als ID zu nehmen.
Um Platz im Bytecode zu sparen nimmt man ein Short, weil Khronos nie 65k Funktionen knacken wird aber mehr als 256 Fuktionen(Byte) hat.
Jetzt hat man also für jede Funktion eine Short, also 2 Bytes.
Nun trennen sich die Geister, wie legt man Parameter und Rückgabewerte von den Funktionen Handhabt.

Man kann wie bei ASM mov Befehle verwenden um pointerinhalt und Konstanten zu kopieren, es gibt dann z.B. ein 1,2,4,8,16(sse) byte move Befehle, die dann den Inhalt in ein Register/ParameterSlot schieben(das mach ich z.B.).

Alternative kann man auch wie Lua, Python und viele andere Sprachen ein Virtuellen Stack haben und intern wird dann der Stack den Parametern des Befehls gemappt.
Man hat also Push Befehle für 1,2,4,8,16 Byte Variablen.

Eine weitere Möglichkeit(mit der ich mehr und mehr anfreunde) ist davon aus zu gehen das es nur Variablen gibt und man die Offsets nach dem OpenGL Befehl Bytecode schreibt. Die VM muss dann wissen, dass die Funktion X Parameter hat und welche Datengröße welcher Parameter hat um die Offsets/Konstanten Daten korrekt zu interpretieren(steht alles in den Specs Beschreibung mit drin).
Dies hat den Vorteil, dass man sich weiteren OpCode sparen kann(mehr Nativer Code) aber den Nachteil, dass man viel mehr Cache-Misses hat, weil man ständig die Offsets Auflösen muss.
Ich mag die Variante immer mehr als Intermediate Format, weil die anderen beiden Varianten oben drauf gepackt werden können. Man kann den Custom VM kram beim Laden aus den Daten generieren.

Der Bytecode setzt sich also aus den Befehlen und den Parameterdaten zusammen.

Vinz hat geschrieben:
2. Hab ich es richtig verstanden, dass performancetechnisch hier der größte Vorteil ist, dass man Rendercalls quasi sehr effizient (dynamisch) umsortieren bzw. weglassen kann? Und wie sinnvoll wäre das ganze unter Verwendung der Vulkan-Api, die ja vorraussichtlich diesbezüglich von Haus aus optimierter arbeiten wird.

Vulkan API erlaubt tiefergehende Eingriffe in die Pipeline/Hardware und damit auch mehr Spielraum für Falsche benutzung und damit sinkender Performance aber bei richtiger Benutzung halt steigende Performance.
Vulkan sollte auch nicht OpenGL ersetzen, es sollte vielmehr als Werkzeug dienen, bestimmte Aufgaben, die sehr Zeitintensive sind tiefgehender zu optimieren und somit die ganze Performance zu verbessern.

Der größte Vorteil ist die Abstraktion und die damit greifbare Möglichkeit diesen Code zu Manipulieren und Analysieren und in Verbindung mit Mehreren Kernen zu Optimieren.
Wenn ich OpenGL Befehle oder auch Vulkan aufrufe, dann sind die in der Pipeline und ich kann ledeglich noch nachgelagert eine Analyse machen und fest Stellen, wo es Klemmt.
Um OpenGL/Vulkan Befehle ab zu setzen, muss man Daten Interpretieren, in der Regel Objekte von irgendwelchen Klassen, die in einen Szenegraph oder ähnlichen liegen.
Hoch optimierte Engines laufen mit mehreren Threads durch diese Objekte und erzeugen super kleine Renderobjekte, wie z.B. RenderMesh(Mesh ID, Textur IDs, Shader) und diese werden dann in den Renderthread gegeben, dort interpretiert und entsprechende OGL Calls gemacht.
Hoch optimiert, weil die Last von Cache Misses, Page Misses und die Operationen selber auf die Kerne Aufgeteilt wird.
Solche Abstratkion werden per Hand geschrieben und haben häufig Probleme bei Erweiterung der Engine(ich hab in meinem letzten Job da diverse male in die Tischkante gebissen).
Die Abstraktion auf OpenGL ist hingegen Einfach, da man diese aus den Spezifikationen gl.xml generieren kann und somit keine Pflege bei erweiterungen braucht.
Es gibt viele Möglichkeiten zu Optimieren aber in der Regel macht man nur das absolute Minimum, weil es Zeit kostet aber wenn man nicht mehr soviel Zeit investieren muss, weil viele Leute kleine Optimierungen einbringen, dann hat man nicht mehr Zeit investiert aber mehr Performance.
Der Umstand, dass OpenGL Bytecode so ziemlich die unterste Abstraktion und wenn der Bytecode tatsächlich als Intermediate Format dient, macht das Tür und Tor auf Optimizer zu schreiben, die wieder verwendet werden.
Ich kann z.B. Code schreiben, der erkennt, das ein VBO weniger als 200 Vertice und ein IBO mit gleicher Anzahl an Indices verwendet wird und ersetze dann den Bytecode durch ein VBO mit 200 Vertices, weil es mit IBO langsamer wäre.
Diesen kannst du dann als Shared Lib laden und auf die VM packen, ohne Code schreiben zu müssen.
Ein wohl besseres Beispiel, was mir gerade noch einfällt, sind Hardwareabhängige Optimierungen im Ladeprozess.
Statt die Katastrophal schlechte 513x411 Pixel Textur über die normalen Texturapi laufen zu lassen, kann ich auf ner NV Karte dann diese durch die RectangleTexture Extension ersetzen.
Also treffe ich auf glTexImage2D(GL_TEXTURE_2D, ... w,h) && (!pow2(w) || !pow2(h)) && HasSupport(GL_TEXTURE_RECTANGLE_NV), dann ... .

Vinz hat geschrieben:
3. Wäre es denkbar, mit der präsentierten Methode auch Calls von Displaylisten durch Drawcalls von VBOs etc. zu ersetzen.

Ja aber DL ist so ziemlich das komplizierteste, was OpenGL bietet, weil diverse Befehle und Verschachtelungen drin liegen dürfen und es diverse Limitierungen hat.
Wenn aktueller Befehl glGenLists ist, dann entferne den Befehl und suche das nächste glNewList mit der gleichen variable(Offset).
Bei Treffer suche das nächste glEndList und Prüfe ob alle Befehle dazwischen Valide sind, gibt es color, normal, was auch immer und bau dann ein VBO und bei glCallList mit der Variable wird dann das VBO gebunden/gezeichnet.
Das ist allerdings sehr Primitiv gedacht, denn DL können ineinander verschachtetl werden und dann wird es komplizierter oder man schleift den teil einfach mit und statt glCallList macht mann dann alle Befehle die man nicht verarbeiten will und dann die VBO optimierung.

Das ist halt genau das gleiche wie z.B. llvm oder AST basierte optimierungen.

Vinz hat geschrieben:
4. Ein bisschen bin ich verwirrt über die Bedeutung von CommandBuffer.
Was hat der hier genau bzw. bei Vulkan zu bedeuten?

Die Command Buffer sind sich recht ähnlich von der Aufgabe her, der größte Unterschied ist, dass in meiner Variante Bytecode raus kommt, den man selber weiter verarbeitet und in der von Vulkan hat man eine Blackbox repräsentiert durch ein Handle.

Ich denke, dass es wenig Sinn macht Vulkan als VM Laufen zu lassen und zu optimieren aber durchaus sinnig OpenGL VM Code in Vulkan VM Code umzuwandeln und somit OpenGL Code zu optimieren.
Das sollte man dann aber im Ladeprozess machen, wenn man schon weiß ob die Machiene es kann.
Bei aktueller Lage könnte es vieleicht auch noch sinn machen Vulkan Code in Vulkan VM Code um zu wandeln und auf OpenGL VM Code zu mappen, da die Treiber ja noch alle Propritär und mit NDA's an ausgewählte Entwickler geht.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Fr Okt 16, 2015 17:48 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich werde heute Abend mal den ganzen Code in ein GitHub Projekt verschieben.
Eigentlich wollte ich das erst sauber machen aber hey, wer mag nicht zusammen gehackten c,c++ und c# Code, der noch nicht vollständig Funktionsfähig ist.

@yunharla kleiner Tipp Private Nachrichten empfangen aktivieren ;)

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Sa Okt 17, 2015 14:56 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab mal alles zusammen gesucht und auf ein Stand gebracht dass es baut und nicht abschmiert, wenn man es startet aber Funktionsfähig ist das Sample aktuell nicht mehr, da ich mittem im Umbau für die Variablen stecke und die Demo nun den Kamera und Triangle Code nicht ausführt.
https://github.com/tak2004/OpenGLVM

Ich hab mir diverse Gedanken und Revisionen wegen einem Intermediate Format gemacht und bin nun auf folgenden Stand.
Bild
( yEd Graph Datei )

Ich habe 8 Byte Identifier, die Struktur soll sich nie veränder und es enthält den FourCC zur Erkennung des Formates und die Version des Formates.
Alle weiteren Teile können über Versionen hinweg beliebig verändert werden.

Der Header soll dem Identifier direkt folgen und ist in der aktuellen Version 12Byte groß.
4 Byte Dateigröße, weil nur Befehlscode enthalten ist keine Daten und da reicht das völlig aus, ich hab keine 2Byte genommen, weil man vieleicht sein ganzen OpenGL Code in eine File packen will und dann könnten 65KB schon eng werden, wenn das Projekt größer wird.
Der DatenHash ist 8Byte, weil er von den ganzen Daten gemacht wird.

Ab nun kommen Daten, welche Sections eingeleitet werden.
4Byte Section ID, wobei alle Werte ab 0x10000000 frei verwendet werden können und prinzipiell in der freien Wildbahn ignoriert werden sollte.
Es ist ratsam nach dem Section Header ein FourCC folgen zu lassen, wenn man private Sections verwendet und eindeutig identifizieren will.
4Byte SectionSize enthält die Payload von der Sektion und durch einfach addieren auf den File/Speicher-Offset kann man eine Sektion überspringen.

Struktur
Hier bin ich leider noch nicht sicher ob die Struktur auf Random Access oder Seriellen Zugriff ausgelegt werden sollte.
Der Unterschied wäre folgender.
Random Access:
-Identifier
-Header
-alle Section Headers(benötigen noch ein Byteoffset von Sektionsdaten aus)
-alle Sektionsdaten
Random Access lohnt sich, wenn man mit den Daten weiterarbeiten will und die Ladezeit ist extrem schnell, da man aktuell nur 1 ReadFile, 3 Pointer Additionen braucht und das Ganze Format im Speicher benutzbar hätte.

Serieller Zugriff(aktuelles Konzept):
-Identifier
-Header
-Section list(jedes Element enthält ein Section Header und darauf folgend die Daten)
Serieller Zugriff lohnt sich, wenn man die Daten eh nicht weiter nutzen will und eine eigene Struktur beim parsen aufbaut.
Ich glaube das dies eher der Weg ist, da Entwickler unterschiedliche Arten von VM's bevorzugen und für jede Variante macht es sinn weiteren OpCode zu generieren und damit braucht man dann den Intermediate Bytecode nicht mehr.

ByteCode
Diese Sektion enthält eine ID, welche sich aus den Hash eines Strings bildet. Aktuell tendiere ich zur 32Bit Variante von FNV, da diese um einiges schneller ist als Murmur32 Bit und dafür die Bitkipper ineffizienter verteilt.
Der Name selber kann auch mitgeliefert werden und liegt dann in den Debug_SymbolLookupTable.
Prinzipiell sollten zur Laufzeit eh keine Strings mehr rumschwirren und Hashwerte von gebildet werden müssen.
Im aktuellen Code nutze ich 64Bit Murmur, der zur Compiletime aus den Stringliterals generiert wird.
Die Bytedaten selber sollten wie folgt aufgebaut sein.
[FunktionsID-UInt32,VariableId-UInt32]...
So benötigt man kein OpCode.

MemoryData
Hier liegen die eigentlichen Werte, so kann man über mehrere ByteCode Sektionen hinweg Variablen Teilen.

Konstanten liegen in den passenden Variablentypen vor.

Definierte Daten, wie z.B. ein ID-Array, dessen größe Konstant und bekannt ist werden auch mit passender Speichergröße hinterlegt(z.B. glCreateBuffers(2,&ubo); <- ubo 2xsizeof(T) ).

Externe Variablen enthalten keine Daten, nur Identifier(z.B. Vertexdaten oder externe Shader IDs). Es wird ein Mapping benötigt und sind die einzigen Variablen, die nach aussen sichtbar sind.

Debug_SymbolLookupTable
KeyValuePair besteht aus der Hash ID und dem byteoffset von Section Header beginnend.
Man erzeugt ein Pointer und weißt die Position vom ersten Symbol zu, mit Hilfe von SymbolCount kann man dann Binary search machen, vorrausgesetzt, man legt per spec fest das symbole sortiert sein müssen.
TextData sind 0 Terminierte Strings.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Mi Okt 21, 2015 20:38 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Nov 08, 2010 18:41
Beiträge: 769
Programmiersprache: Gestern
Ja PM ist mir ein wenig zu blöde. Dafür ist ja die Email public :)

Achja ich habe mir mal deinen Generator angeschaut. Bei mir funktioniert das wie folgt, vielleicht hilfts ja:
Code:
  1.  
  2.         static dynamic Parse(XElement node) {
  3.             dynamic ou = new ExpandoObject();
  4.             if ( node.HasElements ) {
  5.                 var gs = node.Elements().GroupBy(n => n.Name.LocalName);
  6.                 foreach ( var g in gs ) AddProperty(ou, g.Key, g.Select(Parse));
  7.             }
  8.             if ( node.HasAttributes ) {
  9.                 foreach ( var att in node.Attributes() )
  10.                     AddProperty(ou, att.Name.ToString(), att.Value.Trim());
  11.             }
  12.             return ou;
  13.         }
  14.  
  15.         private static void AddProperty(dynamic parent, string name, object value) {
  16.             if ( parent is List<dynamic> ) {
  17.                 (parent as List<dynamic>).Add(value);
  18.             } else {
  19.                 (parent as IDictionary<String, object>) [name] = value;
  20.             }
  21.         }
  22.  

_________________
Meine Homepage


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Data driven rendering
BeitragVerfasst: Mi Okt 21, 2015 22:23 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Also wenn ich das richtig verstehe, schreibst du XML files, wo du dann die Funktionen als Tags hast, mit Parameter als Attribute oder nested Tags rein packst.
Da du C# nutzt kann ich dir noch ein Stück Code empfehlen, welcher die Spec files parsen kann und in Klassenbaum verpackt.
Das nutze ich im Builder Tool, weil der C# ist.

Aktuell schreibe ich ne C-Lib names libOGLIF(OpenGL Intermediate Format), welche das lesen, schreiben und manipulieren von IntermediateTree Objekten erlaubt.
So muss ich in C# dann nur noch ein paar C Funktionen wrappen und keine ganze C# Implementierung machen.
Das gleiche plane ich auch mit meiner VM, die ja nach aussen nicht viel macht ausser Init, lade OGLIF und führe bytecode aus.
Hat auch den Vorteil, dass ich keine C# OpenGL bindings brauch und C++ ist dann doch um einiges schneller als C#.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 16 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Foren-Übersicht » Programmierung » OpenGL


Wer ist online?

Mitglieder in diesem Forum: Google [Bot] und 25 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.077s | 19 Queries | GZIP : On ]