.net sdk memory management problem


#21

Ironically, none of these classes actually implement IDisposable… sigh


#22

Looking to optimize this further for real-time revisualization, I tried another approach. I haven’t looked at the level of protection of the SetMapVert() method, but I am sure the following is not recommended. However if this is for yourself and you don’t find any bugs, it might speed up the preview.

I’ve noticed that you can directly overwrite the mapVerts List, so you don’t even need to create a “dummy” IPoint3 and overwrite it, just overwrite the mapVerts List directly as follow:

public void GradeMesh()
  {
  	IObject obj = node.EvalWorldState(ip.Time, true).Obj;
  	IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.TRIOBJ_CLASS_ID, 0);
  	if (obj.CanConvertToType(cid) != 0)
  	{
  		ITriObject triMesh = obj.ConvertToType(ip.Time, cid) as ITriObject;
  		IMesh mesh = triMesh.Mesh;
  		IList<IPoint3> mapVerts = mesh.MapVerts(Channel);
  		int numMapVerts = mesh.GetNumMapVerts(Channel);
  
  		IPoint3 Pt1 = global.Point3.Create(1, 1, 1);
  		
  		for (int i = 0; i < numMapVerts; i++)
  		{
  			IPoint3 vertex = mesh.GetVert(i);
  			IPoint3 mapVert = mapVerts[i];
  
  			//float r = mapVert.X * 255.0f;
  			float g = mapVert.Y * 255.0f;
  			float b = mapVert.Z * 255.0f;
  
  			if (vertex.Z > HeightThreshold)
  			{
  				mapVert.X = 1;
  				mapVert.Y = Math.Max(0, (g - FadeInValue) / 255);
  				mapVert.Z = Math.Max(0, (b - FadeInValue) / 255);
  				mapVerts[i] = mapVert;
  			}
  			else if (g < 255)
  			{
  				if (g + FadeOutValue > 255)
  				{
  					mapVerts[i] = Pt1;
  				}else{
  					mapVert.X = 1;
  					mapVert.Y = (g + FadeOutValue) / 255;
  					mapVert.Z = (g + FadeOutValue) / 255;
  					mapVerts[i] = mapVert;
  				}
  			}
  		}
  	}
  }

This seems to perform 50% faster than the previous improvement which makes it around 3 times faster than creating the points and with no memory leaking.

I’ve tried with a static mesh and the improvement is noticeable, but when running an animated mesh, the performance is capped by Max itself, so I got 80FPS on a 120K mesh with or without changing the vertex colors which indicates that the function is very efficient.

I’ve tried different meshes and animations with no problems at all, no memory leaking, no crash and the performance keeps as good as playing the animation alone. In all cases the FPS appears to be capped wither by the internal mesh update or by the viewport redrawing.


#23

This is really great, thanks Jorge!


#24

for every map vertex in the loop you multiply g and b values by 255 and later divide them by 255. you do it only because fade-in and out values come in the function in range [0,255].
it would be better just re-range them as [0,1] (divide them before loop by 255) and not touch map vert values at all.


#25

another thing i don’t like is:

for (int i = 0; i < numMapVerts; i++)
{
  	IPoint3 vertex = mesh.GetVert(i);
  	IPoint3 mapVert = mapVerts[i];
        ...

it means you always expect to have the same number of geo and map verts. it’s a very specific case. is it a requirement of your tool? if so you have to check it before the function use.


#26

That is a good point.

I can’t tell you much about the requirements of the tool, I was just helping a guy to speed it up, and basically converted each maxscript line to .net :). The idea is to create a mask for foam on animated oceans. I guess the next step is to add more criteria when checking if a vertex should be colored, such as speed and angles etc.


#27

it might make the function about 2-3 times faster.


#28

after optimization the loop should look as (IMHO):

		  for (int i = 0; i < numMapVerts; i++)
		  {
			  IPoint3 vertex = mesh.GetVert(i);
  
			  mapVerts[i].X = 1; // if you need to double-check it
			
			if (vertex.Z > HeightThreshold)
			  {
				  mapVerts[i].Y = Math.Max(0, mapVerts[i].Y - FadeInValue);
				  //mapVerts[i].Z = Math.Max(0, mapVerts[i].Z - FadeInValue);  // if it's not a grey scale
			  }
			  else 
			{
				  mapVerts[i].Y = Math.Min(1, mapVerts[i].Y + FadeInValue);
				  //mapVerts[i].Z = Math.Min(1, mapVerts[i].Z + FadeInValue);  // if it's not a grey scale
			  }
			mapVerts[i].Z = mapVerts[i].Y; // if it's a grey scale
		  }

i believe we can do color assignment in place. and after compilation Math.Min or Math.Max is exactly the same as one comparison and one assignment.


#29

If it is for previewing you can just initialize the default map faces and it should work.


#30

Thank you Håvard, I now have a better idea of what this tool could be used for. Will try with an ocean.


#31

I would had been surprised if avoiding the multiplications and divisions in this case would cause such a huge performance impact. So I tried it and as far as I can see it doesn’t speed up the function at all.


#32

This runs over 2 times slower on my tests.

I suppose this is due to the constant access to the mapVerts List (mapVerts[i].X, mapVerts[i].Y, mapVerts[i].Z) instead of writing the value back at once as I did in the latest function.


#33

Beautiful!

Tried with an animated ocean and it works wonderful. Perfect for creating foam masks, among other things.

Performance is great, I don’t think it needs any further optimizations. After all, the ocean I tried is another example that probes the FPS are capped by the animation itself and not by coloring the vertices with this function.


#34

Are you compiling with optimizations enabled?


#35

Yes… I think :).
Are you getting different results? I mean for comparison with Denis’s code on #28 I get around 2.2 slowdown. Is that what you are talking about?


#36

I haven’t tried compiling, just thought it would explain why you get such different performance.


#37

I’ll test all these potential optimizations when I get back home from work. I appreciate all the tips so far!

Yeah, it works pretty well with just the height criterion. Here is a test the guy I helped made:

//youtu.be/iw0t_W_k7XA


#38

Would you try both functions if you get some time?
It would be great to have your opinion and findings.


#39

slowing down for in-place method doesn’t make sense for me. probably it’s some issue specific to max .net sdk.
if removing unnecessary multiplications and divisions don’t really affect the performance that means 99% of time the method is doing something different. but i don’t see anything unless taking a value from one piece of memory and putting to another. in c++ sdk it’s almost nothing and doesn’t take any time.


#40

I tried both versions and I get same result as PolyTools3D - the in-place version is about 60% slower for me.

I believe this is because that no value data is actually stored on the IPoint3:

bFlags = (dotnetClass "System.Reflection.BindingFlags")
allFields = dotnet.CombineEnums bFlags.Public bFlags.NonPublic bFlags.Instance
fields = ((((dotnetclass "Autodesk.Max.GlobalInterface").Instance.Point3.Create 1 1 1).GetType()).GetFields allFields)
for p in fields do print (p.ToString())

"Point3* unmanaged_"
"Boolean owning_"

The only data is a pointer to a native Point3. Each access requires dereferencing a native pointer, which I assume is slower from a c# assembly.

Nevertheless, the method runs very fast (1-2 ms for 40000 vertices on my machine), and if you need it any faster than this, you might as well start over in C++.