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)
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)