| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- using System;
- using System.Diagnostics;
- using System.Collections.Generic;
- using System.Runtime.InteropServices;
- using System.Linq;
- using static System.Reflection.IntrospectionExtensions;
- #if ATOMIC_IOS
- using ObjCRuntime;
- #endif
- namespace AtomicEngine
- {
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate void EventDispatchDelegate(IntPtr sender, uint eventType, IntPtr eventData);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate void UpdateDispatchDelegate(float timeStep);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate void RefCountedDeletedDelegate(IntPtr refCounted);
- public class NativeType
- {
- public Type Type => type;
- public NativeType(IntPtr nativeClassID, Type type, Func<IntPtr, RefCounted> managedConstructor)
- {
- this.nativeClassID = nativeClassID;
- this.type = type;
- this.managedConstructor = managedConstructor;
- NativeCore.RegisterNativeType(this);
- }
- internal Type type;
- internal IntPtr nativeClassID;
- internal Func<IntPtr, RefCounted> managedConstructor;
- }
- public static class NativeCore
- {
- static internal void SubscribeToEvent(AObject receiver, uint eventType)
- {
- SubscribeToEvent(receiver, null, eventType);
- }
- static internal void SubscribeToEvent(AObject receiver, AObject sender, uint eventType)
- {
- List<EventSubscription> eventReceivers;
- if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
- {
- eventReceivers = eventReceiverLookup[eventType] = new List<EventSubscription>();
- }
- AObject obj;
- foreach (EventSubscription er in eventReceivers)
- {
- if (!er.Receiver.TryGetTarget(out obj))
- continue; // GC'd
- // already on list?
- if (obj == receiver)
- return;
- }
- WeakReference<RefCounted> w = null;
- if (!nativeLookup.TryGetValue(receiver.nativeInstance, out w))
- {
- throw new InvalidOperationException("NativeCore.SubscribeToEvent - unable to find native receiver instance");
- }
- eventReceivers.Add(new EventSubscription(receiver, sender));
- }
- static internal void UnsubscribeFromEvent(AObject receiver, uint eventType)
- {
- List<EventSubscription> eventReceivers;
- if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
- return;
- AObject obj;
- foreach (EventSubscription er in eventReceivers)
- {
- if (!er.Receiver.TryGetTarget(out obj))
- continue; // GC'd
- if (obj == receiver)
- {
- eventReceivers.Remove(er);
- return;
- }
- }
- }
- static internal void UnsubscribeFromAllEvents(AObject receiver)
- {
- // TODO: Optimize
- AObject obj;
- foreach (var subList in eventReceiverLookup.Values)
- {
- subList.RemoveAll(item => item.Receiver.TryGetTarget(out obj) && obj == receiver);
- }
- }
- static internal void RemoveEventSender(IntPtr sender)
- {
- //TODO: OPTIMIZE
- if (sender == IntPtr.Zero)
- return;
- foreach (var subList in eventReceiverLookup.Values)
- {
- var len = subList.Count;
- subList.RemoveAll(item => item.Sender == sender);
- //TODO: The events are still in the receiver lookup tables!
- }
- }
- static ScriptVariantMap[] svm;
- static int svmDepth = 0;
- const int svmMax = 256;
- internal static void Initialize()
- {
- // preallocate script variant maps
- svm = new ScriptVariantMap[svmMax];
- for (int i = 0; i < svmMax; i++)
- svm[i] = new ScriptVariantMap();
- }
- static float expireDelta = 0.0f;
- // called ahead of E_UPDATE event
- #if ATOMIC_IOS
- [MonoPInvokeCallback(typeof(UpdateDispatchDelegate))]
- #endif
- public static void UpdateDispatch(float timeStep)
- {
- expireDelta += timeStep;
- if (expireDelta > 2.0f)
- {
- expireDelta = 0.0f;
- // TODO: tune GC
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
- ExpireNatives();
- }
- }
- #if ATOMIC_IOS
- [MonoPInvokeCallback(typeof(EventDispatchDelegate))]
- #endif
- public static void EventDispatch(IntPtr sender, uint eventType, IntPtr eventData)
- {
- List<EventSubscription> eventReceivers;
- if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
- {
- // This should not happen, as event NET objects are subscribed to are filtered
- throw new InvalidOperationException("NativeCore.EventDispatch - received unregistered event type");
- }
- // iterate over copy of list so we can modify it while running
- ScriptVariantMap scriptMap = null;
- NativeEventData nativeEventData = null;
- AObject receiver;
- foreach (EventSubscription er in eventReceivers.ToList())
- {
- // GC'd?
- if (!er.Receiver.TryGetTarget(out receiver))
- continue;
- if (er.Sender != IntPtr.Zero && er.Sender != sender)
- continue;
- if (scriptMap == null)
- {
- if (svmDepth == svmMax)
- {
- throw new InvalidOperationException("NativeCore.EventDispatch - exceeded max svm");
- }
- scriptMap = svm[svmDepth++];
- scriptMap.CopyVariantMap(eventData);
- nativeEventData = NativeEvents.GetNativeEventData(eventType, scriptMap);
- nativeEventData.sourceEventData = eventData;
- }
- receiver.HandleEvent(eventType, scriptMap, nativeEventData);
- }
- if (scriptMap != null)
- {
- svmDepth--;
- if (nativeEventData != null)
- {
- NativeEvents.ReleaseNativeEventData(nativeEventData);
- }
- }
- }
- static void ExpireNatives()
- {
- var watch = new Stopwatch();
- watch.Start();
- // expire event listeners
- //int eventListenersRemoved = 0;
- //int nativesRemoved = 0;
- AObject obj;
- foreach (List<EventSubscription> receiverList in eventReceiverLookup.Values)
- {
- foreach (EventSubscription er in receiverList.ToList())
- {
- if (!er.Receiver.TryGetTarget(out obj))
- {
- receiverList.Remove(er);
- //eventListenersRemoved++;
- }
- if (watch.ElapsedMilliseconds > 16)
- break;
- }
- if (watch.ElapsedMilliseconds > 16)
- break;
- }
- RefCounted r;
- foreach (var native in nativeLookup.Keys.ToList())
- {
- var w = nativeLookup[native];
- if (!w.TryGetTarget(out r))
- {
- // expired
- csi_AtomicEngine_ReleaseRef(native);
- nativeLookup.Remove(native);
- //nativesRemoved++;
- }
- if (watch.ElapsedMilliseconds > 16)
- break;
- }
- /*
- if (nativesRemoved != 0 || eventListenersRemoved != 0)
- {
- Console.WriteLine("Released {0} natives and {1} event receivers", nativesRemoved, eventListenersRemoved);
- }
- */
- }
- // Called from RefCounted native destructor, refCounted is not valid for any operations here
- #if ATOMIC_IOS
- [MonoPInvokeCallback(typeof(RefCountedDeletedDelegate))]
- #endif
- public static void RefCountedDeleted(IntPtr refCounted)
- {
- nativeLookup.Remove(refCounted);
- RemoveEventSender(refCounted);
- }
- // register a newly created native
- public static IntPtr RegisterNative(IntPtr native, RefCounted r)
- {
- if (nativeLookup.ContainsKey(native))
- {
- throw new InvalidOperationException("NativeCore.RegisterNative - Duplicate IntPtr key");
- }
- r.nativeInstance = native;
- // keep native side alive
- r.AddRef();
- nativeLookup[native] = new WeakReference<RefCounted>(r);
- r.InstantiationType = InstantiationType.INSTANTIATION_NET;
- r.PostNativeUpdate();
- return native;
- }
- // wraps an existing native instance, with downcast support
- public static T WrapNative<T>(IntPtr native) where T : RefCounted
- {
- if (native == IntPtr.Zero)
- return null;
- RefCounted r;
- WeakReference<RefCounted> w;
- // first see if we're already available
- if (nativeLookup.TryGetValue(native, out w))
- {
- if (w.TryGetTarget(out r))
- {
- // we're alive!
- return (T)r;
- }
- else
- {
- // we were seen before, but have since been GC'd, remove!
- nativeLookup.Remove(native);
- if (csi_Atomic_RefCounted_Refs(native) == 1)
- {
- // only managed ref remains, so release and return null
- csi_AtomicEngine_ReleaseRef(native);
- return null;
- }
- csi_AtomicEngine_ReleaseRef(native);
- }
- }
- IntPtr classID = RefCounted.csi_Atomic_RefCounted_GetClassID(native);
- // and store, with downcast support for instance Component -> StaticModel
- // we never want to hit this path for script inherited natives
- NativeType nativeType;
- if (!nativeClassIDToNativeType.TryGetValue(classID, out nativeType))
- {
- throw new InvalidOperationException("NativeCore.WrapNative - Attempting to wrap unknown native class id");
- }
- r = nativeType.managedConstructor(native);
- w = new WeakReference<RefCounted>(r);
- NativeCore.nativeLookup[native] = w;
- // store a ref, so native side will not be released while we still have a reference in managed code
- r.AddRef();
- // Note: r.InstantiationType may be INSTANTIATION_NET here is we were GC'd, native still had a reference, and we came back
- if (r.InstantiationType == InstantiationType.INSTANTIATION_NET)
- {
- //Log.Warn($"Wrapped {r.GetType().Name} was originally instantiated in NET, changing to native, this is likely an error");
- //r.InstantiationType = InstantiationType.INSTANTIATION_NATIVE;
- }
- r.PostNativeUpdate();
- return (T)r;
- }
- [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- private static extern IntPtr csi_Atomic_AObject_GetTypeName(IntPtr self);
- [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- private static extern int csi_Atomic_RefCounted_Refs(IntPtr self);
- public static void RegisterNativeType(NativeType nativeType)
- {
- if (nativeClassIDToNativeType.ContainsKey(nativeType.nativeClassID))
- {
- throw new InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType class id registered");
- }
- if (typeToNativeType.ContainsKey(nativeType.type))
- {
- throw new InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType type registered");
- }
- nativeClassIDToNativeType[nativeType.nativeClassID] = nativeType;
- typeToNativeType[nativeType.type] = nativeType;
- }
- public static bool IsNativeType(Type type)
- {
- if (typeToNativeType.ContainsKey(type))
- return true;
- return false;
- }
- public static Type GetNativeAncestorType(Type type)
- {
- Type ancestorType = type;
- do
- {
- ancestorType = ancestorType.GetTypeInfo().BaseType;
- } while (ancestorType != null && !IsNativeType(ancestorType));
- return ancestorType;
- }
- static public IntPtr NativeContructorOverride
- {
- get
- {
- IntPtr value = nativeContructorOverride;
- nativeContructorOverride = IntPtr.Zero;
- return value;
- }
- set
- {
- if (nativeContructorOverride != IntPtr.Zero)
- {
- throw new InvalidOperationException("NativeCore.NativeContructorOverride - Previous nativeContructorOverride not consumed");
- }
- nativeContructorOverride = value;
- }
- }
- static public void VerifyNativeContructorOverrideConsumed()
- {
- if (nativeContructorOverride != IntPtr.Zero)
- {
- throw new InvalidOperationException("NativeCore.VerifyNativeContructorOverrideConsumed - NativeContructorOverride not consumed");
- }
- }
- private static IntPtr nativeContructorOverride = IntPtr.Zero;
- // weak references here, hold a ref native side
- internal static Dictionary<IntPtr, WeakReference<RefCounted>> nativeLookup = new Dictionary<IntPtr, WeakReference<RefCounted>>();
- // weak references here, hold a ref native side
- internal static Dictionary<uint, List<EventSubscription>> eventReceiverLookup = new Dictionary<uint, List<EventSubscription>>();
- // Native ClassID to NativeType lookup
- internal static Dictionary<IntPtr, NativeType> nativeClassIDToNativeType = new Dictionary<IntPtr, NativeType>();
- // Managed Type to NativeType lookup
- internal static Dictionary<Type, NativeType> typeToNativeType = new Dictionary<Type, NativeType>();
- [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- private static extern void csi_AtomicEngine_ReleaseRef(IntPtr refCounted);
- internal struct EventSubscription
- {
- public WeakReference<AObject> Receiver;
- public IntPtr Sender;
- public EventSubscription(AObject receiver)
- {
- Receiver = new WeakReference<AObject>(receiver);
- Sender = IntPtr.Zero;
- }
- public EventSubscription(AObject receiver, IntPtr sender)
- {
- Receiver = new WeakReference<AObject>(receiver);
- Sender = sender;
- }
- }
- }
- }
|