22#include < future>
33#include < unordered_set>
44#include < stacktrace>
5+ #include < wrl/client.h>
56
67#include < spdlog/spdlog.h>
78#include < utility/Thread.hpp>
89#include < utility/Module.hpp>
910#include < utility/String.hpp>
1011#include < utility/RTTI.hpp>
12+ #include < utility/Scan.hpp>
13+ #include < utility/ScopeGuard.hpp>
1114
1215#include " REFramework.hpp"
1316
1619#include " D3D12Hook.hpp"
1720
1821static D3D12Hook* g_d3d12_hook = nullptr ;
22+ thread_local bool g_inside_d3d12_hook = false ;
1923
2024D3D12Hook::~D3D12Hook () {
2125 unhook ();
2226}
2327
28+ void * D3D12Hook::Streamline::link_swapchain_to_cmd_queue (void * rcx, void * rdx, void * r8, void * r9) {
29+ if (g_inside_d3d12_hook) {
30+ spdlog::info (" [Streamline] linkSwapchainToCmdQueue: {:x} (inside D3D12 hook)" , (uintptr_t )_ReturnAddress ());
31+
32+ auto & hook = D3D12Hook::s_streamline.link_swapchain_to_cmd_queue_hook ;
33+ return hook->get_original <decltype (link_swapchain_to_cmd_queue)>()(rcx, rdx, r8, r9);
34+ }
35+
36+ std::scoped_lock _{g_framework->get_hook_monitor_mutex ()};
37+
38+ spdlog::info (" [Streamline] linkSwapchainToCmdQueue: {:x}" , (uintptr_t )_ReturnAddress ());
39+
40+ bool hook_was_nullptr = g_d3d12_hook == nullptr ;
41+
42+ if (g_d3d12_hook != nullptr ) {
43+ g_framework->on_reset (); // Needed to prevent a crash due to resources hanging around
44+ g_d3d12_hook->unhook (); // Removes all vtable hooks
45+ }
46+
47+ auto & hook = D3D12Hook::s_streamline.link_swapchain_to_cmd_queue_hook ;
48+ const auto result = hook->get_original <decltype (link_swapchain_to_cmd_queue)>()(rcx, rdx, r8, r9);
49+
50+ // Re-hooks present after the above function creates the swapchain
51+ // This allows the hook to immediately still function
52+ // rather than waiting on the hook monitor to notice the hook isn't working
53+ if (!hook_was_nullptr) {
54+ g_framework->hook_d3d12 ();
55+ }
56+
57+ return result;
58+ }
59+
60+ void D3D12Hook::hook_streamline (HMODULE dlssg_module) try {
61+ if (D3D12Hook::s_streamline.setup ) {
62+ return ;
63+ }
64+
65+ std::scoped_lock _{D3D12Hook::s_streamline.hook_mutex };
66+
67+ if (D3D12Hook::s_streamline.setup ) {
68+ return ;
69+ }
70+
71+ spdlog::info (" [Streamline] Hooking Streamline" );
72+
73+ if (dlssg_module == nullptr ) {
74+ dlssg_module = GetModuleHandleW (L" sl.dlss_g.dll" );
75+ }
76+
77+ if (dlssg_module == nullptr ) {
78+ spdlog::error (" [Streamline] Failed to get sl.dlss_g.dll module handle" );
79+ return ;
80+ }
81+
82+ const auto str = utility::scan_string (dlssg_module, " linkSwapchainToCmdQueue" );
83+
84+ if (!str) {
85+ spdlog::error (" [Streamline] Failed to find linkSwapchainToCmdQueue" );
86+ return ;
87+ }
88+
89+ const auto str_ref = utility::scan_displacement_reference (dlssg_module, *str);
90+
91+ if (!str_ref) {
92+ spdlog::error (" [Streamline] Failed to find linkSwapchainToCmdQueue reference" );
93+ return ;
94+ }
95+
96+ const auto fn = utility::find_function_start_with_call (*str_ref);
97+
98+ if (!fn) {
99+ spdlog::error (" [Streamline] Failed to find linkSwapchainToCmdQueue function" );
100+ return ;
101+ }
102+
103+ D3D12Hook::s_streamline.link_swapchain_to_cmd_queue_hook = std::make_unique<FunctionHook>(*fn, (uintptr_t )&Streamline::link_swapchain_to_cmd_queue);
104+
105+ if (D3D12Hook::s_streamline.link_swapchain_to_cmd_queue_hook ->create ()) {
106+ spdlog::info (" [Streamline] Hooked linkSwapchainToCmdQueue" );
107+ } else {
108+ spdlog::error (" [Streamline] Failed to hook linkSwapchainToCmdQueue" );
109+ }
110+
111+ D3D12Hook::s_streamline.setup = true ;
112+ } catch (...) {
113+ spdlog::error (" [Streamline] Failed to hook Streamline" );
114+ }
115+
24116bool D3D12Hook::hook () {
25117 spdlog::info (" Hooking D3D12" );
26118
27119 g_d3d12_hook = this ;
28120
121+ g_inside_d3d12_hook = true ;
122+
123+ utility::ScopeGuard guard{[]() {
124+ g_inside_d3d12_hook = false ;
125+ }};
126+
29127 IDXGISwapChain1* swap_chain1{ nullptr };
30128 IDXGISwapChain3* swap_chain{ nullptr };
31129 ID3D12Device* device{ nullptr };
@@ -330,7 +428,9 @@ bool D3D12Hook::hook() {
330428 return false ;
331429 }
332430
333- utility::ThreadSuspender suspender{};
431+ hook_streamline ();
432+
433+ // utility::ThreadSuspender suspender{};
334434
335435 try {
336436 spdlog::info (" Initializing hooks" );
@@ -341,20 +441,20 @@ bool D3D12Hook::hook() {
341441 m_is_phase_1 = true ;
342442
343443 auto & present_fn = (*(void ***)target_swapchain)[8 ]; // Present
344- m_present_hook = std::make_unique<PointerHook>(&present_fn, ( void *) &D3D12Hook::present);
444+ m_present_hook = std::make_unique<PointerHook>(&present_fn, &D3D12Hook::present);
345445 m_hooked = true ;
346446 } catch (const std::exception& e) {
347447 spdlog::error (" Failed to initialize hooks: {}" , e.what ());
348448 m_hooked = false ;
349449 }
350450
351- suspender.resume ();
451+ // suspender.resume();
352452
353- device->Release ();
354453 command_queue->Release ();
355- factory->Release ();
356454 swap_chain1->Release ();
357455 swap_chain->Release ();
456+ device->Release ();
457+ factory->Release ();
358458
359459 if (hwnd) {
360460 ::DestroyWindow (hwnd);
@@ -368,6 +468,8 @@ bool D3D12Hook::hook() {
368468}
369469
370470bool D3D12Hook::unhook () {
471+ std::scoped_lock _{g_framework->get_hook_monitor_mutex ()};
472+
371473 if (!m_hooked) {
372474 return true ;
373475 }
@@ -385,57 +487,67 @@ bool D3D12Hook::unhook() {
385487
386488thread_local int32_t g_present_depth = 0 ;
387489
388- HRESULT WINAPI D3D12Hook::present (IDXGISwapChain3* swap_chain, UINT sync_interval, UINT flags) {
490+ HRESULT WINAPI D3D12Hook::present (IDXGISwapChain3* swap_chain, uint64_t sync_interval, uint64_t flags, void * r9 ) {
389491 std::scoped_lock _{g_framework->get_hook_monitor_mutex ()};
390492
391493 auto d3d12 = g_d3d12_hook;
392494
393- HWND swapchain_wnd{nullptr };
394- swap_chain->GetHwnd (&swapchain_wnd);
395-
396495 decltype (D3D12Hook::present)* present_fn{nullptr };
397496
398- // if (d3d12->m_is_phase_1) {
497+ if (d3d12->m_is_phase_1 ) {
399498 present_fn = d3d12->m_present_hook ->get_original <decltype (D3D12Hook::present)*>();
400- /* } else {
499+ } else {
401500 present_fn = d3d12->m_swapchain_hook ->get_method <decltype (D3D12Hook::present)*>(8 );
402- }*/
501+ }
502+
503+ HWND swapchain_wnd{nullptr };
504+ swap_chain->GetHwnd (&swapchain_wnd);
403505
404506 if (d3d12->m_is_phase_1 && WindowFilter::get ().is_filtered (swapchain_wnd)) {
405507 // present_fn = d3d12->m_present_hook->get_original<decltype(D3D12Hook::present)*>();
406- return present_fn (swap_chain, sync_interval, flags);
508+ return present_fn (swap_chain, sync_interval, flags, r9 );
407509 }
408510
409511 if (!d3d12->m_is_phase_1 && swap_chain != d3d12->m_swapchain_hook ->get_instance ()) {
410- return present_fn (swap_chain, sync_interval, flags);
512+ return present_fn (swap_chain, sync_interval, flags, r9 );
411513 }
412514
413515 if (d3d12->m_is_phase_1 ) {
414- // d3d12->m_present_hook.reset();
516+ // Remove the present hook, we will just rely on the vtable hook below
517+ // because we don't want to cause any conflicts with other hooks
518+ // vtable hooks are the least intrusive
519+ // And doing a global pointer replacement seems to have
520+ // conflicts with Streamline's hooks, causing unexplainable crashes
521+ d3d12->m_present_hook .reset ();
415522
416523 // vtable hook the swapchain instead of global hooking
417524 // this seems safer for whatever reason
418525 // if we globally hook the vtable pointers, it causes all sorts of weird conflicts with other hooks
419526 // dont hook present though via this hook so other hooks dont get confused
420527 d3d12->m_swapchain_hook = std::make_unique<VtableHook>(swap_chain);
421- // d3d12->m_swapchain_hook->hook_method(8, (uintptr_t)&D3D12Hook::present);
528+ // d3d12->m_swapchain_hook->hook_method(2, (uintptr_t)&D3D12Hook::release);
529+ d3d12->m_swapchain_hook ->hook_method (8 , (uintptr_t )&D3D12Hook::present);
422530 d3d12->m_swapchain_hook ->hook_method (13 , (uintptr_t )&D3D12Hook::resize_buffers);
423531 d3d12->m_swapchain_hook ->hook_method (14 , (uintptr_t )&D3D12Hook::resize_target);
424532 d3d12->m_is_phase_1 = false ;
533+
534+ present_fn = d3d12->m_swapchain_hook ->get_method <decltype (D3D12Hook::present)*>(8 );
425535 }
426536
427537 d3d12->m_inside_present = true ;
428538 d3d12->m_swap_chain = swap_chain;
429539
430- swap_chain->GetDevice (IID_PPV_ARGS (&d3d12->m_device ));
540+ {
541+ Microsoft::WRL ::ComPtr<ID3D12Device4> temp_device{};
542+ swap_chain->GetDevice (IID_PPV_ARGS (&temp_device));
543+ d3d12->m_device = temp_device.Get ();
544+ }
431545
432- if (d3d12->m_device != nullptr ) {
433- if (d3d12->m_using_proton_swapchain ) {
434- const auto real_swapchain = *(uintptr_t *)((uintptr_t )swap_chain + d3d12->m_proton_swapchain_offset );
435- d3d12->m_command_queue = *(ID3D12CommandQueue**)(real_swapchain + d3d12->m_command_queue_offset );
436- } else {
437- d3d12->m_command_queue = *(ID3D12CommandQueue**)((uintptr_t )swap_chain + d3d12->m_command_queue_offset );
438- }
546+ if (d3d12->m_using_proton_swapchain ) {
547+ const auto real_swapchain = *(uintptr_t *)((uintptr_t )swap_chain + d3d12->m_proton_swapchain_offset );
548+ d3d12->m_command_queue = *(ID3D12CommandQueue**)(real_swapchain + d3d12->m_command_queue_offset );
549+ } else {
550+ d3d12->m_command_queue = *(ID3D12CommandQueue**)((uintptr_t )swap_chain + d3d12->m_command_queue_offset );
439551 }
440552
441553 if (d3d12->m_swapchain_0 == nullptr ) {
@@ -462,7 +574,7 @@ HRESULT WINAPI D3D12Hook::present(IDXGISwapChain3* swap_chain, UINT sync_interva
462574 spdlog::info (" Attempting to call real present function" );
463575
464576 ++g_present_depth;
465- const auto result = present_fn (swap_chain, sync_interval, flags);
577+ const auto result = present_fn (swap_chain, sync_interval, flags, r9 );
466578 --g_present_depth;
467579
468580 if (result != S_OK ) {
@@ -485,7 +597,7 @@ HRESULT WINAPI D3D12Hook::present(IDXGISwapChain3* swap_chain, UINT sync_interva
485597 auto result = S_OK ;
486598
487599 if (!d3d12->m_ignore_next_present ) {
488- result = present_fn (swap_chain, sync_interval, flags);
600+ result = present_fn (swap_chain, sync_interval, flags, r9 );
489601
490602 if (result != S_OK ) {
491603 spdlog::error (" Present failed: {:x}" , result);
0 commit comments