TV graphics and memory usage
tjhenry
Posts: 4
Lately I have been working on a project that is basically a simulated plane cockpit, using SPIN. It has a simulated EFIS (Electronic Flight instrument system), audible alerts (via a separate chip using SPI), along with numerous switches and lights. While I have all the separate pieces working, it appears I'm running out of RAM when combining it all together. I have 1 top level cog that reads the switches and updates leds, this cog also creates new cogs for the video generation (TV NTSC) and the sound/spi part. The TV portion is by far the memory hog though. While my code in there is not excessive in terms of instructions or stack usage, the RAM needed for the two buffers takes up almost all of the free RAM (hub memory). It appears to require two blocks of memory $3000 each, $140 extra, and 64 longs for the stack (those numbers are not arbitrary and were found with many trial and errors). When compiled I barely have 6100 free space in hub memory (via View Status of top level object). I'm still a little fuzzy on the units mentioned there though, so please correct me if something seems wrong. If I try to run everything at once it appears to run out of memory; the video starts, but when the audio portion is called the whole thing crashes. I've tried running it Gear to debug, while that is great for the logic analyzer part, but unless I uncomment all delays it runs way too slow to trace exactly when the error is occuring. I speculate that if I changed some parts to use ASM instead of SPIN I could take use of cog ram instead of hub ram giving an extra margin. I would prefer not to rewrite everything unless necessary though.
Which brings me to my questions:
* Does anyone have suggestions for reducing the amount of memory required for the TV video generation, or even a better way to organize the program to avoid hitting this memory limit?
* Am I missing something major that specifies using cog ram (as opposed to hub) for SPIN based components?
Which brings me to my questions:
* Does anyone have suggestions for reducing the amount of memory required for the TV video generation, or even a better way to organize the program to avoid hitting this memory limit?
* Am I missing something major that specifies using cog ram (as opposed to hub) for SPIN based components?
Comments
It might take some time reordering all of the items you display, but it works great and frees up a bunch of memory.
Bill
Post Edited (wjsteele) : 4/23/2010 6:14:51 PM GMT
Some general things:
If you have a 64kB EEPROM you can put the PASM portion of the TV driver (and whatever additional PASM drivers you have) into the upper part of memory.
The TV driver can be modified to a single buffer version. If I remember right there should be a single buffer version available in ObEx or the Forum.
I know the next part is more complicated (had lots of fun with the 6502 on almost the same problem)...
Since the graphics 'writing' part is Spin and the TV driver itself is PASM and runs in another cog, it seems like it should be able to wait in the spin code for a vblank to occur, then quick write a small amount of graphics, wait for another vblank, write some more, until several screens later it is fully built. The components themselves may blink relative to each other, but I won't get the nasty black polygons running down the screen. Is this remotely right?
I tried using this in the spin portion:
It draws one frame perfect, then waits indefinitely and does not update.
Is there a reliable way to wait in the spin portion for the vblank?
The short story is write a 1 right at the end of the drawing of the screen graphics, then write a 0 right at the beginning of your screen graphics.
When that HUB value is 1, you can draw! Put your graphics into a repeat while statement, and you've got drawing during the blanking periods.
It's highly likely that drawing everything will take more time than you have. What you do in that case, is build a draw list, and work through it given the time permitted. That will keep flicker to a minimum.
Buffer the list, so that you can write out a new set of values for the graphics to be drawn, leaving the other list static. You might consider looking at the draw order, so that big features get done in one shot, spreading out the details over however many frames it takes. Also, using draw order, you can then build things so that you never clear the screen. Use smaller background color fills to erase a portion of the screen, then draw in the new graphic, keeping these within the time, working through the screen one blank at a time.
With that method, if there is flicker, it will be localized, and the screen will "ripple" through the graphics, getting everything updated as blanking time permits.
When you've worked your way through the list, switch the list pointer to the updated list, and draw from that, updating the other one. That way, whatever you draw will remain static even though the program is making changes to the list. You essentially get a "snapshot", that will draw well.
Don't put a wait in the driver. Once it's running, it needs to just loop, or the signal is lost.
I would draw everything that is static once, then build localized erase routines for the dynamic bits. One routine to erase it, one to draw it. That might actually do most of your graphics in one frame, but if it doesn't, you can very easily do two or three for a perfectly fine update period.
With your graphics structured this way, you can increase the Propeller clock, or decrease it and still have the screen work, with updates just being a matter of how many frames it takes to get stuff done. It's also best to time your graphics, so you know what takes what in terms of cycles. Just start a counter, and run the erase and draw routines, with suitable values, say 50 times. Divide, and you know your cycle time. Repeat for the other graphics elements, then sort the order of them to build your list, generally fitting things into one blank.
Finally, you can use draw order to update different things at different times. Let's say you've one indicator that really needs to display rapidly, and another that doesn't. You can use a list for that too, cycling through a repeating pattern, or you can look at the values to determine what gets drawn. If there isn't a material change, skip it! That kind of thing will result in the fastest display, but not the most consistent.
I would wager the flicker you are seeing is the screen clear before drawing everything. That takes long enough that a largely blank screen will be seen on a frame, and that's where the flicker is at. Keeping most of the screen drawn is the better way to go.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Post Edited (potatohead) : 4/24/2010 2:45:58 AM GMT
Just due to the nature of what I'm displaying, it doesn't seem as conducive to erasing and updating small sections (with the exception of the main altitude and airspeed display blocks). There are a few static lines, but numbers are constantly shifting up or down and the background is almost always in motion. (video below)
Would interspersing the "wait till 1" loops through the graphics calls do the same as having a list? Although I guess for organization purposes alone having them separated out more would make more sense and would facilitate the optimizations.
If it helps here is a crude and blurry video of what it looks like:
www.youtube.com/watch?v=1oTe83cc_qc
The tv driver already has a vsync indicator:
'' tv_status
''
'' driver sets this to indicate status:
'' 0: driver disabled (tv_enable = 0 or CLKFREQ < requirement)
'' 1: currently outputting invisible sync data
'' 2: currently outputting visible screen data
But your right, I can't believe I didn't notice that tv_status before. It will require some more tweaking and reorganizing, but I'm definitely on the right path now.
I suspect the horizon draw is where the work is, along with the thicker text. Can you run this display at a bit lower resolution? That will speed things up, as would clocking up your prop. From what I saw on the video, you could give up 20 percent of the horizontal pixels and still have a good display. That frees ram too.
Where readable, use single pixel width text. The lower resolution can help with this. Doubling up with the width option is a big slow down. For the lines, consider just drawing multiple ones, instead of a thicker one. I think that's faster.
Basically draw things using different primitives and time it, so you know what's quick and what's not, and start making cases for the display draw cycle, packing things into the blank time depending on what you know will have to occur.
What about drawing part of the screen? Take your fastest draw methods, and ripple draw the screen. On one frame, draw the left, on the next frame draw the middle, on the next frame, if needed, draw the right, using the current values from the data source each time. Each strip fits into one frame, so the viewer always sees all the graphics. What they don't see is all the graphics current, but on this display, that's not such a big deal.
Instead of flicker, you get screen tearing and that's a lot less intrusive, and the viewer can actually act on a lot of information right away, even though it's a partial screen draw, their minds eye will fill in the details, making it useful.
I would seriously consider this one, given this kind of display, particularly if you can get it done in halves, or thirds. I think you might just be able to setup the pointers in the graphics object, such that it does the clipping for you. Configure the display cog for the whole screen, and configure the drawing cog to the narrow strip, changing that each time, always running the same graphics commands. This might be slick!
Another idea would be to use PASM in a spare cog, particularly, if you only have one, to speed up the drawing of specific things, and to do it in parallel. Have that cog waiting for a draw command, then fire it off, having it draw as many things as will fit into the cog program space. Maybe just have it do the horizon / initial screen clear and center graphics, and maybe the lines. Those things are pretty easy to do with simple screen fills and a few bit masks needed to form the center graphics. Small changes to the size of the indicator lines, might land on a byte boundary, making for a fast PASM draw in the spare cog too. Maybe use a second flag so the text drawing happens after the lines and horizon.
That's only going to pay off, if you can pack things into one blanking period though. Maybe this could help with the ripple draw method to the point where you are drawing fewer strips. This one is a lot of work though.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Propeller Wiki: Share the coolness!
8x8 color 80 Column NTSC Text Object
Safety Tip: Life is as good as YOU think it is!
Post Edited (potatohead) : 4/24/2010 8:06:37 AM GMT