在Windows OpenGL应用程序中是否有一种优雅的方式来处理在全屏和窗口模式之间切换

Is there a graceful way to handle toggling between fullscreen and windowed mode in a Windows OpenGL application?

本文关键字:处理 方式 之间 模式 窗口 是否 应用程序 OpenGL Windows 一种      更新时间:2023-10-16

我想知道是否有可能在OpenGL窗口(我使用c++和win32为Windows编写)的全屏模式和窗口模式之间来回切换,而不破坏OpenGL上下文,因此必须在此过程中重新加载资产(纹理,VBOs等)?

这是不可取的,因为它引入了在全屏和窗口模式之间切换的延迟,可能是一个很长的延迟,以及使忘记重新初始化的东西更容易把事情搞砸。

接下来,这样做是否会破坏某些视觉效果?

在过去的几天里,我做了相当多的搜索和阅读,尽管有很多燃烧的SDL和其他框架有同样的问题(我不管怎样都不使用它们,但是…),我设法找到的最好的是在后台打开一个1x1窗口以保留上下文,而次要窗口被摧毁或随意创建的可能的线索。从我找到的关于它的评论来看,这似乎是不可靠的,而且看起来非常笨拙。

是否有一个正确的方法来做到这一点,或者是正确的方式,经常给出的一个例子的方法销毁你的窗口,并重新创建它,包括销毁你的OpenGL上下文和重新创建它?

基本上就是调整窗口的大小并指定边框不可见的标志。

SetWindowLongPtr(hWnd, GWL_STYLE, 
    WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
MoveWindow(hWnd, 0, 0, width, height, TRUE);

设置:

RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);

或对于不可调整大小的窗口:

SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE);
AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE);
MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);

然后调整你的OpenGL视口设置。

如果你也想设置显示模式,使用:

// change display mode if destination mode is fullscreen
if (fullscreen) {
    DEVMODE dm;
    dm.dmSize = sizeof(DEVMODE);
    dm.dmPelsWidth = width;
    dm.dmPelsHeight = height;
    dm.dmBitsPerPel = bitsPerPixel;
    dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
    success = ChangeDisplaySettings(&dm, 0) == DISP_CHANGE_SUCCESSFUL;
}
// reset display mode if destination mode is windowed
if (!fullscreen)
    success = ChangeDisplaySettings(0, 0) == DISP_CHANGE_SUCCESSFUL;

这是我使用的代码,它使用SetWindowPos()而不是MoveWindow(),正如在另一个答案的评论中讨论的那样。

void enter_fullscreen(application* App)
{
  POINT Point = {0};
  HMONITOR Monitor = MonitorFromPoint(Point, MONITOR_DEFAULTTONEAREST);
  MONITORINFO MonitorInfo = { sizeof(MonitorInfo) };
  if (GetMonitorInfo(Monitor, &MonitorInfo)) {
    DWORD Style = WS_POPUP | WS_VISIBLE;
    SetWindowLongPtr(App->Window, GWL_STYLE, Style);
    SetWindowPos(App->Window, 0, MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
        MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
        SWP_FRAMECHANGED | SWP_SHOWWINDOW);
  }
  App->IsFullscreen = true;
}
void exit_fullscreen(application* App)
{
  bool WasMaximized = App->IsMaximized;
  DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN;
  if (WasMaximized) {
    Style = Style | WS_MAXIMIZE;
  }
  ivec2 WindowPosition = WasMaximized ? App->WindowPosition : App->NormalWindowPosition;
  ivec2 WindowSize = WasMaximized ? App->WindowSize : App->NormalWindowSize;
  SetWindowLongPtr(App->Window, GWL_STYLE, Style);
  SetWindowPos(App->Window, 0,
      WindowPosition.X, WindowPosition.Y, WindowSize.X, WindowSize.Y,
      SWP_FRAMECHANGED | SWP_SHOWWINDOW);
  App->IsFullscreen = false;
}

我在F11上调用它,但在WM_ACTIVATE上也调用它。否则,即使其他应用程序接收到所有消息(包括鼠标和键盘),该窗口有时也会在Windows 7上保持顶部呈现。