Canvas EventType_Render and less memory usage

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Canvas EventType_Render and less memory usage

Post by Justin »

Why the canvas gadget does not have a EventType_Render event? It would make things much easier to let the os notify when the canvas needs to be drawn instead of having to guees it.

I also noticed it consumes a lot of memory, i know is double buffered but a canvas at fullscreen at 4k resolution takes 40MB. I did some tests double buffering using gdi plus directly an it just uses 4MB.
Fred
Administrator
Administrator
Posts: 18220
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Canvas EventType_Render and less memory usage

Post by Fred »

What do you mean by guess it ? The canvas is automatically refreshed from the back image on the WM_PAINT event, so you don't have to worry about that. About the memory size, a 4k pic is 3840x2160x4 bytes so about 33MB. How did you have 4MB only meme usage with GDI + ?
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Re: Canvas EventType_Render and less memory usage

Post by Justin »

Hi Fred,

what i mean with guess it is that now we have to call a canvas draw function every time is created, resized, shown, etc..
If there was a render event we had to call it only inside that render event. The system notifies you when it needs to be drawn instead of you having to decide when to draw, basically what you do when you use the api directly when you handle the WM_PAINT.

About the memory usage this code takes only 1.6MB at fullscreen on 4k and it's double buffered.
I am missing something?

Code: Select all

;GDIPDoubleBuffer.pb

EnableExplicit

Import "gdiplus.lib"
	GdipFillRectangle(graphics.i, brush.i, x.f, y.f, width.f, height.f)
	GdipCreateFromHDC(hdc.i, graphics.i)
	GdipGraphicsClear(graphics.i, color.l)
	GdipCreateSolidFill(color.l, brush.i)
	GdipDeleteBrush(brush.i)
	GdipDeleteGraphics(graphics.i)
	GdiplusStartup(token.i, input.i, output.i)
	GdiplusShutdown(token.i)
EndImport

;- GdiplusStartupInput
Structure GdiplusStartupInput Align #PB_Structure_AlignC
	GdiplusVersion.l          
	DebugEventCallback.i
	SuppressBackgroundThread.l
	SuppressExternalCodecs.l
EndStructure

#AlphaShift  = 24
#RedShift    = 16
#GreenShift  = 8
#BlueShift   = 0

#AlphaMask   = $ff000000
#RedMask     = $00ff0000
#GreenMask   = $0000ff00
#BlueMask    = $000000ff

