Tasharen Entertainment Forum

Support => NGUI 3 Support => Topic started by: Wisteso on April 25, 2014, 06:30:29 PM

Title: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on April 25, 2014, 06:30:29 PM
I purchased NGUI as a replacement for the immediate mode GUI that I had been creating for a game that I am developing. The primary reason being performance (fewer draw calls than the greedy immediate mode). It's a pretty great system for the most part. However, I am having an extremely difficult time porting what I think is a fairly simple GUI to NGUI's system.

Perhaps the best way to describe this pre-existing GUI is that we have different scales based on the device (2.0, 1.0, 0.5, 0.25, etc), but the GUI tries to be pixel perfect when possible. We do *not* want to simply scale the entire UI as a whole, because then UI elements are too tiny on small devices or too big on large devices. Instead, the scale is used to modify the size of the individual GUI elements (e.g. button is 256x256 on ipad-retina, but 128x128 on iphone). So the layout can look radically different depending on the scale.

NGUI does not seem to like this approach at all. The biggest issue is that I keep needing to define fixed dimensions and fixed anchor offsets of sprites via the inspector and it's not practical at all for my scaling system. Instead, I'd like them to be defined via the size of the source texture (so the SD/HD atlas can drive the layout). It's not the same approach I used with my immediate mode GUI, but might work even better. There's a few other small issues but I think I can figure those out...

It seems like I'm going to need to add a TON of custom scripts to NGUI GameObjects to supplement the missing functionality. I'm really hoping that maybe I'm just missing something. I've had little luck with searching for existing solutions.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on April 25, 2014, 10:06:03 PM
I think I've figured out a good solution for my problem.

I was mistakenly thinking that I had to rely on "PixelPerfect" mode to achieve pixel perfect rendering. What I am now doing is using "fixed size", and setting the UIRoot's height to  (screen.height / zoomfactor) where zoomfactor is 1, 2, 4, 8, etc. I also had to set texture filtering to point filtering to get rid of transparency pixels creeping in at 2x zoom.

I then set the "pixel size" of my atlas to 0.5 and let NGUI take care of the rest. When zoomlevel = 2, everything will be zoomed in, however it will still look crisp due to the 0.5 pixel size atlas. Though for 4x zoom to look crisp, I would need to create another atlas with a pixel size of 0.25, but I dont think that will be needed.

If I wanted to save texture memory on the SD devices, I could use a separate SD atlas (and maybe I will), but during development time it will be better to just have one atlas to maintain.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on April 26, 2014, 02:58:14 PM
Yup, that's a good solution.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on April 30, 2014, 05:03:03 AM
Thanks for the response, ArenMook. Though I'm having a secondary issue cropping up that your feedback would be much appreciated on... 

The issue is that no matter how I modify UIRoot.manualHeight via code, my static sprites that use OnEnable anchoring (for performance reasons) are still acting upon the default/serialized value instead of the value I set. For example: if I set 480 in the inspector, and then set 720 via code, the UI will still be at 480 scale. One solution is obviously to use OnUpdate anchoring, but I'd really like to avoid that since I will have possibly over 100 static UI elements, and that could be pretty costly on the CPU for the older devices.

The only way I've been able to have OnEnable anchoring work for my UI elements so far has been...

1) Set the height via the inspector (serialization) but this does not allow the dynamic size I need, so it's not really a solution here
2) Change line 41 of UIRoot.cs to be something like "private int manualHeight = SomeClass.getDynamicSize();"

I've tried extending UIRoot.cs and then overriding the value of manualHeight in the constructor, but it does not work either. I think due to serialization overriding my override? I also cant stop that process since I cant leave manualheight blank in the inspector. I've also tried adding my custom script to the Script Execution Order registry, right after UIRoot but that didn't help either.

Is there a better way than option 2? Ultimately, I would rather have that ugly one line hack than have 100+ objects needlessly calling Update() constantly when they will never change their anchor values.

