Dictionaries and Settings¶
Settings is a generalized subsystem designed to provide a simple to use interface to various Kit’s subsystems, which can be automated, enumerated, serialized and so on. It is accessible form both C++ and scripting bindings such as Python bindings. carb.settings
is a Python namespace (and, coincidentally, a C++ plugin name) for the Settings subsystem.
Settings uses carb.dictionary
under the hood, and is effectively a singleton dictionary with a specialized set of API for easier/more streamlined access.
carb.dictionary
is a Dictionary subsystem, which provides functionality to work with the data structure type known as dictionary, associative array, map, and so on.
Dictionaries¶
For the low-level description of the design and general principles, please refer to the Carbonite documentation for the carb.dictionary
interfaces.
Settings¶
As mentioned above, settings subsystem is using carb.dictionary
under the hood, and to learn more about the low-level description of design and general principles, please refer to the Carbonite documentation.
On the higher level, there are several important principles and guidelines of using settings infrastructure, best practices of using settings within Omniverse Kit.
Default values¶
Default values need to be set for settings at the initialization stage of the plugin, and in the extension configuration file.
A rule of thumb is that no setting should be read when there is no value for it. As always, there are exception to this rule, but in the vast majority of cases, settings should be read after the setting owner set some default value for this particular setting.
Notifications¶
To ensure the optimal performance, it is recommended to use notifications instead of direct polling of settings, to avoid costs of accessing settings backend when the value didn’t change.
DON’T: This is an example of polling in a tight loop, and it is not recommended to do things this way:
while (m_settings->get<bool>("/snippet/app/isRunning"))
{
doStuff();
// Stop the loop via settings change
m_settings->set("/snippet/app/isRunning", false);
}
DO: Instead, use the notification APIs, and available helpers that simplify the notification subscription code, to reduce the overhead significantly:
carb::settings::ThreadSafeLocalCache<bool> valueTracker;
valueTracker.startTracking("/snippet/app/isRunning");
while (valueTracker.get())
{
doStuff();
// Stop the loop via settings change
m_settings->set("/snippet/app/isRunning", false);
}
valueTracker.stopTracking();
With the bool value, getting and setting the value is cheap, but in case of more complicated type, e.g. string, marking and clearing dirty marks could be used in the helper.
In case helper is not sufficient for the task at hand - it is always possible to use settings API such as subscribeToNodeChangeEvents
/subscribeToTreeChangeEvents
and unsubscribeToChangeEvents
to achieve what’s needed with greater flexibility.
Settings structure¶
Settings are aimed to be easily tweakable, serializable and human readable. One of the use cases is automatic UI creation from the settings snapshot to help users view and tweak settings at run time.
DO: Simple and readable settings like /app/rendering/enabled
DON’T: Internal settings that don’t make sense to anyone outside the core developer group, things like:
/component/modelArray/0=23463214
/component/modelArray/1=54636715
/component/modelArray/2=23543205
...
/component/modelArray/100=66587434
Reacting to and consuming settings¶
Ideally settings should be monitored for changes and plugin/extensions should be reacting to the changes accordingly. But exceptions are possible, and in these cases, the settings changes should still be monitored and user should be given a warning that the change in setting is not going to affect the behavior of a particular system.
Combining API and settings¶
Often, there are at least two ways to modify behavior: via the designated API function call, or via changing the corresponding setting. The question is how to reconcile these two approaches.
One way to address this problem - API functions should only change settings, and the core logic tracks settings changes and react to them. Never change the core logic value directly when the corresponding setting value is present. By adding small detour into the settings subsystem from API calls, you can make sure that value stored in the core logic and corresponding setting value are never out of sync.