bah.cairo advice needed
BlitzMax Forums/Brucey's Modules/bah.cairo advice needed
| ||
I have this bit of code that draws 2 paths that represent a golf shot's top view trajectory (a main path and a simulated drop shadow). I've got it running in a loop at the moment to test it out with some random control points. The problem I am having is that it is chewing up memory big time. FYI my app is a MaxGui App running at 2160x1920, fullscreen over 2 1920x1080 monitors in Nvidia DualView, rotated 90 degrees. Whew, got that? In this case, the paths are being drawn on the second monitor. relavant code: Cls Local cairo:TCairo = TCairo.Create(TCairoImageSurface.CreateForPixmap(1080,1920)) Local normalizeMat:TCairoMatrix = TCairoMatrix.CreateScale(1080.0,1920.0) cairo.SetMatrix(normalizeMat) cairo.SetLineWidth(0.04) Local offx2:Double = Rnd(0.8) Local offx3:Double = Rnd(0.8) cairo.SetSourceRGBA(0.7,0.7,0.7, 0.6) cairo.CurveTo(0.49, 0.89, offx2, 0.56, offx3, 0.35) cairo.Stroke() cairo.SetSourceRGB(1.0,1.0,1.0) cairo.CurveTo(0.50, 0.89, offx2 + 0.01, 0.56, offx3 + 0.01, 0.35) cairo.Stroke() cairo.Destroy() Local image:TImage = LoadImage(TCairoImageSurface(cairo.getTarget()).pixmap()) DrawImage image,1080,0 flip As far as I can tell, the LoadImage command is the issue. Any idea on how to keep this from turning my PC to goo? |
| ||
I wonder if it could be related to this: http://www.blitzbasic.com/Community/posts.php?topic=86916#985353 |
| ||
since your doing a LoadImage every single loop, the problem likely is that you collect a ton of data before the garbage collector gets around to running. Once blitzmax has allocated memory, it will never release it back to the system until the application is ended - the GC will only mark it as available for blitzmax to re-use, but never shrink. You may want to disable the automatic GC, and manually run it each loop, at least that way you may catch things before the memory usage spirals out of control. Alternatively, you could try to explicitly release the image:timage before loading another image into it and see if that makes any difference... Or see if the behaviour is any different if you compile your application with the threaded mode enabled, to force the use of the alternate garbage collector, just in case that makes a difference. |
| ||
Thanks xlsior. What is the recomended way to release a timage resource? |
| ||
I tried the things mentioned above with no luck - it still gobbles up memory. I can get around the problem by doing this:DrawPixmap(TCairoImageSurface(cairo.getTarget()).pixmap(),1080,0) instead of: Local image:TImage = LoadImage(TCairoImageSurface(cairo.getTarget()).pixmap()) DrawImage image,1080,0 No more memory problem but the pixmap ends up being the paths draws on a black background, thus covering up the background image shown below it. Is there a way for the pixmap background to be transparent so that it only shows the paths? |
| ||
Ouch. DrawPixmap is quite inefficient. As for releasing memory, you can always call GCCollect(), which should force BlitzMax to free any memory for unreferenced objects. Is there a way for the pixmap background to be transparent so that it only shows the paths? Hmmm. Possibly. I'll need to look into it. |
| ||
Seems it already works that way. Try this modified arc example (which simply has some text drawn before the image) : Notice how the image is transparent ;-) What if you don't fill the background rectangle? |
| ||
I tried GCCollect(), did not work. Then again, I might have implemented it wrong. I did this after Flip. image = null GCCollect() |
| ||
That example does work. Maybe mine does not because I am drawing on a MaxGui canvas? SetBlend ALPHABLEND crashes my app if I stick it in there. |
| ||
Maybe mine does not because I am drawing on a MaxGui canvas? Ah. Any graphics related commands can only be run once you have a valid context. So you might want to run some initialisation stuff against the canvas graphics context the first time you make it active. But otherwise, it shouldn't crash. I would have expected GCCollect to work... Maybe something else holds a reference to the TImage (and its embedded TPixmap object). |
| ||
Hmm, I see that SetBlend isn't the issue. How do you not fill the background rectangle? EDIT: I should clarify - if I use LoadImage, the background is transparent, works fine - but I get the memory problem. I have to use LoadImage in the main loop like this because I need to calculate and draw a new path while the program is running. |
| ||
I assume your original code snippet has the issue you describe? |
| ||
The memory issue, yes. |
| ||
Here is the entire code.Import maxgui.Drivers Import blide.fontmachine Import BaH.Cairo Local Window1:TGadget = CreateWindow:TGadget("Window1",10,20,2160,1920,Null,Null) 'MaximizeWindow( Window1:TGadget ) SetWindowFullscreen(Window1) Local canvas:TGadget=CreateCanvas(0,0,2160,1920,Window1) SetGadgetSensitivity(Window1, SENSITIZE_KEYS) Local fnt1:TBitmapFont 'We create the object, loading the font samplefont.fmf fnt1 = LoadBitmapFont("38pt.fmf") 'background Global background:TImage = LoadImage("back.jpg") CreateTimer(60) Local oldTime:Int = MilliSecs() Local newTime:Int While True WaitEvent() ActivateGadget(canvas) Select EventID() Case EVENT_TIMERTICK RedrawGadget canvas Case EVENT_GADGETPAINT SetGraphics CanvasGraphics(canvas) newTime = MilliSecs() If newTime > oldTime + 1000 Local yds1:Int = Rnd(0,300) Local yds2:Int = Rnd(0,300) Local yds3:Int = Rnd(0,300) Local yds4:Int = Rnd(0,300) Local yds5:Int = Rnd(0,300) Local yds6:Int = Rnd(0,300) Local yds7:Int = Rnd(0,300) Local yds8:Int = Rnd(0,300) Local yds9:Int = Rnd(0,300) Cls DrawSomeStuff() Local cairo:TCairo = TCairo.Create(TCairoImageSurface.CreateForPixmap(1080,1920)) Local normalizeMat:TCairoMatrix = TCairoMatrix.CreateScale(1080.0,1920.0) cairo.SetMatrix(normalizeMat) cairo.SetLineWidth(0.04) Local offx1:Double = Rnd(0.8) Local offx2:Double = Rnd(0.8) Local offx3:Double = Rnd(0.8) cairo.SetSourceRGBA(0.7,0.7,0.7, 0.6) cairo.CurveTo(0.49, 0.89, offx2, 0.56, offx3, 0.35) cairo.Stroke() cairo.SetSourceRGB(1.0,1.0,1.0) cairo.CurveTo(0.50, 0.89, offx2 + 0.01, 0.56, offx3 + 0.01, 0.35) cairo.Stroke() cairo.Destroy() Local image:TImage = LoadImage(TCairoImageSurface(cairo.getTarget()).pixmap()) DrawImage Image,1080,0 Local data1:String = yds1 + " mph" Local data2:String = yds2 + " mph" Local data3:String = yds3 + " °" Local data4:String = yds4 + " null" Local data5:String = yds5 + " rpm" Local data6:String = yds6 + " °" Local data7:String = yds7 + " null" Local data8:String = yds8 + " yds" Local data9:String = yds9 + " sec" fnt1.DrawText(data1, 874 - (fnt1.GetTxtWidth(data1)/2), 460) fnt1.DrawText(data2, 874 - (fnt1.GetTxtWidth(data2)/2), 610) fnt1.DrawText(data3, 874 - (fnt1.GetTxtWidth(data3)/2), 770) fnt1.DrawText(data4, 874 - (fnt1.GetTxtWidth(data4)/2), 932) fnt1.DrawText(data5, 874 - (fnt1.GetTxtWidth(data5)/2), 1076) fnt1.DrawText(data6, 874 - (fnt1.GetTxtWidth(data6)/2), 1236) fnt1.DrawText(data7, 874 - (fnt1.GetTxtWidth(data7)/2), 1400) fnt1.DrawText(data8, 874 - (fnt1.GetTxtWidth(data8)/2), 1550) fnt1.DrawText(data9, 874 - (fnt1.GetTxtWidth(data9)/2), 1712) Flip image = Null oldTime = MilliSecs() EndIf Case EVENT_WINDOWCLOSE FreeGadget canvas End Case EVENT_APPTERMINATE End End Select Wend Function SetWindowFullscreen:Int(Window:TGadget) Extern "Win32" Function SetWindowLongA:Int(hWnd:Int, nIndex:Int, dwNewLong:Int)="SetWindowLongA@12" Function SetLayeredWindowAttributes(hWnd:Int, temp:Int, alpha:Int, buh:Int)="SetLayeredWindowAttributes@16" Function GetWindowLongA:Int(hWnd:Int, nIndex:Int)="GetWindowLongA@8" Function MoveWindow:Int(hwnd:Int, x:Int, y:Int, nWidth:Int, nHeight:Int, bRepaint:Int)="MoveWindow@24" Function GetSystemMetrics:Int (nIndex:Int) = "GetSystemMetrics@4" Function SetWindowPos:Int (hwnd:Int, hWndInsertAfter:Int, x:Int, y:Int, cx:Int, cy:Int, wFlags:Int) = "SetWindowPos@28" End Extern Local hWnd:Int Local Style:Int hWnd = QueryGadget(Window, QUERY_HWND) If Not hWnd Return False EndIf 'Style = GetWindowLongA(hWnd, GWL_STYLE) 'Style:|WS_EX_LAYERED Const SM_XVIRTUALSCREEN:Int =76 Const SM_YVIRTUALSCREEN:Int =77 Const SM_CXVIRTUALSCREEN:Int =78 Const SM_CYVIRTUALSCREEN:Int =79 Const SM_CMONITORS:Int =80 Const SM_SAMEDISPLAYFORMAT:Int =81 Style = WS_VISIBLE Local window_w:Int = GetSystemMetrics(SM_CXVIRTUALSCREEN) Local window_h:Int = GetSystemMetrics(SM_CYVIRTUALSCREEN) ReleaseCapture() SetWindowLongA(hWnd, GWL_STYLE, Style) MoveWindow(hWnd, (GetSystemMetrics(SM_CXVIRTUALSCREEN) - window_w) / 2, (GetSystemMetrics(SM_CYVIRTUALSCREEN) - window_h) / 2, window_w, window_h, 1) Return True End Function Function DrawSomeStuff() DrawImage(background,0,0) End Function |
| ||
Well, running this code, you can see that Cairo is not the problem : :-) |
| ||
After your "SetGraphics CanvasGraphics(canvas)", try addingSetBlend ALPHABLEND |
| ||
Your Cairo Mod is definitely not the issue. I just need a way to draw a new path in my main loop, often, with alpha, without blitzing the memory. I put in SetBlend ALPHABLEND as you suggested but no dice. Doesn't work if I draw the Pixmap as opposed to using LoadImage,then drawing the image. |
| ||
Brucey, I have a work around in mind and have a question. I want to use DrawPixmap because it does not chew up the memory (note the cairo clock demo has the memory issue by the way though on a smaller scale than mine). Since I can't get the background of the pixmap to be transparent for some reason, I thought I might do this: create the cairo context first load the background into a pixmap and draw it with cairo then draw the paths then draw the pixmap So: Local cairo:TCairo = TCairo.Create(TCairoImageSurface.CreateForPixmap(1080,1920)) Local normalizeMat:TCairoMatrix = TCairoMatrix.CreateScale(1080.0,1920.0) cairo.SetMatrix(normalizeMat) cairo.SetLineWidth(0.04) Local photo:TPixmap = LoadPixmap("rt_back.jpg") Local photosurf:TCairoSurface = TCairoImageSurface.CreateFromPixmap(photo) cairo.Scale(1.0/PixmapWidth(photo), 1.0/PixmapHeight(photo)) cairo.SetSourceSurface(photosurf, 0, 0); cairo.Paint() Local offx1:Double = Rnd(0.8) Local offx2:Double = Rnd(0.8) Local offx3:Double = Rnd(0.8) cairo.SetSourceRGBA(0.7,0.7,0.7, 0.6) cairo.CurveTo(0.49, 0.89, offx2, 0.56, offx3, 0.35) cairo.Stroke() cairo.SetSourceRGB(1.0,1.0,1.0) cairo.CurveTo(0.50, 0.89, offx2 + 0.01, 0.56, offx3 + 0.01, 0.35) cairo.Stroke() cairo.Destroy() photosurf.Destroy() DrawPixmap(TCairoImageSurface(cairo.getTarget()).pixmap(),1080,0) It almost works but the paths do not show up. Any idea? I'm thinking I need to create something to draw the paths on after the photo but I am not sure. |
| ||
Just wanted to update my progress - It's working now. I had to switch to 1.34 (was using 1.35). Then I needed to add GCCollect() after setting the image to NULL. Lastly, I had to use the OpenGL driver because 1.34 was giving me an unknown DXError. I am running a stress test on the app now but so far it all looks good. If all goes well then the app will be part of the set for a TV show coming next year! |
| ||
I wonder what's changed between 1.34 and 1.35 on Windows.... |
| ||
I'm looking at this: "Major news is the addition of an official d3d9 max2d driver." |
| ||
I suppose that's a possibility, assuming that the dx9 driver is the default? Have you tried OpenGL on 1.35 ? |
| ||
I don't think I did. I can try it a bit later. Would be interesting to know if it works on 1.35 fine by using OpenGL. |
| ||
Interesting! 1.35 works as long as I use the OpenGL driver and as long as I collect the garbage after I release the image. |
| ||
So... a possible bug in whatever is the default dx driver, perhaps? |
| ||
Looks that way. The DX7 driver in 1.34 works fine with LoadImage() in your main loop with GCCollect() as well. I had a different problem related to my app being displayed over 2 monitors on 1.34, which is what prompted me to try OpenGL. But it looks like: 1.35 + DX + LoadImage() in main loop = bad mojo |