The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: minor28 on October 09, 2010, 09:31:41 AM

Title: Advice on memory handling
Post by: minor28 on October 09, 2010, 09:31:41 AM
I am working on my own chart plotter with GPS data. I have the free SeaClear (http://www.sping.com/seaclear/) and the commersial Fugawi chart plotters.

I would appreciate some advice on memory handling. A marine chart file is about 5MB of size. The image is about 13,000px wide and 9,200px high. You can find us free marine charts here (http://www.charts.noaa.gov/RNCs/RNCs.shtml).

I have compared memory use for the three applications.

Loading a chart in SeaClear will use 7,644kB and the Page File increases with a couple of MB.

The same chart and Fugawi uses 31,556kB and the Page File increases with 25MB.

My app with the same chart uses 254,152kB and the Page File increases with 240MB.

I don't know how these two apps handles the chart image but this is how I do it


The filemapping is unmapped after allocated memory is filled. Allocated memory is freed when my app is closed and the heap is destoyed and the contexts are released.

I have not find a better way to minimize memory usage but there obviously is a better way. Does anyone have any ideas how?

If you try my app the com port is hardcoded to com1. If you register charts, charts data is stored in registry at HKEY_CURRENT_USER\Software\MINOR28\NavApp. This registration gives the ability to automatic load a new chart whitin the new charts bounderies.
Title: Re: Advice on memory handling
Post by: oex on October 13, 2010, 12:11:08 AM
I would guess that the other applications are only loading and displaying a partial image as needed.... Additionally they might be using a lower color depth (ie 8 bit rather than 32 bit....

As different areas of the maps are loaded onscreen they are loaded from file

(Similar to google earth.... You dont download the whole earth just images a bit at a time)
Title: Re: Advice on memory handling
Post by: clive on October 13, 2010, 01:12:43 AM
They probably load enough of the image to display, plus some extra around the edges to anticipate movement. Boats move relatively slowly, and typically don't go up/down (waves of course, but water tends to be flat at rest, and charts are 2D with depths). Even in a car going at 60 mph, you are only looking at a mile a minute, so caching a mile on either side would allow for relatively few updates. Plus if you have some idea of course-over-ground you can anticipate what data would be needed next. A thread could go off and speculatively fetch and prepare tiles.

You could probably parse the whole chart, and store a catalogue of tiles for quick access, and information for tiles in adjoining charts/files.

The zoom level may also permit a great deal of information to be discarded. The display resolution is small relative to the whole chart.

People have classically coded these kinds of applications to use memory as limited commodity, loading the whole file tends to be a lazy approach. The charts are typically very large, and the plotters are lower powered processors. The old Northstar 962's used a 200 MHz Pentium MMX, NT 4.0 and 64-128MB of RAM.
Title: Re: Advice on memory handling
Post by: Farabi on October 13, 2010, 03:17:26 AM
You can load the whole big image on a single DC without any problem, since it only render spesific location specified by you using bitblt.
Title: Re: Advice on memory handling
Post by: minor28 on October 13, 2010, 08:04:29 AM
Thank you very much for your answers. I really appraciate it.

A marine chart file consists of three sections. A header, pixeldata and a jumptable.

Header:
different chart data, at least 3 ref points but mostly more than 30, points for the chart outlining, a colortable etc.

Pixeldata:
compressed data

Jumptable:
with file offset to each image row.

As I understand I must load the whole file. But this is not a problem with filemapping.

My first atempt was to read the file with memoryblocks of 32kB with API crt_fread and unpack. Unpacking was three times slower than reading. The compressing algorithm is something like RLE compressing algo.

Quote
Additionally they might be using a lower color depth (ie 8 bit rather than 32 bit....
All charts have max 256 colors (8bit)

Quote
I would guess that the other applications are only loading and displaying a partial image as needed....
Quote
They probably load enough of the image to display, plus some extra around the edges to anticipate movement. Boats move relatively slowly ..
I have tryed. As I understand the image can only be devided in vertically parts. The whole row must be unpacked. If I unpack and load only a part of the image, dragging and zooming will be too slow compared to the other applications.

Quote
You could probably parse the whole chart, and store a catalogue of tiles for quick access, and information for tiles in adjoining charts/files.
I don't quite understand. But if you mean how to access adjacent chart I store registered charts outlingings in registry and when I dblclick or the boat is near the new chart the new chart is loaded. I use API CreatePolygonRgn and PtInRegion.

Quote
The zoom level may also permit a great deal of information to be discarded. The display resolution is small relative to the whole chart.
Yes, but don't I still have to unpack the whole image to be able to zoom? For route planning reasons I zoom the whole chart into view with HALFTONE palette. This will slow down painting but I can live with that because the image is much better quality.

Quote
You can load the whole big image on a single DC without any problem, since it only render spesific location specified by you using bitblt.
Yes, no problem. This will reduce memory use but is still more than the other apps. And other functions will be worse.

Best regards
Title: Re: Advice on memory handling
Post by: Tedd on October 13, 2010, 10:40:40 AM
Keep the file mapping open, and cache pointers to the various sections in your own variables.
Now, consider the image to be split into a grid of squares ("tiles") - I know it's not, but that's how you'd like to be able to treat it - then you can update only the tiles you need as the map view shifts.
So, write yourself a function that takes (x,y) co-ordinates and returns an array of pixels for 'that' tile. In order to do this, you'll need to work out which rows are required, but that's quite simple. Once you have the row numbers (the file gives you offsets to their start), you can skip through the RLE data without actual decoding, just keep an accumulated count until you reach the required 'x' value, and then decode enough pixels to fill a tile row.
Once you've initially filled the area with tiles, keep those tiles in memory so they don't need to be decoded again. When you need to shift the view, you already have 90% of the data cached, and you only need to decode the new tiles that come into view - which will be much quicker.
Title: Re: Advice on memory handling
Post by: minor28 on October 13, 2010, 01:04:40 PM
Thank you,

I will test new methods based on your suggestions. The next few days I will be away from home so I am not sure when I can come back with a report
Title: Re: Advice on memory handling
Post by: oex on October 13, 2010, 01:36:54 PM
Quote
I would guess that the other applications are only loading and displaying a partial image as needed....
Quote
They probably load enough of the image to display, plus some extra around the edges to anticipate movement. Boats move relatively slowly ..
Quote
I have tryed. As I understand the image can only be devided in vertically parts. The whole row must be unpacked. If I unpack and load only a part of the image, dragging and zooming will be too slow compared to the other applications.

Maybe they resave the data in another format? Maybe in a temporary file.... Even if the whole vertical column is unpacked you wont fit 13,000 pixels on a screen.... On a 1280x1024 display you will fit 1280 pixels which means about a 6Mb image at 32 bit

Indexng each vertical column with a load of pointers will allow you to access only what you need

Quote
You could probably parse the whole chart, and store a catalogue of tiles for quick access, and information for tiles in adjoining charts/files.
Quote
I don't quite understand. But if you mean how to access adjacent chart I store registered charts outlingings in registry and when I dblclick or the boat is near the new chart the new chart is loaded. I use API CreatePolygonRgn and PtInRegion.

You could compress these tiles of the main image in memory also
Title: Re: Advice on memory handling
Post by: clive on October 13, 2010, 11:03:54 PM
Quote from: minor28
Quote
The zoom level may also permit a great deal of information to be discarded. The display resolution is small relative to the whole chart.
Yes, but don't I still have to unpack the whole image to be able to zoom? For route planning reasons I zoom the whole chart into view with HALFTONE palette. This will slow down painting but I can live with that because the image is much better quality.

Ok, If you zoom in fully you only have to process enough rows/rasters to fill the screen.

If you zoom out, you can basically skip dozens of rows/rasters for every one you display. Depends of course of you want to interpolate data from multiple rows into the visible one. Even then there is probably little reason to interpolate across all the rows.
Title: Re: Advice on memory handling
Post by: minor28 on October 19, 2010, 09:29:26 AM
Quote
If you zoom out, you can basically skip dozens of rows/rasters for every one you display. Depends of course of you want to interpolate data from multiple rows into the visible one. Even then there is probably little reason to interpolate across all the rows.

I have not had time to start with zooming yet. But how do I interpolate across rows?

I have been concentrated on handling the image in normal scale. First I keep the filemapping and I allocated memory for an imagesize equal to the client area and StretchDIBits direct to static window hDC. Painting worked well but moving did not. Jerky movements. Memory use, if I remeber right, about 9MB.

Next I allocated memory for an imagesize of three times the client area in a compatible DC and then BitBlt. Now I could move the image without jerky movements when moving within client area. To move the image I use WM_LBUTTONDOWN, WM_MOUSEMOVE and WM_LBUTTONUP. On WM_LBUTTONUP I uppdate the pixeldata from filemapping. However on next move the image jumps back to first position. Memory use about 24MB.

I have cleared the app from all but image handling to work with. Attached. Since us charts are free I have uploaded one chart to my homesite (http://www.bostream.nu/minor28/14929_1.zip) if anybody wants to try.

I have been struggling with attempts to minimize memory use without enough success. My first solution with more than 250MB memory use seems to be the solution for further work.
Title: Re: Advice on memory handling
Post by: minor28 on October 19, 2010, 07:57:19 PM
A few adjustments seems to work

In bsbChart4 proc:

;Allocate memory
.if pPixelData!=0
invoke HeapFree,hHeap,HEAP_NO_SERIALIZE,pPixelData
mov pPixelData,0
.endif

mov eax,yPos
neg eax
mov RowStart,eax
sub eax,FrameRectangle.bottom
.if sdword ptr eax>0
mov RowStart,eax
add eax,FrameRectangle.bottom
add eax,FrameRectangle.bottom
add eax,FrameRectangle.bottom
mov RowEnd,eax
.else
mov RowStart,0
mov eax,yPos
neg eax
add eax,FrameRectangle.bottom
add eax,FrameRectangle.bottom
mov RowEnd,eax
.endif
.if eax>imageH
mov eax,imageH
mov RowEnd,eax
.endif
sub eax,RowStart
mov PixH,eax

mov eax,xPos
neg eax
mov ColStart,eax
sub eax,FrameRectangle.right
.if sdword ptr eax>0
mov ColStart,eax
add eax,FrameRectangle.right
add eax,FrameRectangle.right
add eax,FrameRectangle.right
mov ColEnd,eax
.else
mov ColStart,0
mov eax,xPos
neg eax
add eax,FrameRectangle.right
add eax,FrameRectangle.right
mov ColEnd,eax
.endif
.if eax>imageW
mov eax,imageW
mov ColEnd,eax
.endif
sub eax,ColStart
mov PixW,eax


and in MainDlgProc:

.elseif eax==WM_LBUTTONUP
mov x,0
mov y,0
invoke bsbChart4
invoke CreateChart
Title: Re: Advice on memory handling
Post by: minor28 on October 20, 2010, 08:48:46 AM
If I save a kap image (size 6.4MB) as a tiff image (size 9.5MB) and open it in mspaint, memory use is 368,044kB and page file increases with 350MB. This makes me wonder if it is so bad to use 250MB of memory in my application. Any comments on that please?
Title: Re: Advice on memory handling
Post by: oex on October 20, 2010, 09:08:21 AM
QuoteThe image is about 13,000px wide and 9,200px high

Why are you loading the entire image? You cant display it all at once....

Maybe you could split it into 10x10 or even 100x100 smaller images and compress them all.... Then as the user moves about the view you can uncompress and display them in realtime

I have many apps I have written that use far more than 250Mb RAM :bg however it may be an issue if used on a computer with less than 512 Mb (or more) of RAM.... I guess it depends how you intend to use it.... If only ever on a laptop with 2Gb I wouldnt worry too much....

I havent spent too much time understanding kap format however my assumption is that you must be able to split it into 100x100 smaller kap files, store them end on end and keep a log of what *current image* X,Y the user is at....

This way you will use 40? times less RAM maybe and spend far less time in compression/decompression.... (At least far less time at any given time)....

A 1280x1024x4 image is about 5Mb only....
Title: Re: Advice on memory handling
Post by: minor28 on October 20, 2010, 11:30:38 AM
Yes I can display it all at once like this.


invoke GetClientRect,hWin,addr rect
push rect.bottom
pop imageH

finit
fild bsbW
fild bsbW
fidiv bsbH
fimul rect.bottom
fist imageW
fdivr
fstp zoomFactor
fwait

invoke GetBrushOrgEx,hCDC,addr pt
invoke CreateHalftonePalette,hCDC
invoke SetStretchBltMode,hCDC,HALFTONE
invoke SetBrushOrgEx,hCDC,pt.x,pt.y,0

invoke StretchDIBits,hCDC,0,0,imageW,imageH,
0,0,bsbW,bsbH,pPixelData,pBMI,DIB_RGB_COLORS,SRCCOPY
invoke GdiFlush


This will slow down painting but I can live with that because the image is much better quality and is used only for route planning purposes.

Kap format is header, pixeldata and a jump table. Splitting a kap file into 10,000 files. I don't know if that is the way. Then I think it is better to uncompress directly from filemapping and a sufficient part of the image to display.
Title: Re: Advice on memory handling
Post by: oex on October 20, 2010, 10:27:00 PM
Quote from: minor28 on October 20, 2010, 11:30:38 AM
Kap format is header, pixeldata and a jump table. Splitting a kap file into 10,000 files. I don't know if that is the way. Then I think it is better to uncompress directly from filemapping and a sufficient part of the image to display.

I thought you had said there were format issues that prevented you from uncompressing a portion of an image beforehand.... Simply you want only to have decompressed the screen map drawing dimensions eg 1280x1024 plus a small margin for scrolling so there is no decompression delay.... What you do not want is to be drawing a massive uncompressed image....

If you are able to uncompress just a portion then as I suggested before (but without compressing into seperate files) split the image logically into many pieces and display the relevent ones for the viewable area....
Title: Re: Advice on memory handling
Post by: minor28 on October 22, 2010, 09:53:43 AM
Yes you are right. I have been consentrating on memory too much. I have succeeded to improve the unpacking algo. Now the memory use is less than 9MB and nothing on page file when painting on only the client area. On a 24" screen it is some jerky movements, but acceptable.

I run it on an 333 Mhz 128MB RAM laptop. The image is loaded without delay. Movements are jerky but almost acceptable. Memory use 6,140KB

If I encrease the image height and width three times the loading on the laptop is some delayed but movements are jerky free. Memory use 16,680MB.

I think something in between will be the best solution.

Thank you all for your help.