Overview of Skin loading and running workflow
#1
While trying to track down why my skin was showing high CPU utilisation figures while sitting idle (pulse on select was generating dirty region screen redraws), I realised that I have no idea what XBMC does to startup and then display my skin. Could one of the Devs give us a run down on what exactly happens when a skin is first loaded and then starts to display each window that we code? The reason for the huge question is that I would like to try to optimise my skin and feel that having some background knowledge before asking more specific questions would be useful.

Ta muchly
Wyrm (xTV-SAF)
If required a FULL debug log can now be submitted from the skin in settings->skin settings->support. Or follow instructions here if you can't access skin settings.

FAQ's located at :- http://kodi.wiki/view/Add-on:AppTV
Reply
#2
On skin load we:

1. Load colors.
2. Load fonts.
3. Load skin strings.
4. Load includes.
5. Load custom skin windows (to determine id, vis conditions + add to the window manager).
6. Load windows that should be loaded at skin start (not on demand) (Busy, ExtendedProgress, KaiToast, Mute, SeekBar, Volume, Pointer).
7. Activate window.

On window activate we:

1. Load the XML (if not previously loaded).
2. Resolve includes (if needed - only needed if info conditions have changed).
3. Reset controls on the window, initialize animation states etc.
4. Set last focused control, reinitialize animation states (that may depend on focus).
5. Start rendering, accepting control input etc.

Hope this helps,
Jonathan
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#3
(2013-04-04, 10:14)jmarshall Wrote: On skin load we:

1. Load colors.
2. Load fonts.
3. Load skin strings.
4. Load includes.
5. Load custom skin windows (to determine id, vis conditions + add to the window manager).
6. Load windows that should be loaded at skin start (not on demand) (Busy, ExtendedProgress, KaiToast, Mute, SeekBar, Volume, Pointer).
7. Activate window.

On window activate we:

1. Load the XML (if not previously loaded).
2. Resolve includes (if needed - only needed if info conditions have changed).
3. Reset controls on the window, initialize animation states etc.
4. Set last focused control, reinitialize animation states (that may depend on focus).
5. Start rendering, accepting control input etc.

Hope this helps,
Jonathan
Thank you Jonathan,

That certainly clears up a number of areas of ignorance on my part, will go off and digest that and hopefully formulate some sensible questions to follow up on this.

But to just kick off things, I take it that on window activation steps 2 to 5 are handled in a loop, how often is that loop executed? I ask this as during my investigation of why the CPU utilisation was so high for a system sitting "idle" on the home screen, I discovered that disabling the "vertical blank sync" setting shot the FPS up to 100 (where it pretty well stays on my Core duo 3GHz). Does this mean that the loop runs at 100Hz, and if so why? Surely there is no point in running this loop any faster than the video frame rate as no matter how many times you you calculate/check the listed information, its the last value that is used when it comes time to render the frame. And in a lot of cases most of the screen information will not change until the user submits a command (move left, click, etc), so anything that depend on moving to another item(etc) really does not need to be checked until the user does something.

Wyrm (xTV-SAF)
If required a FULL debug log can now be submitted from the skin in settings->skin settings->support. Or follow instructions here if you can't access skin settings.

FAQ's located at :- http://kodi.wiki/view/Add-on:AppTV
Reply
#4
Nope, only number 5 is what is looped. During the app loop we basically do any processing required, handle input/events from outside, update the UI as needed, increment animation counters etc, decide what is being rendered and then render. We then wait for vsync (or if no vsync, we sleep for 10ms) and then do it all over again.

Thus, the loop runs as fast as vsync and your CPU allow. We don't run it faster unless you disable vsync, whereby we put a brake on it so it doesn't needlessly go over 100fps.

Cheers,
Jonathan
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#5
(2013-04-05, 03:00)jmarshall Wrote: Nope, only number 5 is what is looped. During the app loop we basically do any processing required, handle input/events from outside, update the UI as needed, increment animation counters etc, decide what is being rendered and then render. We then wait for vsync (or if no vsync, we sleep for 10ms) and then do it all over again.

Thus, the loop runs as fast as vsync and your CPU allow. We don't run it faster unless you disable vsync, whereby we put a brake on it so it doesn't needlessly go over 100fps.

Cheers,
Jonathan
Jonathan,

Yes you are right, of course only 5 would loop. Still don't see the point of running the loop any faster than frame rate, but as the default setting is for vsync on I guess the point is moot anyway.

