使用 OpenGL 和 Gdi+ 的 GetDC、ReleaseDC、CS_OWNDC

Posted

技术标签:

【中文标题】使用 OpenGL 和 Gdi+ 的 GetDC、ReleaseDC、CS_OWNDC【英文标题】:GetDC, ReleaseDC, CS_OWNDC with OpenGL and Gdi+ 【发布时间】:2018-02-07 12:27:33 【问题描述】:

阅读 GetDC/ReleaseDC 我应该总是做后者,并且窗口上的 CS_OWNDC 被认为是邪恶的:

 https://blogs.msdn.microsoft.com/oldnewthing/20060601-06/?p=31003

查看我的代码,我发现我持有从 GetDC 检索到的 DC,我已将其发送到 wglCreateContextAttribARB。我假设上下文是在该 DC 上创建的,因此随后从驱动程序下释放它是不礼貌的。我的假设正确吗?目前,当我销毁其他 OpenGL 资源时,我正在调用 ReleaseDC。

另外,在我的库的其他地方,我调用 GetDC 来实例化一个 GDI+ 图形对象,然后在我完成绘图后再次释放它。我认为出于性能原因在绘图调用之间保留 DC 和 Graphics 对象会很好,仅在 WM_DISPLAYCHANGE 等上重新创建它。

那么,是否有该领域最佳实践的权威指南?我努力释放 GDI+ DC 但坚持使用 OpenGL DC 的方式似乎有些不一致。

【问题讨论】:

【参考方案1】:

OpenGL 和 GDI+ 的行为不同。

在 OpenGL 中,您需要一个上下文,它附加DC。这意味着DC 必须在上下文存在时存在。因此,OpenGL 绘制的窗口确实需要CS_OWNDC 样式。 删除上下文后致电ReleaseDC

GDI+ 像任何常见的DC 一样在 MS Windows 中使用:检索 DC,绘制到它,释放该 DC。在这种情况下,CS_OWNDC 的使用可能是邪恶的,正如您发布的link 中所指出的那样。 MS GDI+ 使用图形硬件的方式(即创建上下文或其他)与您无关。


编辑由于 Chris Becke 的评论:

不需要使用 CS_OWNDC

引用https://msdn.microsoft.com/es-es/library/windows/desktop/dd374387(v=vs.85).aspx:

hdc 参数必须引用 OpenGL 支持的绘图表面。 它不必与传递给 wglCreateContext 时相同的 hdc hglrc 已创建,但它必须在同一设备上并且具有相同的 像素格式。

建议使用 CS_OWNDC。

在过去的 Windows 9x 中,获取和发布设备上下文既昂贵又缓慢。拥有一个固定直流会更有效。在窗口注册时使用CS_OWNDC 标志是获得固定直流的方法。

CS_OWNDC 用法提供了一个私有设备上下文(参见https://msdn.microsoft.com/en-us/library/windows/desktop/ms633574(v=vs.85).aspx#class_styles)。 引用 MS 文档 (https://msdn.microsoft.com/en-us/library/windows/desktop/dd162872(v=vs.85).aspx):

虽然私有设备上下文使用起来很方便,但它是 在系统资源方面内存密集,需要800或更多 要存储的字节。建议使用私有设备上下文 性能考虑大于存储成本。

您必须注意,您必须避免使用私有设备上下文的ReleaseDC

应用程序可以通过以下方式检索私有设备上下文的句柄 在创建窗口后随时使用 GetDC 函数。这 应用程序必须只检索一次句柄。此后,可以 保持和使用手柄任意次数。因为是私人设备 上下文不是显示设备上下文缓存的一部分,一个 应用程序永远不需要通过使用释放设备上下文 释放DC函数。

在通过检索DC、设置当前上下文、绘制、交换缓冲区和释放DC 来绘制到唯一窗口的常见场景中,使用 CS_OWNDC 而不是 GetDC&ReleaseDC 是很自然的。

无论您的 GetDC/ReleaseDC 代码如何,都可能使用 wglGetCurrentDC()(例如通过外部库)。通常不会发生任何问题。但如果当前 gl-context 为 NULL(就像您在 ReleaseDC 之后立即做的那样),那么 wglGetCurrentDC() 将失败。

没有 CS_OWNDC 的代码 在两个窗口中使用像素格式相同如下所示:

myGLContext = wglCreateContext(...)

//Draw to window A
HDC hdcA = GetDC(hWndA)
wglMakeCurrent(hdcA, myGLContext)
... render...
SwapBuffers(hdcA)
ReleaseDC(hWndA, hdcA)

//Draw to window B
HDC hdcB = GetDC(hWndB)
wglMakeCurrent(hdcB, myGLContext)
... render...
SwapBuffers(hdcB)
ReleaseDC(hWndA, hdcA)

wglMakeCurrent(hdcB, NULL)

【讨论】:

好吧,在我看来,这听起来像是一个很好的一般规则。 CS_OWNDC 在 GDI 的上下文中是不好的,GDI+。在所有情况下都不是一概而论。 这是垃圾。像素格式在 window 上设置,为该窗口检索到的所有窗口 DC 都将使用 gl 上下文。甚至 OpenGL 也不需要使用 CS_OWNDC。 @ChrisBecke 但是在官方的 Khronos wiki (khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL)) 中我们可以读到应该使用 CS_OWNDC 标志:“当你创建你的 HWND 时,你需要确保它设置了 CS_OWNDC它的风格。” 是的——我也读过。但那是一个质量可疑的维基页面。我在任何官方标准文档中都找不到任何对这种奇怪说法的佐证。它确实与 wgl* 和 SetPixelFormat* 函数的 MSDN 文档隐含冲突,至少对我而言,这些函数的措辞暗示任何表示窗口的 HDC 都是等效的。 @ChrisBecke MSDN 说:“GetDC 函数根据指定窗口的类样式检索公共、类或私有 DC。”因此,当未指定 CS_OWNDC 标志时,我们会得到常见的 DC 类。所以对于这样的DC,像素格式只能设置一次。如果我们创建一个“假”窗口来检索扩展和函数以更好地创建 OpenGL 上下文,并且如果我们想创建另一个具有另一个上下文的窗口,我们不能选择其他像素格式(例如支持多重采样的像素格式)。为此,我们需要一个与给定窗口关联的 DC,因此需要 CS_OWNDC 标志。

以上是关于使用 OpenGL 和 Gdi+ 的 GetDC、ReleaseDC、CS_OWNDC的主要内容,如果未能解决你的问题,请参考以下文章

从 OpenGL 切换到 GDI

如何让 OpenGL 和 GDI 同时正常工作?

Windows_GDI概述

是否可以在已经使用 GDI+ 的窗口中使用 OpenGL?

DrawFrameControl 中的 GDI 资源泄漏

带有opengl的GDI +可能吗?