All notable changes to the InfoPanel Presentmon Plugin are documented here.
This release represents a complete rewrite of the plugin with a modern, maintainable architecture and significantly improved performance.
- Service-Based Architecture: Introduced proper separation of concerns with dedicated service classes
PresentMonService: Handles P/Invoke calls, session management, and frame processingFullscreenDetectionService: Win32 API integration for detecting fullscreen applications
- Data Models: Created structured models for better data management
FrameMetrics: Processed performance data with all FPS metricsMonitoringState: Current monitoring status and process informationFrameProcessingConfig: Configurable processing parameters
- P/Invoke Integration: Direct native DLL integration with
PresentMonAPI2.dllPresentMonApi: Centralized P/Invoke declarations with proper error handling- Native
PMFramestruct processing for optimal performance
- Event-Driven Updates: Real-time sensor updates via service events instead of polling
- Background Processing: Non-blocking frame data processing with async patterns throughout
- BREAKING: Project renamed from
InfoPanel.IPFPStoInfoPanel.Presentmonfor clarity - BREAKING: Completely replaced EXE-based PresentMon integration with direct DLL calls
- Performance: Eliminated process spawning, CSV parsing, and inter-process communication overhead
- Architecture: Migrated from single 1100+ line monolith to clean service-based architecture
- Data Flow: Switched from polling-based updates to event-driven real-time updates
- Resource Management: Improved memory efficiency with direct native frame data access
- Error Handling: Enhanced error handling with proper P/Invoke result codes (
PM_RESULT)
- EXE Dependencies: No longer requires
PresentMon-2.3.0-x64.exeorPresentMonService.exe - Windows Service Management: Eliminated complex service creation/management code
- CSV Processing: Removed string parsing overhead in favor of native struct processing
- Process Management: Removed complex process orchestration and cleanup logic
- Reliability: Eliminated process-related crashes and hanging issues
- Performance: Reduced CPU overhead and memory usage significantly
- Maintainability: Clear separation of concerns makes debugging and testing easier
- Thread Safety: Proper async/await patterns throughout with cancellation support
- Framework: Targets .NET 8.0-windows with x64 architecture
- Dependencies: Only requires
PresentMonAPI2.dll(bundled) andVanara.PInvoke.*packages - Compatibility: Maintains identical sensor interface for seamless InfoPanel integration
- Build Output: Simplified to
InfoPanel.Presentmon-v2.0.0.zipwith clean folder structure
- Users upgrading from v1.x will need to replace the entire plugin folder
- All existing functionality is preserved but with significantly improved performance
- Configuration and sensor names remain unchanged for compatibility
- New
PluginTextsensor (windowtitle) to display the currently captured window title (e.g., "Arma Reforger") or "Nothing to capture" when idle.
- Corrected 1% and 0.1% low FPS calculations in
ProcessOutputLineto use the worst frames (highest frame times) by sorting frame times in descending order and selecting the appropriate percentiles (99th and 99.9th). Previously, the best frames were incorrectly used, leading to inflated low FPS values (e.g., 176 FPS instead of < average).
- Increased
MaxFrameSamplesfrom default (assumed 100) to 1000 for finer granularity in frame time sampling, providing a rolling window of ~8-10 seconds at typical FPS rates (100-120 FPS). This improves the accuracy of average, 1%, and 0.1% low FPS metrics over a longer period.
- Verified fix with logs, confirming 1% low (e.g., 86.74 FPS → 10.09 FPS) and 0.1% low (e.g., 82.60 FPS → 8.36 FPS) now correctly reflect performance dips below the average FPS (~128 FPS → 122 FPS).
- Improved: fullscreen detection reliability across multi-monitor setups
- Enhanced: Enhanced PID transition handling for seamless game restarts
- Optimized: performance metric averaging for smoother output
- Enhanced: Added robust cleanup on game exit (stops PresentMon, resets sensors)
- Fixed: minor logging truncation issues
- Enhanced: Added new sensors: GpuTime, GpuBusy, GpuWait, and GpuUtilization
- Enhanced: Improved PresentMon integration for real-time data capture
- Enhanced: Refined CPU metrics with CpuBusy and CpuWait sensors
- Improved: Replaced synchronous
process.WaitForExit(timeout)with asynchronousWaitForExitAsync(cancellationToken)andTask.WhenAnyfor timeouts inExecuteCommandAsyncandStopCaptureAsync. - Optimized: Updated
ProcessExistsandGetProcessNameto useProcess.GetProcessByIdwith exception handling instead of iterating all processes, improving performance. - Refactored: Consolidated cleanup logic in
Disposeto call a unifiedCleanupAsyncmethod, streamlining capture stop, service shutdown, and ETW session clearing. - Enhanced: Added try-catch blocks to output and error reading tasks in
StartCaptureAsyncfor better exception handling during asynchronous stream reads. - Tweaked: Simplified fullscreen detection in
GetActiveFullscreenProcessIdwith early exits and additional logging to reduce unnecessary API calls. - Fixed: Resolved
CS8625warnings inWaitForExitAsyncby usingTaskCompletionSource<bool>instead of passingnulltoTaskCompletionSource<object>.
- Fixed: Removed hardcoded game check, making the plugin agnostic to specific apps.
- Replaced with generic handling of access-denied cases in
IsReShadeActive, assuming no ReShade interference unlessdxgi.dllis confirmed.
- Replaced with generic handling of access-denied cases in
- Fixed: Resolved
CS8600warning by declaringprocessNameasstring?inStartCaptureAsync.
- Fixed: Bypassed ReShade check for anti-cheat protected games to avoid
Win32Exceptiondue to access denial. AddedGetProcessNameto safely identify processes and skip unnecessary checks. - Restored: Functionality from v1.2.1 with stability fixes.
- Fixed
StartAsyncto prevent task completion races and added PID logging for debugging. - Simplified
StartCaptureAsyncto ensure reliable output capture and added error logging for PresentMon diagnostics. - Retained robust fullscreen detection and service management from v1.2.1.
- Avoided LINQ in
ProcessExiststo prevent disposal-related crashes.
- Fixed
- Improved: Asynchronous optimization phase 1, fixed race condition in process monitoring.
- Fullscreen/Borderless Detection: Implemented comprehensive window enumeration using window styles (
WS_CAPTION,WS_THICKFRAME) and client area matching (98%+ monitor coverage) to detect fullscreen and borderless applications universally. - PresentMon Integration: Added robust service management (
InfoPanelPresentMonService) with start/stop functionality and ETW session cleanup vialogman.exe. - FPS and Frame Time Monitoring: Added 5-frame window averaging for smooth FPS and frame time output using PresentMon’s CSV data.
- ReShade Detection: Included detection of
dxgi.dllin process modules, with a fallback assumption of ReShade presence on access denial for safety with anti-cheat systems. - Anti-Cheat Safety: Minimized module enumeration to reduce interference, with checks limited to
IsReShadeActive.
- Exception Noise Reduction:
- Replaced
Process.GetProcessByIdwithProcess.GetProcessesinProcessExiststo eliminateSystem.ArgumentExceptionin the debugger when games exit. - Simplified
IsReShadeActiveto checkHasExitedfirst, reducing unnecessarySystem.ComponentModel.Win32Exceptionoccurrences fromproc.Modules. - Suppressed
ArgumentExceptionlogging inProcessExistsfor expected game exits, improving log cleanliness.
- Replaced
- Type Safety: Fixed type mismatch warnings (CS1503) by using
Vanara.PInvoke.RECTandVanara.PInvoke.POINTstructs for window geometry calculations. - Cleanup Logic: Enhanced cleanup with a 10-second timeout check in
StartCaptureAsyncand forced termination of lingering PresentMon processes inStopCaptureandDispose.
- Resolved false positives in fullscreen detection by filtering out system UI windows (e.g.,
explorer,textinputhost) and small/invalid windows (client area < 1000 pixels or off-monitor). - Fixed "Access is denied" errors in module checking by gracefully handling exceptions in
CanAccessModules. - Corrected cleanup issues ensuring PresentMon and its service stop reliably, including ETW session termination.
- A
System.ComponentModel.Win32Exception("Access is denied") may still appear in the debugger whenIsReShadeActivechecksproc.Modulesfor games with anti-cheat protection. This is caught and handled, affecting only debug output, not functionality.
- This release consolidates all prior development efforts into a stable version, reverting from an unstable v1.2.6 attempt that introduced
OpenProcessfor module access checking, which caused cascading exceptions (Win32Exception,InvalidOperationException) and broke PresentMon startup.
-
Added:
- Detailed comments throughout
IPFpsPlugin.csfor code clarity.
- Detailed comments throughout
-
Changed:
- Consolidated cleanup logic into
StartCaptureAsync’s PID monitoring loop.- Removed redundant cleanup from
StartMonitoringLoopAsync. - Ensures single-point shutdown when the target PID (e.g.,
20332) exits.
- Removed redundant cleanup from
- Updated
ProcessExiststo includeproc != null && !proc.HasExitedfor extra reliability. - Bumped version to
1.1.0in constructor and documentation.
- Consolidated cleanup logic into
-
Fixed:
- Resolved issue where PresentMon lingered after the target app closed.
- Now reliably stops PresentMon and service.
- Resolved issue where PresentMon lingered after the target app closed.
-
Notes:
-
Left
System.ArgumentExceptionin logs as a debug artifact (e.g., before"PID 20332 no longer exists."). -
Harmless and only visible in debug mode; no functional impact.
-
Purpose:
-
Finalized a production-ready plugin with stable FPS tracking and no resource leaks.
-
Added:
-
proc != null && !proc.HasExitedcheck inProcessExistsfor robustness. -
Enhanced logging in
GetActiveFullscreenProcessIdwith window/monitor rects and fullscreen state. -
Changed:
-
Moved cleanup trigger to
StartCaptureAsync’s PID monitoring loop. -
Checks
ProcessExistsevery second, stops PresentMon if the target PID is gone. -
Simplified
StartMonitoringLoopAsyncto only detect new fullscreen apps. -
Fixed:
-
Stalled cleanup when Arma exited (previously no
"Fullscreen app exited"log). -
Now detects via
ProcessExistsand triggers full shutdown. -
Purpose:
-
Addressed intermittent cleanup failures, improved debug visibility (e.g.,
Foreground PID: 20332, Window rect: {0,0,2560,1440}, Fullscreen: True).
-
Added:
-
StartPresentMonServiceandStopPresentMonServicefor ETW session management. -
Installs and starts
InfoPanelPresentMonServicewithsc.exe. -
--terminate_on_proc_exitflag to PresentMon arguments. -
ClearETWSessionsto remove lingering PresentMon ETW sessions on startup. -
Changed:
-
Updated PresentMon launch to use stdout redirection for FPS data.
-
Implemented 5-frame averaging in
ProcessOutputLinefor smoother FPS output. -
Fixed:
-
PresentMon not terminating when the target app closed.
-
Added explicit
Kill(true)inStopCapturewith 5s timeout. -
ETW session leaks (e.g.,
PresentMon_15a132264c0649a59270077c6dd9a2bb) on shutdown. -
Purpose:
-
Established a working plugin for DXGI apps (e.g., game), capturing ~140-175 FPS with clean startup/shutdown.
-
Added:
-
Basic fullscreen detection with
GetActiveFullscreenProcessIdusingUser32.GetForegroundWindow. -
Initial PresentMon integration with hardcoded PID testing.
-
FPS parsing from PresentMon CSV output (
MsBetweenPresentscolumn). -
Changed:
-
Iterated on cleanup logic, initially using
Disposeonly. -
Experimented with monitoring loops and service-less ETW handling.
-
Fixed:
-
Early issues with PresentMon not starting (missing executable path).
-
Incorrect FPS calculations (no averaging).
-
Purpose:
-
Proof of concept to integrate PresentMon with InfoPanel.