Toon posts:

[.net] Matrix Rotate performance probleem

Pagina: 1
Acties:

Verwijderd

Topicstarter
Voor een hobby projectje heb ik een form waar ik met GDI+ wat wijzertjes op teken. Dat wijzertje is een System.Drawing Ellipse en twee Line's. Die voeg ik samen in een GraphicsPath. Ik stel vervolgens mijn draaipunt in.

Nu wordt er om de 100ms (de updatetijd als het ware van mijn form) de refresh method aangeroepen van het object waar ik in teken. Op dat moment wordt er vanuit een double waarin de snelheid staat (bv 120.00) en een double waarin het toerental staat (bv 4000.00) een hoek uitgerekend die mijn wijzer moet aannemen.

Bijvoorbeeld:
Visual Basic .NET:
1
2
3
4
        dblPercentageHoekKM = dblSnelheid / 2.4 + 4.166666666666667
        dblHoekKM = (dblPercentageHoekKM / 100) * 360
        dblPercentageHoekRPM = dblToeren / 120 + 12.5
        dblHoekRPM = (dblPercentageHoekRPM / 100) * 360


Vervolgens doe ik (in het geval van de KM wijzer, de RPM wijzer is hetzelfde):
Visual Basic .NET:
1
2
3
4
        Dim gdiMatrixKM As New Matrix(1, 0, 0, 1, 1, 1)
        gdiMatrixKM.RotateAt(dblHoekKM, gdiDraaipuntKM)
        gdiGraphicsPathKM.Transform(gdiMatrixKM)
        gdiGraphics.DrawPath(Pens.Red, gdiGraphicsPathKM)


Dit alles moet dus om de 100ms gebeuren. Daarnaast worden ook om de 100ms labels van een nieuwe text voorzien.

Werken doet het wel, met één meter vloeiend zonder label updates, dat is al 100% CPU! Als ik de rest erbij doe gaat het nog wel snel en foutloos maar niet vloeiend.

Is die rotateat method met de transform de boosdoener? Is er een efficientere manier?

Screenshot:
http://home.planet.nl/~jpabst/Scr/sc_per4.jpg

Dat van die labels snap ik wel, de controls zijn transperant en daar is Forms uit .NET niet zo efficient mee dus die zal ik dan zelf moeten tekenen... Is dus ook niet de vraag.

Verwijderd

Dat kun je eenvoudig proberen door te kijken naar de CPU usage wanneer je het deel waarvan je denkt dat het de boosdoener is weg laat.

Wellicht dat je gebruik kunt maken van Direct-X of WPF om een performance boost te krijgen?

Hoe zorg je ervoor dat het geheel iedere 100ms wordt geupdate? Doe je dat via de onPaint methode?

Verwijderd

Topicstarter
Jep teken in de onPaint event. Maar als ik geen rotate doe bewegen ze niet dus ik weet dat het in de rotate zit ;).

Verwijderd

Ja maar wat is de CPU usage op het moment dat je de rotate weglaat?

Hoe zorg je ervoor dat je precies na 100ms de boel invalidate? (of is die 100ms gemeten?)

Als je namelijk continu invalidate zal de CPU usage altijd 100% zijn (en daarom vooral geschikt voor spellen)

Verwijderd

Topicstarter
100ms timer -> tick -> snelheid, toeren += x -> object.refresh

Verwijderd

Een timer daarvoor gebruiken lijkt me qua performance niet echt handig, daar zijn ze absoluut niet voor bedoelt.

Probeer dat eens via een andere thread te doen, ondertussen laat je de onPaint enkel vanuit de gegevens de grafische weergave doen dus zo min mogelijk verwerking alleen puur dat wat nodig is om de grafische representatie voor elkaar te krijgen.

Als je dan gebruik gaat maken van DirectX of WPF zou je voor zoiets gerust een framerate van 60+ zelfs op tragere systemen moeten krijgen.

Als je de verwerkingsthread blocking maakt kun je ervoor zorgen dat er hoe dan ook iedere 100ms verwerkt wordt, bij een ingewikkelde verwerking zal dan gewoon je framerate even inkakken zoals je dat ook ziet bij games.

Verwijderd

Topicstarter
Thanks, ik ga er eens achteraan. Nog nooit met DirectX gewerkt... Weer een leuke uitdaging ;).

Verwijderd

Noujah je kunt allereerst even proberen de timer te vervangen voor een thread, in .NET kan dat met 2 regels code dus dat moet een kleine moeite zijn.

Maar aangezien je nog meer grafisch wilt doen is DirectX of WPF echt aan te raden.

