Skip to main content

云控机型分档

云控机型分档功能可以快速实现针对Android和iOS的机型分档功能,支持Unity以及Unreal引擎。PerfSight(APM)提供了默认的机型分档配置文件,业务可以针对实际需要进行微调。

Unity接口:
int CheckDeviceClass(string domainName)
int CheckDeviceClassByString(string domainName, string json)//通过传入json字符串判断机型的档位

Unreal接口:

//iOS平台
int GetDeviceLevelByQcc(const char* domainName, const char* fileDir);
int GetDeviceLevelByString(const char* domainName,const char* json);//通过传入json字符串判断机型的档位

//Android平台
int GetDeviceLevelByQcc(const char* domainName, const char* gpuFamily);// gpuFamily为GPU的型号
int GetDeviceLevelByString(const char* domainName, const char* gpuFamily, const char* json);//通过传入json字符串判断机型的档位

1. 云控文件定义

1.1 整体结构

云控文件采用 JSON 格式定义,整体结构如下图所示。 qcc_arch 云控文件宏观上包含3个部分,分别是链接版本号定义、配置域列表定义以及配置域定义,在同一个云控文件中可以包含多个配置域的定义,各个配置域之间是隔离的,在调用接口CheckDeviceClass(const char* domain)进行判定时,传入指定的配置域域名进行规则判定。

链接版本号:JSON数字类型(后续会支持文本类型),SDK会在初始化云控功能的同时基于此版本号到云端下载最新的配置文件,故命名为“链接版本号”。例如:在安装包中包含了一个默认的配置文件,其中链接版本号为version1,则在业务进行初始化云控功能后,SDK会主动去云控下载version1的配置文件。

配置域列表:JSON字符串类型,定义了此配置文件中具体的配置域,也是CheckDeviceClass(const char* domain)函数中需要传入的名字。注意:所有的“配置域”必须在“配置域列表”中进行申明,否则会视为非法配置。

配置域:JSON对象类型,Key为定义在“配置域列表”中的名字,Value为具体的定义(下节会详细说明),每一个定义在“配置域列表”中的名字都需要有对应的配置域定义

由于Android和iOS“配置域”的定义有轻微区别,因此针对Android和iOS平台需要两个单独的配置文件。


1.2 配置域定义

Android和iOS的“配置域”定义稍有区别。Android由于碎片化的问题,因此设置了多个配置项,包括机型分档数定义、详细分档定义、总控制开关定义、判定维度开关定义、正则表达式开关、模拟器档位开关、白名单定义以及区间阈值判定的定义,而iOS中只有机型分档数定义、详细分档定义、正则表达式开关、机型白名单、SoC白名单的配置。

1.2.1 机型分档数: JSON数字类型, 表示分档的个数, 如m

1.2.2 详细分档: JSON数组(整型数组), 表示详细的分档值,数组中共有m个元素,如[C1,C2…,Cm]。注意:建议C1>=1,0作为一个保留档位,当发生内部错误时,则返回的机型分档为0

1.2.3 默认分档:JSON数字类型,如果只配置了白名单,没有进行区间阈值判定的配置时,白名单中没有匹配,则返回此值

1.2.4 模拟器档位:已废弃,客户端判定的模拟器准确性不高,因此废弃

1.2.5 控制开关: 分为总控制开关(switchops)以及区间阈值判定开关(andopts),均JSON数字类型,用于控制各个模块是否启用,只有启用的模块才会参与规则判定,否则会被忽略(iOS平台没有此项)。总控制开关用于对“白名单”匹配策略以及“区间阈值判定”的控制。“白名单”开关主要用于控制机型白名单、GPU白名单、SoC白名单、厂商白名单是否生效。区间阈值判定开关主要用于控制分辨率区间判定维度、内存区间判定维度、 CPU区间判定频率维度、CPU核数区间判定维度、GPU区间判定维度是否生效。

控制开关基于“位”运算的方式定义,各个开关位的值定义如下:

  1. 机型白名单的开关值为21
  2. GPU白名单的开关值为22
  3. SoC白名单的开关值为23
  4. 厂商白名单的开关值为24
  5. 分辨率区间阈值判定维度开关值为25
  6. 内存区间阈值判定开关值为26
  7. CPU频率区间阈值判定开关值为27
  8. CPU核数区间阈值判定值为28
  9. GPU维度区间阈值判定值为29

