vr高帧率版本修复
This commit is contained in:
parent
bb6eee2250
commit
1c356ed6dc
@ -132,6 +132,11 @@ class VRManager(DirectObject):
|
||||
self.submit_failures = 0
|
||||
self.pose_failures = 0
|
||||
|
||||
# OpenVR 帧ID跟踪(防止重复提交)
|
||||
self.openvr_frame_id = 0
|
||||
self.left_eye_last_render_frame = -1
|
||||
self.right_eye_last_render_frame = -1
|
||||
|
||||
# 高级性能监控
|
||||
self.performance_monitoring = True # 是否启用性能监控
|
||||
self.debug_output_enabled = True # 是否启用调试输出
|
||||
@ -898,26 +903,36 @@ class VRManager(DirectObject):
|
||||
if self.vr_task:
|
||||
self.world.taskMgr.remove(self.vr_task)
|
||||
|
||||
self.vr_task = self.world.taskMgr.add(self._update_vr, "update_vr")
|
||||
print("✓ VR更新任务已启动")
|
||||
# 设置高优先级(sort=-1000),确保在渲染之前执行,参考 panda3d-openvr
|
||||
self.vr_task = self.world.taskMgr.add(self._update_vr, "update_vr", sort=-1000)
|
||||
print("✓ VR更新任务已启动(高优先级,sort=-1000)")
|
||||
|
||||
def _update_vr(self, task):
|
||||
"""VR更新任务 - 每帧调用"""
|
||||
"""VR更新任务 - 每帧调用(参考 panda3d-openvr 架构)"""
|
||||
if not self.vr_enabled or not self.vr_system:
|
||||
return task.cont
|
||||
|
||||
try:
|
||||
# 📌 第一件事:waitGetPoses 阻塞等待 VSync(参考 panda3d-openvr)
|
||||
# 这会阻塞整个任务,确保每个 VSync 周期只执行一次
|
||||
should_call_waitgetposes = (
|
||||
not self.vr_test_mode or # 普通VR模式总是需要
|
||||
self.test_mode_wait_poses # 测试模式:仅当启用姿态等待时
|
||||
)
|
||||
|
||||
if should_call_waitgetposes:
|
||||
self._wait_get_poses_immediate()
|
||||
# 立即更新手柄,使用最新姿态(避免过时数据)
|
||||
self.update_tracked_devices()
|
||||
|
||||
# 性能监控
|
||||
self.frame_count += 1
|
||||
|
||||
# 🚀 手动垃圾回收控制 - 避免16-19帧周期性峰值
|
||||
self._manual_gc_control()
|
||||
|
||||
# 🚀 自动启用性能模式 - 减少计时对象创建
|
||||
if not self.performance_mode_enabled and self.frame_count >= self.performance_mode_trigger_frame:
|
||||
self.performance_mode_enabled = True
|
||||
print(f"🎯 性能模式已启用 (帧#{self.frame_count}) - 禁用详细监控以提升性能")
|
||||
print(" 现在将减少每帧对象创建,显著提升VR性能稳定性")
|
||||
if self.frame_count <= self.performance_mode_trigger_frame + 5: # 只输出一次
|
||||
print(f"🎯 性能模式已启用 (帧#{self.frame_count}) - 禁用详细监控以提升性能")
|
||||
|
||||
# 记录帧时间
|
||||
self._track_frame_time()
|
||||
@ -932,36 +947,31 @@ class VRManager(DirectObject):
|
||||
self.last_fps_check = self.frame_count
|
||||
self.last_fps_time = current_time
|
||||
|
||||
# 更新系统性能指标
|
||||
self._update_performance_metrics()
|
||||
|
||||
# 更新GPU渲染时间统计
|
||||
if self.enable_gpu_timing:
|
||||
self._get_gpu_frame_timing()
|
||||
|
||||
# 🚀 Running Start策略:WaitGetPoses在Submit后调用,此处只更新相机
|
||||
# 不在此处调用WaitGetPoses,避免错过VSync窗口
|
||||
self.poses_updated_in_task = True
|
||||
|
||||
# 使用上一帧获取的姿态更新相机
|
||||
# 📌 使用刚获取的姿态更新相机(而不是旧姿态)
|
||||
self._update_camera_poses()
|
||||
|
||||
# 输出策略信息(仅第一次)
|
||||
if not hasattr(self, '_running_start_logged'):
|
||||
print("✓ Running Start模式已启用 - WaitGetPoses在Submit后调用")
|
||||
self._running_start_logged = True
|
||||
if not hasattr(self, '_new_architecture_logged'):
|
||||
print("✅ 新架构已启用 - waitGetPoses 在任务开始阻塞,参考 panda3d-openvr")
|
||||
print(" 这确保每个 VSync 周期只渲染一次,解决 AlreadySubmitted 错误")
|
||||
self._new_architecture_logged = True
|
||||
|
||||
# 1. 更新手柄和其他跟踪设备
|
||||
self.update_tracked_devices()
|
||||
|
||||
# 2. 更新VR动作状态
|
||||
# 更新VR动作状态
|
||||
self.action_manager.update_actions()
|
||||
|
||||
# 3. 更新VR交互系统
|
||||
# 更新VR交互系统
|
||||
self.interaction_manager.update()
|
||||
|
||||
# 🚀 阶段2&3:渲染同步和纹理提交(替代DrawCallback)
|
||||
self._handle_vr_rendering_and_submit()
|
||||
# 更新系统性能指标(减少频率)
|
||||
if self.frame_count % 30 == 1: # 每30帧更新一次,减少开销
|
||||
self._update_performance_metrics()
|
||||
|
||||
# 更新GPU渲染时间统计(减少频率)
|
||||
if self.enable_gpu_timing and self.frame_count % 60 == 1:
|
||||
self._get_gpu_frame_timing()
|
||||
|
||||
# 🚀 手动垃圾回收控制 - 避免16-19帧周期性峰值
|
||||
self._manual_gc_control()
|
||||
|
||||
# 定期输出性能报告 - 默认10秒间隔
|
||||
report_interval = getattr(self, 'performance_report_interval', 600)
|
||||
@ -975,47 +985,15 @@ class VRManager(DirectObject):
|
||||
|
||||
return task.cont
|
||||
|
||||
def _handle_vr_rendering_and_submit(self):
|
||||
"""VR帧管理 - DrawCallback现在处理实际渲染和提交"""
|
||||
def _sync_gpu_if_needed(self):
|
||||
"""可选的GPU同步 - 仅在需要时使用"""
|
||||
try:
|
||||
import time
|
||||
|
||||
# 记录帧处理开始时间
|
||||
frame_start_time = time.perf_counter()
|
||||
|
||||
# DrawCallback会处理实际的渲染和纹理提交
|
||||
# 这里主要处理帧管理和诊断
|
||||
|
||||
# 同步等待GPU完成(如果需要)
|
||||
gsg = self.world.win.getGsg()
|
||||
if gsg:
|
||||
gsg.getEngine().syncFrame()
|
||||
|
||||
# 计算帧处理时间
|
||||
frame_time = (time.perf_counter() - frame_start_time) * 1000
|
||||
|
||||
# Running Start的备用逻辑(DrawCallback可能已处理)
|
||||
# 确保每帧都有姿态获取
|
||||
if not hasattr(self, '_waitgetposes_called_this_frame') or not self._waitgetposes_called_this_frame:
|
||||
pose_start = time.perf_counter()
|
||||
self._wait_get_poses_immediate()
|
||||
self.wait_poses_time = (time.perf_counter() - pose_start) * 1000
|
||||
self._waitgetposes_called_this_frame = True
|
||||
|
||||
# 重置标记,准备下一帧
|
||||
self.world.taskMgr.doMethodLater(0.001, self._reset_waitgetposes_flag, "reset_waitgetposes_flag")
|
||||
|
||||
# 调试信息(仅在启用时)
|
||||
if self.debug_output_enabled and self.frame_count % 300 == 1:
|
||||
print(f"🎯 帧#{self.frame_count} DrawCallback模式:")
|
||||
print(f" 帧管理时间: {frame_time:.2f}ms")
|
||||
print(f" 渲染由回调处理: 左眼={self.left_render_count}, 右眼={self.right_render_count}")
|
||||
print(f" 姿态获取: {self.wait_poses_time:.2f}ms")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ VR帧管理异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print(f"❌ GPU同步异常: {e}")
|
||||
|
||||
def simple_left_cb(self, cbdata):
|
||||
"""简化的左眼渲染回调 - 精确控制渲染和提交"""
|
||||
@ -1033,25 +1011,25 @@ class VRManager(DirectObject):
|
||||
# 记录渲染次数
|
||||
self.left_render_count += 1
|
||||
|
||||
# 检查避免重复提交
|
||||
current_frame = getattr(self, 'frame_count', 0)
|
||||
if not hasattr(self, '_left_submitted_frame'):
|
||||
self._left_submitted_frame = -1
|
||||
# 📌 OpenVR 帧边界检查:防止同一 OpenVR 帧内重复渲染
|
||||
if self.left_eye_last_render_frame == self.openvr_frame_id:
|
||||
return # 已在当前 OpenVR 帧渲染过,跳过
|
||||
|
||||
if self._left_submitted_frame != current_frame:
|
||||
self._left_submitted_frame = current_frame
|
||||
# 🔧 OpenVR最佳实践:左眼只渲染,不立即提交
|
||||
# 基于官方hellovr示例:两眼都渲染完后再批量提交
|
||||
self.left_eye_last_render_frame = self.openvr_frame_id
|
||||
|
||||
# 🔧 渐进式VR功能测试:根据调试标志决定是否提交纹理
|
||||
should_submit = not self.vr_test_mode or self.test_mode_submit_texture
|
||||
# 渐进式VR功能测试:单独启用纹理提交时保留原逻辑
|
||||
should_submit = self.vr_test_mode and self.test_mode_submit_texture and not self.test_mode_wait_poses
|
||||
|
||||
if should_submit:
|
||||
# 渲染后立即提交纹理(普通模式或测试模式启用提交时)
|
||||
if self.vr_compositor and self.vr_left_texture:
|
||||
self.submit_texture(openvr.Eye_Left, self.vr_left_texture)
|
||||
if should_submit:
|
||||
# 测试模式:单独启用纹理提交时的兼容模式
|
||||
if self.vr_compositor and self.vr_left_texture:
|
||||
self.submit_texture(openvr.Eye_Left, self.vr_left_texture)
|
||||
|
||||
if self.vr_test_mode:
|
||||
# 测试模式:始终触发屏幕显示更新
|
||||
self._update_test_display()
|
||||
if self.vr_test_mode:
|
||||
# 测试模式:始终触发屏幕显示更新
|
||||
self._update_test_display()
|
||||
|
||||
except Exception as e:
|
||||
print(f"左眼渲染回调错误: {e}")
|
||||
@ -1072,33 +1050,56 @@ class VRManager(DirectObject):
|
||||
# 记录渲染次数
|
||||
self.right_render_count += 1
|
||||
|
||||
# 检查避免重复提交
|
||||
current_frame = getattr(self, 'frame_count', 0)
|
||||
if not hasattr(self, '_right_submitted_frame'):
|
||||
self._right_submitted_frame = -1
|
||||
# 📌 OpenVR 帧边界检查:防止同一 OpenVR 帧内重复渲染
|
||||
if self.right_eye_last_render_frame == self.openvr_frame_id:
|
||||
return # 已在当前 OpenVR 帧渲染过,跳过
|
||||
|
||||
if self._right_submitted_frame != current_frame:
|
||||
self._right_submitted_frame = current_frame
|
||||
# 🔧 OpenVR最佳实践:右眼渲染完成后批量提交两眼纹理
|
||||
# 基于官方hellovr示例:避免分散提交导致的VSync阻塞
|
||||
self.right_eye_last_render_frame = self.openvr_frame_id
|
||||
|
||||
# 🔧 渐进式VR功能测试:根据调试标志决定启用哪些功能
|
||||
should_submit = not self.vr_test_mode or self.test_mode_submit_texture
|
||||
should_wait_poses = not self.vr_test_mode or self.test_mode_wait_poses
|
||||
# 🔧 渐进式VR功能测试:根据调试标志决定启用哪些功能
|
||||
should_batch_submit = not self.vr_test_mode or self.test_mode_submit_texture
|
||||
should_wait_poses = not self.vr_test_mode or self.test_mode_wait_poses
|
||||
|
||||
if should_submit:
|
||||
# 渲染后立即提交纹理(普通模式或测试模式启用提交时)
|
||||
# 特殊处理:测试模式单独启用功能时的兼容模式
|
||||
if self.vr_test_mode:
|
||||
if self.test_mode_submit_texture and not self.test_mode_wait_poses:
|
||||
# 单独测试纹理提交:使用原来的分散提交方式
|
||||
if self.vr_compositor and self.vr_right_texture:
|
||||
self.submit_texture(openvr.Eye_Right, self.vr_right_texture)
|
||||
|
||||
if should_wait_poses:
|
||||
# Running Start:右眼提交后立即获取姿态(普通模式或测试模式启用时)
|
||||
if not hasattr(self, '_waitgetposes_called_this_frame') or not self._waitgetposes_called_this_frame:
|
||||
self._wait_get_poses_immediate()
|
||||
self._waitgetposes_called_this_frame = True
|
||||
self.world.taskMgr.doMethodLater(0.001, self._reset_waitgetposes_flag, "reset_waitgetposes_flag")
|
||||
# 🚀 测试模式也需要PostPresentHandoff避免36FPS
|
||||
try:
|
||||
if hasattr(self.vr_compositor, 'postPresentHandoff'):
|
||||
self.vr_compositor.postPresentHandoff()
|
||||
elif hasattr(self.vr_compositor, 'PostPresentHandoff'):
|
||||
self.vr_compositor.PostPresentHandoff()
|
||||
except Exception as e:
|
||||
pass # 测试模式静默忽略错误
|
||||
should_batch_submit = False
|
||||
elif self.test_mode_wait_poses and not self.test_mode_submit_texture:
|
||||
# 单独测试姿态等待:不提交纹理
|
||||
should_batch_submit = False
|
||||
|
||||
if self.vr_test_mode:
|
||||
# 测试模式:始终更新性能HUD
|
||||
self._update_test_performance_hud()
|
||||
if should_batch_submit:
|
||||
# 🚀 OpenVR最佳实践:批量提交两眼纹理
|
||||
# 这是解决36FPS问题的关键修复
|
||||
self._batch_submit_textures()
|
||||
|
||||
# 🔧 关键修复:移除Submit后立即WaitGetPoses的错误实现
|
||||
# 根据OpenVR官方文档:"Calling WaitGetPoses immediately after Submit is conspicuously wrong"
|
||||
# WaitGetPoses应该在下一帧开始时通过update_vr_task调用,不是Submit后立即调用
|
||||
#
|
||||
# if should_wait_poses:
|
||||
# # 错误的实现:Submit后立即获取姿态导致36FPS
|
||||
# self._wait_get_poses_immediate() # ← 这是36FPS的根本原因!
|
||||
#
|
||||
# 正确的实现:让update_vr_task在下一帧开始时调用WaitGetPoses
|
||||
|
||||
if self.vr_test_mode:
|
||||
# 测试模式:始终更新性能HUD
|
||||
self._update_test_performance_hud()
|
||||
|
||||
except Exception as e:
|
||||
print(f"右眼渲染回调错误: {e}")
|
||||
@ -1165,6 +1166,9 @@ class VRManager(DirectObject):
|
||||
# 渲染姿态用于绘制,游戏姿态用于逻辑,避免ATW过度补偿
|
||||
result = self.vr_compositor.waitGetPoses(self.poses, self.game_poses)
|
||||
|
||||
# 📌 waitGetPoses 成功后立即递增 OpenVR 帧ID(新帧开始)
|
||||
self.openvr_frame_id += 1
|
||||
|
||||
# 结束计时
|
||||
wait_time = self._end_timing(timing)
|
||||
|
||||
@ -1204,10 +1208,15 @@ class VRManager(DirectObject):
|
||||
self._return_pooled_matrix(self.controller_poses[device_id])
|
||||
del self.controller_poses[device_id]
|
||||
|
||||
# 🔧 关键修复:立即更新手柄和跟踪设备
|
||||
# Running Start模式下必须在WaitGetPoses后立即更新,避免手柄消失
|
||||
self.update_tracked_devices()
|
||||
|
||||
# 调试信息 - 仅在第一次成功时输出
|
||||
if not hasattr(self, '_dual_pose_mode_logged') and valid_poses > 0:
|
||||
print(f"✅ 双姿态立即获取模式启用 - 有效姿态数: {valid_poses}")
|
||||
print(" 渲染姿态用于绘制,游戏姿态用于逻辑,防止ATW过度补偿")
|
||||
print(" 手柄和跟踪设备在WaitGetPoses后立即更新")
|
||||
self._dual_pose_mode_logged = True
|
||||
|
||||
except Exception as e:
|
||||
@ -1500,7 +1509,8 @@ class VRManager(DirectObject):
|
||||
|
||||
# VR性能优化:使用Running Start模式(Valve最佳实践)
|
||||
print("🚀 VR性能优化:Running Start模式已启用")
|
||||
print(" 优势:Submit后立即获取姿态,避免错过VSync窗口")
|
||||
print(" 优势:在帧开始时获取姿态,提供VSync前3ms的渲染时间")
|
||||
print(" 注意:Submit后立即调用WaitGetPoses是错误实现")
|
||||
self.set_prediction_time(11.0) # 11ms预测时间 - OpenVR标准值,平衡准确性和延迟
|
||||
|
||||
# 🚀 动态调整Qt Timer频率以支持VR
|
||||
@ -1835,8 +1845,8 @@ class VRManager(DirectObject):
|
||||
|
||||
# 显示Running Start模式信息
|
||||
print(f"🎯 Running Start模式:")
|
||||
print(f" 模式: Valve Running Start - Submit后立即获取姿态")
|
||||
print(f" 优势: 避免VSync延迟,提升VR性能")
|
||||
print(f" 模式: Valve Running Start - 帧开始时获取姿态")
|
||||
print(f" 优势: VSync前3ms获取姿态,提供充足渲染时间")
|
||||
print(f" 预测时间: {self.use_prediction_time * 1000:.1f}ms")
|
||||
|
||||
# 分析最大瓶颈
|
||||
@ -1866,11 +1876,11 @@ class VRManager(DirectObject):
|
||||
recommendations = []
|
||||
|
||||
# FPS相关建议
|
||||
if stats['vr_fps'] < 72:
|
||||
if stats['vr_fps'] < 60:
|
||||
recommendations.append(" ⚠️ VR帧率过低,可能影响体验")
|
||||
|
||||
# 帧时间相关建议
|
||||
if stats['frame_time_avg'] > 13.9: # 72fps = 13.9ms
|
||||
if stats['frame_time_avg'] > 16.7: # 60fps = 16.7ms
|
||||
recommendations.append(" ⚠️ 平均帧时间过高,建议降低渲染质量")
|
||||
|
||||
if stats['frame_time_max'] > 50:
|
||||
@ -2658,7 +2668,7 @@ class VRManager(DirectObject):
|
||||
# Running Start模式信息
|
||||
print("🎯 Running Start模式:")
|
||||
print(f" 预测时间: {self.use_prediction_time * 1000:.1f}ms")
|
||||
print(f" 模式: Valve Running Start - Submit后立即获取姿态")
|
||||
print(f" 模式: Valve Running Start - 帧开始时获取姿态")
|
||||
|
||||
# 测试管线统计
|
||||
if self.enable_pipeline_monitoring:
|
||||
@ -3437,6 +3447,13 @@ class VRManager(DirectObject):
|
||||
print("❌ VR初始化失败")
|
||||
return False
|
||||
|
||||
# 🔧 关键修复:确保测试模式的纹理资源已初始化
|
||||
# 这解决了测试模式纹理提交失败导致的36FPS问题
|
||||
print("🔧 检查VR测试模式纹理资源...")
|
||||
if not self._ensure_test_mode_textures():
|
||||
print("❌ VR测试模式纹理资源初始化失败")
|
||||
return False
|
||||
|
||||
# 设置测试模式
|
||||
self.vr_test_mode = True
|
||||
self.test_display_mode = display_mode
|
||||
@ -3483,6 +3500,157 @@ class VRManager(DirectObject):
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def _ensure_test_mode_textures(self):
|
||||
"""确保VR测试模式的纹理资源已正确初始化
|
||||
|
||||
这解决了测试模式启用纹理提交时的36FPS问题:
|
||||
- VR测试模式可能跳过了VR渲染缓冲区的初始化
|
||||
- submit_texture()需要有效的texture ID和OpenVR Texture对象
|
||||
- 如果未初始化会导致提交失败,造成帧率减半
|
||||
"""
|
||||
try:
|
||||
print(" 检查VR渲染缓冲区...")
|
||||
|
||||
# 检查VR渲染缓冲区是否存在
|
||||
buffers_exist = (
|
||||
hasattr(self, 'vr_left_eye_buffer') and self.vr_left_eye_buffer and
|
||||
hasattr(self, 'vr_right_eye_buffer') and self.vr_right_eye_buffer
|
||||
)
|
||||
|
||||
if not buffers_exist:
|
||||
print(" ⚠️ VR渲染缓冲区不存在,正在创建...")
|
||||
if not self._setup_vr_render_buffers():
|
||||
print(" ❌ VR渲染缓冲区创建失败")
|
||||
return False
|
||||
print(" ✅ VR渲染缓冲区创建成功")
|
||||
else:
|
||||
print(" ✅ VR渲染缓冲区已存在")
|
||||
|
||||
# 检查纹理ID是否已缓存
|
||||
print(" 检查纹理ID缓存...")
|
||||
texture_ids_cached = (
|
||||
hasattr(self, 'left_texture_id') and self.left_texture_id and self.left_texture_id > 0 and
|
||||
hasattr(self, 'right_texture_id') and self.right_texture_id and self.right_texture_id > 0
|
||||
)
|
||||
|
||||
if not texture_ids_cached:
|
||||
print(" ⚠️ 纹理ID未缓存,正在准备纹理...")
|
||||
if not self._prepare_and_cache_textures():
|
||||
print(" ❌ 纹理准备和缓存失败")
|
||||
return False
|
||||
print(f" ✅ 纹理ID缓存成功 - 左眼:{self.left_texture_id}, 右眼:{self.right_texture_id}")
|
||||
else:
|
||||
print(f" ✅ 纹理ID已缓存 - 左眼:{self.left_texture_id}, 右眼:{self.right_texture_id}")
|
||||
|
||||
# 检查OpenVR Texture对象是否已创建
|
||||
print(" 检查OpenVR Texture对象...")
|
||||
ovr_textures_exist = (
|
||||
hasattr(self, '_left_ovr_texture') and self._left_ovr_texture and
|
||||
hasattr(self, '_right_ovr_texture') and self._right_ovr_texture
|
||||
)
|
||||
|
||||
if not ovr_textures_exist:
|
||||
print(" ⚠️ OpenVR Texture对象未创建,正在创建...")
|
||||
self._create_cached_ovr_textures()
|
||||
print(" ✅ OpenVR Texture对象创建成功")
|
||||
else:
|
||||
print(" ✅ OpenVR Texture对象已存在")
|
||||
|
||||
print(" ✅ VR测试模式纹理资源检查完成,可安全启用纹理提交")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ VR测试模式纹理资源检查失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def _create_cached_ovr_textures(self):
|
||||
"""创建缓存的OpenVR Texture对象 - 避免每帧创建新对象"""
|
||||
try:
|
||||
import openvr
|
||||
self._left_ovr_texture = openvr.Texture_t()
|
||||
self._right_ovr_texture = openvr.Texture_t()
|
||||
|
||||
# 设置固定属性(这些不变)
|
||||
self._left_ovr_texture.eType = openvr.TextureType_OpenGL
|
||||
self._left_ovr_texture.eColorSpace = openvr.ColorSpace_Gamma
|
||||
self._right_ovr_texture.eType = openvr.TextureType_OpenGL
|
||||
self._right_ovr_texture.eColorSpace = openvr.ColorSpace_Gamma
|
||||
|
||||
print("✅ OpenVR Texture对象缓存已创建")
|
||||
except Exception as e:
|
||||
print(f"⚠️ OpenVR Texture对象创建失败: {e}")
|
||||
# 不抛出异常,使用备用方案
|
||||
|
||||
def _batch_submit_textures(self):
|
||||
"""批量提交两眼纹理 - OpenVR最佳实践
|
||||
|
||||
基于官方hellovr示例的实现:
|
||||
- 两眼都渲染完成后,快速连续提交
|
||||
- 减少submit阻塞时间,避免错过VSync窗口
|
||||
- 这是解决36FPS问题的关键
|
||||
"""
|
||||
try:
|
||||
if not self.vr_compositor:
|
||||
return False
|
||||
|
||||
# 检查纹理是否准备好
|
||||
if not (self.vr_left_texture and self.vr_right_texture):
|
||||
return False
|
||||
|
||||
# 🚀 关键:快速连续提交两眼,最小化阻塞时间
|
||||
# 这符合OpenVR官方示例的做法
|
||||
success_left = False
|
||||
success_right = False
|
||||
|
||||
# 提交左眼纹理
|
||||
try:
|
||||
self.submit_texture(openvr.Eye_Left, self.vr_left_texture)
|
||||
success_left = True
|
||||
except Exception as e:
|
||||
print(f"❌ 批量提交左眼失败: {e}")
|
||||
|
||||
# 立即提交右眼纹理(不等待)
|
||||
try:
|
||||
self.submit_texture(openvr.Eye_Right, self.vr_right_texture)
|
||||
success_right = True
|
||||
except Exception as e:
|
||||
print(f"❌ 批量提交右眼失败: {e}")
|
||||
|
||||
# 🚀 关键修复:调用PostPresentHandoff解除compositor阻塞
|
||||
# 这是解决36FPS问题的核心 - 确保compositor不会等待VSync
|
||||
if success_left and success_right:
|
||||
try:
|
||||
# PostPresentHandoff告诉compositor我们已完成帧处理
|
||||
# 防止compositor等待下一个VSync周期
|
||||
if hasattr(self.vr_compositor, 'postPresentHandoff'):
|
||||
self.vr_compositor.postPresentHandoff()
|
||||
elif hasattr(self.vr_compositor, 'PostPresentHandoff'):
|
||||
self.vr_compositor.PostPresentHandoff()
|
||||
else:
|
||||
# 备用方案:如果没有PostPresentHandoff,记录警告
|
||||
if not hasattr(self, '_post_present_warning_logged'):
|
||||
print("⚠️ PostPresentHandoff方法未找到,可能影响时序")
|
||||
self._post_present_warning_logged = True
|
||||
except Exception as handoff_error:
|
||||
if not hasattr(self, '_handoff_error_logged'):
|
||||
print(f"⚠️ PostPresentHandoff调用失败: {handoff_error}")
|
||||
self._handoff_error_logged = True
|
||||
|
||||
# 记录批量提交状态(仅首次成功时)
|
||||
if not hasattr(self, '_batch_submit_success_logged'):
|
||||
print("✅ OpenVR批量提交模式+PostPresentHandoff已启用")
|
||||
print(" 这应该解决36FPS → 72FPS的问题")
|
||||
self._batch_submit_success_logged = True
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 批量提交纹理失败: {e}")
|
||||
return False
|
||||
|
||||
def disable_vr_test_mode(self):
|
||||
"""禁用VR测试模式"""
|
||||
try:
|
||||
|
||||
@ -503,6 +503,26 @@ class MainWindow(QMainWindow):
|
||||
self.vrTestModeAction.setCheckable(True)
|
||||
self.vrTestModeAction.setChecked(False) # 默认关闭
|
||||
|
||||
# VR测试模式调试子菜单
|
||||
self.vrTestDebugMenu = self.vrDebugMenu.addMenu('测试模式调试')
|
||||
|
||||
# 渐进式功能开关
|
||||
self.vrTestSubmitTextureAction = self.vrTestDebugMenu.addAction('启用纹理提交')
|
||||
self.vrTestSubmitTextureAction.setCheckable(True)
|
||||
self.vrTestSubmitTextureAction.setChecked(False) # 默认关闭
|
||||
|
||||
self.vrTestWaitPosesAction = self.vrTestDebugMenu.addAction('启用姿态等待')
|
||||
self.vrTestWaitPosesAction.setCheckable(True)
|
||||
self.vrTestWaitPosesAction.setChecked(False) # 默认关闭
|
||||
|
||||
self.vrTestDebugMenu.addSeparator()
|
||||
|
||||
# 快捷测试预设
|
||||
self.vrTestStep1Action = self.vrTestDebugMenu.addAction('步骤1: 只启用纹理提交')
|
||||
self.vrTestStep2Action = self.vrTestDebugMenu.addAction('步骤2: 只启用姿态等待')
|
||||
self.vrTestStep3Action = self.vrTestDebugMenu.addAction('步骤3: 同时启用两者')
|
||||
self.vrTestResetAction = self.vrTestDebugMenu.addAction('重置: 禁用所有功能')
|
||||
|
||||
self.vrDebugSettingsAction = self.vrDebugMenu.addAction('调试设置...')
|
||||
|
||||
# 初始状态下禁用退出VR选项
|
||||
@ -984,6 +1004,15 @@ class MainWindow(QMainWindow):
|
||||
self.vrPoseUpdateTaskAction.triggered.connect(lambda: self.onSetVRPoseStrategy('update_task'))
|
||||
self.vrTestPipelineAction.triggered.connect(self.onTestVRPipeline)
|
||||
self.vrTestModeAction.triggered.connect(self.onToggleVRTestMode)
|
||||
|
||||
# 连接VR测试模式调试菜单事件
|
||||
self.vrTestSubmitTextureAction.triggered.connect(self.onToggleVRTestSubmitTexture)
|
||||
self.vrTestWaitPosesAction.triggered.connect(self.onToggleVRTestWaitPoses)
|
||||
self.vrTestStep1Action.triggered.connect(lambda: self.onSetVRTestStep(1))
|
||||
self.vrTestStep2Action.triggered.connect(lambda: self.onSetVRTestStep(2))
|
||||
self.vrTestStep3Action.triggered.connect(lambda: self.onSetVRTestStep(3))
|
||||
self.vrTestResetAction.triggered.connect(lambda: self.onSetVRTestStep(0))
|
||||
|
||||
self.vrDebugSettingsAction.triggered.connect(self.onShowVRDebugSettings)
|
||||
|
||||
|
||||
@ -2554,6 +2583,51 @@ class MainWindow(QMainWindow):
|
||||
self.vrTestModeAction.setChecked(False)
|
||||
QMessageBox.critical(self, "错误", f"切换VR测试模式时发生错误:\n{str(e)}")
|
||||
|
||||
def onToggleVRTestSubmitTexture(self):
|
||||
"""切换VR测试模式纹理提交功能"""
|
||||
try:
|
||||
if hasattr(self.world, 'vr_manager') and self.world.vr_manager:
|
||||
enabled = self.vrTestSubmitTextureAction.isChecked()
|
||||
self.world.vr_manager.set_test_mode_features(submit_texture=enabled)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"设置纹理提交功能时发生错误:\n{str(e)}")
|
||||
|
||||
def onToggleVRTestWaitPoses(self):
|
||||
"""切换VR测试模式姿态等待功能"""
|
||||
try:
|
||||
if hasattr(self.world, 'vr_manager') and self.world.vr_manager:
|
||||
enabled = self.vrTestWaitPosesAction.isChecked()
|
||||
self.world.vr_manager.set_test_mode_features(wait_poses=enabled)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"设置姿态等待功能时发生错误:\n{str(e)}")
|
||||
|
||||
def onSetVRTestStep(self, step):
|
||||
"""设置VR测试步骤"""
|
||||
try:
|
||||
if hasattr(self.world, 'vr_manager') and self.world.vr_manager:
|
||||
if step == 0: # 重置
|
||||
self.world.vr_manager.set_test_mode_features(submit_texture=False, wait_poses=False)
|
||||
self.vrTestSubmitTextureAction.setChecked(False)
|
||||
self.vrTestWaitPosesAction.setChecked(False)
|
||||
QMessageBox.information(self, "VR测试", "已重置为基线状态:两个功能都禁用")
|
||||
elif step == 1: # 只启用纹理提交
|
||||
self.world.vr_manager.set_test_mode_features(submit_texture=True, wait_poses=False)
|
||||
self.vrTestSubmitTextureAction.setChecked(True)
|
||||
self.vrTestWaitPosesAction.setChecked(False)
|
||||
QMessageBox.information(self, "VR测试", "步骤1:只启用纹理提交\n观察FPS变化来判断submit_texture是否影响性能")
|
||||
elif step == 2: # 只启用姿态等待
|
||||
self.world.vr_manager.set_test_mode_features(submit_texture=False, wait_poses=True)
|
||||
self.vrTestSubmitTextureAction.setChecked(False)
|
||||
self.vrTestWaitPosesAction.setChecked(True)
|
||||
QMessageBox.information(self, "VR测试", "步骤2:只启用姿态等待\n观察FPS变化来判断waitGetPoses是否影响性能")
|
||||
elif step == 3: # 同时启用两者
|
||||
self.world.vr_manager.set_test_mode_features(submit_texture=True, wait_poses=True)
|
||||
self.vrTestSubmitTextureAction.setChecked(True)
|
||||
self.vrTestWaitPosesAction.setChecked(True)
|
||||
QMessageBox.information(self, "VR测试", "步骤3:同时启用两者\n这应该完全复现普通VR模式的36FPS问题")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"设置VR测试步骤时发生错误:\n{str(e)}")
|
||||
|
||||
def setup_main_window(world,path = None):
|
||||
"""设置主窗口的便利函数"""
|
||||
app = QApplication.instance()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user