Перерисовка .NET при 60 FPS? - PullRequest
       10

Перерисовка .NET при 60 FPS?

6 голосов
/ 14 января 2010

У меня есть несколько анимаций, запущенных в контекстном окне OpenGL, поэтому мне нужно постоянно перерисовывать их. Поэтому я придумал следующий код:

    void InitializeRedrawTimer()
    {
        var timer = new Timer();
        timer.Interval = 1000 / 60;
        timer.Tick += new EventHandler(timer_Tick);
        timer.Start();
    }

    void timer_Tick(object sender, EventArgs e)
    {
        glWin.Draw();
    }

Это дает мне только 40 FPS. Однако, если я установлю интервал в 1 мс, я смогу достичь 60. Так куда же делись остальные 20 мс? Это просто из-за плохой точности таймера или как? Что если бы я хотел, чтобы моя программа работала как можно быстрее, есть ли способ непрерывного вызова функции draw?

Ответы [ 4 ]

7 голосов
/ 14 января 2010

Вы можете попробовать реализовать игровой цикл.

http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx

Базовый цикл (слегка измененный по сравнению с его оригинальной версией и версией в новом SDK для удобства чтения):

public void MainLoop()
{
        // Hook the application’s idle event
        System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);
        System.Windows.Forms.Application.Run(myForm);
}    

private void OnApplicationIdle(object sender, EventArgs e)
{
    while (AppStillIdle)
    {
         // Render a frame during idle time (no messages are waiting)
         UpdateEnvironment();
         Render3DEnvironment();
    }
}

private bool AppStillIdle
{
     get
    {
        NativeMethods.Message msg;
        return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
     }
}

//And the declarations for those two native methods members:        
[StructLayout(LayoutKind.Sequential)]
public struct Message
{
    public IntPtr hWnd;
    public WindowMessage msg;
    public IntPtr wParam;
    public IntPtr lParam;
    public uint time;
    public System.Drawing.Point p;
}

[System.Security.SuppressUnmanagedCodeSecurity] // We won’t use this maliciously
[DllImport(“User32.dll”, CharSet=CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

Простой, элегантный, эффективный. Никаких дополнительных распределений, никаких дополнительных коллекций, это просто работает. Событие Idle срабатывает, когда в очереди нет сообщений, а затем обработчик продолжает непрерывно циклически повторяться, пока сообщение не появится, и в этом случае оно останавливается. Как только все сообщения обработано, событие простоя запускается снова, и процесс начинается заново.

Эта ссылка описывает ссылку, которая использует событие Idle приложения. Это может быть полезно. Вы можете просто выполнить небольшой тест времени или поспать, чтобы замедлить его до нужного вам fps. Попробуйте использовать класс System.Diagnostics.StopWatch для наиболее точного таймера.

Надеюсь, это немного поможет.

3 голосов
/ 14 января 2010

В зависимости от того, что вы делаете , взгляните на WPF и его поддержку анимации, это может быть лучшим решением, чем написание ваших собственных анимаций в WinForms.

Также рассмотрите возможность использования DirectX, так как он теперь имеет оболочку .NET и очень быстрый, однако труднее использовать WPF или WinForms.

Чтобы непрерывно вызывать функцию рисования:

  • сделать рисунок методом перекраски
  • Добавить в конце метод перерисовки сделать BeginInvoke
  • В делегате, который вы передали BeginInvoke, аннулируйте элемент управления, который вы рисуете

Однако вашим пользователям может не понравиться, что вы убиваете их компьютер!

Application.Idle - это еще один хороший вариант, однако мне нравится, что окна Windows замедляют скорость отправки сообщений WmPaint при необходимости.

2 голосов
/ 14 января 2010

Таймер Windows Forms ограничен по разрешению стандартным разрешением системных часов вашего компьютера. Это варьируется от компьютера к компьютеру, но обычно составляет от 1 мс до 20 мс.

В WinForms, когда вы запрашиваете интервал таймера в коде, Windows гарантирует, что ваш таймер будет вызываться не чаще , чем указанное количество миллисекунд. Это не гарантирует, что ваш таймер будет вызываться именно в указанные вами миллисекунды.

Это потому, что Windows является ОС с разделением времени, а не в режиме реального времени. Другие процессы и потоки будут запланированы для одновременной работы, и поэтому ваш поток будет вынужден регулярно ожидать использования процессора. Поэтому вам не следует писать код таймера WinForms, который использует точные интервалы или задержки в миллисекундах.

В вашей ситуации установка интервала в 1 мс на самом деле просто говорит Windows, чтобы запускать этот таймер так часто, как это возможно. Как вы видели, это определенно гораздо реже, чем 1 мс (60 кадр / с = около 17 мс).

Если вам нужен более точный таймер с более высоким разрешением, Windows API предоставляет таймеры с высоким разрешением / производительностью, если ваше оборудование его поддерживает, см. Как использовать таймер с высоким разрешением . Недостатком этого является то, что использование ЦП вашего приложения будет увеличено для поддержки более высокой производительности.

В целом, я думаю, вам лучше реализовать аппаратно-системный игровой цикл. Таким образом, воспринимаемая анимация вашей сцены выглядит постоянной независимо от фактической достигнутой частоты кадров. Для получения дополнительной информации эти статьи дают хороший фон.

0 голосов
/ 03 марта 2017

Я заметил в приведенном примере кода в строке:

timer.Interval = 1000 / 60;

Используется операция целочисленного деления, поэтому результат будет округлен до целого числа и будет точно не 16.6666мс, а 17мс. Следовательно, таймер делает больше тиков, чем обновление экрана.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...