Thanks for the help so far, and thanks for reading this!
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on April 30, 2014, 05:18:27 PM
Just UIRoot.Broadcast("UpdateAnchors");
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 01, 2014, 03:59:24 AM
Aren, I very much appreciate all the help so far. I'm definitely making tons of progress. I hate to keep coming back with issues, and like to figure things out on my own but i've spent several hours on this new problem without much luck....    I'm not sure if this issue has been going on all along, or just now noticeable, but somehow my UI keeps losing it's pixel perfect state based on seemingly random reasons.

If you look at the screenshot, it's showing an example of my UI is being distorted by about 1% which is causing little fractures all over the place. I'm using sliced sprites now per your suggestion, and all dimensions are divisible by 4 (no more odd dimensions). I have workarounds for the fracturing, but they make no sense at all.

(Keep in mind that the atlas is 0.5 pixel size for both modes.)

1) If I'm in my "HD screen" mode (with manualHeight set to half the screen height), the issue only happens for even resolutions. I can subtract 1 (one) from manualHeight and everything looks great again. So for 720, i'd use manualHeight of 359 and it looks good. For 719 and any other odd height, it will naturally look fine.
2) If I'm in my "SD screen" mode, with manualHeight set to the full screen height, all bets are off. With Unity's built-in player the problem occurs for any screen height if Unity is maximized, but never occurs if Unity is unmaximized (i realize that sounds insane). It does *not* occur in a standalone build at all.

I've checked everything for errors. I'm not doing anything fancy except the halving of the manualHeight. Every objects' scale, position, and rotation look good (integer values, aside from minor float rounding errors here and there). UIRoot being the exception, since it's doing it's scale conversion magic.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 02, 2014, 07:41:38 AM
Quote
the problem occurs for any screen height if Unity is maximized
Open the Stats window. You will most likely notice that the width or height is not dividable by two. Odd dimension = stuff like this happens when point filtering is used, or blurriness when other types of texture filtering are used.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 02, 2014, 09:07:06 PM
Hmm I didn't think to check the width being divisible by two, but I have been checking the height. Though this issue was occurring with a proper standalone build too (running in 720p and 1080p) which is definitely a safe resolution.

Another new data point btw: it will happen to some widgets, but not other parts of the GUI which are near-duplicates (not prefabs though)...

I'm guessing it must be an odd sized resolution (like you said), or somehow there's a bad scale value sneaking in somewhere. I'll keep hunting and post back if I still have no luck after a few days.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 04, 2014, 10:33:30 AM
Is any part of your UI not pixel-perfect? When you duplicate objects, the scale is not (1, 1, 1), but rather something like (1.008, 1.008, 1.008) due to Unity being odd. Select the UIRoot and hit ALT+SHIFT+P to force everything to be pixel-perfect.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 04, 2014, 10:59:13 AM
The ALT-SHIFT-P seems to have fixed it. I was seeing the 1.0008 thing showing up before as well, so yeah it was probably exactly what you said (some weird scale glitch with Unity). I'll keep an eye on the issue, but my usual way of reproducing the issue is no longer showing the bug! Hooray!! Thank you!

Edit: Spoke too soon. The issue is occurring still. I'll mess with it more later to see if I have better luck.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 06, 2014, 05:46:36 PM
This issue is still going on, however I'm starting to notice some very unusual trends.



Aren, if we still cant figure this out, would you be open to looking at a simple project that I can confirm reproduces the issue (on my end at least)?
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 06, 2014, 10:05:35 PM
Well first of all, don't use point filtering. Use trilinear filtering, and make sure to add some padding to the sprites you're displaying that you notice an issue with. By default NGUI adds 1 pixel of padding around sprites (chosen in the Atlas Maker). You can further modify each sprite, adding transparent, tiling, or clamped borders.

