我目前有一个用c#编写的应用程序(使用.NET),要求我在用户在屏幕上看到图像时立即启动计时器,直到用按键响应为止。
现在我意识到,考虑到监视器输入滞后和响应时间,键盘物理发送消息所需的时间,处理它的操作系统等,实际上这非常困难。
但我正在尽我所能将其降低到大部分是一个恒定的错误(响应时间结果将用于比较一个用户与下一个用户,因此常量错误不是真正的问题)。然而,令人烦恼的障碍是由监视器刷新率引起的变量,正如我在调用和完成onPaint消息时收集的那样,这并不意味着图像已经实际处理并从图形缓冲区发送?
不幸的是,时间限制和其他承诺实际上会限制我在c#for Windows中继续执行此任务。
所以我想知道的是,如果可以在屏幕更新时使用OpenGL或DirectX创建事件,是否可以处理OpenGL或DirectX中的所有绘图或者更好的处理?
之前给我的另一个建议是关于V-Sync,如果我将其关闭是一旦它被绘制后立即发送的图像?而不是以与显示器刷新率同步的设定速率发送图像?
答案 0 :(得分:3)
您必须在单独的主题中渲染图形,以便:
初始化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()事件触发器根据需要显示图像并启动秒表。
答案 1 :(得分:1)
首先在应用程序空闲循环上启用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.67ms你应该得到你的用户响应延迟。