Ultra-slow script exit...

I've got a fairly basic script (I can throw it here on demand) - I gather all of the materials in the scene with a specific prefix, confirm their sub-maps are of a specific type (via classof) - and then edit simple flags (visible, etc) - for them.

I added timestamping, and the code itself runs _fast_ - we're only talking a dozen or so simple materials. However right before it ends, it takes 30+ seconds to clear the final function. If I do _all_ of the code _except_ editing a map (I just comment out only the X=Y code) - it runs super fast, exits right away.

Does this raise a flag with anyone? Is there some specific dumb thing I may need to look for? I feel like the fact that the delay is only when an object is changed, that it's related to instancing or some sort of dirty-data cache... but the scene in terms of materials is not that complex - and 30 seconds is a _ton_ of time for a modern machine to do work...

Any and all thoughts would be appreciated...

fn matGather &mat &mats =
(
	if ((findstring mat.name "MAT[") == 1) then
	(
		appendIfUnique mats mat
	)
 
	mats
)
 
fn matGatherMul &mat &mats =
(
	for m in mat.materialList do
	(
		if (classof m == Standardmaterial) then
		(
			matGather &m &mats
		)
	)
)
 
fn matGatherScene &mats =
(
	for m in sceneMaterials do
	(
		if (classof m == Multimaterial) then 
		(
			matGatherMul &m &mats
		)
 
		if (classof m == Standardmaterial) then
		(
			matGather &m &mats
		)
	)
)
 
fn diffGetMap diff =
(
	if (classof diff == Bitmaptexture) then
	(
		diff
	)
	else 
	(
		if (classof diff == CompositeTexturemap) then
		(
			diffGetMap diff.mapList[1]
		) else (
			undefined
		)
	)
)

fn colorApply &cc apply = 
(
	cc.lightnessMode = 1
	cc.enableR = true
	cc.gainR = (apply.r/255)*100
	cc.enableG = true
	cc.gainG = (apply.g/255)*100
	cc.enableB = true
	cc.gainB = (apply.b/255)*100
)
 
fn teamCreate &diffM teamC stripeC =
(
	wantTeam = substitutestring diffM.filename "_DIFF." "_TEAM."
	wantStripe = substitutestring diffM.filename "_DIFF." "_STRP."
	teamM = undefined
	stripeM = undefined
	valid = 1
 
	try ( teamM = BitmapTexture filename:wantTeam ) catch ( valid = 0 )
	try ( stripeM = BitmapTexture filename:wantStripe ) catch ( valid = 0 )
 
	if (valid == 1) then
	(
		holdC = CompositeTexturemap()
		holdC.mapEnabled.count = 3
 
		teamCor = ColorCorrection()
		colorApply teamCor teamC
		teamCor.map = teamM
 
		stripeCor = ColorCorrection()
		colorApply stripeCor stripeC
		stripeCor.map = stripeM
 
		holdC.mapList = #(diffM, teamCor, stripeCor)
		holdC.layerName = #("diff", "team", "stripe")
		holdC
	) else (
		diffM
	)
)
 
fn teamShow &mat show teamC stripeC =
(
	diffC = mat.diffuseMap
	diffM = diffGetMap diffC
 
	if (diffM != undefined) then
	(
		if (classof diffC == BitmapTexture) then
		(
			diffC = teamCreate diffC teamC stripeC
			mat.diffuseMap = diffC
		)
	)
 
	if (classof diffC == CompositeTexturemap) then
	(
		if (diffC.mapList.count == 3) then
		(
			if (diffC.layerName[2] == "team") then
			(
				if (diffC.layerName[3] == "stripe") then
				(
					diffC.mapEnabled[2] = show
					diffC.mapEnabled[3] = show
 
					if (classof diffC.mapList[2] == ColorCorrection) then ( colorApply diffC.mapList[2] teamC )
					if (classof diffC.mapList[3] == ColorCorrection) then ( colorApply diffC.mapList[3] stripeC )
				)
			)
		)
	)
)
 
fn visualizeTeams show teamC stripeC =
(
	matSet = #()
	matGatherScene matSet
 
	for m in matSet do
	(
		teamShow m show teamC stripeC
	)
 
	-- DELAYS RIGHT HERE FOR AGES!!!
 
	matSet = #()
)

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Bitvenom's picture

Weird

I ran an experiment - the _first_ time I do one of these ops, it's pretty fast, and there's no delay.

Any other time following the first one, it's got the 30 second delay before returning... I am totally stumped - feels like something instancing related?

Bitvenom's picture

Due to my work I can't talk too much about what I am doing...

However, the basic idea (not included) is that I've got models coming from an in-game format, into Collada, and imported into Max 2014. I've got a rollup with some buttons for common tasks that the data in the Collada file can't describe (bump maps, this 3-layer team/stripe/base mask texture style, etc) - these are pre-existing assets. So this code allows an artist to press 'show' or 'hide' (with colors already picked) and walks the material stack looking for materials from our namespace ("MAT[-name-XXX-]") - then looks for ones that have valid extra layers, and adds/applys/recolors them. The files have been named by their function (_DIFF, _NORM, _TEAM, etc) - because other parts of our game asset pipe use that info, and we have a ton of variant shaders that may include some/all of each type.