Speaking of default settings, was there a reason why <pulseonselect> was not set to default to off with the change to having dirty regions set to algorithm 3 by default (basically you would appear to not get any benefit from dirty regions unless the skin you are using explicitly sets <pulseonselect> to off)? I know this, as I had a devil of a time trying to figure out why the FPS and CPU utilisation seem to be increasing together (instead of FPS dropping as CPU went up as seems sensible). I thought I had a pretty lean skin, but it was hammering the crap out of the poor old Pi's.

Wyrm (xTV-SAF)
If required a FULL debug log can now be submitted from the skin in settings->skin settings->support. Or follow instructions here if you can't access skin settings.

FAQ's located at :- http://kodi.wiki/view/Add-on:AppTV
Reply
#6
I don't have a problem with defaulting <pulseonselect> to false. It would affect some skins I guess, but it's a single line in defaults.xml to fix it.
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#7
Jonathan,

Would you mind teasing out point 5 to show a little more detail on what is happening in the loop.

Also, hopefully some intelligent questions that I have thought about since your first post.

Visible conditions, could you explain how they are evaluated? Is it better to nest the visible tags in group statements or code them for each control that they may relate to?

If a screen has multiple instances of the same image, are all instances loaded separately, or is only one loaded and then shared. Example, icon image and its reflection, one load or two?

Id's, is it better to assign id numbers for list content, is it better to leave blank, or does it not matter either way?

Thanks again mate
Wyrm (xTV-SAF)
If required a FULL debug log can now be submitted from the skin in settings->skin settings->support. Or follow instructions here if you can't access skin settings.

FAQ's located at :- http://kodi.wiki/view/Add-on:AppTV
Reply
#8
Visible conditions are evaluated at every screen refresh so if you can try using variables instead because as soon as the first condition is met in a variable no more are evaluated. Also try and order them so the most likely conditions are at the the top.
Reply
#9
Depends on a condition in VARs. You can use control.hasFocus in VARs and thus it will be frame evaluated.
My skins:

Amber
Quartz

Reply
#10
How the render loop works is basically like this:

1. Process any messages/actions etc, and update the UI state accordingly.
2. Increment the frame timer for animation etc.
3. Run through all controls and process them. This updates visibility states, updates animation states, updates any sizing, infolabels, colors, starts loading any background textures, or loads on-thread any textures for controls that don't bg load and computes dirty regions.
4. Render if required by dirty-regions (no regions, no render).

Note that number 3 in particular happens once a frame even if nothing changes on screen. Dirty regions just saves having to bother rendering everything again, it doesn't save having to work out if anything has changed.

Visible conditions:

Generally they're evaluated for each control every app loop. So one simple way to reduce them is if they can be done on a group, then do it on the group rather than on each control in the group. There is a cache, however, so they're only actually "worked out" once per frame. e.g. the "Control.HasFocus(10)" is worked out only once, and the result (true/false) is stored. Thus, by reducing them to be done on a group rather than on all controls in the group, what you're doing is saving the lookups into that cache, and the function calls etc. that result. As we move forward, the cache will become more efficient as more variables move to being updated only as needed, rather than every frame.

Some visible conditions are faster than others. Anything that involves some sort of a string processing is slow whereas anything that is basically just a "look up this" where "this" is a boolean state is normally not too bad. Obviously this is a massive generalisation.

Info labels are not cached per frame, so two or more controls using them will look them up separately. Variables, though, are cached, i.e. they're only evaluated once per frame, so a variable that takes the place of a bunch of the same info label lookups might be more efficient (only might, as you're still looking up that variable multiple times).

Images/textures

