Skip to content

Commit

Permalink
UI Compat: Expand scope to bespoke systems outside of AHUD
Browse files Browse the repository at this point in the history
This is achieved by hooking GetRenderTargetTexture
There are some giveaways from where it's being called
that we can use to return our UI render target instead of the scene
  • Loading branch information
praydog committed Feb 5, 2024
1 parent 4c3aaf9 commit bf9bb50
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/CommitHash.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#pragma once
#define UEVR_COMMIT_HASH "18ed2b9982e9573bc6c9c5be4c99e354b1c8acdf"
#define UEVR_BUILD_DATE "04.02.2024"
#define UEVR_COMMIT_HASH "4c3aaf93799e9dbe4170f051cfc28915be6ad0fe"
#define UEVR_BUILD_DATE "05.02.2024"
#define UEVR_BUILD_TIME "00:00"
112 changes: 102 additions & 10 deletions src/mods/vr/FFakeStereoRenderingHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1724,20 +1724,107 @@ void FFakeStereoRenderingHook::viewport_draw_hook(void* viewport, bool should_pr
call_orig();
}

// This function needs some more work for more rigorous filtering
// However it does its job on the relevant titles
// This is only used for the UI compatibility mode.
FRHITexture2D** FFakeStereoRenderingHook::viewport_get_render_target_texture_hook(sdk::FViewport* viewport) {
const auto retaddr = (uintptr_t)_ReturnAddress();
static std::unordered_set<uintptr_t> redirected_retaddrs{};
static std::unordered_set<uintptr_t> call_original_retaddrs{};
static std::recursive_mutex retaddr_mutex{};
static bool has_view_family_tex{false};

SPDLOG_INFO_ONCE("FViewport::GetRenderTargetTexture called!");
const auto og = g_hook->m_viewport_get_render_target_texture_hook->get_original<decltype(&viewport_get_render_target_texture_hook)>();
const auto& vr = VR::get();

if (!vr->is_ahud_compatibility_enabled() || !vr->is_hmd_active() || g_hook->m_slate_draw_window_thread_id == 0) {
return og(viewport);
}

{
std::scoped_lock _{retaddr_mutex};

if (call_original_retaddrs.contains(retaddr)) {
return og(viewport);
}

// Hacky way to allow the first texture to go through
// For the games that are using something other than ViewFamilyTexture as the scene RT.
if (!call_original_retaddrs.empty() && !redirected_retaddrs.contains(retaddr) && !has_view_family_tex) {
return og(viewport);
}

if (!redirected_retaddrs.contains(retaddr) && !call_original_retaddrs.contains(retaddr)) {
SPDLOG_INFO("FViewport::GetRenderTargetTexture called from {:x}", retaddr);

// Analyze surrounding code to determine if this is a valid call.
auto func_start = utility::find_function_start(retaddr);

if (!func_start) {
func_start = retaddr;
}

// The function that has this string reference should ALWAYS get passed
// back to the original function, this is the actual scene render target.
// Everything else we will redirect to the UI render target.
if (utility::find_string_reference_in_path(*func_start, L"ViewFamilyTexture", false)) {
SPDLOG_INFO("Found view family texture reference @ {:x}", retaddr);
call_original_retaddrs.insert(retaddr);
has_view_family_tex = true;
return og(viewport);
}

// Probably NOT...
/*if (utility::find_string_reference_in_path(*func_start, L"r.RHICmdAsyncRHIThreadDispatch")) {
SPDLOG_INFO("Found RHICmdAsyncRHIThreadDispatch reference @ {:x}", retaddr);
call_original_retaddrs.insert(retaddr);
return og(viewport);
}*/

if (utility::find_string_reference_in_path(*func_start, L"FinalPostProcessColor", false)) {
SPDLOG_INFO("Found FinalPostProcessColor reference @ {:x}", retaddr);
call_original_retaddrs.insert(retaddr);
return og(viewport);
}

// TODO? this needs some more rigorous filtering
// some games are insane and have multiple "UnknownTexture" references...
if (utility::find_string_reference_in_path(*func_start, L"UnknownTexture", false)) {
SPDLOG_INFO("Found unknown texture reference @ {:x}", retaddr);
call_original_retaddrs.insert(retaddr);
return og(viewport);
}

SPDLOG_INFO("Redirecting FViewport::GetRenderTargetTexture call to UI render target @ {:x}", retaddr);
redirected_retaddrs.insert(retaddr);
}
}

// Finally redirect the call to the UI render target.
auto& ui_target = g_hook->get_render_target_manager()->get_ui_target();

if (ui_target != nullptr) {
return &ui_target;
}

return og(viewport);
}

