☰
TEngine 图形监控面板实现详解:设备能力查询、Profiler 内存采样与对象池/纹理资源监控
2026/10/14 2:08:26 网站建设 项目流程
  • 游戏开发

【免费下载链接】TEngine

Unity 商用级别开发框架,原生内置 AI 工作流支持,集成 HybridCLR 高性能热更、Obfuz 代码混淆加固、YooAssets 企业级资源管理方案,构建高效、安全、可扩展的工业化开发底座。

项目地址:https://gitcode.com/gh_mirrors/teng/TEngine
点击查看免费下载

本文基于 TEngine 框架的DebugerModule模块,围绕其以即时模式 GUI(IMGUI)实现的运行时图形监控面板展开:说明主控Debugger如何注册并管理各类调试窗口、图形信息面板如何借助SystemInfo/Graphics静态属性查询设备能力并做版本兼容、如何基于ProfilerAPI 采样运行时内存,以及对象池/内存池统计与材质纹理压缩设置如何配合图形性能诊断。读完本文,你可以独立定位 TEngine 中与图形监控相关的调试入口,理解各监控指标的数据来源,并在实际项目中完成纹理压缩与材质 Shader 修复的配套处理。

图形监控面板的总体架构:主控 + 面板窗口

TEngine 的调试器采用“主控-面板”分层架构。主控Debugger是一个MonoBehaviour,负责注册所有调试面板、维护窗口树状态、绘制 IMGUI 界面,并把窗口布局持久化到PlayerPrefs;每个具体面板则是实现IDebuggerWindow接口的独立类。

从接口定义看,一个面板窗口的完整生命周期契约包括Initialize、Shutdown、OnEnter、OnLeave、OnUpdate(区分逻辑流逝时间elapseSeconds与真实流逝时间realElapseSeconds)和OnDraw,详见 IDebuggerWindow。

所有信息型面板都继承自抽象基类ScrollableDebuggerWindowBase,它提供了滚动视图包装和两个关键工具方法:

  • DrawItem(title, content):绘制“标签 + 内容”一行,点击内容可复制到剪贴板(复用主控的CopyToClipboard实现);
  • GetByteLengthString(byteLength):把字节数格式化为 Bytes/KB/MB/GB/TB/PB/EB 分级可读字符串,内存类面板的展示统一使用它。

实现见 DebuggerModule.ScrollableDebuggerWindowBase.cs。

主控在Start方法中按树状路径注册全部面板,其中与图形监控直接相关的路径包括:

  • Information/Graphics:图形信息面板;
  • Profiler/Summary:性能分析器面板;
  • Profiler/Memory/Summary与Profiler/Memory/{All|Texture|Mesh|Material|Shader|AnimationClip|AudioClip|Font|TextAsset|ScriptableObject}:运行时内存采样面板(每种类型各注册一个泛型窗口实例);
  • Profiler/Object Pool:对象池面板;
  • Profiler/Reference Pool:内存池面板。

注册实现见 Debugger.cs。面板的窗口树由模块侧的DebuggerModule维护:它实现IUpdateModule,仅在ActiveWindow为真时轮询窗口根节点(见 DebuggerModule.cs)。

调试器的启用与激活控制

主控中定义了三组布局常量:DefaultIconRect(10, 10, 60, 60)(漂浮框)、DefaultWindowRect(10, 10, 640, 480)(完整窗口)、DefaultWindowScale = 1.5(缩放)。Initialize从PlayerPrefs的Debugger.Icon.X、Debugger.Window.Scale等键恢复上次布局,OnDestroy时调用PlayerPrefs.Save()持久化窗口状态(见 Debugger.cs)。

面板是否打开由DebuggerActiveWindowType枚举控制(见 DebuggerActiveWindowType.cs):

枚举值含义激活条件
AlwaysOpen总是打开始终激活
OnlyOpenWhenDevelopment仅在开发模式打开Debug.isDebugBuild == true
OnlyOpenInEditor仅在编辑器中打开Application.isEditor == true
AlwaysClose总是关闭不激活

主控Start中按 Inspector 配置的activeWindow分支设置ActiveWindow(见 Debugger.cs)。ActiveWindow实际转发到IDebuggerModule.ActiveWindow,同时切换组件的enabled,因此调试器可被整体关闭以省掉OnGUI开销。UI 交互上:点击漂浮的 FPS 图标会打开完整窗口;打开完整窗口时ShowFullWindow置位并反向切换UIRoot/EventSystem的激活状态,避免调试窗口遮挡期间游戏 UI 仍可被操作。

图形信息面板:设备能力查询与版本兼容处理

