Update to spice2x-26-02-17 (pre-apply)
> broken commit
This commit is contained in:
@@ -13,8 +13,10 @@
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "games/mfc/mfc.h"
|
||||
#include "games/io.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "launcher/launcher.h"
|
||||
@@ -40,6 +42,15 @@
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#define D3D9_BACKEND_DEBUG 0
|
||||
|
||||
#if D3D9_BACKEND_DEBUG
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
#define CHECK_RESULT(x) \
|
||||
do { \
|
||||
HRESULT __ret = (x); \
|
||||
@@ -188,7 +199,7 @@ static std::string format2s(D3DFORMAT format) {
|
||||
ENUM_VARIANT(D3DFMT_A2B10G10R10_XR_BIAS);
|
||||
ENUM_VARIANT(D3DFMT_BINARYBUFFER);
|
||||
default:
|
||||
return fmt::to_string(format);
|
||||
return fmt::to_string(static_cast<uint32_t>(format));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,14 +249,17 @@ static void log_create_device_failure(HRESULT hresult) {
|
||||
}
|
||||
|
||||
static bool is_dx9_on_12_enabled() {
|
||||
bool result = false;
|
||||
switch (GRAPHICS_9_ON_12_STATE) {
|
||||
case DX9ON12_FORCE_OFF:
|
||||
log_info("graphics::d3d9", "DirectX 9on12: forced OFF by user (-dx9on12)");
|
||||
return false;
|
||||
result = false;
|
||||
break;
|
||||
|
||||
case DX9ON12_FORCE_ON:
|
||||
log_info("graphics::d3d9", "DirectX 9on12: forced ON by user (-9on12 or -dx9on12)");
|
||||
return true;
|
||||
result = true;
|
||||
break;
|
||||
|
||||
case DX9ON12_AUTO:
|
||||
default:
|
||||
@@ -253,14 +267,33 @@ static bool is_dx9_on_12_enabled() {
|
||||
log_info(
|
||||
"graphics::d3d9",
|
||||
"DirectX 9on12: enabled automatically for current game (tip: use -dx9on12 to force on or off)");
|
||||
return true;
|
||||
result = true;
|
||||
} else {
|
||||
log_info(
|
||||
"graphics::d3d9",
|
||||
"DirectX 9on12: disabled by default, using DX9 (tip: use -dx9on12 to force on or off)");
|
||||
return false;
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
|
||||
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, MAXINT)) {
|
||||
deferredlogs::defer_error_messages({
|
||||
"dx9on12 was force enabled by user (-dx9on12)",
|
||||
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
|
||||
" try again with -dx9on12 option set to default value"
|
||||
});
|
||||
} else {
|
||||
deferredlogs::defer_error_messages({
|
||||
"dx9on12 was force enabled by user (-dx9on12)",
|
||||
" not very game is compatible with this, and can lead to crashes",
|
||||
" try without force enabling this if you are seeing issues"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static IDirect3D9 *WINAPI Direct3DCreate9_hook(UINT SDKVersion) {
|
||||
@@ -491,7 +524,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
||||
if (!modified && games::iidx::is_tdj_fhd() && refresh < 110) {
|
||||
if ((width == 1920 && height == 1080) ||
|
||||
(width == 1280 && height == 720)){
|
||||
log_misc(
|
||||
log_debug(
|
||||
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for TDJ FHD)",
|
||||
Mode, width, height, refresh);
|
||||
memset(pMode, 0, sizeof(*pMode));
|
||||
@@ -504,7 +537,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
||||
// - skip 75 and 90 Hz entries because LDJ game timing is messed up with it
|
||||
// (TDJ should be fine as it assumes a fixed 60 Hz timing)
|
||||
if (!modified && (width == 1360 || width == 1366 || refresh == 75 || refresh == 90)) {
|
||||
log_misc(
|
||||
log_debug(
|
||||
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for LDJ/TDJ)",
|
||||
Mode, width, height, refresh);
|
||||
memset(pMode, 0, sizeof(*pMode));
|
||||
@@ -512,7 +545,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
||||
}
|
||||
|
||||
if (!modified && !games::iidx::TDJ_MODE && games::iidx::FORCE_720P && (height > 720)) {
|
||||
log_misc(
|
||||
log_debug(
|
||||
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (-iidxforce720p)",
|
||||
Mode, width, height, refresh);
|
||||
memset(pMode, 0, sizeof(*pMode));
|
||||
@@ -673,7 +706,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
||||
{
|
||||
log_misc("graphics::d3d9", "IDirect3D9::CreateDevice hook hit ({}, {}, {}, {}, {}, {})",
|
||||
Adapter,
|
||||
DeviceType,
|
||||
static_cast<uint32_t>(DeviceType),
|
||||
fmt::ptr(hFocusWindow),
|
||||
behavior2s(BehaviorFlags),
|
||||
fmt::ptr(pPresentationParameters),
|
||||
@@ -748,9 +781,9 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
||||
params->BackBufferHeight,
|
||||
format2s(params->BackBufferFormat),
|
||||
params->BackBufferCount,
|
||||
params->MultiSampleType,
|
||||
static_cast<uint32_t>(params->MultiSampleType),
|
||||
params->MultiSampleQuality,
|
||||
params->SwapEffect,
|
||||
static_cast<uint32_t>(params->SwapEffect),
|
||||
params->Windowed,
|
||||
params->EnableAutoDepthStencil,
|
||||
format2s(params->AutoDepthStencilFormat),
|
||||
@@ -780,6 +813,14 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
||||
"either use -graphics-force-refresh option or change the desktop resolution beforehand.");
|
||||
}
|
||||
|
||||
if (!GRAPHICS_WINDOWED && num_adapters >= 2 && GRAPHICS_FORCE_REFRESH_SUB.has_value()) {
|
||||
log_info("graphics::d3d9", "force sub refresh rate: {} => {} Hz (-graphics-force-refresh-sub option)",
|
||||
pPresentationParameters[1].FullScreen_RefreshRateInHz,
|
||||
GRAPHICS_FORCE_REFRESH_SUB.value());
|
||||
|
||||
pPresentationParameters[1].FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH_SUB.value();
|
||||
}
|
||||
|
||||
// force single adapter
|
||||
if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||
log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode");
|
||||
@@ -862,7 +903,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
|
||||
log_misc("graphics::d3d9", "IDirect3D9Ex::CreateDeviceEx hook hit ({}, {}, {}, {}, {}, {})",
|
||||
Adapter,
|
||||
DeviceType,
|
||||
static_cast<uint32_t>(DeviceType),
|
||||
fmt::ptr(hFocusWindow),
|
||||
behavior2s(BehaviorFlags),
|
||||
fmt::ptr(pPresentationParameters),
|
||||
@@ -904,26 +945,37 @@ 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()) {
|
||||
if (!GRAPHICS_WINDOWED){
|
||||
if (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);
|
||||
}
|
||||
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"use custom resolution {}x{} => {}x{}",
|
||||
"use custom sub 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);
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second);
|
||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
|
||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,9 +989,9 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
params->BackBufferHeight,
|
||||
format2s(params->BackBufferFormat),
|
||||
params->BackBufferCount,
|
||||
params->MultiSampleType,
|
||||
static_cast<uint32_t>(params->MultiSampleType),
|
||||
params->MultiSampleQuality,
|
||||
params->SwapEffect,
|
||||
static_cast<uint32_t>(params->SwapEffect),
|
||||
params->Windowed,
|
||||
params->EnableAutoDepthStencil,
|
||||
format2s(params->AutoDepthStencilFormat),
|
||||
@@ -951,12 +1003,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||
|
||||
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_ORIENTATION_SWAP) {
|
||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||
if (!GRAPHICS_WINDOWED) {
|
||||
if (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_ORIENTATION_SWAP) {
|
||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||
}
|
||||
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
|
||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
|
||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,7 +1025,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
fullscreen_display_mode->Height,
|
||||
fullscreen_display_mode->RefreshRate,
|
||||
format2s(fullscreen_display_mode->Format),
|
||||
fullscreen_display_mode->ScanLineOrdering);
|
||||
static_cast<uint32_t>(fullscreen_display_mode->ScanLineOrdering));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -999,6 +1056,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
}
|
||||
}
|
||||
|
||||
if (!GRAPHICS_WINDOWED && num_adapters >= 2 && GRAPHICS_FORCE_REFRESH_SUB.has_value()) {
|
||||
log_info("graphics::d3d9", "force sub refresh rate: {} => {} Hz (-graphics-force-refresh-sub option)",
|
||||
pPresentationParameters[1].FullScreen_RefreshRateInHz,
|
||||
GRAPHICS_FORCE_REFRESH_SUB.value());
|
||||
|
||||
pPresentationParameters[1].FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH_SUB.value();
|
||||
if (pFullscreenDisplayMode) {
|
||||
pFullscreenDisplayMode[1].RefreshRate = GRAPHICS_FORCE_REFRESH_SUB.value();
|
||||
}
|
||||
}
|
||||
|
||||
// force single adapter
|
||||
if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||
log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode");
|
||||
@@ -1119,9 +1187,15 @@ IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen() {
|
||||
}
|
||||
|
||||
static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
||||
// iidx/sdvx
|
||||
int swapchain = 1;
|
||||
if (games::gitadora::is_arena_model()) {
|
||||
swapchain = 3;
|
||||
}
|
||||
|
||||
if (!ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE && SUB_SWAP_CHAIN == nullptr) {
|
||||
ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = true;
|
||||
HRESULT hr = wrapped_device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
||||
HRESULT hr = wrapped_device->GetSwapChain(swapchain, &SUB_SWAP_CHAIN);
|
||||
if (FAILED(hr)) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
@@ -1330,14 +1404,19 @@ void graphics_d3d9_on_present(
|
||||
}
|
||||
|
||||
// for IIDX TDJ / SDVX UFC, handle subscreen
|
||||
const bool is_vm =
|
||||
avs::game::is_model("KFC") &&
|
||||
(avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H');
|
||||
const bool is_vm = games::sdvx::is_valkyrie_model();
|
||||
const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE;
|
||||
if (is_vm || is_tdj) {
|
||||
const bool is_gfdm_arena =
|
||||
games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW;
|
||||
if (is_vm || is_tdj || is_gfdm_arena) {
|
||||
graphics_d3d9_ldj_on_present(wrapped_device);
|
||||
}
|
||||
|
||||
const bool is_mfc = avs::game::is_model("KK9") && games::mfc::HG_MODE;
|
||||
if (is_mfc) {
|
||||
wintouchemu::update();
|
||||
}
|
||||
|
||||
// check screenshot key
|
||||
static bool trigger_last = false;
|
||||
auto buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/flags_helper.h"
|
||||
@@ -36,6 +37,14 @@ constexpr bool CUSTOM_RESET = false;
|
||||
|
||||
constexpr D3DFORMAT D3DFMT_DF24 = static_cast<D3DFORMAT>(MAKEFOURCC('D', 'F', '2', '4'));
|
||||
|
||||
auto format_as(D3DPOOL f) {
|
||||
return fmt::underlying(f);
|
||||
}
|
||||
|
||||
auto format_as(D3DFORMAT f) {
|
||||
return fmt::underlying(f);
|
||||
}
|
||||
|
||||
std::string usage2s(DWORD dwUsage) {
|
||||
FLAGS_START(dwUsage);
|
||||
FLAG(dwUsage, D3DUSAGE_RENDERTARGET);
|
||||
@@ -241,8 +250,16 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain(
|
||||
WRAP_VERBOSE;
|
||||
|
||||
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
||||
|
||||
bool create_swap_chain = false;
|
||||
if (avs::game::is_model({"LDJ", "KFC"})) {
|
||||
create_swap_chain = true;
|
||||
} else if (games::gitadora::is_arena_model() &&
|
||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||
pPresentationParameters->BackBufferWidth == 800) {
|
||||
create_swap_chain = true;
|
||||
}
|
||||
|
||||
if (create_swap_chain) {
|
||||
if (SUCCEEDED(hr) && !sub_swapchain) {
|
||||
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
||||
sub_swapchain->should_run_hooks = false;
|
||||
@@ -287,7 +304,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
bool swap = false;
|
||||
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) {
|
||||
swap = true;
|
||||
}
|
||||
if (games::gitadora::is_arena_model() &&
|
||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||
iSwapChain == 3) {
|
||||
swap = true;
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
if (sub_swapchain) {
|
||||
sub_swapchain->AddRef();
|
||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain);
|
||||
@@ -733,6 +760,14 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
const int rectTop = param.BackBufferHeight;
|
||||
const int w = param.BackBufferWidth;
|
||||
const int h = param.BackBufferHeight;
|
||||
|
||||
// this code used to clear the surface using ColorFill on every call, but
|
||||
// this turned out to be very expensive, leading to major frame drops in
|
||||
// SDVX Live2D scenario
|
||||
if (cfg::SCREENRESIZE->need_surface_clean) {
|
||||
pReal->ColorFill(topSurface, nullptr, D3DCOLOR_XRGB(0, 0, 0));
|
||||
cfg::SCREENRESIZE->need_surface_clean = false;
|
||||
}
|
||||
|
||||
D3DLOCKED_RECT rect;
|
||||
HRESULT hr;
|
||||
@@ -750,6 +785,7 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
(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);
|
||||
@@ -757,6 +793,21 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||
}
|
||||
|
||||
bool duplicate = false;
|
||||
RECT originRect2 = originRect;
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||
if (scene.duplicate == cfg::ScreenDuplicateMode::CopyLeft) {
|
||||
duplicate = true;
|
||||
originRect2.right = originRect.left;
|
||||
originRect2.left = (LONG)(originRect2.left - (originRect.right - originRect.left));
|
||||
} else if (scene.duplicate == cfg::ScreenDuplicateMode::CopyRight) {
|
||||
duplicate = true;
|
||||
originRect2.left = originRect.right;
|
||||
originRect2.right = (LONG)(originRect2.left + (originRect.right - originRect.left));
|
||||
}
|
||||
}
|
||||
|
||||
// log_misc(
|
||||
// "graphics::d3d9", "originRect: {} {} {} {}",
|
||||
// originRect.left, originRect.top, originRect.right, originRect.bottom);
|
||||
@@ -768,9 +819,21 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
if (hr != D3D_OK) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
"SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}",
|
||||
"SurfaceHook - StretchRect backbuffer failed for main image, rect: {} {} {} {}",
|
||||
originRect.left, originRect.top, originRect.right, originRect.bottom);
|
||||
}
|
||||
if (duplicate) {
|
||||
hr = pReal->StretchRect(
|
||||
backbuffer, nullptr,
|
||||
topSurface, &originRect2,
|
||||
D3DTEXF_LINEAR);
|
||||
if (hr != D3D_OK) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
"SurfaceHook - StretchRect backbuffer failed for the duplicate, rect: {} {} {} {}",
|
||||
originRect2.left, originRect2.top, originRect2.right, originRect2.bottom);
|
||||
}
|
||||
}
|
||||
topSurface->UnlockRect();
|
||||
|
||||
// phase 2 - viewport calculation - do the actual zoom / offset math
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "cfg/icon.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "games/ddr/ddr.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||
#include "launcher/shutdown.h"
|
||||
@@ -33,6 +34,7 @@ struct CaptureData {
|
||||
|
||||
HWND TDJ_SUBSCREEN_WINDOW = nullptr;
|
||||
HWND SDVX_SUBSCREEN_WINDOW = nullptr;
|
||||
HWND GFDM_SUBSCREEN_WINDOW = nullptr;
|
||||
|
||||
// icon
|
||||
static HICON WINDOW_ICON = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(MAINICON));
|
||||
@@ -60,6 +62,7 @@ graphics_orientation GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_NORMAL;
|
||||
bool GRAPHICS_WINDOWED = false;
|
||||
std::vector<HWND> GRAPHICS_WINDOWS;
|
||||
UINT GRAPHICS_FORCE_REFRESH = 0;
|
||||
std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
||||
std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
||||
bool GRAPHICS_FORCE_SINGLE_ADAPTER = false;
|
||||
bool GRAPHICS_PREVENT_SECONDARY_WINDOW = false;
|
||||
@@ -68,6 +71,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;
|
||||
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
||||
@@ -94,6 +98,7 @@ static decltype(SetCursor) *SetCursor_orig = nullptr;
|
||||
static decltype(SetWindowLongA) *SetWindowLongA_orig = nullptr;
|
||||
static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr;
|
||||
static decltype(SetWindowPos) *SetWindowPos_orig = nullptr;
|
||||
static decltype(ShowWindow) *ShowWindow_orig = nullptr;
|
||||
|
||||
static void reset_window_hook(HWND hWnd) {
|
||||
overlay::destroy(hWnd);
|
||||
@@ -296,6 +301,30 @@ 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,
|
||||
@@ -320,10 +349,19 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
// 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)
|
||||
const auto adjust_orientation =
|
||||
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 {};
|
||||
|
||||
@@ -333,11 +371,16 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
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 {}",
|
||||
orientation);
|
||||
"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) {
|
||||
@@ -352,7 +395,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
|
||||
const auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
||||
log_warning("graphics", "failed to change display settings: {}", disp_res);
|
||||
log_warning("graphics", "ChangeDisplaySettings failed: {}", disp_res);
|
||||
}
|
||||
} else {
|
||||
log_warning("graphics", "failed to get display settings");
|
||||
@@ -369,11 +412,16 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
|
||||
bool is_tdj_sub_window = avs::game::is_model("LDJ") && window_name.ends_with(" sub");
|
||||
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
||||
bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL");
|
||||
|
||||
// TDJ windowed mode with subscreen: hide maximize button
|
||||
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window) {
|
||||
// hide maximize button (prevent misaligned touches)
|
||||
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) {
|
||||
dwStyle &= ~(WS_MAXIMIZEBOX);
|
||||
}
|
||||
// mouse clicks become misaligned when resized
|
||||
if (is_gfdm_sub_window) {
|
||||
dwStyle &= ~(WS_SIZEBOX);
|
||||
}
|
||||
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
||||
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
||||
}
|
||||
@@ -417,7 +465,19 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW);
|
||||
}
|
||||
|
||||
// only hook touch window if multiple windows are allowed
|
||||
if (is_gfdm_sub_window && !games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||
GFDM_SUBSCREEN_WINDOW = result;
|
||||
graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW);
|
||||
}
|
||||
|
||||
disable_touch_indicators(result);
|
||||
log_misc(
|
||||
"graphics",
|
||||
"CreateWindowExA returned {}, {}",
|
||||
fmt::ptr(result),
|
||||
lpWindowName ? lpWindowName : "(null)");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -449,7 +509,8 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
|
||||
}
|
||||
|
||||
// windowed mode adjustments
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
// check the width to filter out invisible system-created windows
|
||||
if (GRAPHICS_WINDOWED && nWidth > 0) {
|
||||
|
||||
// change window style
|
||||
dwExStyle = 0;
|
||||
@@ -484,6 +545,12 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
|
||||
hWndParent, hMenu, hInstance, lpParam);
|
||||
GRAPHICS_WINDOWS.push_back(result);
|
||||
|
||||
log_misc(
|
||||
"graphics",
|
||||
"CreateWindowExW returned {}, {}",
|
||||
fmt::ptr(result),
|
||||
lpWindowName ? ws2s(lpWindowName) : "(null)");
|
||||
|
||||
disable_touch_indicators(result);
|
||||
return result;
|
||||
}
|
||||
@@ -648,10 +715,29 @@ static BOOL WINAPI SetWindowPos_hook(HWND hWnd, HWND hWndInsertAfter,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// prevent gitadora arena model from shifting windows around if the user has preferences
|
||||
if (GRAPHICS_WINDOWED && games::gitadora::is_arena_model() &&
|
||||
GRAPHICS_HOOKED_WINDOW.has_value() && hWnd == GRAPHICS_HOOKED_WINDOW.value() &&
|
||||
cfg::SCREENRESIZE->enable_window_resize) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// call original
|
||||
return SetWindowPos_orig(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
|
||||
}
|
||||
|
||||
static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) {
|
||||
if (games::gitadora::is_arena_model() &&
|
||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||
hWnd != GRAPHICS_HOOKED_WINDOW) {
|
||||
log_info("graphics", "ShowWindow_hook - hiding sub window {}", fmt::ptr(hWnd));
|
||||
return true;
|
||||
}
|
||||
|
||||
// call original
|
||||
return ShowWindow_orig(hWnd, nCmdShow);
|
||||
}
|
||||
|
||||
static ATOM WINAPI RegisterClassA_hook(const WNDCLASSA *lpWndClass) {
|
||||
|
||||
// check for null
|
||||
@@ -794,6 +880,7 @@ void graphics_init() {
|
||||
SetWindowLongA_orig = detour::iat_try("SetWindowLongA", SetWindowLongA_hook);
|
||||
SetWindowLongW_orig = detour::iat_try("SetWindowLongW", SetWindowLongW_hook);
|
||||
SetWindowPos_orig = detour::iat_try("SetWindowPos", SetWindowPos_hook);
|
||||
ShowWindow_orig = detour::iat_try("ShowWindow", ShowWindow_hook);
|
||||
|
||||
detour::iat_try("MessageBoxA", MessageBoxA_hook);
|
||||
detour::iat_try("MessageBoxExA", MessageBoxExA_hook);
|
||||
|
||||
@@ -32,10 +32,12 @@ extern bool GRAPHICS_WINDOWED;
|
||||
extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
||||
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
||||
extern UINT GRAPHICS_FORCE_REFRESH;
|
||||
extern std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
||||
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 std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||
@@ -48,6 +50,7 @@ extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
||||
extern std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
||||
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
|
||||
extern std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||
|
||||
extern bool GRAPHICS_IIDX_WSUB;
|
||||
extern std::optional<std::string> GRAPHICS_WSUB_SIZE;
|
||||
@@ -60,6 +63,7 @@ extern bool GRAPHICS_WSUB_BORDERLESS;
|
||||
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
|
||||
extern HWND TDJ_SUBSCREEN_WINDOW;
|
||||
extern HWND SDVX_SUBSCREEN_WINDOW;
|
||||
extern HWND GFDM_SUBSCREEN_WINDOW;
|
||||
|
||||
extern bool SUBSCREEN_FORCE_REDRAW;
|
||||
|
||||
@@ -98,5 +102,8 @@ void graphics_capture_initial_window(HWND hWnd);
|
||||
void graphics_update_window_style(HWND hWnd);
|
||||
void graphics_update_z_order(HWND hWnd, bool always_on_top);
|
||||
void graphics_move_resize_window(HWND hWnd);
|
||||
bool graphics_window_change_crashes_game();
|
||||
bool graphics_window_options_breaks_game();
|
||||
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();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "graphics.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/logging.h"
|
||||
@@ -23,6 +24,7 @@ std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
||||
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
||||
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
|
||||
std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||
|
||||
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
|
||||
bool GRAPHICS_IIDX_WSUB = false;
|
||||
@@ -55,15 +57,22 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
|
||||
graphics_load_windowed_parameters();
|
||||
|
||||
cfg::SCREENRESIZE->init_window_style = GetWindowLong(hWnd, GWL_STYLE);
|
||||
GRAPHICS_HOOKED_WINDOW = hWnd;
|
||||
|
||||
log_debug("graphics-windowed", "graphics_capture_initial_window called");
|
||||
cfg::SCREENRESIZE->init_window_style = GetWindowLong(hWnd, GWL_STYLE);
|
||||
cfg::SCREENRESIZE->init_window_style_ex = GetWindowLong(hWnd, GWL_EXSTYLE);
|
||||
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_capture_initial_window called, hWnd={}",
|
||||
fmt::ptr(hWnd));
|
||||
|
||||
RECT rect;
|
||||
if (!GetClientRect(hWnd, &rect)) {
|
||||
log_warning(
|
||||
"graphics",
|
||||
"graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
||||
"graphics-windowed",
|
||||
"[{}] graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
||||
fmt::ptr(hWnd),
|
||||
GetLastError());
|
||||
return;
|
||||
}
|
||||
@@ -75,12 +84,22 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
cfg::SCREENRESIZE->init_client_aspect_ratio = (float)client_w / (float)client_h;
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
||||
"[{}] graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
||||
fmt::ptr(hWnd),
|
||||
client_w, client_h, cfg::SCREENRESIZE->init_client_aspect_ratio);
|
||||
|
||||
// ensure frame size is captured
|
||||
graphics_wm_style_changed(hWnd, false);
|
||||
|
||||
// if the game is not compatible, ignore user-supplied values and keep the current size
|
||||
if (graphics_window_resize_breaks_game()) {
|
||||
cfg::SCREENRESIZE->client_width = client_w;
|
||||
cfg::SCREENRESIZE->client_height = client_h;
|
||||
}
|
||||
if (graphics_window_move_and_resize_breaks_game()) {
|
||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||
}
|
||||
|
||||
// if there was no user-supplied dimension, seed it with the current size
|
||||
// so that the next resize operation will work
|
||||
if (cfg::SCREENRESIZE->client_width == 0) {
|
||||
@@ -94,7 +113,8 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
// resize must be done before applying the border
|
||||
if (cfg::SCREENRESIZE->enable_window_resize) {
|
||||
log_info(
|
||||
"graphics-windowed", "change window rect - window offset: {}x{}, client size: {}x{}",
|
||||
"graphics-windowed", "[{}] change window rect - window offset: {}x{}, client size: {}x{}",
|
||||
fmt::ptr(hWnd),
|
||||
cfg::SCREENRESIZE->window_offset_x,
|
||||
cfg::SCREENRESIZE->window_offset_y,
|
||||
cfg::SCREENRESIZE->client_width,
|
||||
@@ -104,12 +124,13 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
// ddr hangs when window frame doesn't have overlapped
|
||||
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
||||
log_info(
|
||||
"graphics-windowed", "change window style - decoration: {}",
|
||||
"graphics-windowed", "[{}] change window style - decoration: {}",
|
||||
fmt::ptr(hWnd),
|
||||
cfg::SCREENRESIZE->window_decoration);
|
||||
graphics_update_window_style(hWnd);
|
||||
}
|
||||
if (cfg::SCREENRESIZE->window_always_on_top) {
|
||||
log_info("graphics-windowed", "change window z-order - always on top");
|
||||
log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd));
|
||||
graphics_update_z_order(hWnd, true);
|
||||
}
|
||||
|
||||
@@ -152,7 +173,7 @@ void graphics_load_windowed_parameters() {
|
||||
cfg::SCREENRESIZE->window_offset_x = result.first;
|
||||
cfg::SCREENRESIZE->window_offset_y = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -windowpos");
|
||||
log_fatal("graphics-windowed", "failed to parse -windowpos");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +202,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
GRAPHICS_WSUB_WIDTH = result.first;
|
||||
GRAPHICS_WSUB_HEIGHT = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
||||
log_fatal("graphics-windowed", "failed to parse -iidxwsubsize / -sdvxwsubsize");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +216,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
GRAPHICS_WSUB_X = result.first;
|
||||
GRAPHICS_WSUB_Y = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
||||
log_fatal("graphics-windowed", "failed to parse -iidxwsubpos / -sdvxwsubpos");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,20 +389,29 @@ void graphics_update_window_style(HWND hWnd) {
|
||||
if (!GRAPHICS_WINDOWED) {
|
||||
return;
|
||||
}
|
||||
if (graphics_window_change_crashes_game()) {
|
||||
if (graphics_window_decoration_change_crashes_game()) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("graphics-windowed", "graphics_update_window_style called");
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_update_window_style called for hWnd={}",
|
||||
fmt::ptr(hWnd));
|
||||
|
||||
// update frame style
|
||||
auto style = cfg::SCREENRESIZE->init_window_style;
|
||||
auto style_ex = cfg::SCREENRESIZE->init_window_style_ex;
|
||||
switch (cfg::SCREENRESIZE->window_decoration) {
|
||||
case cfg::WindowDecorationMode::Borderless:
|
||||
style &= ~WS_OVERLAPPEDWINDOW;
|
||||
style_ex &= ~WS_EX_CLIENTEDGE;
|
||||
style_ex &= ~WS_EX_WINDOWEDGE;
|
||||
break;
|
||||
case cfg::WindowDecorationMode::ResizableFrame:
|
||||
style |= WS_OVERLAPPEDWINDOW;
|
||||
if (graphics_window_resize_breaks_game()) {
|
||||
style &= ~WS_SIZEBOX;
|
||||
}
|
||||
break;
|
||||
case cfg::WindowDecorationMode::Default:
|
||||
default:
|
||||
@@ -393,11 +423,26 @@ void graphics_update_window_style(HWND hWnd) {
|
||||
"graphics_update_window_style - calling SetWindowLong with Mode {}, style 0x{:x}",
|
||||
static_cast<int>(cfg::SCREENRESIZE->window_decoration),
|
||||
style);
|
||||
SetWindowLong(hWnd, GWL_STYLE, style);
|
||||
|
||||
const auto ret = SetWindowLong(hWnd, GWL_STYLE, style);
|
||||
(void)ret;
|
||||
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_update_window_style - SetWindowLong(GWL_STYLE) returned {:x}, GLE={}",
|
||||
ret,
|
||||
GetLastError());
|
||||
|
||||
const auto ret2 = SetWindowLong(hWnd, GWL_EXSTYLE, style_ex);
|
||||
(void)ret2;
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_update_window_style SetWindowLong(GWL_EXSTYLE) returned {:x}, GLE={}",
|
||||
ret2,
|
||||
GetLastError());
|
||||
|
||||
// SetWindowPos must be called after SetWindowLong if the frame style changed
|
||||
// this will be done in WM_STYLECHANGED handler
|
||||
log_debug("graphics-windowed", "graphics_update_window_style returned");
|
||||
}
|
||||
|
||||
void graphics_update_z_order(HWND hWnd, bool always_on_top) {
|
||||
@@ -454,12 +499,19 @@ void graphics_move_resize_window(HWND hWnd) {
|
||||
log_debug("graphics-windowed", "graphics_move_resize_window returned");
|
||||
}
|
||||
|
||||
bool graphics_window_change_crashes_game() {
|
||||
bool graphics_window_decoration_change_crashes_game() {
|
||||
static std::once_flag flag;
|
||||
static bool result = false;
|
||||
std::call_once(flag, []() {
|
||||
// ddr crashes when frame style changes
|
||||
result = avs::game::is_model("MDX");
|
||||
|
||||
// changing window decoration also changes dimensions and position
|
||||
// so this must be prevented
|
||||
if (graphics_window_move_and_resize_breaks_game()) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
log_warning(
|
||||
"graphics-windowed",
|
||||
@@ -468,3 +520,43 @@ bool graphics_window_change_crashes_game() {
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool graphics_window_move_and_resize_breaks_game() {
|
||||
static std::once_flag flag;
|
||||
static bool result = false;
|
||||
|
||||
std::call_once(flag, []() {
|
||||
// games like reflec beat in use spice touch overlay for windowed mode
|
||||
// and mouse input breaks if window is moved as the overlay does not
|
||||
// currently support window moves/resizes
|
||||
result = avs::game::is_model({"KBR", "LBR", "MBR"});
|
||||
if (result) {
|
||||
log_warning(
|
||||
"graphics-windowed",
|
||||
"ignoring changes to window properties due to incompatibility with this game");
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool graphics_window_resize_breaks_game() {
|
||||
// for whatever reason, gitadora games on dx/sd/sd2 cabs behave poorly when window is resized
|
||||
// (eihter at launch or while it's running)
|
||||
// usually results in: soft lock during scene transtions or really long transitions,
|
||||
// backgrounds of menus not rendering, etc.
|
||||
bool is_gitadora = avs::game::is_model({ "J32", "J33", "K32", "K33", "L32", "L33", "M32" });
|
||||
bool is_arena = games::gitadora::is_arena_model();
|
||||
|
||||
bool result = graphics_window_move_and_resize_breaks_game() || (is_gitadora && !is_arena);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&result]() {
|
||||
if (result) {
|
||||
log_warning(
|
||||
"graphics-windowed",
|
||||
"ignoring changes to window size due to incompatibility with this game");
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+137
-21
@@ -9,6 +9,7 @@
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "nvenc_hook.h"
|
||||
|
||||
@@ -16,17 +17,54 @@
|
||||
|
||||
typedef NVENCSTATUS(NVENCAPI *NvEncodeAPICreateInstance_Type)(NV_ENCODE_API_FUNCTION_LIST*);
|
||||
static NvEncodeAPICreateInstance_Type NvEncodeAPICreateInstance_orig = nullptr;
|
||||
|
||||
static PNVENCOPENENCODESESSIONEX nvEncOpenEncodeSessionEx_orig = nullptr;
|
||||
static PNVENCINITIALIZEENCODER nvEncInitializeEncoder_orig = nullptr;
|
||||
static PNVENCGETENCODEPRESETCONFIG nvEncGetEncodePresetConfig_orig = nullptr;
|
||||
static PNVENCGETENCODEPRESETCONFIGEX nvEncGetEncodePresetConfigEx_orig = nullptr;
|
||||
static BOOL initialized = false;
|
||||
static BOOL new_preset_guids = false;
|
||||
|
||||
namespace nvenc_hook {
|
||||
|
||||
bool FORCE_DISABLE;
|
||||
|
||||
std::optional<std::string> VIDEO_CQP_STRING_OVERRIDE;
|
||||
std::optional<NV_ENC_QP> VIDEO_CQP_OVERRIDE;
|
||||
|
||||
void parse_qcp_params();
|
||||
|
||||
void dump_init_params(NV_ENC_INITIALIZE_PARAMS* encode) {
|
||||
log_misc("nvenc_hook", "NV_ENC_INITIALIZE_PARAMS:");
|
||||
log_misc("nvenc_hook", " version: {:x}", encode->version);
|
||||
log_misc("nvenc_hook", " encodeGUID: {}", guid2s(encode->encodeGUID));
|
||||
log_misc("nvenc_hook", " presetGUID: {}", guid2s(encode->presetGUID));
|
||||
log_misc("nvenc_hook", " encodeWidth: {}", encode->encodeWidth);
|
||||
log_misc("nvenc_hook", " encodeHeight: {}", encode->encodeHeight);
|
||||
log_misc("nvenc_hook", " darWidth: {}", encode->darWidth);
|
||||
log_misc("nvenc_hook", " darHeight: {}", encode->darHeight);
|
||||
log_misc("nvenc_hook", " frameRateNum: {}", encode->frameRateNum);
|
||||
log_misc("nvenc_hook", " frameRateDen: {}", encode->frameRateDen);
|
||||
log_misc("nvenc_hook", " enableEncodeAsync: {}", encode->enableEncodeAsync);
|
||||
log_misc("nvenc_hook", " enablePTD: {}", encode->enablePTD);
|
||||
log_misc("nvenc_hook", " maxEncodeWidth: {}", encode->maxEncodeWidth);
|
||||
log_misc("nvenc_hook", " maxEncodeHeight: {}", encode->maxEncodeHeight);
|
||||
log_misc("nvenc_hook", " tuningInfo: {}", static_cast<uint32_t>(encode->tuningInfo));
|
||||
log_misc("nvenc_hook", " bufferFormat: {}", static_cast<uint32_t>(encode->bufferFormat));
|
||||
log_misc("nvenc_hook", " encodeConfig.version: {:x}", encode->encodeConfig->version);
|
||||
log_misc("nvenc_hook", " encodeConfig.profileGUID: {}", guid2s(encode->encodeConfig->profileGUID));
|
||||
log_misc("nvenc_hook", " encodeConfig.gopLength: {}", encode->encodeConfig->gopLength);
|
||||
log_misc("nvenc_hook", " encodeConfig.frameIntervalP: {}", encode->encodeConfig->frameIntervalP);
|
||||
log_misc("nvenc_hook", " encodeConfig.monoChromeEncoding: {}", encode->encodeConfig->monoChromeEncoding);
|
||||
|
||||
const auto rc = &encode->encodeConfig->rcParams;
|
||||
const auto h264 = &encode->encodeConfig->encodeCodecConfig.h264Config;
|
||||
log_misc("nvenc_hook", " encodeConfig.encodeCodecConfig.h264Config.sliceMode: {}", h264->sliceMode);
|
||||
log_misc("nvenc_hook", " encodeConfig.encodeCodecConfig.h264Config.sliceModeData: {}", h264->sliceModeData);
|
||||
log_misc("nvenc_hook", " encodeConfig.rcParams.version: {:x}", rc->version);
|
||||
log_misc("nvenc_hook", " encodeConfig.rcParams.rateControlMode: {}", static_cast<uint32_t>(rc->rateControlMode));
|
||||
}
|
||||
|
||||
NVENCSTATUS NVENCAPI nvEncOpenEncodeSessionEx_hook(
|
||||
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS *openSessionExParams,
|
||||
void **encoder
|
||||
@@ -51,41 +89,113 @@ namespace nvenc_hook {
|
||||
try {
|
||||
// check input params
|
||||
if (createEncodeParams == nullptr || createEncodeParams->encodeConfig == nullptr) {
|
||||
log_warning("nvenc_hook", "nvEncInitializeEncoder called with invalid params");
|
||||
goto done;
|
||||
}
|
||||
|
||||
const auto rc = &createEncodeParams->encodeConfig->rcParams;
|
||||
if (rc->rateControlMode != NV_ENC_PARAMS_RC_CONSTQP) {
|
||||
|
||||
log_misc("nvenc_hook", "nvEncInitializeEncoder hook hit with expected params");
|
||||
dump_init_params(createEncodeParams);
|
||||
if (new_preset_guids) {
|
||||
log_misc("nvenc_hook", "swapping out encoder preset for newer NVENC SDK");
|
||||
memcpy(&createEncodeParams->presetGUID, &NV_ENC_PRESET_P4_GUID, sizeof(GUID));
|
||||
createEncodeParams->tuningInfo = NV_ENC_TUNING_INFO_HIGH_QUALITY;
|
||||
}
|
||||
|
||||
// cqp p/b/i override
|
||||
if (rc->rateControlMode == NV_ENC_PARAMS_RC_CONSTQP) {
|
||||
// print out most relevant video quality settings
|
||||
// note: NvEncoder.cpp sample uses {28, 31, 25} (and that's what some hex edits modify)
|
||||
// but bm2dx later adds 8 to each value, before calling this routine
|
||||
log_misc(
|
||||
"nvenc_hook", "nvEncInitializeEncoder: original constQP p={}, b={}, i={}",
|
||||
rc->constQP.qpInterP, rc->constQP.qpInterB, rc->constQP.qpIntra);
|
||||
|
||||
// video quality override
|
||||
if (VIDEO_CQP_OVERRIDE.has_value()) {
|
||||
rc->constQP = VIDEO_CQP_OVERRIDE.value();
|
||||
log_misc(
|
||||
"nvenc_hook", "nvEncInitializeEncoder: user overriden constQP p={}, b={}, i={}",
|
||||
rc->constQP.qpInterP, rc->constQP.qpInterB, rc->constQP.qpIntra);
|
||||
}
|
||||
} else {
|
||||
log_warning(
|
||||
"nvenc_hook",
|
||||
"nvEncInitializeEncoder: unexpected rateControlMode, expected: NV_ENC_PARAMS_RC_CONSTQP, actual: {}",
|
||||
rc->rateControlMode);
|
||||
goto done;
|
||||
}
|
||||
|
||||
log_misc("nvenc_hook", "nvEncInitializeEncoder hook hit with expected params");
|
||||
|
||||
// print out most relevant video quality settings
|
||||
// note: NvEncoder.cpp sample uses {28, 31, 25} (and that's what some hex edits modify)
|
||||
// but bm2dx later adds 8 to each value, before calling this routine
|
||||
log_misc(
|
||||
"nvenc_hook", "nvEncInitializeEncoder: original constQP p={}, b={}, i={}",
|
||||
rc->constQP.qpInterP, rc->constQP.qpInterB, rc->constQP.qpIntra);
|
||||
|
||||
// video quality override
|
||||
if (VIDEO_CQP_OVERRIDE.has_value()) {
|
||||
rc->constQP = VIDEO_CQP_OVERRIDE.value();
|
||||
log_misc(
|
||||
"nvenc_hook", "nvEncInitializeEncoder: user overriden constQP p={}, b={}, i={}",
|
||||
rc->constQP.qpInterP, rc->constQP.qpInterB, rc->constQP.qpIntra);
|
||||
static_cast<uint32_t>(rc->rateControlMode));
|
||||
}
|
||||
|
||||
} catch (const std::exception &ex) {}
|
||||
|
||||
done:
|
||||
return nvEncInitializeEncoder_orig(encoder, createEncodeParams);
|
||||
const auto status = nvEncInitializeEncoder_orig(encoder, createEncodeParams);
|
||||
log_misc(
|
||||
"nvenc_hook",
|
||||
"nvEncInitializeEncoder returned 0x{:x}",
|
||||
static_cast<uint32_t>(status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NVENCSTATUS NVENCAPI nvEncGetEncodePresetConfig_hook (
|
||||
void* encoder, GUID encodeGUID, GUID presetGUID, NV_ENC_PRESET_CONFIG* presetConfig) {
|
||||
|
||||
// IIDX32 calls this with
|
||||
// presetGUID = {34DBA71D-A77B-4B8F-9C3E-B6D5DA24C012} (NV_ENC_PRESET_HQ_GUID) (for h264)
|
||||
// this preset is deprecated according to NVIDIA
|
||||
|
||||
const auto status = nvEncGetEncodePresetConfig_orig(
|
||||
encoder, encodeGUID, presetGUID, presetConfig);
|
||||
|
||||
log_misc(
|
||||
"nvenc_hook",
|
||||
"NvEncGetEncodePresetConfig called with encodeGUID = {}, presetGUID = {}. and returned 0x{:x}",
|
||||
guid2s(encodeGUID),
|
||||
guid2s(presetGUID),
|
||||
static_cast<uint32_t>(status));
|
||||
|
||||
if (status == NV_ENC_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// in NVIDIA driver 591.44 released in December 2025,
|
||||
// NvEncGetEncodePresetConfig started to fail with NV_ENC_ERR_UNSUPPORTED_PARAM and
|
||||
// eventually cause a crash
|
||||
//
|
||||
// IIDX32 calls this with
|
||||
// presetGUID = {34DBA71D-A77B-4B8F-9C3E-B6D5DA24C012} (NV_ENC_PRESET_HQ_GUID) (for h264)
|
||||
// this preset is deprecated according to NVIDIA
|
||||
// https://docs.nvidia.com/video-technologies/video-codec-sdk/13.0/deprecation-notices/index.html
|
||||
//
|
||||
// references:
|
||||
// https://github.com/NVIDIA/video-sdk-samples/tree/aa3544dcea2fe63122e4feb83bf805ea40e58dbe/Samples/NvCodec/NvEncoder
|
||||
// https://forums.developer.nvidia.com/t/drivers-591-44-broke-nvenc-getencodepresetconfig-no-longer-works/353613
|
||||
// https://docs.nvidia.com/video-technologies/video-codec-sdk/11.1/nvenc-preset-migration-guide/index.html
|
||||
|
||||
new_preset_guids = true;
|
||||
const auto status_ex =
|
||||
nvEncGetEncodePresetConfigEx_orig(
|
||||
encoder,
|
||||
encodeGUID,
|
||||
NV_ENC_PRESET_P4_GUID,
|
||||
NV_ENC_TUNING_INFO_HIGH_QUALITY,
|
||||
presetConfig);
|
||||
|
||||
log_misc(
|
||||
"nvenc_hook",
|
||||
"called NvEncGetEncodePresetConfigEx instead; returned 0x{:x}",
|
||||
static_cast<uint32_t>(status_ex));
|
||||
|
||||
return status_ex;
|
||||
}
|
||||
|
||||
NVENCSTATUS NVENCAPI NvEncodeAPICreateInstance_hook(NV_ENCODE_API_FUNCTION_LIST *pFunctionList) {
|
||||
|
||||
if (FORCE_DISABLE) {
|
||||
return NV_ENC_ERR_NO_ENCODE_DEVICE;
|
||||
}
|
||||
|
||||
// log_misc("nvenc_hook", "NvEncodeAPICreateInstance hook hit");
|
||||
auto status = NvEncodeAPICreateInstance_orig(pFunctionList);
|
||||
|
||||
@@ -101,6 +211,11 @@ namespace nvenc_hook {
|
||||
pFunctionList->nvEncInitializeEncoder,
|
||||
nvEncInitializeEncoder_hook,
|
||||
&nvEncInitializeEncoder_orig);
|
||||
detour::trampoline_try(
|
||||
pFunctionList->nvEncGetEncodePresetConfig,
|
||||
nvEncGetEncodePresetConfig_hook,
|
||||
&nvEncGetEncodePresetConfig_orig);
|
||||
nvEncGetEncodePresetConfigEx_orig = pFunctionList->nvEncGetEncodePresetConfigEx;
|
||||
log_misc("nvenc_hook", "NvEncodeAPICreateInstance_hook called and functions hooked");
|
||||
initialized = true;
|
||||
}
|
||||
@@ -123,6 +238,7 @@ namespace nvenc_hook {
|
||||
} else {
|
||||
log_warning("nvenc_hook", "failed to hook NvEncodeAPICreateInstance");
|
||||
}
|
||||
|
||||
parse_qcp_params();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef SPICE64
|
||||
|
||||
namespace nvenc_hook {
|
||||
|
||||
extern bool FORCE_DISABLE;
|
||||
extern std::optional<std::string> VIDEO_CQP_STRING_OVERRIDE;
|
||||
void initialize();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user