Notifications
Clear all

[Closed] Background worker updating meshes

I’ve read that it’s impossible to update meshes using a background worker thread.
I contest!


 (	--updating meshes in the viewport from a background thread
 	resetMaxFile #noPrompt; clearListener()
 	struct agentStruct
 	(
 		sceneNode, --the node that the agentStruct is associated with (could be tinyCtrl)
 		desiredTarget, --the target the agent wants to get to, could be a node or position or undefined
 		movementSpeed --how quickly the agent moves, 0.0 - 1.0
 	)
 	
 	sliderTime = 0
 	local agentArray = #()
 	--randomly scatter 10 agents, represented as boxes
 	for i=1 to 10 do
 	(
 		myBox = box length:5 width:5 height:20
 		myBox.pos = [(random -200 200),(random -200 200),0]
 		local myAgent = agentStruct sceneNode:myBox desiredTarget:[0,0,0] movementSpeed:0.1
 		append agentArray myAgent
 	)
 	
 	--simulate game cycles
 	local numOfGameCycles = 100 --how many times do we sim the game? this also correlates to frames, as every game cycle = a frame
 	local episilon = 2 --this is the tolerance, decrease to increase accuracy, increase to decrease accuracy
 	fn alongPoint pA pB prop = (pA+((pB-pA)*prop))	--this returns a position between two points, ex: 0.5 would return halfway
 	animationRange = interval 1 numOfGameCycles --set the timeline up
 	fn updateAgent agentSceneNode newPos = (agentSceneNode.pos = newPos)
 	fn incrementTime = (sliderTime += 1)
 	
 	fn gameCycle =
 	(
 		while sliderTime != numOfGameCycles do 
 		(
 			incrementTime()
 			for j=1 to agentArray.count do
 			(
 				--MOVE PHASE
 				--has the agent reached the desiredTarget? (within episilon)
 				if (abs(agentArray[j].sceneNode.pos.x-agentArray[j].desiredTarget.x)<episilon) and (abs(agentArray[j].sceneNode.pos.y-agentArray[j].desiredTarget.y)<episilon) then 
 				(
 					if agentArray[j].desiredTarget == [0,0,0] then --set a random desired target
 					(  
 						agentArray[j].desiredTarget = [(random -200 200),(random -200 200),0]
 					) else ( --reset the desiredTarget to 000
 						agentArray[j].desiredTarget = [0,0,0]
 					)
 				) else (
 					--move the agent by the agent's movement speed toward the desired target
 					local agentNextPos = alongPoint agentArray[j].sceneNode.pos agentArray[j].desiredTarget agentArray[j].movementSpeed
 					--**** this line actually updates the agent's position, the fn exists outside of the background worker
 					updateAgent agentArray[j].sceneNode agentNextPos
 				)
 			) --end agent array check	
 		) --end timeCheck
 	) --end game cycles
 	animButtonState = true
 	MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
 	MainThread.WorkerSupportsCancellation = true		
 	dotNet.addEventHandler MainThread "DoWork" gameCycle
 	MainThread.RunWorkerAsync()
 	print "Pan and Rotate the viewport as the simulation happens."; gc()
 )
 

The cons:
This code sometimes immediately crashes max.
Sometimes the code runs for a while, then crashes max.
Sometimes the listener throws a system exception and the code won’t execute.

Has anyone else got max to update meshes/UI/anything else from a background worker thread?
Much thanks to LoneRobot for the bkg thread example!

61 Replies
1 Reply
 lo1
(@lo1)
Joined: 10 months ago

Posts: 0

nuff said

Lo, point well made, but… sometimes it works without error or crash.

Why? What’s causing the code to work sometimes but not others?
If the code could be altered to be stable 100% of the time, then why can’t we do most of the work in the background – so max’s UI doesn’t lock up and wait while a script finishes?

From Lonerobot’s example, updating a UI progressbar doesn’t seem to be a problem.
I don’t understand how updating a node would be any different.
What’s causing the background worker to crash max?

ok, this script runs fine – does not crash – and I can run it over and over again without error.


(	--background thread tests
	resetMaxFile #noPrompt; clearListener()

	fn bkgWrkr =
	(   
		with redraw off
		(
			for i=1 to 100 do
			(
				sliderTime = i
				box()
			)
		)
		completeRedraw()
	) --end bkgWrkr
	
	MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
	MainThread.WorkerSupportsCancellation = true		
	dotNet.addEventHandler MainThread "DoWork" bkgWrkr
	MainThread.RunWorkerAsync()
	print "Pan and Rotate the viewport while the script runs."; gc()
)


The script moves the timeslider, and creates boxes along the way.
I think the reason this script does not crash is due to with redraw off ()

I propose that one can use the background worker to do all sorts of awesome stuff, as long as the viewport isn’t updated while the background worker is running. I’m guessing the reason max crashes is due to a conflict with what should be displayed in the viewport.

I’m going to start testing other max commands, like mergeMAXFile…

I am happy to announce that you can use the background worker for many, many things.

