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

发布于 2024-12-01 22:15:41 字数 458 浏览 3 评论 0原文

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

这是不可取的,因为它会导致全屏和窗口模式之间的切换出现延迟,而且可能会很长,而且更容易因忘记重新初始化某些内容而把事情搞砸。

作为后续行动,是否会因设法做到这一点而破坏某些视觉效果?

在过去的几天里,我做了相当多的搜索和阅读,尽管 SDL 和其他框架有很多同样的问题(我反正没有使用它们,但是......),但最好的我设法找到了在后台打开 1x1 窗口以保留上下文的可能线索,同时辅助窗口被销毁或随心所欲地创建。从我发现的有关它的评论来看,这似乎不可靠,而且无论如何看起来都很糟糕。

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

I'm wondering if it's possible to toggle back and forth between fullscreen mode and windowed mode in an OpenGL window(I'm writing for Windows using C++ and win32), without destroying the OpenGL context, and thus having to reload assets(Textures, VBOs, etc) in the process?

This is undesirable because it introduces a delay in switching between fullscreen and windowed mode, potentially a long one, as well as making it easier to screw things up by forgetting to reinitialize something.

As a followup to that, are there certain visual effects that are broken by managing to do this?

I've done a fair bit of searching and reading for the past few days, and despite a lot of flaming of SDL and other frameworks for having the same problem(I'm not using them anyway, but...), the best I've managed to find is a possible lead on opening a 1x1 window in the background to retain the context while a secondary window is destroyed or created at whim. And that's seeming unreliable from the comments I found regarding it, and seems very kludgey regardless.

Is there a proper way to do this, or is the proper way the often-given-as-an-example method of destroying your window, and recreating it, including destroying your OpenGL context and recreating it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

东走西顾 2024-12-08 22:15:41

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

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;

Basically it's just resizing the window and specifying flags that the border is invisible.

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

to set it back:

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);

or for a not-resizable window:

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);

and then just resize your OpenGL viewport settings.

If you want to set the display mode too, use this:

// 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;
日暮斜阳 2024-12-08 22:15:41

这是我使用的代码,它使用 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 上保持呈现在顶部。

Here's the code I use, which uses SetWindowPos() rather than MoveWindow(), as discussed in the comments of the other answer.

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;
}

I call it on F11, but also on WM_ACTIVATE. Otherwise the window would sometimes keep rendering on top on Windows 7, even if another application would receive all messages, including mouse and keyboard.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文