Notifications
Clear all

[Closed] Calling C++ function inside C#

 MZ1

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.

32 Replies
1 Reply
(@denist)
Joined: 10 months ago

Posts: 0

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.

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

http://blogs.msdn.com/b/borisj/archive/2006/09/28/769708.asp x” rel=”nofollow noopener”>blog

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

 MZ1

I will check it out, Thanks.

 MZ1

I mostly prevent to jump in to the C++, as I’m not good at it. But yes, sometimes it become necessary to calculate matrixes in the vertex things and also rigging. Set transform was just simple example to describe my question.

 MZ1

Thank you @Serejah, DisableRefMsgs is the trick that I forget about.

 MZ1

I just updated my code with DisableRefMsgs, but I now have problem with complex rigs (mixed biped and bones)

I’m not an animation guy, so can’t really tell you how to tackle that. But I assume you should set/invalidate TMs of all dependents as well (since none of them receive tm change message, when their parents tm changes). Not sure if you can still have any performance improvements after that

 MZ1

To demonstration what I want to achieve, I created a C# project (demo and source codes are attached). This tool will save the pose for all objects in the scene in a xml file, then you can modify objects and blend to the saved pose by using the slider. it works on simple scene, but my goal is to make it compatible with all max rigs (biped, CAT and custom bones). and also increase the performance.
@denisT do you think we should switch to C++ for this?TestProject(2020).rar (3.2 MB)

I played just a little bit and it is clear that you need to somehow hash TMs so you could compare if two TMs are already equal and thus do not require blending whatsoever and as a result you simply skip SetNodeTM call. Check you PM for source files

gif

  <Object Name="Bip001 Spine">
    <Transform Hash="[5.90464E-08,-0.0007963198,0.9999997][-1.402106E-06,-0.9999997,-0.0007963198][1,-1.402059E-06,-6.01629E-08][-73.05801,-21.80891,19.57084]">
      <Row0 X="5.90464E-08" Y="-0.0007963198" Z="0.9999997" />
      <Row1 X="-1.402106E-06" Y="-0.9999997" Z="-0.0007963198" />
      <Row2 X="1" Y="-1.402059E-06" Z="-6.01629E-08" />
      <Row3 X="-73.05801" Y="-21.80891" Z="19.57084" />
    </Transform>
  </Object>

Code I used to make ‘hash’
You can implement something more performant

        static string Point3String( IPoint3 pt )
        {
            return string.Format("[{0},{1},{2}]", pt.X, pt.Y, pt.Z);
        }
        static string Matrix3String( IMatrix3 m3 )
        {
            return string.Concat(Point3String(m3.GetRow(0)), Point3String(m3.GetRow(1)), Point3String( m3.GetRow(2)), Point3String(m3.GetRow(3)));
        }
Page 1 / 3