State of manual double-buffer implementation ?

TeeChart for Microsoft Visual Studio .NET, Xamarin Studio (Android, iOS & Forms) & Monodevelop.
Post Reply
stefans
Newbie
Newbie
Posts: 14
Joined: Mon Jul 09, 2007 12:00 am

State of manual double-buffer implementation ?

Post by stefans » Mon Sep 24, 2007 7:31 am

A few months ago there was a discussion in this forum concerning the
useless chart updates of teechart.net when a redraw of background is required.
this could be solved (i think easily) with a double-buffer implementation.

see subject
http://www.teechart.net/support/viewtop ... ublebuffer
Narcís wrote:I'm afraid there's anything else that can be done here for now. As I posted before, we will try implementing manual DoubleBuffer support to solve this problem.
if the gui is complex ( e.g. multiple panels, windows ) there is a lot of redraw needed because of window movements. The whole application looks sluggish because of this.
This is not acceptable for us. We hope that steema could implement this feature before we need to ship our product. Otherwise we have to implement our own double-buffering.

Can you please tell me what the current state of your implementation is, and give further informations about your sheduling.

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Post by Christopher » Tue Sep 25, 2007 9:49 am

Hello Stefans,

I've created a simple mock up of the buffering situation in teechart.net which you can download here:
http://www.steema.net/~chris/BufferedControls.zip

The AutoBufferedControl class has exactly the same ControlStyles as teechart.net, whereas the ManualBufferedControl implements classes which give manual control over the buffer, as per the articles here:
http://blogs.msdn.com/jeremykuhne/archi ... 62344.aspx
http://msdn2.microsoft.com/en-us/librar ... S.80).aspx

You might like to run the test form and then open an instance of notepad, say, and drag it over the two controls. You will see that both controls repaint every time another window is moved over them (there is a slight difference in that the ManualBufferedControl seems to update instantaneously). The question is: how can the buffer of the ManualBufferedControl be manipulated so that it doesn't repaint when another window is dragged over it? I've been unable to find an easy answer to this question and you might like to have a look at the issue yourself and make some suggestions.
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

stefans
Newbie
Newbie
Posts: 14
Joined: Mon Jul 09, 2007 12:00 am

Post by stefans » Tue Sep 25, 2007 11:15 am

Hello Christopher,

My first impression is:
These controls only prevent flickering when painting is
slow (in OnPaint).
The solution for teechart would be to paint from a back-buffer in case of a simple background refresh. i have seen such implementations but don't have time this week to make a more detailed reseach.

I have time next week and give you a more detailed feedback.

Regards,
Stefan

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Post by Christopher » Wed Sep 26, 2007 6:55 am

Stefan,
stefans wrote: I have time next week and give you a more detailed feedback.
That would be very kind of you, thank you. I'll be looking forward to seeing the implementation!
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

stefans
Newbie
Newbie
Posts: 14
Joined: Mon Jul 09, 2007 12:00 am

Post by stefans » Wed Sep 26, 2007 7:03 am

Hi Christopher,

Anyhow i had some time to look for an double-buffering solution.
I've done a 2nd impl. of a manual double buffering control.

i have posted the file to steema.public.attachments

(after i have mailed it via sales@steema ... but maybe there was a spam filter involved :wink: )

Regards
Stefan

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Post by Christopher » Wed Sep 26, 2007 9:39 am

Stefan,
Anyhow i had some time to look for an double-buffering solution.
I've done a 2nd impl. of a manual double buffering control.

i have posted the file to steema.public.attachments

(after i have mailed it via sales@steema ... but maybe there was a spam filter involved :wink: )
Got it, thanks again!

Interesting. Your implementation certainly solves the problem of the control repainting when another window is moved over it. However, it does have its own problems:
1) If you invalidate all the controls in the invalidate button, e.g.

Code: Select all

        private void invalidateButton_Click(object sender, EventArgs e)
        {
            this.manualBufferedControl21.Invalidate();
						this.manualBufferedControl1.Invalidate();
						this.autoBufferedControl1.Invalidate();
        }