void FFakeStereoRenderingHook::game_viewport_client_draw_hook(sdk::UGameViewportClient* viewport_client, sdk::FViewport* viewport, sdk::FCanvas* canvas, void* a4) {
ZoneScopedN(__FUNCTION__);

// AHUD Compatibility
// Replaces the canvas with the debug canvas if it's available.
// The debug canvas is captured by the Slate UI system, whereas the scene canvas is not.
// This allows the UI to be displayed correctly in VR.
// TODO: Probably fix the scissor and/or view rects.
// Also TODO: Figure out how games that bypass this render directly to the scene RT anyways.
// UI compatibility mode
// Tries to redirect calls to GetRenderTargetTexture to point towards our UI
// texture instead of the scene render target, if it's not the scene itself/the view family texture.
// This usually isn't needed but sometimes there are bespoke changes to the rendering pipeline
// or uses of the AHUD class that make it necessary.
if (g_framework->is_game_data_intialized() && VR::get()->is_ahud_compatibility_enabled() && viewport != nullptr) {
const auto debug_canvas = viewport->get_debug_canvas();

if (debug_canvas != nullptr) {
canvas = debug_canvas;
if (g_hook->m_viewport_get_render_target_texture_hook == nullptr) {
SPDLOG_INFO("Hooking FViewport::GetRenderTargetTexture...");
void** vp_vtable = *(void***)viewport;
g_hook->m_viewport_get_render_target_texture_hook = std::make_unique<PointerHook>(&vp_vtable[1], &viewport_get_render_target_texture_hook);
SPDLOG_INFO("Hooked FViewport::GetRenderTargetTexture!");
}
}

Expand Down Expand Up @@ -4816,13 +4903,18 @@ void* FFakeStereoRenderingHook::slate_draw_window_render_thread(void* renderer,
mod->on_pre_slate_draw_window(renderer, command_list, viewport_info);
}

g_hook->m_inside_slate_draw_window = true;
g_hook->m_slate_draw_window_thread_id = GetCurrentThreadId();

auto call_orig = [&]() {
auto ret = g_hook->m_slate_thread_hook.call<void*>(renderer, command_list, viewport_info, elements, params, unk1, unk2);

for (auto& mod : mods) {
mod->on_post_slate_draw_window(renderer, command_list, viewport_info);
}

g_hook->m_inside_slate_draw_window = false;

return ret;
};

Expand Down
6 changes: 5 additions & 1 deletion src/mods/vr/FFakeStereoRenderingHook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct VRRenderTargetManager_Base {
bool need_reallocate_depth_texture(const void* DepthTarget);

public:
FRHITexture2D* get_ui_target() { return ui_target; }
FRHITexture2D*& get_ui_target() { return ui_target; }
FRHITexture2D* get_render_target() { return render_target; }
void set_render_target(FRHITexture2D* rt) { render_target = rt; }

Expand Down Expand Up @@ -341,6 +341,7 @@ class FFakeStereoRenderingHook : public ModComponent {

// FViewport
static void viewport_draw_hook(void* viewport, bool should_present);
static FRHITexture2D** viewport_get_render_target_texture_hook(sdk::FViewport* viewport);

// UGameViewportClient
static void game_viewport_client_draw_hook(sdk::UGameViewportClient*, sdk::FViewport*, sdk::FCanvas*, void*);
Expand Down Expand Up @@ -388,6 +389,7 @@ class FFakeStereoRenderingHook : public ModComponent {
std::unique_ptr<PointerHook> m_get_desired_number_of_views_hook{};
std::unique_ptr<PointerHook> m_get_view_pass_for_index_hook{};
std::unique_ptr<PointerHook> m_update_viewport_rhi_hook{};
std::unique_ptr<PointerHook> m_viewport_get_render_target_texture_hook{};

std::unique_ptr<IXRTrackingSystemHook> m_tracking_system_hook{};

Expand Down Expand Up @@ -428,7 +430,9 @@ class FFakeStereoRenderingHook : public ModComponent {
bool m_has_view_extension_hook{false};
bool m_has_game_viewport_client_draw_hook{false};
bool m_skip_next_adjust_view_rect{true};
bool m_inside_slate_draw_window{false};
int32_t m_skip_next_adjust_view_rect_count{1};
uint32_t m_slate_draw_window_thread_id{0};

// Synchronized AFR
float m_ignored_engine_delta{0.0f};
Expand Down

0 comments on commit bf9bb50

Please sign in to comment.