图形渲染电源与UE电路技术
图形渲染电源与UE电路技术
将介绍移动设备的电源技术。
智能手机已成为日常生活中不可替代的商品。无论是职业还是个人生活,每项任务都以某种方式或其他方式与这些设备相关。为了满足日益增长的依赖,这些智能手机每天都在变得更加强大。强大的处理器、更多的存储空间和改进的摄像头是每个买家都想要的功能。
除了操作系统,消费者还使用各种应用程序,这些应用程序使用设备的不同传感器和处理能力。所有这些过程都需要一个电源来运行,移动设备中则是一个电池。这些电池必须不时充电,以保持流程正常运行。更长的电池寿命是选择智能手机的另一个重要标准,与电池寿命优化相关的技术发展速度与智能手机行业的其他垂直行业不同。
通过硬件和软件技术可以提高智能手机的电池寿命。改变硬件可能意味着安装更大的电池,但也意味着增加智能手机的尺寸。设计高效的电源管理单元和高效的集成电路(IC)是一种可行的解决方案。此外,操作系统中管理电池密集型应用程序和明智使用可用电池的软件改进也被视为该问题的另一个潜在解决方案。
下图是一款便携式产品的电源管理:
智能手机的耗电组件多种多样,常见的如下图所示:
1 电源硬件
不同的嵌入式系统、芯片、处理器和传感器集成在一起,同步工作,使这些移动设备变得智能。它的每个硬件设备在运行时都会消耗电力。在所有这些电子模块中,收发器模块消耗最大的功率,因为它在很长时间内保持活动状态以接收传入的分组。已经讨论了各种软件技术来优化这些分组的数据传输。数字信号处理器(DSP)是这些收发器模块的关键部件,它处理大量数据以供多媒体使用,降低DSP的电源电压是降低功耗的直接方法。
为了延长电池寿命,智能手机的DSP在通话期间需要低功耗和高吞吐量乘法累加(MAC),在等待期间需要低功率间歇操作。1V多阈值CMOS电路通过简单的并行架构和使用嵌入式处理器的电源管理技术满足这些要求,嵌入式处理器与适用于电源控制的改进DFF一起使用。
除了处理器和收发器,屏幕是电池电量的另一个主要消耗源。需要背光的LED屏幕更耗电,因此可以被更省电的显示器(如OLED)取代,后者耗电更少。与LED和LCD显示器不同,OLED不需要背光,OLED中的每个像素都有自己独立的颜色和光源。因此,OLED上的黑色图像将是完全黑色的,但LED和LCD的情况并非如此。
研究表明,随着时间的推移,电池寿命的下降也可能是由于聚偏氟乙烯(PVDF)。PVDF是一种用于防止电池中石墨阳极剥落的粘合剂,不导电,并且由于粘附率差而溶解在电解质中。还存在一种新的n型共轭共聚物——双亚氨基并萘醌对亚苯基(BP)粘合剂,其性能优于传统的PVDF基粘合剂,延长了电池寿命,并防止了电池老化时的退化。
MC13892的电源结构图。包含电池、接口控制等元件。
MC13892电源管理和用户接口结构图。
研究人员还提出了一种动态电源管理单元(Power Management Unit,PMU),它在智能手机上运行不同应用程序时,收集处理器和输入/输出设备的不同参数的信息,然后PMU将基于收集的信息提出预测功率感知管理方案。
一款名为nRF5340中的电源和时钟管理系统针对超低功耗应用进行了优化,以确保最大功率效率。电源和时钟管理系统的核心是电源管理单元(PMU),如下图所示。
PMU在任何给定时间自动跟踪系统中不同组件所需的电源和时钟资源。为了实现可能的最低功耗,PMU通过评估电源和时钟请求、自动启动和停止时钟源以及选择调节器操作模式来优化系统。
PMU一般有系统开启模式(System ON)、系统关闭(System OFF)、强制关闭(Force OFF)3个模式,具体详情如下所述。
系统开启(System ON)模式是通电复位后的默认操作模式。在System ON(系统开启)中,所有功能块(如CPU和外围设备)都可以处于IDLE(空闲)或RUN(运行)状态,取决于软件设置的配置和正在执行的应用程序的状态。网络核心的CPU和外围设备可以处于空闲状态、运行状态或强制关闭模式。
PMU可以根据电源要求打开和关闭适当的内部电源。外围设备的电源需求与其活动级别直接相关,当触发特定任务或生成事件时,活动级别会增加或减少。
- 电压和频率缩放。nRF5340自动调整内部电压以优化功率效率。一些配置选项要求更高的内部电压,被视为功耗的增加。这些配置如下:
- 将应用程序核心时钟的频率设置为128 MHz。当CPU休眠时,例如在执行WFI(等待中断)或WFE(等待事件)指令后,也会观察到此模式下的功耗增加。通过在进入CPU休眠之前将应用程序核心的时钟配置为64 MHz,可以降低CPU和外围设备处于空闲状态休眠时系统开启期间的功耗。
- 使用96 MHz时钟频率的QSPI。
- 使用USB外围设备。
- 调试时。
- 使用VREQCTRL请求VREGRADIO电源上的额外电压-电压请求控制。
- 电源子模式(Power submode)。在系统开启模式下,当CPU和所有外围设备处于空闲状态时,系统可以处于两种电源子模式之一。
电源子模式包括:- 恒定延迟。在恒定延迟模式下,CPU唤醒延迟和PPI任务响应将保持恒定并保持在最小值,是由一组始终启用的资源保护的。与低功耗模式相比,具有恒定和可预测的延迟的优势是以增加功耗为代价的,通过触发CONSTRAT任务选择恒定延迟模式。
- 低功耗。在低功率模式下,自动电源管理系统选择最省电的电源选项,实现最低功率是以CPU唤醒延迟和PPI任务响应的变化为代价的。通过触发LOWPWR任务选择低功率模式。
当系统进入system ON(系统开启)时,默认为Low power(低功率)子模式。
系统关闭(System OFF)是系统可以进入的最深省电模式。在此模式下,系统的核心功能关闭,所有正在进行的任务都将终止。使用寄存器SYSTEMOFF将设备置于System OFF(系统关闭)模式。以下操作将从System OFF(关闭)启动唤醒:
- GPIO外围设备生成的DETECT信号。
- LPCOMP外围设备生成的ANADETECT信号。
- 由NFCT外围设备产生的SENSE信号在现场唤醒。
- 检测到VBUS引脚上的有效USB电压。
- 调试会话已启动。
- A引脚复位。
当设备从系统关闭状态唤醒时,将执行系统重置。根据外围VMC-易失性存储器控制器中的RAM保留设置,一个或多个RAM部分可以保留在系统关闭状态。在进入系统关闭之前,当进入系统关闭时,启用EasyDMA的外围设备不得处于活动状态。还建议网络核心处于空闲状态,意味着外围设备已停止,CPU处于空闲状态。
强制关闭(Force-OFF)模式仅适用于网络核心。
应用程序核心使用寄存器接口RESET-RESET控件强制网络核心进入强制关闭模式。在此模式下,网络核心被停止,以实现可能的最低功耗。当网络核心处于强制关闭模式时,只有应用程序核心可以释放该模式,导致网络核心唤醒并再次启动CPU。
在应用程序核心将网络核心设置为强制关闭模式之前,建议网络核心处于IDLE状态,如下所示:
- 所有外围设备均已停止。
- 使用VREQCTRL-电压请求控制取消VREGRADIO电源上的附加电压。
- CPU处于IDLE状态,这意味着它正在运行WFI或WFE指令。
当网络核心从强制关闭模式唤醒时,它将被重置。根据外围VMC-易失性存储器控制器中的RAM保留设置,可以在强制关闭模式下保留几个RAM部分。
2 电源软件
具有更高处理能力和更快互联网连接的现代智能手机的巨大普及也增加了Android和iOS中数据和硬件密集型应用程序的数量,WhatsApp、Instagram、Skype等应用程序不仅需要CPU资源,还需要全天候的互联网连接。研究表明,在空闲状态下,互联网使用约占耗电量的62%。此外,与Wi-Fi相比,当频繁交换小尺寸数据包时,3G/4G消耗更多的电池。
数据压缩、数据包聚合和批量调度等多种软件技术可用于优化电池寿命。智能手机上不同应用程序的随机数据传输会消耗更多的电池,因此,可以使用批处理调度机制通过应用程序重复传输数据来最大化睡眠时间并最小化唤醒频率。
从3G/4G到Wi-Fi的数据卸载是提高电池寿命的另一种有效方式,因为Wi-Fi在数据传输方面比3G/4G更高效。另一种软件技术是将更高的计算任务(如CPU密集型软件)卸载到云上进行计算。该策略可用于在移动设备上运行Office 365和MATLAB等软件,但这会增加云与设备之间的通信成本。应用状态代理(ASP)是另一种技术,其中不仅使用CPU资源而且还使用Internet数据的后台应用程序被抑制并传输到另一设备上,并且仅在请求时才被带到设备上。
智能手机行业在处理能力和其他功能方面的进步速度远快于电池,研究人员现在正专注于通过软件和硬件手段来有效管理可用电池能量的电源管理技术,上述不同技术正被用于将智能手机的电池寿命提高多倍。
将能耗分配给并发运行的应用程序具有挑战性,因为功率状态传输有时是不同应用程序动作的累积结果。
例如,假设当每秒发送N个分组时,Wi-Fi接口从低功率状态传输到高功率状态。现在假设两个应用程序以每秒N/2个数据包的速度传输,导致Wi-Fi接口进入高功率状态。类似地,设想一个应用程序以每秒N个数据包的速度传输,另一个应用以每秒9N个数据。在这两种情况下,Wi-Fi接口都处于高功率状态,但不清楚如何为每个应用程序分配功率使用。在第一种情况下,两个应用程序都不会单独触发高功率状态,因此为它们充电有意义吗?在第二种情况下,两个应用程序都会触发高功率状态,因此应该大致相同的充电量,但应该是多少?
一种可能的解决方案是根据应用程序的工作负载在每个应用程序之间分配组件功率,意味着在第一种情况下,每个应用程序将被分配高功率状态功率的一半,而在第二种情况下一个应用程序将分配1/10的功率,另一个将分配9/10的功率。该解决方案具有有利的性质,即应用程序功耗的总和等于全局功耗。然而,这种解决方案是幼稚的,因为电力使用不是传输速率的线性函数,所以用这种方式来分解它没有什么意义。当考虑到Wi-Fi接口的功耗不是一个一维函数时,这个解决方案似乎更加可疑。
相反,需要一个独立于每个组件的强大功能工作的解决方案,并且对应用程序开发人员(PowerTutor的主要目标用户)来说是直观的。对于每个组件,计算功耗,就像每个特定应用程序单独运行一样,意味着在情况1中,每个应用程序将为低功率Wi-Fi状态充电,而在情况2中,每个应用将为高功率状态充电。这就失去了应用程序功耗之和等于全局功耗的良好特性(正如这两个案例所说明的那样,它既不是低估值,也不是高估值)。然而,通过这个定义,可以独立于其他正在运行的应用程序来理解应用程序的功耗,使得PowerTutor的用户可以观察到类似的应用程序级功率特性,而不考虑资源共享:对于专注于优化特定应用程序的工程师来说是一个有用的特性。请注意,PowerTutor还报告了准确的系统级功耗。
PowerTutor界面。(a) 应用程序视图。(b) 图表视图。(c) 饼状视图。图中无意但不适当地使用智能手机硬件组件。
此解决方案存在局限性。第一,如果应用程序正在争夺资源,那么很难预测它们单独执行时的行为。第二,在某些情况下,看到一个应用程序调用另一个来执行某项任务。在这种情况下,如何分配功耗尚不清楚。在真实的Android系统中,媒体服务器进程经常发生这种行为。第三,使用巧妙技术的应用程序,如与其他应用程序同时进行传输,不会在该方案中获得收益。然而,对于第一个案例,无能为力。解决其他两个问题需要对所涉及的应用程序的语义有一个高层次的理解,这目前超出了工具的范围。
下表显示了ADP1和ADP2手机的内部和内部电源型号变化。类型内变化是由同一类型手机样本的平均值归一化的标准差,类型间差异是两种类型手机的样本均值之间的差异。注意,表中的功率模型参数也可以被视为特定工作负载的功率测量,即功率模型参数的变化与预测误差线性相关。例如,对于使用音频设备的应用程序,预计使用为ADP1导出的功率模型预测另一个ADP1的音频设备功耗时,预测误差小于4%。这些数据为以下结论提供了一些支持。
Android系统中的图形架构如下:
下图显示了DRS(Dynamic Resolution Scaling,动态分辨率缩放)系统的架构。为了实现分辨率缩放,在现有Android系统中添加了两个新层:
- 第一层是DRS上层,位于应用层和OpenGL ES/EGL层之间,拦截必要的OpenGLES函数调用和EGL函数调用,以确保以适当的显示分辨率完成图形渲染。它将缩放因子应用于必要的OpenGL ES/EGL函数调用的参数,以将默认显示分辨率转换为目标分辨率。
- 第二层是DRS下层,位于SurfaceFlinger层和Hardware Composer之间。该层拦截传递给硬件合成器的函数调用,以确保以正确的显示分辨率完成合成。第二层的作用是在DRS上层降低分辨率后提高分辨率,以便渲染的内容能够以本地显示分辨率正确显示在屏幕上。
这两个DRS层彼此同步,以确保它们对BufferQueue中的相同图形缓冲区使用相同的目标显示分辨率,此举很有必要,因为如果用户将目标显示分辨率更改为新值,DRS上层将开始使用新的缩放因子将图形缓冲区生成到BufferQueue中。DRS下层需要确保旧的缩放因子用于先前生成的图形缓冲区,并且新的缩放因子在合成期间仅应用于新生成的图形缓冲器。
不同缩放因子下每帧游戏和基准的标准化能量如下表:
从覆盖率测试中的15个应用程序,包括14个游戏和一个基准(上表中列出的名称)来评估在不同的显示分辨率下可以节省多少电量。在S5手机上运行测试用例,并使用季风功率监视器测量系统功率,将手机切换到飞行模式,禁用不必要的硬件组件,如GPS和摄像头,并将背光亮度设置为50%。将GPU频率锁定为500MHz,以避免GPU的DVFS推断。在每次测试前都会对手机进行冷却,以确保GPU能够在500MHz下工作至少60秒。将每个测试重复三次,并报告平均结果。
采用每帧总系统能量(EPF)作为衡量标准来评估原型系统的节能。为了方便地比较不同的结果,将结果标准化为原生显示分辨率的情况。上表显示了不同比例因子的归一化EPF。缩放因子被归一化为原生显示分辨率,即对于全分辨率,缩放因子为1.0。当显示分辨率降低一半(即缩放因子为0.5,将显示分辨率从2560x1440像素降低到1280x720像素)时,对于16个测试用例,平均而言,可以将EPF降低30.1%,范围从15.7%到60.5%。对于这14款游戏,无论缩放因子是什么值,它们总是以固定的帧速率运行。因此,在实践中可以实现相同的功耗节省量(如果仅计算这14款比赛,则为24.9%)。对于两种GFXBench情况,由于基准测试总是试图用尽所有GPU处理能力,因此其功耗在所有缩放因子中几乎保持不变。然而,分辨率会极大地影响帧速率。对于较小的缩放因子,它们可以以较高的帧速率运行,从而提供更好的用户体验。
3 电源优化
在开始应用程序开发之前,分析并定义应用程序的需求、范围和功能,以确保高效的功能和流畅的用户体验。为单一目的设计应用程序,并分析它如何最好地为用户服务。
以下指南帮助您设计和开发适用于具有不同特性(如屏幕大小和输入法支持)的移动设备的应用程序:
- 了解目标用户。了解谁将使用该应用程序,他们将使用它做什么,以及拥有哪些移动设备,然后设计应用程序以适应特定的使用环境。
- 小屏幕设计。移动设备的屏幕尺寸明显小于桌面设备的屏幕大小。仔细考量在应用程序UI上显示的最相关的内容是什么,因为在桌面应用程序中尝试将尽可能多的内容放入屏幕可能是不合理的。
- 多种屏幕尺寸的设计。将每个控件的位置和大小与显示器的尺寸相关联,使得同一组信息能够以所有分辨率显示在屏幕上,更高分辨率的设备只显示更精细的图形。
- 更改屏幕方向的设计。某些设备支持屏幕旋转,在这些设备上,应用程序可以纵向或横向显示,考虑方向并在屏幕旋转时动态调整显示。
- 设计在应用程序中移动的直观方式。移动设备缺少鼠标和全尺寸键盘,因此用户必须使用触摸屏或五向导航板在应用程序中移动,此外,许多用户用一只手控制设备。要创建优化的用户体验,允许用户一键访问信息,不要让它们滚动和键入。
- 有限输入法设计。应用程序从用户那里收集有关手头任务的信息。除了触摸屏输入,一些设备还包含物理键,如五向导航板、键盘和键盘。用户通过使用屏幕控件(如列表、复选框、单选按钮和文本字段)输入信息。
- 缩短响应时间。延迟可能会导致用户交互延迟。如果用户认为某个应用程序速度慢,他们很可能会感到沮丧并停止使用它。
- 节省电池时间。移动设备不总是连接到电源,而是依靠电池供电。优化功耗以将总功耗保持在可接受的水平,并防止用户耗尽电池时间。
- 考虑网络问题。如果用户没有固定费率的数据计划或WLAN支持,移动网络连接会让他们花钱。此外,当用户带着设备四处移动时,可用于连接的网络会不断变化。
- 记住设备的处理限制。设备上可用的内存有限,应谨慎使用。尽管所有移动设备都具有通用功能,但就可用资源和额外功能而言,每个设备都是独立的,因此必须考虑所有目标设备的约束。
- 最大限度地提高应用程序的效率和电池寿命。
- 优化切换器效率(针对大部分时间使用处理器的地方)。
- 使用PFM、PWM-PS提高低功率条件下的效率。
- 最小化物料清单(BOM)成本和面积。
- 电池技术(1、2或3个锂离子电池)。
- 保持功耗在应用范围内。
- 软件驱动程序支持。
- 灵活的加电顺序/默认电压,支持多处理器和外围设备。
- PMIC内部或外部音频。内部优势:降低成本,节省电路板空间,外部优势:噪音更小、更灵活。
- 使用动态分辨率。详见上节。
高通采用了整体系统方法,通过定制关键技术块和整个片上系统(SoC)来实现节能。
该系统方法涉及四个关键级别的功率和热量优化:
- 专用处理引擎。定制设计专用处理引擎和其他关键组件,如电源管理集成电路(PMIC)、射频(RF)芯片等。
- 微架构。
aSMP和其它典型的SMP实现。
- 电路设计。
- 晶体管级别设计。
骁龙SoC内的处理引擎。
- 智能集成。巧妙地集成了技术块并设计了系统架构。
- 系统结构/互连。
- 缓存和内存设计。
- 软件与硬件加速。
- 优化系统软件。将软件与硬件紧密结合。
- 软件工具和API。
- 软件、OS和编译器优化。
- 电源和热量算法。
- 设备级优化。仔细考量移动设备上的所有其他组件,并优化了整个解决方案的操作。
- 电源和热量模型。
- 设备组件优化。
- OEM最佳实践。
为了解决在具有功率和热量限制的设备中提供更高性能的日益增加的挑战,以移动为中心的设计方法至关重要。高通采用整体系统方法进行电源和热管理,其移动SoC通过设计专门的处理引擎,巧妙地集成它们,并优化系统软件和整个设备,实现了功率和热效率的最佳平衡,使移动设备能够提供最佳的用户体验。
19.11 UE硬件
本章节将基于UE 5.1的源码解析涉及的硬件接口和逻辑。
1 CPU
下面的接口可以计算CPU的性能等级等参数:
// GenericPlatformSurvey.h
struct FSynthBenchmarkResults
{
FSynthBenchmarkStat CPUStats[2];
// 计算CPU性能等级,100表明是平局等级的CPU, 小于100更慢, 大于100更快。
float ComputeCPUPerfIndex(TArray<float>* OutIndividualResults = nullptr) const;
};
下面的接口可以追踪CPU的性能,包含追踪数据、利用率、分析器等:
// CpuProfilerTrace.h
struct FCpuProfilerTrace
{
static uint32 OutputEventType(const ANSICHAR* Name, const ANSICHAR* File = nullptr, uint32 Line = 0);
static uint32 OutputEventType(const TCHAR* Name, const ANSICHAR* File = nullptr, uint32 Line = 0);
static void OutputBeginEvent(uint32 SpecId);
static void OutputBeginDynamicEvent(const ANSICHAR* Name, const ANSICHAR* File = nullptr, uint32 Line = 0);
static void OutputBeginDynamicEvent(const TCHAR* Name, const ANSICHAR* File = nullptr, uint32 Line = 0);
static void OutputBeginDynamicEvent(const FName& Name, const ANSICHAR* File = nullptr, uint32 Line = 0);
static void OutputEndEvent();
static void OutputResumeEvent(uint64 SpecId, uint32& TimerScopeDepth);
static void OutputSuspendEvent();
class FEventScope
{
(...)
};
struct FDynamicEventScope
{
(...)
};
(...)
};
// CpuProfilerTraceAnalysis.h
class FCpuProfilerAnalyzer : public UE::Trace::IAnalyzer
{
public:
virtual void OnAnalysisBegin(const FOnAnalysisContext& Context) override;
virtual void OnAnalysisEnd(/*const FOnAnalysisEndContext& Context*/) override;
virtual bool OnEvent(uint16 RouteId, EStyle Style, const FOnEventContext& Context) override;
private:
IAnalysisSession& Session;
IEditableTimingProfilerProvider& EditableTimingProfilerProvider;
IEditableThreadProvider& EditableThreadProvider;
TMap<uint32, FThreadState*> ThreadStatesMap;
TMap<uint32, uint32> SpecIdToTimerIdMap;
TMap<const TCHAR*, uint32> ScopeNameToTimerIdMap;
uint32 CoroutineTimerId = ~0;
uint32 CoroutineUnknownTimerId = ~0;
uint64 TotalEventSize = 0;
uint64 TotalScopeCount = 0;
double BytesPerScope = 0.0;
(...)
};
以下接口包含CPU的时钟、频率、亲缘性等信息和接口:
// GenericPlatformTime.h
// 包含CPU利用率数据
struct FCPUTime
{
float CPUTimePct; // 上一个间隔的CPU利用率百分比。
float CPUTimePctRelative; // 上一个间隔相对于一个核心的CPU利用率百分比,因此如果CPUTimePct为8.0%,而设备有6个核心,则该值将为48.0%。
};
// 时间
struct FGenericPlatformTime
{
// 时间、时钟、频率等接口
static TCHAR* StrDate( TCHAR* Dest, SIZE_T DestSize );
static TCHAR* StrTime( TCHAR* Dest, SIZE_T DestSize );
static const TCHAR* StrTimestamp();
static FString PrettyTime( double Seconds );
static bool UpdateCPUTime( float DeltaTime );
static bool UpdateThreadCPUTime(float = 0.0);
static void AutoUpdateGameThreadCPUTime(double UpdateInterval);
static FCPUTime GetCPUTime();
static FCPUTime GetThreadCPUTime();
static double GetLastIntervalCPUTimeInSeconds();
static double GetLastIntervalThreadCPUTimeInSeconds();
static double GetSecondsPerCycle();
static float ToMilliseconds( const uint32 Cycles );
static float ToSeconds( const uint32 Cycles );
static double GetSecondsPerCycle64();
static double ToMilliseconds64(const uint64 Cycles);
static double ToSeconds64(const uint64 Cycles);
(...)
protected:
static double SecondsPerCycle;
static double SecondsPerCycle64;
static double LastIntervalCPUTimeInSeconds;
};
// PlatformAffinity.h
struct FThreadAffinity
{
uint64 ThreadAffinityMask = FPlatformAffinity::GetNoAffinityMask();
uint16 ProcessorGroup = 0;
};
2 内存
以下代码包含内存的硬件信息、分配、缓存、池化等接口:
// GenericPlatformMemory.h
struct FGenericPlatformMemory
{
static bool bIsOOM; // 是否内存不足
static uint64 OOMAllocationSize; // 设置为触发内存不足的分配大小,否则为零.
static uint32 OOMAllocationAlignment; // 设置为触发内存不足的分配对齐,否则为零。
static void* BackupOOMMemoryPool; // 内存不足时要删除的预分配缓冲区。用于OOM处理和崩溃报告。
static uint32 BackupOOMMemoryPoolSize; // BackupOOMMemoryPool的大小(字节)。
// 可用于内存统计的各种内存区域。枚举的确切含义相对依赖于平台,尽管一般的(物理、GPU)很简单。一个平台可以添加更多的内存,并且不会影响其他平台,除了StatManager跟踪每个区域的最大可用内存(使用数组FPlatformMemory::MCR_max big)所需的少量内存之外.
enum EMemoryCounterRegion
{
MCR_Invalid, // not memory
MCR_Physical, // main system memory
MCR_GPU, // memory directly a GPU (graphics card, etc)
MCR_GPUSystem, // system memory directly accessible by a GPU
MCR_TexturePool, // presized texture pools
MCR_StreamingPool, // amount of texture pool available for streaming.
MCR_UsedStreamingPool, // amount of texture pool used for streaming.
MCR_GPUDefragPool, // presized pool of memory that can be defragmented.
MCR_PhysicalLLM, // total physical memory including CPU and GPU
MCR_MAX
};
// 使用的分配器.
enum EMemoryAllocatorToUse
{
Ansi, // Default C allocator
Stomp, // Allocator to check for memory stomping
TBB, // Thread Building Blocks malloc
Jemalloc, // Linux/FreeBSD malloc
Binned, // Older binned malloc
Binned2, // Newer binned malloc
Binned3, // Newer VM-based binned malloc, 64 bit only
Platform, // Custom platform specific allocator
Mimalloc, // mimalloc
};
static EMemoryAllocatorToUse AllocatorToUse;
enum ESharedMemoryAccess
{
Read = (1 << 1),
Write = (1 << 2)
};
// 共享内存区域的通用表示
struct FSharedMemoryRegion
{
TCHAR Name[MaxSharedMemoryName];
uint32 AccessMode;
void * Address;
SIZE_T Size;
};
// 内存操作.
static void Init();
static void OnOutOfMemory(uint64 Size, uint32 Alignment);
static void SetupMemoryPools();
static uint32 GetBackMemoryPoolSize()
static FMalloc* BaseAllocator();
static FPlatformMemoryStats GetStats();
static uint64 GetMemoryUsedFast();
static void GetStatsForMallocProfiler( FGenericMemoryStats& out_Stats );
static const FPlatformMemoryConstants& GetConstants();
static uint32 GetPhysicalGBRam();
static bool PageProtect(void* const Ptr, const SIZE_T Size, const bool bCanRead, const bool bCanWrite);
// 分配.
static void* BinnedAllocFromOS( SIZE_T Size );
static void BinnedFreeToOS( void* Ptr, SIZE_T Size );
static void NanoMallocInit();
static bool PtrIsOSMalloc( void* Ptr);
static bool IsNanoMallocAvailable();
static bool PtrIsFromNanoMalloc( void* Ptr);
// 虚拟内存块及操作.
class FBasicVirtualMemoryBlock
{
protected:
void *Ptr;
uint32 VMSizeDivVirtualSizeAlignment;
public:
FBasicVirtualMemoryBlock(const FBasicVirtualMemoryBlock& Other) = default;
FBasicVirtualMemoryBlock& operator=(const FBasicVirtualMemoryBlock& Other) = default;
FORCEINLINE uint32 GetActualSizeInPages() const;
FORCEINLINE void* GetVirtualPointer() const;
void Commit(size_t InOffset, size_t InSize);
void Decommit(size_t InOffset, size_t InSize);
void FreeVirtual();
void CommitByPtr(void *InPtr, size_t InSize);
void DecommitByPtr(void *InPtr, size_t InSize);
void Commit();
void Decommit();
size_t GetActualSize() const;
static FPlatformVirtualMemoryBlock AllocateVirtual(size_t Size, ...);
static size_t GetCommitAlignment();
static size_t GetVirtualSizeAlignment();
};
// 数据和调试
static bool BinnedPlatformHasMemoryPoolForThisSize(SIZE_T Size);
static void DumpStats( FOutputDevice& Ar );
static void DumpPlatformAndAllocatorStats( FOutputDevice& Ar );
static EPlatformMemorySizeBucket GetMemorySizeBucket();
// 内存数据操作.
static void* Memmove( void* Dest, const void* Src, SIZE_T Count );
static int32 Memcmp( const void* Buf1, const void* Buf2, SIZE_T Count );
static void* Memset(void* Dest, uint8 Char, SIZE_T Count);
static void* Memzero(void* Dest, SIZE_T Count);
static void* Memcpy(void* Dest, const void* Src, SIZE_T Count);
static void* BigBlockMemcpy(void* Dest, const void* Src, SIZE_T Count);
static void* StreamingMemcpy(void* Dest, const void* Src, SIZE_T Count);
static void* ParallelMemcpy(void* Dest, const void* Src, SIZE_T Count, EMemcpyCachePolicy Policy = EMemcpyCachePolicy::StoreCached);
(...)
};
// 结构用于保存所有平台的通用内存常数。这些值不会在可执行文件的整个生命周期内发生变化。
struct FGenericPlatformMemoryConstants
{
// 实际物理内存量,以字节为单位(对于运行32位代码的64位设备,需要处理>4GB)。
uint64 TotalPhysical;
// 虚拟内存量,以字节为单位
uint64 TotalVirtual;
// 物理页面的大小,以字节为单位,也是物理RAM的PageProtection()、提交和属性(例如访问能力)的粒度。
SIZE_T PageSize;
// 如果内存以大于PageSize的块分配,则某些平台具有优势(例如VirtualAlloc()目前的粒度似乎为64KB),该值是系统将在后台使用的最小分配大小。
SIZE_T OsAllocationGranularity;
// Binned2 malloc术语中“页面”的大小,以字节为单位,至少为64KB。BinnedMloc希望从BinnedAllocFromOS()返回的内存与BinnedPageSize边界对齐。
SIZE_T BinnedPageSize;
// BinnedMalloc术语中的“分配粒度”,即BinnedMlloc将以该值的增量分配内存。如果为0,Binned将对此值使用BinnedPageSize.
SIZE_T BinnedAllocationGranularity;
// AddressLimit-第二个参数是BinnedAllocFromOS()预期返回的地址范围的估计值。Binned Malloc将调整其内部结构,以查找此范围的内存分配O(1)。超出这个范围是可以的,查找会稍微慢一点
uint64 AddressLimit;
// 近似物理RAM(GB),除PC外的所有设备上都有1。用于“course tuning”,如FPlatformMisc::NumberOfCores()。
uint32 TotalPhysicalGB;
};
// 用于保存所有平台的通用内存统计信息,可能会在可执行文件的整个生命周期内发生变化。
struct FGenericPlatformMemoryStats : public FPlatformMemoryConstants
{
// 当前可用的物理内存量,以字节为单位。
uint64 AvailablePhysical;
// 当前可用的虚拟内存量(字节)。
uint64 AvailableVirtual;
// 进程使用的物理内存量,以字节为单位。
uint64 UsedPhysical;
// 进程使用的物理内存的峰值量,以字节为单位
uint64 PeakUsedPhysical;
// 进程使用的虚拟内存总量。
uint64 UsedVirtual;
// 进程使用的虚拟内存的峰值量。
uint64 PeakUsedVirtual;
// 内存压力状态,适用于可用内存估计可能不考虑关闭非活动进程或诉诸交换可回收内存的平台。
enum class EMemoryPressureStatus : uint8
{
Unknown,
Nominal,
Critical, // OOM(Out Of Memory)条件的高风险
};
EMemoryPressureStatus GetMemoryPressureStatus();
struct FPlatformSpecificStat
{
const TCHAR* Name;
uint64 Value;
};
TArray<FPlatformSpecificStat> GetPlatformSpecificStats() const;
uint64 GetAvailablePhysical(bool bExcludeExtraDevMemory) const;
// 由FCsvProfiler::EndFrame调用以设置特定于平台的CSV统计信息。
void SetEndFrameCsvStats() const {}
};
以下接口指示了D3D12的某些资源是CPU或GPU的可读可写性:
// D3D12Util.h
inline bool IsCPUWritable(D3D12_HEAP_TYPE HeapType, const D3D12_HEAP_PROPERTIES *pCustomHeapProperties = nullptr);
inline bool IsGPUOnly(D3D12_HEAP_TYPE HeapType, const D3D12_HEAP_PROPERTIES *pCustomHeapProperties = nullptr);
inline bool IsCPUAccessible(D3D12_HEAP_TYPE HeapType, const D3D12_HEAP_PROPERTIES* pCustomHeapProperties = nullptr);
以下代码是内存追踪相关的类型和接口:
// MemoryTrace.h
enum EMemoryTraceRootHeap : uint8
{
SystemMemory, // RAM
VideoMemory, // VRAM
EndHardcoded = VideoMemory,
EndReserved = 15
};
// 追踪堆标记。
enum class EMemoryTraceHeapFlags : uint16
{
None = 0,
Root = 1 << 0,
NeverFrees = 1 << 1, // The heap doesn't free (e.g. linear allocator)
};
ENUM_CLASS_FLAGS(EMemoryTraceHeapFlags);
enum class EMemoryTraceHeapAllocationFlags : uint8
{
None = 0,
Heap = 1 << 0, // Is a heap, can be used to unmark alloc as heap.
};
ENUM_CLASS_FLAGS(EMemoryTraceHeapAllocationFlags);
class FMalloc* MemoryTrace_Create(class FMalloc* InMalloc);
void MemoryTrace_Initialize();
HeapId MemoryTrace_HeapSpec(HeapId ParentId, const TCHAR* Name, EMemoryTraceHeapFlags Flags = EMemoryTraceHeapFlags::None);
HeapId MemoryTrace_RootHeapSpec(const TCHAR* Name, EMemoryTraceHeapFlags Flags = EMemoryTraceHeapFlags::None);
void MemoryTrace_MarkAllocAsHeap(uint64 Address, HeapId Heap, ...);
void MemoryTrace_UnmarkAllocAsHeap(uint64 Address, HeapId Heap);
void MemoryTrace_Alloc(uint64 Address, uint64 Size, uint32 Alignment, HeapId RootHeap = EMemoryTraceRootHeap::SystemMemory);
void MemoryTrace_Free(uint64 Address, HeapId RootHeap = EMemoryTraceRootHeap::SystemMemory);
void MemoryTrace_ReallocFree(uint64 Address, HeapId RootHeap = EMemoryTraceRootHeap::SystemMemory);
void MemoryTrace_ReallocAlloc(uint64 Address, uint64 NewSize, uint32 Alignment, HeapId RootHeap = EMemoryTraceRootHeap::SystemMemory);
(...)
以下代码涉及了GPU资源数组的操作:
// ResourceArray.h
// 资源数组的独立于元素类型的接口。
class FResourceArrayInterface
{
public:
virtual const void* GetResourceData() const = 0;
virtual uint32 GetResourceDataSize() const = 0;
virtual void Discard() = 0;
virtual bool IsStatic() const = 0;
virtual bool GetAllowCPUAccess() const = 0;
virtual void SetAllowCPUAccess( bool bInNeedsCPUAccess ) = 0;
};
// 允许直接为批量资源类型分配GPU内存。
class FResourceBulkDataInterface
{
public:
virtual const void* GetResourceBulkData() const = 0;
virtual uint32 GetResourceBulkDataSize() const = 0;
virtual void Discard() = 0;
enum class EBulkDataType
{
Default,
MediaTexture,
VREyeBuffer,
};
virtual EBulkDataType GetResourceType() const;
};
// 允许直接为纹理资源分配GPU内存。
class FTexture2DResourceMem : public FResourceBulkDataInterface
{
public:
virtual void* GetMipData(int32 MipIdx) = 0;
virtual int32 GetNumMips() = 0;
virtual int32 GetSizeX() = 0;
virtual int32 GetSizeY() = 0;
virtual bool IsValid() = 0;
virtual bool HasAsyncAllocationCompleted() const = 0;
virtual void FinishAsyncAllocation() = 0;
virtual void CancelAsyncAllocation() = 0;
};
以下是操作系统页缓存分配器:
// CachedOSPageAllocator.h
struct FCachedOSPageAllocator
{
protected:
struct FFreePageBlock
{
void* Ptr;
SIZE_T ByteSize;
};
void* AllocateImpl(SIZE_T Size, uint32 CachedByteLimit, FFreePageBlock* First, FFreePageBlock* Last, ...);
void FreeImpl(void* Ptr, SIZE_T Size, uint32 NumCacheBlocks, uint32 CachedByteLimit, FFreePageBlock* First, ...);
void FreeAllImpl(FFreePageBlock* First, uint32& FreedPageBlocksNum, SIZE_T& CachedTotal, FCriticalSection* Mutex);
};
template <uint32 NumCacheBlocks, uint32 CachedByteLimit>
struct TCachedOSPageAllocator : private FCachedOSPageAllocator
{
void* Allocate(SIZE_T Size, uint32 AllocationHint = 0, FCriticalSection* Mutex = nullptr);
void Free(void* Ptr, SIZE_T Size, FCriticalSection* Mutex = nullptr, bool ThreadIsTimeCritical = false);
void FreeAll(FCriticalSection* Mutex = nullptr);
void UpdateStats();
uint64 GetCachedFreeTotal();
private:
FFreePageBlock FreedPageBlocks[NumCacheBlocks*2];
SIZE_T CachedTotal;
uint32 FreedPageBlocksNum;
};
// CachedOSVeryLargePageAllocator.h
// 超大页面的缓存分配器。
class FCachedOSVeryLargePageAllocator
{
// 将地址空间设置为所需的两倍,并将第一个用于小池分配,第二个用于仍为==SizeOfSubPage的其他分配
#if UE_VERYLARGEPAGEALLOCATOR_TAKEONALL64KBALLOCATIONS
static constexpr uint64 AddressSpaceToReserve = ((1024LL * 1024LL * 1024LL) * UE_VERYLARGEPAGEALLOCATOR_RESERVED_SIZE_IN_GB * 2LL);
static constexpr uint64 AddressSpaceToReserveForSmallPool = AddressSpaceToReserve/2;
#else
static constexpr uint64 AddressSpaceToReserve = ((1024 * 1024 * 1024LL) * UE_VERYLARGEPAGEALLOCATOR_RESERVED_SIZE_IN_GB);
static constexpr uint64 AddressSpaceToReserveForSmallPool = AddressSpaceToReserve;
#endif
static constexpr uint64 SizeOfLargePage = (UE_VERYLARGEPAGEALLOCATOR_PAGESIZE_KB * 1024);
static constexpr uint64 SizeOfSubPage = (1024 * 64);
static constexpr uint64 NumberOfLargePages = (AddressSpaceToReserve / SizeOfLargePage);
static constexpr uint64 NumberOfSubPagesPerLargePage = (SizeOfLargePage / SizeOfSubPage);
public:
void* Allocate(SIZE_T Size, uint32 AllocationHint = 0, FCriticalSection* Mutex = nullptr);
void Free(void* Ptr, SIZE_T Size, FCriticalSection* Mutex = nullptr, bool ThreadIsTimeCritical = false);
void FreeAll(FCriticalSection* Mutex = nullptr);
void UpdateStats();
uint64 GetCachedFreeTotal();
bool IsPartOf(const void* Ptr);
private:
(...)
FLargePage* FreeLargePagesHead[FMemory::AllocationHints::Max]; // no backing store
FLargePage* UsedLargePagesHead[FMemory::AllocationHints::Max]; // has backing store and is full
FLargePage* UsedLargePagesWithSpaceHead[FMemory::AllocationHints::Max]; // has backing store and still has room
FLargePage* EmptyButAvailableLargePagesHead[FMemory::AllocationHints::Max]; // has backing store and is empty
FLargePage LargePagesArray[NumberOfLargePages];
TCachedOSPageAllocator<CACHEDOSVERYLARGEPAGEALLOCATOR_MAX_CACHED_OS_FREES, CACHEDOSVERYLARGEPAGEALLOCATOR_BYTE_LIMIT> CachedOSPageAllocator;
};
CORE_API extern bool GEnableVeryLargePageAllocator;
// PooledVirtualMemoryAllocator.h
// 此Class将从FMallocBinned2进行的OS分配汇集在一起。
struct FPooledVirtualMemoryAllocator
{
void* Allocate(SIZE_T Size, uint32 AllocationHint = 0, FCriticalSection* Mutex = nullptr);
void Free(void* Ptr, SIZE_T Size, FCriticalSection* Mutex = nullptr, bool ThreadIsTimeCritical = false);
void FreeAll(FCriticalSection* Mutex = nullptr);
// 描述特定大小池的结构
struct FPoolDescriptorBase
{
FPoolDescriptorBase* Next;
SIZE_T VMSizeDivVirtualSizeAlignment;
};
uint64 GetCachedFreeTotal();
void UpdateStats();
private:
enum Limits
{
NumAllocationSizeClasses = 64,
MaxAllocationSizeToPool = NumAllocationSizeClasses * 65536,
MaxOSAllocCacheSize = 64 * 1024 * 1024,
MaxOSAllocsCached = 64
};
int32 GetAllocationSizeClass(SIZE_T Size);
SIZE_T CalculateAllocationSizeFromClass(int32 Class);
int32 NextPoolSize[Limits::NumAllocationSizeClasses];
FPoolDescriptorBase* ClassesListHeads[Limits::NumAllocationSizeClasses];
FCriticalSection ClassesLocks[Limits::NumAllocationSizeClasses];
void DecideOnTheNextPoolSize(int32 SizeClass, bool bGrowing);
FPoolDescriptorBase* CreatePool(SIZE_T AllocationSize, int32 NumPooledAllocations);
void DestroyPool(FPoolDescriptorBase* Pool);
FCriticalSection OsAllocatorCacheLock;
TCachedOSPageAllocator<MaxOSAllocsCached, MaxOSAllocCacheSize> OsAllocatorCache;
};
以下是虚拟内存分配器:
// VirtualAllocator.h
class FVirtualAllocator
{
struct FFreeLink
{
void *Ptr = nullptr;
FFreeLink* Next = nullptr;
};
struct FPerBlockSize
{
int64 AllocBlocksSize = 0;
int64 FreeBlocksSize = 0;
FFreeLink* FirstFree = nullptr;
};
FCriticalSection CriticalSection;
uint8* LowAddress;
uint8* HighAddress;
size_t TotalSize;
size_t PageSize;
size_t MaximumAlignment;
uint8* NextAlloc;
FFreeLink* RecycledLinks;
int64 LinkSize;
bool bBacksMalloc;
FPerBlockSize Blocks[64];
void FreeVirtualByBlock(void* Ptr, FPerBlockSize& Block, size_t AlignedSize);
protected:
size_t SpaceConsumed;
virtual uint8* AllocNewVM(size_t AlignedSize)
{
uint8* Result = NextAlloc;
check(IsAligned(Result, MaximumAlignment) && IsAligned(AlignedSize, MaximumAlignment));
NextAlloc = Result + AlignedSize;
SpaceConsumed = NextAlloc - LowAddress;
return Result;
}
public:
uint32 GetPagesForSizeAndAlignment(size_t Size, size_t Alignment = 1) const;
void* AllocateVirtualPages(uint32 NumPages, size_t AlignmentForCheck = 1);
void FreeVirtual(void* Ptr, uint32 NumPages);
struct FVirtualAllocatorStatsPerBlockSize
{
size_t AllocBlocksSize;
size_t FreeBlocksSize;
};
struct FVirtualAllocatorStats
{
size_t PageSize;
size_t MaximumAlignment;
size_t VMSpaceTotal;
size_t VMSpaceConsumed;
size_t VMSpaceConsumedPeak;
size_t FreeListLinks;
FVirtualAllocatorStatsPerBlockSize BlockStats[64];
};
void GetStats(FVirtualAllocatorStats& OutStats);
};
3 GPU
下面的接口可以计算GPU的性能等级等参数:
// GenericPlatformSurvey.h
struct FSynthBenchmarkResults
{
FSynthBenchmarkStat GPUStats[7];
// 计算GPU性能等级,100表明是平局等级的CPU, 小于100更慢, 大于100更快。
float ComputeGPUPerfIndex(TArray<float>* OutIndividualResults = nullptr) const;
// 以秒为单位返回,用于检查基准测试是否耗时过长(硬件速度非常慢,不要使用大型WorkScale进行测试).
float ComputeTotalGPUTime() const;
};
// GPU适配器
struct FGPUAdpater
{
static const uint32 MaxStringLength = 260;
// 名称
TCHAR AdapterName[MaxStringLength];
// 内部驱动版本
TCHAR AdapterInternalDriverVersion[MaxStringLength];
// 用户驱动版本
TCHAR AdapterUserDriverVersion[MaxStringLength];
// 额外的数据
TCHAR AdapterDriverDate[MaxStringLength];
// 适配器专用的内存
TCHAR AdapterDedicatedMemoryMB[MaxStringLength];
};
下面代码涉及了GPU驱动相关的信息和操作:
// GenericPlatformDriver.h
// GPU驱动信息。
struct FGPUDriverInfo
{
// DirectX VendorId,0(如果未设置),请使用以下函数设置/获取
uint32 VendorId;
// e.g. "NVIDIA GeForce GTX 680" or "AMD Radeon R9 200 / HD 7900 Series"
FString DeviceDescription;
// e.g. "NVIDIA" or "Advanced Micro Devices, Inc."
FString ProviderName;
// e.g. "15.200.1062.1004"(AMD)
// e.g. "9.18.13.4788"(NVIDIA)
// 第一个数字是Windows版本(例如7:Vista、6:XP、4:Me、9:Win8(1)、10:Win7),最后5个数字编码了UserDriver版本,也称为技术版本号(https://wiki.mozilla.org/Blocklisting/Blocked_Graphics_Drivers)如果驱动程序检测失败,则TEXT("Unknown")
FString InternalDriverVersion;
// e.g. "Catalyst 15.7.1"(AMD) or "Crimson 15.7.1"(AMD) or "347.88"(NVIDIA)
// 也称为商业版本号
FString UserDriverVersion;
// e.g. 3-13-2015
FString DriverDate;
// e.g. D3D11, D3D12
FString RHIName;
bool IsValid() const;
// get VendorId
bool IsAMD() const { return VendorId == 0x1002; }
// get VendorId
bool IsIntel() const { return VendorId == 0x8086; }
// get VendorId
bool IsNVIDIA() const { return VendorId == 0x10DE; }
bool IsSameDriverVersionGeneration(const TCHAR* InOpWithMultiInt) const;
static FString TrimNVIDIAInternalVersion(const FString& InternalVersion);
FString GetUnifiedDriverVersion() const;
};
// Hardware.ini文件中的一个条目
struct FDriverDenyListEntry
{
// optional, e.g. "<=223.112.21.1", might includes comparison operators, later even things multiple ">12.22 <=12.44"
FString DriverVersionString;
// optional, e.g. "<=MM-DD-YYYY"
FString DriverDateString;
// optional, e.g. "D3D11", "D3D12"
FString RHIName;
// required
FString Reason;
void LoadFromINIString(const TCHAR* In);
bool IsValid() const;
bool IsLatestDenied() const;
};
// GPU硬件信息
struct FGPUHardware
{
const FGPUDriverInfo DriverInfo;
FString GetSuggestedDriverVersion(const FString& InRHIName) const;
FDriverDenyListEntry FindDriverDenyListEntry() const;
bool IsLatestDenied() const;
FString GetVendorSectionName() const;
};
下面代码是GPU的装箱分配器:
// MallocBinnedGPU.h
class FMallocBinnedGPU final : public FMalloc
{
struct FGPUMemoryBlockProxy
{
uint8 MemoryModifiedByCPU[32 - sizeof(void*)]; // might be modified for free list links, etc
void *GPUMemory; // pointer to the actual GPU memory, which we cannot modify with the CPU
};
struct FFreeBlock
{
uint16 BlockSizeShifted; // Size of the blocks that this list points to >> ArenaParams.MinimumAlignmentShift
uint8 PoolIndex; // Index of this pool
uint8 Canary; // Constant value of 0xe3
uint32 NumFreeBlocks; // Number of consecutive free blocks here, at least 1.
FFreeBlock* NextFreeBlock; // Next free block or nullptr
};
struct FPoolTable
{
uint32 BlockSize;
uint16 BlocksPerBlockOfBlocks;
uint8 PagesPlatformForBlockOfBlocks;
FBitTree BlockOfBlockAllocationBits; // one bits in here mean the virtual memory is committed
FBitTree BlockOfBlockIsExhausted; // one bit in here means the pool is completely full
uint32 NumEverUsedBlockOfBlocks;
FPoolInfoSmall** PoolInfos;
uint64 UnusedAreaOffsetLow;
};
struct FPtrToPoolMapping
{
private:
/** Shift to apply to a pointer to get the reference from the indirect tables */
uint64 PtrToPoolPageBitShift;
/** Shift required to get required hash table key. */
uint64 HashKeyShift;
/** Used to mask off the bits that have been used to lookup the indirect table */
uint64 PoolMask;
// PageSize dependent constants
uint64 MaxHashBuckets;
};
struct FBundleNode
{
FBundleNode* NextNodeInCurrentBundle;
union
{
FBundleNode* NextBundle;
int32 Count;
};
};
struct FBundle
{
FBundleNode* Head;
uint32 Count;
};
// 空闲的块列表
struct FFreeBlockList
{
bool PushToFront(FMallocBinnedGPU& Allocator, void* InPtr, uint32 InPoolIndex, uint32 InBlockSize, const FArenaParams& LocalArenaParams);
bool CanPushToFront(uint32 InPoolIndex, uint32 InBlockSize, const FArenaParams& LocalArenaParams);
void* PopFromFront(FMallocBinnedGPU& Allocator, uint32 InPoolIndex);
FBundleNode* RecyleFull(FArenaParams& LocalArenaParams, FGlobalRecycler& GGlobalRecycler, uint32 InPoolIndex);
bool ObtainPartial(FArenaParams& LocalArenaParams, FGlobalRecycler& GGlobalRecycler, uint32 InPoolIndex);
FBundleNode* PopBundles(uint32 InPoolIndex);
private:
FBundle PartialBundle;
FBundle FullBundle;
};
// 逐线程的空闲块列表
struct FPerThreadFreeBlockLists
{
static FPerThreadFreeBlockLists* Get(uint32 BinnedGPUTlsSlot);
static void SetTLS(FMallocBinnedGPU& Allocator);
static int64 ClearTLS(FMallocBinnedGPU& Allocator);
void* Malloc(FMallocBinnedGPU& Allocator, uint32 InPoolIndex);
bool Free(FMallocBinnedGPU& Allocator, void* InPtr, uint32 InPoolIndex, uint32 InBlockSize, const FArenaParams& LocalArenaParams);
bool CanFree(uint32 InPoolIndex, uint32 InBlockSize, const FArenaParams& LocalArenaParams);
FBundleNode* RecycleFullBundle(FArenaParams& LocalArenaParams, FGlobalRecycler& GlobalRecycler, uint32 InPoolIndex);
bool ObtainRecycledPartial(FArenaParams& LocalArenaParams, FGlobalRecycler& GlobalRecycler, uint32 InPoolIndex);
FBundleNode* PopBundles(uint32 InPoolIndex);
int64 AllocatedMemory;
TArray<FFreeBlockList> FreeLists;
};
// 全局回收器
struct FGlobalRecycler
{
void Init(uint32 PoolCount);
bool PushBundle(uint32 NumCachedBundles, uint32 InPoolIndex, FBundleNode* InBundle);
FBundleNode* PopBundle(uint32 NumCachedBundles, uint32 InPoolIndex);
private:
struct FPaddedBundlePointer
{
FBundleNode* FreeBundles[BINNEDGPU_MAX_GMallocBinnedGPUMaxBundlesBeforeRecycle];
};
TArray<FPaddedBundlePointer> Bundles;
};
uint64 PoolIndexFromPtr(const void* Ptr);
uint8* PoolBasePtr(uint32 InPoolIndex);
uint64 PoolIndexFromPtrChecked(const void* Ptr);
bool IsOSAllocation(const void* Ptr);
void* BlockOfBlocksPointerFromContainedPtr(const void* Ptr, uint8 PagesPlatformForBlockOfBlocks, uint32& OutBlockOfBlocksIndex);
uint8* BlockPointerFromIndecies(uint32 InPoolIndex, uint32 BlockOfBlocksIndex, uint32 BlockOfBlocksSize);
FPoolInfoSmall* PushNewPoolToFront(FMallocBinnedGPU& Allocator, uint32 InBlockSize, uint32 InPoolIndex, uint32& OutBlockOfBlocksIndex);
FPoolInfoSmall* GetFrontPool(FPoolTable& Table, uint32 InPoolIndex, uint32& OutBlockOfBlocksIndex);
bool AdjustSmallBlockSizeForAlignment(SIZE_T& InOutSize, uint32 Alignment);
public:
FArenaParams& GetParams();
void InitMallocBinned();
virtual bool IsInternallyThreadSafe() const override;
virtual void* Malloc(SIZE_T Size, uint32 Alignment) override;
virtual void* Realloc(void* Ptr, SIZE_T NewSize, uint32 Alignment) override;
virtual void Free(void* Ptr) override;
virtual bool GetAllocationSize(void *Ptr, SIZE_T &SizeOut) override;
virtual SIZE_T QuantizeSize(SIZE_T Count, uint32 Alignment) override;
virtual bool ValidateHeap() override;
virtual void Trim(bool bTrimThreadCaches) override;
virtual void SetupTLSCachesOnCurrentThread() override;
virtual void ClearAndDisableTLSCachesOnCurrentThread() override;
virtual const TCHAR* GetDescriptiveName() override;
void FlushCurrentThreadCache();
void* MallocExternal(SIZE_T Size, uint32 Alignment);
void FreeExternal(void *Ptr);
bool GetAllocationSizeExternal(void* Ptr, SIZE_T& SizeOut);
MBG_STAT(int64 GetTotalAllocatedSmallPoolMemory();)
virtual void GetAllocatorStats(FGenericMemoryStats& out_Stats) override;
virtual void DumpAllocatorStats(class FOutputDevice& Ar) override;
uint32 BoundSizeToPoolIndex(SIZE_T Size);
uint32 PoolIndexToBlockSize(uint32 PoolIndex);
void Commit(uint32 InPoolIndex, void *Ptr, SIZE_T Size);
void Decommit(uint32 InPoolIndex, void *Ptr, SIZE_T Size);
(...)
// Pool tables for different pool sizes
TArray<FPoolTable> SmallPoolTables;
uint32 SmallPoolInfosPerPlatformPage;
PoolHashBucket* HashBuckets;
PoolHashBucket* HashBucketFreeList;
uint64 NumLargePoolsPerPage;
FCriticalSection Mutex;
FGlobalRecycler GGlobalRecycler;
FPtrToPoolMapping PtrToPoolMapping;
FArenaParams ArenaParams;
TArray<uint16> SmallBlockSizesReversedShifted; // this is reversed to get the smallest elements on our main cache line
uint32 BinnedGPUTlsSlot;
uint64 PoolSearchDiv; // if this is zero, the VM turned out to be contiguous anyway so we use a simple subtract and shift
uint8* HighestPoolBaseVMPtr; // this is a duplicate of PoolBaseVMPtr[ArenaParams.PoolCount - 1]
FPlatformMemory::FPlatformVirtualMemoryBlock PoolBaseVMBlock;
TArray<uint8*> PoolBaseVMPtr;
TArray<FPlatformMemory::FPlatformVirtualMemoryBlock> PoolBaseVMBlocks;
// Mapping of sizes to small table indices
TArray<uint8> MemSizeToIndex;
FCriticalSection FreeBlockListsRegistrationMutex;
TArray<FPerThreadFreeBlockLists*> RegisteredFreeBlockLists;
TArray<void*> MallocedPointers;
};
// GPUDefragAllocator.h
// 简单的最适合分配器,无论何时何地都可以拆分和合并。不是线程安全的。使用TMap查找给定指针的内存块(可能与malloc/free主线程冲突)使用单独的链接列表进行自由分配,假设由于合并导致相对较少的自由块.
class FGPUDefragAllocator
{
public:
typedef TDoubleLinkedList<FAsyncReallocationRequest*> FRequestList;
typedef TDoubleLinkedList<FAsyncReallocationRequest*>::TDoubleLinkedListNode FRequestNode;
// 分配器设置的容器
struct FSettings
{
int32 MaxDefragRelocations;
int32 MaxDefragDownShift;
int32 OverlappedBandwidthScale;
};
enum EMemoryElementType
{
MET_Allocated,
MET_Free,
MET_Locked,
MET_Relocating,
MET_Resizing,
MET_Resized,
MET_Max
};
struct FMemoryLayoutElement
{
int32 Size;
EMemoryElementType Type;
};
// 分配器重新分配统计信息的容器。
struct FRelocationStats
{
int64 NumBytesRelocated;
int64 NumBytesDownShifted;
int64 LargestHoleSize;
int32 NumRelocations;
int32 NumHoles;
int32 NumLockedChunks;
};
// 包含单个分配或空闲块的信息。
class FMemoryChunk
{
public:
uint8* Base;
int64 Size;
int64 OrigSize;
bool bIsAvailable;
int32 LockCount;
uint16 DefragCounter;
// 允许访问FBestFitAllocator成员,如FirstChunk、FirstFreeChunk和LastChunk。
FGPUDefragAllocator& BestFitAllocator;
FMemoryChunk* PreviousChunk;
FMemoryChunk* NextChunk;
FMemoryChunk* PreviousFreeChunk;
FMemoryChunk* NextFreeChunk;
uint32 SyncIndex;
int64 SyncSize;
void* UserPayload;
TStatId Stat;
bool bTail;
};
virtual void* Allocate(int64 AllocationSize, int32 Alignment, TStatId InStat, bool bAllowFailure);
virtual void Free(void* Pointer);
virtual void Lock(const void* Pointer);
virtual void Unlock(const void* Pointer);
void* Reallocate(void* OldBaseAddress, int64 NewSize);
void DefragmentMemory(FRelocationStats& Stats);
void SetUserPayload(const void* Pointer, void* UserPayload);
void* GetUserPayload(const void* Pointer);
int64 GetAllocatedSize(void* Pointer);
bool IsValidPoolMemory(const void* Pointer) const;
void DumpAllocs(FOutputDevice& Ar = *GLog);
int64 GetTotalSize() const;
int32 GetLargestAvailableAllocation(int32* OutNumFreeChunks = nullptr);
uint32 GetBlockedCycles() const
bool InBenchmarkMode() const
bool GetTextureMemoryVisualizeData(FColor* TextureData, int32 SizeX, int32 SizeY, int32 Pitch, const int32 PixelSize);
void GetMemoryLayout(TArray<FMemoryLayoutElement>& MemoryLayout);
virtual int32 Tick(FRelocationStats& Stats, bool bPanicDefrag);
bool FinishAllRelocations();
void BlockOnAsyncReallocation(FAsyncReallocationRequest* Request);
void CancelAsyncReallocation(FAsyncReallocationRequest* Request, const void* CurrentBaseAddress);
static bool IsAligned(const volatile void* Ptr, const uint32 Alignment);
int32 GetAllocationAlignment() const;
(...)
};
下面涉及了动态分辨率:
// DynamicResolutionProxy.h
// 渲染线程代理是动态解析的启发式方法
class FDynamicResolutionHeuristicProxy
{
public:
static constexpr uint64 kInvalidEntryId = ~uint64(0);
void Reset_RenderThread();
uint64 CreateNewPreviousFrameTimings_RenderThread(float GameThreadTimeMs, float RenderThreadTimeMs);
void CommitPreviousFrameGPUTimings_RenderThread(...);
void RefreshCurentFrameResolutionFraction_RenderThread();
float GetResolutionFractionUpperBound() const;
float QueryCurentFrameResolutionFraction_RenderThread() const;
float GetResolutionFractionApproximation_GameThread() const;
static TSharedPtr< class IDynamicResolutionState > CreateDefaultState();
private:
struct FrameHistoryEntry
{
float ResolutionFraction;
float GameThreadTimeMs;
float RenderThreadTimeMs;
float TotalFrameGPUBusyTimeMs;
float GlobalDynamicResolutionTimeMs;
bool bGPUTimingsHaveCPUBubbles;
};
TArray<FrameHistoryEntry> History;
int32 PreviousFrameIndex;
int32 HistorySize;
int32 NumberOfFramesSinceScreenPercentageChange;
int32 IgnoreFrameRemainingCount;
float CurrentFrameResolutionFraction;
uint64 FrameCounter;
};
下面代码吗涉及了多GPU、GPU掩码等逻辑:
// MultiGPU.h
/** A mask where each bit is a GPU index. Can not be empty so that non SLI platforms can optimize it to be always 1. */
struct FRHIGPUMask
{
private:
uint32 GPUMask;
uint32 ToIndex() const;
bool HasSingleIndex() const;
uint32 GetLastIndex() const;
uint32 GetFirstIndex() const;
bool Contains(uint32 GPUIndex) const;
bool ContainsAll(const FRHIGPUMask& Rhs) const;
bool Intersects(const FRHIGPUMask& Rhs) const;
bool operator ==(const FRHIGPUMask& Rhs) const;
bool operator !=(const FRHIGPUMask& Rhs) const;
uint32 GetNative() const;
static const FRHIGPUMask GPU0() { return FRHIGPUMask(1); }
static const FRHIGPUMask All() { return FRHIGPUMask((1 << GNumExplicitGPUsForRendering) - 1); }
static const FRHIGPUMask FilterGPUsBefore(uint32 GPUIndex) { return FRHIGPUMask(~((1u << GPUIndex) - 1)) & All(); }
struct FIterator
{
explicit FIterator(const uint32 InGPUMask) : GPUMask(InGPUMask), FirstGPUIndexInMask(0);
explicit FIterator(const FRHIGPUMask& InGPUMask) : FIterator(InGPUMask.GPUMask);
FIterator& operator++();
FIterator operator++(int);
private:
uint32 GPUMask;
unsigned long FirstGPUIndexInMask;
};
friend FRHIGPUMask::FIterator begin(const FRHIGPUMask& NodeMask);
friend FRHIGPUMask::FIterator end(const FRHIGPUMask& NodeMask);
};
// GPU掩码实用程序,用于获取有关AFR组和兄弟姐妹的信息。AFR组是一起在同一帧上工作的一组GPU。AFR兄弟是其他组中的GPU,在后续帧上执行相同的工作。例如,在不同帧上渲染相同视图的两个GPU是AFR同级。对于具有2个AFR组的4 GPU设置:每个AFR组有2个GPU。0b1010和0b0101是两个组, 每个GPU有一个同级GPU。0b1100和0b0011是兄弟姐妹。
struct AFRUtils
{
static inline uint32 GetNumGPUsPerGroup();
static inline uint32 GetGroupIndex(uint32 GPUIndex);
static inline uint32 GetIndexWithinGroup(uint32 GPUIndex);
static inline uint32 GetNextSiblingGPUIndex(uint32 GPUIndex);
static inline FRHIGPUMask GetNextSiblingGPUMask(FRHIGPUMask InGPUMask);
static inline uint32 GetPrevSiblingGPUIndex(uint32 GPUIndex);
static inline FRHIGPUMask GetPrevSiblingGPUMask(FRHIGPUMask InGPUMask);
static inline FRHIGPUMask GetGPUMaskForGroup(uint32 GPUIndex);
static inline FRHIGPUMask GetGPUMaskForGroup(FRHIGPUMask InGPUMask);
static inline FRHIGPUMask GetGPUMaskWithSiblings(uint32 GPUIndex);
static inline FRHIGPUMask GetGPUMaskWithSiblings(FRHIGPUMask InGPUMask);
#if WITH_MGPU
static TArray<FRHIGPUMask, TFixedAllocator<MAX_NUM_GPUS>> GroupMasks;
static TArray<FRHIGPUMask, TFixedAllocator<MAX_NUM_GPUS>> SiblingMasks;
#endif
};
以下类型或接口涉及了GPU厂商、驱动和特性:
// RHIDefinitions.h
enum class EGpuVendorId
{
Unknown = -1,
NotQueried = 0,
Amd = 0x1002,
ImgTec = 0x1010,
Nvidia = 0x10DE,
Arm = 0x13B5,
Broadcom = 0x14E4,
Qualcomm = 0x5143,
Intel = 0x8086,
Apple = 0x106B,
Vivante = 0x7a05,
VeriSilicon = 0x1EB1,
Kazan = 0x10003, // VkVendorId
Codeplay = 0x10004, // VkVendorId
Mesa = 0x10005, // VkVendorId
};
inline bool RHIHasTiledGPU(const FStaticShaderPlatform Platform);
inline EGpuVendorId RHIConvertToGpuVendorId(uint32 VendorId);
class FGenericDataDrivenShaderPlatformInfo
{
FName Language;
ERHIFeatureLevel::Type MaxFeatureLevel;
uint32 bIsMobile: 1;
uint32 bIsMetalMRT: 1;
uint32 bIsPC: 1;
uint32 bIsConsole: 1;
uint32 bIsAndroidOpenGLES: 1;
uint32 bSupportsDebugViewShaders : 1;
uint32 bSupportsMobileMultiView: 1;
uint32 bSupportsArrayTextureCompression : 1;
uint32 bSupportsDistanceFields: 1; // used for DFShadows and DFAO - since they had the same checks
uint32 bSupportsDiaphragmDOF: 1;
uint32 bSupportsRGBColorBuffer: 1;
uint32 bSupportsCapsuleShadows: 1;
uint32 bSupportsPercentageCloserShadows : 1;
uint32 bSupportsVolumetricFog: 1; // also used for FVVoxelization
uint32 bSupportsIndexBufferUAVs: 1;
uint32 bSupportsInstancedStereo: 1;
uint32 bSupportsMultiView: 1;
uint32 bSupportsMSAA: 1;
uint32 bSupports4ComponentUAVReadWrite: 1;
uint32 bSupportsRenderTargetWriteMask: 1;
uint32 bSupportsRayTracing: 1;
uint32 bSupportsRayTracingProceduralPrimitive : 1;
uint32 bSupportsRayTracingIndirectInstanceData : 1; // Whether instance transforms can be copied from the GPU to the TLAS instances buffer
uint32 bSupportsHighEndRayTracingReflections : 1; // Whether fully-featured RT reflections can be used on the platform (with multi-bounce, translucency, etc.)
uint32 bSupportsPathTracing : 1; // Whether real-time path tracer is supported on this platform (avoids compiling unnecessary shaders)
uint32 bSupportsGPUSkinCache: 1;
uint32 bSupportsGPUScene : 1;
uint32 bSupportsByteBufferComputeShaders : 1;
uint32 bSupportsPrimitiveShaders : 1;
uint32 bSupportsUInt64ImageAtomics : 1;
uint32 bRequiresVendorExtensionsForAtomics : 1;
uint32 bSupportsNanite : 1;
uint32 bSupportsLumenGI : 1;
uint32 bSupportsSSDIndirect : 1;
uint32 bSupportsTemporalHistoryUpscale : 1;
uint32 bSupportsRTIndexFromVS : 1;
uint32 bSupportsWaveOperations : 1; // Whether HLSL SM6 shader wave intrinsics are supported
uint32 bSupportsIntrinsicWaveOnce : 1;
uint32 bSupportsConservativeRasterization : 1;
uint32 bRequiresExplicit128bitRT : 1;
uint32 bSupportsGen5TemporalAA : 1;
uint32 bTargetsTiledGPU: 1;
uint32 bNeedsOfflineCompiler: 1;
uint32 bSupportsComputeFramework : 1;
uint32 bSupportsAnisotropicMaterials : 1;
uint32 bSupportsDualSourceBlending : 1;
uint32 bRequiresGeneratePrevTransformBuffer : 1;
uint32 bRequiresRenderTargetDuringRaster : 1;
uint32 bRequiresDisableForwardLocalLights : 1;
uint32 bCompileSignalProcessingPipeline : 1;
uint32 bSupportsMeshShadersTier0 : 1;
uint32 bSupportsMeshShadersTier1 : 1;
uint32 MaxMeshShaderThreadGroupSize : 10;
uint32 bSupportsPerPixelDBufferMask : 1;
uint32 bIsHlslcc : 1;
uint32 bSupportsDxc : 1; // Whether DirectXShaderCompiler (DXC) is supported
uint32 bSupportsVariableRateShading : 1;
uint32 NumberOfComputeThreads : 10;
uint32 bWaterUsesSimpleForwardShading : 1;
uint32 bNeedsToSwitchVerticalAxisOnMobileOpenGL : 1;
uint32 bSupportsHairStrandGeometry : 1;
uint32 bSupportsDOFHybridScattering : 1;
uint32 bNeedsExtraMobileFrames : 1;
uint32 bSupportsHZBOcclusion : 1;
uint32 bSupportsWaterIndirectDraw : 1;
uint32 bSupportsAsyncPipelineCompilation : 1;
uint32 bSupportsManualVertexFetch : 1;
uint32 bRequiresReverseCullingOnMobile : 1;
uint32 bOverrideFMaterial_NeedsGBufferEnabled : 1;
uint32 bSupportsMobileDistanceField : 1;
uint32 bSupportsFFTBloom : 1;
uint32 bSupportsInlineRayTracing : 1;
uint32 bSupportsRayTracingShaders : 1;
uint32 bSupportsVertexShaderLayer : 1;
uint32 bSupportsVolumeTextureAtomics : 1;
private:
static FGenericDataDrivenShaderPlatformInfo Infos[SP_NumPlatforms];
(...)
}
4 其它
以下代码提供了部分硬件的信息和操作:
// GenericPlatformMisc.h
struct FGenericPlatformMisc
{
// 设备/硬件
static FString GetDeviceId();
static FString GetUniqueAdvertisingId();
static void SubmitErrorReport( const TCHAR* InErrorHist, EErrorReportMode::Type InMode );
static bool IsRemoteSession();
static bool IsDebuggerPresent();
static EProcessDiagnosticFlags GetProcessDiagnostics();
static FString GetCPUVendor();
static uint32 GetCPUInfo();
static bool HasNonoptionalCPUFeatures();
static bool NeedsNonoptionalCPUFeaturesCheck();
static FString GetCPUBrand();
static FString GetCPUChipset();
static FString GetPrimaryGPUBrand();
static FString GetDeviceMakeAndModel();
static struct FGPUDriverInfo GetGPUDriverInfo(const FString& DeviceDescription);
static void PrefetchBlock(const void* InPtr, int32 NumBytes = 1);
static void Prefetch(void const* x, int32 offset = 0);
static const TCHAR* GetDefaultDeviceProfileName();
static int GetBatteryLevel();
static void SetBrightness(float bBright);
static float GetBrightness();
static bool SupportsBrightness();
static bool IsInLowPowerMode();
static float GetDeviceTemperatureLevel();
static inline int32 GetMaxRefreshRate();
static inline int32 GetMaxSyncInterval();
static bool IsPGOEnabled();
static TArray<uint8> GetSystemFontBytes();
static bool HasActiveWiFiConnection();
static ENetworkConnectionType GetNetworkConnectionType();
static bool HasVariableHardware();
static bool HasPlatformFeature(const TCHAR* FeatureName);
static bool IsRunningOnBattery();
static EDeviceScreenOrientation GetDeviceOrientation();
static void SetDeviceOrientation(EDeviceScreenOrientation NewDeviceOrientation);
static int32 GetDeviceVolume();
// 内存
static void MemoryBarrier();
static void SetMemoryWarningHandler(void (* Handler)(const FGenericMemoryWarningContext& Context));
static bool HasMemoryWarningHandler();
// I/O
static void InitTaggedStorage(uint32 NumTags);
static void ShutdownTaggedStorage();
static void TagBuffer(const char* Label, uint32 Category, const void* Buffer, size_t BufferSize);
static bool SetStoredValues(const FString& InStoreId, const FString& InSectionName, const TMap<FString, FString>& InKeyValues);
static bool SetStoredValue(const FString& InStoreId, const FString& InSectionName, const FString& InKeyName, const FString& InValue);
static bool GetStoredValue(const FString& InStoreId, const FString& InSectionName, const FString& InKeyName, FString& OutValue);
static bool DeleteStoredValue(const FString& InStoreId, const FString& InSectionName, const FString& InKeyName);
static bool DeleteStoredSection(const FString& InStoreId, const FString& InSectionName);
static TArray<FCustomChunk> GetOnDemandChunksForPakchunkIndices(const TArray<int32>& PakchunkIndices);
static TArray<FCustomChunk> GetAllOnDemandChunks();
static TArray<FCustomChunk> GetAllLanguageChunks();
static TArray<FCustomChunk> GetCustomChunksByType(ECustomChunkType DesiredChunkType);
static void ParseChunkIdPakchunkIndexMapping(TArray<FString> ChunkIndexRedirects, TMap<int32, int32>& OutMapping);
static int32 GetChunkIDFromPakchunkIndex(int32 PakchunkIndex);
static int32 GetPakchunkIndexFromPakFile(const FString& InFilename);
static FText GetFileManagerName();
static bool IsPackagedForDistribution();
static FString LoadTextFileFromPlatformPackage(const FString& RelativePath);
static bool FileExistsInPlatformPackage(const FString& RelativePath);
static bool Expand16BitIndicesTo32BitOnLoad();
static void GetNetworkFileCustomData(TMap<FString,FString>& OutCustomPlatformData);
static bool SupportsBackbufferSampling();
(...)
};
// GenericPlatformApplicationMisc.h
struct FGenericPlatformApplicationMisc
{
// 模块/上下文/设备
static void LoadPreInitModules();
static void LoadStartupModules();
static FOutputDeviceConsole* CreateConsoleOutputDevice();
static FOutputDeviceError* GetErrorOutputDevice();
static FFeedbackContext* GetFeedbackContext();
static bool IsThisApplicationForeground();
static void RequestMinimize();
static bool RequiresVirtualKeyboard();
static void PumpMessages(bool bFromMainLoop);
// 屏幕/窗口
static void PreventScreenSaver();
static bool IsScreensaverEnabled();
static bool ControlScreensaver(EScreenSaverAction Action);
static struct FLinearColor GetScreenPixelColor(const FVector2D& InScreenPos, float InGamma);
static bool GetWindowTitleMatchingText(const TCHAR* TitleStartsWith, FString& OutTitle);
static void SetHighDPIMode();
static float GetDPIScaleFactorAtPoint(float X, float Y);
static bool IsHighDPIAwarenessEnabled();
static bool AnchorWindowWindowPositionTopLeft();
static EScreenPhysicalAccuracy GetPhysicalScreenDensity(int32& OutScreenDensity);
static EScreenPhysicalAccuracy ComputePhysicalScreenDensity(int32& OutScreenDensity);
static EScreenPhysicalAccuracy ConvertInchesToPixels(T Inches, T2& OutPixels);
static EScreenPhysicalAccuracy ConvertPixelsToInches(T Pixels, T2& OutInches);
// 控制器
static void SetGamepadsAllowed(bool bAllowed);
static void SetGamepadsBlockDeviceFeedback(bool bAllowed);
static void ResetGamepadAssignments();
static void ResetGamepadAssignmentToController(int32 ControllerId);
static bool IsControllerAssignedToGamepad(int32 ControllerId);
static FString GetGamepadControllerName(int32 ControllerId);
static class UTexture2D* GetGamepadButtonGlyph(...);
static void EnableMotionData(bool bEnable);
static bool IsMotionDataEnabled();
(...)
};
// 硬件查询结果
struct FHardwareSurveyResults
{
static const int32 MaxDisplayCount = 8;
static const int32 MaxStringLength = 260;
TCHAR Platform[MaxStringLength];
TCHAR OSVersion[MaxStringLength];
TCHAR OSSubVersion[MaxStringLength];
uint32 OSBits;
TCHAR OSLanguage[MaxStringLength];
TCHAR RenderingAPI[MaxStringLength];
TCHAR MultimediaAPI_DEPRECATED[MaxStringLength];
uint32 HardDriveGB;
uint32 HardDriveFreeMB;
uint32 MemoryMB;
float CPUPerformanceIndex;
float GPUPerformanceIndex;
float RAMPerformanceIndex;
uint32 bIsLaptopComputer:1;
uint32 bIsRemoteSession:1;
uint32 CPUCount;
float CPUClockGHz;
TCHAR CPUBrand[MaxStringLength];
TCHAR CPUNameString[MaxStringLength];
uint32 CPUInfo;
uint32 DisplayCount;
FHardwareDisplay Displays[MaxDisplayCount];
FGPUAdpater RHIAdapter;
uint32 ErrorCount;
TCHAR LastSurveyError[MaxStringLength];
TCHAR LastSurveyErrorDetail[MaxStringLength];
TCHAR LastPerformanceIndexError[MaxStringLength];
TCHAR LastPerformanceIndexErrorDetail[MaxStringLength];
FSynthBenchmarkResults SynthBenchmark;
};
// 不同的平台实现获取FHardwareSurveyResults。
struct APPLICATIONCORE_API FGenericPlatformSurvey
{
static bool GetSurveyResults(FHardwareSurveyResults& OutResults, bool bWait);
};
// HardwareInfo.h
// 硬件信息
struct ENGINE_API FHardwareInfo
{
static void RegisterHardwareInfo( const FName SpecIdentifier, const FString& HardwareInfo );
static FString GetHardwareInfo(const FName SpecIdentifier);
static const FString GetHardwareDetailsString();
};
下面代码提供了性能检测功能:
// GenericPlatformSurvey.h
struct FSynthBenchmarkStat
{
// 计算线性性能指数(>0),在硬件良好的情况下约为100,但数字可能更高.
float ComputePerfIndex() const;
void SetMeasuredTime(const FTimeSample& TimeSample, float InConfidence = 90);
float GetNormalizedTime() const;
float GetMeasuredTotalTime() const;
float GetConfidence() const;
float GetWeight() const;
private:
// -1(如果未定义),以秒为单位,有助于查看测试是否运行时间过长(某些较慢的GPU可能超时).
float MeasuredTotalTime;
// -1(如果未定义),则取决于测试(例如s/g像素),WorkScale被划分.
float MeasuredNormalizedTime;
// -1(如果未定义),则为标准GPU上预期的定时值(索引值100,此处为NVidia 670).
float IndexNormalizedTime;
// 0..100,100:完全自信
float Confidence;
// 1为正常权重,0为无权重,>1为无边界附加权重.
float Weight;
};
下面的代码提供了磁盘的利用率追踪:
// DiskUtilizationTracker.h
struct FDiskUtilizationTracker
{
struct UtilizationStats
{
double GetOverallThroughputBS() const;
double GetOverallThroughputMBS() const;
double GetReadThrougputBS() const;
double GetReadThrougputMBS() const;
double GetTotalIdleTimeInSeconds() const;
double GetTotalIOTimeInSeconds() const;
double GetPercentTimeIdle() const;
uint64 TotalReads;
uint64 TotalSeeks;
uint64 TotalBytesRead;
uint64 TotalSeekDistance;
double TotalIOTime;
double TotalIdleTime;
};
UtilizationStats LongTermStats;
UtilizationStats ShortTermStats;
FCriticalSection CriticalSection;
uint64 IdleStartCycle;
uint64 ReadStartCycle;
uint64 InFlightBytes;
int32 InFlightReads;
FThreadSafeBool bResetShortTermStats;
void StartRead(uint64 InReadBytes, uint64 InSeekDistance = 0);
void FinishRead();
uint32 GetOutstandingRequests() const;
const struct UtilizationStats& GetLongTermStats() const;
const struct UtilizationStats& GetShortTermStats() const;
void ResetShortTermStats();
private:
static float GetThrottleRateMBS();
static constexpr float PrintFrequencySeconds = 0.5f;
};
下面提供了存储IO相关的信息和接口:
// IoStore.h
// I/O存储TOC标头。
struct FIoStoreTocHeader
{
static constexpr char TocMagicImg[] = "-==--==--==--==-";
uint8 TocMagic[16];
uint8 Version;
uint8 Reserved0 = 0;
uint16 Reserved1 = 0;
uint32 TocHeaderSize;
uint32 TocEntryCount;
uint32 TocCompressedBlockEntryCount;
uint32 TocCompressedBlockEntrySize; // For sanity checking
uint32 CompressionMethodNameCount;
uint32 CompressionMethodNameLength;
uint32 CompressionBlockSize;
uint32 DirectoryIndexSize;
uint32 PartitionCount = 0;
FIoContainerId ContainerId;
FGuid EncryptionKeyGuid;
EIoContainerFlags ContainerFlags;
uint8 Reserved3 = 0;
uint16 Reserved4 = 0;
uint32 TocChunkPerfectHashSeedsCount = 0;
uint64 PartitionSize = 0;
uint32 TocChunksWithoutPerfectHashCount = 0;
uint32 Reserved7 = 0;
uint64 Reserved8[5] = { 0 };
};
// 组合偏移量和长度。
struct FIoOffsetAndLength
{
public:
inline uint64 GetOffset() const;
inline uint64 GetLength() const;
inline void SetOffset(uint64 Offset);
inline void SetLength(uint64 Length);
private:
uint8 OffsetAndLength[5 + 5];
};
// TOC条目元数据
struct FIoStoreTocEntryMeta
{
FIoChunkHash ChunkHash;
FIoStoreTocEntryMetaFlags Flags;
};
// 压缩块条目
struct FIoStoreTocCompressedBlockEntry
{
static constexpr uint32 OffsetBits = 40;
static constexpr uint64 OffsetMask = (1ull << OffsetBits) - 1ull;
static constexpr uint32 SizeBits = 24;
static constexpr uint32 SizeMask = (1 << SizeBits) - 1;
static constexpr uint32 SizeShift = 8;
inline uint64 GetOffset() const;
inline void SetOffset(uint64 InOffset);
inline uint32 GetCompressedSize() const;
inline void SetCompressedSize(uint32 InSize);
inline uint32 GetUncompressedSize() const;
inline void SetUncompressedSize(uint32 InSize);
inline uint8 GetCompressionMethodIndex() const;
inline void SetCompressionMethodIndex(uint8 InIndex);
private:
uint8 Data[5 + 3 + 3 + 1];
};
// TOC资源读取操作
enum class EIoStoreTocReadOptions
{
Default,
ReadDirectoryIndex = (1 << 0),
ReadTocMeta = (1 << 1),
ReadAll = ReadDirectoryIndex | ReadTocMeta
};
ENUM_CLASS_FLAGS(EIoStoreTocReadOptions);
// TOC数据容器
struct FIoStoreTocResource
{
enum { CompressionMethodNameLen = 32 };
FIoStoreTocHeader Header;
TArray<FIoChunkId> ChunkIds;
TArray<FIoOffsetAndLength> ChunkOffsetLengths;
TArray<int32> ChunkPerfectHashSeeds;
TArray<int32> ChunkIndicesWithoutPerfectHash;
TArray<FIoStoreTocCompressedBlockEntry> CompressionBlocks;
TArray<FName> CompressionMethods;
FSHAHash SignatureHash;
TArray<FSHAHash> ChunkBlockSignatures;
TArray<FIoStoreTocEntryMeta> ChunkMetas;
TArray<uint8> DirectoryIndexBuffer;
static FIoStatus Read(const TCHAR* TocFilePath, EIoStoreTocReadOptions ReadOptions, FIoStoreTocResource& OutTocResource);
static TIoStatusOr<uint64> Write(const TCHAR* TocFilePath, FIoStoreTocResource& TocResource, ...);
static uint64 HashChunkIdWithSeed(int32 Seed, const FIoChunkId& ChunkId);
};
// 以下是IO的目录、文件、索引相关
// IoDirectoryIndex.h
struct FIoDirectoryIndexEntry
{
uint32 Name = ~uint32(0);
uint32 FirstChildEntry = ~uint32(0);
uint32 NextSiblingEntry = ~uint32(0);
uint32 FirstFileEntry = ~uint32(0);
};
struct FIoFileIndexEntry
{
uint32 Name = ~uint32(0);
uint32 NextFileEntry = ~uint32(0);
uint32 UserData = 0;
};
struct FIoDirectoryIndexResource
{
FString MountPoint;
TArray<FIoDirectoryIndexEntry> DirectoryEntries;
TArray<FIoFileIndexEntry> FileEntries;
TArray<FString> StringTable;
};
class FIoDirectoryIndexWriter
{
public:
void SetMountPoint(FString InMountPoint);
uint32 AddFile(const FString& InFileName);
void SetFileUserData(uint32 InFileEntryIndex, uint32 InUserData);
void Flush(TArray<uint8>& OutBuffer, FAES::FAESKey InEncryptionKey);
private:
uint32 GetDirectory(uint32 DirectoryName, uint32 Parent);
uint32 CreateDirectory(const FStringView& DirectoryName, uint32 Parent);
uint32 GetNameIndex(const FStringView& String);
uint32 AddFile(const FStringView& FileName, uint32 Directory);
static bool IsValid(uint32 Index);
FString MountPoint;
TArray<FIoDirectoryIndexEntry> DirectoryEntries;
TArray<FIoFileIndexEntry> FileEntries;
TMap<FString, uint32> StringToIndex;
TArray<FString> Strings;
};
// IoDispatcherPrivate.h
class FIoBatchImpl
{
public:
TFunction<void()> Callback;
FEvent* Event = nullptr;
FGraphEventRef GraphEvent;
TAtomic<uint32> UnfinishedRequestsCount;
};
下面代码提供了平台无关的亲缘性操作:
// GenericPlatformAffinity.h
class FGenericPlatformAffinity
{
public:
static const uint64 GetMainGameMask();
static const uint64 GetRenderingThreadMask();
static const uint64 GetRHIThreadMask();
static const uint64 GetRHIFrameOffsetThreadMask();
static const uint64 GetRTHeartBeatMask();
static const uint64 GetPoolThreadMask();
static const uint64 GetTaskGraphThreadMask();
static const uint64 GetAudioThreadMask();
static const uint64 GetNoAffinityMask();
static const uint64 GetTaskGraphBackgroundTaskMask();
static const uint64 GetTaskGraphHighPriorityTaskMask();
static const uint64 GetAsyncLoadingThreadMask();
static const uint64 GetIoDispatcherThreadMask();
static const uint64 GetTraceThreadMask();
static EThreadPriority GetRenderingThreadPriority();
static EThreadCreateFlags GetRenderingThreadFlags();
static EThreadPriority GetRHIThreadPriority();
static EThreadPriority GetGameThreadPriority();
static EThreadCreateFlags GetRHIThreadFlags();
static EThreadPriority GetTaskThreadPriority();
static EThreadPriority GetTaskBPThreadPriority();
};
下面定义了许多硬件、ISA、操作系统、编译器、图形API及它们的特性相关的宏:
// Platform.h
PLATFORM_WINDOWS
PLATFORM_XBOXONE
PLATFORM_MAC
PLATFORM_MAC_X86
PLATFORM_MAC_ARM64
PLATFORM_PS4
PLATFORM_IOS
PLATFORM_TVOS
PLATFORM_ANDROID
PLATFORM_ANDROID_ARM
PLATFORM_ANDROID_ARM64
PLATFORM_ANDROID_X86
PLATFORM_ANDROID_X64
PLATFORM_APPLE
PLATFORM_LINUX
PLATFORM_LINUXARM64
PLATFORM_SWITCH
PLATFORM_FREEBSD
PLATFORM_UNIX
PLATFORM_MICROSOFT
PLATFORM_HOLOLENS
PLATFORM_CPU_X86_FAMILY
PLATFORM_CPU_ARM_FAMILY
PLATFORM_COMPILER_CLANG
PLATFORM_DESKTOP
PLATFORM_64BITS
PLATFORM_LITTLE_ENDIAN
PLATFORM_SUPPORTS_UNALIGNED_LOADS
PLATFORM_EXCEPTIONS_DISABLED
PLATFORM_SUPPORTS_PRAGMA_PACK
PLATFORM_ENABLE_VECTORINTRINSICS
PLATFORM_MAYBE_HAS_SSE4_1
PLATFORM_MAYBE_HAS_AVX
PLATFORM_ALWAYS_HAS_AVX_2
PLATFORM_ALWAYS_HAS_FMA3
PLATFORM_HAS_CPUID
PLATFORM_ENABLE_POPCNT_INTRINSIC
PLATFORM_ENABLE_VECTORINTRINSICS_NEON
PLATFORM_USE_LS_SPEC_FOR_WIDECHAR
PLATFORM_USE_SYSTEM_VSWPRINTF
PLATFORM_COMPILER_DISTINGUISHES_INT_AND_LONG
PLATFORM_COMPILER_HAS_GENERIC_KEYWORD
PLATFORM_COMPILER_HAS_DEFAULTED_FUNCTIONS
PLATFORM_COMPILER_COMMON_LANGUAGE_RUNTIME_COMPILATION
PLATFORM_COMPILER_HAS_TCHAR_WMAIN
PLATFORM_COMPILER_HAS_DECLTYPE_AUTO
PLATFORM_COMPILER_HAS_IF_CONSTEXPR
PLATFORM_COMPILER_HAS_FOLD_EXPRESSIONS
PLATFORM_TCHAR_IS_4_BYTES
PLATFORM_WCHAR_IS_4_BYTES
PLATFORM_TCHAR_IS_CHAR16
PLATFORM_UCS2CHAR_IS_UTF16CHAR
PLATFORM_HAS_BSD_TIME
PLATFORM_HAS_BSD_THREAD_CPUTIME
PLATFORM_HAS_BSD_SOCKETS
PLATFORM_HAS_BSD_IPV6_SOCKETS
PLATFORM_HAS_BSD_SOCKET_FEATURE_IOCTL
PLATFORM_HAS_BSD_SOCKET_FEATURE_SELECT
PLATFORM_HAS_BSD_SOCKET_FEATURE_GETHOSTNAME
PLATFORM_SUPPORTS_UDP_MULTICAST_GROUP
PLATFORM_USE_PTHREADS
PLATFORM_MAX_FILEPATH_LENGTH_DEPRECATED
PLATFORM_SUPPORTS_TEXTURE_STREAMING
PLATFORM_SUPPORTS_VIRTUAL_TEXTURES
PLATFORM_SUPPORTS_VARIABLE_RATE_SHADING
PLATFORM_REQUIRES_FILESERVER
PLATFORM_SUPPORTS_MULTITHREADED_GC
PLATFORM_SUPPORTS_TBB
PLATFORM_USES_FIXED_RHI_CLASS
PLATFORM_HAS_TOUCH_MAIN_SCREEN
PLATFORM_SUPPORTS_STACK_SYMBOLS
PLATFORM_HAS_128BIT_ATOMICS
PLATFORM_USE_FULL_TASK_GRAPH
PLATFORM_HAS_FPlatformVirtualMemoryBlock
PLATFORM_USE_FULL_TASK_GRAPH
PLATFORM_IS_ANSI_MALLOC_THREADSAFE
PLATFORM_SUPPORTS_GPU_FRAMETIME_WITHOUT_MGPU
(...)
以下提供了平台属性、输出设备、堆栈遍历等相关的操作:
// 输出设备在大多数平台的通用实现
struct FGenericPlatformOutputDevices
{
static void SetupOutputDevices();
static FString GetAbsoluteLogFilename();
static FOutputDevice* GetLog();
static void GetPerChannelFileOverrides(TArray<FOutputDevice*>& OutputDevices);
static FOutputDevice* GetEventLog();
static FOutputDeviceError* GetError();
static FFeedbackContext* GetFeedbackContext();
protected:
static void ResetCachedAbsoluteFilename();
private:
static constexpr SIZE_T AbsoluteFileNameMaxLength = 1024;
static TCHAR CachedAbsoluteFilename[AbsoluteFileNameMaxLength];
static void OnLogFileOpened(const TCHAR* Pathname);
static FCriticalSection LogFilenameLock;
};
// 平台属性
struct FGenericPlatformProperties
{
static const char* GetPhysicsFormat();
static bool HasEditorOnlyData();
static const char* IniPlatformName();
static bool IsGameOnly();
static bool IsServerOnly();
static bool IsClientOnly();
static bool IsMonolithicBuild();
static bool IsProgram();
static bool IsLittleEndian();
static const char* PlatformName();
static bool RequiresCookedData();
static bool HasSecurePackageFormat();
static bool RequiresUserCredentials();
static bool SupportsBuildTarget( EBuildTargetType TargetType );
static bool SupportsAutoSDK();
static bool SupportsGrayscaleSRGB();
static bool SupportsMultipleGameInstances();
static bool SupportsWindowedMode();
static bool AllowsFramerateSmoothing();
static bool SupportsAudioStreaming();
static bool SupportsHighQualityLightmaps();
static bool SupportsLowQualityLightmaps();
static bool SupportsDistanceFieldShadows();
static bool SupportsDistanceFieldAO();
static bool SupportsTextureStreaming();
static bool SupportsMeshLODStreaming();
static bool SupportsMemoryMappedFiles();
static bool SupportsMemoryMappedAudio();
static bool SupportsMemoryMappedAnimation();
static int64 GetMemoryMappingAlignment();
static bool SupportsVirtualTextureStreaming();
static bool SupportsLumenGI();
static bool SupportsHardwareLZDecompression();
static bool HasFixedResolution();
static bool SupportsMinimize();
static bool SupportsQuit();
static bool AllowsCallStackDumpDuringAssert();
static const char* GetZlibReplacementFormat();
};
// 用于捕获加载pdb所需的所有模块信息。
struct FStackWalkModuleInfo
{
uint64 BaseOfImage;
uint32 ImageSize;
uint32 TimeDateStamp;
TCHAR ModuleName[32];
TCHAR ImageName[256];
TCHAR LoadedImageName[256];
uint32 PdbSig;
uint32 PdbAge;
struct
{
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} PdbSig70;
};
// 与程序计数器相关的符号信息。ANSI版本。
struct FProgramCounterSymbolInfo final
{
enum
{
/** Length of the string used to store the symbol's names, including the trailing character. */
MAX_NAME_LENGTH = 1024,
};
ANSICHAR ModuleName[MAX_NAME_LENGTH];
ANSICHAR FunctionName[MAX_NAME_LENGTH];
ANSICHAR Filename[MAX_NAME_LENGTH];
int32 LineNumber;
int32 SymbolDisplacement;
uint64 OffsetInModule;
uint64 ProgramCounter;
};
// 程序计数器符号信息
struct FProgramCounterSymbolInfoEx
{
FString ModuleName;
FString FunctionName;
FString Filename;
uint32 LineNumber;
uint64 SymbolDisplacement;
uint64 OffsetInModule;
uint64 ProgramCounter;
};
// 堆栈遍历
struct FGenericPlatformStackWalk
{
typedef FGenericPlatformStackWalk Base;
struct EStackWalkFlags
{
enum
{
AccurateStackWalk = 0,
FastStackWalk = (1 << 0),
FlagsUsedWhenHandlingEnsure = (FastStackWalk)
};
};
static void Init();
static bool InitStackWalking()
static bool InitStackWalkingForProcess(const FProcHandle& Process);
static bool ProgramCounterToHumanReadableString( int32 CurrentCallDepth, uint64 ProgramCounter, ...);
static bool SymbolInfoToHumanReadableString( const FProgramCounterSymbolInfo& SymbolInfo, ... );
static bool SymbolInfoToHumanReadableStringEx( const FProgramCounterSymbolInfoEx& SymbolInfo, FString& out_HumanReadableString );
static void ProgramCounterToSymbolInfo( uint64 ProgramCounter, FProgramCounterSymbolInfo& out_SymbolInfo);
static void ProgramCounterToSymbolInfoEx( uint64 ProgramCounter, FProgramCounterSymbolInfoEx& out_SymbolInfo);
static uint32 CaptureStackBackTrace( uint64* BackTrace, uint32 MaxDepth, void* Context = nullptr );
static uint32 CaptureThreadStackBackTrace(uint64 ThreadId, uint64* BackTrace, uint32 MaxDepth, void* Context = nullptr);
static void StackWalkAndDump(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, ... );
static void StackWalkAndDump(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, ...);
static TArray<FProgramCounterSymbolInfo> GetStack(int32 IgnoreCount, int32 MaxDepth = 100, ...);
static void ThreadStackWalkAndDump(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, ...);
static void StackWalkAndDumpEx(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, ...);
static void StackWalkAndDumpEx(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, ...);
static int32 GetProcessModuleCount();
static int32 GetProcessModuleSignatures(FStackWalkModuleInfo *ModuleSignatures, const int32 ModuleSignaturesSize);
static TMap<FName, FString> GetSymbolMetaData();
protected:
static bool WantsDetailedCallstacksInNonMonolithicBuilds();
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2021-12-13 Tensorrt一些优化技术介绍