如何在OpenGL中启用垂直同步?

45

如何启用垂直同步?

glEnable(GL_VSYNC)这样的简单操作吗?(虽然在glEnable可接受的选项中没有类似GL_VSYNC的选项)。

还是说在 OpenGL 中没有标准的方法来实现垂直同步?

4个回答

48

在Windows操作系统中,有一个OpenGL扩展方法wglSwapIntervalEXT。根据b2b3的帖子http://www.gamedev.net/community/forums/topic.asp?topic_id=360862:

  

如果您正在Windows上工作,则必须使用扩展来使用wglSwapIntervalExt函数。 它在wglext.h中定义。 您还需要下载glext.h文件。   在wglext文件中,声明了所有与Windows特定扩展相关的入口点。 所有这样的函数都以前缀wgl开头。   要获取有关所有已发布扩展的更多信息,可以查看OpenGL扩展注册表。

     

wglSwapIntervalEXT来自WGL_EXT_swap_control扩展。它允许您指定每个缓冲区交换之前的最小帧数。通常用于垂直同步(如果将交换间隔设置为1)。可以在此处找到有关整个扩展的更多信息。   在使用此功能之前,您需要查询您的显卡是否支持WGL_EXT_swap_control, 并使用wglGetProcAddress函数获得该函数的指针。

     

要测试给定扩展的支持情况,可以使用类似以下函数的函数:

#include <windows.h>
#include "wglext.h"

bool WGLExtensionSupported(const char *extension_name)
{
    // this is pointer to function which returns pointer to string with list of all wgl extensions
    PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL;

    // determine pointer to wglGetExtensionsStringEXT function
    _wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) wglGetProcAddress("wglGetExtensionsStringEXT");

    if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL)
    {
        // string was not found
        return false;
    }

    // extension is supported
    return true;
}

要初始化函数指针,您需要:

PFNWGLSWAPINTERVALEXTPROC       wglSwapIntervalEXT = NULL;
PFNWGLGETSWAPINTERVALEXTPROC    wglGetSwapIntervalEXT = NULL;

if (WGLExtensionSupported("WGL_EXT_swap_control"))
{
    // Extension is supported, init pointers.
    wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");

    // this is another function from WGL_EXT_swap_control extension
    wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT");
}
然后您可以像使用其他函数指针一样使用这些指针。要启用vync,您可以调用wglSwapIntervalEXT(1),要禁用它,您调用wglSwapIntervalEXT(0)
要获取当前的交换间隔,您需要调用wglGetSwapIntervalEXT()

3
在您的第一个代码片段中,if (strstr(_wglGetExtensionsString(), extension_name) == NULL) 应更改为 if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL) -- 谢谢,这对我很有帮助!!! - HoboBen
4
我使用了wglGetProcAddress而不是LogGetProcAddress。 - HoboBen
4
这不是OpenGL的扩展,而是WGL的扩展(用于OpenGL的Microsoft Windows窗口系统API)。缓冲区交换本质上是一个特定于窗口系统的操作。就GL而言,它只是将内容绘制到前/后左/右缓冲区或某个任意的FBO中。窗口系统是唯一具备足够了解底层主机系统的知识,可以将绘制的缓冲区与某个事件(在这种情况下为显示器的垂直回扫)同步呈现的东西。 - Andon M. Coleman

8

WGL案例在eugensk00的答案中有描述。

对于CGL(MacOSX),请参见这个SO问题的答案

对于EGL,有eglSwapInterval()函数,但显然(见此处此处)它不能保证无撕裂的结果 - 只等待给定的时间段(也许只是由于损坏的驱动程序)。

对于GLX(Linux with X11等),至少有3个类似的扩展,具有不同程度的功能。OpenGL wiki目前只列出了一个扩展名,不支持Mesa <= 10.5.9(可能更高)。以下是从最完整功能的扩展名(在OpenGL wiki中列出)到最少的列表:

  1. GLX_EXT_swap_control

    • 设置每个绘制区域的交换间隔: glXSwapIntervalEXT(dpy, drawable, interval)

    • 获取当前交换间隔: glXQueryDrawable(dpy, drawable, GLX_SWAP_INTERVAL_EXT, &interval)

    • 获取最大交换间隔: glXQueryDrawable(dpy, drawable, GLX_MAX_SWAP_INTERVAL_EXT, &maxInterval)

    • 禁用垂直同步:将interval设置为0

  2. GLX_MESA_swap_control

    • 设置每个上下文的交换间隔: glXSwapIntervalMESA(interval)

    • 获取当前交换间隔: glXGetSwapIntervalMESA()

    • 获取最大交换间隔: 不支持

    • 禁用垂直同步:将interval设置为0

  3. GLX_SGI_swap_control

    • 设置交换间隔: glXSwapIntervalSGI(interval).

    • 获取当前交换间隔: 不支持

    • 获取最大交换间隔: 不支持

    • 禁用垂直同步:不支持interval==0是错误的)

有关自适应垂直同步,请参见OpenGL wiki


5
((BOOL(WINAPI*)(int))wglGetProcAddress("wglSwapIntervalEXT"))(1);

https://www.khronos.org/opengl/wiki/Swap_Interval

使用wglSwapIntervalEXT(1)启用垂直同步;使用wglSwapIntervalEXT(0)禁用垂直同步。

交换间隔为1告诉GPU在交换前等待 1 个垂直消隐期。交换间隔为0指定GPU不应等待垂直消隐期。


或者:#include <GL/wglext.h>中定义了wgl函数类型。

((PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"))(1);

PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
wglSwapIntervalEXT(1);

不鼓励仅提供代码的答案。请添加一些解释,说明如何解决问题,或者与现有答案的区别。来自审核 - Nick
点赞,因为这是我进行快速测试时最简单、最快速的解决方案。 - Jari Komppa

0

关于WGL案例的描述,请参考eugensk的回答

如果遇到nullptr错误,请确保在OpenGL上下文中运行{wglGetProcAddress}部分的代码。

即在代码{glfwMakeContextCurrent(window);}之后。

请查看此处的答案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接