2

我目前有一个我正在用c#(使用.NET)编写的应用程序,它要求我在用户看到屏幕上的图像时立即启动一个计时器,直到他们按下按键。

现在我意识到,考虑到显示器输入延迟和响应时间、键盘物理发送消息所需的时间、操作系统处理它等,这实际上是非常困难的。

但我正在尽我最大的努力将其减少到主要是一个恒定的错误(响应时间结果将用于将一个用户与下一个用户进行比较,因此恒定的错误并不是真正的问题)。然而令人讨厌的障碍是由显示器刷新率引起的变量,正如我在调用和完成我的 onPaint 消息时所收集的那样,这并不意味着图像实际上已被处理并从图形缓冲区发送?

不幸的是,时间限制和其他承诺实际上会限制我在 c# for windows 中继续这项任务。

所以我想知道是否可以在 OpenGL 或 DirectX 中处理所有绘图,或者如果可以在屏幕更新时仅使用 OpenGL 或 DirectX 创建事件,对我来说更好吗?

之前给我的另一个建议是关于垂直同步,如果我关闭它,图像一绘制就发送?而不是以与显示器刷新率同步的设定速率发送图像?

4

2 回答 2

4

您必须在单独的线程中渲染您的图形,以便:

  • 使用垂直同步来精确计时,以有效显示您的图像。
  • 获取用户输入的精确时间(因为用户界面与渲染循环不在同一个线程上。

初始化 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()事件触发器根据需要显示图像并启动秒表。

于 2012-06-12T14:01:30.073 回答
2

首先在您的应用程序空闲循环上启用 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 毫秒,您应该得到用户响应延迟。

于 2012-06-11T13:08:22.130 回答