当需要开启对应的判定维度时,则将其加入到"与"运算当中。例如,如果需要打开机型白名单、GPU白名单、内存区间阈值判定以及GPU维度区间阈值的功能时,则switchops = 21 | 22 | 26 | 29 = 582, andopts = 26 | 29 = 576。

1.2.6 正则表达式开关regex:JSON数字类型,0表示关闭,1表示打开,在配置白名单时可以基于正则表达式来进行配置,默认为0,表示关闭

1.2.7 白名单判定规则:提供基于机型名、GPU型号、SoC型号、厂商型号的白名单匹配规则,一旦命中白名单规则,则立即返回,不继续执行后续的判定(短路规则)。白名单提供了三种字符串匹配规则,均忽略大小写

  1. 精确匹配:机型信息与配置项的信息必须完全匹配(不区分大小写)。默认此规则。
  2. 前缀匹配:当白名单中,某一项的最后一个字符为*号时,则会启用前缀匹配(如果机型的信息以此配置项为前缀,则就命中此规则)。此主要用于一些三星的手机,在不同地域发行时,其命名有些区别,但是前缀都是一致的,比如配置了机型的白名单 SM-G950*,则如果当前的机型名为SM-G9501,SM-G9502则会命中。默认此规则打开。
  3. 正则表达式匹配:当regex开关打开时,则白名单的字符串全部启用正则表达式的规则进行匹配

iOS的白名单只支持机型白名单以及SoC白名单。针对机型信息,iOS系统提供的接口只能获取到内部的代号名称,如iPhone9,1,iPhone5,4,而外部代号名称,如iPhone XS Max,iPhone 12则获取不到,PerfSight SDK默认建立了内部代号和外部代号的映射关系,因此机型白名单中支持内部代号和外部代号的机型配置,但是这种映射关系的更新需要SDK的更新,可以建议还是使用内部代号名称进行配置(大小写均不敏感)

1.2.8 维度区间阈值判定规则: 在白名单无法满足的情况下,提供了一种基于数学分段规则的通用判定模型。维度区间阈值判定支持5种维度的分段阈值判定:1、分辨率判定区间阈值(取屏幕的宽度,不建议开启);2、内存判定区间阈值(单位为MiB);3、CPU频率判定区间阈值(CPU最大核的频率,不建议开启);4、CPU核数判定区间阈值;5、GPU判定区间阈值,这5种维度的分段判定均可基于"区间阈值判定开关位"进行按需开启。

分辨率判定区间阈值、内存判定区间阈值、CPU频率判定区间阈值、CPU核数判定区间阈值这四种维度只需要提供一个阈值数组的定义即可,如:

定义机型分档数为 m,详细的机型分档为[C1,C2…,Cm],因此各个维度控制中需要定义的分档阈值为m-1个,即[T1,T2…,Tm-1]并且满足T1\<=T2…\<=Tm-1(单调递增的关系)。

定义当前维度值t
如果 t \< T1, 则返回当前维度机型分档值为C1
如果 t >= Tm-1, 则返回当前维度机型分档值为Cm
如果满足t >= Tn且t \< Tn+1,则返回机型的分档值为Cn

举例说明:定义机型分档数m=3, 机型详细分档为[1,2,3], 基于内存判定区间阈值为[2499,3499], 如果当前机型的内存为3000MiB,因为3000 > 2499且3000 \< 3499, 因此在内存维度上当前机型的档位为2。

GPU分段阈值判定: 根据统计,市面上的Android平台,GPU型号的市场主流品牌为Adreno、Mali、PowerVR以及Tegra,而对于不同品牌的GPU,其型号的命令有严格的规范,其也代表了芯片的不同性能等级,因此可以通过命名规则来分区GPU的不同档位。

  1. 对于Adreno品牌的GPU型号,统一的命名规则为Adreno (tm) N,其中N为其具体的整型型号,主要分为100系列、200系列、300系列、400系列、500系列
  2. 对于Mali品牌,统一命名规则为Mali-SN,S为具体的系列,N为型号,主要有g系列和t系列
  3. 对于PowerVR品牌,统一命名规则为PowerVR-SN, S为具体的系列,N为型号,主要有sgx系列、gt系列、ge系列、gx系列、g系列
  4. 对于Tegra品牌,统一命名规则为Tegra-S,S为具体的系列,主要有K1、X1系列、2系列、3系列、4系列
