EG/core/vr/performance/monitoring.py
2025-10-14 15:34:20 +08:00

1172 lines
50 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
VR性能监控子系统
负责VR应用的性能监控、GPU计时、管线统计和性能诊断
"""
import sys
import gc
import time
# 可选依赖
try:
import psutil
PSUTIL_AVAILABLE = True
except ImportError:
PSUTIL_AVAILABLE = False
try:
import GPUtil
GPUTIL_AVAILABLE = True
except ImportError:
GPUTIL_AVAILABLE = False
try:
import pynvml
PYNVML_AVAILABLE = True
except ImportError:
PYNVML_AVAILABLE = False
class VRPerformanceMonitor:
"""VR性能监控系统
功能:
- 实时性能监控CPU、内存、GPU
- GPU渲染时间统计
- 渲染管线分析
- 性能诊断和优化建议
"""
def __init__(self, vr_manager):
"""初始化性能监控系统
Args:
vr_manager: VRManager实例的引用
"""
self.vr_manager = vr_manager
# ===== 性能计数器 =====
self.frame_count = 0
self.last_fps_check = 0
self.last_fps_time = 0
self.vr_fps = 0
self.submit_failures = 0
self.pose_failures = 0
# ===== 性能监控配置 =====
self.performance_monitoring = False # 是否启用性能监控
self.debug_output_enabled = False # 是否启用调试输出
self.debug_mode = 'detailed' # 'brief' 或 'detailed'
self.cpu_usage = 0.0
self.memory_usage = 0.0
self.gpu_usage = 0.0
self.gpu_memory_usage = 0.0
self.frame_times = [] # 存储最近帧时间
self.max_frame_time_history = 60 # 保存60帧的历史
self.last_performance_check = 0
self.performance_check_interval = 0.5 # 每0.5秒更新一次性能数据
# ===== 渲染管线监控 =====
self.enable_pipeline_monitoring = True # 是否启用管线监控
self.performance_mode_enabled = False # 性能优化模式(禁用监控以减少对象创建)
self.performance_mode_trigger_frame = 600 # 第600帧后启用性能模式
self.wait_poses_time = 0.0 # waitGetPoses耗时
self.left_render_time = 0.0 # 左眼渲染耗时
self.right_render_time = 0.0 # 右眼渲染耗时
self.submit_time = 0.0 # 纹理提交耗时
self.left_render_count = 0 # 左眼渲染次数计数
self.right_render_count = 0 # 右眼渲染次数计数
self.total_frame_time = 0.0 # 总帧时间
self.vr_sync_wait_time = 0.0 # VR同步等待时间
# ===== 时间监控历史 =====
self.wait_poses_times = []
self.render_times = []
self.submit_times = []
self.sync_wait_times = []
self.pipeline_history_size = 30
# ===== GPU渲染时间监控 =====
self.enable_gpu_timing = False # 是否启用GPU时间监控默认关闭
self.gpu_scene_render_ms = 0.0 # GPU场景渲染时间
self.gpu_pre_submit_ms = 0.0 # 提交前GPU时间
self.gpu_post_submit_ms = 0.0 # 提交后GPU时间
self.gpu_total_render_ms = 0.0 # GPU总渲染时间
self.gpu_compositor_render_ms = 0.0 # GPU合成器渲染时间
self.gpu_client_frame_interval_ms = 0.0 # 客户端帧间隔
self.gpu_timing_history = [] # GPU时间历史记录
self.gpu_timing_history_size = 30 # GPU时间历史记录大小
self.gpu_timing_failure_count = 0 # GPU时间获取失败次数
# ===== VR系统信息 =====
self.current_eye_resolution = (0, 0)
self.recommended_eye_resolution = (0, 0)
self.vr_display_frequency = 0.0
self.vr_vsync_enabled = True
self.vsync_to_photons_ms = 0.0 # VSync到光子的延迟
self.target_frame_time_ms = 0.0 # 目标帧时间
self.vsync_window_ms = 0.0 # VSync时间窗口
self.async_reprojection_enabled = False # 异步重投影状态
self.motion_smoothing_enabled = False # 运动平滑状态
# ===== 性能报告间隔 =====
self.performance_report_interval = 1800 # 默认1800帧30秒@60fps
# ===== 内部状态 =====
self._last_frame_time = None # 用于帧时间计算
# 初始化性能监控库
self._init_performance_monitoring()
# ========== 性能报告方法 ==========
def _print_performance_report(self):
"""输出VR性能报告"""
if not self.performance_monitoring or not self.debug_output_enabled:
return
stats = self.get_performance_stats()
# 简短模式输出
if self.debug_mode == 'brief':
self._print_brief_performance_report(stats)
return
print("📊 ======= VR性能监控报告 =======")
# 帧率和帧时间信息
print(f"🎯 渲染性能:")
print(f" VR帧率: {stats['vr_fps']:.1f} FPS")
print(f" 平均帧时间: {stats['frame_time_avg']:.2f} ms")
print(f" 最小帧时间: {stats['frame_time_min']:.2f} ms")
print(f" 最大帧时间: {stats['frame_time_max']:.2f} ms")
print(f" 95%帧时间: {stats['frame_time_95th']:.2f} ms")
# 系统性能
print(f"💻 系统性能:")
print(f" CPU使用率: {stats['cpu_usage']:.1f}%")
print(f" 内存使用率: {stats['memory_usage']:.1f}%")
# GPU性能
print(f"🎮 GPU性能:")
if self.gputil_available or self.nvidia_ml_available:
print(f" GPU使用率: {stats['gpu_usage']:.1f}%")
print(f" 显存使用率: {stats['gpu_memory_usage']:.1f}%")
else:
print(f" GPU监控: 不可用 (需要安装 GPUtil 或 pynvml)")
print(f" 安装命令: pip install GPUtil nvidia-ml-py")
# GPU渲染时间OpenVR Frame Timing
if self.enable_gpu_timing:
print(f"⚡ GPU渲染时间:")
pipeline_stats = self._get_pipeline_stats()
gpu_stats = pipeline_stats.get('gpu_timing', {})
gpu_current = pipeline_stats.get('current', {})
# 检查是否有可用的GPU时间数据
has_gpu_data = any(
gpu_current.get(field, 0) > 0
for field in ['gpu_scene_render', 'gpu_total_render', 'gpu_pre_submit', 'gpu_post_submit', 'gpu_compositor_render']
)
if has_gpu_data:
# 显示GPU时间统计最近30帧平均
scene_render = gpu_stats.get('scene_render', {'avg': 0})
total_render = gpu_stats.get('total_render', {'avg': 0})
pre_submit = gpu_stats.get('pre_submit', {'avg': 0})
post_submit = gpu_stats.get('post_submit', {'avg': 0})
compositor = gpu_stats.get('compositor_render', {'avg': 0})
if scene_render['avg'] > 0:
print(f" GPU场景渲染: {scene_render['avg']:.2f}ms (min:{scene_render['min']:.1f}, max:{scene_render['max']:.1f})")
if total_render['avg'] > 0:
print(f" GPU总渲染时间: {total_render['avg']:.2f}ms (min:{total_render['min']:.1f}, max:{total_render['max']:.1f})")
if pre_submit['avg'] > 0:
print(f" GPU提交前时间: {pre_submit['avg']:.2f}ms (min:{pre_submit['min']:.1f}, max:{pre_submit['max']:.1f})")
if post_submit['avg'] > 0:
print(f" GPU提交后时间: {post_submit['avg']:.2f}ms (min:{post_submit['min']:.1f}, max:{post_submit['max']:.1f})")
if compositor['avg'] > 0:
print(f" GPU合成器时间: {compositor['avg']:.2f}ms (min:{compositor['min']:.1f}, max:{compositor['max']:.1f})")
# 显示当前帧GPU时间
print(f"🔍 当前帧GPU时间:")
if gpu_current.get('gpu_scene_render', 0) > 0:
print(f" 场景渲染: {gpu_current['gpu_scene_render']:.2f}ms")
if gpu_current.get('gpu_total_render', 0) > 0:
print(f" 总渲染: {gpu_current['gpu_total_render']:.2f}ms")
if gpu_current.get('gpu_compositor_render', 0) > 0:
print(f" 合成器: {gpu_current['gpu_compositor_render']:.2f}ms")
# GPU时间瓶颈分析
current_total = gpu_current.get('gpu_total_render', 0)
current_scene = gpu_current.get('gpu_scene_render', 0)
if current_total > 12.0: # 假设72fps目标留出一些余量
print(f" ⚠️ GPU总渲染时间过长: {current_total:.1f}ms")
elif current_scene > 8.0:
print(f" ⚠️ GPU场景渲染时间偏高: {current_scene:.1f}ms")
else:
print(f" GPU渲染时间: 暂无数据")
if self.gpu_timing_failure_count > 0:
print(f" 获取失败次数: {self.gpu_timing_failure_count}")
else:
print(f" 等待OpenVR Frame Timing数据...")
else:
print(f"⚡ GPU渲染时间: 已禁用")
# VR特定指标
print(f"🥽 VR指标:")
print(f" 总帧数: {stats['frame_count']}")
print(f" 提交失败: {stats['submit_failures']}")
print(f" 姿态失败: {stats['pose_failures']}")
# 计算失败率
if stats['frame_count'] > 0:
submit_fail_rate = (stats['submit_failures'] / stats['frame_count']) * 100
pose_fail_rate = (stats['pose_failures'] / stats['frame_count']) * 100
print(f" 提交失败率: {submit_fail_rate:.2f}%")
print(f" 姿态失败率: {pose_fail_rate:.2f}%")
# 渲染管线监控
target_frame_time = 13.9 # 默认目标帧时间(72Hz)
if self.enable_pipeline_monitoring:
pipeline_stats = self._get_pipeline_stats()
print(f"⚙️ 渲染管线分析:")
print(f" 当前分辨率: {pipeline_stats['vr_info']['eye_resolution'][0]}x{pipeline_stats['vr_info']['eye_resolution'][1]}")
print(f" 推荐分辨率: {pipeline_stats['vr_info']['recommended_resolution'][0]}x{pipeline_stats['vr_info']['recommended_resolution'][1]}")
print(f" 显示频率: {pipeline_stats['vr_info']['display_frequency']:.1f} Hz")
# VSync和时序信息
if pipeline_stats['vr_info']['target_frame_time_ms'] > 0:
print(f"🎯 VSync时序:")
print(f" 目标帧时间: {pipeline_stats['vr_info']['target_frame_time_ms']:.2f}ms")
print(f" VSync到光子: {pipeline_stats['vr_info']['vsync_to_photons_ms']:.2f}ms")
print(f" VSync窗口: ±{pipeline_stats['vr_info']['vsync_window_ms']:.2f}ms")
print(f" 异步重投影: {'启用' if pipeline_stats['vr_info']['async_reprojection'] else '禁用'}")
print(f" 运动平滑: {'启用' if pipeline_stats['vr_info']['motion_smoothing'] else '禁用'}")
# 分析帧时间是否在目标范围内
current_frame_time = stats['frame_time_avg']
target_frame_time = pipeline_stats['vr_info']['target_frame_time_ms']
if current_frame_time > 0 and target_frame_time > 0:
frame_time_ratio = current_frame_time / target_frame_time
if frame_time_ratio > 1.1:
print(f" ⚠️ 帧时间超标: {current_frame_time:.1f}ms (目标:{target_frame_time:.1f}ms)")
elif frame_time_ratio < 0.9:
print(f" ✅ 帧时间充裕: {current_frame_time:.1f}ms (目标:{target_frame_time:.1f}ms)")
else:
print(f" ✓ 帧时间正常: {current_frame_time:.1f}ms (目标:{target_frame_time:.1f}ms)")
# 🔧 性能优化诊断
print(f"🔧 优化诊断:")
current = pipeline_stats.get('current', {}) if self.enable_pipeline_monitoring else {}
# 🔍 渲染回调诊断 - 新增
self._print_render_callback_diagnostics()
# waitGetPoses时序分析
wait_poses_time = current.get('wait_poses', 0)
if wait_poses_time > 10:
print(f" ⚠️ waitGetPoses时间过长: {wait_poses_time:.1f}ms")
print(f" 可能原因: 错过VSync窗口建议调整预测时间")
elif wait_poses_time > 5:
print(f" ⚠️ waitGetPoses时间偏高: {wait_poses_time:.1f}ms")
else:
print(f" ✓ waitGetPoses时间正常: {wait_poses_time:.1f}ms")
# CPU-GPU并行度分析 - 增强诊断
gpu_total = current.get('gpu_total_render', 0)
cpu_render_left = self.left_render_time
cpu_render_right = self.right_render_time
cpu_render_total = cpu_render_left + cpu_render_right
print(f" 真实渲染时间对比:")
print(f" CPU左眼: {cpu_render_left:.2f}ms")
print(f" CPU右眼: {cpu_render_right:.2f}ms")
print(f" CPU总计: {cpu_render_total:.2f}ms")
print(f" GPU总计: {gpu_total:.2f}ms")
if cpu_render_total < 1.0:
print(f" ⚠️ CPU渲染时间异常短: {cpu_render_total:.2f}ms")
print(f" 可能原因: 渲染回调未正确执行或渲染被跳过")
elif gpu_total > 0 and cpu_render_total > 0:
ratio = gpu_total / cpu_render_total
if ratio > 10:
print(f" ⚠️ GPU严重等待CPU: 比例{ratio:.1f}:1")
print(f" 建议: 检查OpenGL命令提交时机和渲染状态")
elif ratio > 3:
print(f" ⚠️ GPU等待CPU: 比例{ratio:.1f}:1")
print(f" 建议: 检查OpenGL命令提交是否及时")
else:
print(f" ✓ CPU-GPU时间匹配: 比例{ratio:.1f}:1")
# 预测时间诊断
current_prediction = self.vr_manager.use_prediction_time * 1000
print(f" 预测时间设置: {current_prediction:.1f}ms")
if current_prediction > 15:
print(f" 建议: 预测时间较高可能增加waitGetPoses延迟")
elif current_prediction < 8:
print(f" 注意: 预测时间较低,可能影响姿态准确性")
else:
print(f" ✓ 预测时间在合理范围内")
# 优化状态总结
optimization_score = 0
if wait_poses_time < 8:
optimization_score += 1
if stats['vr_fps'] > 60:
optimization_score += 1
if stats.get('frame_time_avg', 0) < target_frame_time * 1.1:
optimization_score += 1
if optimization_score >= 2:
print(f" ✅ 优化效果良好 ({optimization_score}/3)")
else:
print(f" ⚠️ 仍有优化空间 ({optimization_score}/3)")
# 显示详细统计信息(仅当管线监控启用时)
if self.enable_pipeline_monitoring:
print(f"🕐 各阶段耗时 (最近{self.pipeline_history_size}帧平均):")
print(f" waitGetPoses: {pipeline_stats['wait_poses']['avg']:.2f}ms (min:{pipeline_stats['wait_poses']['min']:.1f}, max:{pipeline_stats['wait_poses']['max']:.1f})")
print(f" 渲染总计: {pipeline_stats['render']['avg']:.2f}ms (min:{pipeline_stats['render']['min']:.1f}, max:{pipeline_stats['render']['max']:.1f})")
print(f" 纹理提交: {pipeline_stats['submit']['avg']:.2f}ms (min:{pipeline_stats['submit']['min']:.1f}, max:{pipeline_stats['submit']['max']:.1f})")
if pipeline_stats['sync_wait']['avg'] > 0:
print(f" 同步等待: {pipeline_stats['sync_wait']['avg']:.2f}ms (min:{pipeline_stats['sync_wait']['min']:.1f}, max:{pipeline_stats['sync_wait']['max']:.1f})")
print(f"🔍 当前帧详情:")
print(f" 左眼渲染: {pipeline_stats['current']['left_render']:.2f}ms")
print(f" 右眼渲染: {pipeline_stats['current']['right_render']:.2f}ms")
print(f" 姿态获取: {pipeline_stats['current']['wait_poses']:.2f}ms")
# 显示Running Start模式信息
print(f"🎯 Running Start模式:")
print(f" 模式: Valve Running Start - 帧开始时获取姿态")
print(f" 优势: VSync前3ms获取姿态提供充足渲染时间")
print(f" 预测时间: {self.vr_manager.use_prediction_time * 1000:.1f}ms")
# 分析最大瓶颈
current = pipeline_stats['current']
bottleneck_analysis = []
if current['wait_poses'] > 5.0:
bottleneck_analysis.append(f"姿态获取耗时过长({current['wait_poses']:.1f}ms)")
if current['total_render'] > 8.0:
bottleneck_analysis.append(f"渲染耗时过长({current['total_render']:.1f}ms)")
if current['submit'] > 2.0:
bottleneck_analysis.append(f"纹理提交耗时过长({current['submit']:.1f}ms)")
if bottleneck_analysis:
print(f"🚨 瓶颈分析:")
for analysis in bottleneck_analysis:
print(f" ⚠️ {analysis}")
# 性能建议
self._print_performance_recommendations(stats)
print("===============================")
def _print_performance_recommendations(self, stats):
"""根据性能数据输出优化建议"""
print(f"💡 性能建议:")
recommendations = []
# FPS相关建议
if stats['vr_fps'] < 60:
recommendations.append(" ⚠️ VR帧率过低可能影响体验")
# 帧时间相关建议
if stats['frame_time_avg'] > 16.7: # 60fps = 16.7ms
recommendations.append(" ⚠️ 平均帧时间过高,建议降低渲染质量")
if stats['frame_time_max'] > 50:
recommendations.append(" ⚠️ 检测到严重卡顿检查CPU/GPU负载")
# CPU建议
if stats['cpu_usage'] > 80:
recommendations.append(" 🔴 CPU使用率过高可能存在CPU瓶颈")
elif stats['cpu_usage'] > 60:
recommendations.append(" 🟡 CPU使用率偏高注意监控")
# 内存建议
if stats['memory_usage'] > 85:
recommendations.append(" 🔴 内存使用率过高,可能影响性能")
# GPU建议
if self.gputil_available or self.nvidia_ml_available:
if stats['gpu_usage'] > 95:
recommendations.append(" 🔴 GPU使用率接近满载存在GPU瓶颈")
if stats['gpu_memory_usage'] > 90:
recommendations.append(" 🔴 显存使用率过高,可能需要降低纹理质量")
# GPU渲染时间建议
if self.enable_gpu_timing:
if self.gpu_total_render_ms > 12.0:
recommendations.append(" ⚠️ GPU总渲染时间过长建议优化场景复杂度")
if self.gpu_scene_render_ms > 8.0:
recommendations.append(" ⚠️ GPU场景渲染时间偏高考虑降低渲染质量")
if self.gpu_compositor_render_ms > 3.0:
recommendations.append(" ⚠️ GPU合成器时间过长检查VR设置或叠加层")
if self.gpu_timing_failure_count > 100:
recommendations.append(" ⚠️ GPU时间统计频繁失败可能需要更新OpenVR")
# 失败率建议
submit_fail_rate = (stats['submit_failures'] / max(stats['frame_count'], 1)) * 100
if submit_fail_rate > 1:
recommendations.append(" ⚠️ VR帧提交失败率较高检查VR系统状态")
if not recommendations:
recommendations.append(" ✅ 性能表现良好,无明显问题")
for rec in recommendations:
print(rec)
def _print_brief_performance_report(self, stats):
"""输出简短的VR性能报告"""
# 创建一行简短摘要
summary = f"🥽 VR性能: {stats['vr_fps']:.1f}fps"
if stats['frame_time_avg'] > 0:
summary += f" | 帧时间: {stats['frame_time_avg']:.1f}ms"
if self.psutil_available:
summary += f" | CPU: {stats['cpu_usage']:.0f}%"
summary += f" | 内存: {stats['memory_usage']:.0f}%"
# 显示GPU信息如果库不可用则显示提示
if self.gputil_available or self.nvidia_ml_available:
summary += f" | GPU: {stats['gpu_usage']:.0f}%"
else:
summary += " | GPU: N/A"
# 添加失败率指示
if stats['frame_count'] > 0:
submit_fail_rate = (stats['submit_failures'] / stats['frame_count']) * 100
if submit_fail_rate > 0.1:
summary += f" | 提交失败: {submit_fail_rate:.1f}%"
# 添加管线关键信息
if self.enable_pipeline_monitoring:
pipeline_stats = self._get_pipeline_stats()
current = pipeline_stats['current']
# 显示关键瓶颈
if current['wait_poses'] > 5.0:
summary += f" | 姿态: {current['wait_poses']:.1f}ms⚠"
elif current['wait_poses'] > 0:
summary += f" | 姿态: {current['wait_poses']:.1f}ms"
if current['total_render'] > 8.0:
summary += f" | 渲染: {current['total_render']:.1f}ms⚠"
elif current['total_render'] > 0:
summary += f" | 渲染: {current['total_render']:.1f}ms"
# 添加GPU渲染时间信息
if self.enable_gpu_timing:
gpu_total = current.get('gpu_total_render', 0)
gpu_scene = current.get('gpu_scene_render', 0)
if gpu_total > 12.0:
summary += f" | GPU: {gpu_total:.1f}ms⚠"
elif gpu_total > 0:
summary += f" | GPU: {gpu_total:.1f}ms"
elif gpu_scene > 0:
summary += f" | GPU场景: {gpu_scene:.1f}ms"
# 显示目标帧时间对比
vr_info = pipeline_stats['vr_info']
if vr_info['target_frame_time_ms'] > 0:
target = vr_info['target_frame_time_ms']
current_avg = stats['frame_time_avg']
if current_avg > target * 1.1:
summary += f" | 目标:{target:.0f}ms⚠"
else:
summary += f" | 目标:{target:.0f}ms"
# 性能状态指示
if stats['vr_fps'] < 72:
summary += " ⚠️"
elif stats['vr_fps'] > 85:
summary += ""
print(summary)
# ========== 性能监控核心方法 ==========
def _init_performance_monitoring(self):
"""初始化性能监控库"""
self.psutil_available = False
self.gputil_available = False
self.nvidia_ml_available = False
try:
import psutil
self.psutil = psutil
self.psutil_available = True
print("✓ psutil性能监控库已加载")
except ImportError:
print("⚠️ psutil库未安装CPU和内存监控将不可用")
try:
import GPUtil
self.gputil = GPUtil
self.gputil_available = True
print("✓ GPUtil GPU监控库已加载")
except ImportError:
print("⚠️ GPUtil库未安装GPU监控将不可用")
try:
import pynvml
self.pynvml = pynvml
pynvml.nvmlInit()
self.nvidia_ml_available = True
print("✓ NVIDIA-ML GPU监控库已加载")
except ImportError:
print("⚠️ pynvml库未安装NVIDIA GPU详细监控将不可用")
except Exception as e:
print(f"⚠️ NVIDIA-ML初始化失败: {e}")
def _update_performance_metrics(self):
"""更新系统性能指标"""
if not self.performance_monitoring:
return
current_time = time.time()
# 限制更新频率
if current_time - self.last_performance_check < self.performance_check_interval:
return
self.last_performance_check = current_time
try:
# 更新CPU和内存使用率
if self.psutil_available:
self.cpu_usage = self.psutil.cpu_percent(interval=None)
memory = self.psutil.virtual_memory()
self.memory_usage = memory.percent
# 更新GPU使用率
self._update_gpu_metrics()
except Exception as e:
if self.frame_count % 600 == 0: # 每10秒输出一次错误
print(f"⚠️ 性能监控更新失败: {e}")
def _update_gpu_metrics(self):
"""更新GPU相关指标"""
try:
# 方法1: 使用GPUtil
if self.gputil_available:
gpus = self.gputil.getGPUs()
if gpus:
gpu = gpus[0] # 使用第一个GPU
self.gpu_usage = gpu.load * 100
self.gpu_memory_usage = gpu.memoryUtil * 100
# 方法2: 使用NVIDIA-ML (更精确)
elif self.nvidia_ml_available:
try:
handle = self.pynvml.nvmlDeviceGetHandleByIndex(0)
# GPU使用率
utilization = self.pynvml.nvmlDeviceGetUtilizationRates(handle)
self.gpu_usage = utilization.gpu
# GPU内存使用率
memory_info = self.pynvml.nvmlDeviceGetMemoryInfo(handle)
self.gpu_memory_usage = (memory_info.used / memory_info.total) * 100
except Exception as e:
# NVIDIA-ML可能无法在某些系统上工作
pass
except Exception as e:
# GPU监控失败但不影响VR功能
pass
def _track_frame_time(self):
"""记录帧时间 - 性能优化版本"""
current_time = time.time()
if self._last_frame_time is not None:
frame_time = (current_time - self._last_frame_time) * 1000 # 转换为毫秒
# 🚀 性能优化:性能模式下跳过列表操作以减少内存分配
if not self.performance_mode_enabled:
# 添加到帧时间历史
self.frame_times.append(frame_time)
# 限制历史长度
if len(self.frame_times) > self.max_frame_time_history:
self.frame_times.pop(0)
self._last_frame_time = current_time
# ========== GPU计时方法 ==========
def _get_gpu_frame_timing(self, frames_ago=0):
"""获取GPU渲染时间统计
Args:
frames_ago: 获取多少帧之前的数据0表示当前帧
Returns:
dict: GPU时间数据如果获取失败返回None
"""
if not self.enable_gpu_timing or not self.vr_manager.vr_compositor:
return None
try:
# 调用OpenVR的getFrameTiming API
result, timing = self.vr_manager.vr_compositor.getFrameTiming(framesAgo=frames_ago)
if not result:
self.gpu_timing_failure_count += 1
if self.gpu_timing_failure_count % 300 == 1: # 每5秒输出一次错误
print("⚠️ OpenVR getFrameTiming调用失败")
return None
# 提取GPU时间数据单位毫秒
gpu_data = {}
# 检查timing对象是否有GPU时间相关的属性
if hasattr(timing, 'm_flSceneRenderGpuMs'):
gpu_data['scene_render'] = timing.m_flSceneRenderGpuMs
self.gpu_scene_render_ms = timing.m_flSceneRenderGpuMs
if hasattr(timing, 'm_flPreSubmitGpuMs'):
gpu_data['pre_submit'] = timing.m_flPreSubmitGpuMs
self.gpu_pre_submit_ms = timing.m_flPreSubmitGpuMs
if hasattr(timing, 'm_flPostSubmitGpuMs'):
gpu_data['post_submit'] = timing.m_flPostSubmitGpuMs
self.gpu_post_submit_ms = timing.m_flPostSubmitGpuMs
if hasattr(timing, 'm_flTotalRenderGpuMs'):
gpu_data['total_render'] = timing.m_flTotalRenderGpuMs
self.gpu_total_render_ms = timing.m_flTotalRenderGpuMs
if hasattr(timing, 'm_flCompositorRenderGpuMs'):
gpu_data['compositor_render'] = timing.m_flCompositorRenderGpuMs
self.gpu_compositor_render_ms = timing.m_flCompositorRenderGpuMs
if hasattr(timing, 'm_flClientFrameIntervalMs'):
gpu_data['frame_interval'] = timing.m_flClientFrameIntervalMs
self.gpu_client_frame_interval_ms = timing.m_flClientFrameIntervalMs
# 将GPU时间数据添加到历史记录
if gpu_data:
self.gpu_timing_history.append(gpu_data)
if len(self.gpu_timing_history) > self.gpu_timing_history_size:
self.gpu_timing_history.pop(0)
# 调试信息 - 仅在第一次成功时输出
if not hasattr(self, '_gpu_timing_success_logged'):
available_fields = list(gpu_data.keys())
print(f"✅ GPU时间统计已启用 - 可用字段: {available_fields}")
self._gpu_timing_success_logged = True
return gpu_data
except AttributeError as e:
# OpenVR Python绑定可能不包含某些字段
if self.gpu_timing_failure_count == 0:
print(f"⚠️ GPU时间统计部分功能不可用: {e}")
print(" 这可能是由于OpenVR Python绑定版本问题")
self.gpu_timing_failure_count += 1
return None
except Exception as e:
self.gpu_timing_failure_count += 1
if self.gpu_timing_failure_count % 300 == 1: # 每5秒输出一次错误
print(f"⚠️ 获取GPU时间统计失败: {e}")
return None
def enable_gpu_timing_monitoring(self):
"""启用GPU时间监控"""
self.enable_gpu_timing = True
print("✓ VR GPU时间监控已启用")
def disable_gpu_timing_monitoring(self):
"""禁用GPU时间监控"""
self.enable_gpu_timing = False
print("✓ VR GPU时间监控已禁用")
# ========== 管线统计方法 ==========
def _start_timing(self, operation_name):
"""开始计时操作 - 性能优化版本"""
if not self.enable_pipeline_monitoring or self.performance_mode_enabled:
return None
# 🚀 性能优化:减少字典创建,只在必要时创建
return {
'operation': operation_name,
'start_time': time.perf_counter()
}
def _end_timing(self, timing_data):
"""结束计时并记录结果 - 性能优化版本"""
if not self.enable_pipeline_monitoring or self.performance_mode_enabled or not timing_data:
return 0.0
elapsed = (time.perf_counter() - timing_data['start_time']) * 1000 # 转换为毫秒
# 保存到相应的历史记录
operation = timing_data['operation']
if operation == 'wait_poses':
self.wait_poses_time = elapsed
self.wait_poses_times.append(elapsed)
if len(self.wait_poses_times) > self.pipeline_history_size:
self.wait_poses_times.pop(0)
elif operation == 'left_render':
self.left_render_time = elapsed
elif operation == 'right_render':
self.right_render_time = elapsed
elif operation == 'submit':
self.submit_time = elapsed
self.submit_times.append(elapsed)
if len(self.submit_times) > self.pipeline_history_size:
self.submit_times.pop(0)
elif operation == 'sync_wait':
self.vr_sync_wait_time = elapsed
self.sync_wait_times.append(elapsed)
if len(self.sync_wait_times) > self.pipeline_history_size:
self.sync_wait_times.pop(0)
# 计算总渲染时间
total_render = self.left_render_time + self.right_render_time
self.render_times.append(total_render)
if len(self.render_times) > self.pipeline_history_size:
self.render_times.pop(0)
return elapsed
def _get_pipeline_stats(self):
"""获取渲染管线统计信息"""
def get_stats(times_list):
if not times_list:
return {'avg': 0.0, 'min': 0.0, 'max': 0.0}
return {
'avg': sum(times_list) / len(times_list),
'min': min(times_list),
'max': max(times_list)
}
# 计算GPU时间统计
def get_gpu_field_stats(field_name):
"""从GPU时间历史记录中提取特定字段的统计信息"""
values = []
for gpu_data in self.gpu_timing_history:
if field_name in gpu_data:
values.append(gpu_data[field_name])
return get_stats(values)
return {
'wait_poses': get_stats(self.wait_poses_times),
'render': get_stats(self.render_times),
'submit': get_stats(self.submit_times),
'sync_wait': get_stats(self.sync_wait_times),
'gpu_timing': {
'scene_render': get_gpu_field_stats('scene_render'),
'pre_submit': get_gpu_field_stats('pre_submit'),
'post_submit': get_gpu_field_stats('post_submit'),
'total_render': get_gpu_field_stats('total_render'),
'compositor_render': get_gpu_field_stats('compositor_render'),
'frame_interval': get_gpu_field_stats('frame_interval')
},
'current': {
'wait_poses': self.wait_poses_time,
'left_render': self.left_render_time,
'right_render': self.right_render_time,
'submit': self.submit_time,
'sync_wait': self.vr_sync_wait_time,
'total_render': self.left_render_time + self.right_render_time,
'gpu_scene_render': self.gpu_scene_render_ms,
'gpu_pre_submit': self.gpu_pre_submit_ms,
'gpu_post_submit': self.gpu_post_submit_ms,
'gpu_total_render': self.gpu_total_render_ms,
'gpu_compositor_render': self.gpu_compositor_render_ms,
'gpu_frame_interval': self.gpu_client_frame_interval_ms
},
'vr_info': {
'eye_resolution': self.current_eye_resolution,
'recommended_resolution': self.recommended_eye_resolution,
'display_frequency': self.vr_display_frequency,
'vsync_enabled': self.vr_vsync_enabled,
'target_frame_time_ms': self.target_frame_time_ms,
'vsync_to_photons_ms': self.vsync_to_photons_ms,
'vsync_window_ms': self.vsync_window_ms,
'async_reprojection': self.async_reprojection_enabled,
'motion_smoothing': self.motion_smoothing_enabled,
'gpu_timing_enabled': self.enable_gpu_timing,
'gpu_timing_failures': self.gpu_timing_failure_count
}
}
def test_pipeline_monitoring(self):
"""测试管线监控功能 - 用于调试和验证"""
print("🔧 正在测试VR管线监控功能...")
try:
# 测试基本信息获取
print("📊 基本信息:")
print(f" 当前分辨率: {self.current_eye_resolution}")
print(f" 显示频率: {self.vr_display_frequency} Hz")
print(f" 目标帧时间: {self.target_frame_time_ms:.2f}ms")
# Running Start模式信息
print("🎯 Running Start模式:")
print(f" 预测时间: {self.vr_manager.use_prediction_time * 1000:.1f}ms")
print(f" 模式: Valve Running Start - 帧开始时获取姿态")
# 测试管线统计
if self.enable_pipeline_monitoring:
pipeline_stats = self._get_pipeline_stats()
print("⚙️ 管线统计:")
print(f" waitGetPoses历史: {len(self.wait_poses_times)}")
print(f" 渲染历史: {len(self.render_times)}")
print(f" 提交历史: {len(self.submit_times)}")
# 测试性能报告
print("📋 生成详细性能报告:")
self._print_performance_report()
print("✅ 管线监控功能测试完成")
else:
print("⚠️ 管线监控已禁用,无法测试统计功能")
except Exception as e:
print(f"❌ 测试管线监控功能时发生错误: {e}")
import traceback
traceback.print_exc()
def set_prediction_time(self, prediction_time_ms):
"""设置预测时间仅用于update_task策略
Args:
prediction_time_ms: 预测时间单位毫秒通常8-16ms
"""
prediction_time_s = prediction_time_ms / 1000.0
if prediction_time_s < 0.005 or prediction_time_s > 0.020:
print(f"⚠️ 预测时间超出推荐范围(5-20ms): {prediction_time_ms}ms")
old_time = self.vr_manager.use_prediction_time * 1000
self.vr_manager.use_prediction_time = prediction_time_s
print(f"✓ VR预测时间已设置: {old_time:.1f}ms → {prediction_time_ms:.1f}ms")
# ========== 诊断工具方法 ==========
def _print_render_callback_diagnostics(self):
"""输出渲染回调诊断信息 - 包含优化效果分析"""
try:
print(f"🔍 DrawCallback渲染诊断:")
# 回调次数统计
left_count = self.left_render_count
right_count = self.right_render_count
print(f" 渲染次数: 左眼={left_count}, 右眼={right_count}")
if left_count == 0 and right_count == 0:
print(f" ❌ VR缓冲区未被渲染 - 这是严重问题!")
return
# 渲染次数平衡性检查
if abs(left_count - right_count) > 5:
print(f" ⚠️ 左右眼渲染次数不平衡: 差异={abs(left_count - right_count)}")
else:
print(f" ✓ 左右眼渲染次数平衡")
# 🔧 真实渲染时间分析 - 显示优化效果
left_render_time = self.left_render_time
right_render_time = self.right_render_time
total_render_time = left_render_time + right_render_time
print(f" ⏱️ 精确渲染时间测量:")
print(f" 左眼cbdata.upcall(): {left_render_time:.2f}ms")
print(f" 右眼cbdata.upcall(): {right_render_time:.2f}ms")
print(f" 总计: {total_render_time:.2f}ms")
# 渲染性能评估
if total_render_time > 16.0: # 超过60FPS时间
print(f" 🔴 渲染时间过长: {total_render_time:.1f}ms (目标<13.9ms@72Hz)")
print(f" 建议: 检查RenderPipeline优化是否生效")
elif total_render_time > 10.0:
print(f" 🟡 渲染时间偏高: {total_render_time:.1f}ms (可接受)")
else:
print(f" 🟢 渲染性能良好: {total_render_time:.1f}ms")
# 🔧 渲染管线优化状态检查
self._check_rendering_optimizations()
# OpenGL状态诊断
self._diagnose_opengl_state()
except Exception as e:
print(f" 渲染回调诊断失败: {e}")
def _check_rendering_optimizations(self):
"""检查渲染优化状态"""
try:
print(f" 🔧 渲染优化状态:")
# 检查RenderPipeline优化
if hasattr(self.vr_manager.world, 'render_pipeline') and self.vr_manager.world.render_pipeline:
print(f" RenderPipeline: 已检测并优化")
else:
print(f" RenderPipeline: 未检测到使用基础Panda3D")
# 检查GPU同步优化
if hasattr(self.vr_manager, '_smart_sync_logged'):
last_sync_frame = getattr(self.vr_manager, '_last_gpu_sync_frame', 0)
current_frame = self.frame_count
frames_since_sync = current_frame - last_sync_frame
print(f" 智能GPU同步: 已启用 (距离上次同步: {frames_since_sync}帧)")
else:
print(f" 智能GPU同步: 未初始化")
# 检查对象池状态
matrix_pool_status = self.vr_manager.get_object_pool_status()
print(f" 对象池: Mat4={matrix_pool_status['matrix_pool_size']}/{matrix_pool_status['matrix_pool_capacity']}")
# 检查垃圾回收控制
gc_status = self.get_debug_status()
if gc_status['gc_disabled']:
print(f" GC控制: 已启用手动模式 (间隔:{gc_status['manual_gc_interval']}帧)")
else:
print(f" GC控制: 自动模式")
except Exception as e:
print(f" 优化状态检查失败: {e}")
def _diagnose_opengl_state(self):
"""诊断OpenGL渲染状态"""
try:
# 检查VR缓冲区状态
if hasattr(self.vr_manager, 'vr_left_eye_buffer') and self.vr_manager.vr_left_eye_buffer:
left_gsg = self.vr_manager.vr_left_eye_buffer.getGsg()
left_valid = self.vr_manager.vr_left_eye_buffer.isValid()
print(f" 左眼缓冲区: {'有效' if left_valid else '无效'}")
if hasattr(self.vr_manager, 'vr_right_eye_buffer') and self.vr_manager.vr_right_eye_buffer:
right_gsg = self.vr_manager.vr_right_eye_buffer.getGsg()
right_valid = self.vr_manager.vr_right_eye_buffer.isValid()
print(f" 右眼缓冲区: {'有效' if right_valid else '无效'}")
# 检查纹理准备状态
if hasattr(self.vr_manager, 'textures_prepared'):
print(f" 纹理准备状态: {'已准备' if self.vr_manager.textures_prepared else '未准备'}")
# 检查纹理ID缓存
if hasattr(self.vr_manager, 'left_texture_id') and hasattr(self.vr_manager, 'right_texture_id'):
left_id = self.vr_manager.left_texture_id or 0
right_id = self.vr_manager.right_texture_id or 0
print(f" 纹理ID缓存: 左眼={left_id}, 右眼={right_id}")
if left_id == 0 or right_id == 0:
print(f" ⚠️ 检测到无效的纹理ID这可能导致提交失败")
else:
print(f" ✓ 纹理ID缓存正常")
# 检查场景渲染状态
if hasattr(self.vr_manager.world, 'render') and self.vr_manager.world.render:
render_children = len(self.vr_manager.world.render.getChildren())
print(f" 场景节点数: {render_children}")
if render_children == 0:
print(f" ⚠️ 场景为空,可能导致渲染时间异常短")
except Exception as e:
print(f" OpenGL状态诊断失败: {e}")
# ========== 调试控制方法 ==========
def enable_debug_output(self):
"""启用调试输出"""
self.debug_output_enabled = True
print("✓ VR调试输出已启用")
def disable_debug_output(self):
"""禁用调试输出"""
self.debug_output_enabled = False
print("✓ VR调试输出已禁用")
def set_debug_mode(self, mode):
"""设置调试模式
Args:
mode: 'brief''detailed'
"""
if mode in ['brief', 'detailed']:
self.debug_mode = mode
print(f"✓ VR调试模式设置为: {mode}")
else:
print("⚠️ 调试模式只能是 'brief''detailed'")
def toggle_debug_output(self):
"""切换调试输出状态"""
self.debug_output_enabled = not self.debug_output_enabled
status = "启用" if self.debug_output_enabled else "禁用"
print(f"✓ VR调试输出已{status}")
return self.debug_output_enabled
def get_debug_status(self):
"""获取调试状态"""
return {
'debug_enabled': self.debug_output_enabled,
'debug_mode': self.debug_mode,
'performance_monitoring': self.performance_monitoring,
'report_interval_frames': self.performance_report_interval,
'report_interval_seconds': self.performance_report_interval / 60, # 假设60fps
'gc_control_enabled': self.vr_manager._gc_control_enabled,
'gc_disabled': self.vr_manager._gc_disabled,
'manual_gc_interval': self.vr_manager._manual_gc_interval,
}
# ========== 配置方法 ==========
def set_performance_check_interval(self, interval):
"""设置性能检查间隔
Args:
interval: 检查间隔建议0.1-2.0之间
"""
if 0.1 <= interval <= 5.0:
self.performance_check_interval = interval
print(f"✓ 性能监控间隔设置为 {interval:.1f}")
else:
print("⚠️ 性能监控间隔应在0.1-5.0秒之间")
def set_frame_time_history_size(self, size):
"""设置帧时间历史记录大小
Args:
size: 历史记录大小帧数建议30-300之间
"""
if 10 <= size <= 1000:
self.max_frame_time_history = size
# 清理超出的历史记录
if len(self.frame_times) > size:
self.frame_times = self.frame_times[-size:]
print(f"✓ 帧时间历史记录大小设置为 {size}")
else:
print("⚠️ 帧时间历史记录大小应在10-1000帧之间")
def set_performance_report_interval(self, frames):
"""设置性能报告输出间隔
Args:
frames: 帧数间隔建议300-7200之间5秒-2分钟@60fps
"""
if 300 <= frames <= 7200:
# 修改_update_vr中的报告间隔
print(f"✓ 性能报告间隔设置为每 {frames} 帧(约 {frames/60:.1f} 秒@60fps")
# 这里可以添加一个实例变量来控制
self.performance_report_interval = frames
else:
print("⚠️ 性能报告间隔应在300-7200帧之间")
# ========== 查询方法 ==========
def get_performance_stats(self):
"""获取详细的性能统计信息"""
stats = {
'vr_fps': self.vr_fps,
'frame_count': self.frame_count,
'submit_failures': self.submit_failures,
'pose_failures': self.pose_failures,
'cpu_usage': self.cpu_usage,
'memory_usage': self.memory_usage,
'gpu_usage': self.gpu_usage,
'gpu_memory_usage': self.gpu_memory_usage,
}
# 计算帧时间统计
if self.frame_times:
stats['frame_time_avg'] = sum(self.frame_times) / len(self.frame_times)
stats['frame_time_min'] = min(self.frame_times)
stats['frame_time_max'] = max(self.frame_times)
stats['frame_time_95th'] = sorted(self.frame_times)[int(len(self.frame_times) * 0.95)]
else:
stats['frame_time_avg'] = 0
stats['frame_time_min'] = 0
stats['frame_time_max'] = 0
stats['frame_time_95th'] = 0
return stats
def get_current_performance_summary(self):
"""获取当前性能摘要(简短版本)"""
stats = self.get_performance_stats()
summary = f"VR性能: {stats['vr_fps']:.1f}fps"
if stats['frame_time_avg'] > 0:
summary += f" | 帧时间: {stats['frame_time_avg']:.1f}ms"
if self.psutil_available:
summary += f" | CPU: {stats['cpu_usage']:.0f}%"
summary += f" | 内存: {stats['memory_usage']:.0f}%"
# 显示GPU信息如果库不可用则显示提示
if self.gputil_available or self.nvidia_ml_available:
summary += f" | GPU: {stats['gpu_usage']:.0f}%"
else:
summary += " | GPU: N/A"
return summary
def get_performance_monitoring_config(self):
"""获取当前性能监控配置"""
return {
'enabled': self.performance_monitoring,
'check_interval': self.performance_check_interval,
'frame_history_size': self.max_frame_time_history,
'report_interval': self.performance_report_interval,
'psutil_available': self.psutil_available,
'gputil_available': self.gputil_available,
'nvidia_ml_available': self.nvidia_ml_available
}
def print_performance_monitoring_status(self):
"""输出性能监控状态"""
config = self.get_performance_monitoring_config()
print("🔧 ===== VR性能监控配置 =====")
print(f" 监控状态: {'✅ 启用' if config['enabled'] else '❌ 禁用'}")
print(f" 检查间隔: {config['check_interval']:.1f}")
print(f" 帧时间历史: {config['frame_history_size']}")
print(f" 报告间隔: {config['report_interval']}")
print(f" 可用库:")
print(f" psutil (CPU/内存): {'' if config['psutil_available'] else ''}")
print(f" GPUtil (GPU): {'' if config['gputil_available'] else ''}")
print(f" NVIDIA-ML (GPU): {'' if config['nvidia_ml_available'] else ''}")
print("=============================")
# ========== 控制方法 ==========
def enable_performance_monitoring(self):
"""启用性能监控"""
self.performance_monitoring = True
print("✓ VR性能监控已启用")
def disable_performance_monitoring(self):
"""禁用性能监控"""
self.performance_monitoring = False
print("✓ VR性能监控已禁用")
def force_performance_report(self):
"""强制输出一次性能报告"""
print("🔄 手动触发性能报告...")
self._print_performance_report()
def reset_performance_counters(self):
"""重置性能计数器"""
self.frame_count = 0
self.last_fps_check = 0
self.last_fps_time = 0
self.vr_fps = 0
self.submit_failures = 0
self.pose_failures = 0
self.frame_times.clear()
print("✅ 性能计数器已重置")