Verwijderd

Daarom kan het goed zijn dat de performance al goed genoeg is wanneer je de timer vervangt door een thread.

Probeer dat eerst maar eens en zie wat het doet. Scheelt je veel tijd tov alles om te schrijven naar DirectX of WPF.

  • Not Pingu
  • Registratie: November 2001
  • Laatst online: 20-11 21:40

Not Pingu

Dumbass ex machina

GDI+ is sowieso vrij langzaam. Zowat de hele System.Drawing namespace is een wrapper rond native windows DLL's en daardoor nog een stukkie langzamer.
Matrix.RotateAt() roept intern tweemaal SafeNativeMethods.Gdip.GdipTranslateMatrix() aan, wat dus uit een extern gelinkte DLL komt.

Je kunt overwegen zelf een matrix class te implementeren (is echt niet zo moeilijk als je een beetje thuis bent in matrixwiskunde), daarin de transformaties te doen en het resultaat op de een of andere manier omzetten naar een System.Drawing.Drawing2D.Matrix om de koppeling met alle S.D.Drawing2D classes te behouden.

Certified smart block developer op de agile darkchain stack. PM voor info.


Verwijderd

Dat lijkt me omslachtiger als gewoon over te stappen op DirectX of WPF.

Maar dat is wel precies de reden waarom WPF gemaakt is, GDI+ is niet echt het snelste.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Wat mischien ook nog in performance kan schelen is eens in de 100 ms naar een Buffer tekenen en dan in je OnPaint alleen die buffer naar het scherm tekenen. Dan heb je ook geen probleem dat hij elke keer als er iets over het scherm gesleept wordt opnieuw de berekeningen en het tekenen moet gaan doen. Al zou dat voor zulke eenvoudige berekeningen niet zo'n probleem moeten zijn.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • MisterData
  • Registratie: September 2001
  • Laatst online: 27-11 20:42
DirectX is echt overkill voor een simpele wijzerplaat. Wat je kunt proberen is in GDI+ de opties als anti-aliasing en high quality compositing uit te zetten. Dat scheelt enorm!

Verwijderd

MisterData schreef op vrijdag 06 juli 2007 @ 12:58:
DirectX is echt overkill voor een simpele wijzerplaat. Wat je kunt proberen is in GDI+ de opties als anti-aliasing en high quality compositing uit te zetten. Dat scheelt enorm!
Daar wordt het ook niet echt mooier van.

DirectX is niet echt overkill, dat is de hele reden waarom WPF gemaakt is, DirectX is tegenwoordig op ieder systeem beschikbaar, is gemakkelijk aan te sturen en geeft geweldige resultaten.

Waarom dan zoiets in GDI+ doen? GDI+ is onderweg om uitgefaseerd te worden dus als je kunt overstappen op iets anders kun je het beter eerder doen dan later.

Verwijderd

Verwijderd schreef op donderdag 05 juli 2007 @ 22:36:
Dat lijkt me omslachtiger als gewoon over te stappen op DirectX of WPF.

Maar dat is wel precies de reden waarom WPF gemaakt is, GDI+ is niet echt het snelste.
Over omslachtig gesproken... we hebben het hier over een simpele cirkel en twee lijntjes...

Verwijderd

Verwijderd schreef op vrijdag 06 juli 2007 @ 15:21:
[...]


Over omslachtig gesproken... we hebben het hier over een simpele cirkel en twee lijntjes...
Ik neem aan dat de lijntjes later nog grafisch gemaakt moet worden, daarnaast moet je die lijntjes 100x per sec roteren en ik neem ook aan dat er nog meer op het dashboard bij komt.

Overstappen naar WPF is niet zoveel werk en ook managed DirectX is makkelijker als je zou denken.

Maar mijn eerste advies was dan ook om de timer te vervangen door een aparte thread, wellicht levert dat al genoeg performance op.

Ik zou in ieder geval geen concessies doen adhv de grafische weergave, het moet er gewoon top uitzien en dat is goed te doen, de technieken zijn beschikbaar.

  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07 12:07
Voor een ander project moest ik voor de eerste keer c# en winforms gebruiken. Helaas was dat nogal grafish zwaar: eenvoudig opgelost door Flash te embedden... mooi, snel, geen gefuss en ook communicatie tussen c# / actionscript is a doddle.

  • MisterData
  • Registratie: September 2001
  • Laatst online: 27-11 20:42
