Long loading time under MacOS when multithreading

Community Forums/General Help/Long loading time under MacOS when multithreading

Ravl(Posted 2014) [#1]
I am working at a new game and until now I tested only on Windows. I recently bought myself a Macbook and I wanted to give it a try.

I observed I have a very long loading time for my menu. Right now the menu is made from 2-3 pictures and I stay up to 20-30 seconds..

This is my main game code: (deleted some irrelevant code)

Type TGame

	Field imageTitleScreen:TImage
	Field LoadingThread:TThread
	
	Field myLogo:TLogo
	Global myMenu:TMainMenu
	Global myScene:TScene
	
	Method debugPrint(pString:String)
		If Globals.isDebug = 1
			Print pString
		End If
	EndMethod	
	
'#region Loading and Setup Game

	Function threadLoadMainMenu:Object(Data:Object)
		
		myMenu.loadGraphics()	
		
	End Function

	
		
'#endregion
	
	Method initGame()
		myLogo = New TLogo
		myLogo.init(MilliSecs())		
		myMenu = New TMainMenu
		myMenu.init()
		myScene = New TScene
		myScene.init()
		
		imageTitleScreen = LoadImage("graphics/titleScreen.jpg")
		
	End Method

	Method updateGame(gameTime:Float)
	
		If Globals.appStatus = STATUS_LOGO
			myLogo.updateLogo(gameTime)
			If myLogo.status = 6 switchGameState(STATUS_TITLESCREEN)
			
		ElseIf Globals.appStatus = STATUS_TITLESCREEN
		
			If myMenu.threadSafe = 1
				If MouseDown(1)
					switchGameState(STATUS_MENU)					
				End If
			End If
			
		ElseIf Globals.appStatus = STATUS_MENU
		
			myMenu.update()
			checkMainMenuResponses()
		
		ElseIf Globals.appStatus = STATUS_GAME
				
			myScene.update()
		
		End If
		
		
	End Method

	Method renderGame(gameTime:Float)
	
		If Globals.appStatus = STATUS_LOGO
		
			myLogo.renderLogo()
			
		ElseIf Globals.appStatus = STATUS_TITLESCREEN
		
			If LoadingThread
				If ThreadRunning(LoadingThread)
					DrawImage(imageTitleScreen, 0, 0)
					DrawText "Loading Main Menu (in another thread)", 10, 10
				Else
					LoadingThread = Null
				EndIf				
			Else
				DrawImage(imageTitleScreen, 0, 0)
				DrawText "Click anywhere to continue...", 10, 10
			End If
	
		ElseIf Globals.appStatus = STATUS_MENU
		
			myMenu.render()
			
		ElseIf Globals.appStatus = STATUS_GAME
				
			myScene.render()
			
		End If				
		
		DrawText(gameTime, 10, 20)
		
	End Method
	
	Method switchGameState(pNewState:Int)
		Globals.appStatus = pNewState
		
		If pNewState = STATUS_TITLESCREEN			
			LoadingThread = CreateThread(threadLoadMainMenu, Null)
		
			debugPrint("TGame->switchGameState: STATUS_TITLESCREEN")
		ElseIf pNewState = STATUS_MENU
		
			myMenu.swithMenuState(MENU_STATUS_MAIN)
			debugPrint("TGame->switchGameState: STATUS_MENU")
			
		ElseIf pNewState = STATUS_GAME
		
			'myMenu.swithMenuState(MENU_STATUS_MAIN)
			debugPrint("TGame->switchGameState: STATUS_GAME")
			
		End If
						
	End Method
	
	Method checkMainMenuResponses()
	
		If myMenu.responseMustStartGame = 1
		
			myScene.reset()
			myScene.LoadScene()
			myScene.loadGraphics()
			switchGameState(STATUS_GAME)
			
		End If
		
	End Method
	
End Type


If I am not using multithread then the loading is fast as on Windows OS.

Where is my mistake?


AdamStrange(Posted 2014) [#2]
shouldn't be any different as far as I can see.

I'm willing to compile and test here if you want? (mavericks, mac mini)


Ravl(Posted 2014) [#3]
Ok, I will share this with you guys:
http://rvlgames.com/beta2/testBuild.zip

When the mainmenu is loaded in the top left corner will be printed: Loading Main Menu graphics in another thread.

i tested it on maverick


AdamStrange(Posted 2014) [#4]
no problem. Initial test runs it very fast with no pauses for loading.

would you like me to do a full debug and suggest changes to make more mac friendly?


Ravl(Posted 2014) [#5]
sure, but i cannot understand why here is not working fine...


AdamStrange(Posted 2014) [#6]
ok first a couple of coding changes

- mac doesn't use 'esc', also better to use a timer for your loop so you can multitask better. Here's the code for that with a fps of 50 :)

main.bmx/main game loop:
Local AppQuit:Int = False
Local GameTime:Int
Local AppQueue:Int
Local TimeEvent:TTimer = CreateTimer(50)

While Not(AppQuit)
	GameTime = MilliSecs()
	
	AppQueue = PollEvent()
	If AppQueue = 0 Then
		Delay(2)
	Else
		Select EventID()
		Case EVENT_APPTERMINATE, EVENT_WINDOWCLOSE 
			AppQuit = True
		Case EVENT_TIMERTICK 
			GameTime = MilliSecs()
			Cls
			myGame.updateGame(GameTime)
			myGame.renderGame(GameTime)
			Flip
		end Select		
	End If
Wend


- quick mod to stop fullscreen and use a window (better for debugging)
main.bmx/setupGraphics:
	Globals.mySettings.videoWidth = 960'DesktopWidth()
	Globals.mySettings.videoHeight = 640'DesktopHeight()	


quick scan of game code, here is a code suggestion. replace multiple if then with select

game.bmx/renderloop
		Select status
			Case 0, 1, 2
				DrawImage (logo1, 0, 0)

			Case 3, 4, 5
				DrawImage (logo2, 0, 0)
			
		End Select



on to the thread pause issue. work fine here, so I would suggest running in a window and putting
print "start procedure name"
print "end procedure name"
into main parts and watch the output window - that way you can see where the code has stalled


Ravl(Posted 2014) [#7]
Thanks for the tips:

I put some additional Prints in my program. You have the code so here it is what is strange:

game.bmx -> Function threadLoadMainMenu

this is my code here:
Function threadLoadMainMenu:Object(Data:Object)
     Print "start 1"
     myMenu.loadGraphics()
     Print "finish 1"
End Function


I have a very big time here between "start 1" and "finish 1".

BUT the myMenu.loadGraphics() it's starting very hard. (I also put prints there and all the graphic are loaded instantaneously)

Now, in the Game.bmx: swithGameState() I have this code:
Globals.appStatus = pNewState
If pNewState = STATUS_TITLESCREEN
     LoadingThread = CreateThread(threadLoadMainMenu, Null)

     Print("TGame->switchGameState: STATUS_TITLESCREEN")
Else If
.
.
.


If I leave that Print there in my debug window I will have:

TGasmtea-rts w1tc

finish 1


so instead of "start 1", my print functions are one in the other.. I think here is something wrong. o.O

again, If I am not using multithread then the loading is fast as on Windows OS.


Ravl(Posted 2014) [#8]
Any new thoughts guys??


AdamStrange(Posted 2014) [#9]
I'll have a look see :)

mmm, first off in
myMenu.loadGraphics()

you have profile loading code. in the copy I have here, this is commented out. have you done the same on your machine and checked it?

If so (commented out) and there is no delay, then the pause is in the profile loading (I have no pause here with just the image loading)


I've uncommented the load profile code, and still no pause.


ok. one thing. you have a set of loading screens that pause for a certain amount of time. And these happen only once.
If you can't find a way around it, why not split the loadGraphics() amongst these. that way if there is a pause, it will be absorbed ?


Ravl(Posted 2014) [#10]
Hi again Adam,

As I said earlier in the myMenu.loadGraphics() method, everything is super fast. If I put a Print at the start of the method and another one at the end of it, both debugs will be printed almost same time.

The problem is that the myMenu.loadGraphics() is starting VERY HARD.

the profile code is commented cause i doen some tests. runs the same because i said earlier the loadGraphcis itself is fast.

also, u asked me why cant me load the graphics while in logos. well, i want to load them after these logos while a Title Screen will be shon. anyway I want to understand were is my mistake and why u ran the app with no issues and here i have all these problems.

again, if i do not use multithreading the loading is super fast as on Windows. as you can see in my previous post it seems that
switchGameState is called several times while starting the thread LoadMainMenu.. that;s why I have my print output combined....


AdamStrange(Posted 2014) [#11]
myMenu.loadGraphics() is starting VERY HARD.

I've got my stupid head on there. I don't understand what you mean?

I think you mean late??

ok, here's a question for you. what spec is your processor? Mine is a dual core with hyper threading (in effect, 4 cpus) - you can check this by running Application > Utilities > Activity monitor - the amount of CPu's will be shown on the App bar

If you have 2 or 1, then the pause may be to do with having too many threads?

Threading is the one thing that is incredibly difficult to debug.


Ravl(Posted 2014) [#12]
"I think you mean late??" -> Yes.

I have a i5 Haswell 2.4 Ghz
In the Activity Monitor I have 7-8 threads

Another strange thing. I put a DebugStop here:

If pNewState = STATUS_TITLESCREEN
     LoadingThread = CreateThread(threadLoadMainMenu, Null)
     DebugStop
     Print("TGame->switchGameState: STATUS_TITLESCREEN")
Else If
.
.


Well, in this case runs smoothly. I mean, as soon as the logos disappear, the loading menu thread is starting, the app stops (but the graphics are loaded). i just click to resume and it's ok.. :|


AdamStrange(Posted 2014) [#13]
mmm, sounds like some form of race condition where the main threads waiting for other threads to catch up.

the debugstop is interesting though. have you grid replacing it with a delay()?
say delay(1000) - 1 second delay. and see what happens?

delay will give some time back to the system...


Ravl(Posted 2014) [#14]
the delay trick made it run good.. :D

what is happen exactly?


AdamStrange(Posted 2014) [#15]
Hey, brilliant :)

interesting that it worked so well. Here's my thought;

requesting a thread is a low level system operation, so giving some time back to the system allowed it to do its work and tell blitz it had done it.

:)