[VB.net/GDI+] Veel CPU gebruik tijdens painten

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • mrfatmen
  • Registratie: Februari 2001
  • Nu online
Ik ben bezig om een Terminal Emulator te maken om onze applicatie in te draaien.
Het communicatie protocol is Pick PC console welke helaas maar door enkele emulators wordt ondersteund.
We bouwen de emulator zodat we meer windows windows elementen kunnen toevoegen zonder dat we de gebruikers storen met andere applicaties.

De emulatie zelf is geen probleem dit is reeds voor elkaar.
Ook het weergeven / opbouwen van het scherm werkt tegen een acceptable snelheid echter het CPU gebruikt is erg hoog als er meerdere scherm wijzigingen zijn in korte tijd.

In het hoofdmenu van onze applicatie waar een klok en een ticker lopen loopt het cpu gebruik op tot 30%.
Na wat zoek werk blijkt het puur in het scherm opbouwen te zitten. Zet ik de paints uit zakt het verbruik naar 0-2 %.

Wat code die we gebruiken:
Visual Basic .NET:
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Protected Overloads Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        Dim bt As DateTime = Now
        Me.Redraw(e.Graphics, e.ClipRectangle)
        Me.ShowCaret(e.Graphics)

        Console.WriteLine(Now.ToString("HH:mm:ss:fffff") & "  " & (e.ClipRectangle.IsEmpty) & "  " & Now.Subtract(bt).TotalMilliseconds & "  " & e.ClipRectangle.Width & " X " & e.ClipRectangle.Height)

        MyBase.OnPaint(e)
End Sub

Private Sub Redraw(ByVal CurGraphics As System.Drawing.Graphics, ByVal rect As Rectangle)
        Dim CurPoint As System.Drawing.PointF
        Dim CurChar As System.Char

        For Y As System.Int32 = 0 To Me._rows - 1
            For X As System.Int32 = 0 To Me._cols - 1

                CurChar = Me.charGrid(Y)(X)

                If CurChar = ControlChars.NullChar Then
                    CurChar = " "
                End If

                CurPoint = New System.Drawing.PointF(X * Me.CharSize.Width, Y * Me.CharSize.Height)
                If not rect.IsEmpty Then
                    If Not rect.Contains(Point.Ceiling(CurPoint)) Then Continue For
                End If

                Me.ShowChar(CurGraphics, CurChar, CurPoint.Y, CurPoint.X, Me.attribGrid(Y)(X))
            Next
        Next
End Sub

Private Sub ShowChar(ByVal CurGraphics As System.Drawing.Graphics, ByVal CurChar As System.Char, ByVal Y As System.Single, ByVal X As System.Single, ByVal CurAttribs As uc_CharAttrib)
        If CurChar = ControlChars.NullChar Then
            CurChar = " "
        End If

        Dim CurFGColor As System.Drawing.Color = System.Drawing.Color.White
        Dim CurBGColor As System.Drawing.Color = System.Drawing.Color.Black
        Dim blink As Boolean = False

        If CurAttribs IsNot Nothing Then
            CurFGColor = CurAttribs.foreColor
            CurBGColor = CurAttribs.backColor
            blink = CurAttribs.isBlinking
        End If

        Dim sFormat As New StringFormat(StringFormat.GenericTypographic)
        sFormat.Alignment = StringAlignment.Near
        sFormat.LineAlignment = StringAlignment.Near
        sFormat.Trimming = StringTrimming.None

        Dim path As New Drawing2D.GraphicsPath
        path.AddString("Û", Me.Font.FontFamily, 0, Me.Font.Size, New PointF(X - Me.drawStringOffset.X, Y - Me.drawStringOffset.Y), sFormat)
        CurGraphics.DrawPath(New Drawing.Pen(CurBGColor), Path)
        CurGraphics.FillPath(New SolidBrush(CurBGColor), Path)

        If blink And Me._BlinkVisible = False Then Exit Sub

        Path.Reset()
        Path.AddString(CurChar.ToString(), Me.Font.FontFamily, 0, Me.Font.Size, New PointF(X - Me.drawStringOffset.X, Y - Me.drawStringOffset.Y), sFormat)
        CurGraphics.FillPath(New SolidBrush(CurFGColor), Path)

        Threading.Thread.Sleep(0)

End Sub


We gebruiken path om de letters mooi te laten aansluiten, met drawstring is dit bijna niet te doen.
Echter zit daar ook het probleem, het gebruik van Paths op deze manier is erg zwaar.
Is er een manier om dit te versnellen?
Kan ik het rekenwerk verplaatsen naar de GPU?