Okee, kennelijk is GDI+ een heel stuk langzamer onder .NET dan het onder C++ (waar ik het vaak gebruik en het op zich goed performt!). Het bezwaar dat ik tegen DirectX heb is niet zozeer dat het geen fijne toolkit zou zijn (het is juist een hele prettige API!), maar het feit dat het geen vervanger is voor GDI+. DirectX is een systeem om met basis-primitives zoals triangles en vertices figuren te laten zien. GDI+ zit eigenlijk een niveau hoger, en houdt zich bezig met zaken als curves en brushes. Een plaatje blurren in GDI+ bestaat uit het toepassen van een Effect, in DirectX zul je de textures zelf moeten blurren of daarvoor een pixel shader moeten programmeren... om het verschil maar even aan te duiden. DirectX is veel meer 'low level'.

Het kan gewoon niet zo zijn dat GDI+ voor zoiets simpels als een klok te langzaam is. Wellicht doe je iets verkeerd op het moment dat je tekent (je maakt misschien steeds een nieuwe Bitmap aan om double buffering toe te passen ofzo; gebruik je uberhaupt double buffering? Ik kan me voorstellen dat direct tekenen naar het scherm stukken trager is dan eerst offscreen tekenen en dan de boel blitten...). Over het uitschakelen van de anti aliasing hierboven :GDI+ staat het toe om zelf te kiezen wanneer je AA inschakelt en weer uitzet, zelfs bij het tekenen van verschillende figuren in dezelfde Graphics-context. Als je wijzer echt de bottle neck is (wat ik betwijfel!) dan moet je het toch eens proberen of AA uitschakelen daar helpt. Wat je ook zou kunnen doen is (als het tekenen van de achtergrond van de wijzer ook 'zwaar' is) de achtergrond vantevoren tekenen en daar steeds alleen de wijzer overheen tekenen :) Wat ook kan helpen is om niet steeds een nieuwe matrix (en wellicht ook je GraphicsPath) te alloceren (dat kost relatief veel tijd!) maar om die ergens in een variabele op te slaan.

DirectX is mooi, maar het kost veel moeite en daarnaast moet je je hele applicatie, die al GDI+ gebruikt, gaan ombouwen zodat je Direct3D-wijzertje mooi integreert in de rest van je programma en/of je de rest van het programma ook via DirectX gaat tekenen :)

[ Voor 13% gewijzigd door MisterData op 06-07-2007 19:40 ]


Verwijderd

Je hebt helemaal gelijk, DirectX is geen vervanger voor GDI+, maar voor dit specifieke ding wel redelijk geschikt.

Maar het is waarschijnlijk niet zo dat het nodig is om van GDI+ af te stappen, gewoon die timer vervangen door een thread en het gaat waarschijnlijk al een klapje beter.

Omdat DirectX niet echt een vervanger is voor GDI+ heeft Microsoft nu WPF gemaakt, dat is het principe van GDI+ gebaseerd op DirectX dus het beste van 2 werelden.

Mocht je DirectX gaan gebruiken is het wel het beste om dan gelijk alles van de UI in DirectX te doen, als je toch bezig bent. Geeft je gelijk de mogelijkheid om wat meer gebruik te maken van mooie filters en effecten.

[ Voor 19% gewijzigd door Verwijderd op 06-07-2007 19:47 ]


  • SjonnieX
  • Registratie: Juni 2007
  • Laatst online: 15-04 21:57
Wat MisterData al zei denk ik ook: "Het kan gewoon niet zo zijn dat GDI+ voor zoiets simpels als een klok te langzaam is. Wellicht doe je iets verkeerd op het moment dat je tekent".
Die matrix berekening en die thread of timer kosten volgens mij allemaal geen tijd t.o.z. van het tekenen!

Paint je iedere keer je gehele scherm opnieuw?
Je moet alleen de wijzer repainten: dwz:

je onPaint ziet er dan als volgt uit:
onPaint()
{
PaintBackGround();
PaintWijzer() ;
}

je invalidate code moet er als volgt uit zien:

NewWijzerRect = BerekenNewWijzerRect();
invalidate(OldWijzerRect);
invalidate(NewWijzerRect);
OldWijzerRect = NewWijzerRect;
updateWindow();

Verwijderd

Topicstarter
Ik paint alleen een PB, die is transparant over de achtergrond van de form.

Gebruik geen AA, dat lijkt zo op het screenshotje maar dat is gewoon de JPEG compressie die je ziet :D.

Dat doe ik nu nog met een timer, ik ga het met een threat proberen. Ik buffer niet, ook een goeie tip. Kom er wel uit denk ik, anders horen jullie het wel weer ;).
Pagina: 1