Recent

Author Topic: In Paint or not in Paint?  (Read 2730 times)

mm7

  • Full Member
  • ***
  • Posts: 193
  • PDP-11 RSX Pascal, Turbo Pascal, Delphi, Lazarus
In Paint or not in Paint?
« on: April 18, 2020, 01:45:38 am »
My program is CAD-like, it has following parts: building model,  drawing it, and user input (mouse, keyboard).
Lets call them Model, View, Controller accordingly.
Currently it works from Paint method, it calls Build if required, then View. Usually Paint follows Controller actions.

It means all heavy work that Build does is happening in GUI thread. If I understand correctly. Or there is no such thing in FPC/Lazarus?

Would it be better for performance or safety to leave only View in Paint method, and move Build to be activated after Controller actions?

Big part of View is drawing to a "hidden" bitmap, that in the end is copyed to screen (it is not OpenGL, yet). It can be also moved out of Paint. Would it make sense?

What is best practice?

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: In Paint or not in Paint?
« Reply #1 on: April 18, 2020, 02:18:19 am »
Hi!

If you got enough RAM then for speeding up the visual part is - as you said - draw everything in the background to a non visible bitmap. When everything is done then just do a draw on a visible component like a TImage.

If you draw on the Image.Picture.bitmap. canvas then this is persistent and you don't have to care about a onPaint. The Image is doing that for you.

And: For all graphic components exists the rule: Only paint in Onpaint event. Exception: TImage. You can paint at every time on the bitmp.canvas.

So move all your paintings on the hidden bitmaps out of the paint event. Paint them in the background whenever needed.  And then draw them on the Image.

Winni


mm7

  • Full Member
  • ***
  • Posts: 193
  • PDP-11 RSX Pascal, Turbo Pascal, Delphi, Lazarus
Re: In Paint or not in Paint?
« Reply #2 on: April 18, 2020, 06:40:24 pm »
Thanks Winny.

Yes the program paints to bitmap.canvas. Actually there are two bitmaps. One is visible, another is not. All painting goes to the invisible one. In the end they are switched.

So basically onPaint should only switch the bitmaps. Am I right?

Or, may be I do not need the onPaint at all. The background processing should build Model and paint to the invisible bitmap, and then it should switch bitmaps in Image.Picture. This will cause automatic repaint of Image on screen.

Should that background processing be in a separate thread, to be not in Application messages cycle?
If so, should any precautions to be done regarding multi-threading?
(it may happen that background will switching bitmaps while Image reads from it to put it on screen).
Something like Synchronize(SwitchBitmaps)?

loaded

  • Hero Member
  • *****
  • Posts: 824
Re: In Paint or not in Paint?
« Reply #3 on: April 20, 2020, 10:19:41 am »
Yes, there is no need for onpaint operation, it is much faster to make the drawings to the bitmap object and then transfer the image with the canvas.draw method.
I am working on a CAD program. On the https://cadplugin.com/emlakcad-en-pratik-harita-emlak-uygulamasi/ page, under the name Emlakcad  :)
« Last Edit: April 20, 2020, 10:25:59 am by loaded »
Check out  loaded on Strava
https://www.strava.com/athletes/109391137

mm7

  • Full Member
  • ***
  • Posts: 193
  • PDP-11 RSX Pascal, Turbo Pascal, Delphi, Lazarus
Re: In Paint or not in Paint?
« Reply #4 on: April 21, 2020, 05:03:38 am »
In which event do you draw into bitmap and in which event do you do canvas.draw ?
In mouse/keyboard events?

loaded

  • Hero Member
  • *****
  • Posts: 824
Re: In Paint or not in Paint?
« Reply #5 on: April 21, 2020, 08:31:50 am »
First of all, I would be glad if someone who knows better corrects my possible mistakes.
I think the scope of the answer to this question is a bit wide  ;D

To begin with, to use in the drawing process;
You must create a special type dynamic array. In this series;
Single point, Line, Polyline, Polygon, Text, etc. You must transfer your objects to the array.
You should increase the array in each new object and add this object to the array.

Create a function for drawing;
In this function, draw all the objects in the array to the bitmap,
When the drawing is finished, transfer the bitmap to the Image object.

As the software develops, of course; you will be faced with performance issues. For this, drawing processes;
I strongly recommend you do this in thread. :)

The user will use the screen to drag and drop;
-MouseDown, MouseMove, MouseUp procedures  From MouseMove
Or used in enlarging, reducing operations,mousewheel

