Comments (8)
Whoo hoo the prototype is working, running on the new bindings layer!
from cesium-unity.
- The mechanism used to pass exceptions between C# and C++ is not thread safe, so it could end up raising an exception generated in one thread in another thread.
from cesium-unity.
GCHandles are not reference counted, only explicitly allocated and freed. C++ wrappers hold onto a GCHandle. So there are two broad ways of working:
a. Every C++ wrapper instance owns its GCHandle, which it frees in its destructor. If a wrapper is copied, the GCHandle must be copied as well (i.e. call into C# to resolve GCHandle to object, create a new GCHandle from the same object, and return it to the C++ code).
b. GCHandles are shared between C++ wrapper instances, and we use reference counting to know when the last GCHandle reference is released so that we can free it.
UnityNativeScripting
does (b), but I think (a) is the better choice. (a) makes copying wrappers more expensive (requiring a round-trip to C#), but that's easily dealt with in most cases by passing wrappers by reference instead of by copy. Meanwhile (b) will make intial creation much more complicated and/or expensive because we need a separate place to store the reference count. That means a heap allocation to hold the handle and reference count, or some sort of handle -> reference count map with the associated complexity and thread safety concerns.
from cesium-unity.
We need to support at least the following types in method/property signatures:
- Integer types: Int16, Int32, Int64, UInt16, UInt32, UInt64
- Floating-point types: Single, Double
- Boolean
- IntPtr (as void*)
- Arbitrary C# classes (as GCHandles)
- Arbitrary blittable C# structs (pass by reference, return by value)
- Arbitrary non-blittable C# structs (pass as opaque pointers, return as boxed value)
from cesium-unity.
Some useful resources:
- UnityNativeScripting blogs - Series of articles behind UnityNativeScripting, which the prototype uses and which is very much the inspiration for our own binding generator.
- Incremental Roslyn Source Generators - Intro to the C# compiler (Roslyn) feature that we're using as the "hook" to generate the bindings layer.
- Working with types in a Roslyn analyzer
- Roslyn docs:
from cesium-unity.
We can access non-blittable value types (C# structs) from the C++ side in two possible ways:
- Box the value, then treat it exactly like we do a class (i.e. use GCHandle)
- Get a pointer to the struct, pass the pointer to the C++ side, and use that pointer as the object's identity.
(1) will always work, but requires boxing, which is a heap allocation and a copy.
(2) costs only as much as the getting a pointer to the object. If that requires pinning, it's not worth it. But getting a pointer to a method parameter or local variable is basically free. As long as we accept that the pointer is only valid for the lifetime of the method.
Ideally, we do both. Use the pointer when we can, and otherwise use a handle to the boxed value. This can be implemented as a variant on the C++ side. On copy or move of the C++ object, we convert the pointer to the GCHandle.
But it probably makes sense to stick to (1) until we see a clear benefit to optimizing with (2).
from cesium-unity.
Currently, managed classes that have some of their methods implemented in C++ (e.g. MonoBehaviours) hold an IntPtr to the C++ implementation class. And they have a Dispose method and a finalizer to make sure that this C++ implementation class is destroyed at the appropriate time. This isn't great for two reasons:
- When nothing else references a managed object, it's possible for the finalizer to be called while the native code is still executing. This problem is described pretty well here: https://www.mono-project.com/docs/advanced/pinvoke/#gc-safe-pinvoke-code. This shouldn't be a problem in our case because we also pass a reference to the managed class to the native code, so effectively the native code keeps the managed object alive. But this is a little precarious.
- Because of the way finalizers impose work on the garbage collector, finalizable classes should be super lightweight and not hold references to other managed objects. But this will not always be the case. Consider MonoBehaviour, for example.
Both of these problems can be solved by holding a SafeHandle
-derived class instead of holding an IntPtr directly. The SafeHandle encapsulates the finalization logic, keeping the GC overhead as low as possible. The P/Invoke system is also smart about SafeHandles and will ensure they are not finalized while they are in use in native code.
from cesium-unity.
Events and Delegates are tricky. UnityNativeScripting's approach works, but it gets the semantics wrong (IMO). Its approach is described here: https://www.jacksondunstan.com/articles/4174
The problem is in how it manages lifetime. In normal C#, it works like this. If I have an object A which has an event or delegate field MyDelegate, and I assign it a delegate pointing to a method DoStuff on my object B, like this:
A.MyDelegate = B.DoStuff;
Then B won't get garbage collected as long as A is ineligible for garbage collection and that delegate instance continues to exist. That's true even if no one else in the entire app has a reference to B.
On the other hand, the existence of this delegate has no impact on the lifetime of A. If no references to A exists anywhere, then A will be garbage collected, as will myDelegate. If no other references to B exist (other than the delegate), then B will be garbage collected too.
This is all sort of obvious if you understand how delegates work. A delegate instance is really nothing more than a class instance with a target object field and a target method field. However, it often trips up people who are new to C#.
Now extending this to C++, we should be able to create a delegate around a C++ std::function or class instance, and that function or class instance should be kept alive for as long as the delegate is kept alive. With UnityNativeScripting, however, the C++ object's lifetime must be controlled explicitly, and when it is destroyed the delegate effectively becomes inert. Invoking it does nothing.
This is confusing, and attempts to work around it can easily lead to memory leaks.
Consistent with out how we handle C# classes with some of their methods implemented in C++, we should aim to make the semantics match those in C#.
To achieve this, we need:
- A delegate (e.g.
Action
) constructor that takes astd::function
. - The constructor must copy/move the std::function instance into a heap-allocated function. We can do fancier things like pooling, but this is the simplest thing.
- Call a delegate create function on the C# side, passing it a void* to the std::function.
- The C# code creates an instance of a class generated for this purpose, e.g.
ActionNative
. The instance holds onto that void* from the C++ side, and has a Dispose method and a finalizer to delete it at the appropriate time. ActionNative
has anInvoke
method, and a regular Action delegate is created pointing to that Invoke method. A handle to this delegate is returned to the C++ code.- When the delegate is invoked, the Invoke method calls back into the C++ code, passing the void* to the std::function. The C++ code casts the void* back to a std::function* and invokes the function.
One thing that's not great about this plan is that the std::functions will only ever be freed by the finalizer, because C# doesn't have any pattern for explicitly disposing delegates. As long as we're not creating them rapid-fire this should be fine, though. If it does turn out to be a problem, we can probably optimize for certain cases, e.g. provide an explicit dispose on the C++ side, or dispose when the delegate is reassigned null.
from cesium-unity.
Related Issues (20)
- CesiumCreditSystem destroys itself when an additive scene is unloaded HOT 1
- Unable to override Cesium credit system because `CesiumCredit` is marked internal HOT 2
- Add bezier support to Cesium Cartographic Polygon
- Add support for viewing tiles in Iso orientation HOT 3
- Massive Memory Leak in Multitileset Scenes HOT 1
- Android one wont run - Error Unity DllNotFoundException: CesiumForUnityNative HOT 4
- 3Dtile is at the wrong place HOT 1
- Dynamic Camera suddenly slows a lot when going down
- option to 'Terrain Refresh instead of Application Termination/Crash' HOT 1
- Warnings in CesiumSamplesRequiresMagicLeap.cs
- When install Cesium in Unity, the web browser to login to Cesium does not appear? Any thoughts on this?
- BINARY_INVALID_ALIGNMENT works in CesiumJS but not in Cesium-Unity HOT 2
- No way to build and run on android HOT 2
- Error Scaling map on magic leap 2 HOT 1
- Error compiling shaders when building for android HOT 2
- Some Ion imagery assets attempt to load from localhost instead of the correct URLs HOT 1
- Android build broken on v1.9.0 HOT 4
- Rare crash when launching multiple clients HOT 2
- Error on build (missing header files) HOT 7
- OriginShift Lagspikes
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cesium-unity.