you will see that when you repeatedly click it that both the 'manual' buffered controls flicker.

2) Now try doing some more painting in all the controls by calling a drawing-intensive routine such as this one:

Code: Select all

		private void DrawRectangles(Rectangle rect, Graphics g)
		{
			Rectangle tmpRect = Rectangle.FromLTRB(0, 0, 100, 100);
			SolidBrush brush = new SolidBrush(Color.FromArgb(255, 0, 0, 0));
			Pen pen = new Pen(Color.Red, 2);
			for (int i = 0; i <= 255; i++)
			{
				g.FillRectangle(brush, tmpRect);
				g.DrawRectangle(pen, tmpRect);
				if (tmpRect.Bottom < rect.Bottom)
				{
					tmpRect.Offset(1, 1);
				}
				else
				{
					tmpRect.Offset(0, -tmpRect.Top);
				}
				brush.Color = Color.FromArgb(255, i, i, i);
			}

			tmpRect = Rectangle.FromLTRB(rect.Right - 100, 0, rect.Right, 100);

			for (int i = 255; i >= 0; i--)
			{
				g.FillRectangle(brush, tmpRect);
				g.DrawRectangle(pen, tmpRect);
				if (tmpRect.Bottom < rect.Bottom)
				{
					tmpRect.Offset(-1, 1);
				}
				else
				{
					tmpRect.Offset(0, -tmpRect.Top);
				}
				brush.Color = Color.FromArgb(255, i, i, i);
			}
		}
Here you will notice a definitive delay when you click the invalidate button, which is probably just an exaggerated version of 1) above.

As things stand, my program manager would probably not be very happy if I included such an implementation into the production code <g> ... do you have any ideas as how to resolve the issues above using 'manual' buffering?
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

stefans
Newbie
Newbie
Posts: 14
Joined: Mon Jul 09, 2007 12:00 am

Post by stefans » Wed Sep 26, 2007 11:14 am

Hi Chris,

i'm aware that this solution is not really finished, but its the correct approach.

flickering is only because the OnPaintBackround method is called before OnPaint. this method clears the background (gray) therefore you see a flickering. the automatic double buffering calls OnPaintBackround into the back-buffer. we can do this too. take a look at the following snippet.

Code: Select all

private bool _redrawNeeded = true;

		protected override void OnPaint(PaintEventArgs e)
		{
            //There is a solution needed to find out weather the Draw() method
            //needs to be called or not.
            //In this implementation i assume that this should be when
            //the control is resized ( back buffer is null ) or
            //when the control was invalidated.
            //if all drawing is done in OnPaint and all model updates are
            //commited with Invalidate() this should work.
            //there is also a solution possible to use the backbuffer only
            //in WM_ERASE_BACKGROUND messages -> see WndProc

            if (_useDoubleBuffering)
            {
                bool redrawNeeded = _redrawNeeded;

                //enshure that buffer is allocated
                if (_backBuffer == null)
                {
                    _backBuffer = _bufferedGraphicsContext.Allocate(CreateGraphics(), DisplayRectangle);
                    redrawNeeded = true;
                }

                //draw into buffer if needed.
                if (redrawNeeded)
                {
                    //First Paint Background.
                    if (!this.GetStyle(ControlStyles.Opaque))
                    {
                        this.OnPaintBackground( new PaintEventArgs(_backBuffer.Graphics, DisplayRectangle));
                    }
                    Draw(DisplayRectangle, _backBuffer.Graphics);
                    _redrawNeeded = false;
                }

                //flip buffer to screen.
                _backBuffer.Render();
            }
            else
            {
                Draw(DisplayRectangle, e.Graphics);
            }
			
		}

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            if (!_useDoubleBuffering)
            {
                base.OnPaintBackground(pevent);
            }
        }

        protected override void OnInvalidated(InvalidateEventArgs e)
        {
            _redrawNeeded = true;
            base.OnInvalidated(e);
        }

        protected override void OnResize(EventArgs e)
        {
            //dispose _backBuffer because the DisplayRectangle has changed.
            if (_backBuffer != null)
            { 
                _backBuffer.Dispose(); 
                _backBuffer = null;
            }
            base.OnResize(e);
        }

        /// <summary>
        /// This Method performs the time consuming drawing
        /// and should be called as few times as possible.
        /// </summary>
        /// <param name="rect"></param>
        /// <param name="g"></param>
		private void Draw(Rectangle rect, Graphics g)
		{
            //System.Threading.Thread.Sleep(500);
			++count;
			g.FillRectangle(Brushes.Green, rect);
			Font font = new Font(FontFamily.GenericSansSerif, 16);
			g.DrawString("ManualBufferedControl2: Times Drawed: " + count.ToString(), font, Brushes.Black, new PointF(10, 10));
            
		}
