2011-09-19, 20:28
Greetings all,
The GUI of XBMC is presently quite a heavy load on the CPU. I intend to fix this, by re-designing the interaction between renderer and back-end data sources, using an event-driven infrastructure. It's a large undertaking, and I'd like to avoid duplication of effort, request feedback and help with some items.
Current State
I have written the underlying event-driven infrastructure already, and plan to get it included into debian, as it is generic and can be used by many other projects, as a library. It includes extensive unit tests, using CPPUNIT framework. It also includes a C++ parser/lexer marrying the event-driven code with an interpreted grammar. I have now started to integrate it into xbmc. This infra is effectively a number of C++ templates around two main classes: Notifying<type> and Notified<type>. These two are smart wrappers around basic types like int, bool, CStdString, std::list, std:et, and represent data sources and data sinks respectively. The "smart" part is that they handle dependencies between sources and sinks transparently, along with managing the lifecycles of both sources and sinks (ie, when a source/sink is constructed/destroyed, the dependents are notified appropriately).
There are examples of use in the library code itself, which will be called notified (ie, libnotified).
I have started implementing the plan outlined below, and I intend on publishing the sources once I have them cleaned up enough to compile again. To this end I have set up a launchpad PPA for libnotified and hope this can be kept as an external library, and branched xbmc to http://github.com/drok/xbmc for this development.
I expect a pull request (xbmc/notified running usably) will be ready in 2-months time or so.
Plan
Underlying datasources will change minimally to replace return by value to return by (smart) reference. Basically API that currently returns bool or int will return Notifying<bool>& or Notifying<int>& respectively. Member variables of data sources (ie, g_application.m_pPlayer->m_dBitrate, g_settings.m_IsPlayingVideo, etc) will change from their basic types to Notifying<basic type>, but the way they are used by their objects stays the same - minimal source changes.
The GUI elements will change more:
The CGUIControl::UpdateVisibility() api is gone. A new api function OnNotify() is added, which will be called when any dependent data of Notified<type> member variables is changed (animation data is one of the dependencies, of course). In this call, the Control can queue itself for rendering if it is presently visible and contents have changed.
The Renderer will only draw controls listed on the render queue, instead of polling the entire list of controls in the skin.
Functions like g_infoManager.GetBool(), etc no longer be called at render time, but only at skin load time or other significant events, typically due to user input. That call will return a Notifying<bool> which will be assigned to a member Notified<bool>. This assignment creates the relationship between the datasources and the CGUIControl object. The data may change, (and CGUIControl OnNotify()'ed), but the dependency relationship does not.
The GUIInfoManager.cpp will be replaced by 3 others, a Parser grammar, Lexer vocabulary and a new CPP that marries the Parser/Lexer. This will allow much easier maintenance of available keywords, and add a freebie feature - logical and arithmetic expressions (more complex expressions that supported currently, such as ((a || b) && (!c || d) && (player.chapter > 2)), or ( (a + b) * c / d > 4 ? foo : bar ) ).
For instance, a call like GUInfoManager::GetInt( "player.chapter / player.chaptercount > 0.5 ? foo : bar )" ) will return a Notifying<int>& reference, which will represent this arithmetic relationship between the dependent source variables chapter, chaptercount, foo and bar. If this were the Label property of a GUIControl, the GUIControl would be OnNotify()'ed whenever the result of that expression changes.
The Lexer Vocabulary will look similar to:
This snippet supports the player.hasvideo, player.recording, player.progress, and player.volume skin variables:
I will need help with:
The GUI of XBMC is presently quite a heavy load on the CPU. I intend to fix this, by re-designing the interaction between renderer and back-end data sources, using an event-driven infrastructure. It's a large undertaking, and I'd like to avoid duplication of effort, request feedback and help with some items.
Current State
I have written the underlying event-driven infrastructure already, and plan to get it included into debian, as it is generic and can be used by many other projects, as a library. It includes extensive unit tests, using CPPUNIT framework. It also includes a C++ parser/lexer marrying the event-driven code with an interpreted grammar. I have now started to integrate it into xbmc. This infra is effectively a number of C++ templates around two main classes: Notifying<type> and Notified<type>. These two are smart wrappers around basic types like int, bool, CStdString, std::list, std:et, and represent data sources and data sinks respectively. The "smart" part is that they handle dependencies between sources and sinks transparently, along with managing the lifecycles of both sources and sinks (ie, when a source/sink is constructed/destroyed, the dependents are notified appropriately).
There are examples of use in the library code itself, which will be called notified (ie, libnotified).
I have started implementing the plan outlined below, and I intend on publishing the sources once I have them cleaned up enough to compile again. To this end I have set up a launchpad PPA for libnotified and hope this can be kept as an external library, and branched xbmc to http://github.com/drok/xbmc for this development.
I expect a pull request (xbmc/notified running usably) will be ready in 2-months time or so.
Plan
Underlying datasources will change minimally to replace return by value to return by (smart) reference. Basically API that currently returns bool or int will return Notifying<bool>& or Notifying<int>& respectively. Member variables of data sources (ie, g_application.m_pPlayer->m_dBitrate, g_settings.m_IsPlayingVideo, etc) will change from their basic types to Notifying<basic type>, but the way they are used by their objects stays the same - minimal source changes.
The GUI elements will change more:
The CGUIControl::UpdateVisibility() api is gone. A new api function OnNotify() is added, which will be called when any dependent data of Notified<type> member variables is changed (animation data is one of the dependencies, of course). In this call, the Control can queue itself for rendering if it is presently visible and contents have changed.
The Renderer will only draw controls listed on the render queue, instead of polling the entire list of controls in the skin.
Functions like g_infoManager.GetBool(), etc no longer be called at render time, but only at skin load time or other significant events, typically due to user input. That call will return a Notifying<bool> which will be assigned to a member Notified<bool>. This assignment creates the relationship between the datasources and the CGUIControl object. The data may change, (and CGUIControl OnNotify()'ed), but the dependency relationship does not.
The GUIInfoManager.cpp will be replaced by 3 others, a Parser grammar, Lexer vocabulary and a new CPP that marries the Parser/Lexer. This will allow much easier maintenance of available keywords, and add a freebie feature - logical and arithmetic expressions (more complex expressions that supported currently, such as ((a || b) && (!c || d) && (player.chapter > 2)), or ( (a + b) * c / d > 4 ? foo : bar ) ).
For instance, a call like GUInfoManager::GetInt( "player.chapter / player.chaptercount > 0.5 ? foo : bar )" ) will return a Notifying<int>& reference, which will represent this arithmetic relationship between the dependent source variables chapter, chaptercount, foo and bar. If this were the Label property of a GUIControl, the GUIControl would be OnNotify()'ed whenever the result of that expression changes.
The Lexer Vocabulary will look similar to:
Code:
<player>{
hasvideo { V(bool, g_application.IsPlayingVideo()); }
recording { V(bool, g_application.m_pPlayer->IsRecording()); }
progress { V(int, g_application.GetPercentage()); }
volume {
InternalVariable<CStdString> v(new CNConvert_OneOp("%2.1f dB",
g_settings.m_nVolume * g_settings.m_dynamicCompression));
V(CStdString, v);
... ( more commands )
}
This snippet supports the player.hasvideo, player.recording, player.progress, and player.volume skin variables:
I will need help with:
- Building on Windows/OSX/Others
- Flex/Bison code needs to be built on these platforms (specifically ylwrap is not available due to not using automake)
- The Posix threads need to be resolved on windows (perhaps with the pthreads-win32 library)
- Flex/Bison code needs to be built on these platforms (specifically ylwrap is not available due to not using automake)
- Code / Architecture reviews
- Writing Unit Tests
- Lots and lots of testing (including valgrind leak and race condition verification).