Hi,
shiner wrote:it's terrible slow. mouse has 1px/s(pixel per second) speed.
there's how it works:
it has the video mem. and a double buffer.
in a loop, it draws the entire desktop(a screen-size bitmap), the windows and what it has to draw(some text, the mouse cursor, etc) on the double buffer.
then, it copies the double buffer to the video mem.
how can be done some optimizing?
I've heard of dirty lines, but what are they?
thanks, eddyb.
A graphics systems has 3 basic states:
- Waiting for something to change
- Redrawing as little as possible (e.g. depending on what changed)
- Updating the least amount of video display memory necessary (e.g. depending on which areas were redrawn)
This could be done in a simple loop, but you can do "temporal optimizations" - skip work that will only be visible for a short time (and won't be noticed by the user anyway). The basic idea here is to limit the "frames per second" you generate, so that (for e.g.) if the video mode has a 60 Hz refresh rate you never generate more than 60 frames per second. If you're waiting for something to change and something does change, then start a timer and wait for a short amount of time (e.g. "(1 / refresh_rate) / 8" seconds) in case more things change, so that if several things change at the same time you only do the rest of the work once (instead of doing the rest of the work several times). Then, when you've updated display memory remember what time it is and postpone all updates until "1 / refresh_rate" seconds has passed.
Eventually, you should end up with an image for the background and an image for each "thing" (e.g. window) that's in front of the background; and eventually you'll need to combine all these seperate images into a single image (e.g. your double buffer). For 3D pipelines there's a technique called "scan line conversion" that would work well here. For each horizontal line, you build a sorted linked list that describes the contents of the horizontal line. For example, if the horizontal line should look like this (where '.' is the background, '1' is pixel data from the first window and '2' is pixel data from the second window):
Then the linked list for this line would have one entry for the first piece of background, one entry for the left edge of the first window, one entry for the left edge of the second window, and one entry for the second piece of background; where each entry contains the source of the run of pixels (which bitmap they come from), the starting (x, y) position of the run of pixels within the source bitmap, and how many pixels are in the run. Once you've got the linked lists you'd use them (eventually) to update the double buffer, which completely avoids updating the same pixel (in the double buffer) more than once.
For a GUI, these linked lists don't change unless windows are moved or resized, so you only need to update them occasionally (not for every frame) and most of the time you'd reuse the previous linked list. Also, you don't need any pixel data to generate these linked lists - you only need to know where each window is (e.g. the (x, y) of the top left corner of the window and the width and height of the window). While you're generating these linked lists, you can give each window and the background a counter that keeps track of how many linked list entries refer to that window (and the background). If the counter for a window (or the background) is zero, then that window (or the background) isn't visible.
Once you've done this, the next step is to redraw the bitmaps for the background and the bitmaps for each window, but you'd skip everything that isn't visible and recycle previous bitmaps if they didn't change.
Now you use those linked lists (and the bitmaps) to generate the double buffer. While doing this you can generate a set of "line changed" flags by comparing the old data in the double buffer with the new data you're putting in the double buffer. Finally, you copy the double buffer to video display memory, while skipping any lines that were the same as last time.
Also, it's a good idea to use these "line changed" flags much earlier. When you first start redrawing things, clear all the line changed flags. If any window is moved or resized, or if any window is updated, then set the corresponding line changed flags. Then you can skip entire lines when you're updating the double buffer, and for lines that aren't skipped you'd check if the pixel data changed and clear the corresponding line changed flag if the line didn't change.
Lastly, there's the mouse pointer. Before you copy the double buffer to display memory you copy any pixels that are underneath the mouse pointer somewhere else, then draw the mouse pointer in the double buffer (and set the corresponding "line changed" flags), then copy the double buffer to display memory, then restore the pixels under the mouse pointer. Of course if the only thing that changed is the mouse pointer, then you could skip everything else and only do this part.
Note 1: The "line changed" flags could be something else - for e.g. you could have one flag for each horizontal line, but you could have one flag for each dword, or one flag for each 64-byte area, or something else.
Note 2: This could be extended by taking a little more from 3D pipelines, to allow windows to be rotated about the Z axis and scaled. For this you'd need to do clipping and translation, then do the scan line conversion (build those linked lists that describe the contents of each horizontal line); and you'd need to add more details to the linked list entries so you can calculate where the next pixel comes from (e.g. X and Y step). You could probably extend this further by allowing windows to be rotated about the X axis and Y axis, but this complicates things a lot because Z isn't constant anymore. There's probably other effects you could add that aren't as complicated though (e.g. transparency or alpha blending, fog/darkness).
Note 3: It'd be wise to forget about windows and think in terms of "canvases" or "bitmaps" or something. It'd be nice if most of the code to update the double buffer is also used to draw each window. For example, have a "update destination bitmap from source data" function.
Note 4: Don't forget to implement all this with hardware accelerated video in mind, so that if someone (eventually, maybe, one day) writes a video driver that supports hardware acceleration then the graphics subsystem can use the video driver's hardware acceleration. This mostly means that most of the above needs to be done by the video driver on behalf of the applications, GUI, etc; and not done by applications, GUI, etc themselves. Also, it'd be very nice if your video driver is multi-threaded, so that on a multi-CPU system you get multiple CPUs sharing all the work, especially if there's no hardware acceleration.
Cheers,
Brendan