有没有想知道什么时候屏幕更新/刷新(OpenGL或DirectX可能?)

时间:2012-06-10 21:33:39

标签: c# opengl directx vsync

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

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

但我正在尽我所能将其降低到大部分是一个恒定的错误(响应时间结果将用于比较一个用户与下一个用户,因此常量错误不是真正的问题)。然而,令人烦恼的障碍是由监视器刷新率引起的变量,正如我在调用和完成onPaint消息时收集的那样,这并不意味着图像已经实际处理并从图形缓冲区发送?

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

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

之前给我的另一个建议是关于V-Sync,如果我将其关闭是一旦它被绘制后立即发送的图像?而不是以与显示器刷新率同步的设定速率发送图像?

2 个答案:

答案 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你应该得到你的用户响应延迟。