WIP Moving to an explicit layering systems with Kodi M*
#1
As a small heads up, I'm thinking about moving to an explicit layering system in Kodi 21.

Right now, Kodi uses an implicit system. The order in which assets are rendered is determined by their respective position in the XML. An explicit system would mean that there would be a tag which specifies the layer for the controls. It would be up to the skinner to ensure proper layering.

In a prototype of mine, each control group would bump its layer height by one. On the Estuary home screen, the left hand menu would render below the elements on the right hand side, due to its group stack being lower. But this can easily be fixed by setting the layer height manually.

This system has a multitude of advantages. From a skinner side, it would mean that elements of different groups can be interleaved in the depth they are rendered at. This could reduce the the numbers of controls needed. It eliminates the need to fix layering issues by copying elements.

From the GUI engine side, it would make it possible to apply a number of optimizations which could lead to significant performance improvements on more expensive skins.
Reply
#2
This sounds like a really interesting idea and I can definitely see the benefits. However, I'm concerned about just how much this work would involve if skinners need to go through and explicitly layer every control.

It will be particular problematic when reusing skin code via includes. What might work at one layer level in one part of the skin, might not work at that same layer level in another part. As a simple example, consider the following buttons

Image

I "fake" these buttons by using grouped layers that split the button into two halves. The left side of the button is a fixed width group with the bg button texture layer and a fg icon layer. The right side is a standard auto width button a texture and label. Put both sides together in an include and now you have an auto sizing button that allows independent control over the icon, label and colordiffuse for each element.

This approach significantly cuts down on the textures I need. Making a button like this using the "normal" approach would mean generating individual focus and unfocus textures for every possible icon and colordiffuse combo. Instead, now I just need one standard focus/unfocus texture for all button styles and can switch out the icon as needed.

With explicit layering, say the button BG layer is 1 and the FG is 2. Then I have a pop-up overlay on layer 3. In that pop-up I want a button. The layers defined in my include for buttons will be wrong for that layer level.

Even if I use a $PARAM to pass the new layer levels as needed, it won't work in all scenarios. For instance, if I have a block of code in an include that I want to reuse at a different layer level, there's no way for me to increment the values. Skins can't do math, so I would need to redefine every single layer in that include block just to move it up one layer for a different window.

Currently the process is simple - all I need to do is worry that the control I want to appear above another control is further down in the code. This explicit layering approach will be incredibly complex to manage and will involve a lot of rewriting/debugging of skin code. Ultimately, that much work will mean will lose the majority of skins because it will be too much effort to implement.

Maybe I'm just misunderstanding something here and it isn't as complex as I'm thinking, but to me this sounds like it will essentially break every single skin and is asking skinners to go to significant effort to redesign their skins from the ground up.

PS - I really hope I don't come off as sounding too negative here as that's not my intention. I'm really excited that someone is looking into this side of things in more detail and I can see the benefits. More just want to raise my concerns early before the horse has bolted!
Arctic Fuse - Alpha now available. Support me on Ko-fi.
Reply
#3
Every children of a node would inherent the layer depth of its parent. As default, groups automatically increase the depth by one (maybe 10 to leave some room for layering in a group). The skin can overwrite this behavior by setting an absolute value or an relative amount for a group or individual items.

If I interpret your example correctly, the button defined in the include will inherent the layer depth of the popup. It should work fine as it is. Maybe you'd have to up the layer in this case, but this shouldn't happen to often.

One issue that might occur is that a shallow group might end up in a lower layer than intended. On the Estuary home screen, that's the case for the left hand menu. To fix this, the group containing the whole menu has to be bumped.

Otherwise, I don't expect much breakage at all. The default behavior should be fine for most skins, requiring only small adjustments.


One goal of this would be the possibility for the GUI engine to reorder elements of a layer, if the skin permits it via a flag. It is faster to render all the button textures and then the text.
Reply
#4
When you say every group increases the depth, are you meaning nested or sequential? I'm assuming nested where the automatic assignment means each indentation increases the layer but children on the same level have same layer like so:
xml:

<control type="group" layer="10">
<control type="group" layer="20">
<control type="group" layer="30">
</control>
</control>
<control type="group" layer="20">
<control type="group" layer="30">
</control>
</control>
</control>