图形信息面板GraphicsInformationWindow是核对“设备图形能力”的入口。它在OnDrawScrollableWindow中读取SystemInfo与Graphics的静态属性并逐项DrawItem展示,可归纳为四类:

设备与驱动信息:Device ID、Device Name、Device Vendor、Device Type、Device Version、Memory Size(显存容量,格式化为 MB)、Multi Threaded;在UNITY_2019_3_OR_NEWER下追加Rendering Threading Mode,在UNITY_2020_1_OR_NEWER下追加HRD Display Support Flags。

着色器与渲染特性:Shader Level(内部通过GetShaderLevelString把整型换算成 “Shader Model x.y” 形式)、Global Maximum LOD、Global Render Pipeline、Min OpenGLES Version(2020.2+)、Active Tier、Active Color Gamut、Preserve Frame Buffer Alpha。

纹理与渲染目标:NPOT Support、Max Texture Size、Supported Render Target Count、Supported Random Write Target Count、Copy Texture Support、Uses Reversed ZBuffer、Max Cubemap Size、Graphics UV Starts At Top,以及 2020.2+ 的Constant Buffer Offset Alignment(2019.1~2020.1 则显示对应的Min Constant Buffer Offset Alignment)。

计算着色器与高级特性:Supports Compute Shader、Supports Instancing、2D 数组纹理、运动矢量、立方体贴图数组纹理、3D 渲染纹理、纹理镜像重复一次、图形围栏/GPU 围栏(新版本用supportsGraphicsFence,旧版本回退显示supportsGPUFence)、异步计算、多重采样纹理、异步 GPU 回读、32 位索引缓冲、硬件四边形拓扑、Mip Streaming、多重采样自动解析、分离式渲染目标混合、设置常量缓冲区、几何着色器、光线追踪、细分着色器、保守光栅、GPU 录制器、多重视图、从顶点着色器输出渲染目标数组索引等。

实现上,整个文件大量使用#if UNITY_x_x_OR_NEWER条件编译——例如Rendering Threading Mode需 2019.3+、Supports Ray Tracing需 2019.3+、Supports Multiview需 2020.2+(见 DebuggerModule.GraphicsInformationWindow.cs)。这样同一份面板可以在不同 Unity 版本上编译运行,并按版本呈现不同的能力项集合。对跨端图形问题(例如 Android 与 PC 上某特性表现不一致),该面板提供了逐项对照的基准。

性能分析器面板:Profiler 状态与内存指标

ProfilerInformationWindow回答两类问题:“当前构建环境能否使用 Profiler”与“当前运行时内存构成如何”。

状态字段(见 DebuggerModule.ProfilerInformationWindow.cs):

  • Supported:Profiler.supported。这是排查“内存采样无数据”时的关键字段——部分平台/构建模式下运行时 Profiler 不可用;
  • Enabled:Profiler.enabled;
  • Enable Binary Log:Profiler.enableBinaryLog为真时展示日志文件路径;
  • Enable Allocation Callstacks:Profiler.enableAllocationCallstacks(2019.3+)。

内存指标按版本分成两组条件编译,Unity 5.6+ 使用 long 精度接口,避免大内存量下 32 位int的精度损失:

指标API(5.6+ / 旧版本)含义
Mono Used SizeProfiler.GetMonoUsedSizeLong()/GetMonoUsedSize()Mono 运行时当前使用量
Mono Heap SizeProfiler.GetMonoHeapSizeLong()/GetMonoHeapSize()Mono 堆当前大小
Used Heap SizeProfiler.usedHeapSizeLong/usedHeapSizeMono 堆内已用内存
Total Allocated MemoryProfiler.GetTotalAllocatedMemoryLong()运行时总分配内存
Total Reserved MemoryProfiler.GetTotalReservedMemoryLong()总保留内存
Total Unused Reserved MemoryProfiler.GetTotalUnusedReservedMemoryLong()保留但未使用的内存(反映保留碎片程度)

此外还展示Allocated Memory For Graphics Driver(2018.1+,图形驱动分配的内存,是诊断显存问题的关键项)、Temp Allocator Size(5.5+,临时分配器)以及Marshal Cached HGlobal Size,后者来自 TEngine 自有的Utility.Marshal(见 Utility.Marshal.cs),记录用于原生互操作的缓存 HGlobal 缓冲区字节数。long/int 接口的切换见 DebuggerModule.ProfilerInformationWindow.cs。

FPS 计数器:固定间隔的帧率统计

