The process memory is out of control might because of the DisplayConduit

I run the code in Debug mode using Visual Studio.

But the “Process Memory” part in the “Diagnostic Tools” panel in Visual Studio works weird.
image

As soon as my own GH_component “DrawSprite” is set to run = true and running, the memory quickly raise up from around 2.2 GB to 4.8 GB. And then every time I press “Alt + Tab” to swap the Rhino UI with VisualStudio UI, the momery will raise up another 200MB until the memory of my computer is not enough to use.

Here is my code:

using System;

using Grasshopper.Kernel;
using Rhino.Geometry;
using Rhino.Display;
using System.Drawing;

namespace Hani
{
    public class DrawSprite : GH_Component
    {
        static public RhinoView rhinoView;
        static public Bitmap bmp;
        bool run;
 
        public DrawSprite()
          : base("DrawSprite", "DS",
              "Draw the Bitmap onto the Viewport",
              "Hani", "Viewport")
        {
        }

      
        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
            pManager.AddGenericParameter("rhinoView", "rView", "The viewport to draw Bitmap on.", GH_ParamAccess.item);
            pManager.AddGenericParameter("Bitmap", "B", "The Bitmap to draw.", GH_ParamAccess.item);
            pManager.AddBooleanParameter("run", "r", "Run or not", GH_ParamAccess.item);
        }

       
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
        }

      
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            if (!DA.GetData<RhinoView>(0, ref rhinoView) || !DA.GetData<Bitmap>(1, ref bmp) || !DA.GetData<bool>(2, ref run))
                return;

            var conduit = new MyConduit();
            if (run)
            {
                conduit.Enabled = true;
            }
            else if (!run)
            {
                conduit.Enabled = false;
            }
        }

    class MyConduit : DisplayConduit
    {
        protected override void CalculateBoundingBox(CalculateBoundingBoxEventArgs e)
        {
            base.CalculateBoundingBox(e);

            var bbox = new BoundingBox();
            bbox.Union(e.Display.Viewport.ConstructionPlane().Origin);
            e.IncludeBoundingBox(bbox);
        }

        protected override void PostDrawObjects(DrawEventArgs e)
        {
            base.PreDrawObjects(e);

            if (e.Viewport.Name == DrawSprite.rhinoView.MainViewport.Name)
            {
                e.Display.EnableDepthWriting(false);
                e.Display.EnableDepthTesting(false);

                DisplayBitmap d_bmp = new DisplayBitmap(DrawSprite.bmp);
                int width = DrawSprite.rhinoView.MainViewport.Bounds.Width;
                int height = DrawSprite.rhinoView.MainViewport.Bounds.Height;

                DisplayPipeline ppl = DrawSprite.rhinoView.DisplayPipeline;
                ppl.DrawSprite(d_bmp, new Point2d(width / 2, height / 2), width, height);
                DrawSprite.rhinoView.Redraw();

                e.Display.EnableDepthWriting(false);
                e.Display.EnableDepthTesting(false);
            }
        }
    }
}

Does it help if you explicitly dispose d_bmp at the end of your PostDrawObjects in the if-block where it is newed up?

Also make sure you’re not leaking memory when setting up your DrawSprite.bmp.

I’ve tried to explicitly dispose d_bmp at the end of my PostDrawObjects in the if-block.

Sadly, it doesn’t work. :see_no_evil:

It is indeed because of the Bitmap. If I scale down the Bitmap to 1/10. The memory is not raising so quickly but it still meets another problem.
Here is the exception throwed by VisualStudio:

System.Runtime.InteropServices.ExternalException
  HResult=0x80004005
  Message=A general error occurred in GDI+.
  Source=System.Drawing
  StackTrace:
   at System.Drawing.Bitmap.GetHbitmap(Color background)
   at System.Drawing.Bitmap.GetHbitmap()
   at Rhino.Display.DisplayBitmap..ctor(Bitmap bitmap)
   at Hani.Vision.MyConduit.PostDrawObjects(DrawEventArgs e) in C:\Users\77950\Desktop\Hani\DrawSprite.cs:line 125
   at Rhino.Display.DisplayConduit.ExecConduit(IntPtr pPipeline, UInt32 conduitSerialNumber, UInt32 channel)

