ScriptSpot

Forum topic · General Scripting

Ultra-slow script exit...

By Bitvenom · 2013-12-24

Description

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 (14)

Weird

Bitvenom · 2013-12-24

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?

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

Bitvenom · 2013-12-24

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 · 2013-12-24

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

barigazy · 2013-12-24

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.

barigazy · 2013-12-24

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
)
)

Bitvenom · 2013-12-24

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 · 2013-12-25

This fn clean up bitmaps in memory

freeSceneBitmaps()

barigazy · 2013-12-25

I adde one more fn as filter

fn filterCompTex map = (isKindOf map CompositeTexturemap and map.mapList.count == 3 and map.layerName[2] == "team" and map.layerName[3] == "stripe")
fn teamShow mat show teamC stripeC =
(
if isKindOf mat.diffuseMap BitmapTex then (mat.diffuseMap = teamCreate mat.diffuseMap teamC stripeC) else
(
if filterCompTex mat.diffuseMap do
(
mat.diffuseMap.mapEnabled[2] = mat.diffuseMap.mapEnabled[3] = show
if isKindOf mat.diffuseMap.mapList[2] ColorCorrection do ( colorApply mat.diffuseMap.mapList[2] teamC )
if isKindOf mat.diffuseMap.mapList[3] ColorCorrection do ( colorApply mat.diffuseMap.mapList[3] stripeC )
)
)
)

barigazy · 2013-12-25

And the last one


fn visualizeTeams show teamC stripeC =
(
	local matSet = matGather patt:"*MAT[*"
	undo off with redraw off
	(
		for m in matSet do (teamShow m show teamC stripeC)
	)
	free matSet ; freeSceneBitmaps()
)

BTW if your material name starts with MAT... then use pattern in this form "MAT[*"

barigazy · 2013-12-25

I hope that this correction until now works faster.
Cheers!

;)

Thanks!

Bitvenom · 2013-12-24

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 · 2013-12-24

Wait for more. :) In a minute

barigazy · 2013-12-25

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)
)

barigazy · 2013-12-24

Your code is too long. I'm sure that this can be shorter and better is you explain
what do you want to achive in brief