请求最新版本的OpenGL上下文。

4
我正在开发一个应用程序,可以通过逐步禁用一些功能和优化,使用任何从4.6降至2.0的OpenGL版本。这意味着它可以与2.0共存,但首选最新支持的版本,以便能够使用OpenGL 3.x-4.x中的所有可用功能。此外,它处理了核心和兼容性上下文之间的所有差异,因此应该适用于任何配置文件。
在Windows上似乎不会有问题,因为我可以省略版本和配置文件,并自动获得与最新支持版本兼容的上下文。
但是在macOS和Mesa上工作方式不同。在那里,我必须请求某个特定版本的核心向前兼容上下文,即使我不想要特定的版本,我也想要最新的版本。
我如何处理这个问题?我需要循环尝试所有版本4.6、4.5、4.4、4.3、4.2、4.1、4.0、3.3、3.2、3.1、3.0、2.1、2.0,直到成功创建上下文吗?还是有更好的解决方案?
如果没有更好的通用解决方案,我想了解在不同平台上的不同驱动程序中实际运作方式。
2个回答

5
如果您请求OpenGL版本X.Y,系统可以提供与X.Y向后兼容的任何支持的OpenGL版本。也就是说,请求X.Y表示“我已经根据GL版本X.Y编写了我的代码,请不要给我会破坏我的代码的东西。”
然而,OpenGL 3.2+的核心配置文件与2.0不兼容。事实上,这就是核心/兼容性区别的全部意义:兼容性配置文件提供对API更高功能的访问,同时与现有代码向后兼容。核心配置文件则不能。例如,2.0缺少顶点数组对象,而核心配置文件OpenGL没有这些对象将无法工作。
现在,每个配置文件的所有OpenGL版本都与该配置文件中所有较低的API版本向后兼容。因此,3.2核心配置文件向后兼容4.6及其之间的所有版本。而兼容性配置文件向后兼容OpenGL的所有早期版本。
但是,实现不需要支持OpenGL的兼容性配置文件,只需要支持核心配置文件。因此,如果您请求OpenGL版本2.0,则实现将必须为您提供与GL 2.0兼容的最高版本的OpenGL。如果实现不支持兼容性配置文件,则这将不是所支持的最高核心配置文件版本。
如果要支持核心和任何“兼容性”版本的OpenGL,则必须为每个路径编写特定的代码。您必须拥有2.0版本和3.2核心版本的代码。由于您有两个版本的代码,因此必须检查要在哪个上下文中使用哪个版本。
这意味着您无需执行您正在请求的操作。只需尝试创建3.2核心配置文件版本,如果不起作用,则创建2.0版本即可。

我实际上有一个单一的代码库,其中有很多分支来检查每个功能的可用性。因此,如果该功能不可用,则尝试通过扩展获取它,模拟它或禁用它。例如,VAO周围有一个包装器,如果它不可用,则可以模拟它。无论如何,如果我请求3.2核心,是否保证在每个实现中都获得最新支持的版本(最高达4.6或macOS上的4.1)? - devoln

1

我进行了一些快速测试。

在Windows AMD Radeon (Ryzen 7)上:

  1. 请求任何上下文版本,最高可达2.1,将得到4.6兼容性上下文。这正是我想要的。
  2. 请求任何高于2.1的上下文版本,将得到所请求版本的上下文。
  3. 我认为在Linux专有驱动程序上也是同样的情况。
  4. 在英特尔和NVIDIA上可能也是同样的情况,但我现在无法测试。

在Windows 20.3.2的Mesa上:

  1. 请求任何上下文版本,最高可达3.1,将得到3.1上下文。
  2. 请求任何高于3.1的上下文版本,将得到4.5核心上下文。这正是我想要的。
  3. 我认为在Linux开源驱动程序上也是同样的情况。
  4. 请求OpenGL ES 2.0-3.2之间的任何版本,将得到3.2上下文。这正是我想要的。

在Android(Adreno 640)上:

  1. 请求任何OpenGL ES版本在2.0-3.2之间的结果都会得到一个3.2上下文。
  2. 我认为在Android上其他供应商的情况也是如此。

似乎只有第一次创建上下文时速度较慢。在这两种情况下,尝试创建上下文的额外尝试会在我的系统上增加约4毫秒的应用程序启动时间,而整个上下文+窗口创建时间大约为300毫秒(使用本地驱动程序)或70毫秒(使用Mesa)。

我没有macOS进行测试,所以我将采用保守的方法,先尝试向前兼容4.1、3.3、3.2,然后再尝试2.1。不管怎样,大多数Mac都支持正好4.1,因此对于它们来说,上下文将在第一次尝试时创建。

这就是iOS OpenGL ES文档建议做的事情:

为了在应用程序中支持多个OpenGL ES版本作为渲染选项,您应该首先尝试初始化您想要定位的最新版本的渲染上下文。如果返回的对象为空,则初始化旧版本的上下文。因此,在GLFW伪代码中,我的OpenGL策略如下:
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
GLFWwindow* wnd;
#ifdef __APPLE__ // macOS
const std::array<char, 2> versions[] = {{4, 1}, {3, 3}, {3, 2}, {2, 1}};
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
for(auto ver: versions)
{
    if(ver[0] < 3) glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, false);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, ver[0]);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, ver[1]);
    wnd = glfwCreateWindow(...);
    if(wnd) break;
}
glfwMakeContextCurrent(wnd);
#else // Windows, Linux and other GLFW supported OSes
glfwWindowHint(GLFW_VISIBLE, false);
wnd = glfwCreateWindow(...);
glfwMakeContextCurrent(wnd);
std::string_view versionStr = glGetString(GL_VERSION);
if(versionStr[0] < '4' && versionStr.contains("Mesa"))
{
    glfwDestroyWindow(wnd);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    wnd = glfwCreateWindow(...);
    glfwMakeContextCurrent(wnd);
}
glfwShowWindow(wnd);
#endif

OpenGL ES的代码看起来类似但更简单。移动平台将使用不同的库,而不是GLFW(GLFM / SDL或本地EGL)。对于iOS,我必须尝试ES 3.0然后尝试ES 2.0。对于Mesa和Android,我只需请求一个2.0上下文并获取最新的上下文(3.2)。但是,对于Android,我假设Mali和其他供应商的工作方式相同。请在评论中让我知道您是否可以测试我的假设以确认或否认它们。

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