鸣潮的优化洗地艺术大赏

本文疑似鸣潮在外网投放的洗地软文
某日深夜,一位睡不着的程序猿群友在外网发现的一篇关于鸣潮卡顿的研究,因为原因过于的神人,遂进行搬运,毕竟独乐乐不如众乐乐 :winhalf:
作者:ToonXD,来源:git

我是原文链接

https://pqmlmaoxd.github.io/gamedev/analysis/2026/04/12/wuthering-waves-performance-issues.html

含小可爱含量过高的个人感想

你小可爱的b肚子里藏着花花肠子是吧,老娘这就把全文搬过来附上中英对照,我倒想看看划开肚子你藏着几碗粉

我鸣潮卡是因为ue4的问题,绝对不是因为我开服就把搭脚手架的引擎大佬踹了以至于没来得及搭设完整的工作流,绝对不是因为我把程序语言换成C#导致包体里同时有两套石山代码需要的素材,绝对不是我没能力用分区块加载所以只能做一次读取所有素材的箱庭地图,绝对不是因为我滥用TS导致资源回收和资源加载这俩性能开销大户永远撞车的问题,全都是UE4底层架构有问题,哼

来自程序猿群友关于此文问题之处的大致说明1

1.粗暴的把高负载时阻塞的锅 甩到V8的GC机制上,事实上不整活GC不会导致这么严重的问题
2.不考虑优化gameThread长度可以缓解渲染等待问题,只说UE4的OOP模型导致缓存频繁未命中,才会让RenderThread空置等待
3.说“没有UE4开放世界能避免此类架构问题”,事实上架构问题从来不代表无法优化,见前两条
4.甩锅Soc大小核调度导致卡顿,说“加载完成之后,Soc把GameThread调度至小核,升档不及时导致卡顿” 但是业界是一直有让gameThread负载平滑,避免EAS误判、负载突变的方案的

总之 就是把卡顿这个复合问题拆开了,但是一细说就是架构问题、前负责人技术选型问题 忽略了本来可以做到的合理的优化

大致说明2

从他PC端到移动端,所有的问题都可以总结举例为:把可以均摊到10帧渲染前做完的事情,放到1帧做完,那可不负载爆炸 然后渲染线程空置,CPU有难GPU围观么?包括大小核调度的EAS问题也是 非要一口气把所有的Streaming和前置事项做完,然后放任GameThread低负载空置,那Soc可不把你扔小核上去吗? 平滑处理 让GameThread一直保证中高程度的算力需求,就是比较通用的做法了,另外他们这种玩法,和小明的生涯总览真的很像从来不懂什么是细水长流,就想一口吃个狠的 然后没吃到还炸缸了,也算是一种对应吧,全方位的短生种思维

大致说明3

所有UE引擎的游戏 都会面临OOP模型的问题,业界关于如何在确定架构下尽可能优化都有方案的 对象池 还有我之前也提到了的 分帧激活实体群 都是很基础的优化思路,这篇文章里提到了 进入新区域时 会在短期产生大量ts实体,就把他们没这么做的屁股给露出来了 ,另外 可以在离开区域的时候不销毁实体,只归还池子,这也是比较常见的优化方案。结果他们高频触发的GC也把他们没这么做的屁股漏了,说白了就是想造奇观导致的,进入一个区域就想着把区域内的全部实体都激活了,离开一个区域就把实体全销毁了,在不造奇观、实体量级不大的早期,这么一口闷不会带来太严重的问题。但后面量级一大,他不卡谁卡?

大致说明4

甩锅给GameThread真的很难绷 既然知道TaskGraph利用多线程 为什么不尽可能让单次GameThread短一点 为什么要在TS实例化一整套行为树和交互逻辑? 刚刚我同事在旁边,他说看到这套写法的第一反应是 这很像一个写JAVA的被强制要求做改造 就按照JAVA的思路写,让克劳德改一下就用了 产物就差不多是这个逻辑。这能称得上用心优化吗?别假装自己在努力了

分段搬运如下:
前言:

原文

