Есть ли какой-нибудь способ узнать, когда экран был обновлен / обновлен (возможно, 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 мс, вы должны получить задержку ответа пользователя.