Iedere letter wordt appart getekend omdat elke letter een verschillende voor en achtergrond kleur kan hebben.
Elke scherm wijziging krijgt bijbehorende invalidate region meegestuurd.


Om een beeld te vormen van de applicatie: Terminal Client in aanmaak
De omcircelde stukken worden 2x per seconde vernieuwt.
"Û" is een effe gekleurd blokje

Heeft uw auto pijn? Ga dan naar de onderdelenlijn
Het bedrijf waar ik met veel plezier werk - Mijn eigen vertrouwde domein


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 10:08

Haan

dotnetter

Als ik je screenshot bekijk, lijkt het alsof je een UI uit begin jaren '90 hebt nagemaakt, of is dat die emulator?

Rekenwerk verplaatsen naar GPU is natuurlijk onzin, dat kan alleen voor specifieke berekeningen waar een GPU goed in is, en is niet een magische oplossing waarmee je zomaar problemen in je Forms applicatie op kan lossen.

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • mrfatmen
  • Registratie: Februari 2001
  • Nu online
De hele applicatie is de emulator. Het scherm wat je ziet wordt vanuit Pick (de database omgeving) naar de emulator gestuurt. Het gaat puur om de juiste letters in je juiste kleuren op je juiste plek te zetten.
En dan vooral om welke manier hiervoor het beste is.

Het programma bestaat ook reeds 20 jaar maar is kwa functies erg modern alleen het uiterlijk loopt achter. Helemaal omzetten naar windows is wel een wens maar momenteel niet redelijk gezien de kosten en de afzet.

Heeft uw auto pijn? Ga dan naar de onderdelenlijn
Het bedrijf waar ik met veel plezier werk - Mijn eigen vertrouwde domein


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik kan me voorstellen dat het probleem idd is dat je paths gebruikt. Waarom cache je tekens niet gewoon? Een blit van een plaatje is velen malen sneller dan een path uittekenen met een pen. En aangezien je het over een terminal hebt, zijn het aantal verschillende tekens en aantal verschillende kleuren zeer gering.

Waarom doe je de achtergrond eigenlijk überhaupt ook met een path, en niet gewoon met een fillrect?