As a follow-up to the storage bloat analysis (https://pqmlmaoxd.github.io/gamedev/analysis/2026/03/15/wuthering-waves-memory-bloat.html), today I want to go deeper into something more contested: why WuWa performs the way it does, and whether the community complaints are actually fair.

Some context before we start: I spent a significant amount of time on this because I genuinely wanted to know whether the frustration was justified — or whether people were blaming developers for problems that go a lot deeper than implementation quality.

This post is not a defense of Kuro Games. It is also not a list of excuses. What it is, I hope, is an honest attempt to explain why the performance situation is considerably more complicated than “the devs didn’t optimize” or “skill issue.”

:warning: Warning: This post is significantly more technical and academic than the memory bloat article before it. It covers CPU architecture, engine threading models, garbage collection mechanics, and data-oriented programming — if that starts to feel overwhelming, feel free to skip straight to the TL;DR at the end.

机翻

作为对存储膨胀分析(https://pqmlmaoxd.github.io/gamedev/analysis/2026/03/15/wuthering-waves-memory-bloat.html)的后续,今天我想更深入探讨一个更具争议的话题:《鸣潮》为何会有这样的表现,以及社区的抱怨是否真的合理。
在我们开始之前,先说一下背景:我花了大量时间在这件事上,因为我真心想知道这种不满是否合理——或者人们是否在把那些远不止于开发质量的问题归咎于开发者。
这篇帖子并非为 Kuro Games 辩护,也不是在罗列借口。我希望它能诚实地解释一下,为何性能问题远比“开发者没有优化”或“技术问题”要复杂得多。
:warning:警告:与之前的内存膨胀文章相比,这篇帖子的技术性和学术性要强得多。它涵盖了 CPU 架构、引擎线程模型、垃圾回收机制以及面向数据编程——如果这些内容让您感到难以承受,随时可以跳到文末的 TL;DR 部分。

一个谁也达不成共识的问题

原文

I keep seeing the same complaints across Reddit, Discord, and YouTube:

“Startorch Academy is stuttering badly on my mid/high-end PC — what is going on.”
“The devs just don’t give AF about optimization, they just keep pushing flashy effects…”
“Look at AKE, Genshin, RDR2 — they all run smoothly. WuWa can’t do it after 2 years of development?”
What nobody in those threads distinguishes is that “the game performs badly” is actually describing at least two completely separate phenomena — with different root causes, different locations on the map, and different fix timelines.

Pattern A — Stutter while inside the city. Microstutters, unstable frame pacing, FPS that never quite locks. This happens in Startorch Academy, in Septimont, in any dense populated area — regardless of whether you are in combat or just walking. GPU load is unremarkable. The problem is entirely CPU-side.

Pattern B — Hard freeze when leaving the city. This one is harder to forget: you cross the boundary, and the game drops to 0 FPS for 500ms to a few seconds, then recovers completely. Players who have been with WuWa since launch will remember earlier versions where this was severe. It has improved considerably through patches — v3.1’s streaming pipeline optimization made a visible difference — but it has not disappeared entirely.

These two patterns look related because they both happen near dense areas. They are not the same thing:

Pattern When it happens What’s happening Status
A — City stutter While inside dense area GameThread overload from active logic tick count — NPC AI, scripting, physics, interaction handlers Engine-level ceiling, unresolved
B — Exit freeze Leaving dense area Asset streaming flush + V8 GC cycle firing simultaneously Improved significantly, residual remains
A third phenomenon exists separately from both of these:

Also happening When Root cause
Combat FPS drops (mobile) Dense combat with VFX GameThread particle saturation → GPU renderer inefficiency cascade
This post will go through all of these. More importantly, it will try to explain why they are different problems — because that context is what most community analysis misses entirely, and it changes what “optimization” even means here.

第 1 列 第 2 列 第 3 列 第 4 列
Pattern When it happens What’s happening Status
A — City stutter While inside dense area GameThread overload from active logic tick count — NPC AI, scripting, physics, interaction handlers Engine-level ceiling, unresolved
B — Exit freeze Leaving dense area Asset streaming flush + V8 GC cycle firing simultaneously Improved significantly, residual remains
A third phenomenon exists separately from both of these:
第 1 列 第 2 列 第 3 列 第 4 列
Also happening When Root cause
Combat FPS drops (mobile) Dense combat with VFX GameThread particle saturation → GPU renderer inefficiency cascade

This post will go through all of these. More importantly, it will try to explain why they are different problems — because that context is what most community analysis misses entirely, and it changes what “optimization” even means here.

机翻

我在 Reddit、Discord 和 YouTube 上不断看到同样的抱怨:

“我的中高端 PC 跑星炬学院卡得一塌糊涂——到底怎么回事?”
“开发者根本不在乎优化,只知道不断加花里胡哨的特效……”
“看看 终末地、原神、大镖客 2——它们都很流畅。鸣潮开发两年了还做不到?”

在这些讨论中,没有人区分的是:“这游戏性能很差”实际上描述的是至少两种完全不同的现象——它们的根本原因不同、发生的地图位置不同,修复的时间表也不同。
在这些讨论中,没有人区分的是:“这游戏性能很差”实际上描述的是至少两种完全不同的现象——它们的根本原因不同、发生的地图位置不同,修复的时间表也不同。


模式 A——城内卡顿
微卡顿、帧时间不稳定、帧数始终锁不住。这种情况发生在 Startorch Academy、Septimont 以及任何人口密集的区域——无论你是在战斗还是仅仅在走路。GPU 负载并不高。问题完全出在 CPU 端。

模式 B——出城时的硬冻结
这种卡顿更难忘记:你穿过边界,游戏帧数瞬间掉到 0,持续 500 毫秒到几秒,然后完全恢复。从《鸣潮》开服就玩过来的玩家会记得早期版本中这个问题有多严重。经过多个补丁的改进,情况已有显著提升——3.1 版本的流送管线优化带来了肉眼可见的改善——但它并没有完全消失。

第 1 列 第 2 列 第 3 列 第 4 列
模式 发生时机 发生了什么 状态
城市卡顿 在城市内部时 GameThread因活动逻辑Tick数量过载 NPC AI、脚本、物理、交互处理器 引擎级别的上限,未解决
离开时卡死 离开密集区域 资源流送刷新 + V8 GC周期同时触发 显著改善,残留仍存
还有第三种现象独立于上述两者:
第 1 列 第 2 列 第 3 列 第 4 列
现象 发生时机 根本原因
战斗掉帧(移动端) 密集战斗带VFX GameThread粒子饱和 → GPU渲染器低效级联
6 个赞
  1. 引擎的基本约束

1.1 UE4 中一帧是如何真正生成的

原文

In Unreal Engine 4, every frame flows through three stages, in order:
[GameThread] ──► [RenderThread] ──► [GPU]
The GameThread is where everything that makes the game a game happens: actor updates, physics simulation, AI decisions, animation state machines, particle system tick updates, collision detection, scripting logic — all of it. Every frame. In sequence. The GameThread is the central coordination point that all game logic must pass through before work can be dispatched downstream.

A clarification worth making here: UE4 does have a TaskGraph system that allows some async work to be dispatched to worker thread groups — WuWa’s client log confirms this, showing NP (Normal Priority), HP (High Priority), and BP (Background Priority) task thread groups active at runtime. This means WuWa is not running a completely single-threaded model. However, TaskGraph parallelism does not eliminate the GameThread as a bottleneck — it reduces some of the work. The GameThread remains the mandatory coordination and synchronization point. Gameplay APIs, AI state, scripting logic, and actor tick coordination that are not thread-safe by design must still pass through it. When that coordination overhead saturates, downstream threads wait regardless of how many workers exist.

When the GameThread finishes, it hands a list of rendering commands to the RenderThread. The RenderThread prepares those commands for the GPU. And here is the critical part: the two threads synchronize at the end of each frame. Neither can start the next frame until both are done with the current one.

From Epic’s own documentation:
“In Unreal Engine 4 (UE4), the entire renderer operates in its own thread that is a frame or two behind the game thread.” “The game thread inserts the command into the rendering command queue… RHI functions can only be called from the rendering thread.”
Source: https://dev.epicgames.com/documentation/unreal-engine/threaded-rendering-in-unreal-engine

In simple terms: if the GameThread is slow, the RenderThread waits. The GPU idles. You get a frame spike. It doesn’t matter how fast your GPU is — it has to wait for the GameThread to finish feeding it work.

Think of it like a restaurant kitchen where every order — no matter how simple — must be personally reviewed and signed off by the head chef before it goes out. The kitchen has 16 sous chefs standing ready. But every ticket still waits for the head chef’s signature. More sous chefs don’t help. A faster head chef helps a little. The bottleneck is the process, not the people.

In UE4, the GameThread is the head chef. Every piece of game logic needs its sign-off every frame, in sequence, before anything moves forward.

The diagnostic command stat unit in UE4 breaks out Game, Draw, and GPU times separately. When Frame time ≈ Game time, the bottleneck is the GameThread. That is where WuWa consistently ends up in dense areas.

机翻

在虚幻引擎4中,每一帧按顺序流经三个阶段:
[游戏线程] ──► [渲染线程] ──► [GPU]
游戏线程是所有让游戏成为游戏的事情发生的地方:角色更新、物理模拟、AI决策、动画状态机、粒子系统Tick更新、碰撞检测、脚本逻辑——所有这些。每一帧。按顺序。游戏线程是中央协调点,所有游戏逻辑在分派到下游工作之前都必须通过它。

这里需要澄清一点:UE4 确实有一个任务树系统,允许将一些异步工作分派给工作线程组——《鸣潮》的客户端日志确认了这一点,显示运行时存在 NP(普通优先级)、HP(高优先级)和 BP(后台优先级)任务线程组。这意味着《鸣潮》并非完全运行在单线程模型上。然而,任务树 并行性并不能消除游戏线程作为瓶颈的问题——它只是减少了部分工作。游戏线程仍然是强制性的协调和同步点。那些非线程安全的游戏 API、AI 状态、脚本逻辑和逐帧更新协调仍然必须通过它。当这种协调开销饱和时,无论有多少工作线程,下游线程都会等待。

当游戏线程完成后,它会将一系列渲染命令交给渲染线程。渲染线程为图形处理器准备这些命令。这里是关键部分:两个线程在每帧结束时同步。只有当前帧两者都完成了,才能开始下一帧。

来自 Epic 自己的文档:
“在虚幻引擎4中,整个渲染器在自己的线程中运行,比游戏线程落后一两帧。”
“游戏线程将命令插入渲染命令队列……RHI 函数只能从渲染线程调用。”

来源:https://dev.epicgames.com/documentation/unreal-engine/threaded-rendering-in-unreal-engine

简单来说:如果游戏线程慢了,渲染线程就会等着。GPU 会闲置。你就得到一个瞬时卡顿。你的 GPU 再快也没用——它必须等游戏线程完成给它的工作。

把它想象成一家餐厅厨房,每张订单——无论多么简单——都必须由主厨亲自审查和签字才能送出。厨房有16名副厨待命。但每张票仍然等待主厨签字。更多的副厨无济于事。更快的主厨有点帮助。瓶颈在于流程,而不是人员。

UE4 的诊断命令 stat unit 分别列出了游戏、渲染和图形处理器时间。当帧时间 ≈ 游戏时间时,瓶颈就是游戏线程。在密集区域,《鸣潮》始终是这个情况。

1 个赞

这个在豆蒸区讨论了,感觉这个洗地和抬咖的情况有点明显 :syq_naotou:

1.2 为什么你的6核或8核 CPU 无法解决这个问题

原文

tasks are inherently sequential, and splitting them up might introduce complexity without performance gains.”

Source: https://dev.epicgames.com/community/learning/tutorials/BdmJ/unreal-engine-multithreading-techniques

I captured two benchmark sessions on the same hardware — i7-12700KF + RTX 5070, identical settings: 1080p, settings, ray tracing, DLAA, no frame generation. Two different cities, same question: is the CPU the ceiling?

Huanglong (v1.0 city):

Average FPS: 40.2 · CPU max thread: 97% · GPU avg: 54%


Frame time distribution during Huanglong city traversal. Note the consistent spike pattern — and the 245ms outlier at ~t=85s, deliberately triggered by flying out of the city boundary at maximum speed (Pattern B).

Startorch Academy (v3.0 city):

Average FPS: 38.5 · CPU max thread: 100% · GPU avg: 58%


Frame time distribution during Startorch Academy traversal. The 175ms outlier spike (organic, not triggered) occurs with no GPU load correlation — consistent with a V8 GC stop-the-world pause.

Two cities. Two sessions. Same answer both times: CPU max thread saturated, GPU underutilized. The sessions corroborate each other — this is not an artifact of one area or one patch. The RTX 5070 averaging 54–58% utilization while the CPU max thread sits at 97–100% is the clearest possible signal. The GPU is waiting. This is just existing in a city — not combat, not a stress test.

Mobile thread statistics — direct confirmation:

Community member @xLOCKnLOADx (author of the Reddit mobile profiling post referenced in Section 5) provided per-thread load data from WuWa running on mobile. The thread distribution tells the story directly:

GameThread and RenderThread are the dominant threads. TaskGraph worker threads are active but sit at 13% and below. The V8 DefaultWorker entries are direct runtime confirmation that V8/Puerts is active alongside the main game threads.

Architecture comparison — WuWa vs Arknights Endfield:

Profiled on the same device, AKE’s thread distribution shows a fundamentally different pattern:


Similar main thread load (~70%), but AKE’s Job Workers are genuinely doing substantial work — 20%+ each. This is Unity’s Job System distributing simulation work across cores effectively. WuWa’s TaskGraph workers sit at 7% and below by comparison.

An important behavioral distinction also worth noting: WuWa’s worker threads display a sawtooth/burst pattern rather than sustained load. They activate in short bursts, complete tasks quickly, then stall waiting for the main thread to provide more work or resolve sync points. This is not a lack of parallel capability — it is a coordination bottleneck: the system can use more cores, but is not being fed work in a way that allows sustained parallel execution. This framing is more precise than simply saying “more cores don’t help” — the correct statement is that additional cores cannot accelerate work that remains serialized at the coordination point.


WuWa per-thread load on mobile. GameThread and RenderThread carry the overwhelming majority of work. TaskGraph workers active but contribute minimally. V8 DefaultWorker instances confirm Puerts/V8 runtime active. Source: @xLOCKnLOADx

Architecture comparison — WuWa vs Arknights Endfield:


@xLOCKnLOADx summarizes the practical implication directly: “It’s basically all main thread, the threads he mentioned actually don’t do all too much work compared to a game like AKE. If an 8E chokes so badly with a beefy prime, you can only imagine how lower end hardware would struggle.”

In short: the GameThread’s work involves constantly accessing data scattered across RAM. A larger L3 cache keeps more of that data close to the CPU, reducing the stall time on each access. This is why AMD’s X3D CPU — which stack additional L3 cache onto the die — often deliver noticeably smoother WuWa experiences than higher-clocked CPUs with smaller caches, even if their average FPS on paper is lower. A Ryzen 5700X3D may have lower average FPS than an i7-12700KF, but 1% lows and frame consistency in city areas will likely be meaningfully better.

That said: X3D is the best hardware mitigation available, not a cure. It reduces the severity of stutter, but cannot eliminate it — because the root cause is architectural, not a cache size problem that can be fully patched with hardware. The GameThread ceiling, the V8 GC pauses, the OOP pointer-chasing — those all remain. A larger cache makes them hurt less, not disappear.

Practical takeaway: if WuWa stutters and your GPU is below 80% utilization — the upgrade that will help most is a CPU with a large L3 cache, not a new GPU. And calibrate expectations accordingly.

我是机翻

这是这篇文章中最反直觉的一点,所以我想说清楚。

更多的 CPU 核心对解决 GameThread 瓶颈没有帮助。

一位社区成员分享了他们在城市密集区域游玩时截取的 Process Lasso 截图——模式一目了然:
[图片:社区的 Process Lasso 截图——i7-11700F,在城市区域运行《鸣潮》。Client-Win64-Shipping.exe 显示产生了 319 个线程,但整体 CPU 使用率读数为 31%,而游戏同时报告 100% 的响应压力。角落的 CPU 条形图显示了一个严重偏斜的分布——一个核心集群在努力工作,其余大多数接近空闲。]
来源:https://www.reddit.com/r/WutheringWaves/comments/1p2pwq0/cpu_issue_apparently_the_new_area_is_full/

这台机器有足够的计算能力。但决定你是否会卡顿的工作——NPC行为树、交互处理器、AI Tick协调、脚本逻辑——不能自由地分配到所有核心上。大部分在能被分派到工作线程之前,必须先通过 GameThread 的协调点,而该点的同步开销正是单一线程饱和的原因。存在 319 个线程;引擎的线程约束限制了关键路径的并行化程度。

我自己的 CapFrameX 基准测试证实了 Process Lasso 模式所显示的内容:在 i7-12700KF + RTX 5070 上的两次城市会话中,CPU 最大单线程峰值达到 97-100%,而整体 CPU 负载平均为 40-46%。总体的 CPU 数字看起来还行。但每线程的情况则完全不同。

vkguide 的多线程文章用 UE4 描述了这个问题:

“你通常会看到虚幻引擎游戏很难扩展到4核以上……一个在 UE4 中大量使用蓝图和 AI 计算的游戏会让 Game Thread 在一个核心上忙碌工作,而机器上的所有其他核心几乎未被使用。”

来源:https://vkguide.dev/docs/extra-chapter/multithreading/

以及来自 Epic 自己的开发者文档:

“设计上,许多引擎 API 和游戏操作不是线程安全的,因此它们必须在 GameThread 上执行……Game Thread——中央执行线程:每帧协调所有游戏逻辑。”但是“多线程并不总是银弹。有些任务本质上是顺序的,拆分它们可能会引入复杂性而得不到性能提升。”

来源:https://dev.epicgames.com/community/learning/tutorials/BdmJ/unreal-engine-multithreading-techniques

我在同一硬件上捕获了两个基准测试会话——i7-12700KF + RTX 5070,相同设置:1080p 设置,光线追踪,DLAA,无帧生成。两个不同的城市,同一个问题:CPU 是瓶颈吗?
瑝珑(v1.0 城市):
· 平均 FPS:40.2
· CPU 最大线程:97%
· GPU 平均:54%


瑝珑城市遍历期间的帧时间分布。注意一致的尖峰模式——以及约 85 秒处的 245ms 异常值,这是通过以最高速度飞出城市边界故意触发的(模式 B)

星炬学院(v3.0 城市):

· 平均 FPS:38.5
· CPU 最大线程:100%
· GPU 平均:58%


星炬学院遍历期间的帧时间分布。175ms 异常尖峰(自然发生,非触发)出现时没有 GPU 负载相关性——与 V8 GC 停止世界暂停一致。

两个城市。两次会话。两次答案相同:CPU 最大线程饱和,GPU 未充分利用。两次会话相互印证——这不是某个区域或某个补丁的假象。在 CPU 最大线程达到 97-100% 的同时,RTX 5070 平均利用率只有 54-58%,这是最清晰的信号。GPU 在等待。这仅仅是身处城市中——而不是战斗或压力测试。
移动端线程统计数据——直接确认:

社区成员 @xLOCKnLOADx(即第5节中引用的 Reddit 移动端性能分析文章的作者)提供了在移动端运行《鸣潮》的每线程负载数据。线程分布直接说明了问题:


游戏线程和渲染线程是主导线程。任务树工作线程处于活动状态,但负载在 13% 及以下。V8 默认工作线程(V8 DefaultWorker)条目直接确认了 V8/Puerts 与主游戏线程一起活跃。

架构比较——《鸣潮》 vs 《明日方舟:终末地》:

在同一设备上分析,《终末地》的线程分布显示了一个根本不同的模式:

主线程负载相近(约70%),但 终末地的任务工作线程确实在执行大量工作——每个都超过 20%。这是 Unity 的任务系统在高效地将模拟计算分散到多个核心上。相比之下,《鸣潮》的任务图工作线程仅占 7% 甚至更低。

还有一个值得注意的重要行为差异:《鸣潮》的工作线程呈现出锯齿状/突发模式,而非持续负载。它们以短时突发的方式激活,快速完成任务,然后停滞等待主线程提供更多工作或解决同步点。这并非缺乏并行能力,而是一个协调瓶颈:系统确实可以使用更多核心,但未能以支持持续并行执行的方式被供给工作。这种表述比简单地说“更多核心没有帮助”更加精确——准确的说法是,额外的核心无法加速那些在协调点上仍保持串行化的工作。


移动端《鸣潮》的每线程负载情况:游戏线程和渲染线程承担了绝大部分工作,任务树工作线程虽处于活跃状态,但贡献极小。V8 默认工作线程实例表明 Puerts/V8 运行时处于活动状态。来源:@xLOCKnLOADx

架构比较——《鸣潮》 vs 《明日方舟:终末地》:


@xLOCKnLOADx 直接总结了实际含义:“基本上全是主线程,他提到的那些线程实际上并没有像《终末地》这样的游戏做那么多工作。如果一颗8核精英芯都卡得这么厉害,你可以想象低端硬件会有多挣扎。”

额外的高端 PC 数据点: 社区成员 @Phantom_Tempest 在 Ryzen 9 7900X + RTX 5070 Ti 配置上进行了测试,设置为 4K 超高画质、开启光追、关闭帧生成。结果:GPU 利用率约 100%,总 CPU 利用率约 18%,各核心负载极不均衡,工作线程呈现突发/锯齿状行为。关键是,将分辨率从 4K 依次降至 1080p 和 720p 后,帧率提升甚微——这证实了 GPU 处于瓶颈的下游环节,而非瓶颈本身。这一现象与跨多个硬件档次和分辨率目标下的“协调瓶颈”模型一致。注:在 4K 下开启重负载光追时,GPU 自然会接近满载——相关观察在于:降低分辨率并未显著提升帧率,从而将 CPU 协调路径确定为限制因素。

问:那么《鸣潮》是需要更多 CPU 核心,还是更高的主频?

—— 致谢:@eggsee(《鸣潮》移动端配置作者)提供了客户端日志证据,证实了《鸣潮》的 TaskGraph 线程组结构,从而在 1.1 节中给出了更准确的线程模型描述。原先“单一 GameThread 通道”的说法过于简化了。

密集城区的另一个影响因素——Mip 流送竞争:

社区成员 @eggsee 还指出了导致星源学院等区域出现卡顿的第二种机制:纹理 Mip 流送竞争。即便是被遮挡的几何体——例如墙壁后面、玩家视野之外——仍然会被流送加载,与可见内容争夺 Mip 流送带宽。这会堵塞 Mip 流送管线,迫使 CPU 消耗游戏渲染预算来管理那些当前帧根本不会被看到的几何体的资源请求。

以下是翻译:

具体到移动端,这一问题会与游戏渲染的协调开销叠加,同时增加 GPU 帧时间——这就是为什么在某些移动端性能追踪中,CPU 和 GPU 的利用率数据有时会与预期模式不符。

一个实际影响: 在新区域停留一段时间后,卡顿通常会有所改善——一旦着色器完全编译、流送系统趋于稳定。这与“流送预热效应”相符,而非单纯的静态 GameThread 上限——上限确实存在,但其严重程度会随流送状态而变化。

以下是翻译:

答案比(单纯增加核心数或提高主频)两者都要微妙。原始时钟频率和 IPC(每时钟周期指令数)固然重要,但具体到《鸣潮》,对 CPU 提升效果最显著的往往是大容量 L3 缓存——而其原因正直接源于 1.3 节所描述的架构问题。

以下是翻译:

简而言之: GameThread 的工作涉及不断访问散布在 RAM 中的数据。更大的 L3 缓存能让更多数据保留在离 CPU 更近的地方,从而减少每次访问时的等待延迟。这就是为什么 AMD 的 X3D 系列 CPU(在芯片上堆叠了额外 L3 缓存)往往能带来比那些频率更高但缓存更小的 CPU 明显更流畅的《鸣潮》体验——即使后者的平均帧数在纸面上更高。一颗 Ryzen 5700X3D 的平均帧率可能低于 i7-12700KF,但在城区场景中的 1% 低帧率和帧时间一致性很可能会显著更好。

以下是翻译:

实际结论: 如果你玩《鸣潮》时感到卡顿,且 GPU 利用率低于 80%——那么最能解决问题的升级选择是配备大容量 L3 缓存的 CPU,而不是换一块新显卡,并就此调整预期。

所以这边决定全搬过来并进行中英对照,我倒要看看这b肚子里藏着多少花花肠子

5 个赞

1.3 OOP、缓存未命中以及为什么引擎会自相矛盾

原文

There is a deeper problem underneath the GameThread bottleneck. It is not just that everything runs on one thread — it is how that thread accesses data when it runs.

Unreal Engine 4 is built around an Object-Oriented Programming (OOP) model. This is not an informal observation — it is the documented design philosophy of the engine. Epic’s official programming documentation describes UE4 as built around a class hierarchy where UObject is the base for all engine objects and AActor is the base class for everything placeable in a game world, with components attached to actors to define their behavior.

Sources: UE4 Programming Basics · OOP Principles in Unreal Engine

In practice, this means every entity in the game world — every NPC, building facade, particle emitter, tree, physics prop — is an AActor object. Each AActor has a vtable pointer, owns a list of UComponent objects, and those components are heap-allocated individually, sitting wherever the memory allocator placed them at creation time.

In simple terms: every object lives in its own corner of RAM. To update it, the CPU has to go find it.

Imagine the GameThread needs to process logic ticks for a large number of active actors in a single frame — NPCs with behavior trees, buildings with interaction handlers, physics props, particle systems — which happens constantly in a scene as dense as Startorch Academy or Septimont City/Ragunna. UE4 does have systems like HISM, HLOD to reduce rendering load, but these don’t reduce the logic tick burden on the GameThread — behavior trees still need to execute, interaction logic still needs to evaluate, scripting ticks still run, regardless of whether the actor is being rendered at full detail or not. The CPU’s work on the GameThread looks like this:

Tick ActorA → follow pointer → find ComponentList → follow pointer → find data
              [CPU fetches from RAM: cache miss]
Tick ActorB → follow pointer → find ComponentList → follow pointer → find data
              [CPU fetches from RAM: different address, cache miss again]
Tick ActorC → [different address again]
Tick ActorD → [different address again]
...10,000 times

Every single actor tick involves pointer-chasing through memory. When the CPU looks for data not already in its L1 or L2 cache, it stalls waiting for main RAM to respond. On a modern CPU, an L1 cache hit costs ~4 cycles. A main memory fetch costs up to ~200 cycles in the worst case — though in practice many accesses hit L2 or L3 rather than going all the way to RAM, and CPUs use prefetching to mitigate some of this. The actual penalty depends on access patterns, data locality, and cache hit rates. Nevertheless, in a scene with thousands of actors updating per frame, the aggregate impact of cache-unfriendly access patterns is real and measurable — the CPU spends meaningful time waiting rather than computing, even if not every access is a worst-case miss.

A note on UE4’s Binned Allocator: UE4’s default memory allocator (Binned/Binned2) is sophisticated — it uses a tiered allocation system that pools objects by size class and is designed to maximize L1/L2 cache utilization for individual allocations. This meaningfully reduces heap fragmentation and improves per-allocation cache behavior compared to naive allocators. However, it does not connect cache lines between different actors — the pointer-chasing traversal pattern described above persists regardless of allocator strategy, because actors that need to be processed together are not guaranteed to be co-located in memory. The allocator mitigates the problem at the micro-allocation level; the OOP traversal pattern is the macro-level issue. — Clarification credit: @aizen76 (Indie-us Games, UE specialist)

A note on Actor Clustering: UE4 has an Actor Clustering feature, but its function is specific and worth clarifying. Actor Clustering helps skip random-access pointer searches during UE4’s garbage collection pass — reducing GC-induced cache miss overhead. It is not a Tick cache locality system and does not improve the per-frame logic tick access pattern described above. Its benefit is therefore more relevant to GC-related pauses (Pattern B territory) than to the per-frame logic processing bottleneck (Pattern A). — Clarification credit: @aizen76
A useful mental model: imagine a librarian who needs to look up 10,000 books. In a well-organized library (DOD/ECS), books on the same topic are shelved together — she grabs one section and works through it sequentially. In UE4’s model, each book was shelved wherever it happened to fit when it arrived. She walks across the entire building for every single book. The reading is the same amount of work. The walking is what kills performance.

The L3 cache is a small room next to her desk where she keeps recently-used books nearby. A bigger room (more L3 cache) means fewer trips across the building. This is exactly why X3D processors — with their stacked cache — help specifically with WuWa’s workload where other games don’t see the same benefit.

This is exactly why a large L3 cache matters more for WuWa than raw clock speed. The L3 cache acts as a buffer between the CPU cores and main RAM — the larger it is, the more actor data can sit nearby instead of requiring a full round-trip to RAM. A processor with a 96MB L3 cache (like AMD’s X3D lineup) can keep far more of WuWa’s scattered actor data resident than a processor with 20MB or 30MB, even if the smaller-cache CPU has higher clock speeds. The result in practice: lower average FPS on benchmarks, but meaningfully better 1% lows and frame consistency in city areas — which is exactly where the problem lives.

This is not a problem Kuro introduced. It is a structural consequence of how UE4’s object model works. The engine was designed this way, and there is no mechanism within UE4 to change it without rebuilding the entity system from scratch. A developer in the Unreal Engine community forum observed this limitation directly back in 2018:
“For large scale entity simulation, UE4 will likely not be a great tool choice here. As far as I can tell, none of the large engines are particularly well suited for that type of large scale simulation though.”

Source: https://www.reddit.com/r/unrealengine/comments/9uzkpk/

That last clause — none of the large engines — was accurate at the time. Since then, two studios have changed the equation — not by choosing a better engine, but by investing heavily enough to transform the one they had.

Why Genshin Impact and Arknights Endfield Feel Different
The Genshin comparison comes up constantly in this community, and it is worth addressing properly rather than dismissing.

Genshin Impact runs on Unity. But the reason Genshin handles entity density more gracefully is not “Unity is better than UE4” — it is the result of HoYoverse committing a decade of R&D investment into a heavily customized engine that bears the name Unity but operates very differently from the stock version any other studio would ship.

HoYoverse’s previous major title, Honkai Impact 3rd, was built on Unity and ran for years at scale. That experience gave them two things Kuro didn’t have when starting WuWa: a team with deep Unity expertise, and years to understand how to push Unity’s architecture toward data-oriented patterns before the hard limits became visible. By the time Genshin launched, they were not shipping Unity — they were shipping HoYoverse’s Unity, a heavily modified engine reflecting a decade of institutional investment in custom systems, tooling, and DOD-oriented architecture.

Similarly, Arknights Endfield is not a stock Unity game — Hypergryph has invested substantially in customizing their Unity pipeline. They benefit from being able to leverage official DOTS/ECS infrastructure (which Unity only shipped for production use from 2022 onward) while also building on their own engine expertise.

The key point: the credit belongs to Hoyo and Hypergryph’s engineering investment, not to Unity as a product. A studio shipping stock Unity today would not automatically achieve Genshin’s entity handling. Stock Unity is also OOP-heavy in its classic form. The performance characteristics we observe in these titles are the result of what those studios built on top of the engine — not the engine itself.

Unity’s component-based design is architecturally closer to DOD than UE4’s inheritance-heavy AActor model — it is a more favorable starting point. But “more favorable starting point” and “better engine” are not the same thing. The distance between stock Unity and what Genshin actually runs on is enormous, and that distance represents years of engineering work that no other studio can simply acquire by choosing Unity.

OOP approach (UE4 AActor):
  Actor_A.Position → address 0x1A3F00  (somewhere in heap)
  Actor_B.Position → address 0x7C2104  (completely different location)
  Actor_C.Position → address 0x4E8830  (different again)
  → CPU cache: evicted and refilled on every single actor tick

DOD/ECS approach (Unity DOTS / custom):
  PositionArray: [ A.pos | B.pos | C.pos | D.pos | E.pos | ... ]
                   ↑ single contiguous memory block
  → CPU loads one cache line, processes several entities, moves to next
  → Cache stays warm for the entire batch

Note: The memory addresses above (0x1A3F00, etc.) are illustrative examples only, not actual values from any real process.

Source: https://unity.com/ecs

Unity’s C# Job System makes the thread-safety implications of this data layout explicit. From Unity’s own documentation:

“To make it easier to write multithreaded code, the Unity C# Job System detects all potential race conditions and protects you from the bugs they can cause.” “The C# Job System solves this by sending each job a copy of the data it needs to operate on, rather than a reference to the data in the main thread.”

Source: https://docs.unity3d.com/2020.1/Documentation/Manual/JobSystemSafetySystem.html

This is the architectural reason why Unity’s Job System can safely dispatch work to worker threads. UE4’s AActor graph has no equivalent — actors share references, making safe multi-threading on arbitrary game logic nearly impossible without architectural surgery. But accessing this capability in a game like Genshin required Hoyo to build much of the infrastructure themselves before official support existed.

An honest caveat: Genshin has a significantly lower visual ceiling than WuWa. The trade runs both ways. DOD-friendly architecture comes with real constraints on content authoring and system interaction. And Hoyo had years of prior work and institutional runway that Kuro simply did not have when starting WuWa on a new engine with a new team.

There is no ECS or DOD framework in UE4. Unreal Engine 5 does improve the threading situation somewhat — Mass Entity offers limited large-scale entity simulation support — but the fundamental OOP heritage remains deeply embedded. WuWa predates UE5 and is not built on it.

我是机翻

在 GameThread 瓶颈之下有一个更深层次的问题。不仅仅是所有事情都在一个线程上运行——而是该线程在运行时如何访问数据。

虚幻引擎 4 是围绕面向对象编程模型构建的。这并非非正式观察——这是引擎的设计哲学。Epic 的官方编程文档将 UE4 描述为构建在一个类层次结构上,其中 UObject 是所有引擎对象的基类,Actor 是游戏中所有可放置物件的基类,组件附加到 actor 上以定义其行为。

来源:UE4 Programming Basics · OOP Principles in Unreal Engine

在实践中,这意味着游戏世界中的每个实体——每个 NPC、建筑物立面、粒子发射器、树木、物理道具——都是一个 Actor 对象。每个 Actor 都有一个虚函数表指针,拥有一个 Component 对象列表,而这些组件是在堆上单独分配的,位于内存分配器在创建时放置它们的任何位置

简单来说:每个对象都住在自己那片 RAM 角落里。为了更新它,CPU 必须去找到它。

想象一下,GameThread 需要在一帧内为大量活动 Actor 处理逻辑 Tick——在 Startorch Academy 或 Septimont City/Ragunna 这样密集的场景中,这种情况经常发生。UE4 确实有 HISM、HLOD 等系统来减少渲染负载,但这些并不能减少 GameThread 上的逻辑 Tick 负担——行为树仍需执行,交互逻辑仍需评估,脚本 Tick 仍在运行,无论 Actor 是否以全细节渲染。CPU 在 GameThread 上的工作看起来像这样:

Tick ActorA - 跟随指针 - 找到 ComponentList - 跟随指针 - 找到数据 [CPU 从 RAM 读取:缓存未命中]
Tick ActorB - 跟随指针 - 找到 ComponentList - 跟随指针 - 找到数据 [CPU 从 RAM 读取:不同地址,再次缓存未命中]
Tick ActorC - [又是不同地址]
Tick ActorD - [又是不同地址]
… 重复 10,000 次

每个 Actor Tick 都是一次穿越分散内存的指针追逐。每次 CPU 查找不在其 L1 或 L2 缓存中的数据时,它就会停顿并等待主 RAM 响应。在现代 CPU 上,L1 缓存命中约需 4 个周期。主内存读取约需 200 个周期。在拥有数千个 Actor 的场景中,这种惩罚累积成真实且可测量的帧时间损失——不是因为游戏逻辑慢,而是因为 CPU 大部分时间在等待而非计算。

关于 UE4 的分箱分配器的说明:UE4 的默认内存分配器(Binned/Binned2)是复杂的——它使用分层的分配系统,按大小类别对对象进行池化,旨在最大化单个分配的 L1/L2 缓存利用率。这相比幼稚的分配器有用地减少了堆碎片并改善了每个分配的缓存行为。然而,它并不连接不同 Actor 之间的缓存行——上述指针追逐遍历模式无论分配器策略如何都持续存在,因为需要一起处理的 Actor 不能保证在内存中相邻。分配器在微观分配层面缓解了问题;OOP 遍历模式是宏观层面的问题。—— 澄清致谢:@aizen76(Indie-us Games,UE 专家)

关于 Actor 聚簇的说明:UE4 有一个 Actor Clustering 功能,但其功能值得明确。Actor Clustering 有助于在 UE4 的垃圾回收过程中跳过随机访问指针搜索——减少 GC 引起的缓存未命中开销。它不是一个 Tick 缓存局部性系统,也不改进上述每帧逻辑 Tick 访问模式。因此,它的好处更多地与 GC 相关的暂停(模式 B 领域)相关,而不是与每帧逻辑处理瓶颈(模式 A)相关。—— 澄清致谢:@aizen76

一个有用的心智模型:想象一个图书管理员需要查阅 10,000 本书。在一个组织良好的图书馆(DOD/ECS)中,主题相同的书放在一起——她拿过一个区域,按顺序工作。在 UE4 的模型中,每本书都是在到达时随便找了个有空的地方放下的。她为每一本书都要穿过整个图书馆。阅读量是相同的。走路才是杀死性能的东西。
L3 缓存是她桌子旁边的一个小房间,用来存放最近用过的书。更大的房间(更多的 L3 缓存)意味着减少穿越大楼的次数。这正是为什么 X3D 处理器——具有堆叠缓存——特别有助于解决《鸣潮》的工作负载,而其他游戏则没有同样的好处。

这正是为什么大容量 L3 缓存对《鸣潮》比原始时钟速度更重要。L3 缓存充当 CPU 核心和主 RAM 之间的缓冲区——它越大,就越多的 Actor 数据可以坐在附近,而不需要往返 RAM。一个拥有 96MB L3 缓存的处理器(如 AMD 的 X3D 系列)可以让《鸣潮》分散的 Actor 数据中更多部分保持驻留,而一个只有 20MB 或 30MB 缓存的处理器则做不到,即使后者有更高的时钟速度。实际结果是:基准测试中的平均 FPS 较低,但在城市地区——问题所在——的 1% 低帧率和帧一致性要好得多。

这不是库洛引入的问题。这是 UE4 对象模型的结构性后果。引擎就是这样设计的,UE4 内部没有任何机制可以改变这一点而不从头重建实体系统。一位开发者早在 2018 年就在虚幻引擎社区论坛中直接观察到了这个限制:

“对于大规模实体模拟,UE4 很可能不是一个好的工具选择。据我所知,没有一个大型引擎特别适合那种大规模模拟。”
来源:https://www.reddit.com/r/unrealengine/comments/9uzkpk/

最后那句——“没有大型引擎”——在当时是准确的。从那时起,其中一个已经发生了显著变化。

为什么《原神》、《明日方舟:终末地》感觉不同

《原神》的比较在这个社区中不断出现,值得妥善处理而不是置之不理。

《原神》运行在 Unity 上。但《原神》能更优雅地处理实体密度的原因并不仅仅是“Unity 更好”——而是米哈游对 Unity 架构进行了深入、长期的投资,这早在《原神》概念形成之前就开始了。

米哈游的前作《崩坏3》就是基于 Unity 构建的,并大规模运行了多年。这段经历给了库洛在开始制作《鸣潮》时所没有的两样东西:一个拥有深厚 Unity 专业知识的团队,以及在硬性限制显现之前了解如何将 Unity 的架构推向数据导向模式的时间。

Unity 的基本设计——一个基于组件的系统,实体拥有组件而非继承行为——在架构上比 UE4 继承重量级的 Actor 模型更接近 DOD。它不是 ECS,但它是垫脚石。米哈游有多年《崩坏3》的开发经验,可以构建自定义系统、工具和制度知识,将这种架构推向缓存友好、并行友好的模式。到《原神》发布时,他们已经在使用一个高度定制的 Unity,反映了近十年的这种投资。

Unity 直到 2022 年才开始正式将其 DOTS/ECS 框架用于生产——这个概念源于 2018 年的研究——那时《原神》早已发布。像《明日方舟:终末地》这样的新作仍然使用高度修改的 Unity,但它们开发的时代已经存在官方的 ECS 支持——这意味着它们可以直接利用 Unity 自己的数据导向基础设施,而不必像米哈游在《原神》中那样从头构建。
OOP 方法(UE4 Actor):

Actor_A.Position → 地址 0x1A3F00(堆中的某处)
Actor_B.Position → 地址 0x7C2104(完全不同的位置)
Actor_C.Position → 地址 0x4E8830(再次不同)
→ CPU 缓存:对每个 Actor Tick 都被驱逐和重新填充


DOD/ECS 方法(Unity DOTS / 自定义):

PositionArray: [A.pos | B.pos | C.pos | D.pos | E.pos | … ]
↑ 单个连续内存块
→ CPU 加载一条缓存行,处理多个实体,移动到下一行
→ 缓存在整个批次中保持温暖

注意:上面的内存地址(0x1A3F00 等)仅为说明性示例,并非来自任何真实进程的值。

来源:https://unity.com/ecs

Unity 的 C# Job System 使这种数据布局的线程安全性含义变得明确。根据 Unity 自己的文档:

“为了更容易编写多线程代码,Unity C# Job System 会检测所有潜在的竞争条件,并保护你免受它们可能引起的错误的影响。”
“C# Job System 通过向每个 Job 发送一份它需要操作的数据的副本(而不是主线程中数据的引用)来解决这个问题。

来源:https://docs.unity3d.com/2020.1/Documentation/Manual/JobSystemSafetySystem.html

这就是为什么 Unity 可以安全地将物理和模拟工作分派给工作线程的架构原因:每个 Job 接收一个隔离的数据副本,消除了共享状态的风险。UE4 的 AActor 图没有等效的机制——actors 共享引用,使得在没有架构手术的情况下对任意游戏逻辑进行安全多线程几乎不可能。

数据所有权模型也改变了并行性的可能性。连续的组件数组可以干净地交给工作线程。UE4 互联的对象图则不能。这就是为什么《原神》的系统可以跨核心分配工作,而《鸣潮》的工作仍然串行在一个线程上。

一个诚实的警告在这里:与原神相比,《鸣潮》的视觉上限要高得多。这种权衡是双向的。DOD 友好的架构对内容创作和系统交互带来了实际限制。米哈游有多年先期工作和有利的引擎基础可以依赖。而库洛正在构建他们的第一个开放世界 3D 项目,使用的是本质上是 OOP 的实体模型的引擎,没有那种制度性的缓冲。

UE4 中没有 ECS 或 DOD 框架。虚幻引擎 5 确实在一定程度上改善了线程状况——Mass Entity 提供了有限的大规模实体模拟支持,UE5 的渲染管线比 UE4 有更好的多线程利用率——但引擎的 OOP 传统仍然存在。AActor、UObject 和组件模型深深嵌入在 UE5 的工作方式中。使用 UE5 上限会适度提高,但不会消失。《鸣潮》早于 UE5 且并非基于 UE5 构建,所以无论如何这都是学术性的。

当你看到这句话的时候,说明至冬前瞻的200原石已经过期辣哈哈哈哈哈哈

你小可爱的老娘看到与原神相比,《鸣潮》的视觉上限要高得多这句就绷不住了,别的不说,八月见

吃饭,吃完继续

2.脚本层及其代价
2.1什么是 Puerts以及他为什么存在于此

原文

Somewhere between UE4’s C++ foundation and the gameplay logic you interact with, WuWa has a scripting layer. This is no longer a matter of inference — it is confirmed by direct evidence from the game’s own runtime logs and binary.

Direct evidence — runtime logs:

WuWa writes diagnostic output to Client\Saved\Logs\Client.log on every launch. The following entries appear consistently across multiple log backups (Client-backup-*.log), ruling out a one-time fluke:

[Client.log line 2564]  启动v8 V8ThreadHelper
[Client.log line 2565]  PuertsJsEnv: ... v8 version: header-11.8.172.18, lib-11.8.172.18
[Client.log line 2566]  LoadModule: Puerts

The game is explicitly initializing a V8 thread helper, loading the Puerts module, and reporting its V8 version at startup. This is as direct as runtime evidence gets without a full profiler.

Direct evidence — binary strings:

String extraction from Client\Binaries\Win64\Client-Win64-Shipping.exe yields the following identifiers embedded in the binary:

Puerts
PuertsJsEnv
typescript
TypeScriptGeneratedClass
KuroPuerts
puerts/*.js
/Game/Aki/TypeScript/...   (multiple paths)

The KuroPuerts identifier in particular suggests a Kuro-specific fork or customization of the Puerts framework, not a stock integration.

Datamined configuration evidence:

This is consistent with what the Arikatsu/WutheringWaves_Data repository shows in terms of content pipeline structure:

BinData/level_entity/          ← legacy TypeScript path (present v1.0 onward)
BinData/prefab/                ← legacy TypeScript path (present v1.0 onward)
BinData/level_entity_csharp/   ← new C# path (first appears v2.8)
BinData/prefab_csharp/         ← new C# path (first appears v3.0)

And confirmed in aki_base.csv, the game’s master database manifest:

v1.0 through v2.7:  zero entries mentioning "csharp"

v2.8:  LevelEntityForCSharpConfig,  db_level_entity_csharp.db   ← first migration signal
v3.0:  PrefabForCSharpConfig,       db_prefab_csharp.db         ← expanded to prefab system
v3.1–v3.2:  same two entries, infrastructure stable

On accessing pak contents:

While Kuro rotates AES keys per release, the community project at https://github.com/ClostroOffi/wuwa-aes-archive maintains an updated archive of keys across versions, making pak content accessible via tools like FModel. Inspection of unpacked assets through this pipeline reveals the ScriptAssemblies directory under Client/Content/Aki/, containing C# runtime assemblies including CSharpScript.dll, Microsoft.CSharp.dll, mscorlib.dll, and numerous System.* assemblies — confirming the C# runtime environment is fully deployed alongside the TypeScript/Puerts layer.

The directories exist. The database entries exist. The log entries confirm active use at runtime. The Puerts/V8 claim is no longer circumstantial — it is directly confirmed.

Q: Why did Kuro choose a TypeScript scripting layer if it affects performance?

Because at the time WuWa’s development was getting started, the priority was development velocity, and a scripting VM layer delivers exactly that.

Kuro’s previous major project was Punishing: Gray Raven — built on Unity, not UE4. Shifting to Unreal Engine for WuWa meant the team was already navigating an unfamiliar engine. A scripting layer like Puerts addresses a real practical problem: game designers and gameplay engineers can write and iterate on logic in TypeScript without touching C++ or rebuilding the engine. This matters more than it sounds — native C++ development means recompiling the entire source tree for every debug iteration or new function implementation, which is extremely time-consuming on a large project. Hot reload works. Iteration cycles are shorter. The cost is runtime performance at scale — but that cost is invisible early in development when the game is small.

This is the classic live-service bootstrap tradeoff: buy development velocity now, pay the performance cost later. The engineering team almost certainly understood the risk — a scripting VM accumulating GC pressure at scale is not an obscure edge case. But when velocity is non-negotiable and the alternative is slower iteration on an unfamiliar engine, this tradeoff becomes a forced choice, not a mistake. The debt was knowingly taken on. The migration is the repayment.

我是机翻

在 UE4 的 C++ 基础层与你实际交互的游戏逻辑层之间,《鸣潮》存在一个脚本层。这已不再是推测——而是由游戏自身的运行时日志和二进制文件中的直接证据所证实。

直接证据——运行时日志:

《鸣潮》每次启动时会将诊断信息写入 Client\Saved\Logs\Client.log 。以下条目在多个日志备份文件(Client-backup-*.log )中稳定出现,排除了一次性偶然情况的可能:

text

[Client.log line 2564] 启动v8 V8ThreadHelper [Client.log line 2565] PuertsJsEnv: … v8 version: header-11.8.172.18, lib-11.8.172.18 [Client.log line 2566] LoadModule: Puerts

游戏在启动时显式初始化了一个 V8 线程辅助器,加载了 Puerts 模块,并上报了其 V8 版本。这已经是无须完整性能剖析工具就能得到的最直接的运行时证据。

直接证据——二进制文件中的字符串:

Client\Binaries\Win64\Client-Win64-Shipping.exe 中提取字符串,可以得到以下嵌入在该二进制文件中的标识符:

  • Puerts
  • PuertsJsEnv
  • typescript
  • TypeScriptGeneratedClass
  • KuroPuerts
  • puerts/*.js
  • /Game/Aki/TypeScript/... (多个路径)

尤其是 KuroPuerts 这一标识符,表明这是 Kuro 对 Puerts 框架的定制或分叉版本,而非原样集成。

数据挖掘得到的配置证据:

这与 Arikatsu/WutheringWaves_Data 仓库中展示的内容管线结构一致:

  • BinData/level_entity/ ← 旧版 TypeScript 路径(v1.0 起存在)
  • BinData/prefab/ ← 旧版 TypeScript 路径(v1.0 起存在)
  • BinData/level_entity_csharp/ ← 新版 C# 路径(v2.8 首次出现)
  • BinData/prefab_csharp/ ← 新版 C# 路径(v3.0 首次出现)

并且在游戏的 Master 数据库清单 aki_base.csv 中也能得到确认:

  • v1.0 至 v2.7:没有任何包含 csharp 的条目
  • v2.8:出现 LevelEntityForCSharpConfigdb_level_entity_csharp.db ← 第一次迁移信号
  • v3.0:出现 PrefabForCSharpConfigdb_prefab_csharp.db ← 扩展到 prefab 系统
  • v3.1–v3.2:以上两个条目保持不变,基础设施趋于稳定

关于访问 pak 内容:

虽然 Kuro 每个版本都会更换 AES 密钥,但社区项目 https://github.com/ClostroOffi/wuwa-aes-archive 持续维护着各版本的密钥归档,使得通过 FModel 等工具访问 pak 内容成为可能。通过对解包资源的检查,可以在 Client/Content/Aki/ 下找到 ScriptAssemblies 目录,其中包含 C# 运行时程序集,包括 CSharpScript.dllMicrosoft.CSharp.dllmscorlib.dll 以及多个 System.* 程序集——这证实了 C# 运行时环境与 TypeScript/Puerts 层是同时完整部署的。

目录存在。数据库条目存在。日志条目确认了其在运行时的活跃使用。Puerts/V8 的声称不再是间接推论——它已被直接证实。


问:如果 TypeScript 脚本层影响性能,Kuro 为什么还要选择它?

因为《鸣潮》项目开始开发的时候,首要关注点是开发效率,而脚本 VM 层正好能带来这种效率。

Kuro 上一个主要项目是《战双:帕弥什》——基于 Unity,而非 UE4。转向虚幻引擎开发《鸣潮》意味着团队已经在应对一个并不熟悉的引擎。采用像 Puerts 这样的脚本层解决了一个切实的问题:游戏策划和玩法工程师可以用 TypeScript 编写和迭代逻辑,无需触碰 C++ 或重新编译引擎。这一点比听起来更重要——原生 C++ 开发意味着每次调试迭代或新增功能都要重新编译整个源码树,在大型项目上极其耗时。热重载是可行的。迭代周期更短。代价是运行时的性能开销——但在项目早期、游戏规模尚小时,这个代价是看不见的。

这就是典型的“长线服务启动阶段权衡”:先用开发效率换时间,后期再偿还性能债务。工程团队几乎肯定理解这个风险——脚本 VM 在大规模下积累 GC 压力并非什么边缘场景。但当效率是不可妥协的硬要求,而替代方案是在不熟悉的引擎上做更慢的迭代时,这个权衡就变成了被迫的选择,而不是错误。这笔技术债务是在明知后果的情况下背负的。当前的迁移就是偿还。

2.2 V8 的 GC 问题 —— 以及模式 B

原文

V8 has two garbage collectors running in tandem. The Minor GC (Scavenger) handles short-lived objects in the young generation — cheap, frequent, mostly invisible. The problem is the Major GC (Mark-Compact), which runs when long-lived objects have accumulated enough to fill the old generation heap.

The default approach, as V8’s own engineering blog describes it:

“A straight-forward approach is to pause JavaScript execution and perform each of these tasks in sequence on the main thread. This can cause jank and latency issues on the main thread, as well as reduced program throughput.”

Source: https://v8.dev/blog/trash-talk

The blog also identifies the specific scenario that makes major GC expensive — exactly the scenario WuWa creates when transitioning from a dense area to open world:

“One potential weakness of a garbage collector which copies surviving objects is that when we allocate a lot of long-living objects, we pay a high cost to copy these objects.”

Source: https://v8.dev/blog/trash-talk

In Startorch Academy or Septimont, JavaScript objects are being created constantly: NPC behavior trees, quest trigger states, building interaction handlers, UI update callbacks. Many survive long enough to be promoted to the old generation heap. When the player leaves the area, those objects become unreachable — but V8 doesn’t know that until the major GC runs.

Think of V8’s garbage collector like a cleaning crew that only comes when called — but when they arrive, they lock the entire building until they’re done. While you’re inside Startorch Academy, clutter accumulates everywhere (an object created for every NPC tick, every quest state update, every interaction). When you walk out, the crew decides now is the time to clean. Everything freezes until they finish, then resumes normally.

The v3.1 streaming update improved how quickly the building empties out when you leave. It didn’t change how the cleaning crew operates. The TS→C# migration is what replaces the cleaning crew itself with one that works incrementally — no lockout required.

This is the mechanism behind Pattern B — the hard freeze when leaving a city or when a large volume of geometry is unloaded at once. The trigger is not limited to city boundaries specifically; any transition where a large pak unload and accumulated GC pressure coincide can produce the same event. Dense city exits are just the most consistent and predictable trigger. It is a compound event:

Player crosses city boundary
        │
        ├─► UE4 asset streaming: unload city pak data
        │   Memory deallocation, I/O flush
        │   → Streaming spike
        │
        └─► V8 major GC: heap has been filling
            during entire city visit
            → Stop-the-world pause fires at transition
            
Both simultaneously → 0 FPS freeze 500ms–2s

An additional contributing factor during boundary transitions is **on-demand shader compilation**. In open-world games, shaders are frequently compiled on first encounter with new materials or effects — and boundary transitions often introduce new asset types requiring new shader variants. When shader compilation coincides with streaming flush and GC pressure, it can amplify the compound event further. The severity is highly hardware-dependent: systems with stronger single-thread CPU performance and larger caches resolve shader work faster, reducing visible impact.

Modern V8 (the “Orinoco” collector) has improved this with concurrent collection, but acknowledges the limits:

“The advantage here is that the main thread is totally free to execute JavaScript — although there is minor overhead due to some synchronization with helper threads.”

Source: https://v8.dev/blog/trash-talk

Even with Orinoco, the major GC cycle cannot be fully offloaded. In a game engine where the GameThread is already under city load, the compound timing of streaming flush and GC cycle is what produced the severe 0 FPS freezes that long-time players remember.

Why Pattern B has improved but not disappeared: Kuro’s v3.1 streaming pipeline update (“updated the loading pipeline and accelerating data streaming”) addressed the asset streaming component of the compound event. The GC component remains — because that requires migrating the scripting layer away from V8, which is precisely what the TS→C# migration targets. The partial improvement is exactly what you would expect from fixing one of two concurrent causes.

This is the spike that appears with no corresponding GPU load change, no temperature change, nothing on screen to explain it.

I have two benchmark captures that together tell this story clearly.

Startorch Academy session — traversal including a motorbike exit from the academy toward the open world. Worst single frame: 175ms, against a P99.9 of 77ms. That outlier sits far outside the normal distribution tail. At 58% average GPU utilization, the GPU was not the source of a 175ms pause. The profile is consistent with a V8 major GC stop-the-world cycle firing at the area transition.

Huanglong session — same hardware, same settings. During this session, I deliberately flew out of the city boundary at maximum speed using the glider. Worst single frame: 245ms. This was not an organic stutter — it was a controlled trigger of Pattern B. The faster and more abrupt the boundary crossing, the more severe the compound event: streaming flush and GC fire simultaneously with less time between them to stagger the load.

The two numbers together are useful. 175ms organic (Startorch) vs 245ms deliberate rapid exit (Huanglong) shows both the floor and the ceiling of Pattern B severity. Normal traversal produces the 175ms case. Aggressive boundary crossing produces the 245ms case. Earlier versions of the game, before v3.1’s streaming pipeline improvements, likely produced even higher values because the streaming flush component was larger.

Caveat on both spikes: I cannot confirm these are V8 GC events without scripting VM profiler access. The pause profile, magnitude, and absence of GPU correlation are consistent with V8 major GC behavior. I am calling this inferred, not confirmed.

我是机翻

V8 有两个垃圾回收器并行运行。次要 GC(Scavenger)处理新生代中的短生命周期对象 —— 成本低、频率高、大多不可见。问题在于主 GC(Mark-Compact,标记-压缩),它在长生命周期对象积累到足以填满老生代堆时运行。

V8 官方工程博客对此的默认做法描述如下:

“一种直接的方法是在主线程上暂停 JavaScript 执行,按顺序执行上述各项任务。这会导致主线程上的卡顿和延迟问题,并降低程序吞吐量。”

来源:https://v8.dev/blog/trash-talk

该博客还明确了使主 GC 成本高昂的具体场景 —— 恰好就是《鸣潮》在从密集区域切换到开放世界时所创建的场景:

“复制存活对象的垃圾回收器的一个潜在弱点是:当我们分配大量长生命周期对象时,复制这些对象的成本会很高。”

来源:https://v8.dev/blog/trash-talk

在「始源学院」或「Septimont」(注:游戏内地点)中,JavaScript 对象在持续不断地创建:NPC 行为树、任务触发状态、建筑交互处理器、UI 更新回调。其中许多对象存活足够久,被提升到老生代堆中。当玩家离开该区域时,这些对象变得不可达 —— 但 V8 要等到主 GC 运行时才知道这一点。

可以把 V8 的垃圾回收器想象成一个清洁团队,只在被呼叫时才来 —— 但他们一旦到达,就会锁住整栋楼直到工作完成。当你在始源学院内部时,杂物会在各处堆积(每个 NPC 的 tick、每个任务状态更新、每次交互都会创建一个对象)。当你走出学院时,清洁团队决定现在正是打扫的时机。一切都会冻结,直到他们完成,然后恢复正常运行。

v3.1 的流式加载更新改善了“你离开时建筑清空的速度”,但并未改变清洁团队的运作方式。TS → C# 的迁移所做的,正是用一个可以增量工作的清洁团队来替换原有的清洁团队 —— 不再需要锁死。

这就是模式 B 背后的机制 —— 离开城市或一次性卸载大量几何体时出现的硬冻结。触发条件并不仅限于城市边界;任何大量 pak 卸载与 GC 压力累积同时发生的过渡都可能产生同样的事件。密集城市出口只是最稳定、最可预测的触发器。这是一个复合事件:

text
玩家穿过城市边界

├─► UE4 资产流式加载:卸载城市 pak 数据
│ 内存释放、I/O 刷新
│ → 流式加载尖峰

└─► V8 主 GC:在整个城市停留期间
堆内存一直在填充
→ 停止世界(stop-the-world)暂停在过渡时触发

两者同时发生 → 0 FPS 卡顿 500ms–2s
边界切换期间的另一个影响因素是按需着色器编译。在开放世界游戏中,首次遇到新材质或特效时经常会编译着色器 —— 而边界切换往往引入需要新着色器变体的新资产类型。当着色器编译与流式刷新、GC 压力同时发生时,会进一步放大复合事件。严重程度高度依赖于硬件:单线程 CPU 性能更强、缓存更大的系统能更快完成着色器工作,从而降低可见影响。

现代 V8(Orinoco 回收器)通过并发收集改进了这一点,但也承认其局限:

“这里的优势是主线程完全可以自由执行 JavaScript —— 尽管由于与辅助线程的少量同步会带来一些额外开销。”

来源:https://v8.dev/blog/trash-talk

即使有了 Orinoco,主 GC 周期也无法完全卸载。在一个游戏线程已经承受城市负载的引擎中,流式刷新与 GC 周期的复合时机,正是产生了老玩家们记忆中的严重 0 FPS 卡顿的原因。

为什么模式 B 有所改善但并未消失: Kuro 的 v3.1 流式加载管线更新(“更新了加载管线并加速数据流式加载”)针对的是复合事件中的资产流式加载部分。GC 部分仍然存在 —— 因为那需要将脚本层从 V8 迁移出去,而这正是 TS → C# 迁移的目标。部分改善正是你期望从修复两个并发原因之一中看到的结果。

这就是那个伴随现象:没有对应的 GPU 负载变化、没有温度变化、屏幕上没有任何可解释原因 —— 却出现的尖峰。

我有两组基准测试数据,共同清晰说明了这个问题。

星炬学院会话 —— 遍历过程包括骑摩托车从学院驶向开放世界。最差单帧:175ms,而 P99.9 为 77ms。该异常值远超出正态分布的尾部。在平均 GPU 利用率仅为 58% 的情况下,GPU 不可能是 175ms 暂停的来源。该性能剖析结果与 V8 主 GC 在区域切换时触发 stop-the-world 周期的行为一致。

瑝珑会话 —— 相同硬件、相同设置。在此次会话中,我使用滑翔伞刻意以最高速度飞出城市边界。最差单帧:245ms。这不是自然的卡顿 —— 而是一次对模式 B 的人为触发。边界跨越越快、越突然,复合事件就越严重:流式刷新和 GC 几乎同时触发,两者之间没有足够时间错开负载。

这两个数字放在一起很有价值。自然的 175ms(星炬学院)与刻意快速离开的 245ms(瑝珑)分别展示了模式 B 严重程度的下限和上限。正常遍历产生 175ms 的情形;激进的边界跨越产生 245ms 的情形。在 v3.1 流式加载管线改进之前的游戏早期版本,很可能产生更高的数值,因为当时的流式刷新部分更大。

对这两次卡顿的说明: 如果没有脚本 VM 性能分析器的访问权限,我无法确认这些就是 V8 GC 事件。暂停的形态、量级以及缺乏 GPU 相关性,与 V8 主 GC 行为一致。我将此称为推断,而非已确认。

2.3 迁移及其真正解决的问题

原文

2.3 The Migration and What It Actually Fixes

The TS→C# migration is not a performance patch. It is an architectural foundation change.

Modern managed runtimes can use incremental, concurrent GC modes that significantly reduce pause severity compared with V8 major GC in this workload — depending on runtime configuration, worst-case pauses can drop from hundreds of milliseconds to single digits. C# does have stop-the-world phases in some configurations, but the migration path from V8’s mark-compact behavior toward a tunable managed runtime represents meaningful progress on Pattern B regardless of the specific implementation Kuro adopts.

What the migration does not fix: the GameThread single-thread ceiling. UE4’s threading architecture will still serialize all game logic on one core. Even after a complete migration, WuWa will still hit the same UE4 scalability wall in dense scenes.

What the migration does fix: removes the scripting VM’s contribution to the worst-case frame time spikes. The 175ms outlier events — those become dramatically less severe or disappear entirely.

TypeScript (V8) C# (managed runtime)
GC model Stop-the-world major cycles Incremental, concurrent
Worst-case GC pause 100–500ms <10ms typical
Per-patch debt growth Accumulates (every new system adds heap pressure) Substantially reduced
Interop with UE4 Through V8 bridge More direct

The migration also compounds. Every new content system built on the C# path costs less in GC pressure than it would have on V8. The ROI grows with every patch. This is why the migration is worth doing during an active live-service cycle even though players will never see it on a patch note.

The project scale data from the datamine illustrates what they were up against:

Version Total files BinData dirs
v1.0 1,264 228
v2.0 1,442 266
v2.7 1,903 337
v3.2 2,292 402

The content database grew 81% from launch before the C# migration infrastructure was even scaffolded. Every entity, quest trigger, and prefab added during that period ran through the V8 path. The migration is catching up to nearly three years of accumulated scripting debt.

我是机翻

TS → C# 迁移不是一次性能补丁。它是一次架构层面的基础变更。

现代托管运行时可以使用增量的、并发的 GC 模式,与在此类工作负载下的 V8 主 GC 相比,能显著降低暂停的严重程度 —— 具体取决于运行时配置,最坏情况下的 GC 暂停可以从数百毫秒降至个位数毫秒。C# 在某些配置下确实也存在 stop-the-world 阶段,但从 V8 的标记-压缩行为迁移到一个可调优的托管运行时这条路径,无论 Kuro 最终采用哪种具体实现,都能代表在模式 B 上取得有意义的进展。

迁移不能解决的问题: GameThread 单线程上限。UE4 的线程架构仍然会将所有游戏逻辑串行化在一个核心上。即使完成全面迁移,《鸣潮》在密集场景下依然会撞上同样的 UE4 可扩展性瓶颈。

迁移能够解决的问题: 移除了脚本 VM 对最坏情况帧时间尖峰的贡献。那些 175ms 的异常事件 —— 将变得显著不那么严重,或者完全消失。

方面 TypeScript (V8) C# (托管运行时)
GC 模型 停止世界(stop-the-world)的主周期 增量、并发
最坏情况 GC 暂停 100–500ms 典型值 <10ms
每个补丁的技术债务增长 累积(每个新系统都会增加堆压力) 大幅减少
与 UE4 的互操作 通过 V8 桥接 更直接

迁移还具有复利效应。每一条基于 C# 路径构建的新内容系统,相比在 V8 上构建所产生的 GC 压力都要更低。投资回报率随每个补丁而增长。这就是为什么在活跃的长期服务周期中进行这次迁移是值得的 —— 尽管玩家永远也不会在补丁说明中看到它。

来自数据挖掘的项目规模数据,展示了团队所面临的挑战:

版本 总文件数 BinData 目录数
v1.0 1,264 228
v2.0 1,442 266
v2.7 1,903 337
v3.2 2,292 402

从上线到 C# 迁移基础设施甚至还未搭建之前,内容数据库已经增长了 81%。在此期间添加的每一个实体、任务触发器和预制体,都运行在 V8 路径上。这次迁移正在追赶将近三年积累下来的脚本技术债务。

3. 背景:其他游戏告诉我们的经验

3.1 霍格沃茨之遗:同样的病症,不同的患者

原文

If you want to understand whether WuWa’s stutter problems are a Kuro problem or a UE4 problem, Hogwarts Legacy is the cleanest data point available.

It runs on UE 4.27. It was made by Avalanche Software with a Warner Bros. budget. And it shipped with some of the most documented PC performance issues of any recent major release — the pattern identical to WuWa:

  • CPU-bound specifically in dense areas (Hogsmeade, populated castle sections) while open areas run fine
  • GPU underutilized relative to the stutter being experienced
  • Single-core CPU frequency matters; adding more cores does not help

A player with a Ryzen 9 5900X and RTX 4090 — one of the stronger gaming setups available — documented the problem directly in a Steam thread:

“5900x with 4090 here. I’m gaming at 4K. If I don’t use frame generation, I can hit 90–99% GPU utilization with 30–40% CPU utilization but the framerate is trash (50fps to 70fps).”

Source: https://steamcommunity.com/app/990080/discussions/0/3824161508141330042/

High GPU load, middling CPU overall load, bad framerate. The same shape as WuWa’s profile. And from a different thread, someone who had figured out the actual cause:

“Its only CPU limited because the engine doesn’t scale to more than a core or 2 so a CPU with more cores isn’t going to help, its single core performance (IPC) it wants and needs. If the engine scaled to 8 cores we wouldn’t see a CPU limitation.”

Source: https://steamcommunity.com/app/990080/discussions/0/3789254716328881575/

That second quote comes from a player — not a developer — who arrived at the correct technical diagnosis through observation. It describes UE4’s GameThread architecture more accurately than most technical write-ups.

This is not coincidence. It is the same engine, the same threading model, the same single-thread ceiling — on a project with far greater resources than WuWa had at launch. The lesson is not that Kuro failed to solve a problem other studios successfully solved. No clearly comparable UE4 open-world live-service title has publicly demonstrated a complete escape from this pattern.

Q: So why did Kuro choose UE4 if it has these limitations?

Because they wanted to build a next-generation gacha game, and in 2019–2020, UE4 was the most practical foundation for that ambition. UE5 did not yet exist. Unity’s DOTS/ECS was not production-stable at this scale. Building a custom engine from scratch — as CD Projekt did with REDEngine, or Rockstar with RAGE — requires years of dedicated R&D that simply wasn’t available.

UE4 offered mature tooling, a large hiring pool, and enough flexibility to be pushed hard. And to Kuro’s credit, the version of UE4 they shipped is not a stock build — it incorporates custom modifications including a custom global illumination solution with goals comparable to UE5’s Lumen system (Lumen itself is not directly portable to UE4, but the team implemented similar lighting capabilities through their own engineering). Solon Lee from Kuro confirmed this direction publicly, noting the team actively ported features inspired by UE5’s rendering advances back into their UE4.26 build (source). That kind of deep engine work doesn’t happen by accident. The team that shipped WuWa understood Unreal well enough to modify it at a significant level.

What was harder to foresee was how badly these specific limitations would surface at WuWa’s scale. In 2020 when development began, open-world UE4 games with comparable ambition were scarce — Tower of Fantasy had just released in 2021, Hogwarts Legacy in 2023. The full picture of what this engine pattern costs in a live-service open-world context at this visual fidelity wasn’t visible yet. Nobody had walked this exact road before. In hindsight the technical debt is clear. At the time it was much less so.

我是机翻

如果你想判断《鸣潮》的卡顿问题到底是 Kuro 的问题还是 UE4 的问题,《霍格沃茨之遗》就是最干净的数据点。

它运行在 UE 4.27 上。由 Avalanche Software 开发,华纳兄弟的预算。而且它上线时带有的 PC 性能问题,是近期大型游戏中被记录最详尽的之一 —— 其模式与《鸣潮》完全一致:

  • 密集区域(霍格莫德村、人口密集的城堡区域)明确地受限于 CPU,而开放区域运行正常
  • 相对于所经历的卡顿,GPU 未被充分利用
  • 单核 CPU 频率至关重要;增加更多核心无济于事

一位拥有 Ryzen 9 5900X 和 RTX 4090(市面上最强游戏配置之一)的玩家,在 Steam 帖子里直接记录了这个问题:

“这里是 5900x + 4090。我在 4K 下游戏。如果不使用帧生成,我能跑到 90–99% 的 GPU 利用率和 30–40% 的 CPU 利用率,但帧率还是很垃圾(50fps 到 70fps)。”

来源:https://steamcommunity.com/app/990080/discussions/0/3824161508141330042/

高 GPU 负载、总体 CPU 负载中等、帧率糟糕。这与《鸣潮》的性能剖析形态一致。而在另一个帖子中,有人已经搞清楚了实际原因:

“它之所以受限于 CPU,是因为引擎无法扩展到超过一两个核心,所以更多核心的 CPU 没有帮助,它想要且需要的是单核性能(IPC)。如果引擎能扩展到 8 个核心,我们根本不会看到 CPU 限制。”

来源:https://steamcommunity.com/app/990080/discussions/0/3789254716328881575/

第二个引用来自一名玩家 —— 而非开发者 —— 他通过观察得出了正确的技术诊断。这个描述比大多数技术文章都更准确地描述了 UE4 的 GameThread 架构。

这不是巧合。这是同一个引擎、同一个线程模型、同一个单线程天花板 —— 在一个比《鸣潮》上线时资源充裕得多的项目上。教训并不是 Kuro 未能解决其他工作室成功解决的问题。因为没有任何一个明确可比的、使用 UE4 的开放世界长期服务游戏,曾公开展示过完全摆脱这种模式。

问:既然如此,Kuro 为什么还要选择有这些局限的 UE4?

因为他们想打造一款下一代抽卡游戏,而在 2019–2020 年,UE4 是实现这一愿景最实用的基础。UE5 尚未问世。Unity 的 DOTS/ECS 在那个规模上尚未达到生产稳定。从头构建自定义引擎 —— 如同 CD Projekt 的 REDEngine 或 Rockstar 的 RAGE —— 需要数年专门的研发投入,而他们根本没有这个条件。

UE4 提供了成熟的工具链、庞大的人才招聘池,以及足够被深度定制的灵活性。而且公平地说,他们上线的 UE4 版本并非原版构建 —— 它包含了自定义修改,包括一套目标堪比 UE5 Lumen 系统的自定义全局光照方案(Lumen 本身并不能直接移植到 UE4,但团队通过自己的工程实现了类似的光照能力)。Kuro 的 Solon Lee 公开确认过这一方向,团队主动地将受 UE5 渲染进步启发的特性移植回了他们自有的 UE4.26 分支中(来源)。这种深度引擎工作绝非偶然。上线《鸣潮》的团队对 Unreal 的理解足够深刻,能够在相当高的层面上对其进行修改。

更难预见的是:这些特定局限会在《鸣潮》的规模下暴露得如此严重。在 2020 年项目启动时,拥有类似野心的开放世界 UE4 游戏还很少 ——《幻塔》2021 年才上线,《霍格沃茨之遗》2023 年才上线。在当时,以这样的视觉保真度在长期服务的开放世界语境下使用这种引擎模式到底要付出多大代价,全貌尚未显现。没有人走过这条路。事后看技术债务很清楚,但在当时远非如此。

3.2 战争机器5 与 堡垒之夜:UE4 的例外情况(以及为何它们不适用

原文

Gears 5, made by The Coalition, is frequently cited as the best UE4 implementation. It runs at locked 60fps on consoles, scales cleanly to PC, and has smooth frame times. The reason is directly relevant: it is not an open world. Linear level design allows aggressive pre-baked culling. There is no streaming complexity, no unpredictable actor density, no live-service accumulation. The Coalition designed around UE4’s constraints for a game type that fits them naturally.

Fortnite deserves a mention here as the other commonly cited UE4 exception — and it is genuinely exceptional, because it is made by Epic Games themselves, the developers of Unreal. Having direct access to the engine team, the ability to modify the engine at any depth on any timeline, and institutional knowledge that no external studio can match produces a different class of result. Fortnite is also not an open-world game in the sense WuWa is — its dense city areas and match structure are architecturally different from a persistent open world with thousands of simultaneously ticking actors. But even acknowledging those differences, it is worth noting that the smoothest UE4 experience available comes from the engine’s own creators.

WuWa is neither of these. The game type won’t allow Gears 5’s approach, and Kuro doesn’t have Epic’s internal access.

我是机翻

以下是上述内容的完整中文翻译:


3.2 战争机器5 与 堡垒之夜:UE4 的例外情况(以及为何它们不适用)

由 The Coalition 开发的《战争机器5》,经常被引为最佳 UE4 实现案例。它在主机上以锁定的 60fps 运行,能干净地扩展到 PC 平台,并且帧时间平滑。其原因直接相关:它不是开放世界。线性关卡设计允许激进的预烘焙剔除。这里没有流式加载的复杂性,没有不可预测的 Actor 密度,也没有长期服务式的积累。The Coalition 是针对 UE4 的约束进行设计的,但其所做的游戏类型本身就天然契合这些约束。

《堡垒之夜》在此值得提及,作为另一个常被引用的 UE4 例外案例 —— 它确实是个例外,因为它由 Epic Games 自己 —— Unreal 引擎的开发者 —— 制作。能够直接接触引擎团队、能在任意时间线上以任意深度修改引擎、以及任何外部工作室都无法比拟的机构性知识,这些条件共同产生了不同级别的结果。《堡垒之夜》也不是《鸣潮》意义上的开放世界游戏 —— 其密集城市区域和比赛结构,在架构上不同于一个拥有数千个同时 tick 的 Actor 的持久化开放世界。但即便承认这些差异,也值得指出:当下可用的最流畅的 UE4 体验,恰恰来自引擎自身的创造者。

《鸣潮》两者都不是。它的游戏类型不允许采用《战争机器5》的做法,而 Kuro 也没有 Epic 那样的内部通路。

看不懂思密达 :103:

1 个赞

翻译:对不起伦家没法像国内第一大厂一样找人代孕嘤嘤嘤

简而言之,洗地,我鸣潮卡是因为ue4的问题,绝对不是因为我开服就把搭脚手架的引擎大佬踹了以至于没来得及搭设完整的工作流,绝对不是因为我把程序语言换成C#导致包体里同时有两套石山代码需要的素材,绝对不是我没能力用分区块加载所以只能做一次读取所有素材的箱庭地图,绝对不是因为我滥用TS导致资源回收和资源加载这俩性能开销大户永远撞车的问题,全都是UE4底层架构有问题,哼

2 个赞

3.3 赛博朋克2077 与 荒野大镖客2:当人们把“苹果”与自研引擎比较时

原文

Another common community comparison goes like this: “Cyberpunk 2077, Red Dead Redemption 2 looks incredible and runs really stable. Why does WuWa struggle at stuttering?” It is worth addressing this directly, because the comparison fundamentally misunderstands what is being compared.

REDEngine 4 (Cyberpunk) and RAGE (RDR2) do not have the same GameThread problem because they were not built with UE4’s design philosophy — or UE4 at all.

CD Projekt redesigned REDEngine specifically for open-world streaming. Its job system is native to the architecture, not bolted on. Draw call submission distributes across threads. The entity component system was built for this use case from scratch.

RAGE has over twenty years of iteration specifically for dense open-world environments. Rockstar’s streaming system anticipates geometry before the camera reaches it. Memory layout was designed for cache coherency long before the term “data-oriented design” became mainstream.

Comparing WuWa’s frame times to these games is not a fair performance comparison. It is a comparison between a general-purpose engine used for a demanding use case, and custom infrastructure built specifically to handle that use case over many years. Different tools, different constraints, different histories.

我是机翻

以下是上述内容的完整中文翻译:


3.3 赛博朋克2077 与 荒野大镖客2:当人们把“苹果”与自研引擎比较时

社区中另一个常见的比较是这样的:“《赛博朋克2077》《荒野大镖客2》画面惊艳,运行也非常稳定。为什么《鸣潮》还在为卡顿挣扎?”这个问题值得直接回应,因为这种比较从根本上误解了被比较的对象。

REDEngine 4(《赛博朋克》)和 RAGE(《荒野大镖客2》)并不存在同样的 GameThread 问题,因为它们不是基于 UE4 的设计理念构建的 —— 或者说,根本不是基于 UE4 构建的。

CD Projekt 专门为开放世界流式加载重新设计了 REDEngine。它的任务系统是架构原生的,而非后来附加。绘制调用提交可以跨线程分布。其实体组件系统从一开始就是为此用例构建的。

RAGE 拥有超过二十年的迭代历史,专门服务于密集的开放世界环境。Rockstar 的流式加载系统能在镜头到达几何体之前就进行预判。内存布局早在“数据导向设计”这个术语成为主流之前,就已经为缓存一致性进行了长期优化。

将《鸣潮》的帧时间与这些游戏进行比较,并不是一个公平的性能对比。这是在比较一个被用于严苛用例的通用引擎,与一个经过多年专门构建、专为该用例而生的自定义基础设施。不同的工具、不同的约束、不同的历史。

1 个赞

4. Kuro 实际上在做什么

4.1 补丁中的性能改进

原文

Every patch note that mentions performance optimization is a payment against the technical debt balance. Looking at the official patch notes from v1.2 through v3.2, a pattern emerges — not just that Kuro is working on performance, but what kind of work is being done and how that evolves over time.

The early patches focused on surface-level stability:

v1.2“Optimized game performance for select mobile devices.” Targeted, device-specific. No architectural change.

v1.4 — Optimized shader compilation on PC; moved it to title screen to reduce visual glitches during gameplay. Added Auto FPS for Android to prevent overheating. This patch is notable because it addresses one of UE4’s most notorious pain points — in-game shader compilation — and pushes it out of the main gameplay loop.

The middle era (v2.x) shows scope expanding:

v2.0“Optimized CPU usage for NPCs, allowing more NPCs to appear on the screen at the same time.” This is a direct GameThread optimization. Kuro is explicitly acknowledging that NPC density has a CPU cost, not a GPU cost — and working on it.

v2.2“Optimized the efficiency of the shader compilation process and the hardware resource usage on PC.” Second pass on the shader problem.

v2.8 — The most substantive performance patch in the dataset:

  • “GPU load from Ray Tracing and increased frame rates will be reduced”
  • “Memory and VRAM usage will be reduced when various types of scenes are being displayed”

Two separate categories of improvement in one patch — rendering overhead and memory pressure simultaneously. This is not a surface fix.

v3.1“Optimized the game’s resource loading performance by updating the loading pipeline and accelerating data streaming, to help reduce loading stutter on some PC/mobile devices with lower specs.” Direct address of loading stutter — the symptom category that asset streaming problems produce.

v3.3 (April 30, 2026) — Introduced self-developed mobile frame generation targeting mid-range devices. This is significant in two directions: it demonstrates continued investment in mobile performance infrastructure, and it is an implicit acknowledgment that the GameThread ceiling on mobile is not being solved at the architectural level in this patch cycle — frame generation is being used to compensate for frame rate limitations rather than reduce them at source. Also added DLAA and Anisotropic Filtering options on PC. Resource Cleanup on mobile now allows removing map environment resources, and Basic Resources package size was reduced — incremental progress on the storage footprint discussed in the previous memory bloat analysis.

The trajectory is meaningful: from device-specific tweaks in 1.x, to CPU/NPC optimization in 2.0, to RT overhead and memory reduction in 2.8, to streaming pipeline improvements in 3.1. Each payment is incremental. None of it solves the architectural ceiling. But it accumulates.

Two observations worth flagging:

First, v3.1’s streaming pipeline update“updated the loading pipeline and accelerating data streaming” — is the patch that visibly reduced Pattern B (the hard 0 FPS freeze when leaving cities). Players who have been with the game since 2024 will recognize this. The improvement is real and measurable in daily play. What remains is the GC component of that compound event, which the streaming fix could not address.

Second, v2.8 is a milestone in both directions simultaneously: the most substantive performance optimization patch in the dataset, and the version where LevelEntityForCSharpConfig first appears in aki_base.csv. The surface-level optimizations and the architectural migration began in the same patch. They are not sequential — they are parallel tracks.

机翻

每个提及性能优化的补丁说明,都是在偿还技术债务的余额。从 v1.2 到 v3.2 的官方补丁说明中,可以看到一种模式 —— 不仅是 Kuro 正在做性能优化工作,而且是他们具体在做哪类工作,以及这项工作如何随时间演变。

早期的补丁侧重表面级别的稳定性:

  • v1.2 — “优化了部分移动端设备的游戏性能。” 有针对性的、设备特定的优化。没有架构层面的变更。
  • v1.4 — 优化了 PC 上的着色器编译;将其移到标题界面进行,以减少游戏过程中的视觉卡顿。为 Android 端增加了自动 FPS 以防止过热。这个补丁值得注意,因为它直接处理了 UE4 最臭名昭著的痛点之一 —— 游戏内着色器编译 —— 并将其推离了主要游戏循环。

中期(v2.x)的范围开始扩大:

  • v2.0 — “优化了 NPC 的 CPU 使用率,允许同一屏幕出现更多 NPC。” 这是一次直接的 GameThread 优化。Kuro 明确承认 NPC 密度消耗的是 CPU 成本而非 GPU 成本 —— 并正在对此进行优化。
  • v2.2 — “优化了 PC 上着色器编译过程的效率以及硬件资源使用。” 对着色器问题的第二次处理。
  • v2.8 — 数据集中最有实质内容的性能补丁:
    • “降低光线追踪的 GPU 负载并提高帧率”
    • “在显示各类场景时降低内存和显存使用量”
      一个补丁中包含了两个不同类别的改进 —— 同时处理渲染开销和内存压力。这不是表面级别的修复。
  • v3.1 — “通过更新加载管线、加速数据流式加载,优化了游戏的资源加载性能,以帮助减少部分低配置 PC/移动设备的加载卡顿。” 直接针对加载卡顿 —— 这正是资产流式加载问题所产生的症状类别。
  • v3.3(2026 年 4 月 30 日) — 引入了针对中端设备的自研移动端帧生成。这有两个重要意义:一方面证明了 Kuro 在移动端性能基础设施上的持续投入;另一方面也隐含承认,在这个补丁周期中,移动端的 GameThread 瓶颈并未在架构层面被解决 —— 帧生成被用于补偿帧率限制,而非从源头减少帧率问题。同时 PC 端增加了 DLAA 和各向异性过滤选项。移动端增加了资源清理功能,可以移除地图环境资源,且基础资源包体积有所减小 —— 这是在之前内存膨胀分析中讨论过的存储占用方面取得的渐进式进展。

这个演进轨迹是有意义的:从 1.x 的设备特定调整 → 2.0 的 CPU/NPC 优化 → 2.8 的光线追踪开销和内存削减 → 3.1 的流式加载管线改进。每一笔偿还都是增量式的。没有任何一笔能解决架构天花板。但它们正在累积。

两个值得注意的观察:

第一,v3.1 的流式加载管线更新 —— “更新了加载管线并加速数据流式加载” —— 正是那个显著减轻了模式 B(离开城市时出现的 0 FPS 硬冻结)的补丁。自 2024 年就在玩这款游戏的玩家会认出这一点。在日常游戏中,这项改进是真实且可测量的。剩下的就是复合事件中 GC 的部分,这是流式加载修复无法解决的。

第二,v2.8 同时在两个方向上都是一个里程碑:它是数据集中最有实质内容的性能优化补丁,同时也是 LevelEntityForCSharpConfig 首次出现在 aki_base.csv 中的版本。表面级别的优化和架构迁移是在同一个补丁中开始的。它们不是串行的 —— 而是并行的两条轨道。

4.2 迁移的上下文视角

原文

The TS→C# migration deserves a specific comparison to the other optimization work that could theoretically be done.

Consider CPU-side culling improvements, VFX LOD authoring, or shader precision tuning. Each of these is a real fix for a real problem. None of them require a new engine. And yet none of them have been fully shipped.

The reason is not that Kuro doesn’t know about them. It is that “technically fixable” and “feasible within a live-service release cadence” are different things.

The engineering team is running on a treadmill. Optimizing deeply means stopping to fix the treadmill — but if the treadmill stops, you fall off. The TS→C# migration is the equivalent of replacing the treadmill’s core components one at a time, while still running, without players noticing the machine is being rebuilt underneath them.

Retrofitting VFX LOD for WuWa’s character skill sets, for example, is not an engineering task. It is a content production task: reopen every existing skill VFX — across 50+ characters with 5–6 skills each — author LOD variants, QA each one for visual regressions on content that is already live, train artists on the new workflow, and enforce it for every future release. That work competes directly with the characters, events, and areas that have to ship in the next six-week window to keep the game viable.

The TS→C# migration gets prioritized over these fixes because it is the one change with compounding infrastructure returns. Every future content addition on the C# path carries less GC risk than it would have on V8. The ROI is exponential, not linear. Shader precision tuning has one-time ROI. The migration’s value grows with every patch.

Kuro is doing this migration in addition to maintaining the six-week content schedule. When it works correctly, players don’t notice. That is the nature of infrastructure work.

机翻

TS → C# 迁移值得与理论上可以做的其他优化工作做一个具体对比。

考虑一下 CPU 端的剔除改进、VFX 细节层级(LOD)制作,或者着色器精度调优。这些每一项都是针对真实问题的真实修复。但它们都不需要一个新的引擎。然而,它们中没有一项是完全落地了的。

其原因并不是 Kuro 不知道这些做法。而是“技术上可修复”与“在长期服务的发布节奏内可行”是两回事。

工程团队就像在跑步机上奔跑。深度优化往往意味着停下来修理跑步机 —— 但如果跑步机停了,你就会摔下来。TS → C# 迁移相当于在依然奔跑的同时,一次更换跑步机的一个核心部件,而且不让玩家察觉到他们脚下的机器正在被重建。

举例来说,为《鸣潮》的角色技能特效改造 VFX LOD,根本不是一项工程任务,而是一项内容制作任务:重新打开每一个现有的技能 VFX —— 横跨 50 多个角色,每人 5–6 个技能 —— 制作 LOD 变体,对已经上线的内容进行逐个 QA 视觉回归,培训美术人员掌握新工作流,并强制要求每个后续版本都遵守该流程。这项工作会直接与接下来六周窗口期内必须上线、以维持游戏活力的角色、活动及区域的内容开发形成资源竞争。

TS → C# 迁移之所以能优先于这些修复,是因为它是唯一一项能带来复合基础设施回报的变更。未来每一条在 C# 路径上新增的内容,其携带的 GC 风险都比在 V8 上更低。其投资回报率是指数级的,而非线性。着色器精度调优是一次性的回报。而迁移的价值随着每个补丁而增长。

Kuro 是在维持每六周一次的内容发布节奏之外,再额外进行这项迁移的。当它正确工作时,玩家根本不会察觉。这就是基础设施工作的本质。

为什么不看看3.0开始爆炸的内存呢 :rofl:

1 个赞

4.3 MagicDawn:更长期的视角

原文

MagicDawn is Tencent Games’ internal rendering research team. Their published work from 2025–2026 includes:

  • Neural Dynamic GI (CVPR 2026): Neural compression for temporal lightmap sets, enabling dynamic global illumination with dramatically reduced storage and compute overhead
  • Gaussian Probe Compression (SIGGRAPH 2025): Light probe compression up to 1:50 ratio with real-time GPU decompression
  • Lightmap Compression (Eurographics 2026): 83% storage reduction in UV-space lightmaps with PSNR improvements

Source: https://magicdawnlab.github.io/

Importantly, MagicDawn’s own promotional video explicitly states: “MagicDawn is already powering a wide range of blockbuster games including Wuthering Waves” — confirming an active collaboration with Kuro, not merely theoretical research.

That said, the specific features integrated, their scope, and the extent of their contribution to WuWa’s current rendering pipeline have not been confirmed in Kuro’s official patch notes or public communications. “Powering” in a research team’s promotional context may refer to anything from full integration of specific techniques to ongoing testing and collaboration. Until Kuro provides specific confirmation, the precise nature of MagicDawn’s role in WuWa’s production build remains unclear — but the collaboration itself is confirmed from MagicDawn’s side.

What this does confirm is that Tencent is investing in rendering infrastructure at the research level relevant to WuWa’s visual ambitions, and that the relationship between MagicDawn and Kuro is active rather than speculative.

机翻

MagicDawn 是腾讯游戏内部的渲染研究团队。他们在 2025–2026 年间发表的工作成果包括:

  • 神经动态全局光照(CVPR 2026) :面向时序光照贴图集的神经压缩技术,能够以大幅降低的存储和计算开销实现动态全局光照。
  • 高斯探针压缩(SIGGRAPH 2025) :光照探针压缩比最高可达 1:50,支持实时 GPU 解压缩。
  • 光照贴图压缩(Eurographics 2026) :UV 空间光照贴图存储减少 83%,同时 PSNR(峰值信噪比)有所提升。

来源:https://magicdawnlab.github.io/

重要的是,MagicDawn 自己的宣传视频中明确表述:“MagicDawn 已经在为包括《鸣潮》在内的多款重磅游戏提供技术支持”—— 这确认了与 Kuro 之间的积极合作关系,而不仅仅是理论研究。

不过,具体集成了哪些特性、它们的覆盖范围,以及它们对《鸣潮》当前渲染管线的贡献程度,并未在 Kuro 的官方补丁说明或公开沟通中得到确认。在一个研究团队的宣传语境中,“提供技术支持”可能涵盖从特定技术的完整集成,到正在进行的测试和协作等不同层面。在 Kuro 提供具体确认之前,MagicDawn 在《鸣潮》正式版本管线中所扮演的确切角色仍不明确 —— 但合作本身已从 MagicDawn 一方得到证实。

这一点能够确认的是:腾讯正在与《鸣潮》的视觉目标相关的研究层面上投资渲染基础设施,并且 MagicDawn 与 Kuro 之间的关系是实际存在的,而非推测。