[C# WPF] Efficiente data bindings en UI update rate meten

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Ik heb een WPF applicatie die in principe gezien kan worden als een soort live powerpoint presentatie. Er is een hoofdscherm waarop de gebruiker verschillende elementen kan slepen die live data kunnen tonen, zoals text elementen. De gebruiker kan dus zijn eigen scherm ontwikkelen.

Nu wil ik graag meten hoe lang WPF nodig heeft om dit scherm te updaten elke keer als de achterliggende data een update krijgt.

Het systeem werkt eigenlijk vrij eenvoudig. Ik hou een lijstje bij met de text elementen die de gebruiker op zijn scherm heeft geplaatst. De text elementen zijn in de basis gewoon een TextBlock met een data binding op de Text property;
C#:
1
<TextBlock Text="{Binding Value}" />


De Value property van alle text elementen krijgt ongeveer 30 keer per seconde een update vanuit de achterliggende live data (waar die vandaan komt lijkt me niet belangrijk hier). In een loop draai ik de volgende updater:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private double _previousUpdate = 0;

public void Update()
{
    // Get bindings (cached after first run)
    var bindings = GetBindings();
    
    // Update all
    foreach (var binding in bindings)
    {
        binding.UpdateValue();
    }
    
    // Measure time
    var now = (double) DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond;
    FPS = 1d / (now - _previousUpdate);
    _previousUpdate = now;
}

private List<Binding> GetBindings()
{
    ...
}

public class Binding : INotifyPropertyChanged
{
    public string Value {get;set;} // notify property changed omitted 

    public void UpdateValue()
    {   
        // Set appropriate value, triggers property changed event, triggers UI update
        Value = ...
    }
}


Kort gezegd, de Value property van de Binding class krijgt ongeveer 30 keer per seconde een nieuwe waarde. Via de INotifyPropertyChanged krijgt de UI vervolgens een update.


In de Update loop kan ik in principe de "FPS" meten van de update code. Dit geeft me het tijd verschil tussen twee updates. Dit zou ongeveer 30 moeten zijn maar in de praktijk is dat ~20-25, wat prima is.

In de UI kan echter nog een veel grotere lag zitten. Als er veel elementen zijn, of veel afbeeldingen, dan loopt de UI redelijk traag met een FPS die ik onder de 10 schat.

De vraag is nu - hoe kan ik deze FPS meten? De "data FPS" die ik nu al meet is leuk maar niet heel relevant, de UI FPS vind ik veel belangrijker om te meten en te laten zien.


Ik zie nu twee problemen:

1. Ik roep in een loop de UpdateValue aan voor elk text element, wat op zijn beurt een NotifyPropertyChanged aanroept en de UI gaat updaten. Maar eigenlijk is het onzin om dit voor elk text element apart te doen. Ik zou eigenlijk moeten wachten tot alle updates klaar zijn, en dan 1 keer de UI updaten.
Is WPF slim genoeg dat dit al vanzelf gebeurt? Of moet ik hier op een of andere manier rekening mee houden?

2. Ik kan niet vinden hoe ik de 'delay' kan meten die er zit tussen het eind van de Update loop, en de eerstvolgende UI refresh. Ik wil dus eigenlijk meten wat de update rate / fps van de UI is, maar ik zie geen manier om dat te doen.

Ik heb geprobeerd om de OnRender of LayoutUpdated events van de view te gebruiken, en daar de laatste update tijd op te slaan om te meten hoe lang er tussen twee updates zit. Maar WPF lijkt slim genoeg om niet constant te updaten en alleen als er daadwerkelijk iets veranderd. Deze FPS is dus niet heel relevant en varieert van ontzettend hoge waardes (als er paar sec niks veranderd) naar heel lage waardes (als er ineens meerdere updates in een korte tijd komen).


Eigenlijk twee vragen dus:
Kan ik dit proces optimalizeren?
Hoe kan ik meten hoe efficient het momenteel is (zodat ik weet of ik het daadwerkelijk aan het verbeteren ben)?

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 27-09 13:03
Kun je zien/meten hoe vaak je UI/controls WM_PAINT messages krijgen?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
farlane schreef op dinsdag 19 september 2017 @ 12:07:
Kun je zien/meten hoe vaak je UI/controls WM_PAINT messages krijgen?
Ik heb een test applicatie gemaakt die deze situatie nabootst en daar zit ik dit in te proberen. Het lijkt echter niet te werken.
Ik gebruik deze methode om de WM_PAINT message af te wachten:
https://pingfu.net/receive-wndproc-messages-in-wpf

Maar ik krijg gewoon nooit een WM_PAINT message binnen (waarde 0x000f of 15 zou het moeten zijn?). Zelfs niet als ik de window ga resizen ofzo. Ondertussen staan er 20 textboxes continu random getallen weer te geven. Wel komen tig andere messages binnen.

Misschien ligt het aan mij... Ik ga nog wat andere manieren proberen.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 27-09 13:03
I understand why you tried WM_PAINT to fix your problem. However WM_PAINT really has nothing to do with WPF rendering at all.
Blijkbaar doet WPF niet aan WM_PAINT ...

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • epic007
  • Registratie: Februari 2004
  • Laatst online: 07-10 10:46

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Bedankt, ik heb er mee zitten spelen. Hiermee kan ik inderdaad een soort van FPS van de UI zien.

Maar ik wil het graag ook aan de gebruiker laten zien, dus in dat geval heb ik hier niet zoveel aan. Het helpt wel bij debuggen.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Feanathiel
  • Registratie: Juni 2007
  • Niet online

Feanathiel

Cup<Coffee>

Volgens mij is CompositionTarget.Rendering "as good as it gets". Als er iets gerendered moet worden dan gaat dit event af.
Pagina: 1