26

我有一个 wxWidgets 应用程序,它有许多子 opengl 窗口。我正在使用我自己的 GL 画布类,而不是 wx 类。窗口共享它们的 OpenGL 上下文。我不认为它是 wxwidgets 的事实在这里真的很重要。

opengl 窗口是一个窗口的子窗口,它们是彼此的兄弟窗口,包含在选项卡控件中。一种 MDI 风格的界面,但它不是 MDI 窗口。每个都可以单独调整大小。除非启用 Aero 并且 DWM 处于活动状态,否则一切正常。

调整任何窗口(甚至是 opengl 窗口)的大小会导致所有 opengl 窗口偶尔闪烁,并带有一个陈旧的后备存储视图,其中包含当时屏幕上的任何垃圾,而不是 opengl。这只发生在启用 Aero 的情况下。

我很确定这是 DWM 实际上在其绘图表面后备存储中没有 opengl 内容,并且窗口没有在正确的时刻重新绘制。

我已经尝试了很多方法来解决这个问题,我确实有一个解决方案,但它不是很好,并且涉及将带有 glReadPixels 的帧缓冲区读取到 DIB 中,然后在我的 onPaint 例程中将其传送到绘图 DC。仅当 DWM 处于活动状态时才启用此解决方法,但我宁愿根本不必这样做,因为它会稍微损害性能(但在功能强大的系统上并不算太糟糕 - 场景是相对简单的 3d 图)。也不建议将 GDI 和 opengl 混合使用,但令人惊讶的是,这种方法很有效。我现在可以忍受它,但我宁愿不必。无论如何,如果我想截取子窗口的屏幕截图,我仍然必须在 WM_PRINT 中执行此操作,我看不到解决方法。

有谁知道更好的解决方案?

在有人问之前,我肯定会做以下事情:

  • 窗口类有 CS_OWNDC
  • WM_ERASEBACKGROUND 什么都不做并返回 TRUE。
  • 双缓冲已启用。
  • Windows 具有 WS_CLIPSIBLINGS 和 WS_CLIPCHILDREN 窗口样式。
  • 在我的 resize 事件处理程序中,我立即重新绘制窗口。

我试过了:

  • 在像素格式描述符中设置 PFD_SUPPORT_COMPOSITION。
  • 不在绘制处理程序中使用 wxPaintDC 而是调用 ::ValidateRect(hwnd, NULL) 。
  • 处理 WM_NCPAINT 并排除客户区
  • 通过 DWM API 禁用 NC 绘制
  • 在绘制事件中排除客户区
  • 在缓冲区交换前后调用 glFlush 和/或 glFinish。
  • 在每个绘画事件中使窗口无效(作为测试!) - 仍然闪烁!
  • 不使用共享 GL 上下文。
  • 禁用双缓冲。
  • 写入 GL_FRONT_AND_BACK

禁用 DWM 不是一种选择。

据我所知,如果您在 OpenGL 上使用 Direct3D 而不是这甚至是一个问题,尽管我没有对此进行测试,因为它代表了很多工作。

4

3 回答 3

3

这是一个远景,但我自己刚刚解决了同样的问题。

长镜头部分出现是因为我们正在对围绕我们的 OpenGL 窗口的无标题组框的轮廓进行所有者绘制(即,制作一个漂亮的小边框),这可能无法描述您的情况。

我们发现导致问题的原因是:

我们一直在使用 RoundRect() 调用(带有 HOLLOW_BRUSH)来绘制组框的轮廓。将其更改为 MoveToEx() 和 LineTo() 调用以确保仅绘制线条并且在组框内没有任何操作可以防止 GDI 尝试意外地重绘控件的整个内容。无效逻辑可能存在差异(或者我们在加载预期的空心画笔时遇到了错误)。我们还在调查。

-诺埃尔

于 2015-08-30T18:02:48.340 回答
2

我的应用程序只有一个 OpenGL 窗口(主窗口),但我在调整窗口大小时遇到​​了一些令人讨厌的 DWM 撕裂问题,我想知道其中一种解决方案是否适合您。

首先,我发现在窗口调整大小期间,至少有两个不同的坏人想要通过修改您的客户区来“帮助”您,然后您才有机会自己更新窗口,从而产生闪烁。

第一个坏人可以追溯到 XP/Vista/7BitBltSetWindowPos()Windows 内部在窗口调整大小期间所做的内部操作,并且可以通过涉及拦截的技巧WM_NCCALCSIZE或其他涉及拦截的技巧来消除WM_WINDOWPOSCHANGING

在 Windows 8/10 中,我们仍然有这个问题,但我们有一个新的坏人,Aero DWM.exe 窗口管理器,BitBlt当他认为你“落后”更新屏幕时,他会做他自己的不同类型。

我怀疑您看到的垃圾像素实际上可能是 DWM 在等待您绘制时故意且非常糟糕的尝试填充“可接受”的东西。我发现当 DWM 对新客户区进行 blit 时,它会扩展旧客户区数据的边缘像素,这太疯狂了。

不幸的是,我不知道有任何 100% 的解决方案可以阻止 DWM 这样做,但我确实有一个计时技巧,可以大大降低它的频率。

有关WM_NCCALCSIZE/ WM_WINDOWPOSCHANGINGhack 以及 DWM 计时 hack 的源代码,请参阅:

如何在调整窗口大小时平滑难看的抖动/闪烁/跳跃,尤其是拖动左/上边框(Win 7-10;bg、bitblt 和 DWM)?

于 2018-10-26T08:34:14.423 回答
0

嗯,也许您遇到了同样的问题:如果您使用的是“新”MFC,它将使用选项卡和窗口拆分器创建和应用程序。

拆分器有一些逻辑(我猜测透明窗口周围的某个地方并为拆分绘制 XOR 线)会导致这种行为。卸下分离器以确认它可以解决您的问题。如果您需要拆分功能 - 放入不同的拆分器。

选项卡还允许停靠并再次拆分具有相同问题的窗口 - 删除/替换。

祝你好运,伊戈尔

于 2012-09-05T01:01:40.233 回答