3555 lines
148 KiB
Python
3555 lines
148 KiB
Python
"""
|
||
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
|