关于 scenario-godot-performance-export
面向 Godot 4.7.2 项目的性能分析与发布技能,供开发者测量瓶颈、比较优化前后结果,并检查导出构建。它覆盖帧耗时分类、卡顿排查、多平台导出验证和发布审计。
主要功能
- 01采集 CPU、GPU 与绘制数据
- 02分类性能瓶颈
- 03应用优化并比较结果
- 04分析加载与着色器卡顿
- 05验证多平台导出文件
- 06检查发布配置与构建
适用场景
- 01定位游戏帧率受限于 CPU 还是 GPU
- 02比较场景优化前后的性能
- 03排查首次加载或运行时卡顿
- 04发布前验证导出构建能否启动
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-godot-performance-export安装后在 Claude Code 中输入/scenario-godot-performance-export确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/gd_perf.pyscripts/agentkit/perf/mobile_touch.gdperf_audit.gdperf_bench.gdperf_fixes.gdperf_logger.gdperf_stress.gdship_boot.gdweb_bridge.gd详细介绍
Godot 性能与发布
目标:Godot 4.7.2.stable,macOS Apple Silicon;Android 辅助工具也可在 Windows 和 Linux 上运行。
这里所说的专家级,意味着每项速度相关的结论都要附上同一测试框架测得的优化前后数据;每个构建都必须先验证确实是一个文件,才能称为已交付;Agent 还要知道 Godot 哪些数据报告得不正确。做法是:测量、判断瓶颈类型、修复最大开销、重新测量、对比画面,然后只交付自己打开过、运行过并列出过的构建。如果这里提到的同级技能不在你可用的技能中,请让用户安装它(npx skills add scenario-labs/skills --skill <name>);若需无人值守地继续,则根据工具架构推进,并指出缺少的技能。
必备背景: scenario-godot-expert(通道、审查循环、工具包、4.7 陷阱)。
做法(专家级的差异)
- 先判断瓶颈,再修复。 帧耗时取决于 CPU 和 GPU 中较慢的一方,因此优化另一方毫无收益(通用优化文档)。实测:将绘制调用从 2,501 降至 2 次,CPU 耗时从 2.7 ms 降到 0.35 ms,而 GPU 仍为 6.2 ms;只有调整阴影预算才让 GPU 耗时下降(至 1.7 ms)。执行一次半分辨率测试:若 GPU 耗时没有下降,问题就不在分辨率 [新增]。先记录
RenderingServer.get_video_adapter_name():混合显卡笔记本可能运行在集成 GPU 上;--gpu-index N可选择其他 GPU [新增]。 - 节点的开销先于像素。 每个节点的
_process、成千上万个节点和冗余物理对象,是 Godot 中常见的 CPU 开销来源(Firebelley 1PnG3r1rKcs:使用 RenderingServer 项目将耗时从 115 ms 降至约 8 ms;DwMoEdAhtYQ、O5a4AKemkiQ)。将重复对象替换为 MultiMesh 和着色器,并集中处理更新。实测:创建 2,000 个 MeshInstance3D 耗时 2.3 ms,通过RenderingServer.instance_create2创建则耗时 0.6 ms。 - 分散周期性工作。 为每个轮询器设置随机相位;对已禁用的 RayCast 调用
force_raycast_update()(Dan Does Dev,s2C2RO_WMh0 [00:05:34])。实测:600 个 Agent 的最差帧耗时从 1.93 ms 降至 0.64 ms,卡顿帧数从 62 降至 16。 - 使用手掌大小的遮挡体,并以绘制对象数为准。 为每面大墙设置盒状遮挡体,效果优于烘焙整个场景(Zenva,VBiBZBVxu1s:烘焙后约为 300 至 80 FPS,使用盒状遮挡体则为 350 FPS)。遮挡是一种 CPU 光栅化操作:应在多个摄像机位置观察绘制对象数来判断效果。
- 卡顿源于加载、管线和运行时烘焙。 在线程之外的主线程上加载并实例化都不够;应在主线程之外加载并实例化,随包提供预热的管线缓存(Shader Baker 导出选项,4.5),并记录每帧
PIPELINE_COMPILATIONS_*的变化量,将每次卡顿归类为加载或编译。导航网格烘焙以及区域边界处设为 Always 的 ReflectionProbe 也是可疑因素 [新增]。实测最差帧:同步冷启动为 453 ms;线程加载并实例化时冷启动为 230 ms;预热后为 9.6 ms。 - 在真实目标设备上进行足够长时间的测试。 With Debug 导出不会让游戏变慢;只有长时间运行才能显示节流问题(Ian Bolton,Arm,WrjaUNAXYqk [00:05:20, 00:09:35])。让手机在最繁重的区域连续运行 30 分钟,并以第 20 至 30 分钟的数据作为门槛 [时长为新增内容]。每份性能报告都必须基于干净构建:过时的缓存曾将一个性能缺陷掩盖了数周(Claire Blackshaw,grdqHJOL5F4 [00:16:15])。
- 导出失败通常是环境和凭据问题,而非 Godot 问题。 模板必须与精确版本匹配;配置好 JDK 和 SDK 路径、ETC2/ASTC 导入、bundle id、签名身份和服务器标头。凭据应放在
GODOT_*环境变量中,绝不能放入export_presets.cfg(导出文档;Nihamkin _n9l15CANag [00:25:38])。 - Web 仅支持 Compatibility 渲染器,且默认采用单线程。 线程需要通过 HTTPS 设置 COOP
same-origin和 COEPrequire-corp(Web 文档)。实测:未设置标头的多线程构建无法启动。 - 书面计划必须无需此工具包也能运行。 每次调用
gd_perf时,都要注明其封装的引擎代码或 CLI:下方的日志记录器、godot --headless --export-release、环境变量、bundletool、xcodebuild。第一轮盲评曾扣分,因为计划无法由读者直接执行。
首先确定
| 输入 | 为什么会影响计划 | 未知时的默认值 |
|---|---|---|
| 目标硬件和帧预算 | 60 fps 时为 16.67 ms,30 fps 时为 33.33 ms,90 fps(VR)时为 11.11 ms,120 fps 时为 8.33 ms | 在列出的最低配置设备上达到 60 fps |
| 各平台的渲染器 | 自动实例化、遮挡和功能各不相同;Web 强制使用 Compatibility | 桌面端使用 Forward+,手机使用 Mobile,Web 使用 Compatibility |
| 卡顿发生的位置 | 持续低帧率,还是加载或转向时出现卡顿 | 两种情况都进行性能分析 |
| 平台和商店 | 签名、ID、大小限制、AAB 与 APK | 询问;每个商店都需要完成账号相关步骤 |
| 下载预算 | wasm、pck、APK 大小 | 主机实际提供的字节数 |
| 凭据由谁保管 | 密钥库、Apple 团队、Steam 构建器 | Agent 绝不编造或存储密钥 |
工作流
- 在干净的项目副本上建立基线。 使用
gd_env.base_project克隆,运行gd_perf.install(P), 重命名application/config/name(Base3D 克隆版会共用一个user://和着色器缓存)。 重新读取project.godot:确认 3D 使用 Jolt,并确认渲染器符合预期。在重场景上运行gd_perf.scene_audit。门槛:已保存审计 JSON,已确认物理引擎和渲染器。 - 在窗口模式下逐项测量。
gd_perf.profile_variant(P, scene, size=(1280, 720), driver="vulkan"):生成逐帧 CSV、可见和阴影绘制调用数,以及来自哨兵节点的场景_process时间;它会在运行gd_perf.install(P)后比较settings、toggles或rendering_method变体;而要对未更改的场景给出单一结论,负责人只需运行gd_run.profile_scene(driver="vulkan")。不要逐帧使用Performance.TIME_PROCESS:它会将一个值保留约一秒。macOS 上 Metal 报告的 GPU 值为 0;请使用 Vulkan。窗口模式下的帧时间受刷新率限制;请根据 gpu_ms 和 cpu_ms 判断。 深入分析:针对单帧使用--gpu-profile、Visual Profiler、脚本 Profiler(查看 Self,而非 Inclusive;它不涵盖 C#)、RenderDoc 或厂商工具。门槛:两次运行的结果相差不超过 10% [added]。 - 分类。
gd_perf.classify(full, half_res)。门槛:给出书面结论(CPU 脚本、CPU 渲染、GPU 填充、GPU 几何体或阴影),并以数据为依据。 - 修复一项、重新测量并检查画面。
gd_perf.apply_fix(P, scene, out, fix)会写入新场景:share_materials、multimesh、shadow_budget、box_occluders、visibility_range、strip_process(仅限实验室:着色器完成_process脚本的工作后移除这些脚本)。gd_perf.compare(before, after)会应用 5 % 和 0.1 ms 的噪声门槛。分别捕获修改前后的画面,生成联系表并打开查看。分辨率缩放低于约定的下限(0.67)需要签字批准 [added]。 门槛:指标提升幅度超过噪声水平,没有指标退步,平均亮度差异低于 0.02,或已批准此更改。 - 卡顿检查。 使用全新的
private_home运行perf_bench.gd:hitch(通过home_env运行),以获得冷着色器缓存。门槛:暖缓存下最差帧低于预算的 2 倍,管线编译不占用游戏帧。 - 生成发布构建。 通过
gd_run.ensure_preset和gd_perf.preset_fields设置预设;在gd_perf.export_env(**gd_perf.home_env(home))下导出,其中home = gd_perf.private_home(...)。 普通 CLI:godot --headless --path . --export-release "<preset>" <out>。验证每个产物:verify_macos、verify_apk、verify_xcode_project、pck_listing、size_report、web_smoke。 门槛:符合下方各平台的要求。 - 运行导出的构建。
使用
perf_logger.gd运行gd_perf.run_exported(exe, ["--headless", "--", "--perf-log", "--perf-quit"]);Web 版通过无头 Chrome 运行。门槛:SHIP_BOOT显示预期标签和渲染器,PERF_SUMMARY的 p95 符合预算,没有错误。 - 发布审计与交接。 运行
gd_perf.release_audit(P);为持有相关账户的人编写签名、公证和上传步骤(notarize_plan、steam_vdf)。门槛:没有高优先级标记;账户相关步骤列为“未运行”,并注明原因。 手机。 移动端渲染器;若要覆盖最广泛的低端设备,则使用兼容性渲染器(Ray Hayes,KWhVVMpihsc [00:06:03]);默认启用fallback_to_opengl3,这会改变画面效果,因此也要捕获回退模式下的画面 [00:05:12]。低端设备:将scaling_3d_scale设为 0.67 到 0.8,设置Engine.max_fps = 30,物理帧率设为 30 并启用插值(O5a4AKemkiQ [00:03:43])。Android 也要应用安全区域:实测 4.7.2 APK 的目标 SDK 为 36,因此 Android 15 及更高版本会采用全面屏绘制 [added]。Android 构建:--install-android-build-template必须与导出命令一起使用(实测发现,使用--quit时不会安装任何内容),将密钥库放在GODOT_ANDROID_KEYSTORE_RELEASE_{PATH,USER,PASSWORD}中,使用bundletool build-apks --connected-device构建后,再运行get-size total查看大小 [not run]。热浸测试:每隔 10 秒记录一次adb shell dumpsys thermalservice;在 CI 中使用 Streamline 区域标记和 Performance Advisor(WrjaUNAXYqk [00:14:43]);使用Performance.add_custom_monitor记录设备上的游戏指标。接入崩溃报告工具,并在发布前测试下一个操作系统测试版(8zqjiRuhQLE [00:03:40, 00:09:11])。iOS: 导出会生成一个 Xcode 项目,然后运行xcodebuild archive和-exportArchive;首次上传前创建 App Store Connect 记录,每次都递增构建编号;Vulkan 渲染器不支持模拟器;如果导出时提示 "xcodebuild requires Xcode",则运行sudo xcode-select -s /Applications/Xcode.app/Contents/Developer(x_ZFAV2id3I [00:14:41, 00:17:04, 00:40:34])。IAP:使用商店插件和账号,绝不伪造。
Web。 除非实测确认多线程确有必要,否则使用单线程。itch.io 会按上传时的原样提供文件(不会即时压缩),因此要控制发送的数据量。后台标签页会暂停 _process,并导致联网游戏掉线;HTTPRequest 不支持分块响应,而且每帧只报告一次进度;过时的服务工作线程可能会显示另一个项目(Web 文档)。隔离后,跨源请求需要 CORS 或 CORP [added]。C# 项目无法导出到 Web(版本差异 16)。浏览器中的排行榜只读。
仅使用引擎的帧记录器(不使用工具包,运行于 2026-10-02)
extends Node # autoload "Bench"; run the game with: -- --bench
var ai_ms := 0.0
var _vp: RID
var _rows := PackedStringArray(["frame_ms,render_cpu_ms,gpu_ms,draw_calls,pipelines_total"])
func _ready() -> void:
if not "--bench" in OS.get_cmdline_user_args():
set_process(false)
return
_vp = get_viewport().get_viewport_rid()
RenderingServer.viewport_set_measure_render_time(_vp, true)
Performance.add_custom_monitor("game/ai_ms", _get_ai_ms) # a method, never a lambda
print("BENCH adapter=", RenderingServer.get_video_adapter_name())
func _get_ai_ms() -> float:
return ai_ms
func _process(delta: float) -> void:
var pipes := Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_SURFACE) \
+ Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_SPECIALIZATION)
_rows.append("%.2f,%.3f,%.3f,%d,%d" % [delta * 1000.0,
RenderingServer.viewport_get_measured_render_time_cpu(_vp),
RenderingServer.viewport_get_measured_render_time_gpu(_vp),
Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME), pipes])
func _exit_tree() -> void:
if is_processing():
Performance.remove_custom_monitor("game/ai_ms")
FileAccess.open("user://bench.csv", FileAccess.WRITE).store_string("\n".join(_rows))
管线计数器是累计值:若卡顿行的总数上升,即表示发生了编译。前几帧的 GPU 时间显示为 0。
数值
| 数值 | 对比 | 来源 |
|---|---|---|
| hitch = 超过中位数 2 倍的帧 | 每次运行 | toolkit [添加] |
| 2,501 到 33 次绘制调用 | 2,500 个立方体、8 种共享材质、Forward+ | 实时,自动实例化 |
| 2,501 到 2,501 | Mobile 下相同 | 实时:无自动实例化 |
| 400 个透明立方体:1 次绘制,2 种交替材质:117(不透明:2) | Forward+ | 实时:深度排序会打破批次 |
| 400 个 Label3D:800 次绘制;一个共享 TextMesh:1 次 | Forward+ | 实时:轮廓加文本,无法实例化 |
| omni 阴影:Cube(默认)19 次绘制,Dual Paraboloid 2 次 | Forward+,400 个投射体 | 实时;Mobile 上相反(124 对 220) |
| GPU 时间 6.6 到 1.7 毫秒 | 16 到 4 个带阴影的 omni 灯光 | 实时 |
| 1,260 到 90 个对象 | 一个盒状遮挡体 | 实时,全部三种渲染器 |
| 38.1 MB 原始大小、9.7 gzip、7.8 brotli | 4.7.2 Web 导出,小型 3D 场景 | 实时;wasm 占主导 |
| 26.5 到 28.3 MB | Android arm64 APK,发布版 / 调试版 | 实时;引擎库 24 MB |
质量门槛
- **测量:**目标级别上的 p95 帧时间或 GPU 时间符合预算;区域加载时的最差帧;
compare显示预期指标已改善且没有指标退化;scene_audit没有 high 标记。 - **视觉:**已打开前后对比图集(最大 1600 px);遮挡体调试截图;Web 画布截图。
- macOS:
codesign --verify --deep --strict通过,关闭 Debugging entitlement,包含两种架构; 只有经过公证后,Gatekeeper 才会接受。 - Android:
apksigner验证通过,签名者 SHA-256 与密钥库一致,versionCode 已提升,发布版不可调试,包含计划中的 ABI,预设中不含密码。 - **iOS:**Xcode 项目包含 scheme、bundle id 和 team;安装 iOS 平台后,未签名的
xcodebuild可通过。 - **Web:**在无头 Chrome 中通过服务启动;若使用多线程,
crossOriginIsolated为 true;页面无错误;服务大小符合预算。 - **资源包:**基础 pck 不含排除的内容;DLC 使用
replace_files=false加载;仅演示版包含 demo 标签。
常见错误
| 错误 | 表现 | 修正 |
|---|---|---|
监控 CSV 中的 process_ms p95 | 120 行的值都相同 | 哨兵计时 |
| Metal 上的 GPU 数值 | gpu_ms 为 0 | --rendering-driver vulkan |
| 无头模式下的帧时间 | 什么都没绘制,却显示“60 fps” | 使用窗口运行 |
| 在无头任务中构建 MultiMesh | 保存的场景没有实例 | 写入 MultiMesh.buffer |
| Mobile 上的共享材质 | 绘制调用数没有变化 | 使用 MultiMesh |
| 同一区域内混用透明材质 | 绘制调用数随对象数量增加 | 减少透明材质;拆分混合的不透明表面和 Alpha 表面(3D 文档) |
| 用 Label3D 显示名牌或伤害数字 | 每个标签需要 2 次绘制 | 使用共享 TextMesh 或单个文本节点(grdqHJOL5F4 [00:09:33]) |
| LightmapGI 旁的灯光仍设为 Dynamic | 烘焙区域产生动态灯光开销 | 使用 Static 烘焙模式,DirectionalLight3D 保持为 Dynamic(3D 文档) |
| 自定义监视器使用 lambda | 值停留在初始状态;4.7.2 在退出时崩溃(信号 11) | 绑定方法;退出时调用 remove_custom_monitor |
| 开启遮挡剔除但没有遮挡体 | 绘制的对象没有变化 | 添加 OccluderInstance3D 盒体 |
| itch 上的线程化 Web 构建未配置请求头 | “缺少跨域隔离” | 使用单线程、配置请求头或采用 PWA 变通方案 |
| Android 路径只设置一次 | “必须提供有效的 Java SDK 路径” | 在私有编辑器设置文件中设置路径 |
| 以 replace_files true 挂载 DLC | 旧脚本覆盖新脚本 | load_resource_pack(path, false) |
export_presets.cfg 中的密码 | 密钥泄露到 git | 使用 GODOT_ANDROID_KEYSTORE_*、GODOT_MACOS_* 环境变量 |
移交
scenario-godot-rendering-lighting(阴影预算、烘焙模式、质量层级),
scenario-godot-shaders(MultiMesh 顶点动画、Shader Baker),scenario-godot-3d-world(LOD、
可见性范围、遮挡体、Jolt、大地图的 Large World Coordinates 或原点偏移),
scenario-godot-gameplay 和 scenario-godot-architecture(管理器、对象池、错峰计时器、
线程化加载),scenario-godot-ui(触控布局、安全区域),scenario-godot-multiplayer(Web
传输、专用服务端、可信排行榜写入),scenario-godot-pipeline-automation(CI
导出、干净构建),scenario-godot-audio(Web 上的 Sample 播放)。人工处理:Apple Developer ID、
App Store Connect、Play Console、Steamworks、主机、IAP 商品。审计标记会附带负责人
(perf_audit.gd)。
Godot 4.7 说明
- 4.7.2 中不存在
OS.get_thermal_state:在压力测试期间记录帧耗时,并通过 adb 或 Instruments 查看 OS 热状态。 - 已核查的默认值:
rendering_method.mobile="mobile"、rendering_method.web="gl_compatibility"、关闭遮挡剔除、手写项目中的import_etc2_astc为 false、关闭物理插值、物理频率为 60 Hz、fallback_to_opengl3为 true、OmniLight3D.omni_shadow_mode为 Cube、light_bake_mode为 Dynamic、Web 音频为 Sample。 - Pck 格式 4(
pck_listing可读取)。自 4.6 起,新建 3D 项目默认使用 Jolt;手写的project.godot需要设置该项。 - Android:JDK 17;Godot 仅在创建编辑器设置时,才会从 JAVA_HOME 和 ANDROID_HOME 读取 JDK 和 SDK 路径;自 4.3 起不会自动请求权限。
- iOS 导出无需签名团队,但需要
application/export_project_only;由于不存在.ipa,gd_run.export会报告失败。 - 物理插值(2D 自 4.3 起,3D 自 4.4 起)使降低物理更新频率成为可行方案。
参考资料
references/procedures.md:步骤 1 至 16,包含代码、实时结果以及未运行的内容。references/expert-notes.md:专家按时间戳记录的说法,以及与实时测试不符之处。references/critique.md、references/gui-paths.md、references/sources.md。scripts/gd_perf.py和scripts/agentkit/perf/(perf_stress、perf_fixes、perf_audit、perf_bench、perf_logger、ship_boot、web_bridge、mobile_touch)。