mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
graphics: rewrite image scaler (#278)
## 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)
This commit is contained in:
@@ -81,16 +81,12 @@ namespace cfg {
|
|||||||
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||||
auto& scene = this->scene_settings[i];
|
auto& scene = this->scene_settings[i];
|
||||||
std::string prefix = "";
|
const std::string prefix = fmt::format("scenes/{}/", i);
|
||||||
if (0 < i) {
|
|
||||||
prefix += fmt::format("scenes/{}/", i-1);
|
|
||||||
}
|
|
||||||
load_int_value(doc, root + prefix + "offset_x", scene.offset_x);
|
load_int_value(doc, root + prefix + "offset_x", scene.offset_x);
|
||||||
load_int_value(doc, root + prefix + "offset_y", scene.offset_y);
|
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_x", scene.scale_x);
|
||||||
load_float_value(doc, root + prefix + "scale_y", scene.scale_y);
|
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 + "keep_aspect_ratio", scene.keep_aspect_ratio);
|
||||||
load_bool_value(doc, root + prefix + "centered", scene.centered);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// windowed settings are always under game settings
|
// 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);
|
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
|
||||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||||
auto& scene = this->scene_settings[i];
|
auto& scene = this->scene_settings[i];
|
||||||
std::string prefix = "";
|
const std::string prefix = fmt::format("scenes/{}/", i);
|
||||||
if (0 < i) {
|
|
||||||
prefix += fmt::format("scenes/{}/", i-1);
|
|
||||||
}
|
|
||||||
rapidjson::Pointer(root + prefix + "offset_x").Set(doc, scene.offset_x);
|
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 + "offset_y").Set(doc, scene.offset_y);
|
||||||
rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x);
|
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 + "scale_y").Set(doc, scene.scale_y);
|
||||||
rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio);
|
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
|
// windowed mode settings
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ namespace cfg {
|
|||||||
float scale_x = 1.0;
|
float scale_x = 1.0;
|
||||||
float scale_y = 1.0;
|
float scale_y = 1.0;
|
||||||
bool keep_aspect_ratio = true;
|
bool keep_aspect_ratio = true;
|
||||||
bool centered = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
||||||
|
|||||||
@@ -681,10 +681,27 @@ 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++) {
|
||||||
auto *params = &pPresentationParameters[i];
|
auto *params = &pPresentationParameters[i];
|
||||||
|
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||||
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
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",
|
log_info("graphics::d3d9",
|
||||||
@@ -852,12 +869,25 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
|
|
||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
auto *params = &pPresentationParameters[i];
|
auto *params = &pPresentationParameters[i];
|
||||||
|
|
||||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
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()) {
|
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->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
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);
|
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -890,11 +920,10 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
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);
|
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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: {}, "
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
#include "util/flags_helper.h"
|
#include "util/flags_helper.h"
|
||||||
|
#include "util/utils.h"
|
||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
|
|
||||||
#include "d3d9_backend.h"
|
#include "d3d9_backend.h"
|
||||||
@@ -692,94 +693,159 @@ static IDirect3DSurface9 *backbuffer = nullptr;
|
|||||||
static LPDIRECT3DSWAPCHAIN9 mSwapChain = nullptr;
|
static LPDIRECT3DSWAPCHAIN9 mSwapChain = nullptr;
|
||||||
static IDirect3DTexture9* tex;
|
static IDirect3DTexture9* tex;
|
||||||
|
|
||||||
|
static UINT topSurface_width = 0;
|
||||||
|
static UINT topSurface_height = 0;
|
||||||
|
static float topSurface_aspect_ratio = 1.f;
|
||||||
|
|
||||||
void SurfaceHook(IDirect3DDevice9 *pReal) {
|
void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||||
// log_misc("graphics::d3d9", "SurfaceHook called");
|
|
||||||
D3DPRESENT_PARAMETERS param {};
|
D3DPRESENT_PARAMETERS param {};
|
||||||
|
|
||||||
pReal->GetSwapChain(0, &mSwapChain);
|
pReal->GetSwapChain(0, &mSwapChain);
|
||||||
mSwapChain->GetPresentParameters(¶m);
|
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 (!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,
|
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
|
||||||
D3DPOOL_DEFAULT, &tex, NULL) != D3D_OK) {
|
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) {
|
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;
|
// pre-calculate dimensions used for phase 1
|
||||||
const int rectTop = 576;
|
const int rectLeft = param.BackBufferWidth;
|
||||||
|
const int rectTop = param.BackBufferHeight;
|
||||||
const int w = param.BackBufferWidth;
|
const int w = param.BackBufferWidth;
|
||||||
const int h = param.BackBufferHeight;
|
const int h = param.BackBufferHeight;
|
||||||
|
|
||||||
D3DLOCKED_RECT rect;
|
D3DLOCKED_RECT rect;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||||
if (GRAPHICS_FS_FORCE_LANDSCAPE) {
|
|
||||||
const int w_new = h * 9 / 16;
|
// phase 1 - copy the original back buffer onto the new surface.
|
||||||
const int x_new = rectLeft + ((w - w_new) / 2);
|
// we draw it 1:1 in the center of the surface.
|
||||||
RECT originRect {
|
// if orientation is swapped, fix up the rect and draw it in the center.
|
||||||
x_new,
|
//
|
||||||
rectTop,
|
// for this phase we always render with the same rect so that it's always overwritten
|
||||||
(LONG)(x_new + w_new),
|
// by a new frame on the same spot.
|
||||||
(LONG)(rectTop + h),
|
RECT originRect {
|
||||||
};
|
rectLeft,
|
||||||
hr = pReal->StretchRect(
|
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,
|
backbuffer, nullptr,
|
||||||
topSurface, &originRect,
|
topSurface, &originRect,
|
||||||
D3DTEXF_LINEAR);
|
D3DTEXF_LINEAR);
|
||||||
if (hr != D3D_OK) {
|
if (hr != D3D_OK) {
|
||||||
log_misc("graphics::d3d9", "StretchRect backbuffer failed (forced landscape)");
|
log_warning(
|
||||||
}
|
"graphics::d3d9",
|
||||||
} else {
|
"SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}",
|
||||||
// stretch to add top/left offset to avoid going negative
|
originRect.left, originRect.top, originRect.right, originRect.bottom);
|
||||||
hr = pReal->StretchRect(
|
|
||||||
backbuffer, nullptr,
|
|
||||||
topSurface, nullptr,
|
|
||||||
D3DTEXF_LINEAR);
|
|
||||||
if (hr != D3D_OK) {
|
|
||||||
log_misc("graphics::d3d9", "StretchRect backbuffer failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
topSurface->UnlockRect();
|
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 {
|
RECT targetRect {
|
||||||
rectLeft,
|
rectLeft,
|
||||||
rectTop,
|
rectTop,
|
||||||
(LONG)(rectLeft + w),
|
(LONG)(rectLeft + w),
|
||||||
(LONG)(rectTop + h),
|
(LONG)(rectTop + h),
|
||||||
};
|
};
|
||||||
|
|
||||||
// do the actual zoom / offset math
|
|
||||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||||
if (scene.centered) {
|
auto w_new = w / scene.scale_x;
|
||||||
targetRect.right = (w + rectLeft) / scene.scale_x;
|
auto h_new = h / scene.scale_y;
|
||||||
targetRect.bottom = (h + rectTop) / scene.scale_y;
|
|
||||||
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
|
// make sure the scaling is within bounds
|
||||||
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
|
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||||
targetRect.top -= deltaH;
|
if (w_new > topSurface_width || h_new > topSurface_height) {
|
||||||
targetRect.bottom -= deltaH;
|
w_new = topSurface_width;
|
||||||
targetRect.left -= deltaW;
|
h_new = topSurface_height;
|
||||||
targetRect.right -= deltaW;
|
}
|
||||||
} else {
|
} else {
|
||||||
targetRect.left -= scene.offset_x;
|
w_new = MIN(w_new, topSurface_width);
|
||||||
targetRect.top += scene.offset_y;
|
h_new = MIN(h_new, topSurface_height);
|
||||||
targetRect.right = -scene.offset_x;
|
}
|
||||||
targetRect.right += (w + rectLeft) / scene.scale_x;
|
|
||||||
targetRect.bottom = scene.offset_y;
|
targetRect.left = (topSurface_width / 2) - (w_new / 2) - scene.offset_x;
|
||||||
targetRect.bottom += (h + rectTop) / scene.scale_y;
|
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);
|
backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||||
bool use_linear_filter = true;
|
bool use_linear_filter = true;
|
||||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||||
@@ -791,14 +857,17 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
|||||||
use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
|
use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
|
||||||
backbuffer->UnlockRect();
|
backbuffer->UnlockRect();
|
||||||
if (hr != D3D_OK) {
|
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() {
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EndScene() {
|
||||||
WRAP_DEBUG;
|
WRAP_DEBUG;
|
||||||
|
|
||||||
if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_FORCE_LANDSCAPE) {
|
if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||||
SurfaceHook(pReal);
|
SurfaceHook(pReal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ 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;
|
std::optional<std::pair<uint32_t, uint32_t>> 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
|
// settings
|
||||||
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
|
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ 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 std::optional<std::pair<uint32_t, uint32_t>> 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 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;
|
||||||
|
|||||||
@@ -1167,11 +1167,14 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
|
|
||||||
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
|
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
|
||||||
#if SPICE64
|
#if SPICE64
|
||||||
GRAPHICS_FS_FORCE_LANDSCAPE = true;
|
GRAPHICS_FS_ORIENTATION_SWAP = true;
|
||||||
#else
|
#else
|
||||||
log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
|
log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::FullscreenOrientationFlip].value_bool() && !GRAPHICS_WINDOWED) {
|
||||||
|
GRAPHICS_FS_ORIENTATION_SWAP = true;
|
||||||
|
}
|
||||||
|
|
||||||
// deleted options
|
// deleted options
|
||||||
if (options[launcher::Options::OpenKFControl].value_bool()) {
|
if (options[launcher::Options::OpenKFControl].value_bool()) {
|
||||||
|
|||||||
@@ -218,6 +218,17 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.setting_name = "1280,720",
|
.setting_name = "1280,720",
|
||||||
.category = "Graphics (Full Screen)"
|
.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
|
// Graphics9On12
|
||||||
.title = "DirectX 9 on 12 (DEPRECATED - use -dx9on12 instead)",
|
.title = "DirectX 9 on 12 (DEPRECATED - use -dx9on12 instead)",
|
||||||
@@ -2039,9 +2050,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "sdvxlandscape",
|
.name = "sdvxlandscape",
|
||||||
.desc =
|
.desc =
|
||||||
"Allows you to play in landscape by transposing resolution and applying image scaling.\n\n"
|
"Allows you to play in landscape by transposing resolution and applying image scaling.\n\n"
|
||||||
"Only for SDVX5 and above!\n\n"
|
"Works only for SDVX5 and above! This is identical to -forceorientation.\n\n"
|
||||||
"Can also be combined with -forceres option to render at larger or smaller resolution, "
|
"Will launch at 1080p by default; strongly consider combining this with -forceres option to render at monitor native resolution",
|
||||||
"and with image resize option to zoom into certain areas of the screen",
|
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options"
|
.category = "Game Options"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace launcher {
|
|||||||
GraphicsForceSingleAdapter,
|
GraphicsForceSingleAdapter,
|
||||||
GraphicsForceRefresh,
|
GraphicsForceRefresh,
|
||||||
FullscreenResolution,
|
FullscreenResolution,
|
||||||
|
FullscreenOrientationFlip,
|
||||||
Graphics9On12,
|
Graphics9On12,
|
||||||
spice2x_Dx9On12,
|
spice2x_Dx9On12,
|
||||||
NoLegacy,
|
NoLegacy,
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ namespace overlay::windows {
|
|||||||
for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) {
|
for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) {
|
||||||
auto& scene = cfg::SCREENRESIZE->scene_settings[i];
|
auto& scene = cfg::SCREENRESIZE->scene_settings[i];
|
||||||
scene.keep_aspect_ratio = true;
|
scene.keep_aspect_ratio = true;
|
||||||
scene.centered = true;
|
|
||||||
scene.offset_x = 0;
|
scene.offset_x = 0;
|
||||||
scene.offset_y = 0;
|
scene.offset_y = 0;
|
||||||
scene.scale_x = 1.f;
|
scene.scale_x = 1.f;
|
||||||
@@ -126,21 +125,28 @@ namespace overlay::windows {
|
|||||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||||
|
|
||||||
// general settings
|
// general settings
|
||||||
ImGui::Checkbox("Centered", &scene.centered);
|
ImGui::InputInt("X Offset", &scene.offset_x);
|
||||||
if (!scene.centered) {
|
ImGui::SameLine();
|
||||||
ImGui::InputInt("X Offset", &scene.offset_x);
|
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
|
||||||
ImGui::InputInt("Y Offset", &scene.offset_y);
|
ImGui::InputInt("Y Offset", &scene.offset_y);
|
||||||
}
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
|
||||||
|
|
||||||
// aspect ratio
|
// aspect ratio
|
||||||
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
||||||
if (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;
|
scene.scale_y = scene.scale_x;
|
||||||
}
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||||
} else {
|
} else {
|
||||||
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.65f, 2.0f);
|
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.5f, 2.5f);
|
||||||
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.65f, 2.0f);
|
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();
|
ImGui::EndDisabled();
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace overlay::windows {
|
|||||||
default:
|
default:
|
||||||
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
||||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
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.x /= 2;
|
||||||
this->init_size.y /= 2;
|
this->init_size.y /= 2;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user