MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Получить fps сторонней игры"
Answer 943375
Для мониторинга производительности DirectX в Windows существуют несколько провайдеров Event Tracing for Windows. Наиболее интересны
Microsoft-Windows-D3D9
иMicrosoft-Windows-DXGI
, которые позволяют отследить событие рендеринга кадра. Простейший способ вычисления FPS заключается в том, чтобы посчитать количество событий рендеринга кадров ("PresentStart") в интервале времени и разделить на величину этого интервала.Для того, чтобы задействовать ETW в C#, понадобится подключить пакет Microsoft.Diagnostics.Tracing.TraceEvent.
Пример кода для отображения FPS всех запущенных процессов:
using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Threading; using Microsoft.Diagnostics.Tracing.Session; namespace ConsoleApp1 { //вспомогательный класс для хранения моментов времени отрисовки кадров public class TimestampCollection { const int MAXNUM = 1000; public string Name { get; set; } List<long> timestamps = new List<long>(MAXNUM + 1); object sync = new object(); //добавление значения в коллекцию public void Add(long timestamp) { lock (sync) { timestamps.Add(timestamp); if (timestamps.Count > MAXNUM) timestamps.RemoveAt(0); } } //получение числа значений в определенном интервале времени public int QueryCount(long from, long to) { int c = 0; lock (sync) { foreach (var ts in timestamps) { if (ts >= from && ts <= to) c++; } } return c; } } class Program { //коды событий (https://github.com/GameTechDev/PresentMon/blob/40ee99f437bc1061a27a2fc16a8993ee8ce4ebb5/PresentData/PresentMonTraceConsumer.cpp) public const int EventID_D3D9PresentStart = 1; public const int EventID_DxgiPresentStart = 42; //коды провайдеров ETW public static readonly Guid DXGI_provider = Guid.Parse("{CA11C036-0102-4A2D-A6AD-F03CFED5D3C9}"); public static readonly Guid D3D9_provider = Guid.Parse("{783ACA0A-790E-4D7F-8451-AA850511C6B9}"); static TraceEventSession m_EtwSession; static Dictionary<int, TimestampCollection> frames = new Dictionary<int, TimestampCollection>(); static Stopwatch watch = null; static object sync = new object(); static void EtwThreadProc() { //запускает отслеживание событий m_EtwSession.Source.Process(); } static void OutputThreadProc() { //цикл вывода результатов в консоль while (true) { long t1, t2; long dt = 2000; Console.Clear(); Console.WriteLine(DateTime.Now.ToString() + "." + DateTime.Now.Millisecond.ToString()); Console.WriteLine(); lock (sync) { t2 = watch.ElapsedMilliseconds; t1 = t2 - dt; foreach (var x in frames.Values) { Console.Write(x.Name + ": "); //получаем количество кадров в интервале времени int count = x.QueryCount(t1, t2); //вычисляем FPS Console.WriteLine("{0} FPS", (double)count / dt * 1000.0); } } Console.WriteLine(); Console.WriteLine("Press any key to stop tracing..."); Thread.Sleep(1000); } } public static void Main(string[] argv) { //создаем сессию ETW и регистрируем провайдеры m_EtwSession = new TraceEventSession("mysess"); m_EtwSession.StopOnDispose = true; m_EtwSession.EnableProvider("Microsoft-Windows-D3D9"); m_EtwSession.EnableProvider("Microsoft-Windows-DXGI"); //подписываемся на событие m_EtwSession.Source.AllEvents += data => { //интересуют только события рендеринга кадров if (((int)data.ID == EventID_D3D9PresentStart && data.ProviderGuid == D3D9_provider) || ((int)data.ID == EventID_DxgiPresentStart && data.ProviderGuid == DXGI_provider)) { int pid = data.ProcessID; long t; lock (sync) { t = watch.ElapsedMilliseconds; //если найден новый процесс, добавляем в Dictionary if (!frames.ContainsKey(pid)) { frames[pid] = new TimestampCollection(); string name = ""; var proc = Process.GetProcessById(pid); if (proc != null) { using (proc) { name = proc.ProcessName; } } else name = pid.ToString(); frames[pid].Name = name; } //добавляем кадр в коллекцию frames[pid].Add(t); } } }; watch = new Stopwatch(); watch.Start(); Thread thETW = new Thread(EtwThreadProc); thETW.IsBackground = true; thETW.Start(); Thread thOutput = new Thread(OutputThreadProc); thOutput.IsBackground = true; thOutput.Start(); Console.ReadKey(); m_EtwSession.Dispose(); } } }
Основан на коде проекта PresentMon
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.