-
Nov 5th, 2022, 07:55 PM
#1
Thread Starter
PowerPoster
Double buffering
Hi,
Can anyone please recommend a dummies guide to double buffering ?
Better still, can anyone please produce such a guide ?
I have found several explanations of how to do it for one single item, but I have multiple actions in several different subroutines within a form, they all cause some degree of flicker, some of which are quite major.
Poppa
Along with the sunshine there has to be a little rain sometime.
-
Nov 5th, 2022, 08:48 PM
#2
Re: Double buffering
What are you actually doing? The simplest solution is to just draw on a PictureBox control, which is already double-buffered. This may not be possible in your situation but how would we know. Alternatively, it may be as simple as setting the DoubleBuffered property of your form to True. Again, if we don't know what you're doing, we can't tell you how to do it.
-
Nov 6th, 2022, 08:06 AM
#3
Thread Starter
PowerPoster
Re: Double buffering
Thanks John,
The application is a game, the whole form has a TLP, dock = full, containing a large PictureBox, 3 buttons, (Sound on/off, exit and next game), 7 labels, and another TLP with 26 PictureBoxes.
When the 'Next game' button is clicked the large P'box image is changed, 4 of the labels change text, one of which changes letter by letter, and all 26 P'boxes in TLP2 show.
All of which causes a great deal of flicker.
This all relates to Form2. The DoubleBuffered property of Form2 is True, the command being in the Load subroutine of Form2, but that's the only command in this respect. Adding it made no difference which was what prompted this thread.
Poppa
Along with the sunshine there has to be a little rain sometime.
-
Nov 6th, 2022, 09:13 AM
#4
Re: Double buffering
Double buffering just means that everything draws to an image offscreen, then, once drawing is done, it gets swapped to the screen. Therefore, it won't work if there is too much going on. When talking about a large PB image, and lots of PB, then it may be that you simply can't do the refresh in time. It takes longer than a single screen refresh for the new image to be swapped in. This will cause problems with or without double buffering.
I have a program that could have...well, it could have LOTS of PB, but that didn't matter so long as they were small. Once they got pretty big (I think I saw this at 256x512 pixels), there couldn't be all that many on the screen, but there would be a stutter. A bit of profiling showed that I could redraw one PB in 9ms at the smallest size, but it took 27ms at the largest size, so a few of them took noticeable time to draw. If the screen is drawing at 60 Hz, then it is refreshing every 17ms. This was problematic, because I could only redraw each PB once every other refresh. You may be encountering the same issue. It simply might not be possible for you to get smooth performance with that number of controls...unless you approach it a different way.
The first thing I tried was caching the images. It was composing the image that took all the time, and they rarely changed, so I could cache the existing image and only update those that changed. This was necessary because the user could zoom in or zoom out, which meant that all the images would have to be redrawn, and caching meant that I didn't have to recalculate the images. This worked, until I realized that I'd also need to redraw EVERYTHING at times, and that was pretty costly.
The next thing I tried was moving to MonoGame. Your typical WinForms control doesn't make use of the GPU for drawing. MonoGame uses the GPU. It's a different thing to learn, though not utterly new. It's really focused on a game loop, which doesn't make sense for most WinForms applications, and didn't make sense for mine, but you can also use it to draw controls. When I switched to MonoGame, I was able to get rid of the caching, because I could re-draw all the PB every cycle. I do have a bit of tearing, at times, which happens when the screen can't redraw ALL the controls in one refresh cycle, so it redraws part, then the rest on the next cycle (17ms later, for the most part). There is a simple switch to avoid the tearing by aligning the refresh to the vertical sync signal, but, while I found that to avoid the tearing, I also found that it was less responsive for what I was doing. I felt the tearing was better than the pause.
So, you might have a situation where you simply can't do the redraw you want fast enough. I found that setting the background image of my largest PB was the slowest part of the drawing, so your large PB may be simply too difficult to swap in the image in a single refresh cycle, in which case double buffering might not do a thing for you. Caching may or may not help you, but if not, then you'd have to look at something that is simply faster, and MonoGame would be that thing...though WPF probably would do, as well.
My usual boring signature: Nothing
-
Nov 6th, 2022, 08:01 PM
#5
Thread Starter
PowerPoster
Re: Double buffering
Thanks Shaggy,
That's very interesting, I'm going to try making the text which is currently made letter by letter in the label as a separate string and just do the one change.
The 'large' Pbox is 322,474px and is just a normal PhotoBox1.Image = Image(x), each of the 26 small ones are 24,26px, they don't change their image, just changed to visible if they were hidden during the game, via a For - To loop.
I've since found that the form can be double buffered by default at design, so I've done that (didn't make any noticeable difference)
My project is Winforms, but sadly I have no idea what a MonoGame is, I shall have to search for that too.
Poppa
Along with the sunshine there has to be a little rain sometime.
-
Nov 6th, 2022, 08:21 PM
#6
Re: Double buffering
MonoGame is essentially a library. It is written in C#, so most examples are in C#, but it works just fine in VB.
My usual boring signature: Nothing
-
Nov 7th, 2022, 08:37 AM
#7
Thread Starter
PowerPoster
Re: Double buffering
I wonder if my problem is actually called flicker.
I've made a short video of the problem maybe you could either confirm that it's flicker or correctly identify the problem.
Open the video here.
Poppa
PS
Hmmm... I tried to open this, not sure it'll work !
Pop
Seems it didn't work !
Try again from here.
Pop
Last edited by Poppa Mintin; Nov 7th, 2022 at 10:26 AM.
Reason: PS added
Along with the sunshine there has to be a little rain sometime.
-
Nov 7th, 2022, 09:56 AM
#8
Re: Double buffering
It won't. It appears to be on your OneDrive, which isn't accessible to anybody else without a password.
Flicker generally happens because a control is cleared before it is redrawn. Normally, this happens within one refresh, so the fact that the control area is set to all white is never seen, since the all white is set to the new image before the graphics card gets to drawing the white to the screen. However, if it takes too long to compose the new image, then the white is still there when the screen refreshes. The result is a flash, as the control area goes white for a fraction of a second, then gets the new image. You generally don't really SEE the white, because the image isn't on the screen long enough to really register on your eyes. The result is a flicker, or flash. It isn't a smooth change, though you don't generally really see why not.
What Double Buffering is really doing is just composing to some back buffer, then swapping that in when it is ready. Usually, this is done by creating two areas of memory to hold the display image, and just swapping which one is being used for the display. You see the last one until the new one is fully ready, then the address used for the display image switches from the old memory to the new memory. That should take essentially no time at all, since all that is happening is that a single address is being changed. Whether or not that's how WinForms handles double buffering, I don't know.
My usual boring signature: Nothing
-
Nov 7th, 2022, 10:31 AM
#9
Thread Starter
PowerPoster
Re: Double buffering
I've tried a new link, hopefully it doesn't require a password...
It is here.
Poppa
Last edited by Poppa Mintin; Nov 7th, 2022 at 11:00 AM.
Along with the sunshine there has to be a little rain sometime.
-
Nov 7th, 2022, 10:33 AM
#10
Thread Starter
PowerPoster
Re: Double buffering
Thanks Shaggy,
That certainly sounds like my problem.
Pop
Along with the sunshine there has to be a little rain sometime.
-
Nov 7th, 2022, 11:09 AM
#11
Re: Double buffering
Looking at your vid i guess you mean the "flickers" @0:25 and 0:29, these are not intentional, right?
your problem is not double buffering in this case. something in your code triggers multiple redraws. try debuging this so that each item is drawn only once. if you use multiple picboxes (and your example game is something that should use just one picbox and multiple draws into), then resize/refresh/position them only once.
single step through your code or add debug outs so that you can see what is going on.
-
Nov 7th, 2022, 11:36 AM
#12
Re: Double buffering
Poppa, why don't you draw everything into a single bitmap at it's desired location, then display it in one picture box. This will eliminate the flickering.
You then keep a list of where every letter is located within the bitmap, and track the mouse's location (PictureBox1_MouseMove) as it hovers over the picture box. The mouse click event has to be within the bounds of the letter's location and size. After you click, redraw the bitmap with everything except for the letter you clicked on.
Last edited by Peter Porter; Nov 7th, 2022 at 01:05 PM.
-
Nov 7th, 2022, 12:12 PM
#13
Re: Double buffering
I agree with that approach. Composing a more complex image is a good way to simplify things. Back when I was starting with the project that I have all the drawing in, I started with the idea of a PB that had eight or nine PB in it. This became a bad idea so fast that I have forgotten just how far I took that design concept. In theory, it was easy, since every PB was already set up to have click events and the like. In practice, the behavior was horrible. Once you get to a large enough level of controls on a form, performance degrades badly no matter what you do. Therefore, the fewer controls the better.
With images in PB, composing the image and tracking where different elements in the image are located, will have superior performance to multiple controls. This will be especially true if every element you care about is a rectangle, since figuring out whether or not a point is in a rectangle is a super easy problem to solve. In fact, it is already solved, because the Rectangle can tell you whether or not a point is inside it or not.
My usual boring signature: Nothing
-
Nov 7th, 2022, 07:42 PM
#14
Thread Starter
PowerPoster
Re: Double buffering
Thanks Peter, Shaggy,
I like that idea but how would it cope with different size / resolution screens ?
I've already played with a third form to display whilst I change the active screen behind it, but couldn't get it to work properly.
I guess I could...
Draw the bitmap as you describe.
Draw exactly the same layout as a background image, but with an individual colour for each clickable rectangle.
Then just read the background colour to identify the rectangle.
Wad'ya reckon? would that be feasible?
255³ possible locations (Smiles... I'd only actually need about 30 active locations), no need to track anything, screen size and resolution resolved.
I'm wondering how I'd "redraw the bitmap with everything except for the letter I clicked" and also either add a letter to the 'Found letters' string or change to the next 'failed letter' image.
Rather like this: -
Poppa
Last edited by Poppa Mintin; Nov 7th, 2022 at 07:50 PM.
Reason: Two images added.
Along with the sunshine there has to be a little rain sometime.
-
Nov 8th, 2022, 12:21 PM
#15
Re: Double buffering
Poppa, I thought of a less complicated way for ya. A custom RichTextBox for the alphabet. When a letter is clicked, it makes it's color match the forms backcolor or color of it's background image, giving the illusion it disappeared. That's if you paint the gallow on a double buffered form instead of a picturebox.
The below code handles the mouse down events. The code for a Transparent RichTextBox, which also prevents highlighting and hides the caret, is in my next post.
Code:
'double buffers all the controls on the form
'you have to refresh Form1 if you resize it, to prevent any visual funkiness
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
CreateParams = MyBase.CreateParams
CreateParams.ExStyle = CreateParams.ExStyle Or &H2000000
Return CreateParams
End Get
End Property
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'for testing
Me.BackColor = Color.White
'needs to be set as ReadOnly
TransparentRichTextBox1.ReadOnly = True
TransparentRichTextBox1.Cursor = Cursors.Hand
'adds the alphabet, font style, and centers it
TransparentRichTextBox1.Font = New Font("Papyrus", 14, FontStyle.Bold)
TransparentRichTextBox1.SelectionAlignment = HorizontalAlignment.Center
TransparentRichTextBox1.Text = "A B C D E F G H I J K L M "
TransparentRichTextBox1.AppendText(Environment.NewLine & "N O P Q R S T U V W X Y Z ")
TransparentRichTextBox1.AppendText(Environment.NewLine & "")
TransparentRichTextBox1.ScrollBars = RichTextBoxScrollBars.None
End Sub
Private Sub TransparentRichTextBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles TransparentRichTextBox1.MouseDown
'get character position
Dim SelectedCharPosition As Integer
SelectedCharPosition = TransparentRichTextBox1.GetCharIndexFromPosition(New Point(e.X, e.Y))
'select character
TransparentRichTextBox1.Select(SelectedCharPosition, 1)
'check for spaces to be ignored
If TransparentRichTextBox1.SelectedText = " " Then
'to fix character's right side overflow problem to the next indexed character (space)
'sets the selected index back one and selects the character there
SelectedCharPosition = SelectedCharPosition - 1
TransparentRichTextBox1.Select(SelectedCharPosition, 1)
'if the new selected character is another space, nothing happens
If TransparentRichTextBox1.SelectedText = " " Then
'nothing happens
Else
If TdBrichtextbox1.SelectionColor = Color.White Then
'the selected character will be ignore
Else
'if the index is not a space, get the character
TextBox1.Text = TdBrichtextbox1.SelectedText
'changes the character's color to hide it
TdBrichtextbox1.SelectionColor = Color.White
End If
End If
Else
If TdBrichtextbox1.SelectionColor = Color.White Then
'the selected character will be ignore
Else
'if the cursor wasn't on a space to begin with, get the character
TextBox1.Text = TdBrichtextbox1.SelectedText
'changes the character's color to hide it
TdBrichtextbox1.SelectionColor = Color.White
End If
End If
End Sub
'below refreshes Form1 to prevent any funkiness from double buffering
Private Sub Form1_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SizeChanged
Me.Refresh()
End Sub
With about all of the characters in a regular RichTextBox, no matter what font, if the cursor is hovering over the right side of them when clicking, it will be detected as the next character to the right of it which is a blank space. The above code fixes this overflow problem.
Selected characters can't be selected again if their color is white, or whatever color you choose to hide them with.
Last edited by Peter Porter; Nov 17th, 2022 at 06:34 AM.
-
Nov 9th, 2022, 01:51 PM
#16
Re: Double buffering
The below code for a custom transparent RichTextBox, prevents highlighting, and also hides the caret.
Code:
Option Strict On
Public Class TransparentRichTextBox
Inherits RichTextBox
Public Sub InitializeComponent()
End Sub
'makes this control transparent to see the forms background image
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
Dim CP As CreateParams = MyBase.CreateParams
CP.ExStyle = CP.ExStyle Or &H20
Return CP
End Get
End Property
'prevents highlighting, which also hides the caret
Public Sub New()
Selectable = False
End Sub
Const WM_SETFOCUS As Integer = &H7
Const WM_KILLFOCUS As Integer = &H8
Public Property Selectable As Boolean
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_SETFOCUS AndAlso Not Selectable Then m.Msg = WM_KILLFOCUS
MyBase.WndProc(m)
End Sub
End Class
-
Nov 13th, 2022, 06:13 AM
#17
Hyperactive Member
Re: Double buffering
Well, Hello all. Since this thread isn't closed, I encountered this code to provide "Set all imported user controls double-buffered: TRUE automatically." The code is in C# but online VB.NET interpreters will not return a valid/operational code. Can someone please give me a hand on this? I want to achieve a matrix or UserControls with lowest screen flickering... the video which contains following code
Code:
Public Form1()
{
InitializeComponent();
}
Protected Override CreateParams createparams
{
Get
{
createparams handleparam = base.createparams;
handleparam.exstyle |= 0x02000000;
return handleparam;
}
}
-
Nov 13th, 2022, 11:18 AM
#18
Re: Double buffering
Here you go:
Code:
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
CreateParams = MyBase.CreateParams
CreateParams.exstyle = CreateParams.exstyle Or &H2000000
Return CreateParams
End Get
End Property
Amazing that placing this in Form1's code that it solved the RichTextBox flickering!
Poppa, you can probably keep all your picture boxes with this, but if you want to simplify everything with just one control, use my RichTextBox code.
Last edited by Peter Porter; Nov 13th, 2022 at 11:52 AM.
-
Nov 14th, 2022, 01:20 AM
#19
Hyperactive Member
Re: Double buffering
Thanks immensely. It was literally the code translation. You were added this in top of the code you replied in #15 I added it to first of the code right after class definition but it doesn't work mate. Can you hint me a clue that what it might be? Something bypassed? Cross-threading?
I am using following line in a loop to create a row per column UserControl array. There are a lot of them. According to the video it should be created so fast but it doesn't. What are my opttions?
Code:
Private Sub CREATEMATRIX()
TableLayoutPanel1.Controls.Clear()
For i = 0 To RowNumeric.Value - 1
For j = 0 To ColumnNumeric.Value - 1
TableLayoutPanel1.Controls.Add(New UserControl1 With {.Margin = New Padding(0, 0, 0, 0), .Dock = DockStyle.Fill}, j, i)
Next
Next
End Sub
Last edited by pourkascheff; Nov 15th, 2022 at 07:41 AM.
-
Nov 15th, 2022, 07:47 AM
#20
Hyperactive Member
Re: Double buffering
Anyone?
I even tried this:
Get/Set property of UserControl1.DoubleBuffered public
Code:
Public Property UCDoubleBuffered As Boolean
Get
Return Me.DoubleBuffered
End Get
Set
Me.DoubleBuffered = Value
End Set
End Property
Then, while creating them, set them true:
Code:
TableLayoutPanel1.Controls.Clear()
For i = 0 To RowNumeric.Value - 1
For j = 0 To ColumnNumeric.Value - 1
TableLayoutPanel1.Controls.Add(New TankProperty With {.UCDoubleBuffered = True, .Margin = New Padding(0, 0, 0, 0), .Dock = DockStyle.Fill, .CellXYText = i.ToString & "/" & j.ToString}, j, i)
Next
Next
But nothing special will happen. It is still slow... :-(
-
Nov 16th, 2022, 07:31 PM
#21
Thread Starter
PowerPoster
Re: Double buffering
Originally Posted by Peter Porter
Here you go:
Code:
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
CreateParams = MyBase.CreateParams
CreateParams.exstyle = CreateParams.exstyle Or &H2000000
Return CreateParams
End Get
End Property
Amazing that placing this in Form1's code that it solved the RichTextBox flickering!
Poppa, you can probably keep all your picture boxes with this, but if you want to simplify everything with just one control, use my RichTextBox code.
Thanks Peter,
Where do you propose I use a RichTextBox ?
I don't even have an ordinary TextBox on the relevant form.
I went back to trying to cover the flicker by hiding it behind a third (TopMost = True) form, which has worked to a reasonable extent, whilst there is still some flicker it's almost unnoticeable.
Poppa
Along with the sunshine there has to be a little rain sometime.
-
Nov 17th, 2022, 06:23 AM
#22
Re: Double buffering
Originally Posted by Poppa Mintin
Thanks Peter,
Where do you propose I use a RichTextBox ?
I don't even have an ordinary TextBox on the relevant form.
I went back to trying to cover the flicker by hiding it behind a third (TopMost = True) form, which has worked to a reasonable extent, whilst there is still some flicker it's almost unnoticeable.
Poppa
If you paint the gallow to the form itself instead of a PictureBox, you can place a single transparent RichTextBox for the entire alphabet anywhere over the form, eliminating the 26 PictureBoxes your game currently uses. The double buffer code above in Form1 will prevent the transparent RichTextBox from flickering.
Last edited by Peter Porter; Nov 17th, 2022 at 06:30 AM.
-
Nov 17th, 2022, 06:26 AM
#23
Re: Double buffering
Originally Posted by pourkascheff
Thanks immensely. It was literally the code translation. You were added this in top of the code you replied in #15 I added it to first of the code right after class definition but it doesn't work mate. Can you hint me a clue that what it might be? Something bypassed? Cross-threading?
I am using following line in a loop to create a row per column UserControl array. There are a lot of them. According to the video it should be created so fast but it doesn't. What are my opttions?
Code:
Private Sub CREATEMATRIX()
TableLayoutPanel1.Controls.Clear()
For i = 0 To RowNumeric.Value - 1
For j = 0 To ColumnNumeric.Value - 1
TableLayoutPanel1.Controls.Add(New UserControl1 With {.Margin = New Padding(0, 0, 0, 0), .Dock = DockStyle.Fill}, j, i)
Next
Next
End Sub
pourkascheff, where did you place the double buffer code?
If you place it in Form1, it will double buffer all controls on it.
-
Nov 17th, 2022, 09:18 AM
#24
Re: Double buffering
FWIW
Code:
''' <summary>
''' Sets a controls DoubleBuffered property
''' </summary>
''' <param name="theControl">which control</param>
''' <param name="setting">true or false (on / off)</param>
''' <remarks>DoubleBuffered is not visible in the designer for most controls</remarks>
Public Shared Sub SetControlDoubleBuffered(theControl As Windows.Forms.Control,
Optional setting As Boolean = True)
Try
Dim typ As Type = theControl.GetType
Dim pi As Reflection.PropertyInfo = typ.GetProperty("DoubleBuffered",
Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
pi.SetValue(theControl, setting, Nothing)
Catch ex As Exception
'don't care
End Try
End Sub
-
Nov 18th, 2022, 06:36 AM
#25
Hyperactive Member
Re: Double buffering
Originally Posted by Peter Porter
pourkascheff, where did you place the double buffer code?
Form1, before all, right after Class definition. I'm suspected to two things. 1) The UserControls themselves which are containing at least 20 controls. 2) The way I'm adding lots of them programmatically in a bad way.
Originally Posted by dbasnett
FWIW
Dear dbasnett! long time no see sir. Not sure what did you mean by possible initialism of "for what it's worth". you were talking to peter or me? I'm away from project solution but the code seems it should be given a try ASAP!
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|