mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 14:20:42 -07:00
graphics: rewrite auto-rotate and refresh rate options to be more reliable (#607)
## Description of change Rotate monitor & change the refresh rate **well before** the game is launched. This is much faster and more reliable compared to what we do now, which is trying to do monitor changes during CreateWindow hooks. For example, SDVX makes a bunch of OS / NVAPI calls to figure out monitor geometry while it's booting, and by the time CreateWindow is called, the game already made up its mind, which leads to weird graphical issues. This affects both windowed mode and full screen. In addition, add options for normal orientation (landscape) and upside down (landscape, flipped) options. This can be used if the monitor is normally rotated to portrait, but the game needs to be in landscape.
This commit is contained in:
@@ -552,18 +552,6 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modified &&
|
||||
(width > height) &&
|
||||
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
||||
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW)) {
|
||||
log_misc(
|
||||
"graphics::d3d9", "swapping width and height for mode {}, {}x{} @ {}Hz (-autoorientation)",
|
||||
Mode, width, height, refresh);
|
||||
pMode->Height = width;
|
||||
pMode->Width = height;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_RESULT(ret);
|
||||
|
||||
@@ -301,30 +301,6 @@ static BOOL WINAPI ClipCursor_hook(const RECT *lpRect) {
|
||||
return ClipCursor_orig(lpRect);
|
||||
}
|
||||
|
||||
static void fix_up_rb_auto_rotate_dimensions(DWORD &w, DWORD &h) {
|
||||
// reflec beat
|
||||
// for whatever reason, rb creates the first window it weird dimensions (774x1389 for reflesia, for example)
|
||||
// so doing ChangeDisplaySettings as-is ends up with DISP_CHANGE_BADMODE
|
||||
// while a second window (with title D3DProxyWindow) does get created with correct dimensions, it's
|
||||
// too late to rotate the window then; as a workaround force the display change now with patched up dimensions
|
||||
|
||||
bool changed = false;
|
||||
if (w == 774) {
|
||||
// LBR, MBR reflesia
|
||||
w = 768;
|
||||
h = 1360;
|
||||
changed = true;
|
||||
} else if (w == 1086) {
|
||||
// MBR (before reflesia)
|
||||
w = 1080;
|
||||
h = 1920;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
log_misc("graphics", "fix auto-rotate dimensions for reflec beat: w={}, h={}", w, h);
|
||||
}
|
||||
}
|
||||
|
||||
static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName,
|
||||
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
|
||||
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
|
||||
@@ -346,62 +322,6 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
fmt::ptr(hInstance),
|
||||
fmt::ptr(lpParam));
|
||||
|
||||
// set display orientation and/or refresh rate
|
||||
// only set orientation when the target window is portrait
|
||||
// (avoid doing this for SDVX subscreen which is in landscape, for example)
|
||||
auto adjust_orientation =
|
||||
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
||||
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW);
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model("KBR")) {
|
||||
// KBR (rb1) launches landscape and draws rotated by itself, don't do any rotation
|
||||
adjust_orientation = false;
|
||||
} else if (avs::game::is_model({"LBR", "MBR"}) && window_name == "D3DProxyWindow") {
|
||||
// do not auto-rotate for second window (D3DProxyWindow)
|
||||
adjust_orientation = false;
|
||||
}
|
||||
|
||||
if ((nHeight > nWidth && adjust_orientation) || GRAPHICS_FORCE_REFRESH > 0) {
|
||||
DEVMODE mode {};
|
||||
|
||||
// get display settings
|
||||
if (EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &mode)) {
|
||||
if (adjust_orientation) {
|
||||
DWORD orientation = GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ? DMDO_90 : DMDO_270;
|
||||
log_misc(
|
||||
"graphics",
|
||||
"auto-rotate: call ChangeDisplaySettings and rotate display to DMDO_xx mode {}, w={}, h={}",
|
||||
orientation, nWidth, nHeight);
|
||||
mode.dmPelsWidth = nWidth;
|
||||
mode.dmPelsHeight = nHeight;
|
||||
mode.dmDisplayOrientation = orientation;
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model({"LBR", "MBR"})) {
|
||||
fix_up_rb_auto_rotate_dimensions(mode.dmPelsWidth, mode.dmPelsHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (GRAPHICS_FORCE_REFRESH > 0) {
|
||||
log_info(
|
||||
"graphics",
|
||||
"call ChangeDisplaySettings to force refresh rate: {} => {} Hz (-graphics-force-refresh)",
|
||||
mode.dmDisplayFrequency,
|
||||
GRAPHICS_FORCE_REFRESH);
|
||||
|
||||
mode.dmDisplayFrequency = GRAPHICS_FORCE_REFRESH;
|
||||
}
|
||||
|
||||
const auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
||||
log_warning("graphics", "ChangeDisplaySettings failed: {}", disp_res);
|
||||
}
|
||||
} else {
|
||||
log_warning("graphics", "failed to get display settings");
|
||||
}
|
||||
}
|
||||
|
||||
// gfdm
|
||||
if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
||||
// set window name
|
||||
@@ -1149,3 +1069,119 @@ std::string graphics_screenshot_genpath() {
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_dmdo_string(DWORD dmdo) {
|
||||
switch (dmdo) {
|
||||
case DMDO_DEFAULT:
|
||||
return "DMDO_DEFAULT";
|
||||
case DMDO_90:
|
||||
return "DMDO_90";
|
||||
case DMDO_180:
|
||||
return "DMDO_180";
|
||||
case DMDO_270:
|
||||
return "DMDO_270";
|
||||
default:
|
||||
return fmt::format("Unknown ({})", dmdo);
|
||||
}
|
||||
}
|
||||
|
||||
void update_monitor_on_boot() {
|
||||
// note: all of this is only being done for the primary motnior
|
||||
|
||||
// get current settings
|
||||
DEVMODEA dm = {};
|
||||
dm.dmSize = sizeof(dm);
|
||||
if (!EnumDisplaySettingsExA(NULL, ENUM_CURRENT_SETTINGS, &dm, 0)) {
|
||||
log_info("graphics", "EnumDisplaySettingsExa failed {}", get_last_error_string());
|
||||
return;
|
||||
}
|
||||
|
||||
bool needs_update = false;
|
||||
|
||||
// convert orientation values, and figure out if resolution needs to be swapped
|
||||
bool rotate_resolution = false;
|
||||
DWORD orientation = DMDO_DEFAULT;
|
||||
switch (GRAPHICS_ADJUST_ORIENTATION) {
|
||||
case ORIENTATION_CW:
|
||||
orientation = DMDO_90;
|
||||
if (dm.dmDisplayOrientation == DMDO_DEFAULT || dm.dmDisplayOrientation == DMDO_180) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
case ORIENTATION_CCW:
|
||||
orientation = DMDO_270;
|
||||
if (dm.dmDisplayOrientation == DMDO_DEFAULT || dm.dmDisplayOrientation == DMDO_180) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
case ORIENTATION_FLIPPED:
|
||||
orientation = DMDO_180;
|
||||
if (dm.dmDisplayOrientation == DMDO_90 || dm.dmDisplayOrientation == DMDO_270) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
orientation = DMDO_DEFAULT;
|
||||
if (dm.dmDisplayOrientation == DMDO_90 || dm.dmDisplayOrientation == DMDO_270) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// update orientation (and resolution if it must be swapped)
|
||||
if (dm.dmDisplayOrientation != orientation) {
|
||||
log_misc("graphics",
|
||||
"current orientation {} => desired orientation {}",
|
||||
get_dmdo_string(dm.dmDisplayOrientation), get_dmdo_string(orientation));
|
||||
|
||||
const DWORD originalWidth = dm.dmPelsWidth;
|
||||
const DWORD originalHeight = dm.dmPelsHeight;
|
||||
|
||||
// change orientation
|
||||
dm.dmDisplayOrientation = orientation;
|
||||
dm.dmFields |= DM_DISPLAYORIENTATION;
|
||||
|
||||
// rotate resolution
|
||||
if (rotate_resolution) {
|
||||
dm.dmPelsWidth = originalHeight;
|
||||
dm.dmPelsHeight = originalWidth;
|
||||
dm.dmFields |= DM_PELSHEIGHT | DM_PELSWIDTH;
|
||||
}
|
||||
|
||||
needs_update = true;
|
||||
}
|
||||
|
||||
// update refresh rate
|
||||
if (GRAPHICS_FORCE_REFRESH > 0) {
|
||||
log_misc("graphics",
|
||||
"current refresh rate {} => desired refresh rate {}",
|
||||
dm.dmDisplayFrequency, GRAPHICS_FORCE_REFRESH);
|
||||
|
||||
dm.dmDisplayFrequency = GRAPHICS_FORCE_REFRESH;
|
||||
dm.dmFields |= DM_DISPLAYFREQUENCY;
|
||||
needs_update = true;
|
||||
}
|
||||
|
||||
if (!needs_update) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
|
||||
if (result != DISP_CHANGE_SUCCESSFUL) {
|
||||
log_fatal(
|
||||
"graphics",
|
||||
"failed to update display settings ({}px x {}px @ {}Hz): {}",
|
||||
dm.dmPelsWidth,
|
||||
dm.dmPelsHeight,
|
||||
dm.dmDisplayFrequency,
|
||||
get_last_error_string());
|
||||
} else {
|
||||
log_info("graphics", "display settings updated successfully ({}px x {}px @ {}Hz)",
|
||||
dm.dmPelsWidth,
|
||||
dm.dmPelsHeight,
|
||||
dm.dmDisplayFrequency);
|
||||
}
|
||||
|
||||
// sleep for a little bit after changing monitor settings to delay game launch
|
||||
Sleep(1000);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ enum graphics_orientation {
|
||||
ORIENTATION_CW = 0,
|
||||
ORIENTATION_CCW = 1,
|
||||
ORIENTATION_NORMAL = 2,
|
||||
ORIENTATION_FLIPPED = 3
|
||||
};
|
||||
|
||||
enum graphics_dx9on12_state {
|
||||
@@ -107,3 +108,5 @@ bool graphics_window_decoration_change_crashes_game();
|
||||
bool graphics_window_resize_breaks_game();
|
||||
bool graphics_window_move_and_resize_breaks_game();
|
||||
void graphics_load_windowed_subscreen_parameters();
|
||||
|
||||
void update_monitor_on_boot();
|
||||
@@ -2133,6 +2133,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
sysutils::print_gpus();
|
||||
}
|
||||
|
||||
// fix up monitor
|
||||
update_monitor_on_boot();
|
||||
|
||||
// initialize raw input
|
||||
RI_MGR = std::make_unique<rawinput::RawInputManager>();
|
||||
for (const auto &device : sextet_devices) {
|
||||
|
||||
@@ -224,11 +224,9 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Graphics (Full Screen)",
|
||||
},
|
||||
{
|
||||
.title = "Force Monitor Refresh Rate",
|
||||
.title = "Monitor Refresh Rate",
|
||||
.name = "graphics-force-refresh",
|
||||
.desc =
|
||||
"Attempt to change the refresh rate for the primary monitor before the game boots; "
|
||||
"works in both full screen and windowed modes, but known to fail for some games.",
|
||||
.desc = "Change the refresh rate for the primary monitor before launching the game. It will be restored on exit.",
|
||||
.type = OptionType::Integer,
|
||||
.category = "Graphics (Common)",
|
||||
},
|
||||
@@ -1847,17 +1845,20 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// spice2x_AutoOrientation
|
||||
.title = "Auto-rotate Display",
|
||||
.title = "Rotate Monitor",
|
||||
.name = "sp2x-autoorientation",
|
||||
.display_name = "autoorientation",
|
||||
.aliases= "autoorientation",
|
||||
.desc = "Automatically adjust the orientation of your display when launched. "
|
||||
"WARNING: game may launch at incorrect refresh rate! Use in combination with "
|
||||
"-graphics-force-refresh and potentially either -9on12 or full-screen optimizations (FSO) to fix.",
|
||||
.desc = "Change the orientation of the primary display before launching the game. It will be restored on exit",
|
||||
.type = OptionType::Enum,
|
||||
.category = "Graphics (Common)",
|
||||
// match graphics_orientation
|
||||
.elements = {{"0", "90 (CW)"}, {"1", "270 (CCW)"}},
|
||||
// match graphics_orientation enum
|
||||
.elements = {
|
||||
{"0", "Portrait"},
|
||||
{"1", "Portrait, Flipped"},
|
||||
{"2", "Landscape"},
|
||||
{"3", "Landscape, Flipped"}
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "AVS Log Level",
|
||||
|
||||
Reference in New Issue
Block a user