To explain how my button group will break this, look at pseudo code for the button I describe:

xml:

<include name="my_button">
<control type="group" description="left_button_half" id="80$PARAM[id]">
<width>80</width>
<left>0</left>
<control type="group" description="no_focus">
<visible>!Control.HasFocus(90$PARAM[id])</visible>
<control type="image" description="background button texture" layer="1">
<texture>left_buttontexture_nf.png</texture>
</control>
<control type="image" description="foreground button icon" layer="2">
<texture colordiffuse="$PARAM[colordiffuse_nf]">$PARAM[button_icon]</texture>
</control>
</control>
<control type="group" description="focus">
<visible>Control.HasFocus(90$PARAM[id])</visible>
<control type="image" description="background button texture" layer="1">
<texture>left_buttontexture_fo.png</texture>
</control>
<control type="image" description="foreground button texture" layer="2">
<texture colordiffuse="$PARAM[colordiffuse_fo]">$PARAM[button_icon]</texture>
</control>
</control>
</control>
<control type="button" description="right_button_half" id="90$PARAM[id]" layer="1">
<texturefocus>right_buttontexture_fo.png</texturefocus>
<texturenofocus>right_buttontexture_nf.png</texturenofocus>
<label>button_label</label>
<textcolor>$PARAM[colordiffuse_nf]</textcolor>
<focusedcolor>$PARAM[colordiffuse_fo]</focusedcolor>
<width>auto</width>
<left>80</left>
</control>
</include>

What happens when I drop this code into a layer with a higher layer number? Obviously this piece of code is a straight forward example so it could increment based on the group. But what happens when I have a more complex include with more complex layers that nest multiple includes within each other? What happens if I go over the hard coded limit of 10 internal layers? 640K ought to be enough for anyone?

I feel like I'm really missing a piece of the puzzle about how it would work, because currently it sounds like it would cause similar issues to a GOTO statement with line numbers. For instance, what happens when I change my mind and want to put a layer *between* layers 1 and 2? Do I just renumber every single layer in my entire skin to accommodate a single additional control? Or do I use 10 increments myself for forward compatibility, thereby quickly breaking the internal layer numbering?

Also consider this type of example where groups are used for alignment. There is no reason why this image control should have a higher layer just because I've nested a group for the purposes of alignment.
xml:

<control type="group">
<top>80</top>
<control type="group">
<top>20</top>
<height>40</height>
<control type="image">
<texture>separator.png</texture>
<height>1</height>
<centertop>50%</centertop>
</control>
</control>
</control>

I'm sure it is just something I'm not quite connecting here about how it would work and it actually makes sense. I just can't quite join the dots to see the picture yet.
Arctic Fuse - Alpha now available. Support me on Ko-fi.
Reply
#5
Yes, moving to such a system will require some effort of the skinners to adjust to. But there can be a legacy system in place to support the current behavior.

My prototype was just to exemplify that a layering system can work without changing the skin. It can serve as a initial starting point to merge layers together.

The layering from front to back of the Estuary home screen would look like this:
1. Kodi logo + clock
2. Top and bottom gradients
3. Shadow of the vertical menu
4. Text
5. Posters
6. Icons
7. Button surfaces
8. Vertical menu surface
9. Background

From the GUI rendering side, this would work very well even without reordering the elements. If the engine could reorder the elements of the layers, layers 4/5/6 can be merged as they don't overlap while keeping all the performance advantages.
Reply
#6
I understand the benefit. What I don't understand is how it works from the practical side of actually coding a skin.

For instance, what happens when I want to put a layer between 6 & 7? How do I do that without needing to renumber every layer in the skin?
Arctic Fuse - Alpha now available. Support me on Ko-fi.
Reply
#7
You have to renumber the layers above in this case. But this is done in ten seconds if you use constants.
Reply
#8
(2022-06-27, 13:27)sarbes Wrote: You have to renumber the layers above in this case. But this is done in ten seconds if you use constants.
Not in a complex skin that reuses includes. You can't use constants because the includes might be at a different layer height depending on where you use them.

For instance the button include that has three layers (bg, icon, text) but then might be necessary to use at different layer heights depending on where it is used in the skin.
Arctic Fuse - Alpha now available. Support me on Ko-fi.
Reply
#9
You have a bunch of options.

