diff --git a/core/vr_manager.py b/core/vr_manager.py index a9242f9b..7d60bdf9 100644 --- a/core/vr_manager.py +++ b/core/vr_manager.py @@ -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: diff --git a/ui/main_window.py b/ui/main_window.py index b7dfc0f2..b40718ba 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -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()