Loading...
Loading...
Compare original and translation side by side
| Metric | Quest 2 / Quest Pro | Quest 3 / Quest 3S |
|---|---|---|
| Draw calls (busy simulation) | 80-200 | 200-300 |
| Draw calls (medium simulation) | 200-300 | 400-600 |
| Draw calls (light simulation) | 400-600 | 700-1000 |
| Triangles per frame | 750K-1M | 1M-2M |
| SetPass calls | < 50 | < 80 |
| 指标 | Quest 2 / Quest Pro | Quest 3 / Quest 3S |
|---|---|---|
| 绘制调用(高负载场景) | 80-200 | 200-300 |
| 绘制调用(中等负载场景) | 200-300 | 400-600 |
| 绘制调用(轻负载场景) | 400-600 | 700-1000 |
| 每帧三角形数量 | 750K-1M | 1M-2M |
| SetPass调用 | < 50 | < 80 |
// BAD: Allocates every frame
void Update() {
string label = "Score: " + score.ToString();
var enemies = FindObjectsOfType<Enemy>();
var filtered = enemies.Where(e => e.IsAlive).ToList();
}
// GOOD: Zero allocations in Update
private StringBuilder _sb = new StringBuilder(32);
private List<Enemy> _enemyCache = new List<Enemy>();
private Enemy[] _enemyArray;
void Start() {
_enemyArray = FindObjectsOfType<Enemy>();
}
void Update() {
_sb.Clear();
_sb.Append("Score: ");
_sb.Append(score);
}// BAD: Allocates every frame
void Update() {
string label = "Score: " + score.ToString();
var enemies = FindObjectsOfType<Enemy>();
var filtered = enemies.Where(e => e.IsAlive).ToList();
}
// GOOD: Zero allocations in Update
private StringBuilder _sb = new StringBuilder(32);
private List<Enemy> _enemyCache = new List<Enemy>();
private Enemy[] _enemyArray;
void Start() {
_enemyArray = FindObjectsOfType<Enemy>();
}
void Update() {
_sb.Clear();
_sb.Append("Score: ");
_sb.Append(score);
}OVRInputOVRHandOVRSkeletonOVREyeGazeOVRInputOVRHandOVRSkeletonOVREyeGaze| Area | Target | Notes |
|---|---|---|
| Draw calls | 80-200 (busy) to 400-600 (light) | Use batching, instancing, atlasing |
| Triangles | 750K-1M/frame | Use LODs, occlusion culling |
| Texture resolution | Max 2K, 4K sparingly | ASTC compression required |
| Shader | URP mobile shaders | No Standard shader, no screen-space effects |
| Rendering mode | Single-pass multiview | Must be enabled in XR settings |
| FFR | Enabled (High or HighTop) | Fixed foveated rendering reduces edge fragment cost |
| MSAA | 4x quality / 2x perf | Free on tile-based GPU when configured correctly |
| Target frame rate | 72 Hz minimum | 90 Hz recommended, 120 Hz for smooth experiences |
| GC allocations | 0 B/frame in steady state | No allocations in Update/LateUpdate/FixedUpdate |
| Audio sources | < 16 simultaneous | Use pooling for audio sources |
| 领域 | 目标 | 说明 |
|---|---|---|
| 绘制调用 | 80-200(高负载)至400-600(轻负载) | 使用批处理、实例化、图集 |
| 三角形数量 | 750K-1M/帧 | 使用LODs、遮挡剔除 |
| 纹理分辨率 | 最大2K,谨慎使用4K | 必须使用ASTC压缩 |
| Shader | URP移动shaders | 禁止使用Standard shader及屏幕空间特效 |
| 渲染模式 | 单通道多视图 | 必须在XR设置中启用 |
| FFR | 启用(High或HighTop) | 固定注视点渲染可降低边缘片段开销 |
| MSAA | 4x画质 / 2x性能 | 配置正确时,基于瓦片的GPU可免费获得该效果 |
| 目标帧率 | 最低72 Hz | 推荐90 Hz,120 Hz可获得更流畅体验 |
| GC分配 | 稳态下每帧0 B | Update/LateUpdate/FixedUpdate中无分配 |
| 音频源 | < 16个同时播放 | 使用对象池管理音频源 |
// Flag these patterns in code review:
Camera.main // Calls FindWithTag internally
GameObject.Find("name") // Linear search every call
GetComponent<T>() in Update // Cache the result
new List<T>() in Update // Allocates on heap
string + string in Update // Creates new string objects
foreach on non-List collections // Enumerator allocation
LINQ queries (.Where, .Select) // Multiple allocations
Boxing (int -> object) // Heap allocation
delegate/lambda in hot paths // Closure allocation// Flag these patterns in code review:
Camera.main // Calls FindWithTag internally
GameObject.Find("name") // Linear search every call
GetComponent<T>() in Update // Cache the result
new List<T>() in Update // Allocates on heap
string + string in Update // Creates new string objects
foreach on non-List collections // Enumerator allocation
LINQ queries (.Where, .Select) // Multiple allocations
Boxing (int -> object) // Heap allocation
delegate/lambda in hot paths // Closure allocation// BAD: Empty Update still has overhead
void Update() { }
// BAD: Logic that doesn't need per-frame execution
void Update() {
SavePlayerPrefs(); // Should be event-driven
}
// GOOD: Use events, coroutines, or InvokeRepeating for non-per-frame logic
void OnScoreChanged(int newScore) {
UpdateScoreUI(newScore);
}// BAD: Empty Update still has overhead
void Update() { }
// BAD: Logic that doesn't need per-frame execution
void Update() {
SavePlayerPrefs(); // Should be event-driven
}
// GOOD: Use events, coroutines, or InvokeRepeating for non-per-frame logic
void OnScoreChanged(int newScore) {
UpdateScoreUI(newScore);
}// BAD: Camera.main uses FindWithTag internally
void Update() {
transform.LookAt(Camera.main.transform);
}
// GOOD: Cache the reference
private Transform _cameraTransform;
void Start() {
_cameraTransform = Camera.main.transform;
}
void Update() {
transform.LookAt(_cameraTransform);
}// BAD: Camera.main uses FindWithTag internally
void Update() {
transform.LookAt(Camera.main.transform);
}
// GOOD: Cache the reference
private Transform _cameraTransform;
void Start() {
_cameraTransform = Camera.main.transform;
}
void Update() {
transform.LookAt(_cameraTransform);
}// BAD: Expensive search every frame
void Update() {
var player = GameObject.FindWithTag("Player");
var rb = player.GetComponent<Rigidbody>();
}
// GOOD: Cache in Awake/Start or use dependency injection
private Rigidbody _playerRb;
void Awake() {
_playerRb = GameObject.FindWithTag("Player").GetComponent<Rigidbody>();
}// BAD: Expensive search every frame
void Update() {
var player = GameObject.FindWithTag("Player");
var rb = player.GetComponent<Rigidbody>();
}
// GOOD: Cache in Awake/Start or use dependency injection
private Rigidbody _playerRb;
void Awake() {
_playerRb = GameObject.FindWithTag("Player").GetComponent<Rigidbody>();
}metavrmetavr <args>metavrmetavrnpx -y metavr <args>undefinedmetavrmetavr <args>metavrmetavrnpx -y metavr <args>undefined
Use device-side profiling to validate that code review findings translate to real performance improvements.
使用设备端分析工具验证代码评审发现的问题是否转化为实际性能提升。