From 104a9cbffd212fab08f5c145ec723e37813b5ee3 Mon Sep 17 00:00:00 2001 From: bicarus-dev <202771338+bicarus-dev@users.noreply.github.com> Date: Fri, 28 Mar 2025 17:23:01 -0700 Subject: [PATCH] graphics: rewrite image scaler (#278) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Link to GitHub Issue, if one exists Fixes #276 ## Description of change Note: this change is not compatible with previous screen resize config; i.e., if you had the zoom setting dialed into a certain view, after this change it won't be the same area, so you'll have to set up a scene again. I think this is a worthwhile cost since the old logic is just broken in weird ways, and preserving old settings is really tedious math (for a feature that not many people use). Besides, this change will only use the new Scenes values from the JSON. Rewrite the DX9 image scaler logic. * Previously, the rendering surface was fixed at `4096*4096 px`. Now, this is relative to the backbuffer dimensions, which depends on the game & respects user provided `-forceres`. * Remove "center" option, make it the default. Above two changes fix the aspect ratio issue (scaling in portrait games not respecting screen ratio) and things breaking at higher res (1080p when zoomed out far enough, or when forced to run at 4k resolution for example) * Use `scenes` leaf of screen resize JSON config for Scene 1 as well, completely removing ourselves from being compatible with older spice2x versions for these values. * Add additional bounds check before calling `StretchRect` to avoid users getting stuck with a bad layout, which can kill performance. ## Compiling 🦾 ## Testing Tested 32 bit (popn / ddr), 64 bit (ldj), portrait and landscape modes (kfc) --- src/spice2x/cfg/screen_resize.cpp | 12 +- src/spice2x/cfg/screen_resize.h | 1 - .../graphics/backends/d3d9/d3d9_backend.cpp | 45 ++++- .../graphics/backends/d3d9/d3d9_device.cpp | 173 ++++++++++++------ src/spice2x/hooks/graphics/graphics.cpp | 4 +- src/spice2x/hooks/graphics/graphics.h | 4 +- src/spice2x/launcher/launcher.cpp | 5 +- src/spice2x/launcher/options.cpp | 16 +- src/spice2x/launcher/options.h | 1 + src/spice2x/overlay/windows/screen_resize.cpp | 24 ++- src/spice2x/overlay/windows/sdvx_sub.cpp | 2 +- 11 files changed, 200 insertions(+), 87 deletions(-) diff --git a/src/spice2x/cfg/screen_resize.cpp b/src/spice2x/cfg/screen_resize.cpp index 10956e8..d31040c 100644 --- a/src/spice2x/cfg/screen_resize.cpp +++ b/src/spice2x/cfg/screen_resize.cpp @@ -81,16 +81,12 @@ namespace cfg { load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter); for (size_t i = 0; i < std::size(this->scene_settings); i++) { auto& scene = this->scene_settings[i]; - std::string prefix = ""; - if (0 < i) { - prefix += fmt::format("scenes/{}/", i-1); - } + const std::string prefix = fmt::format("scenes/{}/", i); load_int_value(doc, root + prefix + "offset_x", scene.offset_x); load_int_value(doc, root + prefix + "offset_y", scene.offset_y); load_float_value(doc, root + prefix + "scale_x", scene.scale_x); load_float_value(doc, root + prefix + "scale_y", scene.scale_y); load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio); - load_bool_value(doc, root + prefix + "centered", scene.centered); } // windowed settings are always under game settings @@ -201,16 +197,12 @@ namespace cfg { rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter); for (size_t i = 0; i < std::size(this->scene_settings); i++) { auto& scene = this->scene_settings[i]; - std::string prefix = ""; - if (0 < i) { - prefix += fmt::format("scenes/{}/", i-1); - } + const std::string prefix = fmt::format("scenes/{}/", i); rapidjson::Pointer(root + prefix + "offset_x").Set(doc, scene.offset_x); rapidjson::Pointer(root + prefix + "offset_y").Set(doc, scene.offset_y); rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x); rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y); rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio); - rapidjson::Pointer(root + prefix + "centered").Set(doc, scene.centered); } // windowed mode settings diff --git a/src/spice2x/cfg/screen_resize.h b/src/spice2x/cfg/screen_resize.h index 103ae77..dd60cc6 100644 --- a/src/spice2x/cfg/screen_resize.h +++ b/src/spice2x/cfg/screen_resize.h @@ -20,7 +20,6 @@ namespace cfg { float scale_x = 1.0; float scale_y = 1.0; bool keep_aspect_ratio = true; - bool centered = true; }; extern std::optional SCREEN_RESIZE_CFG_PATH_OVERRIDE; diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp index 9ce8916..bf88b88 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp @@ -681,10 +681,27 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice( // dump presentation parameters 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) { + GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth; + GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight; + log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT); + if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { + log_misc( + "graphics::d3d9", + "use custom resolution {}x{} => {}x{}", + params->BackBufferWidth, params->BackBufferHeight, + GRAPHICS_FS_CUSTOM_RESOLUTION.value().first, + GRAPHICS_FS_CUSTOM_RESOLUTION.value().second); + params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; + params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; + } else if (GRAPHICS_FS_ORIENTATION_SWAP) { + log_misc( + "graphics::d3d9", + "swap full orientation {}x{} => {}x{}", + params->BackBufferWidth, params->BackBufferHeight, + params->BackBufferHeight, params->BackBufferWidth); + std::swap(params->BackBufferWidth, params->BackBufferHeight); + } } log_info("graphics::d3d9", @@ -852,12 +869,25 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx( for (size_t i = 0; i < num_adapters; i++) { auto *params = &pPresentationParameters[i]; - if (!GRAPHICS_WINDOWED && i == 0) { + GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth; + GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight; + log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT); if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { + log_misc( + "graphics::d3d9", + "use custom resolution {}x{} => {}x{}", + params->BackBufferWidth, params->BackBufferHeight, + GRAPHICS_FS_CUSTOM_RESOLUTION.value().first, + GRAPHICS_FS_CUSTOM_RESOLUTION.value().second); params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; - } else if (GRAPHICS_FS_FORCE_LANDSCAPE) { + } else if (GRAPHICS_FS_ORIENTATION_SWAP) { + log_misc( + "graphics::d3d9", + "swap full orientation {}x{} => {}x{}", + params->BackBufferWidth, params->BackBufferHeight, + params->BackBufferHeight, params->BackBufferWidth); std::swap(params->BackBufferWidth, params->BackBufferHeight); } } @@ -890,11 +920,10 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx( 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) { + } else if (GRAPHICS_FS_ORIENTATION_SWAP) { std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height); } } - log_info("graphics::d3d9", "D3D9Ex fullscreen display mode for adapter {}: Width: {}, Height: {}, RefreshRate: {}, " diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp index 7dbdf0d..bb50c16 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp @@ -7,6 +7,7 @@ #include "hooks/graphics/graphics.h" #include "overlay/overlay.h" #include "util/flags_helper.h" +#include "util/utils.h" #include "cfg/screen_resize.h" #include "d3d9_backend.h" @@ -692,94 +693,159 @@ static IDirect3DSurface9 *backbuffer = nullptr; static LPDIRECT3DSWAPCHAIN9 mSwapChain = nullptr; static IDirect3DTexture9* tex; +static UINT topSurface_width = 0; +static UINT topSurface_height = 0; +static float topSurface_aspect_ratio = 1.f; + void SurfaceHook(IDirect3DDevice9 *pReal) { - // log_misc("graphics::d3d9", "SurfaceHook called"); D3DPRESENT_PARAMETERS param {}; pReal->GetSwapChain(0, &mSwapChain); mSwapChain->GetPresentParameters(¶m); + + // phase 0 - create a surface that has the same aspect ratio as the original back buffer + // but larger in size. this is needed to ensure that when the user zooms out, we have + // sufficient buffer space (black border) around the original image + // + // (only done once) if (!topSurface) { - if (pReal->CreateTexture(4096, 4096, 1, + topSurface_width = param.BackBufferWidth * 3; + topSurface_height = param.BackBufferHeight * 3; + topSurface_aspect_ratio = (float)topSurface_width / (float)topSurface_height; + if (pReal->CreateTexture(topSurface_width, topSurface_height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &tex, NULL) != D3D_OK) { - log_misc("graphics::d3d9", "create texture failed"); - + log_warning("graphics::d3d9", "SurfaceHook - create texture failed"); } - log_misc("graphics::d3d9", "Backbuffer: {} {} {}", - param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount); - tex->GetSurfaceLevel(0, &topSurface); + log_misc( + "graphics::d3d9", "SurfaceHook - Backbuffer: {} {} {}", + param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount); + + tex->GetSurfaceLevel(0, &topSurface); if (mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backbuffer) != D3D_OK) { - log_misc("graphics::d3d9", "GetBackBuffer failed"); + log_warning("graphics::d3d9", "SurfaceHook - GetBackBuffer failed"); } } - const int rectLeft = 1024; - const int rectTop = 576; + // pre-calculate dimensions used for phase 1 + const int rectLeft = param.BackBufferWidth; + const int rectTop = param.BackBufferHeight; 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( + + // phase 1 - copy the original back buffer onto the new surface. + // we draw it 1:1 in the center of the surface. + // if orientation is swapped, fix up the rect and draw it in the center. + // + // for this phase we always render with the same rect so that it's always overwritten + // by a new frame on the same spot. + RECT originRect { + rectLeft, + rectTop, + (LONG)(rectLeft + w), + (LONG)(rectTop + h), + }; + if (GRAPHICS_FS_ORIENTATION_SWAP) { + originRect.left = (topSurface_width / 2) - (GRAPHICS_FS_ORIGINAL_WIDTH / 2); + originRect.top = (topSurface_height / 2) - (GRAPHICS_FS_ORIGINAL_HEIGHT / 2); + originRect.right = originRect.left + GRAPHICS_FS_ORIGINAL_WIDTH; + originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT; + } + + // log_misc( + // "graphics::d3d9", "originRect: {} {} {} {}", + // originRect.left, originRect.top, originRect.right, originRect.bottom); + + 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"); - } + if (hr != D3D_OK) { + log_warning( + "graphics::d3d9", + "SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}", + originRect.left, originRect.top, originRect.right, originRect.bottom); } topSurface->UnlockRect(); + // phase 2 - viewport calculation - do the actual zoom / offset math + // figure out what region of the surface to copy back to the back buffer. RECT targetRect { rectLeft, rectTop, (LONG)(rectLeft + w), (LONG)(rectTop + h), }; - - // do the actual zoom / offset math 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; + auto w_new = w / scene.scale_x; + auto h_new = h / scene.scale_y; + + // make sure the scaling is within bounds + if (cfg::SCREENRESIZE->client_keep_aspect_ratio) { + if (w_new > topSurface_width || h_new > topSurface_height) { + w_new = topSurface_width; + h_new = topSurface_height; + } } 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; + w_new = MIN(w_new, topSurface_width); + h_new = MIN(h_new, topSurface_height); + } + + targetRect.left = (topSurface_width / 2) - (w_new / 2) - scene.offset_x; + targetRect.top = (topSurface_height / 2) - (h_new / 2) - scene.offset_y; + targetRect.right = targetRect.left + w_new; + targetRect.bottom = targetRect.top + h_new; + + // boundary check to prevent StretchRect failures + if (targetRect.left < 0) { + targetRect.left = 0; + targetRect.right = w_new; + } else if (targetRect.right > (LONG)topSurface_width) { + targetRect.left = topSurface_width - w_new; + targetRect.right = topSurface_width; + } + if (targetRect.top < 0) { + targetRect.top = 0; + targetRect.bottom = h_new; + } else if (targetRect.bottom > (LONG)topSurface_height) { + targetRect.top = topSurface_height - h_new; + targetRect.bottom = topSurface_height; + } + + } else if (GRAPHICS_FS_ORIENTATION_SWAP) { + // increase the viewport to fit the image on screen (effectively, zoom out a little) + if (GRAPHICS_FS_ORIGINAL_WIDTH < GRAPHICS_FS_ORIGINAL_HEIGHT) { + // portrait game on landscape display + // height should match the original image + targetRect.top = originRect.top; + targetRect.bottom = originRect.bottom; + + // width of viewport should be wider, effectively shrinking the image on x-axis when rendered + const auto w_new = GRAPHICS_FS_ORIGINAL_HEIGHT * topSurface_aspect_ratio; + targetRect.left = (topSurface_width / 2) - (w_new / 2); + targetRect.right = targetRect.left + w_new; + } else { + // landscape game on portrait display + targetRect.left = originRect.left; + targetRect.right = originRect.right; + + // height of viewport should be taller, effectively shrinking the image on y-axis when rendered + const auto h_new = GRAPHICS_FS_ORIGINAL_WIDTH / topSurface_aspect_ratio; + targetRect.top = (topSurface_height / 2) - (h_new / 2); + targetRect.bottom = targetRect.top + h_new; } } - // draw to back buffer + // log_misc("graphics::d3d9", "targetRect: {} {} {} {}", + // targetRect.left, targetRect.top, targetRect.right, targetRect.bottom); + + // phase 3 - draw the surface to back buffer backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT); bool use_linear_filter = true; if (cfg::SCREENRESIZE->enable_screen_resize) { @@ -791,14 +857,17 @@ void SurfaceHook(IDirect3DDevice9 *pReal) { use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE); backbuffer->UnlockRect(); if (hr != D3D_OK) { - log_misc("graphics::d3d9", "StretchRect targetRect failed"); + log_warning( + "graphics::d3d9", + "SurfaceHook - StretchRect targetRect failed, you must reset image scaling to default values! rect: {} {} {} {}", + targetRect.left, targetRect.top, targetRect.right, targetRect.bottom); } } HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EndScene() { WRAP_DEBUG; - if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_FORCE_LANDSCAPE) { + if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_ORIENTATION_SWAP) { SurfaceHook(pReal); } diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index 128855d..9aa0521 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -68,7 +68,9 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false; bool SUBSCREEN_FORCE_REDRAW = false; bool D3D9_DEVICE_HOOK_DISABLE = false; std::optional> GRAPHICS_FS_CUSTOM_RESOLUTION; -bool GRAPHICS_FS_FORCE_LANDSCAPE = false; +bool GRAPHICS_FS_ORIENTATION_SWAP = false; +uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0; +uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0; // settings std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146"; diff --git a/src/spice2x/hooks/graphics/graphics.h b/src/spice2x/hooks/graphics/graphics.h index 907c9e0..8c80159 100644 --- a/src/spice2x/hooks/graphics/graphics.h +++ b/src/spice2x/hooks/graphics/graphics.h @@ -36,7 +36,9 @@ extern std::optional GRAPHICS_FORCE_VSYNC_BUFFER; extern bool GRAPHICS_FORCE_SINGLE_ADAPTER; extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW; extern std::optional> GRAPHICS_FS_CUSTOM_RESOLUTION; -extern bool GRAPHICS_FS_FORCE_LANDSCAPE; +extern bool GRAPHICS_FS_ORIENTATION_SWAP; +extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH; +extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT; extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE; extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 5d4c21b..52dd72e 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1167,11 +1167,14 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) { #if SPICE64 - GRAPHICS_FS_FORCE_LANDSCAPE = true; + GRAPHICS_FS_ORIENTATION_SWAP = true; #else log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring..."); #endif } + if (options[launcher::Options::FullscreenOrientationFlip].value_bool() && !GRAPHICS_WINDOWED) { + GRAPHICS_FS_ORIENTATION_SWAP = true; + } // deleted options if (options[launcher::Options::OpenKFControl].value_bool()) { diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index edc8421..93d3fe0 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -218,6 +218,17 @@ static const std::vector OPTION_DEFINITIONS = { .setting_name = "1280,720", .category = "Graphics (Full Screen)" }, + { + // FullscreenOrientationFlip + .title = "Full Screen Orientation Swap", + .name = "forceresswap", + .desc = + "Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n" + "Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n" + "Strongly consider combining this with -forceres option to render at monitor native resolution", + .type = OptionType::Bool, + .category = "Graphics (Full Screen)" + }, { // Graphics9On12 .title = "DirectX 9 on 12 (DEPRECATED - use -dx9on12 instead)", @@ -2039,9 +2050,8 @@ static const std::vector OPTION_DEFINITIONS = { .name = "sdvxlandscape", .desc = "Allows you to play in landscape by transposing resolution and applying image scaling.\n\n" - "Only for SDVX5 and above!\n\n" - "Can also be combined with -forceres option to render at larger or smaller resolution, " - "and with image resize option to zoom into certain areas of the screen", + "Works only for SDVX5 and above! This is identical to -forceorientation.\n\n" + "Will launch at 1080p by default; strongly consider combining this with -forceres option to render at monitor native resolution", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options" diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index e148d79..3f6ae1d 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -30,6 +30,7 @@ namespace launcher { GraphicsForceSingleAdapter, GraphicsForceRefresh, FullscreenResolution, + FullscreenOrientationFlip, Graphics9On12, spice2x_Dx9On12, NoLegacy, diff --git a/src/spice2x/overlay/windows/screen_resize.cpp b/src/spice2x/overlay/windows/screen_resize.cpp index 03143ef..ed242aa 100644 --- a/src/spice2x/overlay/windows/screen_resize.cpp +++ b/src/spice2x/overlay/windows/screen_resize.cpp @@ -51,7 +51,6 @@ namespace overlay::windows { for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) { auto& scene = cfg::SCREENRESIZE->scene_settings[i]; scene.keep_aspect_ratio = true; - scene.centered = true; scene.offset_x = 0; scene.offset_y = 0; scene.scale_x = 1.f; @@ -126,21 +125,28 @@ namespace overlay::windows { auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene]; // general settings - ImGui::Checkbox("Centered", &scene.centered); - if (!scene.centered) { - ImGui::InputInt("X Offset", &scene.offset_x); - ImGui::InputInt("Y Offset", &scene.offset_y); - } + ImGui::InputInt("X Offset", &scene.offset_x); + ImGui::SameLine(); + ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly."); + ImGui::InputInt("Y Offset", &scene.offset_y); + ImGui::SameLine(); + ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly."); // aspect ratio ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio); if (scene.keep_aspect_ratio) { - if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.65f, 2.0f)) { + if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.5f, 2.5f)) { scene.scale_y = scene.scale_x; } + ImGui::SameLine(); + ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value."); } else { - ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.65f, 2.0f); - ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.65f, 2.0f); + ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.5f, 2.5f); + ImGui::SameLine(); + ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value."); + ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.5f, 2.5f); + ImGui::SameLine(); + ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value."); } ImGui::EndDisabled(); diff --git a/src/spice2x/overlay/windows/sdvx_sub.cpp b/src/spice2x/overlay/windows/sdvx_sub.cpp index 3d2d6a1..9e128bc 100644 --- a/src/spice2x/overlay/windows/sdvx_sub.cpp +++ b/src/spice2x/overlay/windows/sdvx_sub.cpp @@ -35,7 +35,7 @@ namespace overlay::windows { default: this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f); this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight(); - if (GRAPHICS_FS_FORCE_LANDSCAPE) { + if (GRAPHICS_FS_ORIENTATION_SWAP) { this->init_size.x /= 2; this->init_size.y /= 2; }