Есть ли какой-нибудь способ узнать, когда экран был обновлен / обновлен (возможно, OpenGL или DirectX?)

В настоящее время у меня есть приложение, которое я пишу на C# (используя.NET), которое требует от меня запуска таймера, как только пользователь увидит изображение на экране, пока он не ответит нажатием клавиши.

Теперь я понимаю, что практически это очень сложно, учитывая задержку на входе монитора и время отклика, время, которое клавиатура отнимает для физического отправки сообщения, ОС для его обработки и т. Д.

Но я стараюсь изо всех сил уменьшить его до постоянной ошибки (результаты времени отклика будут использоваться для сравнения одного пользователя с другим, поэтому постоянная ошибка на самом деле не проблема). Однако раздражающим препятствием является переменная, вызванная частотой обновления монитора, которую я получаю, когда мое сообщение onPaint вызывается и обрабатывается, не означает ли это, что изображение фактически было обработано и отправлено из графического буфера?

К сожалению, временные ограничения и другие обязательства реально ограничат меня в продолжении этой задачи в C# для Windows.

Так что мне было интересно, обрабатывал ли я все рисунки в OpenGL или DirectX, или еще лучше для меня, если можно просто использовать OpenGL или DirectX для создания события при обновлении экрана?

Еще одно предложение, данное мне ранее, касалось V-Sync. Если я отключу это, отправляется ли изображение сразу после его отрисовки? в отличие от отправки изображений с установленной скоростью, синхронизированной с частотой обновления монитора?

2 ответа

Решение

Вы должны представить свою графику в отдельном потоке, чтобы:

  • Используйте вертикальную синхронизацию, чтобы иметь точные сроки эффективного отображения вашего изображения.
  • Получите точную синхронизацию вашего пользовательского ввода (поскольку пользовательский интерфейс не находится в том же потоке, что и цикл рендеринга.

Инициализируйте Direct3D, чтобы включить VSync во время рендеринга:

// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;

device = new Device(...

Выполните рендеринг в отдельном потоке:

Thread renderThread = new Thread(RenderLoop);
renderThread.Start();

shouldDisplayImageEvent = new AutoResetEvent();

Затем используйте следующий цикл рендеринга:

void RenderLoop()
{
    while(applicationActive)
    {
          device.BeginScene();

        // Other rendering task

        if (shouldDisplayImageEvent.WaitOne(0))
        {
            // Render image
            // ...

            userResponseStopwatch = new Stopwatch();
            userResponseStopwatch.Start();
        }

        device.EndScene();

        device.Present();
    }
}

Затем обработайте пользовательский ввод:

void OnUserInput(object sender, EventArgs e)
{
    if (userResponseStopwatch != null)
    {
        userResponseStopwatch.Stop();

        float userResponseDuration = userResponseStopwatch.ElapsedMillisecond - 1000 / device.DisplayMode.RefreshRate - displayDeviceDelayConstant;
        userResponseStopwatch = null;
    }
}

Теперь вы используете триггер события shouldDisplayImageEvent.Set() для отображения изображения по мере необходимости и запуска секундомера.

Сначала включите VSync в цикле ожидания вашего приложения:

// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;

device = new Device(...

Application.Idle += new EventHandler(OnApplicationIdle);

// More on this here : http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx
internal void OnApplicationIdle(object sender, EventArgs e)
{
    Msg msg = new Msg();
    while (true)
    {
        if (PeekMessage(out msg, IntPtr.Zero, 0, 0, 0))
            break;
    }

    // Clearing render
    // ...

    if (displayImage)
    {
        // Render image
        // ...

        renderTime = DateTime.now();
    }
    device.Present();
}

Если функция vsync включена, функциональный блок device.Present будет отображаться до следующей синхронизации кадров, поэтому, если вы вычислите время между renderTime и временем ввода пользователя и удалите задержку устройства отображения + 16,67 мс, вы должны получить задержку ответа пользователя.

Другие вопросы по тегам