Recent

Author Topic: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)  (Read 2276 times)

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Hi all. I have some TPaintBoxes that I want to repaint at a constant rate (as close to 60hz as possible, basically). I have a TTimer on my form with an interval of 16, which essentially just calls Invalidate on my paintboxes. This is fine, and it (kinda) works.

Let's say my timer event looks like this:

Code: Pascal  [Select][+][-]
  1. procedure TMyForm.Timer1Timer(Sender: TObject);
  2. begin
  3.     MyPaintBox.Invalidate;
  4. end;

The problem is that now menu items (on the main menu) don't respond to being clicked, and any other forms that exist can't be closed with the X button. I don't really know why this occurs, I'm guessing because the repainting at 60hz is clogging the message queue with timer events or something?

I put this in as a fix:

Code: Pascal  [Select][+][-]
  1. procedure TMyForm.Timer1Timer(Sender: TObject);
  2. begin
  3.     if PerformingTimerUpdate then Exit;
  4.  
  5.     PerformingTimerUpdate := True;
  6.     MyPaintBox.Invalidate;
  7.     Application.ProcessMessages;
  8.     PerformingTimerUpdate := False;
  9. end;

Alright, this allows menu items and other forms to work properly, most of the time. About 1 out of every 10 clicks on a menu item will fail to work, or a form will fail to close when you click the button. When you click a menu item, the menu just stays up as if nothing happened. I have no clue why this is going on. There's no way I'm exceeding the depth of the message queue because it can store 10,000 messages and I'm clearing them out pretty quickly. So why is it seemingly dropping click events!?

What's the recommended way to do this? I know it has to be possible because other programs can do fast repaints no problem.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #1 on: January 24, 2020, 03:18:36 am »
Hi!

With 60 hz you are in the "danger zone" (and on the wrong continent).
It depends on your hardware and OS. The dropouts and that not reacting on clicks means that there is not enough time between to time ticks to react.

Yes - other programms can do that - but not with a high level component like a paintbox. That takes too much time.

To use some components with higher speed read this:

https://wiki.lazarus.freepascal.org/Developing_with_Graphics

But anyway: you are realy  at the edge with 60 hz. Lazarus was never developed to react in real time. 

Winni

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #2 on: January 24, 2020, 03:30:54 am »
What's the solution then? It seems to me that the root of the problem is that I'm wasting precious LCL GUI main-thread time on painting, but you can only update the UI in the main thread so it's sort of a dilemma. I know Lazarus programs can do multimedia and games so how can I do something like that?

Handoko

  • Hero Member
  • *****
  • Posts: 5149
  • My goal: build my own game engine using Lazarus
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #3 on: January 24, 2020, 03:31:10 am »
Code: Pascal  [Select][+][-]
  1. procedure TMyForm.Timer1Timer(Sender: TObject);
  2. begin
  3.     if PerformingTimerUpdate then Exit;
  4.  
  5.     PerformingTimerUpdate := True;
  6.     MyPaintBox.Invalidate;
  7.     Application.ProcessMessages;
  8.     PerformingTimerUpdate := False;
  9. end;

Have you tried to put the Application.ProcessMessage; before the PerformingTimerUpdate block?

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #4 on: January 24, 2020, 03:32:10 am »
Quote
Have you tried to put the Application.ProcessMessage; before the PerformingTimerUpdate block?

Yes, it results in the same issue.

Handoko

  • Hero Member
  • *****
  • Posts: 5149
  • My goal: build my own game engine using Lazarus
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #5 on: January 24, 2020, 03:42:00 am »
LCL components (which are crossplatform) use OS functions to do screen update, and that is slow. Only suitable for simple graphics/games. If you do heavy graphics or games, you may need to use hardware-accelerated graphics library.

One simple trick to improve graphics performance and reduce flickering effect, is to paint all objects in a virtual layer (memory buffer) before showing it on the screen. Read more here:

https://forum.lazarus.freepascal.org/index.php/topic,38136.msg263143.html#msg263143

Maybe that can solve your issue.
« Last Edit: January 24, 2020, 03:49:39 am by Handoko »

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #6 on: January 24, 2020, 03:55:50 am »
If anyone's curious, here's an example of the problem. Try clicking the "update number" menu item and notice that it works. Then, click "open other form" and notice that you won't be able to close it.

Uncomment the Application.ProcessMessages line, and notice that you can now close the other form, but the program as a whole becomes a lot less responsive (clicks are dropped).

