5

我正在使用 SDL2 和 C++11 构建游戏引擎(只是作为个人项目,为了娱乐和练习),我想做的一件事是尝试让图形驱动程序使用最新的支持版本OpenGL,并根据版本改变引擎的图形部分的渲染方式。这样,我可以在相关且有用的地方使用 OpenGL 的最新功能,同时也支持旧硬件。我能想到的方法有两种:

  1. 检测最新支持的 OpenGL 版本并使用它,但我想不出任何方法来做到这一点。是的,我试过谷歌。

  2. 使用反复试验,我从最新版本开始(4.3,但我的 GTX 460 仅支持最高 4.2,即使我更新了驱动程序),如果失败(我通过检查 SDL 来检测返回 NULL 上下文),我降低版本号并重试。

我使用的方法(#2)在创建 4.3 上下文后立即失败。我知道我的图形驱动程序现在只支持 4.2,但我曾认为 GLX 旨在引发错误,提供 NULL 上下文,并让您的程序继续运行,但它无法通过上下文创建失败. 我对 GLX 行为方式的假设是否错误?有没有办法在不创建上下文的情况下检测最新的支持版本?

因为我知道有些人更喜欢看到演示错误的完整和最小的源,所以它是:

#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
int main() {
    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
        return -1;
    }
    SDL_Window* window = SDL_CreateWindow( "SDL Window", 0, 0, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
    if( window == nullptr ) {
        return -1;
    }
    unsigned int major = 4;
    unsigned int minor = 3;
    SDL_GLContext context = nullptr;
    while( context == nullptr ) {
        SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, major );
        SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, minor );
        context = SDL_GL_CreateContext( window );
        if( context == nullptr ) {
            if( minor == 0 && major > 1 ) {
                --major;
                minor = 9;
            }
            else if( minor > 0 ) {
                --minor;
            }
            else {
                SDL_DestroyWindow( window );
                return -1;
            }
        }
    }
    SDL_Delay( 5000 );
    SDL_GL_DeleteContext( context );
    SDL_DestroyWindow( window );
    return 0;
}

此外,如果我将该代码更改为以 4.2 上下文而不是 4.3 开头,它就可以正常工作。所以错误在于专门创建 4.3 上下文。

4

2 回答 2

2

这有点晚了,但我最近遇到了这个问题(尝试创建 SDL2 不支持的 opengl 上下文时仍然崩溃)

我只是创建了一个虚拟上下文,然后使用该上下文来获取 GL 版本。然后破坏该上下文(除非它正是我想要的)并制作另一个。

//Create Dummy Context
context = SDL_GL_CreateContext(window);

//Get OpenGL Version
const GLubyte* sGLVersion =  glGetString(GL_VERSION);

version = (sGLVersion[0]-'0')*10;
version += (sGLVersion[2]-'0');

//Now Check that it can support the version that you want to make and stop if you can't
//or in your case set it to that version

//If you need to make another context destroy the old one
SDL_GL_DeleteContext(context);

//and make a new one
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, minorVersion );

context = SDL_GL_CreateContext(window);

这也比暴力破解版本号要快,直到你碰巧找到一个可以工作的版本。(您无法通过谷歌搜索找到您的解决方案 1 :))

'the swine' 误认为默认版本是最大支持的版本(至少在我有它默认为 2.1 的版本中,我有 4.3 可用)

于 2013-08-02T15:42:37.770 回答
1

这可能表明 SDL 的 X11_GL_CreateContext() 中存在错误(在网上它说 SDL2 仍处于试验阶段)。您找出支持的 OpenGL 版本的策略是有效的,但是为了让 SDL 为您提供前向兼容的上下文,它首先需要创建一个临时上下文,调用 glXGetProcAddress() 来获取 glCreateContextAttribsARB (如果没有一个活动的 OpenGL 上下文),然后它会尝试初始化您请求的 OpenGL 版本。现在,如果它失败了,它会处理临时上下文并返回 NULL。

我的猜测是它第二次无法创建临时上下文,我建议使用调试信息构建 SDL,并尝试找出 X11_GL_CreateContext() 中失败的确切位置(或至少插入几个 printf)。

另一方面,如果您将 SDL_GL_CONTEXT_MAJOR_VERSION 和 SDL_GL_CONTEXT_MINOR_VERSION 保留为默认值,则您已经获得了最大的 OpenGL 版本。你只是没有得到一个向前兼容的上下文,这意味着它会让一些在未来版本中被认为是“错误”的行为溜走。

此外,一般来说,创建具有最大版本的前向兼容上下文并不是一个好主意。例如,在 OpenGL 3.2 中,向前兼容的配置文件强制使用顶点数组对象,这使得不使用 VAO 的旧代码被破坏。因此,如果为 OpenGL 3.1(或任何其他版本)编写的应用程序试图获得最新的向前兼容配置文件,它实际上可能在将来停止工作。这不是一个好主意。

一种选择是决定使用一个向前兼容的配置文件,这非常有用,因为它会报告有关已弃用功能的错误。如果您真的想使用更多版本,则需要更多代码路径(= 更多版本的游戏引擎)才能这样做。要检测 OpenGL 版本,我建议创建一个虚拟 SDL 窗口,初始化一个简单的 OpenGL 配置文件,获取版本并关闭所有内容,然后使用您决定的版本再次初始化。那应该行得通。

另一种选择是仅将向前兼容的配置文件用于开发,并将简单的旧配置文件用于分发。它是最兼容的并且涉及最少的工作(无论如何您都想忽略发布版本中的大多数 OpenGL 错误 - 错误报告甚至可能被驱动程序禁用)。

于 2012-11-09T11:58:52.313 回答