PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Ratlos: Uniform Buffer Updates in OpenGL 3.3


Expandable
2013-07-16, 18:28:42
Hallo zusammen,

ich bin etwas ratlos, wie man unter OpenGL 3.3 Uniform Buffers effizient updaten soll. Zum Szenario: Ich habe eine kleine Engine für ein kleines Spiel, die unter Windows/Linux und D3D11 (Feature Level 10) und OpenGL 3.3 läuft. Unter D3D11 läuft soweit alles schnell (getestet unter Windows 7/8 mit verschiedenen NVIDIA und AMD Karten).

Der OpenGL-Modus war jedoch lange deutlich langsamer (um Größenordnungen langsamer). Jetzt habe ich mich mal dran gemacht, dem Problem auf den Grund zu gehen: Viel zu viel CPU/GPU Synchronisation. Zum Teil von glGet*-Aufrufen (dämliche Bind-API... zum Glück gibt's aber EXT_direct_state_access), aber auch, und das ist das eigentliche Problem: glMapNamedBufferRangeEXT hauptsächlich.

Unter D3D11 braucht es < 0.0x ms, um einen Constant Buffer zu mappen. Unter OpenGL 3.3 hingegen irgendwo im Bereich von 0.1ms bis 2ms, da scheinbar bei jedem Map eine CPU/GPU Synchronisation stattfindet (D3D11 macht das offensichtlich nicht). Klar, dass dann insgesamt viel größere Frametimes rauskommen.

Ich verwende relativ wenig Constant/Uniform Buffers, die aber häufig pro Frame aktualisiert werden (also z.B. ein Constant Buffer für alle "Laser", die gerade über den Bildschirm schwirren, welcher Position, Farbwerte, etc. speziell für diesen einen Laser enthält. Instancing verwende ich (noch) nicht). Laut verschiedenen Präsentationen ist das ja genau so gedacht unter D3D11 (siehe z.B. [1]), dass man viele Buffer hat und diese mit D3D11_MAP_WRITE_DISCARD immer wieder überschreibt. Der Treiber kümmert sich darum, aus einem Pool "verfügbare" Buffer zu verwenden.

Aber wie ist das unter OpenGL 3.3? glMapNamedBufferRangeEXT synchronisiert CPU/GPU (auf allen getesteten Grafikkarten und OSsen), wenn man GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT angibt. glBufferSubData laut Beschreibung auch. Nun habe ich probiert, einfach GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT zu verwenden - Resultat: Fast so schnell wie D3D11, funktioniert problemlos mit NVIDIA-Karten. Mit AMD-Karten bekomme ich aber Grafikfehler, da der Treiber anscheinend keinen neuen Speicherbereich für den überschriebenen Uniform-Buffer zurück gibt, und dann natürlich einfach die zuvor gesetzten Werte des Buffers überschreibt, die ja aber noch in Verwendung sind.

Was tun? Google findet dazu nicht viel, und wenn dann sind's D3D11-Tips. Wie updated man effizient Uniform-Buffer unter OpenGL 3.3, analog zur Art und Weise wie's in D3D11 funktioniert? Oder ist das ein Treiberbug seitens AMD?

Danke!

[1] Don't Throw it All Away – Managing Buffers Efficiently (NVIDIA) (http://developer.nvidia.com/sites/default/files/akamai/gamedev/files/gdc12/Efficient_Buffer_Management_McDonald.pdf)

Simon
2013-07-19, 18:11:24
Betrifft das Performance-Problem nur AMD-Karten? Falls ja, ist das normal. AMDs OpenGL-Treiber ist ein ganz Stueck (~50%) langsamer als D3D. Quelle: Eigene Benchmarks mit Radeon 7970 und Unigine Benchmarks.
Bei id Tech Engine Spielen ist das vielleicht anders.

Anyway, Xplane hatte ein aehnliches Problem: http://hacksoflife.blogspot.com/2012/04/beyond-glmapbuffer.html

Expandable
2013-07-19, 22:39:29
Ohne GL_MAP_UNSYNCHRONIZED_BIT ist es sowohl bei NVIDIA als auch bei AMD langsam - mit GL_MAP_UNSYNCHRONIZED_BIT ist es bei beiden schnell, führt aber bei AMD zu Grafikfehlern.

Danke für den Link, sehr interessant. Sieht aber wohl so aus, als gäbe es keine triviale Lösung dafür. Also anscheinend: Grafikkarten-Hersteller beim Start erkennen, bei NVIDIA GL_MAP_UNSYNCHRONIZED_BIT dazu, bei AMD nicht. Dann ist NVIDIA halt deutlich schneller als AMD.

Zum Glück läuft's bei beiden unter D3D11 schnell...

Nighthawk13
2013-07-22, 17:49:56
Evtl. doch "glBufferSubData" für Datenupdates?
(Vor 5+ Jahren war das in meinem Fall(VBOs) schneller als entsprechende map-Aufrufe auf ATI-Karten).

Expandable
2013-08-13, 13:34:08
Letztendlich habe ich tatsächlich einfach glBufferSubData verwendet. Das hat auf NVIDIA-Karten keinen Performance-Nachteil, funktioniert dafür aber immer korrekt, auch auf AMD-Karten.

Was ich trotzdem nicht verstehe ist, dass sowohl auf NVIDIA als auch auf AMD-Karten im OpenGL-Modus sowohl die CPU als auch GPU Frametime fast das Doppelte des D3D-Modus betragen. Ist das normal??