Macro ARGB(a, r, g, b)
	(((b) << #BlueShift) | ((g) << #GreenShift) | ((r) << #RedShift) | ((a) << #AlphaShift))
EndMacro

;- _APP
Structure _APP
	win.i
	cont.i
	contOldProc.i
EndStructure
Global._APP app

Procedure.i cont_onPaint(hwnd.i, msg.l, wparam.i, lparam.i)
	Protected.PAINTSTRUCT ps
	Protected.i hdc, memDC, memBM, memOldObj, memGraphics
	Protected.i brush, pen
	Protected.RECT rc
	Protected.l color1, color2
	Protected.l rectSize
	
	hdc = BeginPaint_(hwnd, @ps)
	
	;Create memory graphics
	GetClientRect_(hwnd, @rc)
	memDc = CreateCompatibleDC_(hdc)
	memBM = CreateCompatibleBitmap_(hdc, rc\right, rc\bottom) 
	memOldObj = SelectObject_(memDC, memBM)
	GdipCreateFromHDC(memDc, @memGraphics)

	;Draw to memory graphics
	color1 = ARGB(255, 255, 0, 0)
	color2 = ARGB(255, 0, 0, 255)
	rectSize = DesktopScaledX(200)
	
	GdipGraphicsClear(memGraphics, color2)
	
	GdipCreateSolidFill(color1, @brush)

	GdipFillRectangle(memGraphics, brush, (rc\right / 2) - (rectSize / 2), (rc\bottom / 2) - (rectSize / 2), rectSize, rectSize)
	
	;Copy to dc
	BitBlt_(hdc, 0, 0, rc\right, rc\bottom, memDc, 0, 0, #SRCCOPY)

	;Release
	SelectObject_(memDc, memOldObj)
	DeleteObject_(memBm)
	DeleteDC_(memDc)
	GdipDeleteBrush(brush)
	GdipDeleteGraphics(memGraphics)
	
	EndPaint_(hwnd, @ps)
	
	ProcedureReturn 0
EndProcedure

Procedure.i cont_proc(hwnd.i, msg.l, wparam.i, lparam.i)
	Select msg
		Case #WM_ERASEBKGND : ProcedureReturn 1
		
		Case #WM_PAINT : ProcedureReturn cont_onPaint(hwnd.i, msg.l, wparam.i, lparam.i)
		
	EndSelect
	
	ProcedureReturn CallWindowProc_(app\contOldProc, hwnd, msg, wparam, lparam)
EndProcedure

Procedure win_onSize()
	ResizeGadget(app\cont, 0, 0, WindowWidth(app\win), WindowHeight(app\win))
EndProcedure

Procedure main()
	Protected.l ev
	Protected.GdiplusStartupInput su
	Protected.i gdipToken
	
	;Init gdip
	su\GdiplusVersion = 1
	su\DebugEventCallback = #Null 
	su\SuppressBackgroundThread = #False
	su\SuppressExternalCodecs = #False
	GdiplusStartup(@gdipToken, @su, #Null)
	
	app\win = OpenWindow(#PB_Any, 10, 10, 600, 400, "Test", #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)
	app\cont = ContainerGadget(#PB_Any, 0, 0, 0, 0)
	app\contOldProc = SetWindowLongPtr_(GadgetID(app\cont), #GWLP_WNDPROC, @cont_proc())
	
	BindEvent(#PB_Event_SizeWindow, @win_onSize())
	win_onSize()
	
	Repeat
		ev = WaitWindowEvent()
		
	Until ev = #PB_Event_CloseWindow
	
	GdiplusShutdown(gdipToken)
EndProcedure

main()
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Canvas EventType_Render and less memory usage

Post by jacdelad »

I don't understand your intention. You have to call your drawing function anyway. The internal drawing process makes sure the current picture is redrawn when needed, and if the canvas is resized it's clear that you have to react to adapt the picture to the new size. How would the internal function know what to draw?
Also there's no difference between manually calling your drawing function or binding it with BindGadgetEvent (I know there's some difference, butt this does not matter here). You have to call your drawing function anyway.
So, why the need for a custom reaction on the repaint notification?
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Re: Canvas EventType_Render and less memory usage

Post by Justin »

For me is more logical being notified when to draw than having to draw after resizing for example, but PB uses a different drawing model and it won't change, no problem.

But what about the memory usage?
With the previous example at 4k fullscreen:
CanvasGadget 25MB
Gdip api 1.6MB
mestnyi
Addict
Addict
Posts: 1098
Joined: Mon Nov 25, 2013 6:41 am

Re: Canvas EventType_Render and less memory usage

Post by mestnyi »

Justin wrote: Sat Sep 28, 2024 3:22 pm For me is more logical being notified when to draw than having to draw after resizing for example
I think so too.
Fred
Administrator
Administrator
Posts: 18220
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Canvas EventType_Render and less memory usage

Post by Fred »

Your are drawing directly on the window's buffer that's why it doesn't take extra memory, but for every WM_PAINT event, you will have to redraw all your picture from scratch which can be very time consuming (or you need a backbuffer to store your pic, like the canvas does for really fast redraw).
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Canvas EventType_Render and less memory usage

Post by jacdelad »

@Justin, @mestnyi: I can't follow. The redraw functions do e internally just repaint, what's already put on the canvas. Why should I take control? I just need to repaint, e.g. if the size changes, because the content changes. If there was a window above my canvas and now not anymore, the repaint just paints again what's already on the canvas. This is a serious question: Why should I do this myself?
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Canvas EventType_Render and less memory usage

Post by spikey »

Justin wrote: Sat Sep 28, 2024 12:28 pm About the memory usage this code takes only 1.6MB at fullscreen on 4k and it's double buffered.
I am missing something?
Just where are you getting your memory usage readings? Task manager? This isn't entirely trustworthy. It tells the truth but not necessarily the whole truth.

I ran your code on my laptop (which doesn't have a 4k display) and it's using about 40Mb according to Process Explorer but Task Manager only reports 3Mb.

The GDI code lives in a shared DLL. Consequently, its memory allocations may not show as private to your process because they may be being shared across multiple processes.

However, the canvas gadget is a PB specific library and is statically linked into the process. It will only be used by your program so its allocations will be entirely attributable to the parent process and will be reported accordingly.

I suspect that what you're seeing as a difference in memory is that in which the system is unable to definitively assign to a single process because its being shared, or is being reported somewhere else. On my machine the memory consumption of dwm.exe peaks and drops when I start and stop your process. This is the "Desktop Window Manager" process, so this doesn't surprise me.
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Re: Canvas EventType_Render and less memory usage

Post by Justin »

@Fred,
if it's a matter of speed i would prefer less speed and less ram, i don't think i would notice anything in a nowadays computer when drawing a custom gadget. 25mb for drawing a simple window is to much imo, just a personal choice.
How about a flag #PB_Canvas_NoBackBuffer to use the method i posted?

@spikey,
where did you read 40mb?
This is what process explorer showed here
Image
test is gdip 1988 + 11348 = 13336
test2 is canvas 26316 + 40896 = 67212

@jacdelad,
pb uses a different drawing model, the point is not having to redraw after resizing, if you look at my explample i had to do nothing when i resized the container it was redrawn automatically becasuse the system called wm_paint. But like i said pb uses another system, it's ok.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Canvas EventType_Render and less memory usage

Post by spikey »

Justin wrote: Sat Sep 28, 2024 7:24 pm @spikey,
where did you read 40mb?
Process Explorer, as well but I wouldn't worry if that's higher than you're expecting. This machine has a broken BitDefender installation and I'm procrastinating about doing an OS re-install because its really time consuming. I didn't think properly about the implications of that before posting, I shouldn't have quoted specific values. Sorry!

My point was not to fall into the taking Task Manager at face value trap (but you haven't) and that you may not be seeing the entire memory load when using shared DLLs and so may not be comparing apples with apples entirely. Application memory usage is more complicated than most people expect.
But if you already know how to use Process Explorer I'm probably not telling you anything you didn't already know!
Post Reply