Next, what's the size of your window? If the size of the window is not even (ie: dimensions are not dividable by two), pixels won't align properly.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 07, 2014, 01:50:14 PM
Simple stuff first, the window (panel) size is 360 width and 100% of the "UI Root" height. The screen resolutions that I'm concerned with are all the standard iOS ones (iPhone 4 and newer). I've made sure all images are divisible by 4, but the border offsets of sliced sprites might sometimes only be divisible by 2. (e.g. a sprite that's 64x64 but has top/bottom/left/right offsets of 22 rather than 24). Is that okay?

As far as filtering, I'd really really like to avoid using linear filtering if at all possible, since I think it will just hide the underlying issue and also cause blur with the textures when there isn't an exact 1-to-1 mapping of texels to pixels. We're also only using integer zoom levels (1x and 2x), which should be totally compatible with point filtering. (for cases where linear filtering is a must, I dont think trilinear has any benefit over bilinear when using ortho perspective and fixed Z values, but it will still lower FPS).

I'm wondering if half pixel coordinates (http://msdn.microsoft.com/en-us/library/windows/desktop/bb219690(v=vs.85).aspx) are used by NGUI/Unity3d? When doing OpenGL/D3D programming, I remember similar issues occurring without it being used.

(Sorry in advance about my reluctance, but our game has a need for extremely precise graphics that aren't blurred/smoothed)
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 08, 2014, 01:55:41 AM
Honestly, I would be very surprised if you notice any is any noticeable performance difference between point filtering and linear filtering considering how well it's optimized on the hardware. As for blurring... well, yes. If you upscale you get blur with linear filtering and blockiness with point filtering. Depends on which one you see as the lesser of two evils I suppose.

NGUI automatically adds the half-pixel offset on DX9 devices (so only the windows platform). It's not used on mobiles. Line 837 of UIPanel.cs, or just search for "half-pixel".
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 11, 2014, 10:33:56 PM
We definitely want the blocky "nearest neighbor" look as opposed to the blurry "bi/trilinear" look.

So since bi-linear won't solve the issue for us, there's a few more thoughts I had with the symptoms we're seeing...

1) I'm pretty sure that using "nearest" mode for texture filtering means you do not need borders to prevent bleeding, but we've been seeing single pixel bleeding (from neighboring sprites in the atlas) as well. The only way you should see bleeding with "nearest" mode is with incorrect texture-coordinate values.

2) Perhaps I should have mentioned this sooner, but there's been an "off by one" error that I've been seeing with almost all my sprites since day 1 of using NGUI, which you can see in my attached screenshot (look at the scene view). I'm wondering if perhaps the same root issue is causing all of the above mentioned texel-to-pixel problems.

I have some experience as an OpenGL graphics programmer and I've worked with getting native android apps to be pixel perfect before. If you'd like to work together on figuring this out, I'd be happy to provide examples and do whatever I can.

Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 13, 2014, 07:48:36 AM
1. I'd need an example to look at as your assumption is correct -- pixel filtering means no bleeding at all.

2. The offset is actually by a half-pixel, which is a DirectX 9 issue. You won't see this on a mac. Google "dx9 half pixel offset" for more info. It's a hardware thing.

In your case it's a full pixel because the size of the texture is likely half of its normal pixel size.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 14, 2014, 09:26:47 PM
Aren:

Please check your PM inbox for a message labeled "Issue repro example and demo (topic 9253)"

The message has links to a stripped Unity project and a brief demo video on how to reproduce the issue.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 14, 2014, 10:56:40 PM
Note what I said above:
make sure to add some padding to the sprites you're displaying that you notice an issue with. By default NGUI adds 1 pixel of padding around sprites (chosen in the Atlas Maker).
I opened your project, saw the issue right away. Opened the Atlas Maker, chose your atlas, selected White_Pixel texture and hit Update with the default Padding set to 1. Just like that, the issue was gone.

Padding is important...
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 15, 2014, 11:05:50 PM
Adding 1 pixel of padding has removed the bleeding, but instead, there are now small half-pixel gaps between sprites that should be perfectly butted up against each other.

However, I have a really strong instinct that the bleeding and gaps are only a symptom of the still unknown primary issue. The bleeding was not occurring at all unless the "fracturing" was happening as well and the the fracturing is also *still* happening with the 1 pixel padding.

I sent you a small screen recording that shows some observations that I think are pretty significant. Though I later figured out that some of those problems were due to the DX9 half pixel adjustment. Disabling the DX9 adjustment fixed some of the off-by-1 issues I was having, but it did not fix the fracturing. Considering the fracturing occurs on OS X too, I'm not surprised by that.


Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 16, 2014, 01:18:50 PM
Make sure that everything is pixel-perfect. If you've adjusted sprite sizes at any point, they likely aren't anymore. Padding in between of sprites has no effect on what's visible.

