请求OpenGL上下文的最新版本



我正在开发一个应用程序,通过逐步禁用一些功能和优化,该应用程序可以使用从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?或者有更好的解决方案吗?

如果没有更好的通用解决方案,我想知道它在不同平台上的不同驱动程序的实践中是如何工作的。

如果您要求OpenGL版本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支持的最高核心配置文件版本。

如果你想同时支持核心和任何";兼容性";OpenGL版本,则必须为每个路径编写专门的代码。你必须有一个2.0版本和3.2核心版本的代码。由于您有两个版本的代码,您必须检查该上下文使用哪个版本。

这意味着你不需要一种方法来做你要求做的事情。只需尝试创建一个3.2核心配置文件版本,如果不起作用,就创建一个2.0版本。

我做了一些快速测试。

在Windows AMD Radeon(Ryzen 7)上:

  1. 请求2.1之前的任何上下文版本都会导致4.6兼容性上下文。这正是我想要的
  2. 请求2.1以上的任何上下文版本都会导致请求版本的上下文
  3. 我认为它在Linux专有驱动程序上也是一样的
  4. 它可能在英特尔和NVidia上运行相同,但我现在无法测试它

On Mesa for Windows 20.3.2

  1. 请求3.1之前的任何上下文版本都会导致3.1上下文
  2. 请求3.1以上的任何上下文版本都会导致4.5核心上下文。这正是我想要的
  3. 我想它在Linux开源驱动程序上也是一样的
  4. 请求2.0-3.2之间的任何OpenGL ES版本都会导致3.2上下文。这正是我想要的

在Android上(Adreno 640)

  1. 请求2.0-3.2之间的任何OpenGL ES版本都会导致3.2上下文
  2. 我认为它与安卓系统上的其他供应商的工作原理相同

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

我没有macOS要测试,所以我将使用一种保守的方法,尝试向前兼容4.1、3.3、3.2,然后是2.1。无论如何,大多数Mac都支持4.1,所以对他们来说,上下文将在第一次尝试时创建。

以下是iOS OpenGL ES文档建议执行的操作:

要在应用程序中支持多个版本的OpenGL ES作为渲染选项,您应该首先尝试初始化您想要目标的最新版本的渲染上下文。如果返回的对象为nil,则初始化旧版本的上下文。

因此,在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,我认为马里和其他供应商的工作原理相同。

请在评论中告诉我,你是否可以测试我的假设来证实或否认它们。

相关内容

最新更新