-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Summary:
A quality/scalability framework should be created that gives developers the ability to apply settings based on the platform and device their application is running on.
What is the relevance of this feature?
Developers do not have a framework for specifying and automatically selecting settings presets based on hardware, capabilities and user preferences. The legacy system which used CVarGroups has been removed and is no longer supported.
Developers typically use this kind of feature to specify Low/Medium/High/VeryHigh quality settings for target platforms so a game or simulation will perform as intended with the expected quality.
Feature design description:
A quality/scalability framework that satisfies the requirements can be built using CVARs and the Settings Registry.
- Settings are defined with CVARs, e.g. a rendering setting for turning on shadows named
r_shadows - Groups of settings are defined in the Settings Registry e.g. a group named
q_graphicscould control all the graphics quality settings. - Rules for which settings to use for specific devices are defined in the Settings Registry
- Settings and rules for specific platforms are defined in the Settings Registry using the Platform Abstraction Layer (PAL) folder structure.
Developers will be able to use the initial framework without any graphical tools or Editor menus, but those tools will be part of a future RFC.
Technical design description:
The technical design is mainly comprised of settings groups and levels, device attributes and device settings rules.
Settings groups and levels
Settings groups cvars can be defined (e.g. q_general, q_graphics, q_physics or GraphicsQuality, GeneralQuality etc.) and within each group, quality levels are defined (e.g. Low, Medium, High).
NOTE: the r_ prefix denotes rendering/graphics CVARs and the q_ prefix denotes CVARs or CVAR groups that control quality. These largely follow a pre-existing set of prefixes listed here: https://github.com/o3de/o3de/blob/development/Code/Legacy/CrySystem/ConsoleHelpGen.cpp#L672
Settings groups and quality levels are defined in .setreg files at the key /O3DE/Quality/Groups/\<group\>/Levels
File: O3DE/Registry/quality.setreg
{
"O3DE":{
"Quality":{
"DefaultGroup":"q_general", // default/fallback quality group
"Groups":{
"q_general": {
"Description":"General quality group. 0 : Low, 1 : Medium, 2 : High",
"Levels":[
"Low", // level 0 (based on array index)
"Medium", // level 1
"High" // level 2
],
"Default": "High", // default level, can also be index if preferred
"Settings": {} // Settings could go in here, but it's more likely those will be in Gems
}
}
}
}
}Gems like Atom would define new settings groups and levels as needed.
File: O3DE/Gems/Atom/Registry/quality.setreg
{
"O3DE": {
"Quality": {
"Groups":{
"q_general":{
"Settings":{
"q_graphics":[0,1,2,2] // q_graphics has one more level than q_general
}
},
"q_graphics": { // General Graphics settings that uses shadow and visibility quality group settings
"Description":"Graphics quality group. 0 : Low, 1 : Medium, 2 : High, 3 : VeryHigh",
"Levels": [ "Low", "Medium", "High", "VeryHigh" ],
"Default": "High",
"Settings": { // Settings could be defined in a separate file if desired like quality.graphics.setreg (see Settings example below)
"q_shadows": [0, 1, 2, 3], // q_shadows group cvar defined below
"q_visibility": [0, 1, 2, 3] // q_visibility group cvar defined below
}
},
"q_shadows": { // Shadows Settings levels
"Levels": [ "Low", "Medium", "High", "VeryHigh" ],
"Settings": {
"r_shadowResolution":[256, 1024, 2048, 4096] // actual shadow cvars
}
},
"q_visibility": { // LOD/Visibility settings
"Levels": [ "Near", "Medium", "Far", "VeryFar"], // different level names
"Settings":{
"r_viewdistance":[100, 256, 512, 2048] // actual visibility cvars
}
}
}
}
}
}Settings
Each setting is a CVAR (e.g. r_shadows, p_gravity etc.) and can be put in a group which denotes a cvar (e.g. q_graphics, q_physics, q_general) within a .setreg file at the address /O3DE/Quality/Groups/<group>/Settings
CVARs can be configured with flags based on the game needs to restrict who can change their settings and when.
File: O3DE/Gems/Atom/Registry/quality.setreg
{
"O3DE":{
"Quality":{
"Groups":{
"q_graphics":{
"Settings":{
"q_shadows":[1,2,3,4], // (compact form) graphics has 4 levels, so 4 values are defined, this compact form makes it easy to compare values for each level
"r_sun":1, // (compact form) r_sun is 1 for all levels
"r_example":{ "0":1, "Medium":2} // alternate form can target a level by index or name
}
}
}
}
}
}These registry files would exist in the Gems that provide the cvars, but can be overridden in the active project.
Settings can be overridden for specific platforms (e.g. iOS, Android, Linux etc) by placing the overrides in .setreg files in the appropriate PAL folder.
File: O3DE/Gems/Atom/Registry/Platform/Android/quality.setreg
{
"O3DE":{
"Quality":{
"Groups":{
"q_graphics":{
"Default":"Low",
"Settings": {
"q_shadows":[0,1,1,1], // lower shadow quality levels
}
},
"q_shadows":{
"Settings": {
"r_shadows":[0,1,1,1] // no shadows at all on lowest
}
}
}
}
}
}Device attribute API
A DeviceAttributeRegistrar exists in AzFramework for registering device attributes and device attribute interfaces will be registered for model and RAM.
Device attribute names must be unique and are case-insensitive.
struct IDeviceAttributeInterface
{
// get the name of the device attribute e.g. gpuMemory, gpuVendor, customAttribute42
virtual AZStd::string_view GetDeviceAttribute() const = 0;
// get a description about this device attribute, used for help text and eventual UI
virtual AZStd::string_view GetDescription() const = 0;
// evaluate a rule and return true if there is a match for this device attribute
virtual bool Evaluate(AZStd::string_view rule) const = 0;
// get the value of this attribute
virtual AZStd::Any GetValue() const = 0;
};
class DeviceAttributeRegistrarInterface
{
// register a device attribute interface, deviceAttribute must be unique, returns true on success
virtual bool RegisterDeviceAttribute(AZStd::string_view deviceAttribute, AZStd::unique_ptr<IDeviceAttributeInterface> deviceAttributeInterface) = 0;
// visit device attribute interfaces with a callback function
virtual void VisitDeviceAttributes(const VisitInterfaceCallback&) const = 0;
// find a device attribute interface
virtual IDeviceAttributeInterface* FindDeviceAttribute(AZStd::string_view deviceAttribute) const = 0;
};
using DeviceAttributeRegistrar = AZ::Interface<DeviceAttributeRegistrarInterface>;Initial device attributes will be created for:
- Device model
- RAM
- GPU Vendor
- GPU Model
- GPU Memory
Device-specific settings
Rules can be specified in Settings Registry files to set CVARs based on device attributes like the model, amount of RAM etc.
Rules are ECMAScript regular expressions or short LUA script that evaluates to true or false.
If a device matches multiple rules, the system will use the /O3DE/DeviceRulesResolution setting to determine how apply the rules. Possible values are:
- "Min" use the lowest setting for all matching rules
- "Max" use the highest settings for all matching rules
- "First" use the first rule that matches, based on the order of the rules in the Settings Registry
- "Last" use the last rule that matches, based on the order of the rules in the Settings Registry (DEFAULT)
A sys_print_device_rules console command will output all matching rules and their settings to the console to help developers debug device rules.
Warnings will be displayed in the console logs when a device matches multiple rules.
Custom rules can be written in LUA so developers can write rules that are difficult or impossible with regular expressions. LUA rules are enclosed within a dollar sign and open and closed parenthesis '$()'
Example: "apiVersion":"$((value > 1.2 and value < 2.4 ) and value != 1.5)"
Alternately, a more verbose option is to use an object format like:
"apiVersion":{
"Lua": "(value > 1.2 and value < 2.4 ) and value != 1.5"
}File: Registry/devices.setreg
{
"O3DE":{
"Devices":{
// settings for the Graphics group for all devices that match rules in the external .setreg file
"GPU low":{ // A human readable descriptive name for the device group
"Settings": {
"q_graphics":0 // the graphics quality level to use
},
"Rules":{ // apply the settings for all devices that match any of these rules
"$import":"devices.gpu-low.rules.setreg" // import rules from an external file
}
},
// example of importing device settings/rules for Samsung devices
"Samsung": {
"$import":"devices.samsung.setreg"
},
// example of custom override rule
"Custom": { // A human readable descriptive name for the device group
"Settings": { // The name of the scalability group to use with the Level
"q_graphics":0, // the Graphics quality level to use
"r_shadows":[0,0,1,1] // device specific settings overrides
}
"Rules":{ // apply the quality level and settings for all devices that match any of these rules
"LG Devices": {"model":"^LG-H8[235]\\d"}, // regex model match
"GeForce 6800": {"gpuVendor":"0x10DE", "gpuModel":"0x004[0-8]"}, // gpu vendor and gpu model regex
"Adreno": {"gpuName":"Adreno.*530", "apiVersion":"Vulcan[123]"}, // gpu name regex with graphics api version regex
"Experimental":{"$import":"devices.experimental.rules.setreg" } // import rules from an external file
}
},
"LG XYZ Overrides": {
// this example shows how you would override settings for a specific device match
// without providing the overall quality level cvar
"Rules":{
"LG Devices": {"model":"^LG-H8[235]\\d"}, // regex model match
"LUA example": {"apiVersion":"$value > 1.0 and value < 2.5"}
},
"Settings":{
"r_shadows":0 // device specific settings override
}
}
}
}What are the advantages of the feature?
- Meets the requirements
- Uses existing systems (CVARs, Settings Registry, PAL)
- Data-driven settings, groups, rules
- Flexible and customizable
What are the disadvantages of the feature?
- Additional processing of quality settings and device rules on application start up.
- Developers must learn how to use the new system.
How will this be implemented or integrated into the O3DE environment?
The bulk of the implementation is explained in the technical description section. Developers with existing projects will need to copy and customize device rules and quality settings registry files from the default template into their own project and customize them.
Are there any alternatives to this feature?
- Re-introduce the Lumberyard .cfg spec system.
- This system is known to work, but all of the specifications and CVAR setting would need to be updated to work with O3DE.
- The primary reason this option was not selected is it relies on hard coded specification detection and does not use the Settings Registry.
- Settings for multiple platforms are intermingled instead of using PAL to separate them.
- Use a simple hard-coded low/medium/high/very-high quality system.
- Because O3DE is a modular engine with uses outside of gaming it is unlikely that a "one size fits all" approach will suit the needs of all developers. And making changes would mean the developers would need to modify c++ code and recompile the engine.
- Port the Lumberyard spec .cfg system and device .xml and .txt files into Settings Registry.
- This is a similar amount of work and less flexible to the system that is proposed, the main time/effort savings would be we would only support REGEX for device rules and wouldn't need to have debugging CVARS, but we'd have the limitations of the Lumberyard system.
How will users learn this feature?
Documentation will be available on the main website and in comments inside the Settings Registry files.
Are there any open questions?
- What is the best name for this framework/system? 'Quality' or 'Scalability'?
- What are the best locations in the Settings Registry for the system?
- Is there a better/simpler way to define device rules than using REGEX and LUA?
- Should this system also let developers specify asset build quality settings?