漂浮图标直接显示当前 FPS,并按日志级别(Fatal/Error/Warning/Normal)区分颜色。帧率由FpsCounter类计算:

  • 构造参数updateInterval必须大于 0,否则记录Log.Error;主控实例化为new FpsCounter(0.5f)(见 Debugger.cs);
  • 主控Update传入Time.deltaTime与Time.unscaledDeltaTime,统计基于未缩放的真实时间累加,因此Time.timeScale变速时统计不会被扭曲;
  • 每帧帧数 +1、_accumulator累加真实时间、_timeLeft递减;当_timeLeft <= 0时按FPS = 帧数 / 累计耗时计算并重置计数。

实现见 DebuggerComponent.FpsCounter.cs。0.5 秒的更新间隔平滑了单帧抖动,使展示值成为该间隔内的平均帧率而非单帧瞬时值。

运行时内存采样面板:对象级采样与类型级聚合

运行时内存监控分两层:对象级采样(泛型RuntimeMemoryInformationWindow<T>,约束where T : UnityEngine.Object)与类型级聚合(RuntimeMemorySummaryWindow)。主控为前者注册了 10 个泛型实例:Object、Texture、Mesh、Material、Shader、AnimationClip、AudioClip、Font、TextAsset、ScriptableObject(见 Debugger.cs)。

对象级采样流程(见 DebuggerModule.RuntimeMemoryInformationWindow.cs):

  1. 点击Take Sample for {TypeName}按钮触发TakeSample;
  2. Resources.FindObjectsOfTypeAll<T>()枚举指定类型全部实例;
  3. 对每个对象用Profiler.GetRuntimeMemorySizeLong查询运行时内存大小(5.6 之前回退到GetRuntimeMemorySize);
  4. 按大小降序排序,大小相同时依次以类型名、名称作为次序键(SampleComparer);
  5. 重复检测:排序后相邻记录若名称、类型、大小完全一致,则标记为疑似重复对象(黄色高亮),并累计其数量与总大小,在顶部摘要栏展示。

为避免大数据集拖垮 IMGUI 绘制,面板设置了SHOW_SAMPLE_COUNT = 300,采样后最多展示前 300 条记录。采样记录结构(Name/Type/Size/Highlight)定义在 DebuggerModule.RuntimeMemoryInformationWindow.Sample.cs。

类型级聚合流程:RuntimeMemorySummaryWindow.TakeSample枚举Resources.FindObjectsOfTypeAll<UnityEngine.Object>(),把每个对象的运行时大小按GetType().Name聚合进记录表,输出 Type/Count/Size 三列、按大小降序排列的统计(见 DebuggerModule.RuntimeMemorySummaryWindow.cs)。从源码结构看,聚合采用对记录列表的线性查找匹配,复杂度约为 O(n·m),开发阶段诊断足够,但不适合高频调用。

实战路径上,可先查看Profiler/Memory/Summary找出内存占主导的类型,再进入Profiler/Memory/Texture等对应子面板下钻到具体资源实例;重复高亮特性直接指向“同名同大小”被重复加载的资源,这是显存重复占用的常见来源。

对象池与内存池面板:资源生命周期监控

对象池面板(注册路径Profiler/Object Pool):Initialize中通过ModuleSystem.GetModule<IObjectPoolModule>()获取模块,取不到时记录Log.Fatal("Object pool component is invalid.")。展示内容(见 DebuggerModule.ObjectPoolInformationWindow.cs):

  • 全局:Object Pool Count;
  • 每个池:Name、Type、Auto Release Interval、Capacity、Used Count、Can Release Count、Expire Time、Priority;
  • 每个对象明细行:名称、Locked、使用标记(池允许多次派发时显示SpawnCount,否则显示IsInUse)、CustomCanReleaseFlag、Priority、Last Use Time。

内存池面板(注册路径Profiler/Reference Pool,实际类名MemoryPoolPoolInformationWindow):头部展示Enable Strict Check(MemoryPool.EnableStrictCheck)与Memory Pool Count(MemoryPool.Count),并提供Show Full Class Name开关。内部将MemoryPool.GetAllMemoryPoolInfos()的结果按程序集名分组,逐行展示每个类型的Unused/Using/Acquire/Release/Add/Remove六个计数(见 DebuggerModule.MemoryPoolInformationWindow.cs)。Acquire/Release计数可用于核对是否存在 acquire 与 release 不配对的泄漏。

材质 Shader 修复与纹理压缩策略

监控面板之外,该主题还包含与之配套的“资源侧”处理策略。

