enumerating adapters and display modes?

Started by
10 comments, last by Washu 11 years, 11 months ago
I am confused about enumerating adapters/display modes, and switching to/from fullscreen/windowed in a multi-monitor environment.

First of all, how should my game start up? I haven't really played any PC games since around 2004 - back when most people had 1 monitor and it was 4:3 aspect ratio. Back then, a game would start up in fullscreen, 640x480 (because just about any monitor/adapter would support it), and you could change to a higher resolution through the menus.

What is the typical way this is handled these days? I can't just pick a typically-supported low resolution, because I don't know if the monitor is widescreen. It would look pretty amateur to start up in a 4:3 aspect ratio on a 16:9 monitor and have the image be stretched.

Also, what choices do you display in your graphics options? Suppose I have two monitors - monitor 1 and monitor 2. Let's say the user has gone into windowed mode, and drags the window around. How do I know which monitor the window is on? (It could even be spanning 2 monitors). I would have to be able to hide display modes that the current monitor can't display.

I suppose I could just download the demo of a modern game and look at their graphics options.
Advertisement
So, first things first:

Your application should never change the display mode unless I ask it to. This is 2012, you don't need to start your game at 640x480. Launch it at the resolution my screen is currently running at, as that's my preferred resolution. The number of games that, even today, start the game by default in a reduced resolution is significant. It irritates me greatly, especially since I'm on a multi-monitor system and it FUCKS UP the windows on the secondary monitor.

You should assume decent defaults, optionally you could run a performance test on first startup that makes a decent guess at the appropriate settings. Make sure to inform the customer of this though, don't just start changing resolutions and running demos to test performance.

Allow the user to change the settings. Either in game or a launcher control panel.

Support Full-screen Windowed mode. This is a window (typically WS_POPUP) that is NOT WS_EX_TOPMOST and does not involve a resolution change. It's a windowed application that takes up the entire desktop. GetDesktopWindow, GetWindowRect can be used to appropriately size the window.

You should always start up on the primary display unless I specify otherwise. If your application is in windowed mode and they drag it around then there are messages you can trap to change it to the appropriate device, although the windowed device is typically virtualized somewhat so its not that big of an issue.

All modern APIs (read DX) support mechanisms for querying the display modes, bit depths, texture modes, and anti-aliasing techniques supported by the card. You can use this to tailor your presented options correctly.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

I have a dialog box that enumerates available display modes and asks the user to choose one from a combobox. It also has a checkbox for fullscreen and a checkbox to save settings. At startup I check for saved display settings and attempt to use them; if that fails, or if settings are not found, I display the dialog. I intentionally don't have default display settings, forcing the dialog the first time the game is run.

The only thing is that I only consider the primary display device. My desk is too small for two monitors, so I haven't gotten around to supporting it in the dialog.
Thanks for bringing me into the modern age, lol.

@washu: starting on the primary display at the current resolution solves all my problems! I didn't think it would be that simple.

Why do you recommend "fullscreen windowed" mode? Does it make your game play nicely with alt-tab?
Yes. A game that is fullscreen-windowed can be trivially alt tabbed between say a "guide" and the game. Or if you have other windows open on other screens your are not constantly changing resolution/flickering every time you alt+tab in and out of the game.

From the player's perspective it makes a lot more sense. They not longer go "what the fuck? I hit some key on my keyboard and the entire game vanished, where did it go?"

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Read that article by Intel on 'resolution independent rendering'. Basically it just uses viewports rather than changing the resolution
Ok, so if I am going to start up in fullscreen windowed mode, do I still give the user the option to go to windowed mode? If so, what would my app do, just change the window style so that the title bar and borders are displayed?

Currently I am starting off with a WS_POPUP window. When the user hits 'W', I run the following code:


SetWindowLongPtr(g_hWnd, GWL_STYLE, WS_OVERLAPPED);
SetWindowPos(g_hWnd, HWND_TOP, curWindowPosX, curWindowPosY, windowedWidth, windowedHeight, SW_SHOW);