this solves the problem of flickering. but there is also another problem:
if the drawing is slowly (e.g uncomment the Sleep call in Draw method) and you resize the window, then the application isvery unrespondable.

a solution would be to reuse the current back-buffer and draw the new one in background and flip after the drawing is finished. this would be a very nice solution but is very difficult to implement ( because of threading issuses).

regards
stefan

stefans
Newbie
Newbie
Posts: 14
Joined: Mon Jul 09, 2007 12:00 am

Post by stefans » Wed Sep 26, 2007 11:24 am

hi chris,
stefans wrote: this solves the problem of flickering. but there is also another problem:
if the drawing is slowly (e.g uncomment the Sleep call in Draw method) and you resize the window, then the application isvery unrespondable.
maybe i have to tell you, that the current tee-chart implementation has the delay issue too (in case your program manager would probably not be very happy). You can do the following to check it out by yourself:

Code: Select all

void chart_AfterDraw(object sender, Steema.TeeChart.Drawing.Graphics3D g)
        {
            System.Threading.Thread.Sleep(1000);
        }
this is exaclty the reason why double buffer is better: you only have the delay in case of a invalidation caused by a model change e.g. new series added wich indirectly triggers the invalidate method ( i really hope this is the case).

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Post by Christopher » Wed Sep 26, 2007 2:22 pm

Stefans,
stefans wrote: maybe i have to tell you, that the current tee-chart implementation has the delay issue too (in case your program manager would probably not be very happy). You can do the following to check it out by yourself:
Ok, I've now implemented this code in TeeChart for testing purposes. I started out with some timing, e.g.

Code: Select all

		long start, stop;

		void tChart1_BeforeDraw(object sender, Graphics3D g)
		{
			start = DateTime.Now.Ticks;
		}
		
		void tChart1_AfterDraw(object sender, Steema.TeeChart.Drawing.Graphics3D g)
		{
			stop = DateTime.Now.Ticks;
			TimeSpan ts = TimeSpan.FromTicks(stop - start);
			this.Text = ts.TotalMilliseconds.ToString();
			System.Threading.Thread.Sleep(1000);
			stop = DateTime.Now.Ticks;
			ts = TimeSpan.FromTicks(stop - start);
			this.Text += " " + ts.TotalMilliseconds.ToString();
		}
Here I could detect little differences in the times between the new 'manual' buffering and how the code was. This is good news, in the sense that the code seems to have little to no performance impact. However, from what you told me I was expecting the Thread.Sleep to produce a noticeable visible difference between the two buffering methods, but I could detect none. On the first run of the form, neither chart showed anything before the first time appeared and and both only showed when the second time appeared. The same happened when zooming on the chart (to imitate repainting).

There is another issue with this technique. In TeeChart, we have a Panel.Transparent property. This can be emulated in the code we've been using thus:

