【发布时间】:2022-12-04 21:20:51
【问题描述】:
我编写了一个程序,在计算着色器上进行一些计算,然后显示返回的数据。这完美地工作,除了在着色器运行时程序执行被阻止(见下面的代码)并且根据参数,这可能需要一段时间:
void CalculateSomething(GLfloat* Result)
{
// load some uniform variables
glDispatchCompute(X, Y, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
GLfloat* mapped = (GLfloat*)(glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY));
memcpy(Result, mapped, sizeof(GLfloat) * X * Y);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
void main
{
// Initialization stuff
// ...
while (glfwWindowShouldClose(Window) == 0)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glfwPollEvents();
glfwSwapInterval(2); // Doesn't matter what I put here
CalculatateSomething(Result);
Render(Result);
glfwSwapBuffers(Window.WindowHandle);
}
}
为了在计算着色器计算时保持主循环运行,我将 CalculateSomething 更改为如下所示:
void CalculateSomething(GLfloat* Result)
{
// load some uniform variables
glDispatchCompute(X, Y, 1);
GPU_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
bool GPU_busy()
{
GLint GPU_status;
if (GPU_sync == NULL)
return false;
else
{
glGetSynciv(GPU_sync, GL_SYNC_STATUS, 1, nullptr, &GPU_status);
return GPU_status == GL_UNSIGNALED;
}
}
这两个函数是一个类的一部分,如果我必须在这里发布所有内容,它会变得有点混乱和复杂(如果需要更多代码,请告诉我)。因此,当类被告知进行计算时,每个循环都会首先检查 GPU 是否忙碌。如果完成,结果将被复制到 CPU 内存(或开始计算),否则它返回到 main 而不做任何其他事情。无论如何,这种方法之所以有效,是因为它会产生正确的结果。但是我的主循环仍然被阻塞。
做一些计时显示CalculateSomething、Render(以及其他一切)运行得很快(正如我所期望的那样)。但现在 glfwSwapBuffers 需要 >3000 毫秒(取决于计算着色器的计算时间)。
在计算着色器运行时是否应该可以切换缓冲区?渲染结果似乎工作正常且没有延迟(只要计算着色器尚未完成,就应该渲染旧结果)。或者我在这里遗漏了什么(排队的 OpenGL 调用在 glfwSwapBuffers 做某事之前得到处理?)?
【问题讨论】:
-
看起来您正在使用 glfwSwapInterval() 来控制应用程序的帧速率。但是,此函数仅影响缓冲区交换的速率,而不影响应用程序运行的速率。换句话说,无论您传递给 glfwSwapInterval() 的值如何,您的应用程序都将继续尽可能快地运行。
-
要解决此问题,您可以使用不同的机制来控制应用程序的帧速率。一种方法是使用计时器测量自上一帧以来经过的时间,然后使用此信息来决定是否渲染新帧。