Fix the Text / Image render batching

Currently, all text rendered by the Roblox UI system on a given ZIndex is rendered above all Images and Frame backgrounds on the same ZIndex. This is not only extremely undesirable for any moderately complex UI, but it makes it downright impossible to correctly implement a lot of complex UIs.

  • Consider for example: The case of trying to add floating draggable windows to a game. This pattern is currently effectively impossible to do correctly on Roblox… is that acceptable? Each floating window has at least images to build the frame and text for a title bar. Now, to have one window correctly float above another one, you have to have them on different ZIndicies. There are only 10 ZIndicies, and your static UI probably already eats up at least a few of them to start out with. If you have more floating windows than the number of remaining ZIndicies = boom. Not to mention the fact that even if you don’t every single time you want to “bring a window to the front”, you need to change every ZIndex of every single GUI object in every single one of those windows… which is a huge burden on any code, especially if there’s encapsulated components involved.

  • Consider the case where we want a “stack” of overlapping items in a UI, and each of the items has text and images in it. Not possible at all right now because of the Text / Image behavior.

  • Not to mention that the entire problem is extremely in-intuative to begin with. After all… text is rendered as images in most engines to begin with.

  • In fact, the only reason that I have ever used ZIndicies at all in the first place is to work around this stupid problem because given the fact that they work at the global level (as opposed to locally to disambiguate depth within a parent), they’re a massive pain to work with (with you having to iterate through an entire UI hierarchy changing possibly dozens of ZIndicies every single time you want to change the ZIndex on an object dynamically. Also complicating any sort of component-based code, because now every single encapsulated component has to have a way to set ZIndex added as part of it’s API)

I mean, I just had to throw Roblox’s nice text rendering out the window and make my own custom image-based text renderer (which only looks okay) to make my latest project possible at all because it was simply not possible otherwise thanks to this behavior (I need stacks of overlapping objects in several places in the UI), which is pretty frustrating.

I feel like fixing that behavior should be priority #1 for making the GUI system better. It’s by far the current largest pain-point with it, to the point where most games just throw up their hands and leave small ugly edge cases where some of their text is overlapping images that it shouldn’t be in the final product because there’s no reasonable way for them to fix it.

Obviously, a backwards-compatible solution is somewhat difficult given the fact that some UIs are likely relying on the fact that their text appears always-on-top. My proposal would be to make a global flag for it, with the new behavior being that text and images are rendered in whatever order they appear in the hierarchy (just like frame backgrounds / images), rather than on a separate batch, if that wasn’t clear. I think it’s easily an important enough problem to deserve such a flag, because the current behavior really isn’t acceptable when it makes a lot of UI patterns downright impossible to implement.

8 Likes

I largely agree.

FWIW the motivation behind this change (which was made when we made the original iPad port) was to make GUI rendering slow instead of horribly slow. Sorting text together allows batching more text labels in a single draw call.

These were the days of OGRE and pretty unoptimized rendering code. Today the performance impact of reverting this change should be much less significant.

We need to figure out two things:

  1. What’s the new behavior. An obvious one is “ZIndex defines global order, within the same ZIndex the items are sorted in the depth first traversal order”, but I’ve also heard that some people prefer ZIndex to only work within the parent.

  2. Whether the new behavior is really breaking. I’ve seen a lot of UI bugs due to the current sorting - are there realistic examples where reverting this back will break the UI? (I know I can come up with some, but are they actually common)

I think that the ZIndex is a big red herring that will make this take forever to be fixed when it should be fixed pronto, while the correct behavior of ZIndex in a whole different can of worms that will take forever to tackle.

I’m aware of the want for ZIndex fixes, but as a much more pressing issue, as far as I see it this is something that can and probably should be fixed separately before tackling the larger ZIndex problem. At least as far as I can tell no matter what the eventual resolution to the ZIndex problem is this fix would still play out the same way, right?

I mean, ZIndex isn’t even an essential part of the API, you could do everything without ever touching ZIndex if this were fixed.

I think the fastest way to solve stravant’s problem, would be to add a DrawPriorityLevel property to the GuiObject class.
GuiObjects with a higher DrawPriorityLevel would be drawn on top of GuiObjects with a lower priority level, within the ZIndex.

This is what the default value would be for each GuiObject (based on observed behavior):

blob.png

I’m pretty sure this is already being done internally, it just needs to be exposed as a property.

I don’t see why that would be necessary, it would just needlessly add more convoluted legacy junk.

I don’t know when you would concievably want anything other than all of the items to have the same DrawPriorityLevel, and even if you did want them to have different ones, it would be a dangerous route to walk, because it would instantly screw up all your GUIs and coding if you did change any of them at some point.

Mkay I see your point.
Ideally ROBLOX just needs to kill the ZIndex property in favor of ordering by the stack layer relative to the ScreenGui, with a configurable LayerOffset?

I could care less if ZIndex was removed entirely, this problem was fixed, and nothing new was added.

I don’t see why anyone would want to use ZIndex at all. Using a system of “container” frames + the Child-Added order to determining the draw stacking (that is, cutting+repasting something to “move it to front” within a parent) works perfectly well for basically every use case as far as I can see. The only downside of that is that it’s not visible in the explorer what’s actually stacked on top (because the explorer items are not shown in child-added order), but if for your static UI you use a system of containers with only a handful of items in each one it’s easy / obvious what you had stacked in what order.

Obviously there ideally should be a more visible way to determine that ordering, but I don’t think that’s a very pressing issue, cutting and pasting items to change the draw order is easy enough and pretty intuitive too.

EDIT: For example, here’s my current gui: All of the frames are non-graphical, simply for layout / determining “ZOrdering” through child-added order. To get things to stack right I just place the dynamically created objects into the appropriate container frame and then they’ll all be stacked right… no need to manually fudge a bunch of ZIndicies.

Right. So I see two ways to go about this:

  1. We just fix the rendering order to be Z-Index + depth first order within same Z-Index. We do NOT provide any option for you to switch - if this fix breaks GUIs, developers will have to fix GUIs.

  2. We do provide an option. In this case we have to consider the ZIndex rework - I don’t want to introduce new API only to change it later anyway.

Hence my question in the original post - how breaking would the change actually be? As I said, I don’t see many opportunities for this to break UI but maybe I’m missing some obvious use cases.

It’s pretty stupid, but you can arrange objects in whatever order you want (with 100% correctness) by putting them in separate ScreenGuis, then ordering those by-child. Here’s a demo.

Yeah, I’m aware that you can use separate ScreenGuis, but that isn’t really an acceptable solution since it slaughters performance for complex UIs on any lower end machine / mobile, at least as far as the last time I tested it. Presumably because there’s no batching cross-ScreenGui. And again it isn’t really an acceptable solution because it means you would have to duplicate any layout that you were doing across all of the GUIs, or do the layout all in Lua code or something like that. And you can’t use any sort of “containers” for grouping dynamically created things, so you’d have to be re-parenting things a lot for complex cases. And you can’t mix it with ZIndex that way, because ZIndex is within a ScreenGui.

So, while it’s technically a solution, it’s not really any more of a solution than me throwing out the Roblox text renderer and drawing all my text with spritesheets images.