Code: Select all

		private void Draw(Rectangle rect, Graphics g)
		{
			++count;
			if (!transparent)
			{
				g.FillRectangle(Brushes.Green, rect);
			}
			Font font = new Font(FontFamily.GenericSansSerif, 16);
			g.DrawString("ManualBufferedControl2: Times Drawed: " + count.ToString(), font, Brushes.Black, new PointF(10, 10));
			//DrawRectangles(rect, g);
		}

		private bool transparent = false;

		public bool Transparent
		{
			get { return transparent; }
			set { transparent = value; }
		}
The trouble is here that as the OnPaintBackround override is stopping this method from being called in the base class when using double-buffering then the control appears black rather than transparent. Getting the colour of teechart's containing control is not an option, I think.

I agree with you that this 'manual' buffering implementation is the way to go. Now, if I could just get over this transparency problem and have a more convincing demonstration of the advantage of the 'manual' buffer in the threading scenario, then I think we could actually convince the boss to include it into the production code <g>.
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

stefans
Newbie
Newbie
Posts: 14
Joined: Mon Jul 09, 2007 12:00 am

Post by stefans » Wed Sep 26, 2007 2:31 pm

Hi Chris,
Chris wrote:
However, from what you told me I was expecting the Thread.Sleep to produce a noticeable visible difference between the two buffering methods, but I could detect none. On the first run of the form, neither chart showed anything before the first time appeared and and both only showed when the second time appeared. The same happened when zooming on the chart (to imitate repainting).
i think i have unclear expressed. there should be no difference in normal, but if you move another window over the chart you see, that double buffering is faster -> no/short delay.
Chris wrote:
The trouble is here that as the OnPaintBackround override is stopping this method from being called in the base class when using double-buffering then the control appears black rather than transparent. Getting the colour of teechart's containing control is not an option, I think.
There was a error in my previously posted code you have to call the base-class to paint ( because in double buffering mode base class is not called in overriden method):

Code: Select all

 //First Paint Background.
                    if (!this.GetStyle(ControlStyles.Opaque))
                    {
                        PaintEventArgs bgPaintArgs = new PaintEventArgs(_backBuffer.Graphics, DisplayRectangle);
                        base.OnPaintBackground(bgPaintArgs);
                    }
                    Draw(DisplayRectangle, _backBuffer.Graphics);
                    _redrawNeeded = false;

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Post by Christopher » Wed Sep 26, 2007 2:54 pm

Stefans,
stefans wrote: i think i have unclear expressed. there should be no difference in normal, but if you move another window over the chart you see, that double buffering is faster -> no/short delay.
Either that or I didn't understand you <g>. I can definitely see what you mean now and yes, the situation is much more preferable with the 'manual' buffering.
stefans wrote: There was a error in my previously posted code you have to call the base-class to paint ( because in double buffering mode base class is not called in overriden method):
Again, I should have really seen that myself. Now it's working perfectly.

Thank you very much for all your help, Stefan, it's been a pleasure communicating with you. I fully expect this code to be included into the next maintenance release, due out within the next few working days.
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Post by Christopher » Wed Oct 03, 2007 9:37 am

Stefans,
Chris wrote: I fully expect this code to be included into the next maintenance release, due out within the next few working days.
Ok, the maintenance release with an implementation of manual double buffering has just been posted.
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

Walt
Newbie
Newbie
Posts: 35
Joined: Wed Aug 17, 2005 4:00 am
Location: San Jose, CA
Contact:

Post by Walt » Fri Sep 19, 2008 12:50 am

Chris wrote:Ok, the maintenance release with an implementation of manual double buffering has just been posted.
I experience this issue when simply scrolling through a page that contains a live TeeChart .NET control. Scrolling is smooth until I hit the chart control.

I would really like to check out this enhancement/fix, but it seems that this was only applied to a maintenance release on version 3 and I am only licensed for version 2. The only evaluation version that I see for version 3 is a beta from April 2007. Could you either update the evaluation version or apply this change to a maintenance release on version 2? Thanks in advance.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Fri Sep 19, 2008 7:56 am

Hi Walt,

TeeChart for .NET v3 evaluation version is up to date. If you fill in the form here you'll get the keys to download latest version available.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

Post Reply