今天我消除了对OpenTK的依赖;我的应用程序不再依赖OpenTK来创建OpenGL上下文。但是,我的代码在 Mesa 3D(OpenGL 的软件渲染实现)中运行时不再可接受;我的代码运行,但 FPS 约为 0.001 FPS(相比之下,使用 OpenTK 的上下文创建约为 16+FPS),并且通常被吸引到 FBO 的内容显示在窗口中,在编写时逐段显示。尽管在没有 Mesa 3D 的情况下运行我的代码会导致正常性能(在 Windows 7 上),但我担心它可能只是巧合,它运行良好。 glGetError
检查没有显示任何错误,这让我觉得也许我在上下文创建中做错了什么?
m_controlHandle = m_winForm.Handle; /* HWND */
m_controlDC = Win32.GetDC(m_controlHandle); /* HWND's DC*/
Win32.PixelFormatDescriptor pixelFormat = new Win32.PixelFormatDescriptor();
pixelFormat.Size = (short)Marshal.SizeOf(typeof(Win32.PixelFormatDescriptor));
pixelFormat.Version = 1;
pixelFormat.Flags =
Win32.PixelFormatDescriptorFlags.DRAW_TO_WINDOW |
Win32.PixelFormatDescriptorFlags.SUPPORT_OPENGL |
Win32.PixelFormatDescriptorFlags.DOUBLEBUFFER;
pixelFormat.PixelType = Win32.PixelType.RGBA;
pixelFormat.ColorBits = 32;
pixelFormat.DepthBits = 0; /* yes, I don't use a depth buffer; 2D sprite game */
pixelFormat.LayerType = Win32.PixelFormatLayerType.MAIN_PLANE;
int formatCode = Win32.ChoosePixelFormat(m_controlDC, ref pixelFormat);
if (formatCode == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Win32.SetPixelFormat(m_controlDC, formatCode, ref pixelFormat))
throw new Win32Exception(Marshal.GetLastWin32Error());
m_openGLContext = Win32.wglCreateContext(m_controlDC);
if (m_openGLContext == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Win32.wglMakeCurrent(m_controlDC, m_openGLContext))
throw new Exception("Could not wglMakeCurrent.");
这是对的吗?有什么建议可以追踪可能导致Mesa3D突然变得疯狂的原因吗?
如果你想避免软件实现,你的逻辑中有一个主要缺陷,选择像素格式。回想一下(或第一次了解)WGL 使用模式匹配启发式方法,该启发式方法查找最低满足所请求参数的所有像素格式的集合。将请求的参数设置为 0 越多,在解决"最佳"(最接近)匹配时,它留下的歧义就越多。
如果要了解为什么 0 位深度缓冲区与 32 位颜色缓冲区相结合可能是一个坏主意,可以枚举显示驱动程序提供的所有像素格式,并检查哪些格式是完全硬件加速的(它们不会设置标志:PFD_GENERIC_FORMAT
或PFD_GENERIC_ACCELERATED
)。有一些软件可以为您执行此操作,尽管我想不出任何超出我的头顶 - 如果您真的决定这样做,请准备好浏览数百种像素格式的列表......
许多奇怪的参数组合是在软件(GDI)中实现的,而不是在硬件中实现的。为了获得硬件格式,尝试的最佳位深度之一是 32 位 RGBA、24 位深度、8 位模板。在现代硬件上,这几乎总是驱动程序将选择的大多数合理输入参数组合的"最接近"匹配项。您越接近选择精确的硬件像素格式,就越有可能阻止 Win32/WGL 为您提供 GDI 像素格式。
根据我自己的观察:
在遥远的过去,我不得不手动枚举像素格式并覆盖ChoosePixelFormat (...)
的行为以解决驱动程序错误;模式匹配行为并不总是对您有利。
在OpenTK的情况下,我怀疑它实际上使用了wglChoosePixelFormatARB (...)
,这是一个用于选择像素格式的更复杂的接口(并且是支持多采样抗锯齿所必需的)。 wglChoosePixelFormatARB
由可安装客户端驱动程序 (ICD) 实现,因此它永远不会尝试将输入参数与 GDI 像素格式匹配。
我怀疑 GDI 提供 32 位 RGBA + 0 位 Z 像素格式,但您的 ICD 没有。由于最接近的匹配始终获胜,并且ChoosePixelFormat
看到 GDI 像素格式,因此您可以了解为什么这是一个问题。
您应该尝试 32 位 RGBA(24 位颜色,8 位 Alpha)+ 24 位 Z + 8 位模板,看看这是否能提高您的性能......
更新:
还有另一个问题可能会绊倒一些更愚蠢的司机。 ColorBits
应该是 RGBA 像素格式的 RGB 位数(通常为 24)。 AlphaBits
应该是 A 位的数量(通常为 8)。许多驱动程序将看到 32 位ColorBits
与 0 AlphaBits
相结合,并了解隐含的行为(24 位 RGB + 8 位填充),但编写代码的方式可能会很麻烦。从技术上讲,这种像素格式称为RGBX8
(其中X表示未使用的位)。
24 ColorBits
和 8 AlphaBits
可能会提供更便携的结果。
好的...我希望没有人必须经历我所做的事情来解决这个问题,所以这是解决方案:
虽然OpenTK会尝试使用wglChoosePixelFormatARB
,但它在Mesa 3D上失败并回退到ChoosePixelFormat
。但是,OpenTK 调用的函数实际上ChoosePixelFormat
DllImports
wglChoosePixelFormat
而不是ChoosePixelFormat
。
是的,ChoosePixelFormat
有两个版本:一个以wgl
为前缀,一个不以前缀。从 OpenGL.org 文档中:
在 Win32 平台上,OpenGL ICD 机制和 GDI 中重复了许多特定于平台的函数调用。这可能会导致混淆,因为它们在功能上似乎是相同的,唯一的区别是 wgl 是否在函数名称的其余部分之前。为了确保OpenGL的正确操作,请使用ChoosePixelformat,DescribePixelformat,GetPixelformat,SetPixelformat和SwapBuffers,而不是wgl等效的wglChoosePixelformat,wglDescribePixelformat,wglGetPixelformat,wglSetPixelformat和wglSwapBuffers。在所有其他情况下,如果可用,请使用 wgl 函数。使用五个 wgl 函数只对链接到 OpenGL 驱动程序的运行时开发人员感兴趣。不使用上述函数可能会导致黑色 OpenGL 窗口,或者在 Windows 9x 中正常运行的应用程序在 Windows NT/2000 上生成黑色 OpenGL 窗口。
当我尝试运行时链接到OpenGL驱动程序(Mesa 3D)时,我对五个wgl
函数很感兴趣。一旦我用wglChoosePixelFormat
、wglSetPixelformat
和wglSwapBuffers
替换了我的ChoosePixelFormat
、SetPixelformat
和SwapBuffers
,Mesa 3D就非常出色!谜团解开了。