Perhaps in your DrawSprite set static public Bitmap bmp to null by default. Then in SolveInstance you could try checking if bmp is valid and dispose that before getting the new bitmap.

Are you feeding your component ever changing bitmaps, as in you update it multiple times per time unit?

I want to feed in a ever changing bitmap. But I’m testing with a unchanging bitmap right now.

I will prepare the bitmap file, .gh file and.gha file and upload them later! Thank you for your help!

test_conduit.zip (1.5 MB)

You’ll have to at least make sure you instantiate your conduit only once per component. Currently you’re newing up one on every SolveInstance. Better to move that to the constructor and keep a reference to it in a member variable, toggling it on and off when needed.

Hi Nathan, I’ve moved the new MyConduit() to the constructor of my GH_Component. I think this is a good change to make! But it doesn’t work.

And I’m testing with an unchanging bitmap, so I think the component may run only once which should be no differences with keeping the new MyConduit() in the SovleInstance() for now.

And I’ve tried running this code with a breakpoint and manully step by step, the memory is not raising up in this condition. But as soon as I remove the breakpoint and hit “continue”, the memory raises up certainly.

Sounds like it is related to this thread

1 Like

I just finished reading that topic. So this is a 8-year problem that hasn’t been solved by McNeel! :face_with_open_eyes_and_hand_over_mouth:

yup that seems to be the case.

@nathanletwory Hi Nathan, I think the reason of this problem is pretty clear in the topic referred by Menno!

Maybe you can change the third constructor of Rhino.Display.DisplayBitmap Class form private to public, then I can solve the problem by importing gdi32.dll and calling the DeleteObject() method.

Here is the third constructor of Rhino.Display.DisplayBitmap:
image

Or is there any other possible solution? Thank you for your support!

Oh, if that is a solution to the problem you could access the private constructor by reflection.
I had to do the same for AreaMassProperties, but that is another story.

DisplayBitmap dbmp = null;
IntPtr pBmp; // defined elsewhere
var privateConstructors = typeof(DisplayBitmap).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
if (privateConstructors.Any()) {
  foreach(var ci in privateConstructors) {
    var param = ci.GetParameters();
    if (param.Length == 1 && param[0].ParameterType == typeof(IntPtr)) {
      dbmp = (DisplayBitmap) ci.Invoke(new object[]{pBmp});
    }
  }
}
if (null != dbmp) {
  // you have successfully used a private constructor :)
}

// category - dirty hack but it works

I’m not sure what happens when the dbmp object gets Disposed though, it looks like it will try to free the pointer, so you may want to keep an eye on that, preventing a double free.

1 Like

Thank you Menno!

I’ve tried to do it with the flection approach. But the private constructor seems not working well. :face_in_clouds:

Here is the exception throwed by Visual Studio, the d_bmp can’t be accessed:

Oh, that is too bad. You’re well into territory where things become unreliable so maybe it just wasn’t meant to be used like this.

Hi @Hani_Leo,

Can you provide a working sample that demonstrates the problem you are having? Or it is posted above?

Thanks,

– Dale

@dale, The process memory is out of control might because of the DisplayConduit - #6 by Hani_Leo

With a decompiler you can see the code of the gha file.

Hi @dale ,

My files are posted above. Let me know if you encounter any problem with them!
Looking forward to your help!

Thanks,
Hani

Hi @Hani_Leo,

Sorry for the confusion. I’m looking for actual project source that, that I can run here, that allows me to reproduce the issue. The ZIP file you’ve pointed me to just contains some binaries written by someone else.

Not sure how to help here.

– Dale

Hi @dale ,

This is the scene.
test_conduit_withSourceCode.zip (1.6 MB)

I put the source code of the “test_assembly” project inside. And I also use “BitmapPlus” (a plugin written by others) to display the bitmap on the Grasshopper canvas.

Please let me know if there is still problem!

Thanks,
Hani