sdvx: landscape mode (#277)

## Link to GitHub Issue, if one exists
n/a

## Description of change
Add `SDVX Landscape Mode` option, allowing you to play SDVX in landscape
monitor orientation, with black borders on left and right - similar to
how EAC does it. This is meant to be an alternative to what people do
today, which is launching in windowed mode.

By default, with just `-sdvxlandscape` enabled, EG will launch at
1920x1080 (transposed from the original 1080x1920 resolution).

It is possible to combine with `-forceres` to launch at higher
resolutions. At some point though, when rendering at large resolutions
past 1440p, at some point scaling may break due to #276.

Disabled on 32-bit SDVX since dx9 `CreateDeviceEx` call is required;
`CreateDevice` won't work. For the same reason this isn't being made a
generic option for museca/rb/etc. In theory you can supply
`-sdvxlandscape` to games like IIDX and play landscape games in portrait
mode, although again, scaling my break due to #276.

Also, add new options (bottomleft/right) for subscreen overlay position
so that they don't overlap with the main screen in landscape mode.

## Compiling
🙂

## Testing
Tested a couple versions of VW and EG.
This commit is contained in:
bicarus-dev
2025-03-26 20:21:05 -07:00
committed by GitHub
parent bf79b5d2aa
commit 45a52cff90
9 changed files with 148 additions and 52 deletions
@@ -853,9 +853,13 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
for (size_t i = 0; i < num_adapters; i++) {
auto *params = &pPresentationParameters[i];
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
if (!GRAPHICS_WINDOWED && i == 0) {
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
} else if (GRAPHICS_FS_FORCE_LANDSCAPE) {
std::swap(params->BackBufferWidth, params->BackBufferHeight);
}
}
log_info("graphics::d3d9",
@@ -882,10 +886,15 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
for (size_t i = 0; i < num_adapters; i++) {
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
if (!GRAPHICS_WINDOWED && i == 0) {
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
} else if (GRAPHICS_FS_FORCE_LANDSCAPE) {
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
}
}
log_info("graphics::d3d9",
"D3D9Ex fullscreen display mode for adapter {}: Width: {}, Height: {}, RefreshRate: {}, "
@@ -718,7 +718,37 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
const int rectTop = 576;
const int w = param.BackBufferWidth;
const int h = param.BackBufferHeight;
D3DLOCKED_RECT rect;
HRESULT hr;
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
if (GRAPHICS_FS_FORCE_LANDSCAPE) {
const int w_new = h * 9 / 16;
const int x_new = rectLeft + ((w - w_new) / 2);
RECT originRect {
x_new,
rectTop,
(LONG)(x_new + w_new),
(LONG)(rectTop + h),
};
hr = pReal->StretchRect(
backbuffer, nullptr,
topSurface, &originRect,
D3DTEXF_LINEAR);
if (hr != D3D_OK) {
log_misc("graphics::d3d9", "StretchRect backbuffer failed (forced landscape)");
}
} else {
// stretch to add top/left offset to avoid going negative
hr = pReal->StretchRect(
backbuffer, nullptr,
topSurface, nullptr,
D3DTEXF_LINEAR);
if (hr != D3D_OK) {
log_misc("graphics::d3d9", "StretchRect backbuffer failed");
}
}
topSurface->UnlockRect();
RECT targetRect {
rectLeft,
@@ -727,44 +757,38 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
(LONG)(rectTop + h),
};
// stretch to add top/left offset to avoid going negative
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
auto hr = pReal->StretchRect(
backbuffer, nullptr,
topSurface, &targetRect,
D3DTEXF_LINEAR);
if (hr != D3D_OK) {
log_misc("graphics::d3d9", "StretchRect backbuffer failed");
}
topSurface->UnlockRect();
// do the actual zoom / offset math
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
if (scene.centered) {
targetRect.right = (w + rectLeft) / scene.scale_x;
targetRect.bottom = (h + rectTop) / scene.scale_y;
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
targetRect.top -= deltaH;
targetRect.bottom -= deltaH;
targetRect.left -= deltaW;
targetRect.right -= deltaW;
} else {
targetRect.left -= scene.offset_x;
targetRect.top += scene.offset_y;
targetRect.right = -scene.offset_x;
targetRect.right += (w + rectLeft) / scene.scale_x;
targetRect.bottom = scene.offset_y;
targetRect.bottom += (h + rectTop) / scene.scale_y;
if (cfg::SCREENRESIZE->enable_screen_resize) {
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
if (scene.centered) {
targetRect.right = (w + rectLeft) / scene.scale_x;
targetRect.bottom = (h + rectTop) / scene.scale_y;
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
targetRect.top -= deltaH;
targetRect.bottom -= deltaH;
targetRect.left -= deltaW;
targetRect.right -= deltaW;
} else {
targetRect.left -= scene.offset_x;
targetRect.top += scene.offset_y;
targetRect.right = -scene.offset_x;
targetRect.right += (w + rectLeft) / scene.scale_x;
targetRect.bottom = scene.offset_y;
targetRect.bottom += (h + rectTop) / scene.scale_y;
}
}
// draw to back buffer
backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
bool use_linear_filter = true;
if (cfg::SCREENRESIZE->enable_screen_resize) {
use_linear_filter = cfg::SCREENRESIZE->enable_linear_filter;
}
hr = pReal->StretchRect(
topSurface, &targetRect,
backbuffer, nullptr,
cfg::SCREENRESIZE->enable_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
backbuffer->UnlockRect();
if (hr != D3D_OK) {
log_misc("graphics::d3d9", "StretchRect targetRect failed");
@@ -774,7 +798,7 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EndScene() {
WRAP_DEBUG;
if (cfg::SCREENRESIZE->enable_screen_resize) {
if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_FORCE_LANDSCAPE) {
SurfaceHook(pReal);
}
+1
View File
@@ -68,6 +68,7 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
bool SUBSCREEN_FORCE_REDRAW = false;
bool D3D9_DEVICE_HOOK_DISABLE = false;
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
bool GRAPHICS_FS_FORCE_LANDSCAPE = false;
// settings
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
+1
View File
@@ -36,6 +36,7 @@ extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
extern bool GRAPHICS_FS_FORCE_LANDSCAPE;
extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE;
extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME;