Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - ChrisPruett

Pages: [1] 2
1
NGUI 3 Support / Re: Crash occurs in kitkat (under opengl 2.0)
« on: April 16, 2014, 06:17:19 PM »
I actually just commented out the entire line because I wanted to see if there would be side-effects in the editor.  I don't think the Unity version has much to do with the actual problem, although perhaps something under the hood masks the issue.  But yes, that change would have effectively solved it for me, as I wouldn't be trimming in this case on Android.

2
NGUI 3 Support / Re: Crash occurs in kitkat (under opengl 2.0)
« on: April 15, 2014, 09:53:09 AM »
Update: commenting out the space-saving trim has indeed resolved 100% of our crashes on Adreno 320/330 devices.  It also significantly improved performance on one particular device (the Moto X, I think).

I suppose our use case, or possibly NGUI, causes VBO reallocations at high frequency.  At any rate, this is probably something to consider for anybody shipping an NGUI game on Android.

3
NGUI 3 Support / Re: Crash occurs in kitkat (under opengl 2.0)
« on: April 11, 2014, 06:52:45 PM »
I think I've found a workaround.

This is clearly not an NGUI bug.  It's not even a Unity bug.  The Adreno 320 / 330 GLES2 driver is at fault here.  Here's some folks who are not using Unity at all that have hit this crash:

https://github.com/opensciencemap/vtm/issues/52

Anyway, the crash callstack is proceeded by some spew from the driver like this:

04-11 13:41:21.927: W/Adreno-GSL(12206): <ioctl_kgsl_sharedmem_write:1668>: kgsl_sharedmem_write:invalid arg offset 49152 size 1536 memdesc size 4096

and the crash itself is in memcpy, so it looks like this has something to do with moving verts to a buffer that isn't sized correctly.  Or something.

On one of my test devices I was able to get the crash to occur at a particular point, and then by manipulating unrelated things, I actually moved the point of the crash about 2 seconds into the future (i.e. it took 2 seconds longer to occur than the normal trigger point).  This suggests to me that this might be some sort of cache buffer overflow in the driver; perhaps error accumulates until finally the crash occurs.

On the theory that reducing the number of vbo size changes might placate the driver, I commented out the line that Aren indicated above (setting trim if the number of verts is < 50% of the buffer in UIDrawCall).  This probably wastes some memory, as we have some VBOs that are allocated for many more verts than they need, but on the other hand if we have a case where the contents of the VBOs change rapidly, and if the driver dies because of an accumulated overflow, this might be sufficient to avoid the crash.

So far it appears to work.  I've run for about an hour on the Galaxy S4 with zero crashes (before this change I could repro in < 1 minute).  I need to do some more testing on other devices, but for now it seems like a pretty benign way to work around the crappy driver.

4
NGUI 3 Support / Re: Crash occurs in kitkat (under opengl 2.0)
« on: April 11, 2014, 11:46:35 AM »
We have a bunch of users who are experiencing this as well, but haven't been able to reproduce it locally yet.

What we do know is that dynamic batching isn't related to this crash.  Same failure occurs on or off.  This is also not the same as the 32-bit depth buffer workaround.  Symptoms are that users are able to play for a while before the process suddenly dies without warning.

I'm agreed that the bug here is in the Adreno 320 driver, but if there's a way to workaround it I'd like to find it.

Aren, other than the UIDrawCall trimming, is there anything that changed between 3.0.8f4 and latest that might have something to do with VBO caching that I could poke at?  It's likely that nothing NGUI is doing is explicitly wrong, but if some subtle change in the way that vertex arrays are handled has caused this bug to surface, I might be able to work around it by changing that area of behavior.

Thanks.

5
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 10, 2014, 01:30:32 AM »
Cool, thanks.  I'll post something here if I come up with a better solution for the first manifestation of this problem.  For now, the timing hack will suffice for us.

6
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 09, 2014, 01:51:19 AM »
Replying to my own message above, this mod to line 1472 of UICamera.cs seems to do the trick with no obvious side-effects:

  1. if (currentTouch.clickNotification != ClickNotification.None && currentTouch.pressed == currentTouch.current)
  2.  

7
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 08, 2014, 09:17:34 PM »
Another manifestation of this problem that I noticed today.

In some cases we have widgets that pop up or slide in over other content.  This isn't a pop-up window, but the overlay content obscures the things below it and has a collider to block clicks.  Think of a comic speech bubble sort of element that appears at somewhat arbitrary places and might have a button embedded within it, and so it blocks clicks to whatever it happens to be on top of.

In this scenario, we can break our game by doing the following:

- Depress a button that is below where we know the speech bubble will appear.  Hold this finger down.  This is Finger #1.
- Using another finger, click (press and release) a button that causes the speech bubble to pop up.  This is Finger #2.
- Now the speech bubble is up right under Finger #1.  It has a collider that should prevent Finger #1 from clicking on the button under the bubble.
- Release Finger #1.  The button under the bubble receives a click.  Since the game was designed around the idea that we could trust the collider on the speech bubble to shield clicks to things below that bubble, the game is now in an undefined (and quite broken) state.

The reason this happens is that in UICamera, currentTouch.pressed is assumed to be valid without a confirmation raycast when the unpressed event occurs.  When currentTouch.pressed is initialized, the raycast succeeds and everything looks fine.  Then, based on stuff that Finger #2 does, the content of the scene changes and a raycast between the finger and currentTouch.pressed should fail.  But it isn't reevaluated, even on release.  So a button that the finger should not be able to reach is clicked.

You might think that this would never happen in the course of regular play, but we caught it because we got testers complaining about bad state situations even after we added the timing hacks I discussed above.  The timing heuristic doesn't help in this scenario because there's no requirement that Finger #1 and Finger #2 come up the same frame, or even within a short duration.  Finger #1 just has to come up anytime after Finger #2 has caused the overlay to spawn.

This isn't the kind of thing we can fix with a window manager because the intent is to only disable clicking below the small portion of the screen that the speech bubble obscures.  And since the bubble can appear in many locations, it's not just a single button that we have to worry about.

I suspect that I could add a test to ensure currentTouch.pressed == lastHit, but I haven't tried this yet.  Mostly this post is just me thinking out about the problem.

8
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 06, 2014, 06:27:32 PM »
If I understand what you're suggesting correctly, you believe that making SendMessage exit out early when one script receives the "abc" function call would solve your issues. I don't see how. First, there is no way to guarantee which script will receive this message first. Is it going to be script A, or script B? And furthermore, why is creating two OnClick listeners on the same game object such a big issue to begin with?

No, I'm sorry, that's not what I was getting at, although that is another issue with SendMessage.

In every other UI system I've worked with in the past, there's be a concept of "consuming" an event.  For example, a button might consume a click event if it has processed it, or not if it wants other things to continue processing.  The first problem with SendMessage() is that there is no return value.  The simplest way that I can think of adding the concept of click consumption to NGUI's existing structure is to allow OnClick() to return true or false.  The most basic heuristic in this case would be to stop sending click events to other colliders once one of them returns true. 

But you can't shoehorn this in to NGUI very easily because SendMessage() doesn't allow you to receive a return value from a method call, or even know whether a method has been called.

On top of that there is the issue you brought up, where you have multiple scripts on the same object responding to a click.  That's also something that is very useful and should be maintained; our heuristic was to say "first collider reporting consumption wins" rather than "first script reporting consumption wins."  This way we still take advantage of multiple scripts on a single collider that respond to click.

But it's all a bit of a hacked mess.  We have a (fairly complicated) window manager.  We have a UI screen state machine system.  We still have cases where you can break our game by hitting the right two buttons correctly.  I suspect that most NGUI games have this issue.  I don't have a great solution to propose (I'm not very happy with my own), which is why I posted originally here.

Quote
P.S. Have you also tried simply changing UICamera's Notify function to this?

I'm not sure filtering on activity is sufficient.  I'll give it a shot, though.  Thanks.

Nikki,

Quote
Couldn't you do something as simple as switch off allowMultiTouch in the instances where you want to make sure only one button is clicked

Sure, but then we're back to the bad UX of not allowing multi touch.  If you've ever watched somebody grip a phone (esp. a small one, like the iPhone 5), it's exceedingly easy to have a finger touching the edge of the screen without realizing it.  If this finger is the one that gets the first touch id then the game suddenly feels hung when you try to tap stuff.

As I wrote above, I'm not asking you to go gut the whole event system to support this, at least not today.  I'm more just trying to put this on your radar and maybe field ideas for a workaround I can implement myself.

9
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 04, 2014, 10:22:54 PM »
Ryan, my "fixes" for this are similar to yours.  Basically opt-outs at the top of all my OnClick() methods.  The singleton code in Rise of the Blobs looked like this:

  1. public bool ProposeClick(Collider collider)
  2. {
  3.         bool clickAllowed = false;
  4.        
  5.         Logger.Assert(collider != null, "NULL Collider passed to Click Focus Manager!");
  6.         float time = Time.realtimeSinceStartup;
  7.         if (lastClickTime == -1 || lastClickTime + MINIMUM_CLICK_DELAY <= time || (collider == lastClickCollider && collider != null))
  8.         {
  9.                 clickAllowed = true;
  10.                 lastClickTime = time;
  11.                 lastClickCollider = collider;
  12.         }              
  13.         return clickAllowed;
  14. }

... then at the top of each OnClick script:

  1. if (!ClickFocusManager.GetInstance().ProposeClick(gameObject.collider)) { return; }

I do not like this solution very much.  It is error prone.

I agree that a real solution is complicated, though I don't think it would involve any extra runtime overhead.  If anything, it probably reduces the number of raycasts and SendMessage() calls per frame while fingers are down.  But I agree that it's not correct for everything.

I do think that this is the sort of core functionality that should be built into NGUI, though.  It's not just window transitions that are affected; every OnClick() that doesn't consider what else might have been clicked in the very recent past is a potential vector for a race condition.  Rather than solve it in every OnClick() method, it's much more robust to solve in the thing sending the events.

My thought had been that if OnClick() were treated as a proper method call, UICamera could simply drop out of the send clicks loop (when this option is enabled).  That might not catch 100% of the cases; there might need to be some heuristic involved, such as "don't send click events until all fingers intersecting colliders have come up or moved far enough away to no longer be considered for clicks."  I don't know what the best heuristic should be.

My question was: if I'd like to implement this myself, and I'd rather not modify UICamera directly, what would you try?  I am thinking of making a ClickReceiver script that implements OnClick, applies some heuristic, and then calls some other method on its children.  At least this way I don't have to modify every click script in our 70k line codebase.  I'll still have to find every instance of every object that can be clicked on and add this script, though.

Perhaps there's a better way?  We haven't used the new event delegate system much yet, but I'll poke around in there.

10
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 04, 2014, 03:16:23 PM »
I am not recommending that you put the 100ms hack into NGUI.  It's a hack workaround that I do not like very much.

I'm also not suggesting that you remove the ability to click on multiple things in a single frame.  If you read my messages above, our game actually relies on this functionality as well in some contexts (gameplay buttons).  It's certainly a valid use case.

However, I strongly disagree with this:
Quote
Simply put, if your game requires this kind of restriction then you should add it for that particular game.

... because I think that the use case for one-button-at-a-time is also exceedingly common.  Common enough that it should probably be the default behavior of NGUI, and multi-button support an option.  In regular menus-and-buttons UI, which of course is what many of us use NGUI for, there's almost never a reason to allow multiple buttons to be clicked at once.  Modern touch OSes go out of their way to prevent this from happening, actually, because having to consider race conditions with every OnClick() script that you write is very arduous.

This is obviously an issue if games like Subway Surfers are forced to turn multitouch off to avoid having more than one button clicked in their UI.  It's certainly an issue for us, and I can see that the Temple Run devs tried to mitigate it with some success but still have problems.

So, to be very clear about my intent with this message: as a long-term feature/bug request, please consider reworking the way that click messages are sent to buttons so that we can avoid having to worry about these sorts of race conditions.

Now, the second part of my message is to ask you about ideas for potential workarounds that I could implement myself.  I think the feature I am requesting is a fairly huge, and I would expect it to take time and some thinking to do right.  But for now, I'm interested in ways I could modify my use of OnClick / UICamera to mitigate race conditions.  From that perspective...

Quote
SendMessage has nothing to do with any of this.

... SendMessage is actually the core of the problem, because messages do not have return values.  UICamera should probably stop sending click events after one of them reaches a target that processes it, but there isn't a good way to drop out of that loop at the moment because SendMessage is used rather than a function call.

I have a bunch of ideas for how I could modify this, or add some support scripts to protect myself against multiple simultaneous clicks.  But perhaps you have a better idea, or at least a direction that I could pursue?

Thanks.

11
I follow your line of thinking, but, in our personal use case, gradient support is not something we need or use regularly, where as reliable markup tint is something we need and use regularly, so this change is one we would prefer not to have. 

Allowing the blend to be gradient + tint when there is no embedded color and gradient + embedded color (no tint) when an embedded color exists would probably have worked better, at least for us.  We see the embedded color as a tint override.

I guess I'll write a version of UILabel that supports this.  My vote is still "current version is worse than previous version."

12
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 04, 2014, 01:16:24 AM »
No disagreement there, but none of those sound like good reasons to allow multiple onClick() events to be sent to different colliders in a small window of time. 

We have a window manager, it has support for transitions and stacks of windows and it disables colliders and all of that jazz.  It can probably be improved, but even doing so won't resolve this issue entirely (as the window transition problem is just one manifestation of the more general issue here).

I maintain that, in most UI environments, it's never correct to click on two different colliders simultaneously (or near-simultaniously).  The exception is on-screen gameplay buttons, and so this sort of multiclick should be supported.  However, I don't think it should be the default behavior.  It's much cleaner to solve once at the event broadcast level than at all of the various receiver levels. 

I understand that this isn't a simple change.  If it were, I would have just modded the code and moved on.  The reliance on SendMessage() is the core of the problem; at the very basic level, you can't exit your loop that sends clicks early if a button accepts the click because you can't tell when a click has been accepted.

So, barring an overhaul of how clicks work in NGUI, any suggestions for how we might tackle this in our codebase?  I'd rather not play whack-a-mole with all the potential race conditions again, though my "one collider per 100 ms" hack worked last time around.  Any ideas for a better solution would be appreciated.

Thanks!

13
Sure, but that's a lot more arduous than the old system.  We have text that is black by default but (depending on the text we jam into the label, which is dependent on game state) may be any number of colors at the same time.  Relying on markup to select the final color was exceedingly useful for this use case.  Now I suppose we can set the text color to white and use markup to mark all of it black in 90% of the cases... but it's going to be error-prone and a lot of work to support (we have a lot of text).

If we're voting, put me down for "markup should override final color."  I still don't understand the use case that prompted this change, but it complicates my use case quite significantly.

14
Thread necro.

So if I'm reading you correctly, it's no longer possible to use embedded colors to tint black text?  That seems... restrictive.

What is the use case that prompted the behavior change for this?  I feel like the embedded color should always override the tint color (though perhaps not the gradient color).  We highlight important text in red, etc.

15
NGUI 3 Support / Re: Fat finger multitouch problems
« on: March 02, 2014, 10:31:15 PM »
OK, so, in order:

- The general issue is that two OnClick() events can occur in a single frame or over a very short number of frames.  The first case occurs when you use two fingers to simultaneously press buttons.  The second case (within a few frames) is relevant for both one or two fingers.  I don't think there's a Unity bug here--I'm not getting more than one touch from a single finger per frame--but most touch screens do not report the same screen position for a given finger from frame to frame, even if you try to hold your hand perfectly steady. 

Consider the case where a pop-up dialog has two buttons, both of which spawn dialogs themselves.  If the dialog spawn results in a transition animation (say, the new dialog fades in), you can easily produce a case where you click both buttons with a single finger over the course of a few frames.  The first click causes a new dialog to start appearing, but before that dialog can finish popping up you manage to click the second button, which also spawns a dialog of its own, and now both dialogs are up at the same time and you probably never planned for this.

You can solve this *specific* case by writing a window manager, or by disabling colliders on the first OnClick(), but even this is difficult to do without race conditions.  And it requires a lot of work above-and-beyond the infrastructure that NGUI provides. 

- As you say, sometimes you want more than one click at once (the Wind-up Knight 2 HUD relies on that behavior, in fact).  But often--in menus and other non-gameplay UI--I think it is almost always a bad idea.  So I'm suggesting that perhaps there should be a mode that stops sending onClick() after one is "accepted" (a concept for which NGUI doesn't currently have a model, due to reliance on SendMessage()).  Our implementation isn't frame based, but it requires some time (100ms, if I remember correctly) before it will accept another click.  Humans cannot voluntarily move that fast, so it doesn't affect normal use, but it prevents the double click problem.  Such a mode built into UICamera would completely remove the chances of this sort of race condition without requiring us to write our own window managers to ensure that colliders are immediately disabled upon every click.

- To reproduce, try making two UIButtons next to each other with OnClick() scripts that play sounds.  Run this on a phone that supports multitouch and spam the screen near the buttons with your finger.  Eventually you'll get both sounds to trigger.  In the Rise of the Blobs case, some people were able to do this 100% of the time, and while adding the collider-disable hack "fixed" the issue for those folks, we were never able to figure out exactly how they were doing it. Perhaps they held their device a certain way?  Maybe the touch driver on their screen was buggy?  We don't know.  But the bug was ours.

Pages: [1] 2