材质 Shader 修复。热更或资源重载场景中,材质 Shader 可能失效或与当前渲染管线不一致。TEngine 在UNITY_EDITOR && EditorFixedMaterialShader条件编译宏下提供一组Utility.MaterialHelper工具:

  • FixedMaterialShader_GameObject:对Transform下每个Renderer,按 Shader 名称重新Shader.Find并回写到sharedMaterials;
  • FixedMaterialShader_UI:对 uGUIGraphic组件的材质做同样修复;
  • FixedMaterialShader_Tmp:在TextMeshPro宏下修复TextMeshProUGUI.fontSharedMaterial;
  • FixedMaterialShader_Scenne:处理场景根对象,并额外修复RenderSettings.skybox天空盒材质;
  • WaitGetRootGameObjects:用UniTask.WaitUntil等待GetRootGameObjects().Length > 0后再执行修复,解决“场景物体尚未就绪”的时序问题。

这些能力被接入场景加载流程:SceneModule在主场景加载(协程版与回调版两处)中,紧随YooAssets.LoadSceneAsync之后插入#if UNITY_EDITOR&&EditorFixedMaterialShader Utility.MaterialHelper.WaitGetRootGameObjects(_currentMainScene).Forget(); #endif(见 SceneModule.cs 与 Utility.MaterialHelper.cs),保证场景根对象的 Shader 修复只在实际加载完成后执行,且仅在编辑器且启用EditorFixedMaterialShader宏时生效。

纹理压缩与平台适配。纹理 meta 文件是显存与包体尺寸的“调节器”。以 Slider11_Fill_Red.png.meta 为例,其platformSettings段按DefaultTexturePlatform、Standalone、Server、Android、WebGL、iPhone各平台单独配置:maxTextureSize: 2048、textureCompression: 1(启用压缩)、compressionQuality: 50、crunchedCompression: 0、allowsAlphaSplitting: 0等。优化策略是:先用Profiler/Memory/Texture面板核对运行时显存与各资源尺寸,再反向调整各平台压缩格式与质量参数,在体积与画质之间取得平衡;对非关键图片可降低compressionQuality或调小maxTextureSize。

性能考量与故障排查

性能考量:

  • FPS 统计采用固定间隔平均值,建议间隔 0.5~1 秒(框架默认 0.5 秒);间隔过短数字抖动明显,过长则响应滞后;
  • 全量对象采样开销来自FindObjectsOfTypeAll与逐对象GetRuntimeMemorySizeLong调用,建议仅在开发阶段使用,发布版本谨慎开启;
  • IMGUI 渲染压力:大对象列表必须配合展示数量上限(框架内置 300 条/页)或筛选,避免 UI 卡顿;
  • 平台与版本差异:Profiler API 的 long/int 版本、supportsGraphicsFence与supportsGPUFence等能力字段在不同 Unity 版本间不同,代码库已用条件编译统一兼容。

故障排查:

现象排查点
调试器不显示检查activeWindow配置与构建环境(开发构建/编辑器);确认UIRoot/EventSystem存在且可激活(见 Debugger.cs)
FPS 异常检查UpdateInterval是否合理,确认 FPS 计数器累加的是Time.unscaledDeltaTime(见 DebuggerComponent.FpsCounter.cs)
内存采样无数据查看面板中Profiler.supported与Profiler.enabled的状态(见 DebuggerModule.ProfilerInformationWindow.cs);检查目标类型是否存在实例
材质/Shader 不匹配启用EditorFixedMaterialShader宏后运行材质修复工具,并等待场景根对象加载完成再执行(见 Utility.MaterialHelper.cs)

小结

TEngine 的图形监控面板通过“主控-面板”架构,把图形设备能力查询、Profiler 状态与内存指标、对象级/类型级运行时内存采样、对象池/内存池使用统计统一到一套可持久化布局的 IMGUI 调试窗口中,数据源覆盖SystemInfo、Graphics、Profiler、Resources与框架模块系统,并以条件编译处理 Unity 版本差异。配合Utility.MaterialHelper的 Shader 修复流程与纹理 meta 的按平台压缩配置,可以形成“监控 → 定位 → 调整”的图形性能优化闭环。实际使用建议:开发阶段充分迭代Information/Graphics、Profiler/*与Profiler/Memory/*各面板;发布构建中保留 FPS、对象池等低开销监控,谨慎启用全量内存采样等高开销项。

  • 游戏开发

【免费下载链接】TEngine

Unity 商用级别开发框架,原生内置 AI 工作流支持,集成 HybridCLR 高性能热更、Obfuz 代码混淆加固、YooAssets 企业级资源管理方案,构建高效、安全、可扩展的工业化开发底座。

项目地址:https://gitcode.com/gh_mirrors/teng/TEngine
点击查看免费下载

相关推荐

上一篇:如何彻底清理Glide缓存:Android应用数据删除完全指南
下一篇:零代码实现VPC流量监控:OpenTofu镜像实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询