Search Unity

How to turn off SendMouseEvents?

Discussion in 'Editor & General Support' started by superpig, Nov 27, 2012.

  1. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    I'm not using any of the OnMouse* events anywhere in my codebase, but the SendMouseEvents class is still being called by the engine every frame, doing raycasts, wasting cycles, and allocating GC memory.

    Is there a flag somewhere I can set to turn it off? I had a quick look with ILSpy but I don't see anything.
     
  2. Stephan-B

    Stephan-B

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    I would love to know that as well... I so despise the GC Monster and its affects.
     
  3. jhoemar

    jhoemar

    Joined:
    Nov 28, 2012
    Posts:
    11
    you guys using any Unity GUIs? Using uses SendMouseEvents() for gui processing.

    -Jhoemar
     
  4. Stephan-B

    Stephan-B

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    In Unity 4.0, an empty scene will still have Monobehavior.OnMouse show up in the profiler.
     
  5. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Really? It doesn't look like that from the code.
     
  6. thegreatzebadiah

    thegreatzebadiah

    Joined:
    Nov 22, 2012
    Posts:
    836
    so...any progress with this? Certainly seems wasteful to have it called and allocating memory when mouse input isn't needed.
     
  7. TakuanDaikon

    TakuanDaikon

    Joined:
    Jun 6, 2010
    Posts:
    268
    Clearing the Camera.eventMask bit for the desired layer will disable SendMouseEvents processing.
     
    PrimalCoder likes this.
  8. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    While great in theory, I haven't actually been able to get this to work. For instance, setting the cullingMask to 0 disables the cullingMask for every layer, but setting the eventMask to 0 does not accomplish the same thing. Everything I've tried has been ineffective at disabling mouse events.

    Have you actually gotten this to work? If so, please let us know how! Thanks!
     
  9. hogwash

    hogwash

    Joined:
    Oct 12, 2012
    Posts:
    117
    bump!
     
  10. TakuanDaikon

    TakuanDaikon

    Joined:
    Jun 6, 2010
    Posts:
    268
    I use camera.eventMask = ~camera.cullingMask to disable the SendMouseEvents processing for a particular camera.

    In another instance, I've used the following to disable SendMouseEvents processing for all cameras, using the GameObject's layer:

    Code (csharp):
    1. public void Start()
    2. {
    3.  
    4.  
    5.     var sceneCameras = FindObjectsOfType( typeof( Camera ) ) as Camera[];
    6.     for( int i = 0; i < sceneCameras.Length; i++ )
    7.     {
    8.  
    9.  
    10.         // Get rid of Unity's extremely annoying tendency to print errors
    11.         // about being unable to call SendMouseEventXXX because the event
    12.         // signatures don't match. Whose idea was that, anyways? Sheesh.
    13.         sceneCameras[ i ].eventMask = ~( 1 << gameObject.layer );
    14.  
    15.  
    16.     }
    17.  
    18.  
    19. }
     
  11. hogwash

    hogwash

    Joined:
    Oct 12, 2012
    Posts:
    117
    I'm doing the same however I consistently have about 1k of data allocated by MonoBehaviour._OnMouse every frame.
     
  12. hogwash

    hogwash

    Joined:
    Oct 12, 2012
    Posts:
    117
    See attached photo of profiler window:

    $Screen Shot 2014-01-09 at 11.12.34 am.png
     
  13. TakuanDaikon

    TakuanDaikon

    Joined:
    Jun 6, 2010
    Posts:
    268
    That's very odd, and does appear to indicate that it didn't work, since the same line in my profiler window typically look like this:



    This is fairly consistent for me. Even with hundreds of objects in the scene, there's little variation in the amount of memory, and expanding the node never shows the lengthy list of subnodes that yours does.

    I admit that I don't know what the difference may be between your situation and mine. Your scene is clearly processing the DoSendMouseEvents(), though. If I can think of something significant that might apply I'll be sure to post it. Maybe it's because I clear the eventMask for all cameras instead of just the main camera? Not sure.
     
  14. Smooth-P

    Smooth-P

    Joined:
    Sep 15, 2012
    Posts:
    214
    I've been working on trimming my allocations and am almost to zero (actually I *am* at zero in my own code), but even with all camera eventMaskS cleared I'm getting 0.6 - 1.2 Kb worth of allocations per frame. Maybe the profiler is counting it's own OnGUI usage? Or maybe it's just broken, which wouldn't be surprising.

    I'm on Unity 4.3.3f1 / OSX 10.8.5 btw.
     
    Last edited: Feb 1, 2014
  15. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    I have decompiled the UnityEngine source code and have found what the issues are in the DoSendMouseEvents method(can source code be posted on these forums?).

    The signature of the method is void DoSendMouseEvents(int mouseUsed, int skipRTCameras). I have no clue why ints are used, as these parameters are used as booleans. For the sake of this post, I am going to speak of them as if they were booleans.

    The issues I have found (I think) are:

    1) If mouseUsed == true, then the mouse position is used to determine if anything was hit during the current frame . If mouseUsed == false, then nothing was hit. The first issue is that unnecessary garbage is generated even when mousedUsed == false.

    Camera.allCameras (which creates 28 Bytes for me, but will create more for you if you have more enabled cameras in your scene. I think each extra camera is 4B's) is called no matter what, even though if mouseUsed == false, the cameras ARE NOT USED.

    In addition, the method allocates an array of length 2 to store two HitInfo objects, which are a struct type internal to the SendMouseEvents class. Normally structs are allocated on the Stack, but in this case because they are being copied to an array, heap allocation occurs (someone correct me if that info is not correct!). I believe in Unity's case, each struct allocates 16B, and the array itself allocates another 8B (so 16 * 2 + 8 = 40).

    In my case, these 68 Bytes make up all of the allocation that is produced by DoSendMouseEvents. I also believe it to be the minimum allocation possible.

    An array in this case is absolutely unnecessary, as there are always only 2 hit info objects, so two separate variables could just as easily be used to store the HitInfo.

    It's important to note that "mouseUsed" does not equate to "mouse events enabled." Instead, mouseUsed appears to simply indicate whether the mouse should be used to determine if anything was hit during the current frame. If mouseUsed == false, then the HitInfo structs are left empty (indicated nothing was hit for that frame).

    This hit info is then sent to another method which determines what methods need to be called based on the current mouse state (left mouse button clicked or released?) and hit info for this frame and the last frame (aka, if a hit occurs and the left mouse button was clicked, call the "OnMouseDown" method of the hit game object).

    2) Now let's talk about what happens when mouseUsed == true. The array of enabled cameras is looped through, and for each camera, if skipRTCameras (which I believe means skip RenderTexture Cameras) is true or the current camera's targetTexture field (which is a RenderTexture) is null, the mouse position is used to determine if the mouse is currently on the camera's rendered screen.

    If it is, then the method tries to get the camera's GUILayout component, in order to determine if the mouse is hovering over any GUI Elements. This step is always executed, regardless of whether you are actually using a GUILayout component or you have set the eventMask to 0. This may be producing garbage, as I have read somewhere that GetComponent produces garbage when no component of that type is on the game object. The Hit Info for this step is stored at index 0 of the array I mentioned above.

    Once the GUI is processed, the mouse position is used to determine if any game object was hit on the screen, but only if eventMask for the camera != 0 (yay, eventMask finally did something!). The code looks something like this:

    Code (csharp):
    1.  
    2. if (camera.eventMask != 0)
    3. {
    4.     if (camera.farClipPlane > 0f  Physics.Raycast(camera.ScreenPointToRay(mousePosition), out raycastHit, camera.farClipPlane, camera.cullingMask  camera.eventMask  -5))
    5.     {
    6.         StoreCameraAndObjectHitInHitInfo();
    7.     }
    8.     else if (camera.farClipPlane > 0f)
    9.     {
    10.         if (Physics2D.GetRayIntersectionNonAlloc(camera.ScreenPointToRay(mousePosition), SendMouseEvents.m_MouseRayHits2D, camera.farClipPlane, camera.cullingMask  camera.eventMask  -5) == 1)
    11.         {
    12.             StoreCameraAndObjectHitInHitInfo();
    13.         }
    14.     }
    15.     else if (camera.clearFlags == CameraClearFlags.Skybox || camera.clearFlags == CameraClearFlags.Color)
    16.     {
    17.         //Don't get the point of this, isn't HitInfo already null?
    18.         SetHitInfoToNull();
    19.     }
    20. }
    21.  
    It then sends the hit info to the other method I mentioned earlier.

    The main thing to take away from all this is that there is no way to tell Unity to not use Mouse Events. Event Mask's will stop raycast from being cast for individual cameras, but will not actually stop mouse event processing. In fact, GUI Elements are processed regardless of the event's mask (which is probably a good thing).

    They need to add some way to just disable the call to DoSendMouseEvents, although it would also be a good idea to clean up that method a bit (I am guessing whoever wrote it either a) didn't realize it would generate garbage or b) didn't care - which makes sense, since with a solid GC, this shouldn't be an issue).

    One final thing. From my understanding, only one camera can hold the mouse at any given time. Thus, it doesn't make sense to loop through every camera. When a hit is detected on a camera, the loop could exit rather than checking the next camera.
     
    earhod, CanisLupus and twobob like this.
  16. ycarowr

    ycarowr

    Joined:
    Dec 18, 2014
    Posts:
    6
    I'm not using OnMouse calls too, but on WP8 (Lumia 930) the spikes reach 25ms.
    In the editor the allocation is 1.7 KBs. I tried the indicated solutions (setting the eventMasks of the cameras)
    but anyone works. However, I'm using one call to get the touchInput (Physics.raycast(...)

    I guess that only way to decrease the allocation and maybe avoid the spikes is disabling the camera objects in the scene.
     
  17. vidjo

    vidjo

    Joined:
    Sep 8, 2013
    Posts:
    97
    Has anyone found a solution to this?

    I'm in Unity 5 and it seems that adding a GUILayer component to every camera solves the issue.
     
  18. Jakhongir

    Jakhongir

    Joined:
    May 12, 2015
    Posts:
    37
    Component GUILayer is on camera by default. Sometimes I am able to see huge spikes on method SendMouseEvents.DoSendMouseEvents and it takes 64 ms.
     
  19. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    lag.PNG

    No solution to this yet?
     
    ibyte likes this.
  20. Deleted User

    Deleted User

    Guest

    I just like how the original poster now seems to work at Unity (but probably did not at the time, or else why would he/she ask on here?)
     
    shoopi, earhod and PhoenixRising1 like this.
  21. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    Haha, yeah I noticed that too. :)
     
  22. Jakhongir

    Jakhongir

    Joined:
    May 12, 2015
    Posts:
    37
    No solution yet, unfortunately.
     
  23. FredrikMyxAkerblom

    FredrikMyxAkerblom

    Joined:
    Feb 18, 2014
    Posts:
    4
    This problem appeared in the Unity Bar&Grill Skype chat so I thought I'd take the time to post my solution.

    The profiler reported that MonoBehaviour.OnMouse_ produced 0.6KB of garbage per frame, which is simply not acceptable when developing for smartphone and other similar devices. After some research I concluded that there was no supported way of disabling this event. So I took a peek at the source and produced the following piece of code.

    Slap it on a gameobject and make sure the script is high in ExecutionOrder.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Reflection;
    3. using System;
    4.  
    5. // This script should be first in Script Execution Order
    6. // This script lobotomizes Unity's OnMouse events to remove the related garbage generation
    7. public class MouseEventSupressor : MonoBehaviour
    8. {
    9.     private Action SetMouseMoved;
    10.    
    11.     void Start()
    12.     {
    13.         // Locate the internal class SendMouseEvents
    14.         Assembly assembly = typeof(MonoBehaviour).Assembly;
    15.         Type type = assembly.GetType("UnityEngine.SendMouseEvents");
    16.  
    17.         // Create an Action of the method SetMouseMoved which sets the flag s_MouseUsed
    18.         MethodInfo method = type.GetMethod("SetMouseMoved",
    19.             BindingFlags.Static | BindingFlags.NonPublic);
    20.         SetMouseMoved = (Action)Delegate.CreateDelegate(typeof(Action), method);
    21.         SetMouseMoved();
    22.  
    23.         // Set the length of the m_CurrentHit array to zero, stops the SendEvents calls
    24.         FieldInfo info = type.GetField("m_CurrentHit",
    25.             BindingFlags.NonPublic | BindingFlags.Static);
    26.         info.SetValue(null, Array.CreateInstance(info.FieldType.GetElementType(), 0));
    27.     }
    28.  
    29.     void Update ()
    30.     {
    31.         // Call the SetMouseMoved function at the start of every Update
    32.         // This stops a variety of raycasting and get component calls which create garbage
    33.         SetMouseMoved();
    34.     }
    35. }
     
  24. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    What version of Unity are you running. I'm in 5.1.4f1 and 'method' is null.
     
  25. FredrikMyxAkerblom

    FredrikMyxAkerblom

    Joined:
    Feb 18, 2014
    Posts:
    4
    I'm on 5.2.3f1. The relevant class has most likely been refactored since 5.1.4f1 - one of the dangers of reflection.
     
  26. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    Don't suppose you know one that works with 5.1.4f1? I had a play with Public attributes, but I can't find the relevant method :(
     
  27. Jakhongir

    Jakhongir

    Joined:
    May 12, 2015
    Posts:
    37
    Thanks for sharing your solution. Now I need a little help how to use your script properly. After attaching it to gameobject, I have noticed that GC.Collect started to execute periodically.
     
  28. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Alright, I just banged my head for an hour now and ended up reading the source code.

    1) The whole system is retarded. You have no way to stop Unity from calling that specific loop, since it's called to a static method directly from the native side. You can only force it to do as little as possible. (Like the reflection solution from Fredrik)

    2) "camera.eventMask = 0;" does indeed do something; it prevent that loop from performing raycast. So, you save your millisecond there... but not the garbage generated.

    3) With 3 camera active, we were getting 1.2Kb of garbage every frame. Unity perform a GetComponent<GUILayer>() on every camera. Since Unity 4.6, GetComponent is optimized to cache a previous search and not generate garbage every frame you invoke it. So, why does that specific call in that specific place generating so much crap? Simple, we had no GUILayer on our 2nd and 3rd camera. Unity - being again, a bit retarded - is unable to simply return "null" on a GetComponent. It's returning a new UnityEngine.Object that return true when compared to null, but is NOT null. Every frame you do GetComponent on a GameObject which does not have that component, it generates garbage.

    TL;DR: Do eventMask = 0 and make sure every single camera has a GUILayer. There, no more wasted ms and no more garbage.
     
  29. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    Thanks for playing detective for the rest of us. Will test this.
     
  30. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    @superpig, now that you are on the inside, and are a part of how the sausage is made, so to speak, any insights on this?

    Reflecting into Unity internals to turn off messages that might be used somewhere else just doesn't sound like a good idea.
     
  31. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    The 1.2Kb of garbage on the GetComponent only happens in Editor. Profile your game in a Player, not the Editor.
     
    DgoodingIndi likes this.
  32. silentneedle

    silentneedle

    Joined:
    Mar 14, 2013
    Posts:
    280
    Has this been fixed already? Even on our server scene (no camera, no gui) the garbage is generated.
     
  33. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I know about how unity create a string or something when getting a null off a GameObject, but honestly, Unity being unable to return a proper null is just plain stupid.
     
  34. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    @LightStriker There was a blog post about this about 2 years ago asking if they should change it, I'm assuming they never did.
     
  35. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    And I highly doubt they ever will.
     
  36. Gru

    Gru

    Joined:
    Dec 23, 2012
    Posts:
    142
    When the game is profiled as a build (not in Editor), some garbage will still be allocated from time to time due to this issue.



    For the most part, the allocations won't happen, but scrubbing down the Profiler shows there are still some allocations.

    This is something that should really be fixed.
     
    Mr-Knieves, Stardog and silentneedle like this.
  37. ImpactBlue

    ImpactBlue

    Joined:
    Jun 19, 2013
    Posts:
    15
    Setting the eventMask did not work for me.

    What did work was making sure your main camera has the GUILayer component. The garbage seems to be created by internal mouse events constantly calling GetComponent<GUILayer>().
     
    Polan and andrew-lukasik like this.
  38. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    This totally worked for me:
    Since GuiLayer is slowly becoming depreciated I assume this issue will resolve itself as soon as this class disappears completely in one of future releases.
     
  39. FiveFingerStudios

    FiveFingerStudios

    Joined:
    Apr 22, 2016
    Posts:
    510
    Thanks @ImpactBlue
    This seems to still be an issue. I'm in 2017.4.3

    Adding the GUILayer Component to the main camera solved the issue for me.
     
    Last edited: Jul 13, 2018
  40. mmvlad

    mmvlad

    Joined:
    Dec 31, 2014
    Posts:
    98
    And in 2018.2 still exists LOL.
    But hey, unity added a new transform tool that combines scale rotate and translate, that literally no one asked for. So they are kinda doing something.
     
  41. Enrico-Monese

    Enrico-Monese

    Joined:
    Dec 18, 2015
    Posts:
    77
  42. mmvlad

    mmvlad

    Joined:
    Dec 31, 2014
    Posts:
    98
    It was experimental, released not that long ago I believe. Ok take new editor theme as another example.
    The point is that long-standing problems are ignored for years (bug tracker was full of those), but lots of resources are spent on cosmetics or useless features.
    It makes the engine mostly fitted for fun dev. To be able to ship something top quality you have to build most components from the ground. The engine is hobby oriented. UI needs to be rewritten completely, terrain also along with many other systems. Only because many games run on top tier hardware these problems get overlooked by many. But if you try to do something top quality for mobile - welcome to the DIY world where all if left from Unity is pure rendering, all other systems must be implemented from scratch or use 3rt party solutions.
     
    moonlit_devteam likes this.
  43. zapposh

    zapposh

    Joined:
    Nov 12, 2016
    Posts:
    117
    1.2kb GC / frame due to SendMouseEvents. Still no real solution after all this time.
     
  44. IdiotsCode

    IdiotsCode

    Joined:
    Aug 23, 2015
    Posts:
    15
    Woo! I figured out a fix, seems to work in 2018.3.6. Adding a GUI Layer (Deprecated) to the main camera fixes this issue for me, hope this helps someone!
     
    Stardog likes this.
  45. LindsayParmenter

    LindsayParmenter

    Joined:
    Sep 10, 2014
    Posts:
    21
    Adding GUILayer to every camera fixed it for me...

    Nasty workaround - but seems to work for now. Thanks.
     
    Last edited: Aug 31, 2019
  46. tkeene_vspatial

    tkeene_vspatial

    Joined:
    May 2, 2017
    Posts:
    4
    Can confirm that GUILayer fix works in 2019.2.5f1. Saves 0.6KB per second. Only had to put it on the main camera for my scene. Very annoying, hope Unity fixes it.
     
  47. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,913
    Also worked for me. Now a garbage free game :)
     
  48. laessnb

    laessnb

    Joined:
    Jun 10, 2014
    Posts:
    101
    Likewise, though I had to add this to every camera in my scene. Maybe it depends on how they're randomly sorted. Profiling standalone builds is "correct", but the whole point of Unity is fast iteration. I'm not waiting for a standalone build to finish each time I want to test an improvement, and with this last change, our game is GC free while running in the editor.
     
  49. mgregoirelds

    mgregoirelds

    Joined:
    Feb 3, 2017
    Posts:
    22
    Any idea on how to fix this on Unity 2019.3 as GUILayer component has been removed (and it still takes about 0.52 ms)?
     
  50. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    @superpig, will you ever save yourself 8 years ago from this editor GC pain?

    It really does cause a huge toll on editor perf, and makes the profiler a lot harder to use to find real allocations.