Ich verwende dies, um Untersuchungen zur Ursache niedriger FPS-Werte bei Verwendung des immersiven Modus auf Geräten zu verfolgen.
Einige Informationen, die aufgedeckt wurden:
Eine Spur der Paint-Demo mit Markern für die OpenXR-APIs reproduziert diese Tage und zeigt auch während dieser Zeit:
Angesichts dieser Datenpunkte werde ich zwei Ansätze ausprobieren, um zu beginnen:
https://github.com/servo/servo/pull/25343#issuecomment -567706735 hat weitere Untersuchungen zu den Auswirkungen des Sendens von Nachrichten an verschiedene Stellen in der Engine. Es zeigt keine klaren Finger, sondern schlägt vor, genauere Messungen vorzunehmen.
Die Verwendung von gl::Finish stammt von https://github.com/pcwalton/surfman/blob/6705a9aaa8f33ac1324fdb1913242800e68c7720/surfman/src/platform/windows/angle/context.rs#L259 -L266.
Das Ändern von gl::Finish zu gl::Flush erhöht die Framerate von ~15->30, aber es gibt eine extrem merkliche Verzögerung im Frame-Inhalt, die tatsächlich die Bewegung des Kopfes des Benutzers widerspiegelt, wodurch der aktuelle Frame dem Kopf des Benutzers nach innen folgt die Zwischenzeit.
Geschlüsselte Mutexe sind in ANGLE aus Gründen, die sich mir entziehen, standardmäßig deaktiviert, aber Mozangle aktiviert sie explizit (https://github.com/servo/mozangle/blob/706a9baaf8026c1a3cb6c67ba63aa5f4734264d0/build_data.rs#L175), und damit wird surfman getestet . Ich werde einen Build von ANGLE erstellen, der sie aktiviert, und sehen, ob das ausreicht, um die gl::Finish-Aufrufe zu vermeiden.
Bestätigt! Das Erzwingen von codierten Mutexes in ANGLE gibt mir 25-30 FPS in der Paint-Demo ohne die Verzögerungsprobleme, die beim Ändern des gl::Finish-Aufrufs auftreten.
Oh, und noch eine Information nach Lars' Untersuchungen:
Ich glaube, ich habe das Vorhandensein von std::thread::local::LocalKey<surfman::egll::Egl>
in den Profilen falsch verstanden - ich bin mir ziemlich sicher, dass das TLS-Lesen nur ein sehr kleiner Teil der Zeit ist, die ihm in Rechnung gestellt wird, und es sind die Funktionen, die innerhalb des TLS-Blocks aufgerufen werden, wie eglCreatePbufferFromClientBuffer und DXGIAcquireSync, die _eigentlich_ die Zeit in Anspruch nehmen.
Leider scheint das Deaktivieren von js.ion.enabled die FPS der Paint-Demo zu beeinträchtigen und auf 20-25 zu reduzieren.
Anstatt Device::create_surface_texture_from_texture zweimal in jedem Frame aufzurufen (einmal für jede d3d-Textur für jedes Auge), ist es möglicherweise möglich, Oberflächentexturen für alle Swapchain-Texturen zu erstellen, wenn das openxr webxr-Gerät erstellt wird. Wenn dies funktioniert, würde es den zweitgrößten CPU-Benutzer im immersiven Modus aus dem Hauptthread entfernen.
Eine weitere Idee zur Reduzierung des Speicherverbrauchs: Hat es Auswirkungen, wenn wir den bfcache auf eine sehr niedrige Zahl setzen, damit die ursprüngliche HL-Homepage-Pipeline beim Navigieren zu einer der Demos entfernt wird?
Der folgende webxr-Patch verbessert die FPS nicht eindeutig, aber er könnte die Bildstabilität verbessern. Ich muss zwei separate Builds erstellen, die ich nacheinander ausführen kann, um sie zu überprüfen.
diff --git a/webxr/openxr/mod.rs b/webxr/openxr/mod.rs
index 91c78da..a6866de 100644
--- a/webxr/openxr/mod.rs
+++ b/webxr/openxr/mod.rs
@@ -416,11 +416,30 @@ impl DeviceAPI<Surface> for OpenXrDevice {
}
fn wait_for_animation_frame(&mut self) -> Option<Frame> {
- if !self.handle_openxr_events() {
- // Session is not running anymore.
- return None;
+ loop {
+ if !self.handle_openxr_events() {
+ // Session is not running anymore.
+ return None;
+ }
+ self.frame_state = self.frame_waiter.wait().expect("error waiting for frame");
+
+ // XXXManishearth this code should perhaps be in wait_for_animation_frame,
+ // but we then get errors that wait_image was called without a release_image()
+ self.frame_stream
+ .begin()
+ .expect("failed to start frame stream");
+
+ if self.frame_state.should_render {
+ break;
+ }
+
+ self.frame_stream.end(
+ self.frame_state.predicted_display_time,
+ EnvironmentBlendMode::ADDITIVE,
+ &[],
+ ).unwrap();
}
- self.frame_state = self.frame_waiter.wait().expect("error waiting for frame");
+
let time_ns = time::precise_time_ns();
// XXXManishearth should we check frame_state.should_render?
let (_view_flags, views) = self
@@ -506,12 +525,6 @@ impl DeviceAPI<Surface> for OpenXrDevice {
0,
);
- // XXXManishearth this code should perhaps be in wait_for_animation_frame,
- // but we then get errors that wait_image was called without a release_image()
- self.frame_stream
- .begin()
- .expect("failed to start frame stream");
-
self.left_image = self.left_swapchain.acquire_image().unwrap();
self.left_swapchain
.wait_image(openxr::Duration::INFINITE)
@manishearth irgendwelche Gedanken dazu? Es ist mein Versuch, dem von https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#Session beschriebenen Modell näher zu kommen
Ja, das sieht gut aus. Ich wollte begin()
oben in waf verschieben, und ich glaube, der im Kommentar erwähnte Fehler tritt nicht mehr auf, aber er hatte auch keine merklichen Auswirkungen auf die FPS, also habe ich ihn nicht weiterverfolgt jetzt zu viel. Wenn es die Stabilität verbessert, ist das gut!
Freue mich sehr über die Keyed Discovery! Surfman-Calls nehmen zwar einen Teil des Frame-Budgets in Anspruch, aber es ist ein bisschen schwer zu bestimmen, was notwendig ist und was nicht.
Ja, re: das Deaktivieren von js.ion.enabled
, das wird nur ein Vorteil sein, wenn wir RAM-hungrig sind und die meiste Zeit damit verbringen, Funktionen zu GC und neu zu kompilieren. Und das sollte mit einem neueren SM verbessert werden. IIRC, das ARM64-Backend der 66-Ära, hatte auch eine relativ schlechte JIT- und Interpreter-Basisleistung; Wir sollten mit einem Update auf der ganzen Linie Beschleunigungen sehen, aber insbesondere bei RAM-intensiven Anwendungen.
Neues ANGLE-Paket mit aktivierten verschlüsselten Mutexes veröffentlicht. Ich werde eine Pull-Anfrage erstellen, um sie später zu aktualisieren.
Ich habe versucht, die Oberflächentexturen für alle openxr-Swapchain-Images während der Initialisierung des XR-Geräts zu erstellen, aber der Hauptthread verbringt immer noch eine Menge Zeit damit, eglCreatePbufferFromClientBuffer auf der Oberfläche aufzurufen, die wir in jedem Frame vom webgl-Thread erhalten. Vielleicht gibt es eine Möglichkeit, diese Oberflächentexturen zwischenzuspeichern, damit wir sie wiederverwenden können, wenn wir dieselbe Oberfläche erhalten ...
Die größte CPU-Auslastung des Hauptthreads kommt von render_animation_frame, wobei das meiste davon unter der OpenXR-Laufzeit läuft, aber Aufrufe von BlitFramebuffer und FramebufferTexture2D erscheinen definitiv auch im Profil. Ich frage mich, ob es eine Verbesserung wäre, beide Augen gleichzeitig auf eine einzige Textur zu leuchten? Vielleicht hängt das mit dem Texturarray-Zeug zusammen, das in https://github.com/microsoft/OpenXR-SDK-VisualStudio/#render -with-texture-array-and-vprt besprochen wird.
Wir können beide Augen gleichzeitig blitzen, aber ich verstehe, dass die Laufzeit
kann dann seinen eigenen Blit machen. Das Texturarray ist die schnellste Methode. Aber
einen Versuch wert, die Projektionsansichts-API unterstützt dies.
Was den ANGLE-Verkehr des Hauptthreads betrifft, stoppt das Stoppen der RAF-Schleife von
Leinwand verschmutzen helfen? Das hat bis jetzt noch nichts gebracht aber es lohnt sich
geschossen, idealerweise sollten wir nichts Layout/Rendering am Main machen
Faden.
Am Mo, 6. Januar 2020, 23:49 Uhr Josh Matthews [email protected]
schrieb:
Die größte CPU-Auslastung des Hauptthreads kommt von render_animation_frame, mit
das meiste davon unter der OpenXR-Laufzeit, aber Aufrufe von BlitFramebuffer und
FramebufferTexture2D erscheint definitiv auch im Profil. Ich wundere mich
wenn es eine Verbesserung wäre, beide Augen gleichzeitig zu einem einzigen zu verblenden
Textur? Vielleicht hängt das mit dem besprochenen Texturarray-Zeug zusammen
in
https://github.com/microsoft/OpenXR-SDK-VisualStudio/#render -with-texture-array-and-vprt
.—
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/servo/servo/issues/25425?email_source=notifications&email_token=AAMK6SBRH72JGZMXTUKOXETQ4NY37A5CNFSM4KCRI6AKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMV2ZLOKTJPHA#
oder abmelden
https://github.com/notifications/unsubscribe-auth/AAMK6SECM6MDNZZ6Y7VL7SDQ4NY37ANCNFSM4KCRI6AA
.
Das Entfernen der Leinwandverschmutzung reinigt nur das Profil; es schien nicht zu einer nennenswerten FPS-Erhöhung zu führen.
Ich habe versucht, einen Cache mit Oberflächentexturen für Oberflächen aus dem Webgl-Thread sowie Openxr-Swapchain-Texturen zu erstellen, und während die eglCreatePbufferFromClientBuffer-Zeit vollständig verschwand, habe ich keine nennenswerten FPS-Änderungen bemerkt.
Einige Timing-Informationen für verschiedene Operationen in der immersiven Pipeline (alle Messungen in ms):
Name min max avg
raf queued 0.070833 14.010261 0.576834
<1ms: 393
<2ms: 28
<4ms: 5
<8ms: 1
<16ms: 2
<32ms: 0
32+ms: 0
raf transmitted 0.404270 33.649583 7.403302
<1ms: 123
<2ms: 43
<4ms: 48
<8ms: 48
<16ms: 95
<32ms: 69
32+ms: 3
raf wait 1.203500 191.064100 17.513593
<1ms: 0
<2ms: 17
<4ms: 98
<8ms: 95
<16ms: 48
<32ms: 69
32+ms: 101
raf execute 3.375000 128.663200 6.994588
<1ms: 0
<2ms: 0
<4ms: 5
<8ms: 351
<16ms: 70
<32ms: 1
32+ms: 2
raf receive 0.111510 8.564010 0.783503
<1ms: 353
<2ms: 52
<4ms: 18
<8ms: 4
<16ms: 1
<32ms: 0
32+ms: 0
raf render 2.372200 75.944000 4.219310
<1ms: 0
<2ms: 0
<4ms: 253
<8ms: 167
<16ms: 8
<32ms: 0
32+ms: 1
receive
: Zeit vom Senden der XR-Frame-Informationen vom XR-Thread bis zum Empfang durch den IPC-Router
queued
: Zeit vom Empfang der Frame-Informationen durch den IPC-Router bis zum Aufruf von XRSession::raf_callback
execute
: Zeit von XRSession::raf_callback aufgerufen bis zur Rückkehr von der Methode
transmitted
: Zeit vom Senden der Anforderung eines neuen rAF vom Skript-Thread bis zum Empfang durch den XR-Thread
render
: Zeit, um render_animation_frame
anzurufen und die Oberfläche zu recyceln
wait
: Zeit von wait_for_animation_frame
(mit dem Patch von vorhin in dieser Ausgabe, der über Frames wiederholt, die nicht gerendert werden sollen)
Unter jedem Eintrag befindet sich die Verteilung der Werte über den Verlauf der Sitzung.
Ein interessanter Datenpunkt aus diesen Timing-Informationen - die Kategorie transmitted
scheint viel höher zu sein, als sie sein sollte. Das ist die Verzögerung zwischen der Ausführung des rAF-Callbacks und dem Empfang der Nachricht durch den XR-Thread, die den abgeschlossenen Frame in die Textur von openxr einfügt. Es gibt einige Variationen, die darauf hindeuten, dass entweder der Haupt-Thread mit anderen Dingen beschäftigt ist oder er aufgeweckt werden muss, um ihn zu verarbeiten.
Angesichts der vorherigen Daten kann ich morgen versuchen, https://github.com/servo/webxr/issues/113 wiederzubeleben, um zu sehen, ob sich dies positiv auf das Sendetiming auswirkt. Ich kann zuerst im Haupt-Thread im Profiler stöbern, um zu sehen, ob mir irgendwelche Ideen einfallen, wie ich feststellen kann, ob der Thread mit anderen Aufgaben beschäftigt ist oder schläft.
Ein weiterer Datenpunkt:
swap buffer 1.105938 28.193698 2.154793
<1ms: 0
<2ms: 273
<4ms: 110
<8ms: 15
<16ms: 2
<32ms: 2
32+ms: 0
swap complete 0.053802 4.337812 0.295064
<1ms: 308
<2ms: 9
<4ms: 6
<8ms: 1
<16ms: 0
<32ms: 0
32+ms: 0
swap request 0.003333 24033027.355364 4662890.724805
<1ms: 268
<2ms: 49
<4ms: 5
<8ms: 0
<16ms: 0
<32ms: 1
32+ms: 79
Dies sind Zeitpunkte im Zusammenhang mit 1) der Verzögerung vom Senden der Swap-Puffer-Nachricht bis zur Verarbeitung im Webgl-Thread, 2) der Zeit, die zum Austauschen der Puffer benötigt wird, 3) der Verzögerung vom Senden der Nachricht, die anzeigt, dass der Austausch abgeschlossen ist, bis sie in empfangen wird der Skript-Thread. Nichts Überraschendes hier (außer diesen seltsamen Ausreißern in der Kategorie swap request
, die jedoch gleich zu Beginn der immersiven Sitzung während des Setups auftreten), aber der eigentliche Pufferaustausch dauert durchweg 1-4 ms.
Abgelegt Nr. 117, nachdem ich einen Openxr-Beispielcode gelesen und festgestellt habe, dass die Aufrufe von locate_views im Profil angezeigt werden.
Vermutlich https://github.com/servo/webxr/issues/117
Ein interessanter Datenpunkt aus diesen Timing-Informationen - die übertragene Kategorie scheint viel höher zu sein, als sie sein sollte. Das ist die Verzögerung zwischen der Ausführung des rAF-Callbacks und dem Empfang der Nachricht durch den XR-Thread, die den abgeschlossenen Frame in die Textur von openxr einfügt. Es gibt einige Variationen, die darauf hindeuten, dass entweder der Haupt-Thread mit anderen Dingen beschäftigt ist oder er aufgeweckt werden muss, um ihn zu verarbeiten.
Bezüglich der Variationen des transmitted
Werts könnte dies mit dem Timeout zusammenhängen, das als Teil von run_one_frame
wenn die Sitzung auf dem Hauptthread ausgeführt wird (was es in diesen Messungen ist, oder?) , siehe https://github.com/servo/webxr/blob/c6abf4c60d165ffc978ad2ebd6bcddc3c21698e1/webxr-api/session.rs#L275
Ich vermute, dass, wenn die RenderAnimationFrame
Nachricht (die vom Skript-Thread gesendet wird, nachdem die Callbacks ausgeführt wurden) vor dem Timeout empfangen wird, Sie den "schnellen Pfad" wählen und wenn der Timeout verpasst wird, geht Servo in einen anderen Iteration von perform_updates
, und das "Ausführen eines anderen Frames" erfolgt ziemlich spät im Zyklus, als Teil von compositor.perform_updates
, das selbst ziemlich spät als Teil von servo.handle_events
aufgerufen wird.
Abgesehen davon, dass XR in einen eigenen Thread verschoben wird, kann es sich lohnen, zu sehen, ob ein höherer Wert für das Timeout den Durchschnittswert verbessert (obwohl dies möglicherweise nicht die richtige Lösung ist, da andere erforderliche Dinge im Hauptthread möglicherweise ausgehungert werden).
Ich habe Fortschritte beim Entfernen von openxr aus dem Hauptthread in https://github.com/servo/webxr/issues/113 gemacht , daher werde ich nächste Woche weitere Messungen basierend auf dieser Arbeit vornehmen.
Techniken zum Abrufen nützlicher Profile vom Gerät:
rustflags = "-C force-frame-pointers=yes"
Files
als benutzerdefiniertes Tracing-Profil unter "Performance Tracing" im HL-Geräteportal--features profilemozjs
Diese Ablaufverfolgungen (erhalten von "Trace starten" im Geräteportal) können innerhalb des Windows Performance Analyzer-Tools verwendet werden. Dieses Tool zeigt keine Thread-Namen an, aber die Threads, die die meiste CPU verbrauchen, sind anhand der Stacks einfach zu identifizieren.
So profilieren Sie die Zeitverteilung eines bestimmten openxr-Frames:
Die nützlichsten Ansichten für die CPU-Auslastung:
Eine Möglichkeit, etwas weniger Arbeit im Skriptthread zu machen:
Eine Möglichkeit, beim Rendern eines immersiven Frames weniger Arbeit zu leisten:
surface_origin_is_top_left
). während der immersive Modus ohne Transformation geblinkt werden könnteBasierend auf https://bugzilla.mozilla.org/show_bug.cgi?id=1591346 und im Gespräch mit jrmuizel müssen wir Folgendes tun:
Relevanter Gecko-Code: https://searchfox.org/mozilla-central/rev/c52d5f8025b5c9b2b4487159419ac9012762c40c/gfx/webrender_bindings/RenderCompositorANGLE.cpp#192
Relevanter ANGLE-Code: https://github.com/google/angle/blob/df0203a9ae7a285d885d7bc5c2d4754fe8a59c72/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp#L244
Aktuelle wip-Filialen:
Dazu gehört eine xr-profile
Funktion, die die oben erwähnten Timing-Daten hinzufügt, sowie eine erste Implementierung der ANGLE-Änderungen, um die y-inverse Transformation im immersiven Modus zu entfernen. Der nicht immersive Modus rendert korrekt, aber der immersive Modus steht auf dem Kopf. Ich glaube, ich muss den GL-Code aus render_animation_frame entfernen und durch einen direkten CopySubresourceRegion-Aufruf ersetzen, indem ich das Share-Handle aus der GL-Oberfläche extrahiere, damit ich seine d3d-Textur erhalten kann.
Eingereicht https://github.com/servo/servo/issues/25582 für die ANGLE y-Inversionsarbeit; weitere Aktualisierungen zu dieser Arbeit werden in dieser Ausgabe erfolgen.
Der nächste große Punkt wird untersucht, wie man die glBlitFramebuffer-Aufrufe im openxr-webxr-Backend vollständig vermeiden kann. Dies erfordert:
Das kann schwierig sein, da surfman nur Schreibzugriff auf den Kontext bietet, der die Oberfläche erstellt hat. Wenn die Oberfläche also vom openxr-Thread erstellt wird, kann sie vom WebGL-Thread nicht geschrieben werden. https://github.com/pcwalton/surfman/blob/a515fb2f5d6b9e9b36ba4e8b498cdb4bea92d330/surfman/src/device.rs#L95 -L96
Mir fällt ein - wenn wir das openxr-Rendering im webgl-Thread durchführen würden, wären eine Reihe der Threading-bezogenen Probleme beim direkten Rendern in die Texturen von openxr keine Probleme mehr (dh die Einschränkungen rund um eglCreatePbufferFromClientBuffer, die die Verwendung mehrerer d3d-Geräte verbieten). Erwägen:
Meine Lektüre von https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#threading -behavior legt nahe, dass dieses Design praktikabel sein könnte. Der Trick ist, ob es sowohl für unsere nicht-openxr-Backends als auch für openxr funktionieren kann.
Aus der Spezifikation: "Obwohl xrBeginFrame und xrEndFrame nicht im selben Thread aufgerufen werden müssen, muss die Anwendung die Synchronisierung verarbeiten, wenn sie in separaten Threads aufgerufen werden."
Im Moment gibt es keine direkte Kommunikation zwischen den XR-Gerätethreads und webgl, alles läuft entweder über Skript oder über ihre gemeinsame Swap-Chain. Ich wäre versucht, eine Swap-Chain-API bereitzustellen, die sich entweder über einer Surfman-Swap-Chain oder einer Openxr-Swap-Chain befindet, und diese für die Webgl-to-Openxr-Kommunikation verwenden.
Notizen aus einem Gespräch über die früheren Zeitmessungen:
* concerns about wait time - why?????
* figure out time spent in JS vs. DOM logic
* when does openxr give us should render=false frames - maybe related to previous frame taking too long
* are threads being scheduled on inappropriate cpus? - on magic leap, main thread (including weber) pinned to big core.
* when one of the measured numbers is large, is there correlation with other large numbers?
* probably should pin openxr thread, running deterministic code
* consider clearing after telling script that the swap is complete - measure if clear is taking significant time in swap operation
* consider a swap chain API operation - “wait until a buffer swap occurs”
- block waiting on swapchain
- block waiting on swapchain + timeout
- async????????
- a gc would look like a spike in script execution time
Eingereicht Nr. 25735, um die Untersuchungen zu verfolgen, die ich über das direkte Rendern in die Openxr-Texturen verfolge.
Eine Sache, die wir tun sollten, ist einzugrenzen, wie Spidermonkey auf dem Gerät im Vergleich zu anderen Engines abschneidet. Der einfachste Weg, hier einige Daten zu erhalten, besteht darin, einen einfachen JS-Benchmark zu finden, den Servo ausführen kann, und die Leistung von Servo mit dem auf dem Gerät installierten Edge-Browser zu vergleichen. Darüber hinaus könnten wir versuchen, einige komplexe Babylon-Demos in beiden Browsern zu besuchen, ohne in den immersiven Modus zu wechseln, um zu sehen, ob es einen signifikanten Leistungsunterschied gibt. Dies wird uns auch einen Benchmark zum Vergleich mit dem bevorstehenden Spidermonkey-Upgrade geben.
Einige neue Daten. Dies ist beim ANGLE-Upgrade der Fall, aber nicht beim IPC-Upgrade.
$ python timing.py raw
Name min max mean
raf queued 0.056198 5.673125 0.694902
<1ms: 335
<2ms: 26
<4ms: 17
<8ms: 7
<16ms: 0
<32ms: 0
32+ms: 0
raf transmitted 0.822917 36.582083 7.658619
<1ms: 1
<2ms: 4
<4ms: 31
<8ms: 181
<16ms: 158
<32ms: 8
32+ms: 1
raf wait 1.196615 39.707709 10.256875
<1ms: 0
<2ms: 32
<4ms: 93
<8ms: 67
<16ms: 107
<32ms: 68
32+ms: 17
raf execute 3.078438 532.205677 7.752839
<1ms: 0
<2ms: 0
<4ms: 37
<8ms: 290
<16ms: 52
<32ms: 2
32+ms: 3
raf receive 0.084375 9.053125 1.024403
<1ms: 276
<2ms: 71
<4ms: 27
<8ms: 9
<16ms: 1
<32ms: 0
32+ms: 0
swap request 0.004115 73.939479 0.611254
<1ms: 369
<2ms: 10
<4ms: 5
<8ms: 0
<16ms: 0
<32ms: 0
32+ms: 2
raf render 5.706198 233.459636 9.241698
<1ms: 0
<2ms: 0
<4ms: 0
<8ms: 183
<16ms: 190
<32ms: 10
32+ms: 1
run_one_frame 7.663333 2631.052969 28.035143
<1ms: 0
<2ms: 0
<4ms: 0
<8ms: 3
<16ms: 157
<32ms: 185
32+ms: 41
swap buffer 0.611927 8.521302 1.580279
<1ms: 127
<2ms: 169
<4ms: 74
<8ms: 15
<16ms: 1
<32ms: 0
32+ms: 0
swap complete 0.046511 2.446302 0.215040
<1ms: 375
<2ms: 6
<4ms: 3
<8ms: 0
<16ms: 0
<32ms: 0
32+ms: 0
Timing-Daten: https://gist.github.com/Manishearth/825799a98bf4dca0d9a7e55058574736
Eine gute Datenvisualisierung davon zu erhalten, ist schwierig. Ein gestapeltes Liniendiagramm scheint ideal zu sein, obwohl es erwähnenswert ist, dass run_one_frame mehrere bereits gemessene Timings misst. Es ist hilfreich, an der Reihenfolge der Diagramme herumzufummeln und verschiedene Spalten unten zu platzieren, um ihre Wirkung besser zu sehen. Außerdem müssen Sie die Y-Achse abschneiden, um aufgrund einiger sehr großer Ausreißer etwas Nützliches zu erhalten.
Interessante Dinge zu beachten:
Aktueller Status: Mit IPC-Fixes schwebt die FPS jetzt bei 55. Es wackelt manchmal ein bisschen, aber normalerweise geht es nicht unter 45, _außer_ während der ersten Sekunden nach dem Laden (wo es auf 30 sinken kann) und wenn es sieht zuerst eine Hand (wenn sie auf 20 sinkt).
Neueres Histogramm für Lackdemo ( Rohdaten ):
Name min max mean
raf queued 0.113854 5.707917 0.441650
<1ms: 352
<2ms: 13
<4ms: 5
<8ms: 1
<16ms: 0
<32ms: 0
32+ms: 0
raf transmitted 0.546667 44.954792 6.886162
<1ms: 4
<2ms: 2
<4ms: 23
<8ms: 279
<16ms: 59
<32ms: 3
32+ms: 1
raf wait 1.611667 37.913177 9.441104
<1ms: 0
<2ms: 6
<4ms: 98
<8ms: 82
<16ms: 135
<32ms: 43
32+ms: 6
raf execute 3.336562 418.198541 7.592147
<1ms: 0
<2ms: 0
<4ms: 11
<8ms: 319
<16ms: 36
<32ms: 2
32+ms: 3
raf receive 0.119323 9.804167 0.806074
<1ms: 324
<2ms: 31
<4ms: 13
<8ms: 1
<16ms: 1
<32ms: 0
32+ms: 0
swap request 0.003646 79.236354 0.761324
<1ms: 357
<2ms: 9
<4ms: 2
<8ms: 0
<16ms: 0
<32ms: 0
32+ms: 3
raf render 5.844687 172.898906 8.131682
<1ms: 0
<2ms: 0
<4ms: 0
<8ms: 283
<16ms: 86
<32ms: 1
32+ms: 1
run_one_frame 8.826198 2577.357604 25.922205
<1ms: 0
<2ms: 0
<4ms: 0
<8ms: 0
<16ms: 176
<32ms: 174
32+ms: 22
swap buffer 0.708177 12.528906 1.415950
<1ms: 164
<2ms: 161
<4ms: 38
<8ms: 4
<16ms: 4
<32ms: 0
32+ms: 0
swap complete 0.042917 1.554740 0.127729
<1ms: 370
<2ms: 1
<4ms: 0
<8ms: 0
<16ms: 0
<32ms: 0
32+ms: 0
Längerer Lauf ( roh ). Entwickelt, um die Auswirkungen von Startverzögerungen zu reduzieren.
Name Min. Max. Mittelwert
raf in der Warteschlange 0,124896 6,356562 0,440674
<1ms: 629
<2ms: 13
<4ms: 5
<8ms: 1
<16ms: 0
<32ms: 0
32+ms: 0
raf übertragen 0.640677 20.275104 6.944751
<1ms: 2
<2ms: 3
<4ms: 29
<8ms: 513
<16ms: 99
<32ms: 1
32+ms: 0
raf warten 1.645886 40.955208 9.386255
<1ms: 0
<2ms: 10
<4ms: 207
<8ms: 114
<16ms: 236
<32ms: 65
32+ms: 15
raf ausführen 3.090104 526.041198 6.226997
<1ms: 0
<2ms: 0
<4ms: 68
<8ms: 546
<16ms: 29
<32ms: 1
32+ms: 3
raf erhalten 0.203334 6.441198 0.747615
<1ms: 554
<2ms: 84
<4ms: 7
<8ms: 2
<16ms: 0
<32ms: 0
32+ms: 0
Tauschanfrage 0,003490 73,644322 0,428460
<1ms: 627
<2ms: 18
<4ms: 1
<8ms: 0
<16ms: 0
<32ms: 0
32+ms: 2
raf render 5.450312 209.662969 8.055021
<1ms: 0
<2ms: 0
<4ms: 0
<8ms: 467
<16ms: 176
<32ms: 3
32+ms: 1
run_one_frame 8.417291 2579.454948 22.226204
<1ms: 0
<2ms: 0
<4ms: 0
<8ms: 0
<16ms: 326
<32ms: 290
32+ms: 33
Swap-Puffer 0,658125 12,179167 1,378725
<1ms: 260
<2ms: 308
<4ms: 72
<8ms: 4
<16ms: 4
<32ms: 0
32+ms: 0
Swap komplett 0,041562 5,161458 0,136875
<1ms: 642
<2ms: 3
<4ms: 1
<8ms: 1
<16ms: 0
<32ms: 0
32+ms: 0
Grafiken:
Längerer Lauf:
Kürzerer Lauf:
Die große Spitze ist, wenn ich meine Hand in den Sensorbereich bringe.
Diesmal habe ich die wait/run_one_frame-Zeiten nach oben gesetzt, weil diese die am meisten gezackt sind, und das liegt daran, dass uns das Betriebssystem drosselt.
Ein paar Dinge zu beachten:
Die Leistungsknicke aufgrund des Sehens der Hand und dann des Beginnens des Ziehens sind für Ballschützen nicht vorhanden. Vielleicht macht die Farbdemo viel Arbeit, wenn sie sich zum ersten Mal entscheidet, das Handbild zu zeichnen?
(Dies könnte auch die Paint-Demo sein, die versucht, mit der Webxr-Eingabebibliothek zu interagieren)
@Manishearth Können Sie auch die Speichernutzung überlagern und mit diesen Ereignissen korrelieren? Zusätzlich zur erstmaligen Kompilierung von JS-Code können Sie bei einer Menge neuen Codes Fehler machen, an die physischen Speichergrenzen stoßen und eine Reihe von GCs verursachen, wenn Sie den Speicherdruck erreichen. Ich habe das in den meisten nicht-trivialen Situationen gesehen. Ich hoffe , dass das SM-Update von
Ich habe keine einfache Möglichkeit, Speicherprofilierungsdaten auf eine Weise zu erhalten, die mit dem xr-Profiling-Zeug korreliert werden kann.
Ich könnte möglicherweise die vorhandenen Perf-Tools verwenden und herausfinden, ob die Form gleich ist.
@Manishearth Zeigt das Xr-Profiling-Zeug (oder könnte es zeigen) JS GC-Ereignisse? Das könnte ein vernünftiger Proxy sein.
Wie auch immer, Startspitzen sind nicht mein Hauptanliegen, ich möchte zuerst alles _sonst_ mit 60 fps bekommen. Wenn es beim Start für ein oder zwei Sekunden ruckartig ist, ist das ein weniger dringendes Problem.
Ja, es könnte zeigen, dass es einige Optimierungen erfordern würde.
@Manishearth Übereinstimmende Prioritäten! Ich war mir nicht sicher, ob Sie versuchten, "die Knicke zu lösen" oder im Dauerzustand herunterzufahren. Stimmen Sie letzterem zu, das jetzt wichtiger ist.
Nein, ich habe meistens nur alle möglichen Analysen aufgeschrieben.
Diese Spitzen am Ende des Diagramms des kleineren Durchlaufs, bei denen auch die Übertragungszeit ansteigt: Das war, als ich meinen Kopf bewegte und zeichnete, und Alan bemerkte auch einen Rückgang der FPS, wenn er Dinge tat, und schrieb dies dem Betriebssystem zu, das andere Arbeiten erledigte . Nachdem der IPC meine Vermutung bezüglich Übertragungszeitspitzen behoben hat, ist, dass sie durch das Betriebssystem verursacht werden, das andere Arbeiten verrichtet, also könnte das sein, was dort vor sich geht. In einer Off-Main-Thread-Welt würde ich erwarten, dass es viel reibungsloser ist.
Ignorieren Sie mich, wenn dies bereits in Betracht gezogen wurde. Haben Sie daran gedacht, die Messung von run_one_frame
pro Nachricht aufzuschlüsseln und auch die Zeit zu bestimmen, die thread::sleep()
-ing aufgewendet wurde?
Es könnte sich lohnen, drei Messpunkte hinzuzufügen:
eine Umhüllung https://github.com/servo/webxr/blob/68b024221b8c72b5b33a63441d63803a13eadf03/webxr-api/session.rs#L364
und ein weiteres Wrapping https://github.com/servo/webxr/blob/2841497966d87bbd561f18ea66547dde9b13962f/webxr-api/lib.rs#L124 als Ganzes,
und auch eine, die den Anruf nur an thread::sleep
umschließt.
Was die recv_timeout
, könnte dies etwas sein, das man komplett überdenken sollte.
Ich finde es etwas schwierig, über die Nützlichkeit des Timeouts nachzudenken. Da Sie die gerenderten Frames zählen, sehen Sie sich frame_count
die komplette Event-Schleife des Main-Threads"?
Außerdem habe ich einige Zweifel an der tatsächlichen Berechnung der darin verwendeten delay
, wo derzeit:
delay = timeout / 1000
, wobei timeout
derzeit auf 5 ms eingestellt istdelay = delay * 2;
while delay < timeout
überprüft.Die Schlafsequenz läuft also im schlimmsten Fall etwa so ab: 5micro -> 10 -> 20 -> 40 -> 80 -> 160 -> 320 -> 640 -> 1.28milli -> 2.56 milli -> 5.12 milli
Wenn es 5,12 Millisekunden erreicht, brechen Sie aus der Schleife aus (seit delay > timeout
), nachdem Sie insgesamt 5.115 Millisekunden gewartet haben, plus die zusätzliche Zeit, die Sie damit verbracht haben, auf das Betriebssystem zu warten, um den Thread nach jedem sleep
.
Ich denke also, das Problem ist, dass Sie insgesamt länger als 5 ms schlafen können, und ich denke auch, dass es keine gute Idee ist, seit einer Nachricht zweimal länger als 1 ms zu schlafen (und das zweite Mal länger als 2,5 ms). könnte während dieser Zeit reinkommen und du wirst nicht aufwachen.
Ich bin mir nicht ganz sicher, wie ich es verbessern soll, es hört sich so an, als ob Sie versuchen würden, nach einer möglichen Nachricht zu suchen und schließlich einfach zur nächsten Iteration der Hauptereignisschleife überzugehen, wenn nichts verfügbar ist (warum nicht auf der recv?).
Sie könnten zu https://doc.rust-lang.org/std/thread/fn.yield_now.html wechseln. diesen Artikel über Sperren ansehen , scheint es sich ungefähr 40 Mal zu drehen, während Sie jedes Mal yield
aufrufen , ist optimal (siehe den Absatz "Spinning", kann nicht direkt darauf verlinken). Danach sollten Sie entweder den Empfänger blockieren oder einfach mit der aktuellen Iteration der Ereignisschleife fortfahren (da diese wie eine Unterschleife innerhalb der Haupteinbettungs-Ereignisschleife läuft).
(natürlich, wenn Sie nicht mit Mess ipc
eingeschaltet, der Teil oberhalb auf recv_timeout
ist irrelevant, obwohl Sie immer noch die Calll zu wollen , könnten messen recv_timeout
auf dem mpsc
da der Threaded-Kanal einige interne Drehungen / Nachgiebigkeiten ausführt, die auch die Ergebnisse beeinflussen könnten.Und da oben mehrmals ein nicht identifizierter "IPC-Fix" erwähnt wurde, gehe ich davon aus, dass Sie mit ipc messen).
Ignorieren Sie mich, wenn dies bereits in Betracht gezogen wurde, haben Sie daran gedacht, die Messung von run_one_frame auf eine pro Nachricht behandelte Basis aufzuschlüsseln und auch die Zeit zu bestimmen, die für thread::sleep()-ing aufgewendet wurde?
Es ist bereits aufgeschlüsselt, die Warte-/Renderzeiten sind genau so. Ein einzelner Tick von run_one_frame ist ein Rendern, ein Warten und eine unbestimmte Anzahl von verarbeiteten Ereignissen (selten).
recv_timeout ist eine gute Idee für die Messung
Leider scheint das Spidermonkey-Upgrade in #25678 keine signifikante Verbesserung zu sein - die durchschnittlichen FPS jeder Demo mit Ausnahme der am stärksten eingeschränkten Speicherkapazität sind gesunken; die Hill Valley-Demo stieg leicht an. Das Ausführen von Servo mit -Z gc-profile
in den Initialisierungsargumenten zeigt keinen Unterschied im GC-Verhalten zwischen dem Master und dem Spidermonkey-Upgrade-Zweig - es werden keine GCs gemeldet, nachdem der GL-Inhalt geladen und angezeigt wurde.
Maße für verschiedene Branchen:
master:
- espilit: 14-16 fps
- paint: 39-45 fps
- ball shooter: 30-40 fps
- hill valley: 8 fps, 200mb free mem
- mansion: 10-14fps, 650mb free mem
master + single swapchain:
- espilit: 10-12 fps
- paint: 29-55 fps, 1.2gb free mem
- ball shooter: 25-35 fps, 1.3gb free mem
- hill valley: 6-7 fps, 200mb free mem
- mansion: 10-11 fps, 700mb free mem
texture sharing + ANGLE 2.1.19:
- espilit: 13-15 fps, 670mb free mem
- paint: 39-45 fps
- ball shooter: 30-37 fps, 1.3gb free mem
- hill valley: 9-10 fps, 188mb free mem
- mansion: 13-14 fps, 671mb free mem
smup:
- espilit: 11-13 fps, 730mb free mem
- paint: 25-42 fps, 1.1gb free mem
- ball shooter: 26-30 fps, 1.4gb free mem
- hill valley: 10-11 fps, 145mb
- mansion: 9-11fps, 680mb free mem
Der Smup hat die Leistung verschlechtert???
Mit den Änderungen von https://github.com/servo/servo/pull/25855#issuecomment -594203492 gibt es das interessante Ergebnis, dass das Deaktivieren des Ion JIT bei 12 FPS beginnt und dann einige Sekunden später abrupt auf 1 FPS steigt und bleibt dort.
Habe ein paar Messungen mit diesen Patches gemacht.
Beim Malen erhalte ich 60 fps, wenn nicht viel Inhalt zu sehen ist, und wenn ich gezeichneten Inhalt betrachte, sinkt er auf 50 fps (die gelben Spitzen sind, wenn ich gezeichneten Inhalt betrachte). Es ist schwer zu sagen, warum, meistens scheint die Wartezeit durch Openxr-Drosselung beeinflusst zu werden, aber die anderen Dinge scheinen nicht langsam genug zu sein, um ein Problem zu verursachen. Das Timing der Swap-Anforderung ist etwas langsamer. Die rAF-Ausführungszeit ist anfangs langsam (dies ist die anfängliche Verlangsamung des "ersten Mals, dass ein Controller gesehen wird"), aber danach ist sie ziemlich konstant. Es scheint, als würde uns openxr nur drosseln, aber anderswo gibt es keine sichtbare Verlangsamung, die dies verursachen würde.
Das ist, was ich für die ziehende Demo habe. Die y-Skala ist die gleiche. Hier ist es viel offensichtlicher, dass uns die Ausführungszeit verlangsamt.
Zu beachten ist, dass ich Messungen mit # 25837 durchgeführt habe und dies die Leistung beeinträchtigen könnte.
Ich nicht, aber ich habe ähnliche Ergebnisse wie Sie erhalten
Diagramme des Leistungstools für den Moment, in dem es beim Betrachten von Inhalten von 60 FPS auf 45 FPS geht:
es scheint, als ob die Schuld ausschließlich bei xrWaitFrame liegt, alle anderen Timings liegen ziemlich nah beieinander. Der xrBeginFrame ist immer noch fast unmittelbar nach xrWaitFrame, der xrEndFrame ist 4us nach dem xrBeginFrame (in beiden Fällen). Der nächste xrWaitFrame ist fast unmittelbar nach dem xrEndFrame. Die einzige unberücksichtigte Lücke wird durch xrWaitFrame selbst verursacht.
Bei der ziehenden Demo erhalte ich folgende Spur:
Dies ist die Farbdemo mit dem gleichen Maßstab:
Wir sind langsam zwischen Beginn/Ende des Frames (von 5 ms bis 38 ms beim schnellsten!) beide.
Die ziehende Demo wird verlangsamt, weil ihre Lichtquelle einen Schatten wirft. Das Schatten-Zeug wird auf der GL-Seite gemacht, also bin ich mir nicht sicher, ob wir das leicht beschleunigen können?
Wenn dies vollständig über GLSL erfolgt, können wir Schwierigkeiten haben; Wenn dies in jedem Frame über WebGL-APIs erfolgt, gibt es möglicherweise Optimierungspunkte.
Ja, es scheint alles auf der GLSL-Seite zu liegen; Ich konnte keine WebGL-Aufrufe sehen, wenn es um die Funktionsweise der Shadow-APIs geht, nur einige Bits, die an Shader weitergegeben werden
Ich glaube, das wurde allgemein angesprochen. Wir können Probleme für einzelne Demos einreichen, die noch bearbeitet werden müssen.
Hilfreichster Kommentar
Aktueller Status: Mit IPC-Fixes schwebt die FPS jetzt bei 55. Es wackelt manchmal ein bisschen, aber normalerweise geht es nicht unter 45, _außer_ während der ersten Sekunden nach dem Laden (wo es auf 30 sinken kann) und wenn es sieht zuerst eine Hand (wenn sie auf 20 sinkt).