The key here is that the _code_ runs fast as hell, works great, etc. But right before it exits (after the final loop) - it pauses for almost exactly 30 seconds, every time. If I don't edit any materials, it doesn't pause (and is over in a few dozen ms). So while the code may seem complex and long to you (and I've only been coding in Maxscript for a few days) - aside from the delay it is doing the intended behavior very fast.

barigazy's picture

...

Ok. I will show you shorter way. From the top I saw that you want to collect all standard material which name matched "MAT[" pattern. If I'm wrong please correct me. So this function will collect all standard material instances (as solo or in any material) which name have word "MAT[".

fn matGather patt: =
(
	local mats = #()
	if (std = getclassinstances Standard).count != 0 do
	(
		for m in std where matchPattern m.name pattern:patt ignorecase:on do append mats m
	) ; mats
)
matGather patt:"*MAT[*"

Now what do you need next?
BTW you not need to use anymore functions "matGatherScene" and "matGatherMul".
My function replace your first tree fn's

bga

Bitvenom's picture

Thanks!

Being new to this language, the most elegant way of doing something isn't always obvious to me (for instance your use of the named variable patt: - hadn't done that myself, I like it (is it faster?)...

I put your routine in instead of mine (had no idea I could gather up all objects by type) - and while your search pattern was wrong ("MAT[*" with case-sensitivity works) - it works fine, but doesn't at all resolve the 30 second delay.

I really appreciate the help though - I'll borrow from your style a great deal :)

barigazy's picture

...

Wait for more. :) In a minute

bga

barigazy's picture

...

Next one

fn teamCreate diffM teamC stripeC =
(
	local wantTeam = substitutestring diffM.filename "_DIFF." "_TEAM."
	local wantStripe = substitutestring diffM.filename "_DIFF." "_STRP."
	local teamM = if pathConfig.isLegalPath wantTeam do (BitmapTexture filename:wantTeam)
	local stripeM = if pathConfig.isLegalPath wantStripe do (BitmapTexture filename:wantStripe)
	if teamM != undefined and stripeM != undefined then
	(
 		teamCor = ColorCorrection map:teamM
		colorApply teamCor teamC
		stripeCor = ColorCorrection map:stripeM
		colorApply stripeCor stripeC
		holdC = CompositeTexturemap mapList:#(diffM, teamCor, stripeCor) layerName:#("diff", "team", "stripe")
	) else (diffM)
)

bga

barigazy's picture

...

You have fn arguments that are tool related and I can only guess what they mean.
Explain me what each fn need to do. I undersatnd colorApply fn but other not.

bga

barigazy's picture

...

You not need to use "&" when you can return same map like this

fn colorApply cc apply = 
(
	cc.lightnessMode = 1
	cc.enableR = cc.enableG = cc.enableB = true
	cc.gainR = (apply.r/255)*100
	cc.gainG = (apply.g/255)*100
	cc.gainB = (apply.b/255)*100
	cc
)

And what will need to do this "diffGetMap" fn?
Probably like this

fn diffGetMap diff = 
(
	case classof diff of
	(
		(Bitmaptex): diff
		(CompositeTexturemap): if isKindOf diff.mapList[1] Bitmaptex do diff.mapList[1]
		default:undefined
	)
)

bga

Bitvenom's picture

Yeah, that would do the same

Yeah, that would do the same thing - I'm just trying to grab the 1st map from any of the 'MAT[' textures (as in Collada I assigned in the diffuse texture...

The filename of the diffuse texture _always_ ends with _DIFF - so when trying to see if other maps I need (_NORM for bump, _TEAM/_STRP for decaling layers) exist, I just replace that tail with the right one, and attempt to load/assign the bitmap.

In the end, I've got 2 types of routine: enable/disable bump (working great, very fast, no delay until I use the other one, then all routines have a delay). The 2nd is enable/disable team/stripe. For that I gather all of the materials, look for any not compound and if they have valid team/stripe maps on the disk where the _DIFF texture is, I load/assign them (flowing through ColorCorrects to allow tinting quickly). If the texture _is_ compound already with the properly named layers (team/stripe) - I just change the color or toggle the visibility flag - I don' recreate or set anything up.

While the first pass on a scene may be slow (gathering all of the materials, verifying the extra bitmaps can exist, loading/applying them) - the passes that follow should be _very_ fast, no matter how terrible my code, because I don't create _any_ scene element, I just swap a few flags. And a 30 second delay is a _huge_ delay for this script, especially when you consider that the 'mats' array I am using of gathered materials is 10-12 elements long, it's really nothing.

I appreciate you trying to help me make faster code, but if I add 'prints' to the code, _all_ of the 'work' code gets done in the blink of an eye - the timestamp from start to finish is ~30 ms. But before the function returns, there's an additional 30 second pause - almost like unrolling the stack from the call is taking forever (garbage collection? maybe I am leaking circular-ref objects?)...

barigazy's picture

...

This fn clean up bitmaps in memory

freeSceneBitmaps()

bga

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.