(curWindowPosX and curWindowPosY are both set to 50, and never changed. windowedWidth and windowedHeight are set to 640 and 480 respectively).

But it seems to have no effect. And if I click the window after running that code, it disappears. Any advice on how to switch to a true windowed mode from a fullscreen windowed mode?

Ok, so if I am going to start up in fullscreen windowed mode, do I still give the user the option to go to windowed mode? If so, what would my app do, just change the window style so that the title bar and borders are displayed?

Yes, you should ideally still allow for traditional windowed mode, as some people prefer it that way as well.

Currently I am starting off with a WS_POPUP window. When the user hits 'W', I run the following code:


SetWindowLongPtr(g_hWnd, GWL_STYLE, WS_OVERLAPPED);
SetWindowPos(g_hWnd, HWND_TOP, curWindowPosX, curWindowPosY, windowedWidth, windowedHeight, SW_SHOW);

(curWindowPosX and curWindowPosY are both set to 50, and never changed. windowedWidth and windowedHeight are set to 640 and 480 respectively).

But it seems to have no effect. And if I click the window after running that code, it disappears. Any advice on how to switch to a true windowed mode from a fullscreen windowed mode?
[/quote]
Try something more like:
#include <windows.h>
#include <atlbase.h>
#include <atlwin.h>

class MyWindow : public CWindowImpl<MyWindow, CWindow, CWinTraits<WS_POPUP, 0>>
{
public:
DECLARE_WND_CLASS_EX(NULL, CS_OWNDC | CS_HREDRAW | CS_VREDRAW, reinterpret_cast<HBRUSH>(BLACK_BRUSH));

MyWindow() {
auto desktop = ::GetDesktopWindow();
RECT bounds;
::GetWindowRect(desktop, &bounds);

Create(0, bounds, _T("COOKIES!"));
}

private:
BEGIN_MSG_MAP(MyWindow)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy);
MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown);
END_MSG_MAP()


LRESULT OnKeyDown(UINT msg, WPARAM wParam, LPARAM lParam, BOOL& result) {
if(wParam == VK_RETURN) {
RECT bounds = {50, 50, 800, 600};
LONG_PTR p = GetWindowLongPtr(GWL_STYLE);
if(!(p & WS_POPUP)) {
auto desktop = ::GetDesktopWindow();
::GetWindowRect(desktop, &bounds);
}
p ^= WS_OVERLAPPEDWINDOW;
p ^= WS_POPUP;
SetWindowLongPtr(GWL_STYLE, p);
SetWindowPos(0, bounds.left, bounds.top, bounds.right, bounds.bottom, SWP_NOZORDER | SWP_NOOWNERZORDER);
} else {
result = FALSE;
}
return 0;
}

LRESULT OnDestroy(UINT msg, WPARAM wParam, LPARAM lParam, BOOL& result) {
PostQuitMessage(0);
return 0;
}
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int nCmdShow) {
MyWindow w;
w.ShowWindow(nCmdShow);
w.UpdateWindow();

MSG msg;
while(GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}


Which works fine for me.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Thanks for the code, Washu.

It took me hours to realize that I was using "WS_OVERLAPPED" where I should've used WS_OVERLAPPEDWINDOW. ohmy.png

I will still take a look at your code, though. Looks like it has a nice, clean architecture.

Thanks for the code, Washu.

It took me hours to realize that I was using "WS_OVERLAPPED" where I should've used WS_OVERLAPPEDWINDOW. ohmy.png

I will still take a look at your code, though. Looks like it has a nice, clean architecture.

It's more than that. You should not simply pass in WS_OVERLAPPED or WS_OVERLAPPEDWINDOW, as other window styles are often appended internally. This is why my code uses xor-assignment to add/remove the appropriate flags from the ACTUAL window style code.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

This topic is closed to new replies.

Advertisement