-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Height query API for Unity #507
Conversation
It will still fail, though, because of lack of a valid ion token while running the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple comments @kring ! I'm going to do the ones for documentation nitpicks, but there's a couple TODO's I spotted in the process.
I think that creating an async task is not intuitive to the beginner Unity user, so I'm putting together a working example of a script for my own reference. But I trust that this will work about the same as Unreal, since it's just a wrapper to the cesium-native function 😄
Well...
|
Async is never easy, but Unity coroutines should make this pretty easy compared to what users are faced with in cesium-native or cesium-unreal. The main trick is to realize you can wrap the task in I think the latest versions of Unity may support using async/await for coroutines, in which case this would be even easier (but I haven't tried it). |
I went on quite a debugging odyssey here, so I'm going to write it down for posterity... I was trying to handle the case that the user calls But this didn't work at all. The Tileset was still null inside the The first thing I noticed was that the BUT I happen to know how that mechanism works, and I knew it shouldn't be working in this case, which is why I thought it safe to use a
So of course I confirmed my understanding about when we're considered "in the main thread" (it was correct), and then started digging into how we determine whether we're inside one of the main thread continuations. Conceptually it's pretty simple. Just before we call the continuation, we push a thing onto a thread local vector (a stack). Then we call the continuation. Then we pop the thing off the thread local stack. We can determine we're inside a main thread continuation by whether the thing is on the stack. It turned out that every time I ran the tests, there would be another copy of the thing on the stack. So it seemed we were somehow failing to pop the thing off the stack. While conceptually simple, this whole thing is a little tricky beause of this thread-localness, and I thought that's where it was going wrong. I tried a bunch of things to address some potentially dodgy aspects of it. For example, the thread local variable is declared in a static function in a template class, which is defined in a header. Were we somehow getting a separate copy of the stack per compilation unit due to inlining? It seemed a plausible theory, but fixing that did not make the problem go away. Eventually with lots of debugging, I realized that there was a managed C# exception being thrown at the critical moment. Unity likes to reload AppDomains a lot (especially while running unit tests), which effectively destroys every C# object. However, our native code sometimes still has a reference to these C# objects. When the native code tries to do anything with these destroyed objects (including simply release our reference to them), .NET throws an ArgumentException with a message like Now what I didn't fully appreciate (even though I now realize that I knew this two years ago and wrote it down in #18!) is what happens when that C# exception gets thrown. The exception propagates up the C# call stack, as usual. But in this case, the C# code was invoked by our native code. There's no mechanism for turning a C# exception into a C++ one, so the C# exception goes straight through the C++ code stack without properly unwinding it 😱! No catch or or finally blocks are called. No RAII destructors are called. So yeah this is a disaster for most any C++ code. In this particular case, the RAII that was meant to say "we're leaving the continuation in which we know we're in the main thread" was never being called. So the "thing" stayed on the stack. And we were always considered to be in the main thread (on that thread) from then on. |
Because the general solution to the above (#18) is fairly involved, I hackily worked around it by catching this particular C# exception rather than letting it blow up our C++ call stack. With that, the However, More debugging. This time the problem was more mundane. Test A creates Cesium3DTileset-A, does its thing, and the test passes. Test B creates Cesium3DTileset-B and calls SampleHeightMostDetailed on it, which schedules a task with runInMainThread. Next frame, Unity calls Update on Cesium3DTileset-A, which dispatches the We could.... wait two frames? I decided to make (By the way, this can theoretically happen in Unreal, too. I need to check that.) |
This should be ready now. |
Thanks @kring for the fix -- sorry you had to go through that journey, but at least it works great now! 😅 Here's a snippet of the script I tried with the 04_CesiumSubScenes scene, just for my own future reference. I'm merging this now!
|
Builds on CesiumGS/cesium-native#783 so merge that first.
This is the Unity equivalent of CesiumGS/cesium-unreal#1421.
Note the small Reinterop change to support the C#
params
keyword, which lets you call a method taking an array as if it had an arbitrary number of distinct parameters.