Added 1 pixel on sprites is something NGUI adds if the sprite is not using dimensions that are dividable by two. So for example a 17x17 sprite will have size of 18x18. The drawn area will be 17x17, but the widget size will be 18x18. This is done because otherwise a centered sprite would be using floating point position values.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 16, 2014, 08:26:51 PM
For the most part, I think I've finally (functionally) solved the issue by disabling the DX9 half pixel correction on line 837 of UIPanel. Disabling that line has fixed almost all symptoms of the issue except for one or two (read below). It also confirms what I suspected, which is that pixel padding around all sprites in the atlas is actually not needed when using point filtering. I no longer have any texture bleeding or half pixel gaps, and I've been able to remove the 1 pixel padding around the sprites as well. The fix also makes sense too, since I was not seeing those symptoms on any platform except for Windows (and a tiny bit on OSX, read below for that).

It seems that the code that you're using on line 837 of UIPanel needs some revision. I don't think anyone's noticed this issue so far because: it's only a half pixel change that's usually hard to notice, it's mostly hidden by linear filtering, and it's only visible on Windows systems.

The only remaining symptoms are that random quads of a Sliced sprites are being distorted. I see this symptom on all platforms though so it's probably a totally separate bug. I've attached a screenshot of what it looks like with this post. There seem to be two workarounds...   

1) Use a proper SD atlas for SD mode instead of an HD atlas with 0.5 pixel size. (right now I'm cheating and using an HD atlas for both modes)
2) Use different dimensions for the UISprite that make the UV math happy. (being even and a multiple of 4 is not enough, apparently)

As for your suggestions: I just need to figure out the problem with Sliced sprite, but everything should be pixel perfect already - I frequently do a sweep over my UI check for corrupt/bad values. All sprite dimensions are already divisible by 4 as well.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 17, 2014, 05:04:58 PM
The half-pixel offset is most certainly necessary. Here's what a button looks like with and without half pixel offset in Unity:
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 17, 2014, 06:53:43 PM
I believe you, but that's just with DirectX.

Messing with mHalfPixelOffset does not help me in DirectX mode. However, in OpenGL mode, with mHalfPixelOffset set to false, everything is mostly peachy. I think using a proper SD atlas will fix the remaining issues that seem to be due to floating point round error + nearest-neighbor resampling.

It seems that it's possible to detect if running in OpenGL mode ( http://docs.unity3d.com/Documentation/ScriptReference/SystemInfo-graphicsDeviceVersion.html ) in which case mHalfPixelOffset should be set to false.

E.G.
  1. if (mHalfPixelOffset) mHalfPixelOffset = (SystemInfo.graphicsShaderLevel < 40) && (SystemInfo.graphicsDeviceVersion.Contains("Direct3D"));
or maybe just
  1. if (mHalfPixelOffset) mHalfPixelOffset = SystemInfo.graphicsDeviceVersion.Contains("Direct3D 9");

Of course, this leaves the question of "Why does DirectX look messed up still while OpenGL is mostly fine?" but, for now that's something I can ignore. I greatly prefer OpenGL, and our main platforms are OpenGL based.
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: Wisteso on May 17, 2014, 10:47:55 PM
Just another data point to add...   using "-force-d3d11" also fixes most issues. There's some minor issues in SD mode, but again (like with OpenGL), I think they're due to resampling textures to 50% without using linear filtering. Proper SD textures for SD mode would not need resampling and should fix the issue.

So to recap...
"-force-d3d9" (d3d9 is used by default anyway) = numerous issues - looks messed up in HD and SD modes
"-force-d3d11" = HD mode looks great, SD mode has some issues that should be fixable with proper atlas
"-force-opengl" = HD mode looks great, SD mode has some issues that should be fixable with proper atlas. However, NGUI needs to not apply D3D9 half pxiel correction by checking if the API is OpenGL or DirectX
Title: Re: Having a difficult time with porting my immediate mode GUI to NGUI
Post by: ArenMook on May 18, 2014, 11:30:57 AM
Yup, DX9 is the only one that uses the stupid half-pixel offset BS. I often wonder what "genius" added that gem in there.