考虑到各个品牌GPU系列的扩展性,因此在定义GPU时需要指明参与运算的子系列型号列表:series,如果有新的子系列则可以方便地扩展添加。

举例说明:定义机型分档数m=3,机型详细分档为[1,2,3],基于Adreno GPU定义如下:

"gpu_vendor":{
"adreno":{
"series":["100","200","300","400","500"],
"100":[199,200],
"200":[299,299],
"300":[330,399],
"400":[430,499],
"500":[520,521]
}
}

如果当前机型的GPU型号为Adreno (TM) 530,因为530属于500系列,且530>521, 则GPU维度返回的机型分档为3

1.2.9 综合判定规则:木桶原理,多维度取最小值。 定义分辨率维度分档返回值为 C1, 内存维度分档返回值为 C2, CPU 核数分档返回值为 C3, CPU核数分档返回值为4,GPU维度分档返回值为C5,则最终返回当前设备的分档值为 C=min(C1,C2,C3,C4,C5)。

整体云控配置定义如下所示

Android

{
"version": versionnumber,
"configureList": ["configname1",…"confignameN"],
"configname1": {
"classLevelNum": M,
"classLevelValues": [C1,C2…,CM],
"defLevel": C1 ,
"switchops": S1,
"regex": 0
"andopts": S2,
"emulator": , C1
"filter-model": {"C1": ["model1","model2"],… "CM": ["model1"," model2"]},
"filter-gpu": {"C1": ["gpu1","gpu2"],… "CM": ["gpu1"," gpu2"]},
"filter-soc": {"C1": ["soc1","soc2"],… "CM": ["soc1","soc2"]},
"filter-manu": {"C1": ["manu1","manu2"],… "CM": ["manu1","manu2"]},
"resolution":[T1,T2…TM-1],
"ram": [T1,T2…TM-1],
"cpufreq": [T1,T2…TM-1],
"cpucores": [T1,T2…TM-1],
"gpu_vendor": {
"adreno": {
"series": ["100", "200", "300", "400", "500"],
"100": [T1,T2…TM-1], "200": [T1,T2…TM-1],
"300": [T1,T2…TM-1], "400": [T1,T2…TM-1],
"500": [T1,T2…TM-1]
},"mali": {
"series": ["g", "t"],
"g": [T1,T2…TM-1], "t":[T1,T2…TM-1]
},
"powervr": {
"series": ["sgx", "gt", "ge", "gx", "g"],
"sgx": [T1,T2…TM-1], "gt":[T1,T2…TM-1],
"ge": [T1,T2…TM-1], "gx": [T1,T2…TM-1],
"g":[T1,T2…TM-1]
},
"tegra": {
"series": ["k1", "x1", "2", "3", "4"],
"k1": [T1,T2…TM-1], "x1":[T1,T2…TM-1],
"2": [T1,T2…TM-1], "3": [T1,T2…TM-1],
"4": [T1,T2…TM-1]
}
}
},…
"confignameN": {}
}

iOS

