Results 1 to 20 of 20

Thread: Speed up FlexGrid auto-selection

  1. #1

    Thread Starter
    Member
    Join Date
    Dec 2017
    Posts
    32

    Speed up FlexGrid auto-selection

    I couldn't think of an appropriate title for this thread, so here's the situation:

    I have a special use-case for a FlexGrid control and I currently use Krool's excellent VBFlexGrid.
    I'm writing a mod-tracker like app, in which the user enters musical notes into the grid using the keyboard.
    All notes,volume and instrument values are stored into an array, in a form that is understood by the sound library I'm using (FMOD). Everything works wonderfully at "design-time" but there are performance issues at "play-time"

    There is a loop, where data from the array is fed to the sound library in a row-by-row fashion and an artificial delay is introduced, using the QueryPerformanceCounter. Up to this point, everything works surprisingly well, with no significant lag even at high BPMs.
    It's the visual representation that I'm having issues with. I set the Row property of the control to the loop's counter and the CellEnsureVisible property to FlexVisibilityCompleteOnly. That ensures the current playing row is always visible.

    My current workaround is to put those properties out of the loop and change them in a regular Timer event, with a bigger interval than the one of the "playing" interval. That works quite well but I'm sure there are better ways to do it.
    I made a simple test with 2000 rows and a delay of 1ms. It takes ~2 sec without row selecting and ~6 sec with row selecting.

    Any ideas?

    For anyone not familiar with a tracker here's a wip shot.
    Attached Images Attached Images  

  2. #2
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Speed up FlexGrid auto-selection

    mod trackers... that's how i learned music.
    Currently I use renoise.

    my only suggestion (which is not helpful) is to not use a grid control, and draw everything yourself.

    have you tried using virtual mode? you only provide the values if they're visible. you don't load them all at once.
    Last edited by DEXWERX; Jan 11th, 2018 at 10:08 AM.

  3. #3

    Thread Starter
    Member
    Join Date
    Dec 2017
    Posts
    32

    Re: Speed up FlexGrid auto-selection

    @DEXWERX Although I'm only familiar with the "classic" trackers I've heard that renoise is the true evolution of trackers! I'll have to try it someday.

    I'm not sure what virtual mode is. But I guess it applies when there's lots and lots of data. In my situation, playing a single 65 rows X 20 cols pattern with a couple of notes, brings the whole thing to a crawl. I don't know if I explained it well, but the grid is not databound to anything and no values are "read" from it when I hit the play button. I only need it for looks at playtime!
    Everything is read from an array and played back by the FMOD api. That part works really-really fast. By commenting out a single line:
    Code:
    PatternGrid.Row = PlayCounter
    I get perfect timing even at 600 BPM.

  4. #4
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Speed up FlexGrid auto-selection

    If you can post a sample project (without the fmod dependency?) that illustrates the issue, i bet Krool can figure out where the performance problem is.

  5. #5

    Thread Starter
    Member
    Join Date
    Dec 2017
    Posts
    32

    Re: Speed up FlexGrid auto-selection

    Thanks again DEXWERX I attached an example. I included the VBFlexGrid ocx in case someone doesn't have it handy.
    Attached Files Attached Files

  6. #6
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Speed up FlexGrid auto-selection

    interesting. that project is very fast on my machine?

  7. #7
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,412

    Re: Speed up FlexGrid auto-selection

    Looks like the VBFlexGrid control might be a bit slow at drawing text...if you don't populate your demo with any text, I notice it runs quite a bit faster.

    That said, what about a "paged" approach instead of a scroll, and only redrawing the grid intermittently? It seems to run quite a bit faster for me, but I'm not sure if it is suitable for your use case.

    Code:
    Option Explicit
    
    Dim measurement As Double
    Dim PlayState As Boolean
    
    Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long
    Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long
    Private Type LARGE_INTEGER
       lowpart As Long
       highpart As Long
    End Type
    Const TWOtt = 4294967296#   ' = 256# * 256# * 256# * 256#
    
    
    Private Sub Form_Load()
       Randomize (6)
       PlayState = False
       Label1.Caption = "Delay:" & scrollDelay.Value & " ms"
       Dim i As Integer
       For i = 1 To 500
          VBFlexGrid1.RowHeight(i) = 200
          VBFlexGrid1.Cell(FlexCellText, i, Int(20 * Rnd)) = Int(500 * Rnd)
       Next i
    End Sub
    Private Sub btnStart_Click()
       Dim d As Double
    
       Dim d1 As Double
    
       d1 = HpTimer
    
       PlayState = True
       
       Label2.Caption = "Started...": Label2.ForeColor = vbButtonText
    
       Dim cntr As Integer
    
       d = HpTimer
    
       For cntr = 1 To 500
    
          If chkMode Then
             VBFlexGrid1.Redraw = False
             VBFlexGrid1.Row = cntr
          End If
    
          Waitms scrollDelay.Value
          If PlayState = False Then Exit For
    
          If chkMode Then
             If HpTimer - d > 0.012 Then
                d = HpTimer
    
                If Not VBFlexGrid1.RowIsVisible(VBFlexGrid1.Row) Then
                   VBFlexGrid1.TopRow = VBFlexGrid1.Row
                End If
    
                VBFlexGrid1.Redraw = True
             Else
                If Not VBFlexGrid1.RowIsVisible(VBFlexGrid1.Row) Then
                   VBFlexGrid1.TopRow = VBFlexGrid1.Row
                   VBFlexGrid1.Redraw = True
                End If
             End If
          End If
       Next cntr
    
       VBFlexGrid1.Redraw = True
       VBFlexGrid1.Row = 1
       PlayState = False
       Label2.Caption = "Stopped": Label2.ForeColor = vbRed
    
       Debug.Print "Total time: " & HpTimer - d1
    
    End Sub
    Private Sub btnStop_Click()
       PlayState = False
    End Sub
    Private Sub scrollDelay_Change()
       Label1.Caption = "Delay:" & scrollDelay.Value & " ms"
    End Sub
    
    Private Sub Waitms(ByVal ms As Double)
       Dim m_CounterStart As LARGE_INTEGER
       Dim m_CounterEnd As LARGE_INTEGER
       Dim m_crFrequency As Double
       Dim PerfFrequency As LARGE_INTEGER
       Dim crStart As Double
       Dim crStop As Double
       Dim TimeElapsed As Double
       Dim d As Double
    
       QueryPerformanceFrequency PerfFrequency
       m_crFrequency = LI2Double(PerfFrequency)
       QueryPerformanceCounter m_CounterStart
    
       Do
          QueryPerformanceCounter m_CounterEnd
          crStart = LI2Double(m_CounterStart)
          crStop = LI2Double(m_CounterEnd)
          If 1000# * (crStop - crStart) / m_crFrequency >= ms Then Exit Do
    
          DoEvents
       Loop
    End Sub
    
    Private Function LI2Double(LI As LARGE_INTEGER) As Double
       Dim Low As Double
       Low = LI.lowpart
       If Low < 0 Then
          Low = Low + TWOtt
       End If
       LI2Double = LI.highpart * TWOtt + Low
    End Function
    
    Private Function HpTimer() As Double
       Dim m_CounterStart As LARGE_INTEGER
       Dim m_crFrequency As Double
       Dim PerfFrequency As LARGE_INTEGER
    
       QueryPerformanceFrequency PerfFrequency
       m_crFrequency = LI2Double(PerfFrequency)
       QueryPerformanceCounter m_CounterStart
    
       HpTimer = LI2Double(m_CounterStart) / m_crFrequency
    End Function

  8. #8

    Thread Starter
    Member
    Join Date
    Dec 2017
    Posts
    32

    Re: Speed up FlexGrid auto-selection

    You mean there's no difference running the loop with the checkbox checked/unchecked? On my main machine it's 2 sec vs 6 sec.

  9. #9
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,412

    Re: Speed up FlexGrid auto-selection

    Might have found something else - try setting the Gridlines property FlexGridLineNone...things get much faster. So maybe some inefficiencies in the line rendering code?
    Last edited by jpbro; Jan 11th, 2018 at 05:27 PM.

  10. #10

    Thread Starter
    Member
    Join Date
    Dec 2017
    Posts
    32

    Re: Speed up FlexGrid auto-selection

    Thanks for the example jpbro. I'm doing a similar thing currently but with a regular Timer control. I also expose the Timer interval to the user through a slider, so that it can be tweaked for slower machines. It's still unpredictable though and lags randomly. Disabling the grid lines though (especially if they were set to a big width) made a vast improvement.
    I wish the line control could be drawn over controls, that way I could maybe mimic the gridlines of VBFlexGrid.

  11. #11
    PowerPoster
    Join Date
    Jun 2012
    Posts
    2,373

    Re: Speed up FlexGrid auto-selection

    I will check if I can make an improvement in the gridlines.
    It uses MoveToEx and LineTo API.
    Currently it resets after LineTo to old position via MoveToEx. Maybe that step can only be done once after all cells are drawn. Will check..

    EDIT: I could not measure any improvement by that. So..
    Last edited by Krool; Jan 12th, 2018 at 05:47 AM.

  12. #12
    PowerPoster
    Join Date
    Jun 2012
    Posts
    2,373

    Re: Speed up FlexGrid auto-selection

    The performance drawback comes that in your artificial waitms procedure you make a DoEvents and thus forcing the VBFlexGrid to make a full draw. And I think that is what you expect because you want to see it scrolling and selecting, right?

    EDIT:

    In your waitms procedure you make the following:
    Code:
        Do
            QueryPerformanceCounter m_CounterEnd
            crStart = LI2Double(m_CounterStart)
            crStop = LI2Double(m_CounterEnd)
            If 1000# * (crStop - crStart) / m_crFrequency >= ms Then Exit Do
            DoEvents
        Loop
    So DoEvents is called multiple times, but it would be sufficient to make only once when the time has elapsed, like:
    Code:
        Do
            QueryPerformanceCounter m_CounterEnd
            crStart = LI2Double(m_CounterStart)
            crStop = LI2Double(m_CounterEnd)
            If 1000# * (crStop - crStart) / m_crFrequency >= ms Then DoEvents: Exit Do
        Loop
    Last edited by Krool; Jan 12th, 2018 at 06:14 AM.

  13. #13

    Thread Starter
    Member
    Join Date
    Dec 2017
    Posts
    32

    Re: Speed up FlexGrid auto-selection

    Thanks a lot Krool! I really appreciate that you took time to look into this. It's an excellent control for the purpose it was made and in fact I compared it to MSFlexGrid in the same test and it actually performed a bit better. Not to mention the fact that the selection rectangle on the MSFlexGrid was flickering like crazy!

    EDIT: Now I saw your second reply. Thank you very much, I'll try your suggestion when I get back home.
    Last edited by immortalx; Jan 12th, 2018 at 06:24 AM.

  14. #14
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,412

    Re: Speed up FlexGrid auto-selection

    Before I submitted my suggestions, I tried limiting the number of DoEvents calls as suggested but there was little to no improvement. Maybe asking the control to redraw every millisecond is just asking too much? It is somewhat surprising that the grid line thickness has such a dramatic effect over the drawing time though.

  15. #15
    PowerPoster ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    3,040

    Re: Speed up FlexGrid auto-selection

    Hi,

    why not show a Progressbar in a cell for Playtime = 0%-100%

    Code:
    Option Explicit
    
    
    
    Private Sub Form_Load()
     Dim i As Long, Records As Long
       
       Picture1.BackColor = &HC0FFFF 'for Percent Cell
       Picture1.Visible = False
          
          Records = 100
          With MSFlexGrid1
             .Rows = Records + 4 + 1
             .Cols = 8
             .FixedCols = 0
             
             .RowHeight(0) = .RowHeight(0) * 3
             
             .FillStyle = flexFillRepeat
             For i = 4 To .Rows - 1 Step 4
                .Row = i
                .col = 0
                .ColSel = .Cols - 1
                .RowHeight(i) = 3 * Screen.TwipsPerPixelY
                .CellBackColor = vbYellow
             Next
             .FillStyle = flexFillSingle
          End With
    End Sub
    Private Sub Command1_Click()
    
          With MSFlexGrid1
             .Row = 15
           '  .TopRow = .Row
             .col = 1
             .LeftCol = .col
             Picture1.Width = .CellWidth
             Picture1.Height = .CellHeight
          End With
          
          Timer1.Interval = 100
          Timer1.Enabled = True
    End Sub
    
    
    Public Function ShowPercent(PicBox As Control, ByVal Percent As Variant)
     
       Dim Wert As String
       Dim p As Long
       
          p = Int(CLng(Percent))
          PicBox.AutoRedraw = True
          PicBox.Cls 'clear
          PicBox.ScaleWidth = 100
          PicBox.DrawMode = 14
          Wert = Format(p, "###") & " % Played"
          PicBox.CurrentX = 50 - PicBox.TextWidth(Wert) / 2
          PicBox.CurrentY = (PicBox.ScaleHeight - PicBox.TextHeight(Wert)) / 2
          PicBox.Print Wert
          PicBox.Line (-2, -2)-(Int(p), PicBox.ScaleHeight), vbBlue, BF
          PicBox.Refresh
    End Function
    Private Sub Timer1_Timer()
    
       Static z As Long
       
          z = z + 1
          If z > 100 Then
             Timer1.Enabled = False
             Set MSFlexGrid1.CellPicture = Nothing
             z = 0
             Exit Sub
          End If
          
          ShowPercent Picture1, z
          
          With MSFlexGrid1
             Set .CellPicture = Picture1.Image
          End With
          
    End Sub
    regards
    Chris
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  16. #16
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Speed up FlexGrid auto-selection

    As others have suggested already, the Refresh-Frequency of the Grid has to be reduced.
    Drastically.

    Why should one force some complex Control to refresh itself with "1000 FPS" (like in a "Game-Engine on steroids"),
    when the Screen-Refresh-Rate of a typical LCD-Screen is only 50-70Hz?

    Let's say the LCD refreshes with 50Hz (for easier calculation-ratios).

    In that case the Graphics-Driver will have refreshed the Display only 100 times in the desired 2-seconds timespan.
    So from those 2000-Repainting-enforcements into Krools OffScreen-Bitmap - only 5% were really necessary (were producing a "visual effect" on the screen).

    For a "fluent viewing" from the users end, even 25Hz are entirely sufficient (your typical movie runs with 24hz on the "silverscreen").

    That said, let's adapt "the loop" approriately (with only a few small changes from the posted original):
    Code:
    Private Sub btnStart_Click()
    PlayState = True
    Label2.Caption = "Started...": Label2.ForeColor = vbButtonText
    Dim cntr As Integer, RefrCount As Long
    For cntr = 1 To 2000
        If chkMode.Value = 1 And cntr Mod 40 = 0 Then
          VBFlexGrid1.Row = cntr
          VBFlexGrid1.CellEnsureVisible FlexVisibilityCompleteOnly
          RefrCount = RefrCount + 1 'just a RefreshCounter
        End If
        Waitms scrollDelay.Value
        If PlayState = False Then Exit For
    Next cntr
    VBFlexGrid1.Row = 1
    VBFlexGrid1.CellEnsureVisible FlexVisibilityCompleteOnly 'not sure if that's wanted - but let's reflect the new RowCursor-Position also on loop-exit
    Caption = RefrCount 'reflect, how often we've updated the Grid within the two seconds
    
    PlayState = False
    Label2.Caption = "Stopped": Label2.ForeColor = vbRed
    End Sub
    With that, the code runs only marginally slower on my machine, compared to "unchecked" mode.

    HTH

    Olaf

  17. #17

    Thread Starter
    Member
    Join Date
    Dec 2017
    Posts
    32

    Re: Speed up FlexGrid auto-selection

    @ChrisE thank you for the example. Although it doesn't scroll the grid (which is a must unfortunately) it'll be needed elsewhere.

    @Schmidt Thank you, that seems like a very elegant solution. I was currently using a Timer control (with a greater interval) to refresh the grid separately from the main loop, but your solution kills 2 birds with one stone and it seems to produce more stable results.
    The other nice thing is i can set the mod divisor to a variable depending on the BPM.

    Again thank you all.

  18. #18
    Fanatic Member
    Join Date
    Jul 2007
    Location
    Essex, UK.
    Posts
    578

    Re: Speed up FlexGrid auto-selection

    Quote Originally Posted by Schmidt View Post
    As others have suggested already, the Refresh-Frequency of the Grid has to be reduced.
    Drastically.

    Why should one force some complex Control to refresh itself with "1000 FPS" (like in a "Game-Engine on steroids"),
    when the Screen-Refresh-Rate of a typical LCD-Screen is only 50-70Hz?

    Let's say the LCD refreshes with 50Hz (for easier calculation-ratios).

    In that case the Graphics-Driver will have refreshed the Display only 100 times in the desired 2-seconds timespan.
    So from those 2000-Repainting-enforcements into Krools OffScreen-Bitmap - only 5% were really necessary (were producing a "visual effect" on the screen).

    For a "fluent viewing" from the users end, even 25Hz are entirely sufficient (your typical movie runs with 24hz on the "silverscreen").

    That said, let's adapt "the loop" approriately (with only a few small changes from the posted original):
    Code:
    Private Sub btnStart_Click()
    PlayState = True
    Label2.Caption = "Started...": Label2.ForeColor = vbButtonText
    Dim cntr As Integer, RefrCount As Long
    For cntr = 1 To 2000
        If chkMode.Value = 1 And cntr Mod 40 = 0 Then
          VBFlexGrid1.Row = cntr
          VBFlexGrid1.CellEnsureVisible FlexVisibilityCompleteOnly
          RefrCount = RefrCount + 1 'just a RefreshCounter
        End If
        Waitms scrollDelay.Value
        If PlayState = False Then Exit For
    Next cntr
    VBFlexGrid1.Row = 1
    VBFlexGrid1.CellEnsureVisible FlexVisibilityCompleteOnly 'not sure if that's wanted - but let's reflect the new RowCursor-Position also on loop-exit
    Caption = RefrCount 'reflect, how often we've updated the Grid within the two seconds
    
    PlayState = False
    Label2.Caption = "Stopped": Label2.ForeColor = vbRed
    End Sub
    With that, the code runs only marginally slower on my machine, compared to "unchecked" mode.

    HTH

    Olaf
    You are forgetting that each of those 24 frames per second is shown twice (Film) or three times (Digital). So the minimum Flicker rate has always been 48Hz.

  19. #19
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Speed up FlexGrid auto-selection

    Quote Originally Posted by Steve Grant View Post
    You are forgetting that each of those 24 frames per second is shown twice (Film) or three times (Digital). So the minimum Flicker rate has always been 48Hz.
    If your Grid-Update-frequency is 25 Hz - and your TFT-Screen refreshes itself with 75Hz, then each "Grid-Frame is shown 3 times as well.

    Olaf

  20. #20
    Fanatic Member
    Join Date
    Jul 2007
    Location
    Essex, UK.
    Posts
    578

    Re: Speed up FlexGrid auto-selection

    Ooh! Good point.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width