Have it on the same layer as its parent element. The GUI engine can't rorder the elements of the layer in this case, but it should render fine.

Have it a layer higher than its parent element. This way, at least the elements situated in this layer can be rendered in one go.

Have it on a dedicated layer with other elements by using separate includes. More elements can be pooled together by the engine this way.
Reply
#10
I've been thinking a lot about a practical way to manage numbering of layers from the skin side of things that addresses the issues I've raised and I think I've come up with something.

Essentially I'm thinking something similar to software versioning increments "major.minor.point" (0.0.0) would work where parent layers increment the "major" version but nested children increment the "minor" version (and grandchildren the "point" and so on). That would allow for nested children to have layers "between" without disrupting the base layer numbering and also means there would be no upper limit because you could have 1.99.999 if you wanted and that would still be below 2

To demonstrate, here's an example showing how the default numbering would work (this mimics current behaviour).
xml:

<control type="group">
<layer>1</layer> <!-- 1 -->
<control type="group">
<layer>1</layer> <!-- 1.1 -->
<control type="image">
<layer>1</layer> <!-- 1.1.1 -->
</control>
<control type="label">
<layer>2</layer> <!-- 1.1.2 -->
</control>
</control>
<control type="group">
<layer>2</layer> <!-- 1.2 -->
<control type="image">
<layer>1</layer> <!-- 1.2.1 -->
</control>
<control type="label">
<layer>2</layer> <!-- 1.2.2 -->
</control>
</control>
</control>
<control type="group">
<layer>2</layer> <!-- 2 -->
<control type="image">
<layer>1</layer> <!-- 2.1 -->
</control>
<control type="label">
<layer>2</layer> <!-- 2.2 -->
</control>
</control>



Then an `absolute="true"` optional tag would allow overriding this relative layering behaviour. Additionally, there would need to be a way for the group to inherit the layer of the parent rather than extending a decimal place (e.g. say <layer>0</layer> would inherit the parent group's layer).

For instance, here's three different ways using the above code to have all the images on layer 1.1 and all the labels on layer 1.2 so that the renderer can reorganise them:


xml:

<control type="group">
<layer>1</layer> <!-- 1 -->
<control type="group">
<layer absolute="true">1</layer> <!-- 1 (because absolute) -->
<control type="image">
<layer>1</layer> <!-- 1.1-->
</control>
<control type="label">
<layer>2</layer> <!-- 1.2 -->
</control>
</control>
<control type="group">
<layer>0</layer> <!-- 1 (because inherits parent)-->
<control type="image">
<layer>1</layer> <!-- 1.1 -->
</control>
<control type="label">
<layer>2</layer> <!-- 1.2 -->
</control>
</control>
</control>
<control type="group">
<layer>1</layer> <!-- 1 -->
<control type="image">
<layer>1</layer> <!-- 1.1 -->
</control>
<control type="label">
<layer>2</layer> <!-- 1.2 -->
</control>
</control>


With this approach, the current implicit layering behaviour is retained if skins don't number any layers. Then if the skinner only wants to introduce explicit layering in some areas, they can use relative layering which makes intuitive sense when still in the design phase because elements often get moved around. Once the design of a window is finalised, then the skinner can convert groups to use absolute values to get the most performance benefit from the renderer reordering things.
Arctic Fuse - Alpha now available. Support me on Ko-fi.
Reply
#11
Sorry, but I won't be writing a solver for a problem which doesn't need to be resolved at runtime.

If you don't want to use the layers, that's fine. Per default, every element will be on the first layer, rendering like before.
Reply
#12
Okay I won't bang on about this much more because I feel like I'm annoying you when that's not my intent.

I'm not trying to play devils advocate here. I honestly feel that from a design perspective, not having a good way to manage relative layer order from the start will cause significant issues down the line.

But perhaps I'm approaching the issue from the wrong angle and attempting a bandaid on deeper issue. Perhaps the real underlying problem is the inability for skins to do simple math. If skins were able to do simple addition and multiplication of constant values then the issues I raise could easily be overcome.
Arctic Fuse - Alpha now available. Support me on Ko-fi.
Reply

Logout Mark Read Team Forum Stats Members Help
Moving to an explicit layering systems with Kodi M*0