The texture managers store a single instance of each texture. Those textures are then shared between controls. Thus, the loading is only ever done once - it's essentially free to use the same texture multiple times on the same window (other than render + setup costs). So icon and it's reflection is a single load (though you're typically diffusing the reflection, so if the diffuse isn't loaded it's 2 anyway).

ID numbers

Generally, IDs should be used where it makes sense. If it doesn't make sense, leaving them blank doesn't hurt in any way. i.e. you need an ID if you're referencing a control or listitem. You don't if you're not. For controls, it's slightly more efficient to not have an ID for controls that don't need one (generally anything that you don't navigate to, or don't need to lookup for conditionals). This is because we use a map for ID lookup so all the "no id" controls get dumped into a bucket that is effectively ignored, meaning the map size is smaller, so lookup is quicker.

Maybe we should wiki-fy this Smile

Cheers,
Jonathan
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#11
Jonathan et el,

I know this just seems like a stream of conscientiousness (that's exactly what it is at the moment), but the ultimate aim is to come up with a wiki-able list of optimisations a skinner can make to their skins. The trick to getting useful information sometimes is knowing the right question to ask, so thank you for sticking with this.

Next series of questions.

I take it that there is NO point in using the background tag for textures in the .xbt file, but I'm not sure what exactly happens with this file as far as XBMC is concerned. Are the image textures read directly from the .xbt file as required (via the disk), is the .xbt file read into memory at skin startup, or are the individual textures read and stored 'unpacked' in memory? I guess this would affect the answer to your question about the the number of loads for an icon and its reflection (1.25? loads instead of 2 loads with the mask texture coming from the .xbt file)

Multiple small image files (parts of a larger image) or one large image, which would be quicker? Scaling up a small image to a large size is quicker than loading a large image (a black background for example), but is there anything we should be aware of when scaling an image?

File IO is evil (well as far as speed is concerned), would there be any benefit for checking if a file or directory exists (via a builtin if we could convince the Dev's that it was a good idea) before trying to load a file. Example

PHP Code:
                <control type="multiimage">
                    <
posx>0</posx>
                    <
posy>0</posy>
                    <
width>1280</width>
                    <
height>720</height>
                    <
randomize>true</randomize>
                    <
timeperimage>5000</timeperimage>
                    <
fadetime>400</fadetime>
                    <
imagepath background="true">$INFO[ListItem.Path]extrafanart/</imagepath>
                    <
aspectratio>stretch</aspectratio>
                    <
visible>!IsEmpty(ListItem.Path)</visible>
                </
control

Instead could become

PHP Code:
                <control type="multiimage">
                    <
posx>0</posx>
                    <
posy>0</posy>
                    <
width>1280</width>
                    <
height>720</height>
                    <
randomize>true</randomize>
                    <
timeperimage>5000</timeperimage>
                    <
fadetime>400</fadetime>
                    <
imagepath background="true">$INFO[ListItem.Path]extrafanart/</imagepath>
                    <
aspectratio>stretch</aspectratio>
                    <
visible>FileExists($INFO[ListItem.Path]extrafanart/)</visible>
                </
control

Not sure if it would be quicker, but I'm pretty sure it would cut back on the "file blah does not exist" log spam that we get at the moment when we try to display a file that does not exist. The one other area it maybe quicker is that I (and most other skinners I'm sure) then go on to do a string compare on the label of the image control (does not work for multiimage) to check if a fallback image (blank.png usually) was loaded and then try to display a backup series of images until we have something to display. We could do a !FileExists visible check in a group to avoid any further fallback image loads in this case. What are your thoughts on this?

Wyrm (xTV-SAF)
If required a FULL debug log can now be submitted from the skin in settings->skin settings->support. Or follow instructions here if you can't access skin settings.

FAQ's located at :- http://kodi.wiki/view/Add-on:AppTV
Reply
#12
How about large diffuses on large images?
I.e. Using the mask to hide 80% of a fullscreen fanart image or a background from an extras folder.
Do they get put into memory as the 20% of that image that is visible, as if you had pre-cropped it in Photoshop?
I'm thinking of something like tearing a fanart image in two with an animation. That would require 2x the same differently masked image.
And how much would you gain by having those in the xbt file instead of an extras folder?
Image [RELEASE] Metroid
Image [RELEASE] IrcChat
Reply
#13
Bundled textures

If a texture is bundled, then it will be loaded on the app thread, rather than background loaded, regardless of what you specify for the background attribute. Typically textures are read out of the XBT file (i.e. off disk), decompressed, and uploaded to the GPU each time they need to be read (i.e. if they're not already in memory). There has been suggestions that for some platforms, storing the (compressed) textures in memory might be a useful trade-off if they have a particularly slow disk. There's also been other suggestions, such as using texture atlasing (several large textures containing all the little textures). None of these are in mainline, however.

A single big texture versus multiple small textures

You can typically treat scaling by the GPU as free, so if you can make up a large image using smaller images that total less pixels, then that will almost always be a win, especially if it's considerably less pixels. Typically the only thing you need to worry about when textures are scaled is how aliasing may affect things - for flat textures, it doesn't matter at all. Using the border attribute is great for situations where you have a dialog backdrop or similar made up of the edge art where the edges can be stretched without compromising the texture quality, and the middle can be stretched: Essentially it draws as 9 quads (18 triangles) across the texture.

File existence

The simplest way to treat file existence is to simply not use paths you don't know exist (or should exist). i.e. use information from the database, don't try and guess stuff by appending things to listitem.path or similar. I'm not sure if extrafanart can be done in this way or not - need to talk with Martijn et. al. regarding the artwork downloader and how it can properly deal with this sort of art. The problem is that you want to be able to fill the art table in the database with a single URL that corresponds to multiple different images. I *think* a stack:// might be usable for this, but am not sure.

When XBMC tries fails to load a texture (for any reason) we mark it as failed and don't try again next frame. However, we will try again if that same image control changes it's texture path to something else and then back to the same thing (e.g. if you have a shared multiimage control that loads different images when the user focuses a different item - each time they go back to the same item, XBMC will try and load the non-existent directory).

On general speed/low processor use

* Make your textures as small as you can get away with so they load fast off disk.
* Background load everything else.
* For the few platforms that require power of 2 textures, having a 257x513 texture takes up 4 times the texture memory of a 256x512 texture, so where reasonable, limit them down. You need only worry about the cases where you're just a little bit over a power of 2 - the rest don't matter too much.
* Keep your code clean and simple - if you can use fewer controls, do so. Same with conditions, skin settings etc.
* Don't unnecessarily make the screen dirty (limit scrolling text to where necessary, don't use pulseonselect etc.)
* Limit script usage.
* Make animations what you think is a good speed, then speed them up at least another 25%. Faster is better - it makes things seem snappier which can have a large perceptual difference.

And most importantly: Don't try to optimise without understanding where the skin is slow and what the cause of it is. Thus this thread Smile

Cheers,
Jonathan
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#14
Diffuse images - how do they work

Basically it's a multiplication of the images, so both images are rendered, but before they're pasted on top of the framebuffer, the individual pixels are multiplied together. Thus, they're stored as full images even if the diffuse is basically making 80% of the texture transparent. They're rendered as full images and blended as full images as well, so on particularly crap GPUs (e.g. maybe the RPi) the fillrate might cause rendering to slow down a bit. Ofcourse, dirty-regions comes through, as long as nothing is moving on the screen, you don't have to do that every frame.

On pretty much any other GPU, you can consider the blending free, so don't worry about it too much. Ofcourse, the diffuse image itself can probably be stored at a relatively low resolution (e.g. a vertical gradient can be done as a 512x1 or 256x1 image) so the only real memory consumption is the main texture. This is why we recommend using 720p fanart as much as possible - it's half the memory usage of 1080p and in many cases doesn't make any difference quality-wise once it's under a diffuse.

Any large texture needs to be outside of the texture bundle - you want to background load them so that they don't hold up the opening of the window. Speed of opening is similar to speed of animation - it can often give a larger perceptual difference than the actual empirical difference.

Cheers,
Jonathan
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#15
Jonathan,

Quote:And most importantly: Don't try to optimise without understanding where the skin is slow and what the cause of it is. Thus this thread

Yep, thus this thread. On the '...without understanding where the skin is slow...', other than turning on the debugging and showing CPU% and FPS (oh, and the old fashioned eyeball) is there some way to tell how quick things are running? I would think the best way to spot slow parts of the skin would be to run XBMC on the slowest box you have available to you. I know about the skin startup time shown in the log (other than Transparency! and to a lesser degree Confluence, which is about the startup speed of my skin) my skin appears to startup pretty quickly compared to most. Not sure I would agree about you perceived startup time, or maybe I'm under the time where it appears to matter anymore.

On file existence, yep and I should not be wasting as much time on this skinning lark as I do. The thing about this mob around here is that they are always pushing the envelope, so you can almost guarantee that someone will come up with another 'hack' to display some non standard image. Would be nice to be able to check something exists before we go hitting the system. I just went looking but can't find it again, but there is a reference somewhere in the wiki for a python call to the XBMC 'vfs' for FileExists (think it made use of CDirectory::Exists() from memory ). Would be nice to expose this to skinners from a builtin.

And last of all for the moment. I asked about placing visible conditions in group blocks, again I suppose that placing animations in group blocks would likewise be a good idea, is that correct?

Wyrm (xTV-SAF)
If required a FULL debug log can now be submitted from the skin in settings->skin settings->support. Or follow instructions here if you can't access skin settings.

FAQ's located at :- http://kodi.wiki/view/Add-on:AppTV
Reply

Logout Mark Read Team Forum Stats Members Help
Overview of Skin loading and running workflow0