Here is code that tests creating primitives, applying custom attributes, getting custom attributes back, and merging in max files from a background worker. I’m testing other commands right now, but I think that almost all commands will work, with some exceptions (for example: loadMaxFile).
Just don’t redraw the viewport while the background worker is running.


  (	--background thread tests
  	resetMaxFile #noPrompt; clearListener()
  	
  	local sfn = getSourceFileName()
  	local sfnp = getFilenamePath sfn
  	local theMaxDir = (sfnp + @"maxFiles\")
  	fn collectFiles directoryToSearch fileExtension = (local myFiles = getFiles (directoryToSearch + "*." + fileExtension); return myFiles)
  	myMaxFiles = collectFiles theMaxDir "max"
  	
  	fn bkgWrkr =
  	(	--let's see if we can break the background worker
  		with redraw off
  		(
  			--can we build/update meshes?
  			myBox = box()
  			myBox.pos = [200,100,0]
  			
  			--apply custom attributes?
  			customAttributes = attributes objectData
  			(
  				rollout paramsRollout "BkgWrkr CAs"
  				(
  					--THIS IS THE DEFAULT CASE, MAKES A  "NEW" TINYCTRL
  						label charFileLb "Character file:" pos:[10,10] width:150.0 height:12.0
  					edittext charFileAddressEt text:"custom attribute applied" fieldWidth:145 height:18 pos:[5,25] type:#string enabled:true
  					checkbutton lockCharCk "Lock Character" checked:false pos:[8,45]	width:145
  						label bipFileLb "Bip file:" pos:[10,80] width:150.0 height:12.0
  					dropdownlist bipFilesDd items:#() pos:[8,95] width:145
  					checkbutton lockBipCk "Lock Bip" checked:false pos:[8,115] width:145
  					dropdownlist nodesDd items:#() pos:[8,165] width:145
  				)
  				parameters main rollout:paramsRollout
  				(
  					tinyCharIDlock type:#boolean ui:lockCharCk default:false
  					tinyBipIDlock type:#boolean ui:lockBipCk default:false
  				)
  			)
  			custAttributes.add myBox customAttributes
  			select myBox --'return' the object selected
  			max modify mode -- now the rollout was created the first time
  				
  			--get custom attributes?
  			local myCA = myBox.objectData.paramsRollout.charFileAddressEt.text
  			print myCA
  			
  			--merge maxfiles?
  			myMaxFiles --array containing .max files
  			for j=1 to myMaxFiles.count do
  			(
  				mergeMAXFile myMaxFiles[j] #select #noRedraw #autoRenameDups #renameMtlDups --#alwaysReparent
  			)
  			
  			--
  		)
  		completeRedraw()
  	) --end bkgWrkr
  	
  	MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
  	MainThread.WorkerSupportsCancellation = true		
  	dotNet.addEventHandler MainThread "DoWork" bkgWrkr
  	MainThread.RunWorkerAsync()
  	print "Pan and Rotate the viewport while the script runs."; gc()
  )
  
  

Can anyone verify? Lo?
To test, you’ll need to create a directory where the script exists called ‘maxFiles’ and fill it with .max files.

 lo1

sorry, this has crashed my max 3 out of 3 times even when the mergeMaxFile line is commented.

I’ve tested it to run correctly for 10 out of 10 tries.
I noticed that the forum messed up a line when I posted the code
.
So, instead, here is a link to a .rar archive containing the script and some test maxfiles.
Decompress the .rar and try running the script again.

Let me also ask, did the 2nd script I posted to this thread run fine?
(It’s the one that increments the timeslider and creates boxes.)

 lo1

I had fixed what the forum garbled up earlier. But to be safe, I also ran your archived script, but with the same result.

The slidertime/boxes script worked fine for me, but crashed if I substantially increased the number of created boxes or did some more operations on the created boxes.

 I had fixed what the forum garbled up earlier. But to be safe, I also ran your archived script, but with the same result.

Very interesting. I don’t know why the script is failing for you.
Here’s a screen capture of the script working on my computer.

The slidertime/boxes script worked fine for me, but crashed if I substantially increased the number of created boxes or did some more operations on the created boxes.

I’m beginning to think that the length of time that the background thread runs for may also contribute to crashing max. What other operations did you perform on the box? I’m interested to see the limits of the background thread worker…

It’s very interesting that we’re getting different results. :shrug:

 lo1

Rest assured, I do not doubt that it works for you. I am not sure why it is not working for me…

I also can not get the boxes script to crash any more, though I abused it quite a bit. :shrug:

Ok, last script to post to this thread:


(	--background crasher
	maxScriptString = @"for i=1 to 10 do (myBox = box(); myBox.pos = [(random -200 200),(random -200 200),0])"
	
	fn bkgWrkr =
	( with redraw off ( execute maxScriptString ); completeRedraw(); )
	
	MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
	dotNet.addEventHandler MainThread "DoWork" bkgWrkr
	MainThread.RunWorkerAsync()
)

This script generalizes the background worker to a function that executes a string as maxscript. Try throwing different code at it and see what crashes max

I’m interested to see how many different background workers can be operating on a max scene at once… I wonder if two background workers can create primitives at the same time, or if that would lead to a race condition with the scene? Hmm…

Page 1 / 5