Page 1 of 1

State of manual double-buffer implementation ?

Posted: Mon Sep 24, 2007 7:31 am
by 13046026
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.

Posted: Tue Sep 25, 2007 9:49 am
by Chris
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.

Posted: Tue Sep 25, 2007 11:15 am
by 13046026
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

Posted: Wed Sep 26, 2007 6:55 am
by Chris
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!

Posted: Wed Sep 26, 2007 7:03 am
by 13046026
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

Posted: Wed Sep 26, 2007 9:39 am
by Chris
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?

Posted: Wed Sep 26, 2007 11:14 am
by 13046026
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

Posted: Wed Sep 26, 2007 11:24 am
by 13046026
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).

Posted: Wed Sep 26, 2007 2:22 pm
by Chris
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>.

Posted: Wed Sep 26, 2007 2:31 pm
by 13046026
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;

Posted: Wed Sep 26, 2007 2:54 pm
by Chris
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.

Posted: Wed Oct 03, 2007 9:37 am
by Chris
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.

Posted: Fri Sep 19, 2008 12:50 am
by 9638056
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.

Posted: Fri Sep 19, 2008 7:56 am
by narcis
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.