Skip to content

Conversation

rogerbarton
Copy link
Contributor

@rogerbarton rogerbarton commented Mar 21, 2020

  1. Editors (component/window) are repainted when dll's are un/loaded using the new attributes
    • Involves running the triggers on the main thread in a queue 39c4fce
  2. Editor variable changes are detected and saved properly
    • Involves creating a clone of the options and comparing changes when the GUI has changed 5bda990

Note: The attributes [NativeDllLoadedTrigger] etc are not searched for in the source code of this tool itself. So by creating an .asmdef in #14 these attributes may not work, do you maybe know of an easy fix for this @mcpiroman?.

Before changing a variable would not be detected and so not saved/serialized in some cases.
This would not work with the `upm-support` branch mcpiroman#14 as the attributes are not searched for in the attribute that these scripts are in by default.
Copy link
Owner

@mcpiroman mcpiroman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: The attributes [NativeDllLoadedTrigger] etc are not searched for in the source code of this tool itself

Yeah, I just search for them in the same assemblies as for native function declarations. I guess I should just search every assembly and maybe exclude standard unity ones for perf, because searching in every assembly doesn't sound the quickest thing.

Admittedly this should have been in mcpiroman#12. However, here are the fixes anyways.

GUI buttons to un/load show in edit mode if enableInEditMode is true. Disabling and re-enabling the DllManipulatorScript works now.
@rogerbarton rogerbarton mentioned this pull request Apr 9, 2020
4 tasks
Previously triggers would not be cleared properly and so be duplicated. (Static variables seem to persist between entering/exiting playmode)
Allow custom triggers to be executed on the main thread in a queue. We need this internally to repaint the editor GUI as it uses the Unity API.
@@ -46,7 +46,7 @@ private void OnEnable()
{
if (EditorApplication.isPlaying)
Destroy(gameObject);
else
else if(_singletonInstance != this)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allows the user to properly toggle enabled in edit mode.

@rogerbarton
Copy link
Contributor Author

Ready for review @mcpiroman.

You will probably want to review 39c4fce separately.

(side note: #14 should already be compatible with these changes)

Initialize() is not always called so we should not always reset.
@rogerbarton rogerbarton requested a review from mcpiroman April 25, 2020 08:13
Copy link
Owner

@mcpiroman mcpiroman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this took me this long to have a look at this.
I'm somewhat scared about that option cloning, comparing (mainly bout forgetting to update that), but (partly because I'm not quite in the problem it tries to solve), I don't see a better way.

/// <summary>
/// Should the method always be executed on the main thread, to allow use of the Unity API.
/// Note: this means the method is not immediately executed but put in a queue.
/// For consistent behaviour, the method is put in the queue even if it is triggered from the main thread.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just about the line 39 - I don't think this behavior is really beneficial, I imagine usually one would want main thread events to stay in order. And as of consistency, dispatching frameworks which I recall tend to invoke calls from target ("main") thread right away. For events though, while it doesn't prevent dead-locks as much as with direct calls, I think this also makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I've changed it. I wasn't quite sure either which is why I left it commented out:
https://github.com/rogerbarton/UnityNativeTool/blob/a0dc185babf73065dad5935be87e2de2f8d5811a/scripts/DllManipulator.cs#L547-L548


// Execute now or queue to the main thread
if (useMainThreadQueue /*&& Thread.CurrentThread.ManagedThreadId != _unityMainThreadId*/)
_mainThreadTriggerQueue.Enqueue(new Tuple<MethodInfo, object[]>(methodInfo, args));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using tuple to wrap arguments, it could be simpler to just store an Action created with lambda, like so:
_mainThreadTriggerQueue.Enqueue(() => methodInfo.Invoke(null, args)); (You could also make e.g. Action<int> to provide an argument at call side, JIC you didn't know).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes makes sense

}
else
{
Debug.LogError($"Trigger method must either take no parameters or one parameter of type {nameof(NativeDll)}. Violation on method {method.Name} in {method.DeclaringType.FullName}");
Debug.LogError($"Trigger method must either take no parameters or one parameter of type {nameof(NativeDll)} or two of type {nameof(NativeDll)} and int. Violation on method {method.Name} in {method.DeclaringType.FullName}");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or two of type {nameof(NativeDll)} and int at first reading this sounded like two NativeDll parameters and one int parameter, don't know if that's just me though.
Also why do you assume the main thread id information would be useful? And when it turns out to be, it should be documented somehow (or I just missed that, in which case I'm sorry)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it to be clearer and given examples of the possible functions in the TriggerAttribute doc.

The mainThreadId can be used to tell if we are on the main thread and are allowed to call unity functions. I guess this is partially obsolete now that we have the MainThreadTriggerQueue.

/// Executes queued methods.
/// Should be called from the main thread in Update.
/// </summary>
public static void InvokeMainThreadQueue()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't wanna be fussy, but I'd move the dispatcher stuff to DllManipulatorScript, as it is closer to Unity, whilst this is intended to do 'the stuff', if you will.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you only mean to move this function, not the queue itself

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the queue itself. I don't quite imagine how would you move just this method, so that might be interesting too.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the queue itself. I don't quite imagine how you would move just the method, so I don't disagree with that now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By making the queue public (its static already)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, but no, I see this whole in the Script as it is the interface with Unity.

@@ -57,6 +57,10 @@ private void OnEnable()

if(EditorApplication.isPlaying || Options.enableInEditMode)
Initialize();

if(!EditorApplication.isPlaying && Options.enableInEditMode)
EditorApplication.update += Update;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doens't just having [ExecuteInEditMode] make the Update be invoke in editor? Or do I misunderstand sth.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ExecuteInEditMode "Update is only called when something in the Scene changed" - Docs. So we need to manually add this to cover all cases.

[NativeDllAfterUnloadTrigger(UseMainThreadQueue = true)]
public static void RepaintAll()
{
RepaintAllEditors.Invoke();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's usually safer to handle null case with events (which can be done simply by putting ? before the dot). In this case you initialize the event, however I'm not sure if adding and removing an listener doesn't reset it back to null. Either or, it's better to be safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed however as the event is initialized with the default delegate it will never be null. https://stackoverflow.com/q/170907/9295437
Ill add it anyways...

Also RegisterTriggerMethod receives attribute instead of bool, 
Fix naming in DllManipulatorOptions
@rogerbarton
Copy link
Contributor Author

Regarding cloning the Options, its just that we need a separate copy for detecting changes. This copy is never changed directly only update when changes are made. Its pretty safe in my opinion.

I've actually found this quite a good solution.

@mcpiroman
Copy link
Owner

I'm fine with that, thanks!

@mcpiroman mcpiroman merged commit 1f0e9bc into mcpiroman:master May 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants