""" VR测试调试子系统 负责VR测试模式、HUD显示和性能测试功能 """ import time class VRTestMode: """VR测试模式系统 功能: - VR内容直接显示在屏幕上(不提交给OpenVR) - 实时性能监控HUD - 支持左眼/右眼/立体三种显示模式 - 渐进式测试功能(纹理提交、姿态等待) - 性能测试和调试工具 """ def __init__(self, vr_manager): """初始化VR测试模式系统 Args: vr_manager: VRManager实例的引用 """ self.vr_manager = vr_manager # ===== 测试模式状态 ===== self.vr_test_mode = False # 是否启用VR测试模式 self.test_display_mode = 'stereo' # 'left', 'right', 'stereo' self.test_mode_initialized = False # 测试模式是否已初始化 # ===== 显示组件 ===== self.test_display_quad = None # 测试显示的四边形 self.test_right_quad = None # 右眼显示的四边形(立体模式) self.stereo_display_created = False # 立体显示是否已创建 # ===== HUD组件 ===== self.test_performance_hud = None # 性能HUD self.test_performance_text = None # 性能文本节点 self.hud_update_counter = 0 # HUD更新计数器 self.hud_update_interval = 30 # HUD更新间隔(帧数), 30帧约0.5秒@60fps # ===== 测试模式调试选项 ===== self.test_mode_submit_texture = False # 是否在测试模式提交纹理到OpenVR self.test_mode_wait_poses = False # 是否在测试模式调用waitGetPoses # ===== 纹理缓存 ===== self._left_ovr_texture = None # 缓存的左眼OpenVR Texture对象 self._right_ovr_texture = None # 缓存的右眼OpenVR Texture对象 # ========== 测试模式控制方法 ========== def enable_vr_test_mode(self, display_mode='stereo'): """启用VR测试模式 - 将VR渲染直接显示在屏幕上 Args: display_mode: 显示模式 - 'stereo': 左右眼并排显示 - 'left': 只显示左眼 - 'right': 只显示右眼 """ if not self.vr_manager.is_vr_available(): print("❌ VR系统不可用,无法启动测试模式") return False try: print(f"🧪 启动VR测试模式 - 显示模式: {display_mode}") # 初始化VR系统(如果还没初始化) if not self.vr_manager.vr_initialized: if not self.vr_manager.initialize_vr(): 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 # 启用VR渲染但不提交给OpenVR if not self.vr_manager.vr_enabled: # 启用VR渲染流程(但会在回调中跳过提交) self.vr_manager.vr_enabled = True self.vr_manager._disable_main_cam() # 设置高帧率用于测试 if hasattr(self.vr_manager.world, 'qtWidget') and self.vr_manager.world.qtWidget: if hasattr(self.vr_manager.world.qtWidget, 'synchronizer'): self.vr_manager.world.qtWidget.synchronizer.setInterval(int(1000/144)) print("✓ 测试模式:Qt Timer设置为144Hz") # 初始化测试显示系统 if not self._initialize_test_display(): print("❌ 测试显示系统初始化失败") return False # 创建性能HUD if not self._initialize_test_performance_hud(): print("❌ 性能HUD初始化失败") return False # 恢复主相机以查看测试内容 self.vr_manager._enable_main_cam() # 重置HUD更新计数器 self.hud_update_counter = 0 print("✅ VR测试模式已启用") print(" - VR内容将显示在屏幕上") print(" - 不会向OpenVR提交纹理") print(" - 可以准确测量纯渲染性能") print(f" - 当前显示模式: {display_mode}") return True except Exception as e: print(f"❌ 启动VR测试模式失败: {e}") import traceback traceback.print_exc() return False def disable_vr_test_mode(self): """禁用VR测试模式""" try: print("🧪 禁用VR测试模式...") # 清理测试显示 self._cleanup_test_display() # 清理性能HUD self._cleanup_test_performance_hud() # 关闭测试模式 self.vr_test_mode = False self.test_mode_initialized = False # 重置HUD更新计数器 self.hud_update_counter = 0 # 恢复正常VR模式或禁用VR if self.vr_manager.vr_enabled: print(" 选择: [1] 恢复正常VR模式 [2] 完全禁用VR") # 这里可以让用户选择,现在默认禁用VR self.vr_manager.disable_vr() print("✅ VR测试模式已禁用") return True except Exception as e: print(f"❌ 禁用VR测试模式失败: {e}") return False def switch_test_display_mode(self, display_mode): """切换测试显示模式 Args: display_mode: 'stereo', 'left', 'right' """ if not self.vr_test_mode: print("⚠️ 请先启用VR测试模式") return False if display_mode not in ['stereo', 'left', 'right']: print(f"⚠️ 无效的显示模式: {display_mode}") return False print(f"🧪 切换显示模式: {self.test_display_mode} → {display_mode}") # 如果切换模式,需要清理旧的显示并重置标志位 if self.test_display_mode != display_mode: self._cleanup_test_display() self.test_display_mode = display_mode # 更新显示 self._update_test_display() return True # ========== 纹理管理方法 ========== 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_manager, 'vr_left_eye_buffer') and self.vr_manager.vr_left_eye_buffer and hasattr(self.vr_manager, 'vr_right_eye_buffer') and self.vr_manager.vr_right_eye_buffer ) if not buffers_exist: print(" ⚠️ VR渲染缓冲区不存在,正在创建...") # 🐛 BUG修复: 原代码调用了不存在的 _setup_vr_render_buffers() # 应该调用 _create_vr_buffers() 或 _create_vr_buffers_with_pipeline() if hasattr(self.vr_manager.world, 'render_pipeline') and self.vr_manager.world.render_pipeline: success = self.vr_manager._create_vr_buffers_with_pipeline() else: success = self.vr_manager._create_vr_buffers() if not success: print(" ❌ VR渲染缓冲区创建失败") return False print(" ✅ VR渲染缓冲区创建成功") else: print(" ✅ VR渲染缓冲区已存在") # 检查纹理ID是否已缓存 print(" 检查纹理ID缓存...") texture_ids_cached = ( hasattr(self.vr_manager, 'left_texture_id') and self.vr_manager.left_texture_id and self.vr_manager.left_texture_id > 0 and hasattr(self.vr_manager, 'right_texture_id') and self.vr_manager.right_texture_id and self.vr_manager.right_texture_id > 0 ) if not texture_ids_cached: print(" ⚠️ 纹理ID未缓存,正在准备纹理...") if not self.vr_manager._prepare_and_cache_textures(): print(" ❌ 纹理准备和缓存失败") return False print(f" ✅ 纹理ID缓存成功 - 左眼:{self.vr_manager.left_texture_id}, 右眼:{self.vr_manager.right_texture_id}") else: print(f" ✅ 纹理ID已缓存 - 左眼:{self.vr_manager.left_texture_id}, 右眼:{self.vr_manager.right_texture_id}") # 检查OpenVR Texture对象是否已创建 print(" 检查OpenVR Texture对象...") ovr_textures_exist = ( self._left_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}") # 不抛出异常,使用备用方案 # 注意: _batch_submit_textures() 方法已移至VRManager # 因为它是VR渲染的核心功能,不仅用于测试模式 # ========== 显示系统方法 ========== def _initialize_test_display(self): """初始化测试显示系统""" try: print("🔧 正在初始化测试显示系统...") # 导入必要的Panda3D组件 from panda3d.core import CardMaker, PandaNode # 创建显示四边形 cm = CardMaker("vr_test_display") if self.test_display_mode == 'stereo': # 并排显示:左眼在左半屏,右眼在右半屏 cm.setFrame(-1, 1, -0.5, 0.5) # 全屏 else: # 单眼显示:占据整个屏幕 cm.setFrame(-1, 1, -1, 1) # 创建节点 self.test_display_quad = self.vr_manager.world.render2d.attachNewNode(cm.generate()) # 设置初始纹理 self._update_test_display() # 设置渲染状态 self.test_display_quad.setTransparency(0) # 不透明 self.test_display_quad.setBin("background", 0) # 背景层 self.test_mode_initialized = True print("✅ 测试显示系统初始化完成") return True except Exception as e: print(f"❌ 测试显示系统初始化失败: {e}") import traceback traceback.print_exc() return False def _update_test_display(self): """更新测试显示内容""" try: if not self.test_mode_initialized: return # 根据显示模式设置纹理 if self.test_display_mode == 'left': if not self.test_display_quad: return if self.vr_manager.vr_left_texture: self.test_display_quad.setTexture(self.vr_manager.vr_left_texture) elif self.test_display_mode == 'right': if not self.test_display_quad: return if self.vr_manager.vr_right_texture: self.test_display_quad.setTexture(self.vr_manager.vr_right_texture) elif self.test_display_mode == 'stereo': # 立体显示:只在第一次创建,后续只更新纹理 if not self.stereo_display_created: self._create_stereo_display() else: # 只更新纹理,不重新创建 if self.test_display_quad and self.vr_manager.vr_left_texture: self.test_display_quad.setTexture(self.vr_manager.vr_left_texture) if self.test_right_quad and self.vr_manager.vr_right_texture: self.test_right_quad.setTexture(self.vr_manager.vr_right_texture) except Exception as e: print(f"⚠️ 更新测试显示失败: {e}") def _create_stereo_display(self): """创建左右眼并排显示""" try: # 如果已经创建,直接返回 if self.stereo_display_created and self.test_display_quad and self.test_right_quad: return # 移除旧的显示 if self.test_display_quad: self.test_display_quad.removeNode() if self.test_right_quad: self.test_right_quad.removeNode() from panda3d.core import CardMaker # 创建左眼显示 left_cm = CardMaker("vr_test_left") left_cm.setFrame(-1, 0, -1, 1) # 左半屏 left_quad = self.vr_manager.world.render2d.attachNewNode(left_cm.generate()) if self.vr_manager.vr_left_texture: left_quad.setTexture(self.vr_manager.vr_left_texture) # 创建右眼显示 right_cm = CardMaker("vr_test_right") right_cm.setFrame(0, 1, -1, 1) # 右半屏 right_quad = self.vr_manager.world.render2d.attachNewNode(right_cm.generate()) if self.vr_manager.vr_right_texture: right_quad.setTexture(self.vr_manager.vr_right_texture) # 保存引用 self.test_display_quad = left_quad # 保存左眼引用 self.test_right_quad = right_quad # 额外保存右眼引用 self.stereo_display_created = True # 标记已创建 print("✓ 立体显示已创建") except Exception as e: print(f"⚠️ 创建立体显示失败: {e}") self.stereo_display_created = False def _cleanup_test_display(self): """清理测试显示""" try: if self.test_display_quad: self.test_display_quad.removeNode() self.test_display_quad = None if hasattr(self, 'test_right_quad') and self.test_right_quad: self.test_right_quad.removeNode() self.test_right_quad = None # 重置立体显示标志位 self.stereo_display_created = False print("✓ 测试显示已清理") except Exception as e: print(f"⚠️ 清理测试显示失败: {e}") # ========== HUD系统方法 ========== def _initialize_test_performance_hud(self): """初始化性能HUD""" try: print("🔧 正在初始化性能HUD...") from direct.gui.OnscreenText import OnscreenText from panda3d.core import TextNode # 创建性能文本显示 self.test_performance_text = OnscreenText( text="初始化中...", pos=(-0.95, 0.9), # 左上角,调整到屏幕内 scale=0.05, fg=(1, 1, 0, 1), # 黄色 align=TextNode.ALeft, shadow=(0, 0, 0, 0.5), # 黑色阴影 parent=self.vr_manager.world.render2d ) print("✅ 性能HUD初始化完成") return True except Exception as e: print(f"❌ 性能HUD初始化失败: {e}") return False def _update_test_performance_hud(self): """更新性能HUD显示(限制更新频率避免重影)""" try: if not self.test_performance_text or not self.vr_test_mode: return # 增加计数器 self.hud_update_counter += 1 # 只在指定间隔更新文本,避免重影 if self.hud_update_counter < self.hud_update_interval: return # 重置计数器 self.hud_update_counter = 0 # 收集性能数据 perf_mon = self.vr_manager.performance_monitor left_render_time = perf_mon.left_render_time if perf_mon else 0 right_render_time = perf_mon.right_render_time if perf_mon else 0 total_render_time = left_render_time + right_render_time # 计算FPS current_fps = perf_mon.vr_fps if perf_mon else 0 # 获取系统性能 cpu_usage = perf_mon.cpu_usage if perf_mon else 0 memory_usage = perf_mon.memory_usage if perf_mon else 0 gpu_usage = perf_mon.gpu_usage if perf_mon else 0 # 构建显示文本 hud_text = f"""VR TEST MODE - {self.test_display_mode.upper()} Render Performance: FPS: {current_fps:.1f} Left Eye: {left_render_time:.2f}ms Right Eye: {right_render_time:.2f}ms Total: {total_render_time:.2f}ms System Performance: CPU: {cpu_usage:.1f}% Memory: {memory_usage:.1f}% GPU: {gpu_usage:.1f}% Render Count: Left Eye: {perf_mon.left_render_count if perf_mon else 0} Right Eye: {perf_mon.right_render_count if perf_mon else 0} Target: <13.9ms@72Hz Status: {'GOOD' if total_render_time < 10 else 'HIGH' if total_render_time < 16 else 'SLOW'} Hotkeys: F1=Left F2=Right F3=SideBySide ESC=Exit Test Mode""" # 更新文本 self.test_performance_text.setText(hud_text) except Exception as e: print(f"⚠️ 更新性能HUD失败: {e}") def _cleanup_test_performance_hud(self): """清理性能HUD""" try: if self.test_performance_text: self.test_performance_text.destroy() self.test_performance_text = None print("✓ 性能HUD已清理") except Exception as e: print(f"⚠️ 清理性能HUD失败: {e}") # ========== 状态查询方法 ========== def get_test_mode_status(self): """获取测试模式状态""" if not self.vr_test_mode: return None perf_mon = self.vr_manager.performance_monitor left_render_time = perf_mon.left_render_time if perf_mon else 0 right_render_time = perf_mon.right_render_time if perf_mon else 0 total_render_time = left_render_time + right_render_time return { 'test_mode_enabled': self.vr_test_mode, 'display_mode': self.test_display_mode, 'vr_fps': perf_mon.vr_fps if perf_mon else 0, 'left_render_time': left_render_time, 'right_render_time': right_render_time, 'total_render_time': total_render_time, 'left_render_count': perf_mon.left_render_count if perf_mon else 0, 'right_render_count': perf_mon.right_render_count if perf_mon else 0, 'performance_rating': ('excellent' if total_render_time < 10 else 'good' if total_render_time < 16 else 'poor') } def get_test_mode_features(self): """获取当前测试模式功能设置""" return { 'submit_texture': self.test_mode_submit_texture, 'wait_poses': self.test_mode_wait_poses, 'test_mode_active': self.vr_test_mode } def set_test_mode_features(self, submit_texture=None, wait_poses=None): """设置测试模式功能开关 - 用于渐进式调试 Args: submit_texture: 是否在测试模式启用纹理提交(True/False/None=保持当前) wait_poses: 是否在测试模式启用waitGetPoses(True/False/None=保持当前) """ if submit_texture is not None: self.test_mode_submit_texture = submit_texture if wait_poses is not None: self.test_mode_wait_poses = wait_poses print(f"🔧 VR测试模式功能设置:") print(f" 纹理提交: {'启用' if self.test_mode_submit_texture else '禁用'}") print(f" 姿态等待: {'启用' if self.test_mode_wait_poses else '禁用'}") # 如果当前在测试模式,输出预期效果 if self.vr_test_mode: print(f" 当前测试模式已启用,设置将立即生效") else: print(f" 设置已保存,将在下次启用测试模式时生效") # ========== 性能测试方法 ========== def run_vr_performance_test(self, duration_seconds=30, display_mode='stereo'): """运行VR性能测试 - 简单的测试入口方法 Args: duration_seconds: 测试持续时间(秒) display_mode: 显示模式 ('stereo', 'left', 'right') Returns: dict: 测试结果统计 """ print("🧪 ======= VR性能测试开始 =======") print(f" 测试模式: {display_mode}") print(f" 测试时长: {duration_seconds}秒") print(" 按ESC键提前退出测试") print(" F1=左眼 F2=右眼 F3=并排显示") print("=================================") # 启动测试模式 if not self.enable_vr_test_mode(display_mode): print("❌ VR测试模式启动失败") return None # 记录测试开始状态 test_start_time = time.time() perf_mon = self.vr_manager.performance_monitor start_frame_count = perf_mon.frame_count if perf_mon else 0 print("✅ VR测试模式已启动") print(" - VR内容直接显示在屏幕上") print(" - 无OpenVR纹理提交开销") print(" - 实时性能监控已启用") print(f" - 当前显示: {display_mode.upper()}模式") print("\n开始性能测试...") # 等待测试完成(用户手动停止或超时) print(f"\n📊 测试将运行 {duration_seconds} 秒") print(" 实时性能数据显示在屏幕左上角") print(" 观察帧率和渲染时间变化") # 这里可以添加自动停止逻辑,但现在让用户手动控制 print(f"\n⏰ 请观察 {duration_seconds} 秒后手动调用 disable_vr_test_mode() 停止测试") print(" 或者随时调用 get_test_mode_status() 查看当前状态") return { 'status': 'running', 'start_time': test_start_time, 'start_frame': start_frame_count, 'display_mode': display_mode, 'instructions': { 'stop_test': 'vr_manager.test_mode.disable_vr_test_mode()', 'check_status': 'vr_manager.test_mode.get_test_mode_status()', 'switch_mode': 'vr_manager.test_mode.switch_test_display_mode("left/right/stereo")' } }