{
"version": 2,
"regex":1,
"configureList": ["level3", "level2"],
"level3": {
"classLevelNum": 3,
"classLevelValues": [2, 4, 8],
"defLevel": 2,
"filter-model": {
"2": ["iPhone11,4", "iPhone XS Max, "iPhone10,6", "iPhone SE.*","iPhone 8 Plus"],
"4": ["iPad6,12"],
"8": ["iPhone3,2"]
}
},
"level2": {
"classLevelNum": 3,
"regex":1,
"classLevelValues": [2, 4, 8],
"defLevel": 4,
"filter-model": {
"4": ["iPhone XS Max", "iPhone XS.*", "iPhone 8.*", "iPhone9,4", "iPhone8,4"],
"8": ["iPhone 7[A10]"],
"2": ["iPhone3,2"]
}
}
}

2. 机型档位判定流程

对于单个配置域,其会根据白名单开关和区间阈值判定开关计算具体的分档值。在白名单判定中,采用"短路规则",即一旦命中则立即返回,而白名单的判定有严格的先后顺序,即先判定机型白名单,再判定GPU白名单,再判定SoC白名单,最后厂商白名单。根据统计分析,机型的覆盖维度最小,而厂商覆盖的维度最大,覆盖的粒度逐渐增大。

如果白名单没有匹配,则根据区间阈值判定算取得最终的机型分档信息, 如果区间阈值没有配置,只使用了白名单控制,而白名单又没有命中,则返回默认分档值配置

3.自定义配置文件

配置文件模板

基于配置文件模板,PerfSight(APM)根据游戏大盘性能数据分别提供了默认的配置文件,业务方可以基于此配置文件进行修改,实现自定义的机型分档配置

Android默认配置文件 iOS默认配置文件

4. 接入

由于Unreal和Unity引擎以及Android和iOS平台针对外部的资源文件有不同的处理方式,因此分开说明

4.1 Unreal引擎

iOS平台和Android平台的配置稍有区别,因此分别进行介绍

4.1.1 iOS平台

  1. 将默认的机型配置文件命名为`QCC_{appId}_IOS` (appId替换成平台申请的id,如67895,QCC_67895_IOS)
  2. 在工程目录Content下新建一个文件夹,如tapmdir,名字任意,路径任意
  3. 将默认的机型配置文件放入到2中的文件夹中,如:`${ProjRootDir}/Content/tapmdir/QCC_{appId}_IOS`
  4. 在UE4 Editor 设置中搜索Additional Non-Asset Directories To Copy 点击添加新建的文件夹tapmdir

步骤2、3、4可以根据实际的项目进行更改

FString tapmdirPath = FPaths::ProjectContentDir() + FString(TEXT("tapmdir")); int deviceLevel = GetDeviceLevelByQcc("level2", TCHAR_TO_UTF8(*tapmdirPath));

4.1.2 Android平台

将默认的云控机型分档的配置文件置于以下路径中:${Plugins}\GPM(PerfSight)\Source\GPMSDKLib(PerfSightSDKLib)\Android\assets,如果没有assets文件夹,需要主动创建一个,将默认的配置文件命名为QCC_appId,例:QCC_67895。 int GetDeviceLevelByQcc("level2", const char* gpuFamily);其中gpuFamily为GPU的型号

4.2 Unity引擎

将默认的机型档位配置放置于StreamingAssets目录中,iOS的默认配置文件命名规则为:QCC_{appId}\_IOS, Android的默认配置文件命名规则为: QCC_{appId}

int CheckDeviceClass(string domainName)

5. 更新配置文件

云控的配置文件国内可以基于https://tapm.wetest.qq.com/cloudFile 或者 https://perfsight.qq.com/cloudFile ,海外可以基于https://tapm.wetest.net/cloudFile 地址进行配置文件的更新。 SDK侧的更新瑞逻辑如下:

  1. 安装完游戏包之后,首次通过GetDeviceByQcc/CheckDeviceClass接口获取机型分档时,其会使用本地的默认配置文件解析得到分档值
  2. 在某一次游戏进程启动中,第一次调用GetDeviceByQcc/CheckDeviceClass接口时会异步请求云端的配置文件,并保存到本地为“当前配置文件”,本地启动过程中后续的GetDeviceByQcc/CheckDeviceClass接口调用并不会解析“当前配置文件”,而是使用第一次调用GetDeviceByQcc/CheckDeviceClass接口的缓存值
  3. 从第二次启动游戏开始(非安装游戏后的首次启动),则会优先使用上一次启动时拉取的“当前配置文件”进行配置解析并返回,如果“当前配置文件”解析失败,则会使用打包时默认配置文件继续解析并返回

如果业务方不需要PerfSight SDK主动拉取云端配置文件,则可以使用CheckDeviceClassByString/CheckDeviceLevelByString接口,传入需要解析的配置文件内容给SDK进行解析,这样SDK不会去请求云端配置文件。

默认的云端配置文件存放在PerfSight平台的CDN网络中,如果业务方需要(为了合规或者更新方便)则可以将配置文件放置于业务方自己的CDN网络中,如下:

Android

<meta-data android:name="APM_QCC_URL" android:value="自定义CDN地址"/>

iOS

iOS平台的CDN可以通过Info.plist或AdditionalPlistData进行配置

<key>GPM或者APM</key>
<dict>
<key>apm_cc_url</key>
<string>自定义CDN地址</string>
</dict>