繁体   English   中英

最大限度地减少 GL 桌面应用程序上的鼠标输入延迟?

[英]Minimize mouse input lag on GL desktop app?

我认为这是一个常见问题,它与 OpenGL 管道以及它如何将渲染帧排队以供显示。

看起来如何

在 Android 上的这个视频中可以看到一个极端的例子。

最简单的桌面应用程序中存在鼠标延迟。 如果您运行我在 C++ 中用 GLFW编写的小应用程序之一,您会发现它真的很明显:

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <stdlib.h>
#include <stdio.h>

const float box_size = 20;

static const struct
{
    float x, y;
} vertices[4] =
{
    { -box_size, -box_size},
    {  box_size, -box_size},
    {  box_size,  box_size},
    { -box_size,  box_size}
};

static const char* vertex_shader_text =
"#version 110\n"
"attribute vec2 vPos;\n"
"varying vec3 color;\n"
"uniform vec2 vMouse;\n"
"uniform vec2 vWindow;\n"
"void main()\n"
"{\n"
"    gl_Position = vec4(vPos/vWindow+vMouse, 0.0, 1.0);\n"
"    color = vec3(1.0, 1.0, 0.);\n"
"}\n";

static const char* fragment_shader_text =
"#version 110\n"
"varying vec3 color;\n"
"void main()\n"
"{\n"
"    gl_FragColor = vec4(color, 1.0);\n"
"}\n";

static void error_callback(int error, const char* description)
{
    fprintf(stderr, "Error: %s\n", description);
}

int main(void)
{
    GLFWwindow* window;
    GLuint vertex_buffer, vertex_shader, fragment_shader, program;
    GLint mouse_location, vpos_location, window_location;

    glfwSetErrorCallback(error_callback);

    if (!glfwInit())
        exit(EXIT_FAILURE);

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);

    window = glfwCreateWindow(500, 500, "Square Follows Mouse - GLFW", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(window);

    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        /* Problem: glewInit failed, something is seriously wrong. */
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    glfwSwapInterval(1);

    // NOTE: OpenGL error checks have been omitted for brevity

    glGenBuffers(1, &vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
    glCompileShader(vertex_shader);

    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
    glCompileShader(fragment_shader);

    program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);

    vpos_location = glGetAttribLocation(program, "vPos");
    mouse_location = glGetUniformLocation(program, "vMouse");
    window_location = glGetUniformLocation(program, "vWindow");

    glEnableVertexAttribArray(vpos_location);
    glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
                          sizeof(vertices[0]), (void*) 0);

    while (!glfwWindowShouldClose(window))
    {
        float ratio;
        int width, height;

        glfwGetFramebufferSize(window, &width, &height);
        ratio = width / (float) height;

        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(program);

        double mouse_x, mouse_y;
        glfwGetCursorPos(window, &mouse_x, &mouse_y);
        glUniform2f(mouse_location, mouse_x/width*2-1, -mouse_y/height*2+1);
        glUniform2f(window_location, (float)width, (float)height);

        glDrawArrays(GL_POLYGON, 0, 4);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwDestroyWindow(window);

    glfwTerminate();
    exit(EXIT_SUCCESS);
}

...或在 C 中使用 GLUT

#include <GL/glut.h>

int window_w, window_h = 0;
float mouse_x, mouse_y = 0.0;
float box_size = 0.02;

void display(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBegin(GL_POLYGON);
  glVertex3f(mouse_x+box_size, mouse_y+box_size, 0.0);
  glVertex3f(mouse_x-box_size, mouse_y+box_size, 0.0);
  glVertex3f(mouse_x-box_size, mouse_y-box_size, 0.0);
  glVertex3f(mouse_x+box_size, mouse_y-box_size, 0.0);
  glEnd();

  glutSwapBuffers();
}

void motion(int x, int y)
{
  mouse_x = (float)x/window_w - 0.5;
  mouse_y = -(float)y/window_h + 0.5;
  glutPostRedisplay();
}

void reshape(int w, int h)
{
  window_w = w;
  window_h = h;
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(-.5, .5, -.5, .5);
}

int main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(500, 500);
  glutCreateWindow("Square Follows Mouse - GLUT");
  glutPassiveMotionFunc(motion);
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutMainLoop();
  return 0;
}

这里也有预编译的二进制文件(Linux x64)。

这篇文章底部的第一个和第三个 gif 是上述 GLFW 应用程序的截屏视频。

我认为是什么

这里的问题一定是显示延迟,即应用程序渲染帧和显示器点亮像素之间的时间。 目标是将其最小化。

为什么我认为是

鉴于垂直同步已打开并且您的渲染很简单并且在比帧周期短得多的时间内完成,此显示延迟通常为两帧。 这是因为该应用程序是三重缓冲的:一个缓冲区正在显示,一个是要在下一次翻转时显示的前缓冲区,一个是应用程序正在绘制的后缓冲区。 一旦后台缓冲区可用,应用程序就会渲染下一帧。 相反,如果应用程序在显示帧之前等待并渲染帧大约半帧,则此延迟可能小于 8.3 毫秒,而不是 33.3-25.0 毫秒(60 帧/秒)。

我通过每帧执行一个睡眠函数 17 毫秒(比一帧多一点点) 确认这一点。 通过这种方式,显示每秒左右抖动一次,但鼠标延迟明显更小,因为帧发送显示更快,因为队列“饥饿”,即没有预渲染的帧。 下面的第二个和第四个 gif 显示了这一点。 如果您将此应用程序置于全屏模式,则从操作系统光标几乎察觉不到延迟。

所以问题就变成了如何同步帧渲染以在相对于它在监视器上显示的时间(例如 T-8ms)开始(例如 T-8ms)。 例如,T 前半帧或我们估计渲染所需的时间。

有没有通用的方法来解决这个问题?

我发现了什么

  • 我只能在 Android 上找到一个类似的问题here ,它展示了如何将两帧延迟缩短半帧周期,但仅限于 Android。
  • 另一个适用于此处的桌面应用程序,但解决方案是仅在有鼠标事件时才渲染帧。 当鼠标开始移动时,这减少了第一帧或两帧的延迟,但帧队列很快填满,两帧延迟再次出现。

我什至找不到用于查询渲染是否落后于监视器的帧消耗的 GL 函数。 在前后缓冲区交换之前都不会阻塞的函数( 文档说它的 glFinish ,但在我的实验中,它总是比后缓冲区可用时更快地返回)。 帧缓冲区上的操作(特别是 CopyTexImage2D)似乎会阻塞,直到缓冲区交换并可以用于同步,但是以这种迂回的方式同步可能会出现其他问题。

任何可以在这个三重缓冲队列上返回一些状态以及它消耗了多少的函数对于实现这种同步非常有帮助。

图片

方形跟随鼠标 - GLFW

方形跟随鼠标 - GLFW - 睡眠

相同的 gif,只是放慢了速度并进行了修剪:

慢 - GLFW

慢 - GLFW - 睡眠

用原生的东西替换输入处理,在 glfw 输入识别是通过回调过程完成的,它们最初的性能比轮询低,但它们涉及到你正在经历的延迟。 回调很笨拙,您想通过 WinAPI 直接从 Windows 上的系统进行轮询,这非常简单,而在 Linux 上,它的 X11 不像 WinAPI 那样简单但可行。 请注意,您可以决定投票率。

看起来您正在使用 Linux 如何获取当前鼠标(指针)在 X 中的位置坐标

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM