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:
- 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).
- 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:
if (camera.eventMask != 0)
{
if (camera.farClipPlane > 0f Physics.Raycast(camera.ScreenPointToRay(mousePosition), out raycastHit, camera.farClipPlane, camera.cullingMask camera.eventMask -5))
{
StoreCameraAndObjectHitInHitInfo();
}
else if (camera.farClipPlane > 0f)
{
if (Physics2D.GetRayIntersectionNonAlloc(camera.ScreenPointToRay(mousePosition), SendMouseEvents.m_MouseRayHits2D, camera.farClipPlane, camera.cullingMask camera.eventMask -5) == 1)
{
StoreCameraAndObjectHitInHitInfo();
}
}
else if (camera.clearFlags == CameraClearFlags.Skybox || camera.clearFlags == CameraClearFlags.Color)
{
//Don't get the point of this, isn't HitInfo already null?
SetHitInfoToNull();
}
}
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.