""" VR管理器模块 负责VR功能的初始化、渲染和交互: - OpenVR/SteamVR集成 - VR头显跟踪和渲染 - VR控制器交互 - VR模式切换 """ import sys import gc import numpy as np from panda3d.core import ( WindowProperties, GraphicsPipe, FrameBufferProperties, GraphicsOutput, Texture, Camera, PerspectiveLens, MatrixLens, Mat4, Vec3, TransformState, RenderState, CardMaker, BitMask32, PandaNode, NodePath, LMatrix4, LVector3, LVector4, CS_yup_right, CS_default, PythonCallbackObject ) from direct.task import Task from direct.showbase.DirectObject import DirectObject try: import openvr OPENVR_AVAILABLE = True except ImportError: OPENVR_AVAILABLE = False print("警告: OpenVR未安装,VR功能将不可用") # 导入手柄控制器、动作系统、交互系统、摇杆系统和传送系统 from core.vr.tracking.controllers import LeftController, RightController from core.vr.interaction.actions import VRActionManager from core.vr.interaction.grab import VRInteractionManager from core.vr.interaction.joystick import VRJoystickManager from core.vr.interaction.teleport import VRTeleportSystem from core.vr.performance.monitoring import VRPerformanceMonitor from core.vr.performance.optimization import VROptimization from core.vr.testing import VRTestMode from enum import Enum class VRRenderMode(Enum): """VR渲染模式枚举""" NORMAL = "normal" # 普通渲染模式 RENDER_PIPELINE = "render_pipeline" # RenderPipeline高级渲染模式 class VRManager(DirectObject): """VR管理器类 - 处理所有VR相关功能""" def __init__(self, world): """初始化VR管理器 Args: world: 主世界对象引用 """ super().__init__() self.world = world self.vr_system = None self.vr_enabled = False self.vr_initialized = False # VR渲染相关 self.vr_left_eye_buffer = None self.vr_right_eye_buffer = None self.vr_left_camera = None self.vr_right_camera = None self.vr_compositor = None # VR纹理和ID缓存 - 修复重复准备问题 self.vr_left_texture = None self.vr_right_texture = None self.left_texture_id = None # 缓存左眼纹理的OpenGL ID self.right_texture_id = None # 缓存右眼纹理的OpenGL ID self.textures_prepared = False # 标记纹理是否已准备 # VR跟踪数据 self.hmd_pose = Mat4.identMat() self.controller_poses = {} self.tracked_device_poses = [] self.poses = None # OpenVR渲染姿态数组 self.game_poses = None # OpenVR游戏逻辑姿态数组 # 🚀 立即初始化对象池和GC控制(在其他组件之前) # 对象池和优化系统 - 模块化重构 try: self.optimization = VROptimization(self) self.optimization._initialize_object_pools() print("✓ VR对象池和优化系统初始化完成") except Exception as e: print(f"⚠️ VR对象池和优化系统初始化失败: {e}") self.optimization = None # VR渲染参数 self.eye_width = 1080 self.eye_height = 1200 self.near_clip = 0.1 self.far_clip = 1000.0 # VR分辨率缩放优化 - 现在由优化模块管理 self.resolution_scale = 0.75 # 默认0.75倍分辨率,性能和质量平衡 self.base_eye_width = 1080 # 原始推荐分辨率 self.base_eye_height = 1200 self.scaled_eye_width = 1080 # 实际使用的缩放后分辨率 self.scaled_eye_height = 1200 # VR质量预设 self.quality_presets = { 'performance': 0.6, # 性能模式 - 约60%分辨率 'balanced': 0.75, # 平衡模式 - 约75%分辨率 'quality': 1.0 # 质量模式 - 100%分辨率 } self.current_quality_preset = 'balanced' # 默认平衡模式 # 🎨 VR渲染模式配置 - RenderPipeline集成 self.vr_render_mode = VRRenderMode.NORMAL # 默认使用普通渲染模式 self.render_pipeline_enabled = False # RenderPipeline是否已启用 self.vr_pipeline_left_target = None # 左眼RenderPipeline渲染目标 self.vr_pipeline_right_target = None # 右眼RenderPipeline渲染目标 self.pipeline_resolution_scale = 0.75 # RenderPipeline模式下的分辨率缩放(性能优化) self.vr_pipeline_controller = None # VR Pipeline控制器(管理完整的VR渲染管线) # RenderPipeline VR优化配置 self.pipeline_vr_config = { 'enable_shadows': True, # 启用阴影 'enable_ao': True, # 启用环境光遮蔽 'enable_bloom': False, # 禁用泛光(VR性能考虑) 'enable_motion_blur': False, # 禁用运动模糊(VR中会引起不适) 'enable_ssr': False, # 禁用屏幕空间反射(性能密集) 'shadow_quality': 'medium', # 阴影质量:low/medium/high 'ao_quality': 'low', # AO质量:low/medium/high } # VR任务 self.vr_task = None # VR锚点层级系统 self.tracking_space = None self.hmd_anchor = None self.left_eye_anchor = None self.right_eye_anchor = None # 坐标系转换矩阵 - 使用Panda3D内置方法 self.coord_mat = LMatrix4.convert_mat(CS_yup_right, CS_default) self.coord_mat_inv = LMatrix4.convert_mat(CS_default, CS_yup_right) # OpenVR 帧ID跟踪(防止重复提交) self.openvr_frame_id = 0 self.left_eye_last_render_frame = -1 self.right_eye_last_render_frame = -1 # waitGetPoses调用策略配置 - 使用高性能的update_task策略 self.use_prediction_time = 0.011 # 11ms的预测时间 - OpenVR标准值,平衡准确性和延迟 self.poses_updated_in_task = True # 始终在更新任务中获取姿态(Running Start模式) # 🎨 初始化VR配置管理器 try: from core.vr.config.vr_config import VRConfigManager self.config_manager = VRConfigManager() # 加载配置并应用 self.config_manager.apply_config_to_vr_manager(self) print("✓ VR配置管理器初始化完成并已加载配置") except Exception as e: print(f"⚠️ VR配置管理器初始化失败: {e}") self.config_manager = None # VR提交策略 - 基于参考实现 self.submit_together = True # 是否在right_cb中同时提交左右眼 # Running Start标记 - Valve最佳实践 self._waitgetposes_called_this_frame = False # 姿态缓存 - 修复时序不匹配 self._cached_render_poses = None # 用于渲染的缓存姿态 self._first_frame = True # 首帧标记 # ATW控制选项 - 备选方案 self.disable_async_reprojection = False # 是否禁用异步重投影 # VR手柄控制器 self.left_controller = None self.right_controller = None self.controllers = {} # 设备索引到控制器的映射 self.tracked_device_anchors = {} # 跟踪设备锚点 # VR动作系统 - 可选择禁用(用于API兼容性问题) self.disable_action_system = True # 禁用动作系统,使用直接控制器输入 if not self.disable_action_system: try: self.action_manager = VRActionManager(self) print("✓ VR动作管理器初始化完成") except Exception as e: print(f"⚠️ VR动作管理器初始化失败: {e}") self.action_manager = None else: self.action_manager = None print("🚫 VR动作系统已禁用,使用直接控制器输入") # VR交互系统 - 添加异常保护 try: self.interaction_manager = VRInteractionManager(self) print("✓ VR交互管理器初始化完成") except Exception as e: print(f"⚠️ VR交互管理器初始化失败: {e}") self.interaction_manager = None # VR传送系统 - 添加异常保护 try: self.teleport_system = VRTeleportSystem(self) print("✓ VR传送系统初始化完成") except Exception as e: print(f"⚠️ VR传送系统初始化失败: {e}") self.teleport_system = None # VR摇杆系统 - 添加异常保护 try: self.joystick_manager = VRJoystickManager(self) print("✓ VR摇杆管理器初始化完成") except Exception as e: print(f"⚠️ VR摇杆管理器初始化失败: {e}") self.joystick_manager = None # 🎯 性能监控系统 - 模块化重构 try: self.performance_monitor = VRPerformanceMonitor(self) print("✓ VR性能监控系统初始化完成") except Exception as e: print(f"⚠️ VR性能监控系统初始化失败: {e}") self.performance_monitor = None # 🧪 测试调试系统 - 模块化重构 try: self.test_mode = VRTestMode(self) print("✓ VR测试模式系统初始化完成") except Exception as e: print(f"⚠️ VR测试模式系统初始化失败: {e}") self.test_mode = None print("✓ VR管理器初始化完成") # ====== 对象池和优化系统委托方法 ====== def enable_gc_control(self): """启用垃圾回收控制 - 减少VR渲染期间的GC峰值""" if self.optimization: return self.optimization.enable_gc_control() return False def disable_gc_control(self): """禁用垃圾回收控制 - 恢复自动垃圾回收""" if self.optimization: return self.optimization.disable_gc_control() return False def set_manual_gc_interval(self, frames): """设置手动垃圾回收间隔 Args: frames: 帧数间隔 (建议100-600) """ if self.optimization: return self.optimization.set_manual_gc_interval(frames) return False def force_manual_gc(self): """强制执行一次垃圾回收""" if self.optimization: return self.optimization.force_manual_gc() return 0 def get_object_pool_status(self): """获取对象池状态""" if self.optimization: return self.optimization.get_object_pool_status() return {} def set_resolution_scale(self, scale): """设置VR分辨率缩放系数 Args: scale: 缩放系数 (0.3-1.0),0.75表示75%分辨率 """ if self.optimization: return self.optimization.set_resolution_scale(scale) return False def set_quality_preset(self, preset_name): """设置VR质量预设 Args: preset_name: 'performance', 'balanced', 'quality' """ if self.optimization: return self.optimization.set_quality_preset(preset_name) return False def cycle_quality_preset(self): """循环切换质量预设""" if self.optimization: return self.optimization.cycle_quality_preset() return False def get_resolution_info(self): """获取分辨率相关信息""" if self.optimization: return self.optimization.get_resolution_info() return {} def print_resolution_info(self): """输出分辨率信息""" if self.optimization: return self.optimization.print_resolution_info() def enable_performance_mode(self): """手动启用性能模式 - 立即禁用详细监控以提升性能""" if self.optimization: return self.optimization.enable_performance_mode() def disable_performance_mode(self): """禁用性能模式 - 重新启用详细监控(用于调试)""" if self.optimization: return self.optimization.disable_performance_mode() def set_performance_mode_trigger_frame(self, frame_count): """设置性能模式自动触发的帧数 Args: frame_count: 触发帧数 (建议300-1200) """ if self.optimization: return self.optimization.set_performance_mode_trigger_frame(frame_count) def get_performance_mode_status(self): """获取性能模式状态""" if self.optimization: return self.optimization.get_performance_mode_status() return {} def convert_mat(self, mat): """ 将OpenVR矩阵转换为Panda3D矩阵 - 基于参考实现 """ if len(mat.m) == 4: result = LMatrix4( mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0], mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1], mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2], mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]) elif len(mat.m) == 3: result = LMatrix4( mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0, mat.m[0][1], mat.m[1][1], mat.m[2][1], 0.0, mat.m[0][2], mat.m[1][2], mat.m[2][2], 0.0, mat.m[0][3], mat.m[1][3], mat.m[2][3], 1.0) return result def is_vr_available(self): """检查VR系统是否可用""" if not OPENVR_AVAILABLE: return False try: # 检查SteamVR是否运行 return openvr.isRuntimeInstalled() and openvr.isHmdPresent() except Exception as e: print(f"VR检查失败: {e}") return False def initialize_vr(self): """初始化VR系统""" if not OPENVR_AVAILABLE: print("❌ OpenVR不可用,无法初始化VR") return False if self.vr_initialized: print("VR系统已经初始化") return True try: print("🔄 正在初始化VR系统...") # 🚀 确保对象池已正确初始化(备用检查) if self.optimization is None: print("⚠️ 优化系统未初始化,正在重新初始化...") from core.vr.performance.optimization import VROptimization self.optimization = VROptimization(self) self.optimization._initialize_object_pools() # 初始化OpenVR - 使用Scene应用类型确保正确的焦点管理 self.vr_system = openvr.init(openvr.VRApplication_Scene) if not self.vr_system: print("❌ 无法初始化OpenVR系统") return False # 获取compositor self.vr_compositor = openvr.VRCompositor() if not self.vr_compositor: print("❌ 无法获取VR Compositor") return False # 获取推荐的渲染目标尺寸 base_width, base_height = self.vr_system.getRecommendedRenderTargetSize() self.base_eye_width = base_width self.base_eye_height = base_height # 应用分辨率缩放 self.scaled_eye_width = int(base_width * self.resolution_scale) self.scaled_eye_height = int(base_height * self.resolution_scale) # 使用缩放后的分辨率作为实际渲染分辨率 self.eye_width = self.scaled_eye_width self.eye_height = self.scaled_eye_height self.current_eye_resolution = (self.eye_width, self.eye_height) self.recommended_eye_resolution = (base_width, base_height) print(f"✓ VR基础分辨率: {base_width}x{base_height}") print(f"✓ VR缩放系数: {self.resolution_scale}") print(f"✓ VR实际分辨率: {self.eye_width}x{self.eye_height}") print(f"📊 分辨率优化: {(1 - self.resolution_scale**2) * 100:.1f}% 像素减少") # 获取VR系统信息 try: # 获取显示频率 self.vr_display_frequency = self.vr_system.getFloatTrackedDeviceProperty( openvr.k_unTrackedDeviceIndex_Hmd, openvr.Prop_DisplayFrequency_Float ) print(f"✓ VR显示频率: {self.vr_display_frequency} Hz") # 获取IPD(瞳距) ipd = self.vr_system.getFloatTrackedDeviceProperty( openvr.k_unTrackedDeviceIndex_Hmd, openvr.Prop_UserIpdMeters_Float ) print(f"✓ IPD(瞳距): {ipd * 1000:.1f}mm") # 获取设备制造商和型号 manufacturer = self.vr_system.getStringTrackedDeviceProperty( openvr.k_unTrackedDeviceIndex_Hmd, openvr.Prop_ManufacturerName_String ) model = self.vr_system.getStringTrackedDeviceProperty( openvr.k_unTrackedDeviceIndex_Hmd, openvr.Prop_ModelNumber_String ) print(f"✓ VR设备: {manufacturer} {model}") # 获取更多系统配置信息 try: # 获取刷新率相关信息 seconds_since_last_vsync = self.vr_system.getFloatTrackedDeviceProperty( openvr.k_unTrackedDeviceIndex_Hmd, openvr.Prop_SecondsFromVsyncToPhotons_Float ) self.vsync_to_photons_ms = seconds_since_last_vsync * 1000 print(f"✓ VSync到光子延迟: {self.vsync_to_photons_ms:.2f}ms") # 获取显示延迟信息 display_refresh_rate = self.vr_display_frequency if display_refresh_rate > 0: self.target_frame_time_ms = 1000.0 / display_refresh_rate print(f"✓ 目标帧时间: {self.target_frame_time_ms:.2f}ms") # 计算VSync时间窗口 self.vsync_window_ms = self.target_frame_time_ms * 0.1 # 10%的窗口 print(f"✓ VSync时间窗口: ±{self.vsync_window_ms:.2f}ms") except Exception as vsync_error: print(f"⚠️ 获取VSync信息失败: {vsync_error}") # 检查是否启用了异步重投影 try: if hasattr(openvr, 'VRSettings'): settings = openvr.VRSettings() if settings: self.async_reprojection_enabled = settings.getBool("steamvr", "enableAsyncReprojection") print(f"✓ 异步重投影: {'启用' if self.async_reprojection_enabled else '禁用'}") self.motion_smoothing_enabled = settings.getBool("steamvr", "motionSmoothing") print(f"✓ 运动平滑: {'启用' if self.motion_smoothing_enabled else '禁用'}") except Exception as settings_error: print(f"⚠️ 获取VR设置失败: {settings_error}") except Exception as e: print(f"⚠️ 获取VR系统信息失败: {e}") # 创建OpenVR姿态数组 - 分离渲染和游戏姿态 poses_t = openvr.TrackedDevicePose_t * openvr.k_unMaxTrackedDeviceCount self.poses = poses_t() # 渲染姿态(预测的) self.game_poses = poses_t() # 游戏逻辑姿态(当前的) print("✓ VR渲染和游戏姿态数组已创建") # 🔧 关键修复:统一初始化流程 # 如果目标模式是RenderPipeline,先用普通模式初始化,然后切换 # 这样两个场景都走相同的、已验证的代码路径 target_render_mode = self.vr_render_mode use_deferred_pipeline_switch = False if target_render_mode == VRRenderMode.RENDER_PIPELINE: print("🎨 目标VR渲染模式: RenderPipeline") print(" 策略:先用普通模式初始化,然后切换到RenderPipeline") self.vr_render_mode = VRRenderMode.NORMAL use_deferred_pipeline_switch = True else: print(f"🎨 VR渲染模式: {self.vr_render_mode.value}") # 创建VR渲染缓冲区 - 始终用普通模式初始化 if not self._create_vr_buffers(): print("❌ 创建VR渲染缓冲区失败") return False # 设置VR相机 if not self._setup_vr_cameras(): print("❌ 设置VR相机失败") return False # 优化VR渲染管线 self._optimize_vr_rendering() # 初始化手柄控制器 self._initialize_controllers() # 初始化动作系统 - 仅在启用时初始化 if not self.disable_action_system and self.action_manager: try: if not self.action_manager.initialize(): print("⚠️ VR动作系统初始化失败,但VR系统将继续运行") except Exception as e: print(f"⚠️ VR动作系统初始化异常: {e}") else: if self.disable_action_system: print("🚫 VR动作系统已禁用,跳过初始化") else: print("⚠️ VR动作管理器未创建,跳过动作系统初始化") # 初始化交互系统 - 检查是否存在 if self.interaction_manager: try: if not self.interaction_manager.initialize(): print("⚠️ VR交互系统初始化失败,但VR系统将继续运行") except Exception as e: print(f"⚠️ VR交互系统初始化异常: {e}") else: print("⚠️ VR交互管理器未创建,跳过交互系统初始化") # 初始化VR传送系统 if self.teleport_system: try: if not self.teleport_system.initialize(): print("⚠️ VR传送系统初始化失败,但VR系统将继续运行") except Exception as e: print(f"⚠️ VR传送系统初始化异常: {e}") else: print("⚠️ VR传送系统未创建,跳过传送系统初始化") # 初始化VR摇杆系统 if self.joystick_manager: try: # 传入传送系统引用给摇杆管理器 self.joystick_manager.initialize(self.teleport_system) print("✓ VR摇杆系统初始化成功") except Exception as e: print(f"⚠️ VR摇杆系统初始化异常: {e}") else: print("⚠️ VR摇杆管理器未创建,跳过摇杆系统初始化") # 可选:禁用异步重投影(备选方案) if self.disable_async_reprojection: self._disable_async_reprojection() # 启动VR更新任务 self._start_vr_task() self.vr_initialized = True print("✅ VR系统初始化成功(普通模式)") # 🔧 关键修复:如果目标是RenderPipeline模式,现在切换过去 # 这样场景2(先设RP再进VR)会走场景1(先VR再设RP)相同的代码路径 if use_deferred_pipeline_switch: print("\n🔄 现在切换到目标渲染模式: RenderPipeline") print(" 这将触发buffer重建和visualizer刷新...") if self.set_vr_render_mode(target_render_mode): print("✅ 已成功切换到RenderPipeline模式") else: print("⚠️ 切换到RenderPipeline失败,保持普通模式") return True except Exception as e: print(f"❌ VR初始化失败: {e}") import traceback traceback.print_exc() return False def _create_vr_buffers(self): """创建VR渲染缓冲区 - 使用分辨率缩放优化""" try: print(f"🔧 创建VR缓冲区:") print(f" 推荐分辨率: {self.base_eye_width}x{self.base_eye_height}") print(f" 缩放系数: {self.resolution_scale}") print(f" 实际分辨率: {self.eye_width}x{self.eye_height}") print(f" 像素减少: {(1 - self.resolution_scale**2) * 100:.1f}%") # 创建左眼纹理和缓冲区 self.vr_left_texture = self._create_vr_texture("VR Left Eye Texture") self.vr_left_eye_buffer = self._create_vr_buffer( "VR Left Eye", self.vr_left_texture, self.eye_width, self.eye_height ) if not self.vr_left_eye_buffer: print("❌ 创建左眼缓冲区失败") return False # 设置左眼缓冲区属性 self.vr_left_eye_buffer.setSort(-100) self.vr_left_eye_buffer.setClearColor((0.1, 0.2, 0.4, 1)) # 深蓝色背景便于调试 self.vr_left_eye_buffer.setActive(True) # 创建右眼纹理和缓冲区 self.vr_right_texture = self._create_vr_texture("VR Right Eye Texture") self.vr_right_eye_buffer = self._create_vr_buffer( "VR Right Eye", self.vr_right_texture, self.eye_width, self.eye_height ) if not self.vr_right_eye_buffer: print("❌ 创建右眼缓冲区失败") return False # 设置右眼缓冲区属性 self.vr_right_eye_buffer.setSort(-99) self.vr_right_eye_buffer.setClearColor((0.1, 0.2, 0.4, 1)) # 深蓝色背景便于调试 self.vr_right_eye_buffer.setActive(True) # 🚀 关键优化:立即准备纹理并缓存OpenGL ID,避免后续重复准备 print("🔧 准备纹理并缓存OpenGL ID...") if self._prepare_and_cache_textures(): print("✅ VR渲染缓冲区和纹理缓存创建成功") return True else: print("❌ 纹理准备失败") return False except Exception as e: print(f"❌ 创建VR缓冲区失败: {e}") import traceback traceback.print_exc() return False def _create_vr_texture(self, name): """创建VR纹理对象 - 基于参考实现""" texture = Texture(name) texture.setWrapU(Texture.WMClamp) texture.setWrapV(Texture.WMClamp) texture.setMinfilter(Texture.FTLinear) texture.setMagfilter(Texture.FTLinear) return texture def _prepare_and_cache_textures(self): """准备纹理并缓存OpenGL ID - 解决重复准备问题""" try: # 🔧 验证纹理对象存在 if not self.vr_left_texture or not self.vr_right_texture: print("❌ VR纹理对象不存在") print(f" 左眼纹理: {self.vr_left_texture}") print(f" 右眼纹理: {self.vr_right_texture}") return False # 获取graphics state guardian和prepared objects gsg = self.world.win.getGsg() if not gsg: print("❌ 无法获取GraphicsStateGuardian") return False prepared_objects = gsg.getPreparedObjects() if not prepared_objects: print("❌ 无法获取PreparedObjects") return False # 准备左眼纹理并缓存ID print(f" 准备左眼纹理: {self.vr_left_texture.getXSize()}x{self.vr_left_texture.getYSize()}") texture_context = self.vr_left_texture.prepareNow(0, prepared_objects, gsg) if texture_context and hasattr(texture_context, 'getNativeId'): self.left_texture_id = texture_context.getNativeId() if self.left_texture_id > 0: print(f" ✅ 左眼纹理准备完成 ({self.vr_render_mode.value}): ID={self.left_texture_id}") else: print(" ❌ 左眼纹理ID无效") return False else: print(" ❌ 左眼纹理准备失败") return False # 准备右眼纹理并缓存ID print(f" 准备右眼纹理: {self.vr_right_texture.getXSize()}x{self.vr_right_texture.getYSize()}") texture_context = self.vr_right_texture.prepareNow(0, prepared_objects, gsg) if texture_context and hasattr(texture_context, 'getNativeId'): self.right_texture_id = texture_context.getNativeId() if self.right_texture_id > 0: print(f" ✅ 右眼纹理准备完成 ({self.vr_render_mode.value}): ID={self.right_texture_id}") else: print(" ❌ 右眼纹理ID无效") return False else: print(" ❌ 右眼纹理准备失败") return False # 标记纹理已准备 self.textures_prepared = True print(" ✅ 纹理准备完成,ID已缓存,后续帧将直接使用缓存ID") return True except Exception as e: print(f"❌ 纹理准备和缓存失败: {e}") import traceback traceback.print_exc() return False def _create_vr_buffer(self, name, texture, width, height): """创建VR渲染缓冲区 - 基于参考实现 + 性能诊断""" # 设置帧缓冲属性 fbprops = FrameBufferProperties() fbprops.setRgbaBits(1, 1, 1, 1) # 可以在这里添加多重采样抗锯齿 # fbprops.setMultisamples(4) # 创建缓冲区 buffer = self.world.win.makeTextureBuffer(name, width, height, to_ram=False, fbp=fbprops) if buffer: # 🔍 性能诊断:检查缓冲区类型和属性 self._diagnose_buffer_performance(buffer, name, width, height) # 清除默认渲染纹理 buffer.clearRenderTextures() # 🚀 使用RTMBindOrCopy模式 - 已经是最优选择 # RTMBindOrCopy会尝试直接绑定到纹理,只有硬件不支持时才回退到复制 # 这已经是Panda3D中最高效的渲染到纹理模式 buffer.addRenderTexture(texture, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) else: print(f"⚠️ VR缓冲区创建失败: {name} ({width}x{height})") return buffer def _diagnose_buffer_performance(self, buffer, name, width, height): """诊断VR缓冲区性能特性""" try: # 检查缓冲区类型 buffer_type = type(buffer).__name__ # 检查是否为parasite buffer(性能较差的类型) is_parasite = 'Parasite' in buffer_type # 获取缓冲区信息 is_valid = buffer.isValid() is_hardware = hasattr(buffer, 'getGsg') and buffer.getGsg() is not None # 输出诊断信息(仅第一次创建时) if not hasattr(self, '_buffer_diagnosis_shown'): print(f"🔍 VR缓冲区诊断 [{name}]:") print(f" 类型: {buffer_type}") print(f" 分辨率: {width}x{height} ({width*height/1000000:.1f}M像素)") print(f" 有效性: {'✓' if is_valid else '✗'}") print(f" 硬件支持: {'✓' if is_hardware else '✗'}") if is_parasite: print(f" ⚠️ 检测到Parasite Buffer - 性能可能受限") print(f" 建议: 检查显卡是否支持真正的离屏渲染") else: print(f" ✓ 使用硬件FBO - 性能良好") # 显示帧缓冲区属性 if hasattr(buffer, 'getFbProperties'): fbp = buffer.getFbProperties() if fbp: print(f" 颜色位数: R{fbp.getRedBits()}G{fbp.getGreenBits()}B{fbp.getBlueBits()}A{fbp.getAlphaBits()}") if hasattr(fbp, 'getMultisamples') and fbp.getMultisamples() > 0: print(f" MSAA: {fbp.getMultisamples()}x") self._buffer_diagnosis_shown = True except Exception as e: print(f"缓冲区诊断失败: {e}") def _create_vr_buffers_with_pipeline(self): """创建带RenderPipeline的VR渲染缓冲区 - 高级渲染模式""" try: print(f"🎨 创建RenderPipeline VR缓冲区:") print(f" 推荐分辨率: {self.base_eye_width}x{self.base_eye_height}") print(f" Pipeline缩放: {self.pipeline_resolution_scale}") # 检查RenderPipeline是否可用 if not hasattr(self.world, 'render_pipeline') or not self.world.render_pipeline: print("❌ RenderPipeline未初始化,无法使用高级渲染模式") return False pipeline = self.world.render_pipeline # 计算RenderPipeline模式下的分辨率 pipeline_width = int(self.base_eye_width * self.pipeline_resolution_scale) pipeline_height = int(self.base_eye_height * self.pipeline_resolution_scale) print(f" 实际分辨率: {pipeline_width}x{pipeline_height}") print(f" 像素减少: {(1 - self.pipeline_resolution_scale**2) * 100:.1f}%") # 导入RenderPipeline相关模块 try: from RenderPipelineFile.rpcore.render_target import RenderTarget except ImportError as e: print(f"❌ 无法导入RenderPipeline模块: {e}") return False # 导入VR stages模块 try: from core.vr.rendering.stages import VRPipelineController except ImportError as e: print(f"❌ 无法导入VR stages模块: {e}") return False # 创建VR Pipeline控制器 print(" 初始化VR Pipeline控制器...") self.vr_pipeline_controller = VRPipelineController(pipeline) # 保存分辨率信息(将在_setup_vr_cameras中使用) self.pipeline_vr_width = pipeline_width self.pipeline_vr_height = pipeline_height # 应用RenderPipeline效果配置 self._apply_pipeline_vr_effects() self.render_pipeline_enabled = True print("✅ VR Pipeline控制器已初始化(将在相机设置时创建完整管线)") # 检查天空盒状态 self._check_skybox_status() return True except Exception as e: print(f"❌ 创建RenderPipeline VR缓冲区失败: {e}") import traceback traceback.print_exc() return False def _apply_pipeline_vr_effects(self): """为VR场景应用RenderPipeline效果配置""" try: print("🎨 应用RenderPipeline VR优化配置...") # 根据配置调整RenderPipeline设置 config = self.pipeline_vr_config # 注意:实际的插件启用/禁用需要在pipeline.yaml中配置 # 这里只是记录当前的VR优化意图 print(f" 阴影: {'启用' if config['enable_shadows'] else '禁用'}") print(f" 环境光遮蔽: {'启用' if config['enable_ao'] else '禁用'}") print(f" 泛光效果: {'启用' if config['enable_bloom'] else '禁用'} (VR推荐禁用)") print(f" 运动模糊: {'启用' if config['enable_motion_blur'] else '禁用'} (VR推荐禁用)") print(f" 屏幕空间反射: {'启用' if config['enable_ssr'] else '禁用'} (VR推荐禁用)") print("✅ RenderPipeline VR效果配置完成") except Exception as e: print(f"⚠️ 应用RenderPipeline VR效果失败: {e}") def _check_skybox_status(self): """检查并报告天空盒状态""" try: print("\n🌌 检查天空盒状态...") # 方法1:在render节点下搜索所有名字包含skybox的节点 skybox_nodes = self.world.render.findAllMatches("**/skybox*") if skybox_nodes and skybox_nodes.getNumPaths() > 0: print(f" ✓ 找到 {skybox_nodes.getNumPaths()} 个天空盒节点(使用名称搜索)") for i in range(skybox_nodes.getNumPaths()): skybox = skybox_nodes.getPath(i) print(f" 天空盒 #{i+1}:") print(f" 名称: {skybox.getName()}") print(f" 位置: {skybox.getPos()}") print(f" 缩放: {skybox.getScale()}") print(f" 父节点: {skybox.getParent().getName()}") # 检查是否有shader if skybox.hasShader(): print(f" ✓ 已应用shader效果") else: print(f" ⚠️ 未应用shader效果") # 检查bin设置 if skybox.hasBin(): print(f" Bin: {skybox.getBinName()}") else: print(" ⚠️ 未找到天空盒(名称搜索)") # 方法2:搜索所有GeomNode(几何节点),天空盒通常是一个大的几何体 print(" 搜索所有几何节点...") all_geoms = self.world.render.findAllMatches("**/+GeomNode") print(f" 找到 {all_geoms.getNumPaths()} 个几何节点") # 查找大型几何体(缩放值很大,可能是天空盒) skybox_candidates = [] for i in range(all_geoms.getNumPaths()): geom_node = all_geoms.getPath(i) scale = geom_node.getScale() # 天空盒通常缩放很大(如40000) if scale[0] > 1000 or scale[1] > 1000 or scale[2] > 1000: print(f" ✓ 找到大型几何体(可能是天空盒):") print(f" 名称: {geom_node.getName()}") print(f" 缩放: {scale}") print(f" 位置: {geom_node.getPos()}") if geom_node.hasShader(): print(f" ✓ 已应用shader") skybox_candidates.append(geom_node) # 方法3:检查render节点的所有直接子节点(显示全部) print(" 检查render的所有直接子节点...") render_children = self.world.render.getChildren() print(f" render有 {render_children.getNumPaths()} 个直接子节点:") for i in range(render_children.getNumPaths()): child = render_children.getPath(i) scale = child.getScale() # 标记大型节点(可能是天空盒) if scale[0] > 1000 or scale[1] > 1000 or scale[2] > 1000: print(f" - {child.getName()} ⭐ (大型节点,缩放: {scale})") else: print(f" - {child.getName()}") # 报告结果但不自动创建 if len(skybox_candidates) == 0: print(" ⚠️ 未找到明显的天空盒(缩放>1000的几何体)") print(" 💡 RenderPipeline的天空盒可能在上述列表中,但缩放值不同") print(" 💡 或者可能RenderPipeline未在VR模式下自动创建天空盒") else: print(f" ✓ 找到 {len(skybox_candidates)} 个可能的天空盒") print("🌌 天空盒状态检查完成\n") except Exception as e: print(f"⚠️ 天空盒状态检查失败: {e}") import traceback traceback.print_exc() def _create_vr_skybox(self): """为VR创建天空盒""" try: print(" 🎨 创建VR天空盒...") # 检查RenderPipeline是否可用 if hasattr(self.world, 'render_pipeline') and self.world.render_pipeline: pipeline = self.world.render_pipeline # 使用RenderPipeline的load_default_skybox方法 print(" 使用RenderPipeline方法创建天空盒...") skybox = pipeline.common_resources.load_default_skybox() if skybox: # 设置天空盒属性 skybox.set_scale(40000) # 大型缩放 skybox.reparent_to(self.world.render) skybox.set_bin("unsorted", 10000) # 最后渲染 # 应用天空盒shader效果 pipeline.set_effect(skybox, "effects/skybox.yaml", { "render_shadow": False, "render_envmap": False, "render_voxelize": False, "alpha_testing": False, "normal_mapping": False, "parallax_mapping": False }, 1000) print(f" ✅ VR天空盒已创建") print(f" 名称: {skybox.getName()}") print(f" 缩放: {skybox.getScale()}") print(f" 位置: {skybox.getPos()}") return skybox else: print(" ❌ 无法加载天空盒模型") return None else: print(" ❌ RenderPipeline未初始化,无法创建天空盒") return None except Exception as e: print(f" ❌ 创建VR天空盒失败: {e}") import traceback traceback.print_exc() return None def _setup_vr_cameras(self): """设置VR相机 - 使用锚点层级系统""" try: # 创建VR追踪空间锚点层级 self.tracking_space = self.world.render.attachNewNode('tracking-space') self.hmd_anchor = self.tracking_space.attachNewNode('hmd-anchor') self.left_eye_anchor = self.hmd_anchor.attachNewNode('left-eye') self.right_eye_anchor = self.hmd_anchor.attachNewNode('right-eye') # 获取投影矩阵 projection_left = self.coord_mat_inv * self.convert_mat( self.vr_system.getProjectionMatrix(openvr.Eye_Left, self.near_clip, self.far_clip)) projection_right = self.coord_mat_inv * self.convert_mat( self.vr_system.getProjectionMatrix(openvr.Eye_Right, self.near_clip, self.far_clip)) # 创建左眼相机节点 left_cam_node = Camera('left-cam') left_lens = MatrixLens() left_lens.setUserMat(projection_left) left_cam_node.setLens(left_lens) # 创建右眼相机节点 right_cam_node = Camera('right-cam') right_lens = MatrixLens() right_lens.setUserMat(projection_right) right_cam_node.setLens(right_lens) # 附加相机到眼睛锚点 self.vr_left_camera = self.left_eye_anchor.attachNewNode(left_cam_node) self.vr_right_camera = self.right_eye_anchor.attachNewNode(right_cam_node) # 设置显示区域 - 区分RenderPipeline和普通模式 if self.vr_render_mode == VRRenderMode.RENDER_PIPELINE and self.vr_pipeline_controller: # RenderPipeline模式:使用VRPipelineController创建完整管线 print(" 使用VR Pipeline创建立体渲染管线...") # 创建立体渲染管线(GBuffer + Lighting + Final stages) success = self.vr_pipeline_controller.create_stereo_pipeline( self.pipeline_vr_width, self.pipeline_vr_height, self.vr_left_camera, self.vr_right_camera ) if not success: print("❌ VR Pipeline创建失败") return False # 获取GBuffer的内部buffer(用于DisplayRegion) self.vr_left_eye_buffer = self.vr_pipeline_controller.get_left_buffer() self.vr_right_eye_buffer = self.vr_pipeline_controller.get_right_buffer() # 🔧 关键修复:验证buffer有效性 if not self.vr_left_eye_buffer or not self.vr_right_eye_buffer: print("❌ VR Pipeline buffer创建失败") print(f" 左眼buffer: {self.vr_left_eye_buffer}") print(f" 右眼buffer: {self.vr_right_eye_buffer}") return False # 获取最终输出纹理(用于提交到OpenVR) left_textures = self.vr_pipeline_controller.get_left_textures() right_textures = self.vr_pipeline_controller.get_right_textures() if left_textures and right_textures: self.vr_left_texture = left_textures["final"] self.vr_right_texture = right_textures["final"] else: print("❌ 无法获取VR Pipeline输出纹理") return False # 设置缓冲区排序和清除颜色 if self.vr_left_eye_buffer: self.vr_left_eye_buffer.setSort(-100) self.vr_left_eye_buffer.setClearColorActive(True) self.vr_left_eye_buffer.setClearColor((0.1, 0.2, 0.4, 1)) if self.vr_right_eye_buffer: self.vr_right_eye_buffer.setSort(-99) self.vr_right_eye_buffer.setClearColorActive(True) self.vr_right_eye_buffer.setClearColor((0.1, 0.2, 0.4, 1)) # 🔧 关键修复:为RenderPipeline的GBuffer DisplayRegion设置DrawCallback # 这确保纹理在渲染完成后被提交到OpenVR if self.vr_left_eye_buffer and self.vr_right_eye_buffer: print(" 设置RenderPipeline DisplayRegion回调...") try: # 🔧 验证DisplayRegion数量 left_dr_count = self.vr_left_eye_buffer.getNumDisplayRegions() right_dr_count = self.vr_right_eye_buffer.getNumDisplayRegions() print(f" 左眼buffer DisplayRegion数量: {left_dr_count}") print(f" 右眼buffer DisplayRegion数量: {right_dr_count}") if left_dr_count == 0 or right_dr_count == 0: print(" ❌ DisplayRegion未创建,无法设置回调") return False # 获取RenderTarget创建的DisplayRegion left_dr = self.vr_left_eye_buffer.get_display_region(0) right_dr = self.vr_right_eye_buffer.get_display_region(0) # 验证DisplayRegion有效性 if not left_dr or not right_dr: print(" ❌ DisplayRegion无效") return False # 设置渲染回调(与普通模式一致) left_dr.setDrawCallback(PythonCallbackObject(self.simple_left_cb)) right_dr.setDrawCallback(PythonCallbackObject(self.simple_right_cb)) # 确保DisplayRegion处于活动状态 left_dr.setActive(True) right_dr.setActive(True) print(" ✅ RenderPipeline DisplayRegion回调已设置") except Exception as e: print(f" ⚠️ 设置DisplayRegion回调失败: {e}") import traceback traceback.print_exc() return False # 🔧 关键修复:强制同步GraphicsEngine,确保所有buffer完全初始化 print(" 同步GraphicsEngine...") self.world.graphicsEngine.renderFrame() # 准备纹理并缓存OpenGL ID if not self._prepare_and_cache_textures(): print("❌ RenderPipeline纹理准备失败") return False print("✅ VR Pipeline立体渲染管线已准备完成") else: # 普通模式:创建新的DisplayRegion print(" 使用普通渲染区域...") left_dr = self.vr_left_eye_buffer.makeDisplayRegion() left_dr.setCamera(self.vr_left_camera) left_dr.setActive(True) left_dr.setDrawCallback(PythonCallbackObject(self.simple_left_cb)) right_dr = self.vr_right_eye_buffer.makeDisplayRegion() right_dr.setCamera(self.vr_right_camera) right_dr.setActive(True) right_dr.setDrawCallback(PythonCallbackObject(self.simple_right_cb)) print("✓ VR相机锚点层级系统设置完成") return True except Exception as e: print(f"❌ 设置VR相机失败: {e}") import traceback traceback.print_exc() return False def _get_eye_offset(self, eye): """获取眼睛相对于头显的偏移""" try: if not self.vr_system: # 使用标准IPD(瞳距)估算值 ipd = 0.064 # 64mm,平均IPD if eye == openvr.Eye_Left: return Vec3(-ipd/2, 0, 0) else: return Vec3(ipd/2, 0, 0) # 从OpenVR获取眼睛到头显的变换矩阵 eye_transform = self.vr_system.getEyeToHeadTransform(eye) # 提取位移信息 x = eye_transform[0][3] y = eye_transform[1][3] z = eye_transform[2][3] return Vec3(x, y, z) except Exception as e: print(f"❌ 获取眼睛偏移失败: {e}") # 返回默认值 ipd = 0.064 if eye == openvr.Eye_Left: return Vec3(-ipd/2, 0, 0) else: return Vec3(ipd/2, 0, 0) def _optimize_vr_rendering(self): """优化VR模式下的渲染管线 - 减少不必要的后处理开销""" try: print("🔧 正在优化VR渲染管线...") # 检查是否有RenderPipeline if hasattr(self.world, 'render_pipeline') and self.world.render_pipeline: print(" 检测到RenderPipeline,正在优化...") # 对VR缓冲区应用轻量级渲染设置 if hasattr(self, 'vr_left_eye_buffer') and self.vr_left_eye_buffer: self._apply_lightweight_rendering(self.vr_left_eye_buffer, "左眼") if hasattr(self, 'vr_right_eye_buffer') and self.vr_right_eye_buffer: self._apply_lightweight_rendering(self.vr_right_eye_buffer, "右眼") print("✅ VR渲染管线优化完成") else: print(" 未检测到RenderPipeline,使用默认Panda3D渲染") # 禁用VR缓冲区的不必要功能 self._disable_vr_buffer_extras() except Exception as e: print(f"⚠️ VR渲染优化失败: {e}") import traceback traceback.print_exc() def _apply_lightweight_rendering(self, buffer, eye_name): """为VR缓冲区应用轻量级渲染设置""" try: # 禁用多重采样抗锯齿(MSAA)以提升性能 if hasattr(buffer, 'setMultisample'): buffer.setMultisample(0) print(f" {eye_name}: 禁用MSAA") # 设置更简单的清除颜色操作 buffer.setClearColorActive(True) buffer.setClearColor((0.1, 0.2, 0.4, 1.0)) # 深蓝色,便于调试 # 禁用深度写入到某些不需要的缓冲区 # (保留主要深度测试,但减少不必要的写入) print(f" {eye_name}: 应用轻量级渲染设置") except Exception as e: print(f"⚠️ {eye_name}缓冲区优化失败: {e}") def _disable_vr_buffer_extras(self): """禁用VR缓冲区的额外功能以提升性能""" try: # 禁用VR缓冲区的统计收集 if hasattr(self, 'vr_left_eye_buffer') and self.vr_left_eye_buffer: if hasattr(self.vr_left_eye_buffer, 'setOneShot'): self.vr_left_eye_buffer.setOneShot(False) if hasattr(self, 'vr_right_eye_buffer') and self.vr_right_eye_buffer: if hasattr(self.vr_right_eye_buffer, 'setOneShot'): self.vr_right_eye_buffer.setOneShot(False) print(" VR缓冲区额外功能已优化") except Exception as e: print(f"⚠️ VR缓冲区额外功能优化失败: {e}") def _start_vr_task(self): """启动VR更新任务""" if self.vr_task: self.world.taskMgr.remove(self.vr_task) # 设置高优先级(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更新任务 - 每帧调用(参考 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 # 🚀 自动启用性能模式 - 减少计时对象创建 if (self.optimization and not self.optimization.performance_mode_enabled and self.frame_count >= self.optimization.performance_mode_trigger_frame): self.optimization.performance_mode_enabled = True if self.frame_count <= self.optimization.performance_mode_trigger_frame + 5: # 只输出一次 print(f"🎯 性能模式已启用 (帧#{self.frame_count}) - 禁用详细监控以提升性能") # 记录帧时间 self._track_frame_time() # 计算VR FPS import time current_time = time.time() if self.last_fps_time == 0: self.last_fps_time = current_time elif current_time - self.last_fps_time >= 1.0: # 每秒更新一次FPS self.vr_fps = (self.frame_count - self.last_fps_check) / (current_time - self.last_fps_time) self.last_fps_check = self.frame_count self.last_fps_time = current_time # 📌 使用刚获取的姿态更新相机(而不是旧姿态) self._update_camera_poses() # 输出策略信息(仅第一次) if not hasattr(self, '_new_architecture_logged'): print("✅ 新架构已启用 - waitGetPoses 在任务开始阻塞,参考 panda3d-openvr") print(" 这确保每个 VSync 周期只渲染一次,解决 AlreadySubmitted 错误") self._new_architecture_logged = True # 更新VR动作状态 - 仅在启用时更新 if not self.disable_action_system and self.action_manager: self.action_manager.update_actions() # 更新VR交互系统 if self.interaction_manager: self.interaction_manager.update() # 更新VR摇杆系统 if self.joystick_manager: # 计算帧间隔时间 import time if not hasattr(self, '_last_frame_time'): self._last_frame_time = time.time() dt = 0.016 # 默认60fps else: current_time = time.time() dt = current_time - self._last_frame_time self._last_frame_time = current_time # 限制dt范围,避免异常情况 dt = max(0.001, min(0.1, dt)) self.joystick_manager.update(dt) # 更新系统性能指标(减少频率) 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帧周期性峰值 if self.optimization: self.optimization._manual_gc_control() # 定期输出性能报告 - 默认10秒间隔 report_interval = getattr(self, 'performance_report_interval', 600) if self.frame_count % report_interval == 1: self._print_performance_report() except Exception as e: print(f"VR更新错误: {e}") import traceback traceback.print_exc() return task.cont def _sync_gpu_if_needed(self): """可选的GPU同步 - 仅在需要时使用""" try: # 同步等待GPU完成(如果需要) gsg = self.world.win.getGsg() if gsg: gsg.getEngine().syncFrame() except Exception as e: print(f"❌ GPU同步异常: {e}") def simple_left_cb(self, cbdata): """简化的左眼渲染回调 - 精确控制渲染和提交""" try: # 🔍 精确测量渲染时间 import time render_start = time.perf_counter() # 触发实际渲染 cbdata.upcall() # 计算真实渲染时间 self.left_render_time = (time.perf_counter() - render_start) * 1000 # 转换为毫秒 # 记录渲染次数 self.left_render_count += 1 # 📌 OpenVR 帧边界检查:防止同一 OpenVR 帧内重复渲染 if self.left_eye_last_render_frame == self.openvr_frame_id: return # 已在当前 OpenVR 帧渲染过,跳过 # 🔧 OpenVR最佳实践:左眼只渲染,不立即提交 # 基于官方hellovr示例:两眼都渲染完后再批量提交 self.left_eye_last_render_frame = self.openvr_frame_id # 渐进式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 self.vr_test_mode: # 测试模式:始终触发屏幕显示更新 if self.test_mode: self.test_mode._update_test_display() except Exception as e: print(f"左眼渲染回调错误: {e}") def simple_right_cb(self, cbdata): """简化的右眼渲染回调 - 精确控制渲染和提交""" try: # 🔍 精确测量渲染时间 import time render_start = time.perf_counter() # 触发实际渲染 cbdata.upcall() # 计算真实渲染时间 self.right_render_time = (time.perf_counter() - render_start) * 1000 # 转换为毫秒 # 记录渲染次数 self.right_render_count += 1 # 📌 OpenVR 帧边界检查:防止同一 OpenVR 帧内重复渲染 if self.right_eye_last_render_frame == self.openvr_frame_id: return # 已在当前 OpenVR 帧渲染过,跳过 # 🔧 OpenVR最佳实践:右眼渲染完成后批量提交两眼纹理 # 基于官方hellovr示例:避免分散提交导致的VSync阻塞 self.right_eye_last_render_frame = self.openvr_frame_id # 🔧 渐进式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 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) # 🚀 测试模式也需要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 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 if self.test_mode: self.test_mode._update_test_performance_hud() except Exception as e: print(f"右眼渲染回调错误: {e}") def _wait_get_poses(self): """调用VRCompositor的waitGetPoses来获取焦点和姿态数据""" try: if not self.vr_compositor or not self.poses: return # 调用waitGetPoses获取焦点和姿态数据 # 这个调用可能会阻塞直到下一个VR同步点 result = self.vr_compositor.waitGetPoses(self.poses, None) # 检查姿态数据的有效性 valid_poses = 0 # 更新HMD姿态(设备0通常是头显) if len(self.poses) > 0 and self.poses[0].bPoseIsValid: valid_poses += 1 else: # 如果HMD姿态无效,不要频繁输出错误信息 if not hasattr(self, '_hmd_invalid_warning_shown'): print("⚠️ HMD姿态数据无效") self._hmd_invalid_warning_shown = True # 更新控制器姿态 self.controller_poses.clear() for device_id in range(1, min(len(self.poses), openvr.k_unMaxTrackedDeviceCount)): if self.poses[device_id].bPoseIsValid: device_class = self.vr_system.getTrackedDeviceClass(device_id) if device_class == openvr.TrackedDeviceClass_Controller: controller_matrix = self.poses[device_id].mDeviceToAbsoluteTracking self.controller_poses[device_id] = self._convert_openvr_matrix_to_panda(controller_matrix) valid_poses += 1 # 性能监控 - 偶尔输出姿态状态 if self.frame_count % 600 == 1: # 每10秒输出一次@60fps print(f"📊 VR姿态状态 - 有效姿态数: {valid_poses}, 总帧数: {self.frame_count}") except Exception as e: # 限制错误输出频率 if not hasattr(self, '_last_error_frame'): self._last_error_frame = 0 if self.frame_count - self._last_error_frame > 300: # 每5秒最多输出一次错误 print(f"waitGetPoses失败: {e}") self._last_error_frame = self.frame_count # 记录姿态失败次数 self.pose_failures += 1 def _wait_get_poses_immediate(self): """立即获取VR姿态 - 修复ATW闪烁的关键方法(双姿态版本)""" # 开始计时waitGetPoses操作 timing = self._start_timing('wait_poses') try: if not self.vr_compositor or not self.poses or not self.game_poses: self._end_timing(timing) return # 关键修复:传递渲染姿态和游戏姿态数组 # 渲染姿态用于绘制,游戏姿态用于逻辑,避免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) # 检查姿态数据的有效性 valid_poses = 0 # 更新HMD姿态(设备0通常是头显) if len(self.poses) > 0 and self.poses[0].bPoseIsValid: valid_poses += 1 else: # 如果HMD姿态无效,不要频繁输出错误信息 if not hasattr(self, '_hmd_invalid_warning_shown'): print("⚠️ HMD姿态数据无效(立即模式)") self._hmd_invalid_warning_shown = True # 🚀 优化控制器姿态更新:使用缓存,避免每帧clear()和重新创建对象 for device_id in range(1, min(len(self.poses), openvr.k_unMaxTrackedDeviceCount)): if self.poses[device_id].bPoseIsValid: device_class = self.vr_system.getTrackedDeviceClass(device_id) if device_class == openvr.TrackedDeviceClass_Controller: controller_matrix = self.poses[device_id].mDeviceToAbsoluteTracking # 检查是否已有此设备的缓存矩阵 if device_id not in self.controller_poses: # 第一次检测到该控制器,创建新的缓存矩阵 self.controller_poses[device_id] = self._convert_openvr_matrix_to_panda(controller_matrix) else: # 复用现有矩阵,只更新数值,避免创建新对象 cached_mat = self.controller_poses[device_id] self._update_matrix_from_openvr(cached_mat, controller_matrix) valid_poses += 1 else: # 设备姿态无效,从字典中移除(如果存在) if device_id in self.controller_poses: # 将矩阵返回对象池 if self.optimization: self.optimization._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: # 限制错误输出频率 if not hasattr(self, '_last_immediate_error_frame'): self._last_immediate_error_frame = 0 if self.frame_count - self._last_immediate_error_frame > 300: # 每5秒最多输出一次错误 print(f"立即姿态获取失败: {e}") self._last_immediate_error_frame = self.frame_count self.pose_failures += 1 def _wait_get_poses_with_prediction(self): """使用预测时间获取VR姿态 - 优化性能的新策略""" try: if not self.vr_compositor or not self.poses or not self.game_poses: return # 使用预测时间获取姿态 # 预测时间通常为11-16ms,对应下一个VR帧的时间 result = self.vr_compositor.waitGetPoses(self.poses, self.game_poses) # 检查姿态数据的有效性 valid_poses = 0 # 更新HMD姿态(设备0通常是头显) if len(self.poses) > 0 and self.poses[0].bPoseIsValid: valid_poses += 1 else: # 如果HMD姿态无效,不要频繁输出错误信息 if not hasattr(self, '_hmd_invalid_warning_task'): print("⚠️ HMD姿态数据无效(更新任务模式)") self._hmd_invalid_warning_task = True # 更新控制器姿态 self.controller_poses.clear() for device_id in range(1, min(len(self.poses), openvr.k_unMaxTrackedDeviceCount)): if self.poses[device_id].bPoseIsValid: device_class = self.vr_system.getTrackedDeviceClass(device_id) if device_class == openvr.TrackedDeviceClass_Controller: controller_matrix = self.poses[device_id].mDeviceToAbsoluteTracking self.controller_poses[device_id] = self._convert_openvr_matrix_to_panda(controller_matrix) valid_poses += 1 # 调试信息 - 仅在第一次成功时输出 if not hasattr(self, '_update_task_mode_logged') and valid_poses > 0: print(f"✅ 更新任务姿态获取模式启用 - 有效姿态数: {valid_poses}") print(f" 预测时间: {self.use_prediction_time*1000:.1f}ms") self._update_task_mode_logged = True except Exception as e: # 限制错误输出频率 if not hasattr(self, '_last_task_error_frame'): self._last_task_error_frame = 0 if self.frame_count - self._last_task_error_frame > 300: # 每5秒最多输出一次错误 print(f"更新任务姿态获取失败: {e}") self._last_task_error_frame = self.frame_count # 记录姿态失败次数 self.pose_failures += 1 def _cache_poses_for_next_frame(self): """缓存当前姿态供下一帧渲染使用 - 修复时序不匹配""" try: if not self.poses or len(self.poses) == 0: return # 如果是第一帧,直接使用当前姿态 if self._first_frame: self._cached_render_poses = self.poses self._first_frame = False print("✓ 首帧姿态缓存已设置") return # 复制当前渲染姿态到缓存 # 下一帧将使用这些姿态进行渲染 poses_t = openvr.TrackedDevicePose_t * openvr.k_unMaxTrackedDeviceCount cached_poses = poses_t() # 复制姿态数据 for i in range(len(self.poses)): cached_poses[i] = self.poses[i] self._cached_render_poses = cached_poses except Exception as e: print(f"⚠️ 姿态缓存失败: {e}") def _reset_waitgetposes_flag(self, task): """重置WaitGetPoses标记 - 确保下一帧可以调用WaitGetPoses""" self._waitgetposes_called_this_frame = False return task.done def _update_tracking_data(self): """更新VR追踪数据""" try: # 获取设备姿态 poses = self.vr_system.getDeviceToAbsoluteTrackingPose( openvr.TrackingUniverseStanding, 0.0, openvr.k_unMaxTrackedDeviceCount ) # 更新HMD姿态(设备0通常是头显) if poses[0].bPoseIsValid: hmd_matrix = poses[0].mDeviceToAbsoluteTracking self.hmd_pose = self._convert_openvr_matrix_to_panda(hmd_matrix) # 更新控制器姿态 for device_id in range(1, openvr.k_unMaxTrackedDeviceCount): if poses[device_id].bPoseIsValid: device_class = self.vr_system.getTrackedDeviceClass(device_id) if device_class == openvr.TrackedDeviceClass_Controller: controller_matrix = poses[device_id].mDeviceToAbsoluteTracking self.controller_poses[device_id] = self._convert_openvr_matrix_to_panda(controller_matrix) except Exception as e: print(f"更新追踪数据失败: {e}") def _convert_openvr_matrix_to_panda(self, ovr_matrix): """将OpenVR矩阵转换为Panda3D矩阵 - 使用对象池优化 坐标系转换: OpenVR: X右, Y上, -Z前(右手坐标系) Panda3D: X右, Y前, Z上(右手坐标系) 转换规则: OpenVR X → Panda3D X OpenVR Y → Panda3D Z OpenVR -Z → Panda3D Y """ # 🚀 使用对象池获取预分配的Mat4对象,避免每帧创建新对象 if self.optimization: mat = self.optimization._get_pooled_matrix() else: mat = Mat4() # 修正的坐标转换矩阵 # OpenVR: X右, Y上, -Z前 → Panda3D: X右, Y前, Z上 # 转换规则: (ovr_x, ovr_y, ovr_z) → (panda_x, panda_y, panda_z) # (ovr_x, ovr_y, ovr_z) → (ovr_x, -ovr_z, ovr_y) # X轴行:Panda3D的X轴对应OpenVR的X轴 mat.setCell(0, 0, ovr_matrix[0][0]) # X_x → X_x mat.setCell(0, 1, ovr_matrix[0][1]) # X_y → X_y mat.setCell(0, 2, ovr_matrix[0][2]) # X_z → X_z mat.setCell(0, 3, ovr_matrix[0][3]) # 位移X分量 # Y轴行:Panda3D的Y轴对应OpenVR的-Z轴 mat.setCell(1, 0, -ovr_matrix[2][0]) # -Z_x → Y_x mat.setCell(1, 1, -ovr_matrix[2][1]) # -Z_y → Y_y mat.setCell(1, 2, -ovr_matrix[2][2]) # -Z_z → Y_z mat.setCell(1, 3, -ovr_matrix[2][3]) # 位移Y分量(-Z位移) # Z轴行:Panda3D的Z轴对应OpenVR的Y轴 mat.setCell(2, 0, ovr_matrix[1][0]) # Y_x → Z_x mat.setCell(2, 1, ovr_matrix[1][1]) # Y_y → Z_y mat.setCell(2, 2, ovr_matrix[1][2]) # Y_z → Z_z mat.setCell(2, 3, ovr_matrix[1][3]) # 位移Z分量(Y位移) # 齐次坐标 mat.setCell(3, 0, 0) mat.setCell(3, 1, 0) mat.setCell(3, 2, 0) mat.setCell(3, 3, 1) # 🚀 优化调试信息 - 避免创建Vec3对象,减少GC压力 if not hasattr(self, '_coord_debug_counter'): self._coord_debug_counter = 0 self._coord_debug_counter += 1 if self._coord_debug_counter % 600 == 1: # 每10秒输出一次@60fps print(f"🔄 坐标系转换调试 (第{self._coord_debug_counter}帧)") # 直接输出数值,避免创建Vec3对象 ovr_x, ovr_y, ovr_z = ovr_matrix[0][3], ovr_matrix[1][3], ovr_matrix[2][3] print(f" OpenVR原始位置: ({ovr_x:.3f}, {ovr_y:.3f}, {ovr_z:.3f})") # 直接从矩阵读取数值,避免创建Vec3对象 panda_x = mat.getCell(0, 3) panda_y = mat.getCell(1, 3) panda_z = mat.getCell(2, 3) print(f" Panda3D转换位置: ({panda_x:.3f}, {panda_y:.3f}, {panda_z:.3f})") # 手动验证转换:OpenVR (x,y,z) → Panda3D (x,-z,y) expected_x = ovr_x expected_y = -ovr_z expected_z = ovr_y print(f" 预期转换结果: ({expected_x:.3f}, {expected_y:.3f}, {expected_z:.3f})") # 计算误差,避免创建Vec3对象 diff_x = panda_x - expected_x diff_y = panda_y - expected_y diff_z = panda_z - expected_z diff_magnitude = (diff_x*diff_x + diff_y*diff_y + diff_z*diff_z)**0.5 if diff_magnitude < 0.001: print(f" ✅ 坐标转换正确 (误差: {diff_magnitude:.6f})") else: print(f" ⚠️ 坐标转换可能有误 (误差: {diff_magnitude:.6f})") print(f" 差异: ({diff_x:.6f}, {diff_y:.6f}, {diff_z:.6f})") return mat def update_hmd(self, pose): """ 更新HMD锚点 - 基于参考实现 """ try: # 将OpenVR姿态转换为Panda3D矩阵 modelview = self.convert_mat(pose.mDeviceToAbsoluteTracking) # 应用坐标系转换并设置HMD锚点 self.hmd_anchor.setMat(self.coord_mat_inv * modelview * self.coord_mat) # 获取眼睛到头部的变换 view_left = self.convert_mat(self.vr_system.getEyeToHeadTransform(openvr.Eye_Left)) view_right = self.convert_mat(self.vr_system.getEyeToHeadTransform(openvr.Eye_Right)) # 设置眼睛锚点 self.left_eye_anchor.setMat(self.coord_mat_inv * view_left * self.coord_mat) self.right_eye_anchor.setMat(self.coord_mat_inv * view_right * self.coord_mat) except Exception as e: print(f"更新HMD姿态失败: {e}") def _update_camera_poses(self): """更新相机姿态 - 使用锚点系统简化处理""" try: # 使用锚点系统后,相机位置自动跟随锚点 # 只需要获取HMD姿态并更新锚点即可 # 从poses数组中获取HMD姿态 if hasattr(self, 'poses') and len(self.poses) > 0: hmd_pose = self.poses[openvr.k_unTrackedDeviceIndex_Hmd] if hmd_pose.bPoseIsValid: self.update_hmd(hmd_pose) else: print("⚠️ HMD姿态数据无效") except Exception as e: print(f"更新相机姿态失败: {e}") import traceback traceback.print_exc() def _update_camera_poses_with_cache(self): """使用缓存姿态更新相机 - 符合OpenVR时序假设""" try: # 使用缓存的姿态,符合OpenVR的时序假设 # OpenVR假设你用上一次WaitGetPoses的姿态渲染当前提交的帧 if not self._cached_render_poses: # 如果没有缓存姿态,回退到当前姿态(首帧情况) print("⚠️ 没有缓存姿态,使用当前姿态") return self._update_camera_poses() # 从缓存姿态数组中获取HMD姿态 if len(self._cached_render_poses) > 0: hmd_pose = self._cached_render_poses[openvr.k_unTrackedDeviceIndex_Hmd] if hmd_pose.bPoseIsValid: self.update_hmd(hmd_pose) # 调试信息 - 验证缓存姿态使用 if not hasattr(self, '_cached_pose_logged'): print("✅ 使用缓存姿态更新相机 - 符合OpenVR时序假设") self._cached_pose_logged = True else: print("⚠️ 缓存的HMD姿态数据无效") except Exception as e: print(f"使用缓存姿态更新相机失败: {e}") # 回退到正常更新方式 self._update_camera_poses() import traceback traceback.print_exc() def enable_vr(self): """启用VR模式""" if not self.is_vr_available(): print("❌ VR系统不可用") return False if not self.vr_initialized: if not self.initialize_vr(): return False self.vr_enabled = True # 禁用主相机避免干扰VR渲染 self._disable_main_cam() # VR性能优化:使用Running Start模式(Valve最佳实践) print("🚀 VR性能优化:Running Start模式已启用") print(" 优势:在帧开始时获取姿态,提供VSync前3ms的渲染时间") print(" 注意:Submit后立即调用WaitGetPoses是错误实现") self.set_prediction_time(11.0) # 11ms预测时间 - OpenVR标准值,平衡准确性和延迟 # 🚀 动态调整Qt Timer频率以支持VR if hasattr(self.world, 'qtWidget') and self.world.qtWidget: if hasattr(self.world.qtWidget, 'synchronizer'): # 设置为144Hz,让OpenVR控制实际渲染节奏 self.world.qtWidget.synchronizer.setInterval(int(1000/144)) print("✓ Qt Timer调整为144Hz,让OpenVR控制VR渲染节奏") # 🔧 关键修复:检测并重建缺失的手柄visualizer # 当渲染模式切换时,visualizer可能被清理但控制器对象仍存在 if hasattr(self, 'left_controller') and self.left_controller: if not self.left_controller.visualizer and self.left_controller.anchor_node: print("🔧 检测到左手柄visualizer缺失,正在重建...") self.left_controller._create_visualizer() if self.left_controller.visualizer: print("✅ 左手柄visualizer已重建") if hasattr(self, 'right_controller') and self.right_controller: if not self.right_controller.visualizer and self.right_controller.anchor_node: print("🔧 检测到右手柄visualizer缺失,正在重建...") self.right_controller._create_visualizer() if self.right_controller.visualizer: print("✅ 右手柄visualizer已重建") print("✅ VR模式已启用") return True def disable_vr(self): """禁用VR模式""" self.vr_enabled = False # 清理手柄可视化(但保留控制器对象以便重新启用) if hasattr(self, 'left_controller') and self.left_controller: if hasattr(self.left_controller, 'visualizer') and self.left_controller.visualizer: try: self.left_controller.visualizer.cleanup() self.left_controller.visualizer = None print("✓ 左手控制器可视化已清理") except Exception as e: print(f"⚠️ 清理左手控制器可视化失败: {e}") if hasattr(self, 'right_controller') and self.right_controller: if hasattr(self.right_controller, 'visualizer') and self.right_controller.visualizer: try: self.right_controller.visualizer.cleanup() self.right_controller.visualizer = None print("✓ 右手控制器可视化已清理") except Exception as e: print(f"⚠️ 清理右手控制器可视化失败: {e}") # 隐藏手柄锚点节点 if hasattr(self, 'left_controller') and self.left_controller and self.left_controller.anchor_node: self.left_controller.anchor_node.hide() if hasattr(self, 'right_controller') and self.right_controller and self.right_controller.anchor_node: self.right_controller.anchor_node.hide() # 恢复主相机 self._enable_main_cam() # 恢复Qt Timer到60FPS if hasattr(self.world, 'qtWidget') and self.world.qtWidget: if hasattr(self.world.qtWidget, 'synchronizer'): self.world.qtWidget.synchronizer.setInterval(int(1000/60)) print("✓ Qt Timer恢复为60Hz") print("✅ VR模式已禁用,手柄模型已隐藏") def set_vr_render_mode(self, mode): """切换VR渲染模式 Args: mode: VRRenderMode枚举值或字符串 ('normal' 或 'render_pipeline') Returns: bool: 切换是否成功 """ try: # 转换输入为枚举类型 if isinstance(mode, str): mode_str = mode.lower() if mode_str == "normal": new_mode = VRRenderMode.NORMAL elif mode_str in ["render_pipeline", "renderpipeline", "pipeline"]: new_mode = VRRenderMode.RENDER_PIPELINE else: print(f"❌ 无效的渲染模式: {mode}") print(" 支持的模式: 'normal' 或 'render_pipeline'") return False elif isinstance(mode, VRRenderMode): new_mode = mode else: print(f"❌ 无效的模式类型: {type(mode)}") return False # 检查是否与当前模式相同 if new_mode == self.vr_render_mode: print(f"✓ VR渲染模式已经是 {new_mode.value},无需切换") return True print(f"🔄 正在切换VR渲染模式: {self.vr_render_mode.value} → {new_mode.value}") # 检查VR是否已初始化 if not self.vr_initialized: print("⚠️ VR未初始化,仅更新渲染模式配置") self.vr_render_mode = new_mode print(f"✓ VR渲染模式已更新为 {new_mode.value}(下次启动VR时生效)") return True # 保存当前VR启用状态 was_enabled = self.vr_enabled # 如果VR已启用,先禁用 if was_enabled: print(" 暂时禁用VR...") self.disable_vr() # 更新渲染模式 old_mode = self.vr_render_mode self.vr_render_mode = new_mode # 清理现有缓冲区 print(" 清理现有渲染缓冲区...") self._cleanup_vr_buffers() # 根据新模式重建缓冲区 print(f" 创建新的渲染缓冲区({new_mode.value})...") success = False if new_mode == VRRenderMode.RENDER_PIPELINE: success = self._create_vr_buffers_with_pipeline() if not success: print("⚠️ RenderPipeline模式创建失败,回退到普通渲染模式") self.vr_render_mode = VRRenderMode.NORMAL success = self._create_vr_buffers() else: success = self._create_vr_buffers() if not success: print("❌ 缓冲区创建失败,尝试恢复原模式") self.vr_render_mode = old_mode if old_mode == VRRenderMode.RENDER_PIPELINE: self._create_vr_buffers_with_pipeline() else: self._create_vr_buffers() return False # 重新设置相机 print(" 重新设置VR相机...") if not self._setup_vr_cameras(): print("❌ 相机设置失败") return False # 如果之前VR是启用的,重新启用 if was_enabled: print(" 重新启用VR...") self.enable_vr() # 🔧 关键修复:重建所有手柄的visualizer以适配新渲染模式 print(" 刷新手柄visualizer以适配新渲染模式...") if hasattr(self, 'left_controller') and self.left_controller: self.left_controller.recreate_visualizer() if hasattr(self, 'right_controller') and self.right_controller: self.right_controller.recreate_visualizer() print(f"✅ VR渲染模式已切换为 {self.vr_render_mode.value}") # 保存配置 if self.config_manager: self.config_manager.save_from_vr_manager(self) return True except Exception as e: print(f"❌ 切换VR渲染模式失败: {e}") import traceback traceback.print_exc() return False def get_vr_render_mode(self): """获取当前VR渲染模式 Returns: VRRenderMode: 当前渲染模式 """ return self.vr_render_mode def _cleanup_vr_buffers(self): """清理VR渲染缓冲区""" try: # 清理VR Pipeline Controller(如果使用RenderPipeline模式) if self.vr_pipeline_controller: print(" 清理VR Pipeline...") self.vr_pipeline_controller.cleanup_all() self.vr_pipeline_controller = None # 清理左眼缓冲区 if self.vr_left_eye_buffer: # 如果是RenderPipeline模式,buffer已被VRPipelineController清理 # 如果是普通模式,需要手动清理 if self.vr_render_mode == VRRenderMode.NORMAL: self.world.graphicsEngine.removeWindow(self.vr_left_eye_buffer) self.vr_left_eye_buffer = None # 清理右眼缓冲区 if self.vr_right_eye_buffer: if self.vr_render_mode == VRRenderMode.NORMAL: self.world.graphicsEngine.removeWindow(self.vr_right_eye_buffer) self.vr_right_eye_buffer = None # 清理纹理 self.vr_left_texture = None self.vr_right_texture = None self.left_texture_id = None self.right_texture_id = None self.textures_prepared = False # 清理RenderPipeline渲染目标(旧版,保留兼容性) if self.vr_pipeline_left_target: self.vr_pipeline_left_target = None if self.vr_pipeline_right_target: self.vr_pipeline_right_target = None self.render_pipeline_enabled = False print("✓ VR渲染缓冲区已清理") except Exception as e: print(f"⚠️ 清理VR缓冲区时出错: {e}") def cleanup(self): """清理VR资源""" try: print("🔄 正在清理VR资源...") # 停止VR任务 if self.vr_task: self.world.taskMgr.remove(self.vr_task) self.vr_task = None # 🚀 恢复Python垃圾回收并清理对象池 if self.optimization and self.optimization._gc_disabled: # 最后一次手动垃圾回收 collected = gc.collect() print(f"🗑️ 最终GC清理: {collected} 个对象") # 恢复自动垃圾回收 gc.enable() self.optimization._gc_disabled = False print("✅ Python垃圾回收已恢复为自动模式") # 清理对象池 if self.optimization and hasattr(self.optimization, '_matrix_pool'): pool_size = len(self.optimization._matrix_pool) self.optimization._matrix_pool.clear() print(f"🧹 Mat4对象池已清理: {pool_size} 个对象") # 清理缓存 if self.optimization and hasattr(self.optimization, '_cached_matrices'): self.optimization._cached_matrices.clear() if self.optimization and hasattr(self.optimization, '_controller_poses_cache'): self.optimization._controller_poses_cache.clear() # 清理OpenVR Texture对象缓存 # 清理OpenVR Texture对象缓存 if self.optimization: self.optimization._left_ovr_texture = None self.optimization._right_ovr_texture = None print("🧹 OpenVR Texture对象缓存已清理") # 清理渲染缓冲区 if self.vr_left_eye_buffer: self.vr_left_eye_buffer.removeAllDisplayRegions() self.world.graphicsEngine.removeWindow(self.vr_left_eye_buffer) self.vr_left_eye_buffer = None if self.vr_right_eye_buffer: self.vr_right_eye_buffer.removeAllDisplayRegions() self.world.graphicsEngine.removeWindow(self.vr_right_eye_buffer) self.vr_right_eye_buffer = None # 清理相机 if self.vr_left_camera: self.vr_left_camera.removeNode() self.vr_left_camera = None if self.vr_right_camera: self.vr_right_camera.removeNode() self.vr_right_camera = None # 清理控制器 if hasattr(self, 'left_controller') and self.left_controller: try: self.left_controller.cleanup() self.left_controller = None print("✓ 左手控制器已清理") except Exception as e: print(f"⚠️ 清理左手控制器失败: {e}") if hasattr(self, 'right_controller') and self.right_controller: try: self.right_controller.cleanup() self.right_controller = None print("✓ 右手控制器已清理") except Exception as e: print(f"⚠️ 清理右手控制器失败: {e}") # 清理控制器字典 if hasattr(self, 'controllers'): self.controllers.clear() print("✓ 控制器字典已清理") # 清理VR子系统 if hasattr(self, 'joystick_manager') and self.joystick_manager: try: self.joystick_manager.cleanup() self.joystick_manager = None print("✓ VR摇杆系统已清理") except Exception as e: print(f"⚠️ 清理VR摇杆系统失败: {e}") if hasattr(self, 'teleport_system') and self.teleport_system: try: self.teleport_system.cleanup() self.teleport_system = None print("✓ VR传送系统已清理") except Exception as e: print(f"⚠️ 清理VR传送系统失败: {e}") if hasattr(self, 'interaction_manager') and self.interaction_manager: try: self.interaction_manager.cleanup() self.interaction_manager = None print("✓ VR交互系统已清理") except Exception as e: print(f"⚠️ 清理VR交互系统失败: {e}") if hasattr(self, 'performance_monitor') and self.performance_monitor: try: # 性能监控系统不需要特殊清理,只需要置空引用 self.performance_monitor = None print("✓ VR性能监控系统已清理") except Exception as e: print(f"⚠️ 清理VR性能监控系统失败: {e}") if hasattr(self, 'action_manager') and self.action_manager: try: self.action_manager.cleanup() self.action_manager = None print("✓ VR动作系统已清理") except Exception as e: print(f"⚠️ 清理VR动作系统失败: {e}") # 关闭OpenVR if self.vr_system and OPENVR_AVAILABLE: try: openvr.shutdown() except: pass self.vr_system = None self.vr_enabled = False self.vr_initialized = False print("✅ VR资源清理完成") except Exception as e: print(f"⚠️ VR清理过程中出错: {e}") def get_vr_status(self): """获取VR状态信息""" return { 'available': self.is_vr_available(), 'initialized': self.vr_initialized, 'enabled': self.vr_enabled, 'eye_resolution': (self.eye_width, self.eye_height), 'device_count': len(self.controller_poses) + (1 if self.vr_enabled else 0), 'vr_fps': self.vr_fps, 'frame_count': self.frame_count, 'submit_failures': self.submit_failures, 'pose_failures': self.pose_failures } def submit_texture(self, eye, texture): """优化的VR纹理提交 - 使用缓存的纹理ID,避免重复prepareNow""" try: if not self.vr_compositor: print("❌ VR compositor不可用") self.submit_failures += 1 return # 🚀 关键修复:防止同一帧重复提交 current_frame = getattr(self, 'frame_count', 0) if not hasattr(self, '_last_submit_frame'): self._last_submit_frame = {} # 检查当前帧是否已经提交过此眼睛的纹理 if eye in self._last_submit_frame and self._last_submit_frame[eye] == current_frame: # 静默跳过,避免spam输出 return # 记录当前帧提交 self._last_submit_frame[eye] = current_frame # 🚀 关键优化:直接使用缓存的纹理ID,避免重复prepareNow if eye == openvr.Eye_Left: handle = self.left_texture_id eye_name = "左眼" elif eye == openvr.Eye_Right: handle = self.right_texture_id eye_name = "右眼" else: print(f"❌ 未知的眼睛类型: {eye}") self.submit_failures += 1 return # 检查缓存的纹理ID是否有效 if not handle or handle <= 0: print(f"❌ {eye_name}纹理ID缓存无效: {handle}") print(" 这可能表示纹理准备失败,需要检查_prepare_and_cache_textures()") self.submit_failures += 1 return # ❌ 移除gsg.flush()调用 - 基于OpenVR官方实践 # gsg.flush()等同于OpenGL的glFlush(),导致强制CPU-GPU同步 # 每帧调用2次(左右眼)是GPU周期性峰值的主要原因 # 参考:OpenVR社区经验表明同步调用是性能杀手 # # gsg = self.world.win.getGsg() # if gsg and hasattr(gsg, 'flush'): # try: # gsg.flush() # except Exception as flush_error: # if not hasattr(self, '_gsg_flush_error_logged'): # print(f"⚠️ GSG刷新失败: {flush_error}") # self._gsg_flush_error_logged = True # 🚀 关键优化:使用缓存的OpenVR Texture对象,避免每帧创建 if eye == openvr.Eye_Left: ovr_texture = self.optimization._left_ovr_texture if self.optimization else None else: ovr_texture = self.optimization._right_ovr_texture if self.optimization else None # 检查缓存对象是否存在(向后兼容) if ovr_texture is None: # 备用方案:如果缓存对象不存在,创建新的(性能较差) ovr_texture = openvr.Texture_t() ovr_texture.eType = openvr.TextureType_OpenGL ovr_texture.eColorSpace = openvr.ColorSpace_Gamma if not hasattr(self, '_texture_fallback_warned'): print("⚠️ 使用Texture对象备用方案(性能次优)") self._texture_fallback_warned = True # 只更新handle,其他属性已预设置 ovr_texture.handle = handle # 提交到VR系统 error = self.vr_compositor.submit(eye, ovr_texture) # 检查错误 if error and error != openvr.VRCompositorError_None: print(f"⚠️ VR{eye_name}纹理提交错误: {error}") self.submit_failures += 1 else: # 只在第一次成功时输出 if not hasattr(self, '_optimized_submit_success_logged'): print(f"✅ 优化版VR纹理提交成功! 使用缓存ID,避免重复prepareNow") print(f" {eye_name}纹理ID: {handle}") self._optimized_submit_success_logged = True # 🔧 智能GPU同步策略 - 减少不必要的flush调用 # 只有在检测到性能问题或每N帧时才强制flush if eye == openvr.Eye_Right: self._smart_gpu_sync() except Exception as e: print(f"❌ VR纹理提交异常: {e}") import traceback traceback.print_exc() self.submit_failures += 1 def _smart_gpu_sync(self): """智能GPU同步策略 - 只在必要时进行同步""" try: # 初始化同步控制变量 if not hasattr(self, '_last_gpu_sync_frame'): self._last_gpu_sync_frame = 0 self._gpu_sync_interval = 60 # 每60帧强制同步一次(1秒@60FPS) self._performance_based_sync = True current_frame = getattr(self, 'frame_count', 0) # 策略1: 性能自适应同步 if self._performance_based_sync: # 如果渲染时间过长,增加同步频率 total_render_time = getattr(self, 'left_render_time', 0) + getattr(self, 'right_render_time', 0) if total_render_time > 15.0: # 如果总渲染时间超过15ms # 高负载时更频繁同步 sync_interval = 30 # 每30帧同步 elif total_render_time > 10.0: sync_interval = 45 # 中等负载 else: sync_interval = self._gpu_sync_interval # 正常负载 should_sync = (current_frame - self._last_gpu_sync_frame) >= sync_interval else: # 策略2: 固定间隔同步 should_sync = (current_frame - self._last_gpu_sync_frame) >= self._gpu_sync_interval # 执行同步 if should_sync: gsg = self.world.win.getGsg() if gsg and hasattr(gsg, 'flush'): try: gsg.flush() self._last_gpu_sync_frame = current_frame # 只在首次或Debug时输出 if not hasattr(self, '_smart_sync_logged') or self.debug_output_enabled: if not hasattr(self, '_smart_sync_logged'): print(f"🔧 智能GPU同步已启用 - 间隔:{sync_interval}帧") self._smart_sync_logged = True elif self.debug_output_enabled and current_frame % 600 == 1: print(f"🔧 智能同步触发 (帧#{current_frame}, 间隔:{sync_interval})") except Exception as flush_error: if not hasattr(self, '_smart_sync_error_logged'): print(f"⚠️ 智能GPU同步失败: {flush_error}") self._smart_sync_error_logged = True except Exception as e: print(f"⚠️ 智能GPU同步策略异常: {e}") def _disable_main_cam(self): """禁用主相机 - 基于参考实现""" try: # 保存原始相机状态 if not hasattr(self, '_original_camera_parent'): self._original_camera_parent = self.world.camera.getParent() # 创建空节点并将主相机重新附加到它 self._empty_world = NodePath("empty_world") self.world.camera.reparentTo(self._empty_world) print("✓ 主相机已禁用") except Exception as e: print(f"⚠️ 禁用主相机失败: {e}") def _enable_main_cam(self): """恢复主相机 - 基于参考实现""" try: # 恢复原始相机状态 if hasattr(self, '_original_camera_parent') and self._original_camera_parent: self.world.camera.reparentTo(self._original_camera_parent) else: # 如果没有保存的父节点,重新附加到render self.world.camera.reparentTo(self.world.render) # 清理空世界节点 if hasattr(self, '_empty_world'): self._empty_world.removeNode() delattr(self, '_empty_world') print("✓ 主相机已恢复") except Exception as e: print(f"⚠️ 恢复主相机失败: {e}") def _initialize_controllers(self): """初始化VR手柄控制器""" try: print("🎮 正在初始化VR手柄控制器...") # 创建左右手柄控制器实例 self.left_controller = LeftController(self) self.right_controller = RightController(self) # 检测现有连接的控制器 self._detect_controllers() print("✓ VR手柄控制器初始化完成") except Exception as e: print(f"⚠️ VR手柄初始化失败: {e}") import traceback traceback.print_exc() def _detect_controllers(self): """检测并连接VR控制器""" if not self.vr_system: return try: for device_index in range(openvr.k_unMaxTrackedDeviceCount): # 检查设备是否已连接 if not self.vr_system.isTrackedDeviceConnected(device_index): continue # 获取设备类型 device_class = self.vr_system.getTrackedDeviceClass(device_index) if device_class == openvr.TrackedDeviceClass_Controller: # 获取控制器角色 role = self.vr_system.getControllerRoleForTrackedDeviceIndex(device_index) if role == openvr.TrackedControllerRole_LeftHand and self.left_controller: self.left_controller.set_device_index(device_index) self.controllers[device_index] = self.left_controller # 为设备创建锚点 self._create_tracked_device_anchor(device_index, 'left_controller') elif role == openvr.TrackedControllerRole_RightHand and self.right_controller: self.right_controller.set_device_index(device_index) self.controllers[device_index] = self.right_controller # 为设备创建锚点 self._create_tracked_device_anchor(device_index, 'right_controller') print(f"🎮 检测到 {len(self.controllers)} 个控制器") except Exception as e: print(f"⚠️ 控制器检测失败: {e}") def _create_tracked_device_anchor(self, device_index, name): """为跟踪设备创建锚点节点""" if not self.tracking_space: print(f"⚠️ 无法为设备 {device_index} 创建锚点 - tracking_space未初始化") return try: # 获取设备模型名称 if self.vr_system: model_name = self.vr_system.getStringTrackedDeviceProperty( device_index, openvr.Prop_RenderModelName_String ) anchor_name = f"{device_index}:{model_name}:{name}" else: anchor_name = f"{device_index}:{name}" # 创建锚点节点 device_anchor = self.tracking_space.attachNewNode(anchor_name) self.tracked_device_anchors[device_index] = device_anchor print(f"✓ 为设备 {device_index} 创建锚点: {anchor_name}") except Exception as e: print(f"⚠️ 创建设备锚点失败: {e}") def update_tracked_devices(self): """更新所有跟踪设备的姿态 - 基于参考实现""" if not self.poses or not self.vr_system: return try: # 更新每个已连接的控制器 for device_index, controller in self.controllers.items(): if device_index < len(self.poses): pose_data = self.poses[device_index] # 更新控制器姿态 controller.update_pose(pose_data) # 更新控制器输入状态 controller.update_input_state(self.vr_system) # 更新其他跟踪设备的锚点 for device_index in range(1, min(len(self.poses), openvr.k_unMaxTrackedDeviceCount)): if device_index in self.tracked_device_anchors: pose_data = self.poses[device_index] if pose_data.bPoseIsValid: # 转换姿态矩阵 modelview = self.convert_mat(pose_data.mDeviceToAbsoluteTracking) final_matrix = self.coord_mat_inv * modelview * self.coord_mat # 更新锚点变换 anchor = self.tracked_device_anchors[device_index] anchor.setMat(final_matrix) anchor.show() else: # 姿态无效,隐藏锚点 self.tracked_device_anchors[device_index].hide() except Exception as e: if self.frame_count % 300 == 0: # 每5秒输出一次错误 print(f"⚠️ 更新跟踪设备失败: {e}") def get_controller_by_role(self, role): """根据角色获取控制器 Args: role: 'left' 或 'right' Returns: VRController实例或None """ if role == 'left': return self.left_controller elif role == 'right': return self.right_controller return None def are_controllers_connected(self): """检查是否有控制器连接""" return len(self.controllers) > 0 def get_connected_controllers(self): """获取所有连接的控制器列表""" return list(self.controllers.values()) def trigger_controller_haptic(self, role, duration=0.001, strength=1.0): """触发控制器震动反馈 Args: role: 'left', 'right' 或 'both' duration: 震动持续时间(秒) strength: 震动强度 (0.0-1.0) """ if role in ['left', 'both'] and self.left_controller: self.left_controller.trigger_haptic_feedback(duration, strength) if role in ['right', 'both'] and self.right_controller: self.right_controller.trigger_haptic_feedback(duration, strength) # VR动作系统便捷方法 def is_trigger_pressed(self, hand='any'): """检查扳机是否被按下 Args: hand: 'left', 'right', 'any' """ device_path = None if hand == 'left': device_path = '/user/hand/left' elif hand == 'right': device_path = '/user/hand/right' pressed, _ = self.action_manager.is_digital_action_pressed('trigger', device_path) return pressed def is_trigger_just_pressed(self, hand='any'): """检查扳机是否刚刚被按下""" device_path = None if hand == 'left': device_path = '/user/hand/left' elif hand == 'right': device_path = '/user/hand/right' pressed, _ = self.action_manager.is_digital_action_just_pressed('trigger', device_path) return pressed def is_grip_pressed(self, hand='any'): """检查握把是否被按下""" device_path = None if hand == 'left': device_path = '/user/hand/left' elif hand == 'right': device_path = '/user/hand/right' pressed, _ = self.action_manager.is_digital_action_pressed('grip', device_path) return pressed def is_grip_just_pressed(self, hand='any'): """检查握把是否刚刚被按下""" device_path = None if hand == 'left': device_path = '/user/hand/left' elif hand == 'right': device_path = '/user/hand/right' pressed, _ = self.action_manager.is_digital_action_just_pressed('grip', device_path) return pressed def is_menu_pressed(self, hand='any'): """检查菜单按钮是否被按下""" device_path = None if hand == 'left': device_path = '/user/hand/left' elif hand == 'right': device_path = '/user/hand/right' pressed, _ = self.action_manager.is_digital_action_pressed('menu', device_path) return pressed def is_trackpad_touched(self, hand='any'): """检查触摸板是否被触摸""" device_path = None if hand == 'left': device_path = '/user/hand/left' elif hand == 'right': device_path = '/user/hand/right' touched, _ = self.action_manager.is_digital_action_pressed('trackpad_touch', device_path) return touched def get_trackpad_position(self, hand='any'): """获取触摸板位置 Returns: Vec2或None: 触摸板位置 (-1到1的范围) """ device_path = None if hand == 'left': device_path = '/user/hand/left' elif hand == 'right': device_path = '/user/hand/right' value, _ = self.action_manager.get_analog_action_value('trackpad', device_path) return value # VR交互系统便捷方法 def get_selected_object(self, hand='any'): """获取指定手选中的对象 Args: hand: 'left', 'right', 'any' Returns: 选中的对象节点或None """ if hand == 'any': # 返回任意手选中的对象 for controller in self.get_connected_controllers(): selected = self.interaction_manager.get_selected_object(controller.name) if selected: return selected return None else: return self.interaction_manager.get_selected_object(hand) def get_grabbed_object(self, hand='any'): """获取指定手抓取的对象 Args: hand: 'left', 'right', 'any' Returns: 抓取的对象节点或None """ if hand == 'any': # 返回任意手抓取的对象 for controller in self.get_connected_controllers(): grabbed = self.interaction_manager.get_grabbed_object(controller.name) if grabbed: return grabbed return None else: return self.interaction_manager.get_grabbed_object(hand) def is_grabbing_object(self, hand='any'): """检查是否正在抓取对象 Args: hand: 'left', 'right', 'any' Returns: bool: 是否正在抓取 """ if hand == 'any': # 检查任意手是否正在抓取 for controller in self.get_connected_controllers(): if self.interaction_manager.is_grabbing(controller.name): return True return False else: return self.interaction_manager.is_grabbing(hand) def force_release_all_grabs(self): """强制释放所有抓取的对象""" self.interaction_manager.force_release_all() def add_interactable_object(self, object_node): """将对象标记为可交互 Args: object_node: 要标记的对象节点 """ self.interaction_manager._add_collision_to_object(object_node) def _disable_async_reprojection(self): """禁用异步重投影 - 备选修复方案""" try: # 尝试通过OpenVR设置禁用异步重投影 if hasattr(openvr, 'VRSettings'): settings = openvr.VRSettings() if settings: # 禁用异步重投影 error = settings.setBool("steamvr", "enableAsyncReprojection", False) if error == openvr.VRSettingsError_None: print("✅ 异步重投影已禁用") else: print(f"⚠️ 禁用异步重投影失败: 设置错误 {error}") else: print("⚠️ 无法获取VR设置接口") else: print("⚠️ OpenVR设置接口不可用") except Exception as e: print(f"⚠️ 禁用异步重投影失败: {e}") def enable_async_reprojection_disable(self): """启用异步重投影禁用选项 - 用户可调用的方法""" self.disable_async_reprojection = True print("📝 异步重投影禁用选项已启用,将在下次VR初始化时生效") def disable_async_reprojection_disable(self): """禁用异步重投影禁用选项 - 恢复默认行为""" self.disable_async_reprojection = False print("📝 异步重投影禁用选项已关闭,将使用默认ATW行为") # ====== VR分辨率缩放和质量预设系统 ====== # ====== 性能模式控制方法 ====== # ======================================================================== # 性能监控属性代理 - 属性级别的API向后兼容 # ======================================================================== @property def left_render_count(self): """左眼渲染计数器 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.left_render_count return 0 @left_render_count.setter def left_render_count(self, value): """左眼渲染计数器设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.left_render_count = value @property def right_render_count(self): """右眼渲染计数器 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.right_render_count return 0 @right_render_count.setter def right_render_count(self, value): """右眼渲染计数器设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.right_render_count = value @property def left_render_time(self): """左眼渲染时间 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.left_render_time return 0.0 @left_render_time.setter def left_render_time(self, value): """左眼渲染时间设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.left_render_time = value @property def right_render_time(self): """右眼渲染时间 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.right_render_time return 0.0 @right_render_time.setter def right_render_time(self, value): """右眼渲染时间设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.right_render_time = value # 帧计数和FPS相关属性 @property def frame_count(self): """帧计数器 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.frame_count return 0 @frame_count.setter def frame_count(self, value): """帧计数器设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.frame_count = value @property def vr_fps(self): """VR帧率 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.vr_fps return 0.0 @vr_fps.setter def vr_fps(self, value): """VR帧率设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.vr_fps = value @property def last_fps_check(self): """上次FPS检查 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.last_fps_check return 0 @last_fps_check.setter def last_fps_check(self, value): """上次FPS检查设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.last_fps_check = value @property def last_fps_time(self): """上次FPS时间 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.last_fps_time return 0.0 @last_fps_time.setter def last_fps_time(self, value): """上次FPS时间设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.last_fps_time = value # 失败计数器 @property def submit_failures(self): """提交失败计数 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.submit_failures return 0 @submit_failures.setter def submit_failures(self, value): """提交失败计数设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.submit_failures = value @property def pose_failures(self): """姿态失败计数 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.pose_failures return 0 @pose_failures.setter def pose_failures(self, value): """姿态失败计数设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.pose_failures = value # 性能监控开关 @property def performance_monitoring(self): """性能监控开关 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.performance_monitoring return False @performance_monitoring.setter def performance_monitoring(self, value): """性能监控开关设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.performance_monitoring = value @property def debug_output_enabled(self): """调试输出开关 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.debug_output_enabled return False @debug_output_enabled.setter def debug_output_enabled(self, value): """调试输出开关设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.debug_output_enabled = value # 时间监控属性 @property def wait_poses_time(self): """姿态等待时间 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.wait_poses_time return 0.0 @wait_poses_time.setter def wait_poses_time(self, value): """姿态等待时间设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.wait_poses_time = value @property def submit_time(self): """纹理提交时间 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.submit_time return 0.0 @submit_time.setter def submit_time(self, value): """纹理提交时间设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.submit_time = value # 监控开关 @property def enable_gpu_timing(self): """GPU计时开关 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.enable_gpu_timing return False @enable_gpu_timing.setter def enable_gpu_timing(self, value): """GPU计时开关设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.enable_gpu_timing = value @property def enable_pipeline_monitoring(self): """管线监控开关 - 代理到性能监控系统""" if self.performance_monitor: return self.performance_monitor.enable_pipeline_monitoring return True @enable_pipeline_monitoring.setter def enable_pipeline_monitoring(self, value): """管线监控开关设置 - 代理到性能监控系统""" if self.performance_monitor: self.performance_monitor.enable_pipeline_monitoring = value @property def performance_mode_enabled(self): """性能模式开关 - 代理到优化模块""" if self.optimization: return self.optimization.performance_mode_enabled return False @performance_mode_enabled.setter def performance_mode_enabled(self, value): """性能模式开关设置 - 代理到优化模块""" if self.optimization: self.optimization.performance_mode_enabled = value @property def performance_mode_trigger_frame(self): """性能模式触发帧 - 代理到优化模块""" if self.optimization: return self.optimization.performance_mode_trigger_frame return 600 @performance_mode_trigger_frame.setter def performance_mode_trigger_frame(self, value): """性能模式触发帧设置 - 代理到优化模块""" if self.optimization: self.optimization.performance_mode_trigger_frame = value # GC控制属性代理 - 修复'gc_control_enabled'属性访问问题 @property def _gc_control_enabled(self): """GC控制启用状态 - 代理到优化模块""" if self.optimization: return self.optimization._gc_control_enabled return True @_gc_control_enabled.setter def _gc_control_enabled(self, value): """GC控制启用状态设置 - 代理到优化模块""" if self.optimization: self.optimization._gc_control_enabled = value @property def _gc_disabled(self): """GC禁用状态 - 代理到优化模块""" if self.optimization: return self.optimization._gc_disabled return False @_gc_disabled.setter def _gc_disabled(self, value): """GC禁用状态设置 - 代理到优化模块""" if self.optimization: self.optimization._gc_disabled = value @property def _manual_gc_interval(self): """手动GC间隔 - 代理到优化模块""" if self.optimization: return self.optimization._manual_gc_interval return 900 @_manual_gc_interval.setter def _manual_gc_interval(self, value): """手动GC间隔设置 - 代理到优化模块""" if self.optimization: self.optimization._manual_gc_interval = value # 分辨率缩放属性代理 @property def resolution_scale(self): """分辨率缩放系数 - 代理到优化模块""" if self.optimization: return self.optimization.resolution_scale return 0.75 @resolution_scale.setter def resolution_scale(self, value): """分辨率缩放系数设置 - 代理到优化模块""" if self.optimization: self.optimization.resolution_scale = value @property def base_eye_width(self): """基础眼宽 - 代理到优化模块""" if self.optimization: return self.optimization.base_eye_width return 1080 @base_eye_width.setter def base_eye_width(self, value): """基础眼宽设置 - 代理到优化模块""" if self.optimization: self.optimization.base_eye_width = value @property def base_eye_height(self): """基础眼高 - 代理到优化模块""" if self.optimization: return self.optimization.base_eye_height return 1200 @base_eye_height.setter def base_eye_height(self, value): """基础眼高设置 - 代理到优化模块""" if self.optimization: self.optimization.base_eye_height = value @property def scaled_eye_width(self): """缩放后眼宽 - 代理到优化模块""" if self.optimization: return self.optimization.scaled_eye_width return 1080 @scaled_eye_width.setter def scaled_eye_width(self, value): """缩放后眼宽设置 - 代理到优化模块""" if self.optimization: self.optimization.scaled_eye_width = value @property def scaled_eye_height(self): """缩放后眼高 - 代理到优化模块""" if self.optimization: return self.optimization.scaled_eye_height return 1200 @scaled_eye_height.setter def scaled_eye_height(self, value): """缩放后眼高设置 - 代理到优化模块""" if self.optimization: self.optimization.scaled_eye_height = value @property def current_quality_preset(self): """当前质量预设 - 代理到优化模块""" if self.optimization: return self.optimization.current_quality_preset return "balanced" @current_quality_preset.setter def current_quality_preset(self, value): """当前质量预设设置 - 代理到优化模块""" if self.optimization: self.optimization.current_quality_preset = value @property def quality_presets(self): """质量预设 - 代理到优化模块""" if self.optimization: return self.optimization.quality_presets return { "performance": 0.6, "balanced": 0.75, "quality": 1.0 } @quality_presets.setter def quality_presets(self, value): """质量预设设置 - 代理到优化模块""" if self.optimization: self.optimization.quality_presets = value # ======================================================================== # 性能监控委托方法 - API向后兼容层 # ======================================================================== # 性能报告方法 def _print_performance_report(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._print_performance_report() def _print_performance_recommendations(self, stats): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._print_performance_recommendations(stats) def _print_brief_performance_report(self, stats): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._print_brief_performance_report(stats) # 性能监控核心方法 def _update_performance_metrics(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._update_performance_metrics() def _update_gpu_metrics(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._update_gpu_metrics() def _track_frame_time(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._track_frame_time() # GPU计时方法 def _get_gpu_frame_timing(self, frames_ago=0): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._get_gpu_frame_timing(frames_ago) return None def enable_gpu_timing_monitoring(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.enable_gpu_timing_monitoring() def disable_gpu_timing_monitoring(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.disable_gpu_timing_monitoring() # 管线统计方法 def _start_timing(self, operation_name): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._start_timing(operation_name) return None def _end_timing(self, timing_data): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._end_timing(timing_data) return 0.0 def _get_pipeline_stats(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._get_pipeline_stats() return {} def test_pipeline_monitoring(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.test_pipeline_monitoring() # 诊断方法 def _print_render_callback_diagnostics(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._print_render_callback_diagnostics() def _check_rendering_optimizations(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._check_rendering_optimizations() def _diagnose_opengl_state(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor._diagnose_opengl_state() # 调试控制方法 def enable_debug_output(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.enable_debug_output() def disable_debug_output(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.disable_debug_output() def set_debug_mode(self, mode): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.set_debug_mode(mode) def toggle_debug_output(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.toggle_debug_output() return False def get_debug_status(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.get_debug_status() return {} # 配置方法 def set_performance_check_interval(self, interval): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.set_performance_check_interval(interval) def set_frame_time_history_size(self, size): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.set_frame_time_history_size(size) def set_performance_report_interval(self, frames): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.set_performance_report_interval(frames) def set_prediction_time(self, prediction_time_ms): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.set_prediction_time(prediction_time_ms) # 查询方法 def get_performance_stats(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.get_performance_stats() return {} def get_current_performance_summary(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.get_current_performance_summary() return "性能监控未初始化" def get_performance_monitoring_config(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.get_performance_monitoring_config() return {} def print_performance_monitoring_status(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.print_performance_monitoring_status() # 控制方法 def enable_performance_monitoring(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.enable_performance_monitoring() def disable_performance_monitoring(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.disable_performance_monitoring() def force_performance_report(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.force_performance_report() def reset_performance_counters(self): """委托到性能监控系统""" if self.performance_monitor: return self.performance_monitor.reset_performance_counters() # ======================================================================== # 纹理提交方法 - VR渲染核心功能 # ======================================================================== def _batch_submit_textures(self): """批量提交两眼纹理 - OpenVR最佳实践 """ try: if not self.vr_compositor: return False # 检查纹理是否准备好 if not (self.vr_left_texture and self.vr_right_texture): return False import openvr # 🚀 关键:快速连续提交两眼,最小化阻塞时间 # 这符合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 enable_vr_test_mode(self, display_mode='stereo'): """委托到测试模式系统""" if self.test_mode: return self.test_mode.enable_vr_test_mode(display_mode) return False def disable_vr_test_mode(self): """委托到测试模式系统""" if self.test_mode: return self.test_mode.disable_vr_test_mode() return False def switch_test_display_mode(self, display_mode): """委托到测试模式系统""" if self.test_mode: return self.test_mode.switch_test_display_mode(display_mode) return False def get_test_mode_status(self): """委托到测试模式系统""" if self.test_mode: return self.test_mode.get_test_mode_status() return None def get_test_mode_features(self): """委托到测试模式系统""" if self.test_mode: return self.test_mode.get_test_mode_features() return {} def set_test_mode_features(self, submit_texture=None, wait_poses=None): """委托到测试模式系统""" if self.test_mode: return self.test_mode.set_test_mode_features(submit_texture, wait_poses) def run_vr_performance_test(self, duration_seconds=30, display_mode='stereo'): """委托到测试模式系统""" if self.test_mode: return self.test_mode.run_vr_performance_test(duration_seconds, display_mode) return None @property def vr_test_mode(self): """测试模式启用状态 - 代理到测试模式系统""" if self.test_mode: return self.test_mode.vr_test_mode return False @vr_test_mode.setter def vr_test_mode(self, value): """测试模式启用状态设置 - 代理到测试模式系统""" if self.test_mode: self.test_mode.vr_test_mode = value @property def test_display_mode(self): """测试显示模式 - 代理到测试模式系统""" if self.test_mode: return self.test_mode.test_display_mode return 'stereo' @test_display_mode.setter def test_display_mode(self, value): """测试显示模式设置 - 代理到测试模式系统""" if self.test_mode: self.test_mode.test_display_mode = value @property def test_mode_submit_texture(self): """测试模式纹理提交开关 - 代理到测试模式系统""" if self.test_mode: return self.test_mode.test_mode_submit_texture return False @test_mode_submit_texture.setter def test_mode_submit_texture(self, value): """测试模式纹理提交开关设置 - 代理到测试模式系统""" if self.test_mode: self.test_mode.test_mode_submit_texture = value @property def test_mode_wait_poses(self): """测试模式姿态等待开关 - 代理到测试模式系统""" if self.test_mode: return self.test_mode.test_mode_wait_poses return False @test_mode_wait_poses.setter def test_mode_wait_poses(self, value): """测试模式姿态等待开关设置 - 代理到测试模式系统""" if self.test_mode: self.test_mode.test_mode_wait_poses = value @property def test_mode_initialized(self): """测试模式初始化状态 - 代理到测试模式系统""" if self.test_mode: return self.test_mode.test_mode_initialized return False