gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 15:17:46 26 4
gpt4 key购买 nike

我认为这是一个常见的问题,它与 OpenGL 管道以及它如何将渲染帧排队以供显示有关。
它看起来如何
video 中可以看到一个极端的例子。在安卓上。
鼠标延迟存在于最简单的桌面应用程序中。如果你运行我写的一个小应用程序,你会发现它非常明显with GLFW in C++ :

#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);
}
...或 with GLUT in C :
#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;
}
有预编译的二进制文件 here太(Linux x64)。
这篇文章底部的第 1 和第 3 个 gif 是上述 GLFW 应用程序的截屏视频。
我认为是什么
这里的问题必须是显示延迟,即应用程序渲染帧和监视器点亮像素之间的时间。目标是最小化它。
为什么我认为是
鉴于垂直同步已打开并且您的渲染很简单并且在比帧周期短得多的时间内完成,此显示延迟通常为两帧。这是因为应用程序是三重缓冲的:一个缓冲区正在显示,一个是要在下一次翻转时显示的前缓冲区,一个是应用程序正在绘制的后缓冲区。一旦后台缓冲区可用,应用程序就会渲染下一帧。相反,如果应用程序在显示之前等待并在大约半帧处渲染该帧,则此延迟可能小于 8.3 毫秒,而不是 33.3-25.0 毫秒(在 60 fps 时)。
我通过 executing a sleep function every frame for 17ms 确认了这一点(略多于一帧)。这样,显示每秒左右抖动一次,但鼠标延迟明显更小,因为帧被更快地发送用于显示,因为队列“饥饿”,即没有预渲染的帧。下面的第 2 和第 4 个 gif 图像显示了这一点。如果您将此应用程序置于全屏模式,则从操作系统光标几乎察觉不到延迟。
所以问题变成了如何同步帧渲染以在特定时间(例如T-8ms)开始,相对于它在监视器上显示的时间(T)。例如,在 T 之前半帧或我们估计渲染将花费的帧数。
有没有解决这个问题的通用方法?
我发现了什么
  • 我只能在 Android here 上找到一个类似的问题,它展示了如何将两帧延迟减少半帧周期,但仅限于 Android。
  • 另一个用于桌面应用程序 here ,但解决方案是只在有鼠标事件时渲染帧。当鼠标开始移动时,这减少了第一帧或第二帧的延迟,但帧队列很快填满并且再次出现两帧延迟。

  • 我什至找不到用于查询渲染是否落后于监视器的帧消耗的 GL 函数。在前后缓冲区交换之前都不会阻塞的函数( the docs say its glFinish ,但在我的实验中,它总是比后缓冲区可用时更快地返回)。帧缓冲区(特别是 CopyTexImage2D)上的操作似乎会阻塞,直到缓冲区交换并可用于同步,但以这种迂回的方式同步可能会出现其他问题。
    任何可以在这个三缓冲队列上返回一些状态以及消耗多少状态的函数对于实现这种同步非常有帮助。
    图片
    Square Follows Mouse - GLFW
    Square Follows Mouse - GLFW - Sleep
    相同的 GIF,只是放慢了速度并修剪了:
    Slow - GLFW
    Slow - GLFW - Sleep

    最佳答案

    用 native 替换输入处理,在 glfw 中,输入识别是通过回调过程完成的,它们的性能最初低于轮询,但它们涉及到您正在经历的延迟。回调很麻烦,您想通过 WinAPI 在 Windows 上直接从系统轮询,这非常简单,在 Linux 上,它的 X11 不如 WinAPI 简单但可行。请注意,您可以决定投票的速率。
    好像你在 Linux 上工作
    How can I get the current mouse (pointer) position co-ordinates in X

    关于c++ - 最大限度地减少 GL 桌面应用程序上的鼠标输入延迟?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43821109/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com