Die glNamedBufferSubData-Calls finde ich eigentlich nicht so schlimm. Das sind keine VBOs sondern Uniform Buffer!
Was das für Buffer sind, ist erstmal herzlich egal.
All buffers are created equal (zumindest was das Target angeht). Die Daten dienen als Shader-Input und müssen auch entsprechend synchronisiert werden.
Das Problem ist auch nicht die Menge der Daten, sondern dass da im Grunde folgendes passiert:
1. Client schickt Daten an den Treiber. Treiber puffert die erstmal irgendwo zwischen, allokiert ggf. irgendwo neuen Speicher, der gerade nicht aktiv von der GPU gelesen wird, und sagt der GPU, sie soll per DMA die Daten kopieren.
2. Client schickt Draw Call los. Treiber muss zusehen, dass die Daten aus Schritt 1 für die GPU lesbar sind. Und zwar vollständig.
3. Client schickt neue Daten an den Treiber. Nun ist der Draw Call aber noch nicht fertig. Bleibt also entweder warten (ugh) oder neuen Speicher reservieren (immerhin wird hier der
ganze Buffer überschrieben, da geht das), was aber in der Summe auch einiges an Verwaltungsaufwand darstellt. Vielleicht ist ja auch irgendwo noch ein Block von vor ein paar Draw Calls frei. (ugh)
4. Client schickt nächsten Draw Call los, der aber wieder erst ausgeführt werden kann, wenn alle Daten irgendwo sind.
5. Wiederhole Schritte 3-4 etwa 7000 Mal.
Ein gut optimierter Treiber mag das zwar ohne viel Synchronisation hinbekommen. Sonderlich effizient ist das trotzdem nicht.
Weitaus sinnvoller wäre:
- Uniform-Daten für n (n einigermaßen groß) Objekte in einen größeren Buffer schreiben
- n Objekte per Instancing und/oder MultiDraw rendern und dann per gl_InstanceID oder einem Per-Instance-Vertexattribut auf die entsprechenden Uniform-Daten zugreifen
Ändert zwar am grundlegenden Problem nichts, man müsste es aber statt ~7000 Mal allenfalls einige hundert Male lösen. Ich bin mir ziemlich sicher, dass man die Anzahl der GL-Calls in der Szene um 90% reduzieren könnte. Aber dafür müsste man wie gesagt den Renderer entsprechend anpassen, ein Dx11->GL-Wrapper reicht da nicht.