graphics: custom full screen resolution (#274)

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

## Description of change
Adds a new option to forcibly set the full screen resolution.

Same idea as -windowscale.

## Compiling
🔺 

## Testing
Seems to be showing similar results as -windowscale:

* Works GREAT for IIDX/SDVX
* popn launches at correct resolution but only draws a small image (at
native res) - can be scaled with F11 menu
* nostalgia is broken in weird ways, image is drawn correctly but bottom
`credits` text draws at wrong offset, game's wintouch logic broken
(perhaps expectedly)

## Other notes
In theory this could enable "play sdvx in landscape mode" option but the
image scaling doesn't quite work with extreme values.
This commit is contained in:
bicarus-dev
2025-03-24 16:07:04 -07:00
committed by GitHub
parent 1b7ebd6fc2
commit 89921e5ec2
8 changed files with 82 additions and 43 deletions
@@ -680,7 +680,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
// dump presentation parameters // dump presentation parameters
for (size_t i = 0; i < num_adapters; i++) { for (size_t i = 0; i < num_adapters; i++) {
const auto *params = &pPresentationParameters[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;
}
log_info("graphics::d3d9", log_info("graphics::d3d9",
"D3D9 presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, " "D3D9 presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
@@ -846,7 +851,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
} }
for (size_t i = 0; i < num_adapters; i++) { for (size_t i = 0; i < num_adapters; i++) {
const auto *params = &pPresentationParameters[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;
}
log_info("graphics::d3d9", log_info("graphics::d3d9",
"D3D9Ex presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, " "D3D9Ex presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
@@ -870,7 +880,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
} }
if (pFullscreenDisplayMode) { if (pFullscreenDisplayMode) {
for (size_t i = 0; i < num_adapters; i++) { for (size_t i = 0; i < num_adapters; i++) {
const auto *fullscreen_display_mode = &pFullscreenDisplayMode[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;
}
log_info("graphics::d3d9", log_info("graphics::d3d9",
"D3D9Ex fullscreen display mode for adapter {}: Width: {}, Height: {}, RefreshRate: {}, " "D3D9Ex fullscreen display mode for adapter {}: Width: {}, Height: {}, RefreshRate: {}, "
+1
View File
@@ -67,6 +67,7 @@ graphics_dx9on12_state GRAPHICS_9_ON_12_STATE = DX9ON12_AUTO;
bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false; bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
bool SUBSCREEN_FORCE_REDRAW = false; bool SUBSCREEN_FORCE_REDRAW = false;
bool D3D9_DEVICE_HOOK_DISABLE = false; bool D3D9_DEVICE_HOOK_DISABLE = false;
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
// settings // settings
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146"; std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
+1
View File
@@ -35,6 +35,7 @@ extern UINT GRAPHICS_FORCE_REFRESH;
extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER; extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER; extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW; extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE; extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE;
extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME; extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME;
@@ -123,10 +123,6 @@ void graphics_load_windowed_parameters() {
} }
log_debug("graphics-windowed", "graphics_load_windowed_parameters called"); log_debug("graphics-windowed", "graphics_load_windowed_parameters called");
const auto remove_spaces = [](const char& c) {
return c == ' ';
};
if (GRAPHICS_WINDOW_STYLE.has_value()) { if (GRAPHICS_WINDOW_STYLE.has_value()) {
log_debug( log_debug(
"graphics-windowed", "graphics-windowed",
@@ -148,13 +144,11 @@ void graphics_load_windowed_parameters() {
log_debug( log_debug(
"graphics-windowed", "graphics-windowed",
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_POS"); "graphics_load_windowed_parameters - load GRAPHICS_WINDOW_POS");
int32_t x, y; std::pair<uint32_t, uint32_t> result;
auto s = GRAPHICS_WINDOW_POS.value(); if (parse_width_height(GRAPHICS_WINDOW_POS.value(), result)) {
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
if (sscanf(s.c_str(), "%d,%d", &x, &y) == 2) {
cfg::SCREENRESIZE->enable_window_resize = true; cfg::SCREENRESIZE->enable_window_resize = true;
cfg::SCREENRESIZE->window_offset_x = x; cfg::SCREENRESIZE->window_offset_x = result.first;
cfg::SCREENRESIZE->window_offset_y = y; cfg::SCREENRESIZE->window_offset_y = result.second;
} else { } else {
log_warning("graphics-windowed", "failed to parse -windowpos"); log_warning("graphics-windowed", "failed to parse -windowpos");
} }
@@ -174,20 +168,16 @@ void graphics_load_windowed_subscreen_parameters() {
} }
log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called"); log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called");
const auto remove_spaces = [](const char& c) {
return c == ' ';
};
if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) { if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) {
log_debug( log_debug(
"graphics-windowed", "graphics-windowed",
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE"); "graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE");
uint32_t w, h;
auto s = GRAPHICS_IIDX_WSUB_SIZE.value(); std::pair<uint32_t, uint32_t> result;
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end()); if (parse_width_height(GRAPHICS_IIDX_WSUB_SIZE.value(), result)) {
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) { GRAPHICS_IIDX_WSUB_WIDTH = result.first;
GRAPHICS_IIDX_WSUB_WIDTH = w; GRAPHICS_IIDX_WSUB_HEIGHT = result.second;
GRAPHICS_IIDX_WSUB_HEIGHT = h;
} else { } else {
log_warning("graphics-windowed", "failed to parse -wsubsize"); log_warning("graphics-windowed", "failed to parse -wsubsize");
} }
@@ -197,12 +187,11 @@ void graphics_load_windowed_subscreen_parameters() {
log_debug( log_debug(
"graphics-windowed", "graphics-windowed",
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS"); "graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS");
int32_t x, y;
auto s = GRAPHICS_IIDX_WSUB_POS.value(); std::pair<uint32_t, uint32_t> result;
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end()); if (parse_width_height(GRAPHICS_IIDX_WSUB_POS.value(), result)) {
if (sscanf(s.c_str(), "%d,%d", &x, &y) == 2) { GRAPHICS_IIDX_WSUB_X = result.first;
GRAPHICS_IIDX_WSUB_X = x; GRAPHICS_IIDX_WSUB_Y = result.second;
GRAPHICS_IIDX_WSUB_Y = y;
} else { } else {
log_warning("graphics-windowed", "failed to parse -wsubpos"); log_warning("graphics-windowed", "failed to parse -wsubpos");
} }
+13 -6
View File
@@ -102,6 +102,7 @@
#include "util/sysutils.h" #include "util/sysutils.h"
#include "util/tapeled.h" #include "util/tapeled.h"
#include "util/time.h" #include "util/time.h"
#include "util/utils.h"
#include "avs/ssl.h" #include "avs/ssl.h"
#include "nvapi/nvapi.h" #include "nvapi/nvapi.h"
#include "hooks/graphics/nvapi_hook.h" #include "hooks/graphics/nvapi_hook.h"
@@ -366,6 +367,15 @@ int main_implementation(int argc, char *argv[]) {
GRAPHICS_FORCE_SINGLE_ADAPTER = false; GRAPHICS_FORCE_SINGLE_ADAPTER = false;
} }
if (options[launcher::Options::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
} else {
log_warning("launcher", "failed to parse -forceres");
}
}
if (options[launcher::Options::spice2x_NvapiProfile].value_bool() && !cfg::CONFIGURATOR_STANDALONE) { if (options[launcher::Options::spice2x_NvapiProfile].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
nvapi::ADD_PROFILE = true; nvapi::ADD_PROFILE = true;
} }
@@ -952,12 +962,9 @@ int main_implementation(int argc, char *argv[]) {
GRAPHICS_WINDOW_STYLE = options[launcher::Options::spice2x_WindowBorder].value_uint32(); GRAPHICS_WINDOW_STYLE = options[launcher::Options::spice2x_WindowBorder].value_uint32();
} }
if (options[launcher::Options::spice2x_WindowSize].is_active()) { if (options[launcher::Options::spice2x_WindowSize].is_active()) {
auto s = options[launcher::Options::spice2x_WindowSize].value_text(); std::pair<uint32_t, uint32_t> result;
uint32_t w, h; if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
const auto remove_spaces = [](const char& c) { return c == ' '; }; GRAPHICS_WINDOW_SIZE = result;
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
GRAPHICS_WINDOW_SIZE = std::pair(w, h);
} else { } else {
log_warning("launcher", "failed to parse -windowsize"); log_warning("launcher", "failed to parse -windowsize");
} }
+19 -7
View File
@@ -189,6 +189,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Integer, .type = OptionType::Integer,
.category = "Graphics (Common)", .category = "Graphics (Common)",
}, },
{
.title = "Only Use One Monitor",
.name = "graphics-force-single-adapter",
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
.type = OptionType::Bool,
.category = "Graphics (Common)",
},
{ {
.title = "Force Refresh Rate", .title = "Force Refresh Rate",
.name = "graphics-force-refresh", .name = "graphics-force-refresh",
@@ -197,12 +205,16 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Graphics (Common)", .category = "Graphics (Common)",
}, },
{ {
.title = "Only Use One Monitor", // FullscreenResolution
.name = "graphics-force-single-adapter", .title = "Force Full Screen Resolution",
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n" .name = "forceres",
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!", .desc =
.type = OptionType::Bool, "For full screen mode, forcibly set a custom resolution.\n\n"
.category = "Graphics (Common)", "Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
"This should only be used as last resort if your GPU/monitor can't display the resolution required by the game",
.type = OptionType::Text,
.setting_name = "1280,720",
.category = "Graphics (Common)"
}, },
{ {
// Graphics9On12 // Graphics9On12
@@ -1783,7 +1795,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.title = "Window Forced Render Scaling", .title = "Window Forced Render Scaling",
.name = "windowscale", .name = "windowscale",
.desc = "For windowed mode: forcibly set DX9 back buffer dimensions to match window size. " .desc = "For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
"Reduces pixelated scaling artifacts. Works great on some games, but completely broken on others", "Reduces pixelated scaling artifacts. Works great for some games, but can COMPLETELY BREAK other games - YMMV!",
.type = OptionType::Bool, .type = OptionType::Bool,
.category = "Graphics (Windowed)", .category = "Graphics (Windowed)",
}, },
+2 -1
View File
@@ -27,8 +27,9 @@ namespace launcher {
CaptureCursor, CaptureCursor,
ShowCursor, ShowCursor,
DisplayAdapter, DisplayAdapter,
GraphicsForceRefresh,
GraphicsForceSingleAdapter, GraphicsForceSingleAdapter,
GraphicsForceRefresh,
FullscreenResolution,
Graphics9On12, Graphics9On12,
spice2x_Dx9On12, spice2x_Dx9On12,
NoLegacy, NoLegacy,
+13
View File
@@ -298,3 +298,16 @@ static inline int get_async_secondary_mouse() {
int vk = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON; int vk = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON;
return GetAsyncKeyState(vk); return GetAsyncKeyState(vk);
} }
static inline bool parse_width_height(const std::string wh, std::pair<uint32_t, uint32_t> &result) {
std::string s = wh;
uint32_t w, h;
const auto remove_spaces = [](const char& c) { return c == ' '; };
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
result = std::pair(w, h);
return true;
} else {
return false;
}
}