Perform the drawing, triggering this function within the procedure.
« Last Edit: April 21, 2020, 08:50:01 am by loaded »
Check out  loaded on Strava
https://www.strava.com/athletes/109391137

furious programming

  • Hero Member
  • *****
  • Posts: 852
Re: In Paint or not in Paint?
« Reply #6 on: April 21, 2020, 09:21:12 am »
Yes, there is no need for onpaint operation, it is much faster to make the drawings to the bitmap object and then transfer the image with the canvas.draw method.

Not exactly. 8)

IMO the best way to implement this is to use OnPaint event, at least one back buffer in the form of simple bitmap and force the canvas to be repainted using Invalidate method of the object (component or form) which provides a canvas.

The reason is simple — the canvas must be repainted after changes made by user (for example, when user add, delete or modify a shape), but also when the system decide to do that. In both cases, the OnPaint event is used. So, if you implement whole rendering process inside this event, the canvas will always contain a valid current copy of the back buffer.

But do not do this directly in this event — implement an external renderer (simple class), and in OnPaint event call the appropriate method, which will render everything on the canvas. And do not call the Paint method directly — AFAIR in some situations it can crash the program. But the Invalidate is safe.

In summary, if you want to update the content of the canvas (after user modifications), just call Component.Invalidate method and the OnPaint event will be triggered if it could be performed. And if your application is multithreaded, just synchronize the calling of the Invalidate method.
« Last Edit: April 21, 2020, 05:04:53 pm by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

mm7

  • Full Member
  • ***
  • Posts: 193
  • PDP-11 RSX Pascal, Turbo Pascal, Delphi, Lazarus
Re: In Paint or not in Paint?
« Reply #7 on: April 21, 2020, 02:57:14 pm »
@loaded
Quote
create a special type dynamic array. In this series;
Single point, Line, Polyline, Polygon, Text, etc. You must transfer your objects to the array.
Yes, that what I called Model.

Quote
Create a function for drawing;
In this function, draw all the objects in the array to the bitmap,
When the drawing is finished, transfer the bitmap to the Image object.

As the software develops, of course; you will be faced with performance issues. For this, drawing processes;
I strongly recommend you do this in thread.
Yes, this I called View.

Quote
The user will use the screen to drag and drop;
-MouseDown, MouseMove, MouseUp procedures  From MouseMove
Or used in enlarging, reducing operations,mousewheel
This I called Controller.
Controller is always driven by user actions. Mouse/Keyboard events.

Quote
Perform the drawing, triggering this function within the procedure.

So, as I understood correctly, you call Model from Controller, then initiate a thread do call View?

My concern here is that, if Model called from Mouse/Keyboard events, it may slow down user input, make it delay when while Model is processing.

Imagine a situation your Model has 10000 objects, that will result creating 100000 graphical primitives (points, lines, etc).
Time of processing of Model can be 5 sec.
Time of processing of View that draws primitives to bitmap, can take another 5 sec, but this is done in thread.
So, each time user moves a point on screen, it will block Controller for 5 sec and no other interaction will be possible.
And user will see visual response after View done, after 5 sec. But this will not lock repainting other controls by system.

@ furious programming
please read the example above. What do you think, if whole rendering (Model+View) process is called from inside onPaint, that may delay repaints of other controls on screen, if system decides to repaint them.
Also it delays to process other events. It blocks whole Application event queue. For example it will not refresh ProgressBar, or capture click Cancel button.
This is how it is now in my program, by the way.


loaded

  • Hero Member
  • *****
  • Posts: 824
Re: In Paint or not in Paint?
« Reply #8 on: April 21, 2020, 04:12:35 pm »
Yes, you are close, with a difference;
The model is created once.
The control calls the view every time.

Quote
So, as I understood correctly, you call Model from Controller, then initiate a thread do call View?
My concern here is that, if Model called from Mouse/Keyboard events, it may slow down user input, make it delay when while Model is processing.

The trick is; It designs the view function with thread support.
I mentioned performance issues. Also, you should definitely use Thread to react to user controls simultaneously.
The control can be a counter or a timestamp without calling a view. You assign a number such as gettickcount64 and call the view operation.

For thread supported view, in thread;

First, on the screen with the intersection account will be drawn and will not be drawn you must detect objects.
After that ; You draw objects that need to be drawn on the bitmap.
* This will give you a high performance.