Handoko

  • Hero Member
  • *****
  • Posts: 5149
  • My goal: build my own game engine using Lazarus
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #7 on: January 24, 2020, 04:16:03 am »
I added and tested your issue on my Moving2, I could not reproduce your issue. Showing a new form and closing it was 100% responsive, tested using 10000 particles still working good.  It don't even need to call Application.ProcessMessages. So I believe the trick can solve your problem.

Seriously, don't draw all objects directly on the 'real' screen. That is a stupid thing to do in game programming.

Each time, any direct painting on the screen requires a lot of hard work for the system if you're using OS API. So we really need to reduce the time by painting them to the screen on one single pass. Hope you can understand my explanation.

Alternatively, just as suggested by @winini if you use any library that is optimized for graphics or game, they will handle that thing for you in background without you knowing it.
« Last Edit: January 24, 2020, 04:36:53 am by Handoko »

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #8 on: January 24, 2020, 04:36:23 am »
Well what do you know, I just implemented the trick and it solves the problem. Why doesn't PaintBox buffer what is drawn to its canvas in the OnPaint event anyway?!?!?

Thanks Handoko, you're a baller!

Handoko

  • Hero Member
  • *****
  • Posts: 5149
  • My goal: build my own game engine using Lazarus
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #9 on: January 24, 2020, 04:47:23 am »
Why doesn't PaintBox buffer what is drawn to its canvas in the OnPaint event anyway?!?!?

Don't blame it. TPaintBox simply calls the OS API.

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #10 on: January 24, 2020, 04:51:39 am »
I thought I read somewhere that the semantics of drawing on a paintbox's Canvas property was different in the OnPaint event (I figured it would do buffering or something, but I suppose not).

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #11 on: January 24, 2020, 05:07:16 am »
Well, I guess I spoke a little too soon. I'm still having the problem where windows won't close.

I think this is some sort of LCL bug. Buttons/controls/textboxes all work, which means that events are pumping correctly in the main form. The only issue is of closing other forms, which implies that somehow events aren't being pumped correctly for them, despite the main form working okay. Interestingly, even controls on the other forms work (buttons fire their event handlers, labels can be changed, etc) which means it's just an issue of closing forms.

@Handoko: Here's a version of your Moving2 program which demonstrates the issue. I made it so it does a little extra rendering basically to simulate some heavier drawing.
« Last Edit: January 24, 2020, 05:21:15 am by Superdisk »

Handoko

  • Hero Member
  • *****
  • Posts: 5149
  • My goal: build my own game engine using Lazarus
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #12 on: January 24, 2020, 05:39:14 am »
I compared your code with mine. You set the Timer interval to 1 and the repeat 10x the drawing block.  :D

FYI, setting Timer interval to 1 is useless. That won't happens at least on Linux. I have another trick, not using a TTimer. Instead use a delta time technique. That will give you more accurate time and I believe is more optimized than using a TTimer.

But everything has its limit. As mentioned LCL is not for heavy graphics applications. You may want to try FreeBasic, they are 'better' in handling graphics.

But if you want to stress the hardware like that, you really need to use OpenGL. Also, game developers (using OpenGL) usually will limit their game features to make it runable on the present hardware.
« Last Edit: January 24, 2020, 05:43:23 am by Handoko »

Superdisk

  • Jr. Member
  • **
  • Posts: 73
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #13 on: January 24, 2020, 05:44:08 am »
Quote
I compared your code with mine. You set the Timer interval to 1 and the repeat 10x the drawing block.  :D

Heh, well it was the only way I could reproduce the issue in your code. My actual code (that I'm having problems with) is 4096 calls to LineTo @ 60hz. I find it hard to believe that on 2020 machines this is a problem-- Windows 95 should be able to handle this. I guess I'll plumb through the LCL tomorrow and see what is causing the window bug.

Handoko

  • Hero Member
  • *****
  • Posts: 5149
  • My goal: build my own game engine using Lazarus
Re: Repainting 60hz with LCL (or: Application.ProcessMessages drops clicks!)
« Reply #14 on: January 24, 2020, 05:46:36 am »
Canvas.LineTo is slow.

I ever try OpenGL line drawing. It's still running smoothly when I drew thousands of lines. It started to become noticeable slow if I draw thousands of polygons but that because I used the legacy OpenGL technique. Modern OpenGL API (which I'm still not familiar) is optimized to handle massive object drawing.
« Last Edit: January 24, 2020, 05:55:07 am by Handoko »

 

TinyPortal © 2005-2018