|By Shamus||May 7, 2009||Programming, Projects||63 comments|
Thanks for all the feedback on Pixel City. Stating your version of windows and graphics chipset along with problem reports is invaluable. The current release version has a rather embarrassing memory leak in it, so prolonged use will make the system thrash for a while when you exit it. For those of you compiling along at home, this is already fixed in the codebase and you should integrate revisions #11 and #12, regardless of what else you’re doing with your own forked version.
For this reason, I’ll be issuing at least one more build of Pixel City that fixes this problem. I’d like to kill a few other issues in that same release if I can gather the data needed to do so.
I want to start with this comment from Brandon:
This is very much what I expected: Just barely useful on a five year old machine. That fits with the my impressions on what we should be seeing, which is why I was very taken aback by reports from people with two or three year old machines that said the thing took ten minutes to start and ran at 2 frames per second. This was very alarming and had me worried that I’d somehow grossly misjudged what I was asking of the graphics hardware and the CPU. Brandon’s comment hints that I was probably right in the first place, and that these extreme slowdowns are coming from somewhere else.
The screensaver functionality was added late in the project, and was thrown together more or less at the last minute. I was slightly dubious about taking a system I’d built carefully (but not adequately tested) and setting it atop a system I’d built in great haste and hadn’t tested at all.
For the record, the program makes no special effort to support or not support multi-monitor setups. How Windows screensavers work is that your program is invoked and instead of manually creating a window yourself like you do in most programs, you are simply given a pre-existing window to use. This can be a bit strange when you’ve got a 1920×1200 monitor in front of you and (say) a 1024×768 monitor to one side and Windows is creating a single frame to encompass them both. One obvious problem is that windows must be rectangular. Assuming the small window occupies the same vertical space as the larger one (which is by no means certain) then your program is going to have a 2944×1200 region to fill. Even worse, within that render-hungry expanse is going to be a 1024×432 rectangle that’s being rendered and then discarded.
(EDIT: And just to be clear, the program doesn’t really have any idea that it IS on a multi-monitor setup. It’s just handed a window. I’m sure it’s POSSIBLE to query the setup and the monitor sizes and their geometic relationship, but I’m sure that would be a lot of work, and I’m not even sure what benefit it would be. The program still has just one window and one render context.)
How that window behaves and what monitors is spans is very much open to how multi-monitor support has been implemented. I know some graphics cards have their own multi-monitor setups that exist outside of the same functionality as provided by windows. Couple this with the fact that different versions of windows probably have different ideas about how window regions are stitched together and you have a recipe for something that will take a lot of time and experimentation with different monitor setups to understand. Keep in mind that I was never really that interested in wrangling with the Windows screensaver functionality. My real goal was the procedural city stuff, and the rest of the project was just scaffolding to support that idea.
The extreme slowdowns are a little more perplexing, and right now my instinct is to again blame the unknown behavior of the Windows screensaver functionality.
The directions I’d read online indicated that Windows would provide your program with a pre-existing window, and would then call WM_PAINT as fast as possible. I was a bit surprised at this, as this is a break from the behavior we see elsewhere in the operating system. Normally a window only gets a WM_PAINT when its contents need to be redrawn. If you move a window around the screen, resize it, or bring it out from behind other windows, it gets a WM_PAINT message letting it know that it needs to redraw. But here we have a situation where WM_PAINT is supposedly being called indiscriminately, and at an unknown (or undocumented) interval.
I’m strongly suspecting that WM_PAINT isn’t getting called very often on some systems. In screensaver mode, everything is bound to that one WM_PAINT message, which is putting a lot of trust in a feature that I don’t know much about and didn’t make sense to me in the first place. If Windows only calls WM_PAINT twice a second, then the program will take ~512 seconds to build the world and will run at two frames a second no matter what hardware you have. This seems to meet with observations.
One solution is to set up a WM_TIMER and see if that solves the problem. This would let me direct windows to call me every (say) 17ms or so, which would give me the desired 60fps framerate.