This may be 1000, 2000, 3000 in a range you specify during drawing.
Or it may be in a range you find as a result of an equation you will set up inversely proportional to your screen scale.
At the intervals you set;
With the synchronize method, you transfer the bitmap object to the image object on the form. (In this method, by checking the timestamp You can only make the view operation from the last control on the screen.)

You have; In this way, your program will work comfortably no matter how big the project is, without any delay or slowdown.

With this method, I can easily work with about 1,500,000 objects.
Check out  loaded on Strava
https://www.strava.com/athletes/109391137

furious programming

  • Hero Member
  • *****
  • Posts: 852
Re: In Paint or not in Paint?
« Reply #9 on: April 21, 2020, 04:54:45 pm »
please read the example above. What do you think, if whole rendering (Model+View) process is called from inside onPaint, that may delay repaints of other controls on screen, if system decides to repaint them.

I wrote it wrong earlier — forgive me...  %)

Inside OnPaint event should be only the code resposible to draw the back buffer on the target canvas, no re-rendering the content. But everything what must be rendered on the back buffer, should be done in the side thread. Using the back buffer avoids flickering, and copying the back buffer to the canvas in the OnPaint event eliminates window freezing.

AFAIK this is the fastest way to update interface and probably something like this has @loaded implemented.
« Last Edit: April 21, 2020, 05:02:50 pm by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

mm7

  • Full Member
  • ***
  • Posts: 193
  • PDP-11 RSX Pascal, Turbo Pascal, Delphi, Lazarus
Re: In Paint or not in Paint?
« Reply #10 on: April 23, 2020, 03:48:03 am »
@loaded
I read many times, still not getting what you are saying about using timestamp. Is it you redraw only object(s) that changed recently?

Model can change. Imagine user drags a vertex of a corner of cube. The cube will gradually change shape. This is reflected in Model.

@all
View can change when user changes scale, point of view, rotates whole scene. In this cases sure no need to rebuild Model.

And finally screen/window needs to be re-drawn in case of View is changed, or system needs to refresh window.
I assume if View is not changed, the its image sits in Image.Picture.Bitmap, the Image will refresh automatically.
When View is changed it should re-draw into a separate hidden bitmap, then assign this bitmap to Image.Picture.Bitmap.
If it is done in separate thread, this last assignment should be synchronized.

So if we can have 2 threads:
1. Main thread that processes App Queue, input events and Paint events.
2. Rebuild/Redraw thread, that rebuilds Model, if required, redraws View to hidden bitmap, and finally synchronously assigns this bitmap to Image.Picture.Bitmap

We can start Rebuild/Redraw thread in input events when Model or just View changes.

Is my understanding correct?

loaded

  • Hero Member
  • *****
  • Posts: 824
Re: In Paint or not in Paint?
« Reply #11 on: April 23, 2020, 09:54:58 am »
In my opinion,
When you move the cube, you should change the coordinates of the screen, not the model.

With every appearance change, changing the model creates a huge waste of time in large projects. Instead, it is much faster to calculate the new position of the model on the new screen.

So;
The model is created once.
When your perspective changes, you recalculate the entire Model.
at this stage, you perform the drawing.

Time stamp or counter issue;
Imagine that there is a project with a lot of objects and a hyper-active user, and this user is constantly enlarging, reducing and moving the screen.
Result;
You will have to run drawing successively.
In the meantime, you will face a serious performance problem as other drawing operations will continue before the first drawing process ends.
With the counter, you only guarantee to draw back the last action of the user.
(In my experience such as; It may be necessary to draw 50-60 times a second. In small projects, this problem may not be felt, but in large projects it creates a noticeable discomfort.)
It is useful to state that this is a method I have gained with my experience. I don't know if there is a better method.
Check out  loaded on Strava
https://www.strava.com/athletes/109391137

mm7

  • Full Member
  • ***
  • Posts: 193
  • PDP-11 RSX Pascal, Turbo Pascal, Delphi, Lazarus
Re: In Paint or not in Paint?
« Reply #12 on: April 24, 2020, 03:44:08 pm »
Understood. Good point.

In my case RebuildModel starts as soon user moves a point.
While model is rebuilding all other entries into RebuildModel procedure are skipped.
it looks like this
Code: Pascal  [Select][+][-]
  1. var IsRebuilding:boolean = false;
  2.  
  3. procedure RebuildModel;
  4. begin
  5.   if IsRebuilding then exit;
  6.   IsRebuilding := true;
  7.  
  8.   // Do Actual Rebuilding ....
  9.  
  10.   IsRebuilding := false;
  11. end;
  12.  

 

TinyPortal © 2005-2018