[ Voor 17% gewijzigd door .oisyn op 08-04-2010 17:24 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

Verwijderd

Kan je niet een Console object aanmaken, die dit allemaal doet voor je?

Acties:
  • 0 Henk 'm!

  • jip_86
  • Registratie: Juli 2004
  • Laatst online: 11:11
Waar is onderstaande precies voor:
Visual Basic .NET:
1
Threading.Thread.Sleep(0) 

Acties:
  • 0 Henk 'm!

  • mrfatmen
  • Registratie: Februari 2001
  • Nu online
hmm, cache van de tekens is inderdaad een goed idee daar ga ik eens wat testjes meedoen.
De achtergrond heb ik zo gedaan omdat ik zo kwa formaat altijd juist uitkwam.
Omdat gdi nog wel eens een offset wil toevoegen soms.

Wat bedoel je precies met een console object?
Ik gebruik nu een usercontrol voor de terminal gedeelte om dat van de overige functies te scheiden.
Maar weet niet zeker of je dat bedoelt.

Die thread.sleep(0) was bedoelt om de cpu tijd te geven om zijn stack of te werken.
Hoopte dat hij zo wat load zal verliezen.

[ Voor 13% gewijzigd door mrfatmen op 08-04-2010 17:32 ]

Heeft uw auto pijn? Ga dan naar de onderdelenlijn
Het bedrijf waar ik met veel plezier werk - Mijn eigen vertrouwde domein


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

mrfatmen schreef op donderdag 08 april 2010 @ 17:31:
Wat bedoel je precies met een console object?
Zo'n window die je ook krijgt als je cmd.exe opstart. Een applicatie kan zo'n ding zelf ook maken met AllocConsole(), en dan kun je het scherm vullen met WriteConsoleOutput().
mrfatmen schreef op donderdag 08 april 2010 @ 17:31:
Die thread.sleep(0) was bedoelt om de cpu tijd te geven om zijn stack of te werken.
Hoopte dat hij zo wat load zal verliezen.
Een Sleep(0) zal de timeslice van de huidige thread afbreken, zodat een andere thread die staat te wachten aan de beurt komt. Echter, de load zal nooit minder worden - als er geen andere thread staat te wachten, wat 99% van de tijd het geval is, dan komt de huidige thread weer gewoon doodleuk aan de beurt. Je kunt het dus gebruiken om andere threads even aan de beurt te laten, niet om de load te verminderen. Om dat laatste te doen zul je een langere tijd moeten sleepen, maar doorgaans wil je dat niet.

[ Voor 53% gewijzigd door .oisyn op 08-04-2010 17:38 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • epic007
  • Registratie: Februari 2004
  • Laatst online: 25-08 11:27
Het lijkt alsof je alle tekenroutines nu op de voorgrond uitvoert. Een truc is om een geheugen bitmap te maken en daar al je tekenroutines op los te laten, vervolgens blit je die bitmap in zijn geheel naar het scherm.

http://msdn.microsoft.com...ing.bufferedgraphics.aspx

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

The simplest way to use double buffering is to set the OptimizedDoubleBuffer control style flag on a control using the SetStyle method. Setting the OptimizedDoubleBuffer flag for a control redirects all painting for the control through a default graphics buffer, without requiring any additional code. This flag is set to true by default.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

Verwijderd

Je kan ook nog een optimalisatie doorvoeren door alleen die characters te hertekenen die ook daadwerkelijk veranderen. Zullen er in de praktijk niet zo veel zijn lijkt me>?

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Tsja, waarom gebruik je Path, wil je Cleartype uitschakelen ofzo? ;) Ik zou kijken naar TextRenderer.DrawText met TextFormatFlags.NoPadding + TextFormatFlags.NoPrefix, dan kun je gelijk de achtergrond instellen, en heb je maar 1 aanroep per groep karakters nodig. Hoogte en breedte kun je achterhalen met MeasureText, en als je een font als new Font(FontFamily.GenericMonospace-font,size) hebt, dan is dat stabiel, als je tenminste geen gekke tekens zoals ۝ gebruikt. Bedoel je trouwens geen █ ipv Û?

Dan nog sluit █ alleen op █ aan als je ze in 1 keer tekent. Er blijft een randje van 1 pixel of minder (subpixel) voor en na staan als er niets op volgt, iets met Cleartype denk ik, of stiekem wordt misschien toch niet gewoon GDI gebruikt. Karakters staan tegenwoordig niet meer op zichzelf, als je dat niet wil, dan vrees ik toch dat je GDI zonder + nodig hebt, en zelf de API moet aanroepen. Enkel dan ziet het er waarschijnlijk minder mooi uit, en ik zie in je voorbeeld toch geen karakters met verschillende eigenschappen (kleur/achtergrondkleur) die op elkaar moeten aansluiten.

Het is zaak om zo min mogelijk aanroepen naar DrawText te doen, dus je moet opeenvolgende karakters met dezelfde eigenschappen combineren, en als het kan ook meerdere regels (met Environment.NewLine ertussen). En verder alleen de stukken vernieuwen waar nodig.

Vergeet daarnaast niet om Me.SetStyle(ControlStyles.Opaque, True) te doen.
offtopic:
Ik moest even kijken of dit draadje op 1 april gestart was of niet... :p

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Ik heb zelf ook een app gemaakt die nogal veel tekent, in C#. Zal met wat vertaalwerk ook wel werken in VB. Mijn succesformule (véél sneller dan 'gewoon' double buffered tekenen en véél² sneller dan direct naar het scherm tekenen):


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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
namespace myApp
{
    class myFastDrawingControl : UserControl
    {
        // Buffer
        private BufferedGraphicsContext context;
        private BufferedGraphics grafx;
        private Graphics offScreenGraphics;

        public myFastDrawingControl()
        {
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
            context = BufferedGraphicsManager.Current;
        }

        // Override the onpaint event to render the bufferedgraphics object to screen
        protected override void OnPaint(PaintEventArgs e)
        {
            grafx.Render(e.Graphics);
        }

        // Prepare to draw on entire control using an offscreen graphics object
        private void drawCanvas()
        {
                context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
                if (grafx != null)
                {
                    grafx.Dispose();
                    grafx = null;
                }
                grafx = context.Allocate(this.CreateGraphics(), new Rectangle(0, 0, this.Width, this.Height));
                grafx.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
                grafx.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
                grafx.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
                offScreenGraphics = grafx.Graphics;
        }

        private void drawSomething()
        {
                // Example: fill entire surface with control background color
                offScreenGraphics.Clear(this.BackColor);

                // Example: draw a rectangle
                offScreenGraphics.DrawRectangle(...);

                // Now tell Windows that this control needs a Paint
                this.Invalidate();
        }
   }
}


Merk op dat je ook een beperkte region kunt repainten met this.Invalidate(myRegion). Ook kun je het onPaintBackground event nog blokkeren, maar dat geeft problemen met onvolledige repaints. Verder kun je nog sneller gaan door de pixels van offscreengraphics te locken en rechtstreeks ernaartoe te blitten, maar dat was in mijn geval niet nodig. De andere optimalisaties die hier zijn gesuggereerd lijken me trouwens ook belangrijk.

TabCinema : NiftySplit

Pagina: 1