Calling C++ function inside C#


#1

I’m working on a C# library for the 3dsMax, everything works correct. If I want to increase performance, is there any easy method to write some custom C++ functions and call them inside C#?
Let say I need a function that will set transform for all objects in the scene rapidly.


#2

you can wrap the c++ as if it was native c# then compile with CLI option set then you can call from c#

blog


#3

Did you try to use unsafe pointer reads/writes in c# to improve the performance? You can achieve pretty solid speedup

Single Threaded Sub-Systems of 3ds Max

3ds Max’s reference system and node evaluation system are both single threaded. Thus trying to work with these sub-systems from multiple threads is unsafe and can lead to issues such as race conditions or crashes.

According to this you can get and modify TMs in parallel, but cannot set transforms in parallel.

...
static public void SetSceneTransforms(int variant)
        {
            var global = GlobalInterface.Instance;

            var nodes = new List<IINode>(global.COREInterface14.RootNode.NumberOfChildren);

            for (int i = 0; i < global.COREInterface14.RootNode.NumberOfChildren; i++)
            {
                nodes.Add(global.COREInterface14.RootNode.GetChildNode(i));
            }

            if (variant < 0) return;

            var rand = new Random(12345);

            var interval = global.Interval.Create();

            int t = global.COREInterface.Time;

            if (variant == 0)
            {
                // do all job in single thread

                for (int j = 0; j < nodes.Count; j++)
                {
                    var tm = nodes[j].GetNodeTM(t, interval);

                    tm.Trans.Z += (float)(rand.NextDouble() * 10);

                    nodes[j].SetNodeTM(t, tm);

                }

            }

            if (variant == 1)
            {
                // do all job in single thread with disabled ref messages, then invalidate tms
                global.DisableRefMsgs();

                for (int j = 0; j < nodes.Count; j++)
                {
                    var tm = nodes[j].GetNodeTM(t, interval);

                    tm.Trans.Z += (float)(rand.NextDouble() * 10);

                    nodes[j].SetNodeTM(t, tm);

                }

                global.EnableRefMsgs();

                for (int j = 0; j < nodes.Count; j++)
                {
                    nodes[j].InvalidateTM();
                }

                return;
            }


            if ( variant == 2 )
            {
                // collect and modify TMs in parallel, set in single thread

                var TMs = new IMatrix3[nodes.Count];

                Parallel.For(0, nodes.Count, j =>
               {
                   var tm = nodes[j].GetNodeTM(t, interval);

                   tm.Trans.Z += (float)(rand.NextDouble() * 10);

                   TMs[j] = tm;

               });


                for (int j = 0; j < nodes.Count; j++)
                {
                    nodes[j].SetNodeTM(t, TMs[j]);
                }

            }


            if (variant == 3)
            {
                // collect and modify TMs in parallel, set with disabled refs messages then invalidate

                var TMs = new IMatrix3[nodes.Count];

                Parallel.For(0, nodes.Count, j =>
                {
                    var tm = nodes[j].GetNodeTM(t, interval);

                    tm.Trans.Z += (float)(rand.NextDouble() * 10);

                    TMs[j] = tm;

                });


                global.DisableRefMsgs();

                for (int j = 0; j < nodes.Count; j++)
                {
                    nodes[j].SetNodeTM(t, TMs[j]);
                }

                global.EnableRefMsgs();

                for (int j = 0; j < nodes.Count; j++)
                {
                    nodes[j].InvalidateTM();
                }

            }


        }

test script

(
    tea = for i = 1 to 10000 collect Teapot pos:(random -[1000,1000,0] [1000,1000,0])

	for i = 0 to 3 do
	(
		t1=timestamp();hf = heapfree

			SetSceneTransforms i

		format "% undo on    Time: %sec. Mem: %\n" i ((timestamp()-t1)/1000 as float) (hf-heapfree)
		redrawViews()
		
		t1=timestamp();hf = heapfree

			undo off SetSceneTransforms i

		format "% undo off   Time: %sec. Mem: %\n" i ((timestamp()-t1)/1000 as float) (hf-heapfree)
		redrawViews()
	)
)

my timings

0 undo on Time: 0.13sec. Mem: 64L
0 undo off Time: 0.146sec. Mem: 64L
1 undo on Time: 0.06sec. Mem: 64L
1 undo off Time: 0.062sec. Mem: 64L
2 undo on Time: 0.138sec. Mem: 64L
2 undo off Time: 0.14sec. Mem: 64L
3 undo on Time: 0.058sec. Mem: 64L
3 undo off Time: 0.059sec. Mem: 64L

no difference between parallel and single thread


#4

I think we can set the transform to a billion scene nodes pretty quickly using pure MXS. The only slowdown in this case is the MXS loops.

What performance improvement do you want to get by using c++ and C#? I don’t see anyone … except when you want to do some matrix algebra. But that’s a different story.