Analysing an example project of the official book of Qt
-
Hi all,
I tested this Double Buffering example on my Qt Creator (Qt 5.10). It has some issue but there are many issues on comprehending the explanations! I want to alanyse the program.
So, the first question is about this:
Double buffering is a GUI programming technique that consists of rendering a widget to an off-screen pixmap and copying the pixmap onto the display.Does the author mean, double buffering is used to grasp/select an area (here selecting a rectangle by mouse dragging) and sending/storing it in another place (?) which is a pixel-ized map and then sending that area from that map to the display, please?
-
Hi,
No, it literally is what was written. Render the next frame offscreen while showing the current. Once the rendering done swap the buffers,
rincerinse, and repeat. -
@SGaist I think that tony is referring to the old technique (circa 1989) of copying an area of the screen that you will temporarily modify, e.g., draw a rubber band line or a sprite that is moving. Draw the item and when it moves, copy the area back to the screen and selecting the new area. Keep repeating until the movement stops.
-
Render the next frame offscreen while showing the current.
What frames are you referring to please by "he next frame offscreen" and "the current"?
Once the rendering done
When will that rendering be done?
swap the buffers, rince and repeat.
"rince"? What buffers? What are those buffers in the program, please?
-
It's kinda hard to describe in few sentences how the computer generated graphics get to the display, but here's the basics:
The display has a refresh rate, usually around 60Hz for an average non-gaming display. This means it can switch between 60 pictures in a second. A single picture like that is called a frame. This is where the FPS term comes that you hear about (mostly in games). FPS stands for "Frames Per Second".
A frame is displayed to the screen not a whole picture at a time but line by line, most of the time top to bottom. Presenting a frame to the screen is called vertical refresh. You've probably seen this in many videos - if you record a computer screen and watch it you see lines moving top to bottom on the screen - that's the refresh happening.
In the very old days you would write pixels directly to the display. This caused many problems. For example if you didn't update pixels fast enough and get caught by the refresh line the user would see part of the old frame and part of the new one. This effect is called "tearing", and you can see it in games and videos if the images are moving fast from side to side. Google "screen tearing" and look at the images to see what I mean.
To fight with this a method called V-Sync was invented. V-Sync stands for "Vertical Synchronization". With V-sync enabled you are drawing the pixels always just behind the refresh line, so you never get caught in the middle of it.
That's the theory at least, It's not enough in practice because it's difficult to guarantee that you can draw entire picture fast enough to always stay behind the vertical refresh line.
To fix that another method was invented called double buffering. Instead of writing directly to the screen a series of buffers are introduced. A buffer is an array of pixels. You can think of it as an image or a picture. In double buffering there are two such buffers - the front buffer and a back buffer. The front buffer is the picture from which the display reads pixels to show them. The back buffer is the picture you write pixels to.
When you're done writing pixels to the back buffer you simply switch the two buffers. Back buffer becomes the front and the front becomes the back. This way the front buffer always contains the finished frame, not just part of it.This is not perfect on its own though. The switch can still happen when the screen refresh is in the middle of the front buffer, so the best result is by combining the two methods - double buffering and V-Sync. In this case the switch of buffers happens only when the refresh is finished. The upside is that you never see unfinished frames and you don't have tearing. The downside is that if you finish drawing quickly you can't start drawing the next frame immediately. You need to wait until the next V-Sync happens.
So to answer your questions:
What frames are you referring to please by "he next frame offscreen" and "the current"?
The "offscreen frame" is the back buffer - the one you draw to. "The current" is the front buffer - the one the display is reading from.
When will that rendering be done?
This depends on the technology you use. In Qt this is when the paintEvent() is finished. In OpenGL this is when
glFlush()
is done. In winAPI this is when you call EndPaint(). Every graphics technology has its own way of doing it but the principle is the same. they just use different names for it."rince"?
It's a typo. @SGaist meant "rinse and repeat" which is a phrase that just means "repeat", but cooler ;)
What buffers? What are those buffers in the program, please?
It's the buffers I described earlier. Qt hides these buffers from you so you don't have to deal with the complexity on your own. Just know that when you're using QPainter you're drawing to the back buffer I described and when the
paintEvent
is over Qt swaps the front and the back buffer for you.
This is mentioned in the article because early Qt versions (before Qt 4) didn't use double buffering and you would have all the problems I mentioned - tearing and unfinished frames visible to the user. -
The display has a refresh rate, usually around 60Hz for an average non-gaming display. This means it can switch between 60 pictures in a second. A single picture like that is called a frame. This is where the FPS term comes that you hear about (mostly in games). FPS stands for "Frames Per Second".
A frame is displayed to the screen not a whole picture at a time but line by line, most of the time top to bottom. Presenting a frame to the screen is called vertical refresh. You've probably seen this in many videos - if you record a computer screen and watch it you see lines moving top to bottom on the screen - that's the refresh happening.
So a picture is a frame which is displayed line-by-line from top to down by the system on the screen. So for 60 Hz frequency considering each frame consisting of, say, 100 lines, the display draws 6000 lines vertically per second.
What's the pixmap here which is firstly in the back buffer then to the front and then on the screen?
In the very old days you would write pixels directly to the display. This caused many problems. For example if you didn't update pixels fast enough and get caught by the refresh line the user would see part of the old frame and part of the new one. This effect is called "tearing", and you can see it in games and videos if the images are moving fast from side to side. Google "screen tearing" and look at the images to see what I mean.
You mean this:
With V-sync enabled you are drawing the pixels always just behind the refresh line, so you never get caught in the middle of it.
I didn't understand only this section. What do you mean by "you would write pixels directly to the display" and "you are drawing the pixels always just behind the refresh line", please?
To fix that another method was invented called double buffering. Instead of writing directly to the screen a series of buffers are introduced. A buffer is an array of pixels. You can think of it as an image or a picture. In double buffering there are two such buffers - the front buffer and a back buffer. The front buffer is the picture from which the display reads pixels to show them. The back buffer is the picture you write pixels to.
When you're done writing pixels to the back buffer you simply switch the two buffers. Back buffer becomes the front and the front becomes the back. This way the front buffer always contains the finished frame, not just part of it.So the buffers should be wide enough to house/hold the pictures and the pictures should be <= the size of the buffers. Is it right?
This is not perfect on its own though. The switch can still happen when the screen refresh is in the middle of the front buffer, so the best result is by combining the two methods - double buffering and V-Sync. In this case the switch of buffers happens only when the refresh is finished. The upside is that you never see unfinished frames and you don't have tearing.
That way, each frame/picture for one of the buffers.
When will that rendering be done?
This depends on the technology you use. In Qt this is when the paintEvent() is finished. In OpenGL this is when
glFlush()
is done. In winAPI this is when you call EndPaint(). Every graphics technology has its own way of doing it but the principle is the same. they just use different names for it.Rendering here means mapping the picture from the front frame to the display/screen, yes?
What buffers? What are those buffers in the program, please?
It's the buffers I described earlier. Qt hides these buffers from you so you don't have to deal with the complexity on your own. Just know that when you're using QPainter you're drawing to the back buffer I described and when the
paintEvent
is over Qt swaps the front and the back buffer for you.I thought
paintEvent
shows the picture on the screen too.Thank you very much.
-
What's the pixmap here which is firstly in the back buffer then to the front and then on the screen?
It's not really a pixmap in Qt's sense. It's a system provided buffer. On Windows it could be DirectX framebuffer or a GDI object, on Linux an OpenGL texture etc. It doesn't matter to you, It is hidden by the QPainter so you don't need to think about what it is. You just use a QPainter in the paintEvent.
You mean this:
Yes. By the way - it's a screenshot from "Enslaved: Odyssey to the West". I love that game :)
So the buffers should be wide enough to house/hold the pictures and the pictures should be <= the size of the buffers. Is it right?
It's a bit more complicated, because in reality it's not a one or two buffers but a lot of smaller buffers that get composited together by the window manager. To simplify you can imagine that each window gets its own buffers that are exactly the size of that window. The things you draw don't have to perfectly fit in those frames. If you try to draw outside of that region the painter will clip that for you. You don't have to worry about writing out of bounds or anything like that.
Rendering here means mapping the picture from the front frame to the display/screen, yes?
Rendering is the whole process of making shapes described by some objects (QPoint, QLine, Qrect etc.) to show up on a screen as pixels. It consists of many many stages - double buffering is only one of them. there's also rasterization, composition, gamma correction and many more. I'm omitting many of them to make it easier to understand.
I thought paintEvent shows the picture on the screen too.
No, in
paintEvent
you just draw to the back buffer. After paintEvent is done Qt swaps the buffers and contents of the front buffer start their journey to the display.Here are some gifs to help you visualize the process. Let's say you want to make some moving box on the screen. If you don't know anything about how it works you would think that you just call draw methods on painter and it magically appears on the screen like this:
But that's not how it works at all. In reality there's a buffer in memory that you write to and the screen copies it line by line:
So now you see the problem. When I mentioned drawing just behind the vertical refresh line I meant something like this:
You draw only "behind", or "on top of" the refresh line (the green area).
This is how it used to be done years ago but this doesn't map well to how operating systems and graphics drivers work today. So in practice this approach is almost impossible today.
Double buffering introduces another buffer that you write to (the back buffer). When you're done writing and vertical refresh is finished (the line is at the bottom) a fast swap of the buffers is done and the process repeats:
This makes sure that the vertical refresh never catches you in the middle of drawing and whole frames are transferred from the front buffer to the screen.This is greatly simplified, because in reality there's not one single rectangle being drawn but many many layers of shapes, text glyphs and window compositions, but I hope this makes it a little bit more clear.
-
Thank you.
What we do is done only on the back buffer. That is we only have access to the back buffer, yeah?
And one other question on the last three gifs on a line: shouldn't they be in the adverse order, I mean, first the back buffer, then front buffer and then the screen, from left to right? -
That is we only have access to the back buffer, yeah?
Yes, and it's usually only write access i.e. APIs like Qt don't really give you a way to read from there (well, mostly anyway).
I mean, first the back buffer, then front buffer and then the screen, from left to right?
These buffers are segments of your computer's memory. There's no left or right there so it doesn't matter whats the order in those gifs ;) That's just the order I happened to draw them in.
-
@Chris-Kawa
Thank you very much for your great answers. :)