From 2accb0660833f1950b7de65f658bc8d564cb80af Mon Sep 17 00:00:00 2001 From: FakeMichau <49685661+FakeMichau@users.noreply.github.com> Date: Thu, 24 Sep 2026 13:05:35 +0200 Subject: [PATCH] Allow for grabbing camera data from Unreal Engine Relies on https://github.com/FakeMichau/UeLowLatency and UE4SS Now the requirement for UE games is lowered to just an upscaler + Reflex + Hudless (can be from Hudfix). But seems like the FOV reported by DRG is slightly wrong, will need to check. --- .../reprojection/Reprojection_Dx12.cpp | 18 ++++++ OptiScaler/hooks/LibraryLoad_Hooks.cpp | 24 +++++--- OptiScaler/low_latency/input/input_uell.cpp | 57 +++++++++++++++++++ OptiScaler/low_latency/input/input_uell.h | 5 ++ OptiScaler/menu/menu_common.cpp | 3 +- 5 files changed, 98 insertions(+), 9 deletions(-) diff --git a/OptiScaler/framegen/reprojection/Reprojection_Dx12.cpp b/OptiScaler/framegen/reprojection/Reprojection_Dx12.cpp index 72e8264a..48d3402f 100644 --- a/OptiScaler/framegen/reprojection/Reprojection_Dx12.cpp +++ b/OptiScaler/framegen/reprojection/Reprojection_Dx12.cpp @@ -2,6 +2,7 @@ #include "Reprojection_Dx12.h" #include +#include #include #include @@ -185,6 +186,23 @@ void Reprojection_Dx12::EvaluateState(ID3D12Device* device, FG_Constants& fgCons { Deactivate(); } + + if (State::Instance().fgChanged) + { + LOG_DEBUG("FGchanged"); + + State::Instance().fgChanged = false; + + Hudfix_Dx12::ResetCounters(); + Hudfix_Dx11::ResetCounters(); + + // Pause for 10 frames + UpdateTarget(); + + // Release FG mutex + if (Mutex.getOwner() == 2) + Mutex.unlockThis(2); + } } std::optional Reprojection_Dx12::ReadGpuTime(void* commandQueue) diff --git a/OptiScaler/hooks/LibraryLoad_Hooks.cpp b/OptiScaler/hooks/LibraryLoad_Hooks.cpp index 5a5ebf83..75f70082 100644 --- a/OptiScaler/hooks/LibraryLoad_Hooks.cpp +++ b/OptiScaler/hooks/LibraryLoad_Hooks.cpp @@ -582,16 +582,24 @@ HMODULE LibraryLoadHooks::LoadLibraryCheckW(std::wstring libName, LPCWSTR lpLibF auto module = NtdllProxy::LoadLibraryExW_Ldr(libName.c_str(), NULL, NULL); - auto setTickStartCallback = - (PFN_setTickCallback) KernelBaseProxy::GetProcAddress_()(module, "setTickStartCallback"); - auto setTickEndCallback = - (PFN_setTickCallback) KernelBaseProxy::GetProcAddress_()(module, "setTickEndCallback"); + if (module) + { + auto setTickStartCallback = + (PFN_setTickCallback) KernelBaseProxy::GetProcAddress_()(module, "setTickStartCallback"); + auto setTickEndCallback = + (PFN_setTickCallback) KernelBaseProxy::GetProcAddress_()(module, "setTickEndCallback"); + auto setCameraUpdateCallback = + (PFN_setCameraUpdateExternal) KernelBaseProxy::GetProcAddress_()(module, "setCameraUpdateCallback"); - if (setTickStartCallback) - setTickStartCallback(InputUeLowLatency::tickStart); + if (setTickStartCallback) + setTickStartCallback(InputUeLowLatency::tickStart); - if (setTickEndCallback) - setTickEndCallback(InputUeLowLatency::tickEnd); + if (setTickEndCallback) + setTickEndCallback(InputUeLowLatency::tickEnd); + + if (setCameraUpdateCallback) + setCameraUpdateCallback(InputUeLowLatency::cameraUpdate); + } return module; } diff --git a/OptiScaler/low_latency/input/input_uell.cpp b/OptiScaler/low_latency/input/input_uell.cpp index 7db44409..49fe528d 100644 --- a/OptiScaler/low_latency/input/input_uell.cpp +++ b/OptiScaler/low_latency/input/input_uell.cpp @@ -1,6 +1,10 @@ #include "pch.h" #include "input_uell.h" +#include + +using namespace DirectX; + void InputUeLowLatency::init() { if (inited) @@ -79,3 +83,56 @@ void InputUeLowLatency::tickEnd(int64_t frameId, float DeltaSeconds, bool bIdleM return; } } + +static void GetCameraBasis(const float cameraRotation[3], float cameraUp[3], float cameraRight[3], + float cameraForward[3]) +{ + float pitch = DirectX::XMConvertToRadians(cameraRotation[0]); + float yaw = DirectX::XMConvertToRadians(cameraRotation[1]); + float roll = DirectX::XMConvertToRadians(cameraRotation[2]); + + float SP, CP, SY, CY, SR, CR; + DirectX::XMScalarSinCos(&SP, &CP, pitch); + DirectX::XMScalarSinCos(&SY, &CY, yaw); + DirectX::XMScalarSinCos(&SR, &CR, roll); + + cameraForward[0] = CP * CY; + cameraForward[1] = CP * SY; + cameraForward[2] = SP; + + cameraRight[0] = SR * SP * CY - CR * SY; + cameraRight[1] = SR * SP * SY + CR * CY; + cameraRight[2] = -SR * CP; + + cameraUp[0] = -(CR * SP * CY + SR * SY); + cameraUp[1] = CY * SR - CR * SP * SY; + cameraUp[2] = CR * CP; +} + +void InputUeLowLatency::cameraUpdate(int64_t frameId, float cameraPosition[3], float cameraRotation[3], float fovAngle) +{ + // Technically FSRFG input might have the camera data but not guaranteed + // if (State::Instance().activeFgInput == FGInput::DLSSG) + // return; + + if (auto fg = State::Instance().currentFG) + { + auto index = frameId % BUFFER_COUNT; + + float cameraUp[3]; + float cameraRight[3]; + float cameraForward[3]; + + GetCameraBasis(cameraRotation, cameraUp, cameraRight, cameraForward); + + fg->SetCameraData(cameraPosition, cameraUp, cameraRight, cameraForward, index); + + UINT64 width = 0; + UINT height = 0; + fg->GetInterpolationRect(width, height, index); + float aspectRatio = (float) width / (float) height; + + // TODO: camera near and far are placeholders + fg->SetCameraValues(0.01f, 10000.f, DirectX::XMConvertToRadians(fovAngle), aspectRatio, 0.0f, index); + } +} diff --git a/OptiScaler/low_latency/input/input_uell.h b/OptiScaler/low_latency/input/input_uell.h index 5a274bea..6e497208 100644 --- a/OptiScaler/low_latency/input/input_uell.h +++ b/OptiScaler/low_latency/input/input_uell.h @@ -3,7 +3,11 @@ #include "input_common.h" typedef void (*PFN_FLTickExternal)(int64_t frameId, float DeltaSeconds, bool bIdleMode); +typedef void (*PFN_CameraUpdateExternal)(int64_t frameId, float cameraPosition[3], + float cameraRotation[3], // Pitch, Yaw, Roll + float fovAngle); typedef void (*PFN_setTickCallback)(PFN_FLTickExternal callback); +typedef void (*PFN_setCameraUpdateExternal)(PFN_CameraUpdateExternal callback); class InputUeLowLatency { @@ -19,4 +23,5 @@ class InputUeLowLatency static void init(); static void tickEnd(int64_t frameId, float DeltaSeconds, bool bIdleMode); static void tickStart(int64_t frameId, float DeltaSeconds, bool bIdleMode); + static void cameraUpdate(int64_t frameId, float cameraPosition[3], float cameraRotation[3], float fovAngle); }; diff --git a/OptiScaler/menu/menu_common.cpp b/OptiScaler/menu/menu_common.cpp index e48a2111..b420260f 100644 --- a/OptiScaler/menu/menu_common.cpp +++ b/OptiScaler/menu/menu_common.cpp @@ -4338,7 +4338,8 @@ void MenuCommon::RenderFrameGenerationRuntimeSettings(RenderMenuContext& ctx) if (currentFeature != nullptr && !currentFeature->IsFrozen() && ((state.activeFgOutput == FGOutput::FSRFG && FfxApiProxy::IsFGReady()) || (state.activeFgOutput == FGOutput::XeFG && XeFGProxy::Module() != nullptr) || - (state.activeFgOutput == FGOutput::DLSSG && StreamlineProxy::Module() != nullptr))) + (state.activeFgOutput == FGOutput::DLSSG && StreamlineProxy::Module() != nullptr) || + state.activeFgOutput == FGOutput::Reprojection)) { const bool dx11HudfixTracking = state.swapchainInteropApi == SwapchainInteropApi::Dx11wDx12; const bool hudfixTrackingSupported =