diff --git a/src/core/commands/HotkeySystem.cpp b/src/core/commands/HotkeySystem.cpp index 2928645..f96bcce 100644 --- a/src/core/commands/HotkeySystem.cpp +++ b/src/core/commands/HotkeySystem.cpp @@ -1,6 +1,8 @@ #include "HotkeySystem.hpp" #include "core/backend/FiberPool.hpp" #include "core/backend/ScriptMgr.hpp" +#include "core/renderer/D3D12Hook.hpp" +#include "core/renderer/Renderer.hpp" #include "Commands.hpp" #include "LoopedCommand.hpp" #include "core/util/Joaat.hpp" @@ -95,7 +97,7 @@ namespace YimMenu { while (g_Running) { - if (GetForegroundWindow() == *Pointers.Hwnd && !HUD::IS_PAUSE_MENU_ACTIVE() && !HUD::IS_SOCIAL_CLUB_ACTIVE() && !m_BeingModified && !GUI::IsUsingKeyboard()) + if (GetForegroundWindow() == D3D12Hook::GetWindow() && !HUD::IS_PAUSE_MENU_ACTIVE() && !HUD::IS_SOCIAL_CLUB_ACTIVE() && !m_BeingModified && (Renderer::IsInitialized() && !GUI::IsUsingKeyboard())) { for (auto& [hash, link] : m_CommandHotkeys) { diff --git a/src/core/hooking/Hooking.cpp b/src/core/hooking/Hooking.cpp index 24867e4..f143e66 100644 --- a/src/core/hooking/Hooking.cpp +++ b/src/core/hooking/Hooking.cpp @@ -10,14 +10,8 @@ namespace YimMenu { Hooking::Hooking() { - BaseHook::Add(new DetourHook("WndProc", Pointers.WndProc, Hooks::Window::WndProc)); - BaseHook::Add(new DetourHook("GetRawInputData", reinterpret_cast(GetProcAddress(LoadLibraryA("user32.dll"), "GetRawInputData")), Hooks::RawInput::GetRawInputData)); - auto swapchain_vft = *reinterpret_cast(*Pointers.SwapChain); - BaseHook::Add(new DetourHook("Present", swapchain_vft[Hooks::SwapChain::VMTPresentIdx], Hooks::SwapChain::Present)); - BaseHook::Add(new DetourHook("ResizeBuffers", swapchain_vft[Hooks::SwapChain::VMTResizeBuffersIdx], Hooks::SwapChain::ResizeBuffers)); - // BaseHook::Add(new DetourHook("QueueDependency", Pointers.QueueDependency, Hooks::Anticheat::QueueDependency)); BaseHook::Add(new DetourHook("GameSkeletonUpdate", Pointers.GameSkeletonUpdate, Hooks::Anticheat::GameSkeletonUpdate)); BaseHook::Add(new DetourHook("PrepareMetricForSending", Pointers.PrepareMetricForSending, Hooks::Anticheat::PrepareMetricForSending)); diff --git a/src/core/renderer/D3D12Hook.cpp b/src/core/renderer/D3D12Hook.cpp new file mode 100644 index 0000000..b3bc38d --- /dev/null +++ b/src/core/renderer/D3D12Hook.cpp @@ -0,0 +1,433 @@ +#include "D3D12Hook.hpp" + +namespace YimMenu +{ + static thread_local bool g_InsideD3d12Hook = false; + + bool D3D12Hook::RawVtablePatch::Install(void** vtable, size_t index, void* newFn) + { + if (!vtable) + return false; + + Slot = &vtable[index]; + OriginalFn = *Slot; + + DWORD oldProtect{}; + if (!VirtualProtect(Slot, sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProtect)) // we still need this in case the menu is injected very early + return false; + + *Slot = newFn; + + VirtualProtect(Slot, sizeof(void*), oldProtect, &oldProtect); + + Installed = true; + return true; + } + + void D3D12Hook::RawVtablePatch::Uninstall() + { + if (!Installed || !Slot) + return; + + DWORD oldProtect{}; + if (VirtualProtect(Slot, sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProtect)) + { + *Slot = OriginalFn; + VirtualProtect(Slot, sizeof(void*), oldProtect, &oldProtect); + } + + Installed = false; + Slot = nullptr; + OriginalFn = nullptr; + } + + bool D3D12Hook::InitImpl() + { + std::scoped_lock hookLock{m_HookMutex}; + + g_InsideD3d12Hook = true; + struct Restore + { + ~Restore() + { + g_InsideD3d12Hook = false; + } + } restore{}; + + // if this is a rehook after a swapchain recreation + if (m_CommandQueueOffset != 0 && m_SwapchainVtable && m_FactoryVtable) + { + LOGF(INFO, "Re-initializing via previously discovered vtables."); + + InstallHooks(); + return m_Hooked; + } + + IDXGISwapChain1* swapChain1 = nullptr; + IDXGISwapChain3* swapChain = nullptr; + ID3D12Device* device = nullptr; + + const D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; + DXGI_SWAP_CHAIN_DESC1 swapChainDesc1{}; + swapChainDesc1.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swapChainDesc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDesc1.BufferCount = 2; + swapChainDesc1.SampleDesc.Count = 1; + swapChainDesc1.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + swapChainDesc1.Width = 1; + swapChainDesc1.Height = 1; + + auto d3d12Module = LoadLibraryA("d3d12.dll"); + if (!d3d12Module) + { + LOGF(FATAL, "Failed to load d3d12.dll."); + return false; + } + + auto d3d12CreateDevice = reinterpret_cast(GetProcAddress(d3d12Module, "D3D12CreateDevice")); + if (!d3d12CreateDevice) + { + LOGF(FATAL, "Failed to resolve D3D12CreateDevice."); + return false; + } + + bool deviceCreated = !FAILED(d3d12CreateDevice(nullptr, featureLevel, IID_PPV_ARGS(&device))); + if (!deviceCreated) + { + LOGF(FATAL, "Failed to create dummy D3D12 device."); + return false; + } + + auto dxgiModule = LoadLibraryA("dxgi.dll"); + if (!dxgiModule) + { + LOGF(FATAL, "Failed to load dxgi.dll."); + device->Release(); + return false; + } + + auto createDxgiFactory = reinterpret_cast(GetProcAddress(dxgiModule, "CreateDXGIFactory")); + if (!createDxgiFactory) + { + LOGF(FATAL, "Failed to resolve CreateDXGIFactory."); + device->Release(); + return false; + } + + IDXGIFactory4* factory = nullptr; + if (FAILED(createDxgiFactory(IID_PPV_ARGS(&factory)))) + { + LOGF(FATAL, "Failed to create the dummy DXGI factory."); + device->Release(); + return false; + } + + D3D12_COMMAND_QUEUE_DESC queueDesc{}; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + + ID3D12CommandQueue* commandQueue = nullptr; + if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)))) + { + LOGF(FATAL, "Failed to create the dummy command queue."); + factory->Release(); + device->Release(); + return false; + } + + HWND hwnd = nullptr; + WNDCLASSEXA wc{}; + + auto initDummyWindow = [&]() { + wc.cbSize = sizeof(WNDCLASSEXA); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = DefWindowProcA; + wc.hInstance = GetModuleHandleA(nullptr); + wc.lpszClassName = "YimMenuDummyWindow"; + + RegisterClassExA(&wc); + hwnd = CreateWindowA(wc.lpszClassName, "YimMenu Dummy Window", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, nullptr, nullptr, wc.hInstance, nullptr); + + swapChainDesc1.BufferCount = 3; + swapChainDesc1.Width = 0; + swapChainDesc1.Height = 0; + swapChainDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + swapChainDesc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc1.SampleDesc.Count = 1; + swapChainDesc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + swapChainDesc1.Scaling = DXGI_SCALING_STRETCH; + }; + + const std::vector> swapchainAttempts{ + [&]() { + return !FAILED(factory->CreateSwapChainForComposition(commandQueue, &swapChainDesc1, nullptr, &swapChain1)); + }, + [&]() { + initDummyWindow(); + return !FAILED(factory->CreateSwapChainForHwnd(commandQueue, hwnd, &swapChainDesc1, nullptr, nullptr, &swapChain1)); + }, + [&]() { + return !FAILED(factory->CreateSwapChainForHwnd(commandQueue, GetDesktopWindow(), &swapChainDesc1, nullptr, nullptr, &swapChain1)); + }, + }; + + bool anySucceeded = false; + for (size_t i = 0; i < swapchainAttempts.size(); i++) + { + try + { + if (swapchainAttempts[i]()) + { + LOGF(INFO, "Created dummy swapchain via attempt {}.", i); + anySucceeded = true; + break; + } + } + catch (const std::exception& e) + { + LOGF(FATAL, "Dummy swapchain attempt {} threw: {}", i, e.what()); + } + catch (...) + { + LOGF(FATAL, "Dummy swapchain attempt {} threw an unknown exception.", i); + } + } + + auto cleanupDummyWindow = [&]() { + if (hwnd) + DestroyWindow(hwnd); + if (wc.lpszClassName) + UnregisterClassA(wc.lpszClassName, wc.hInstance); + }; + + if (!anySucceeded) + { + LOGF(FATAL, "Failed to create a dummy swapchain via any method."); + cleanupDummyWindow(); + commandQueue->Release(); + factory->Release(); + device->Release(); + return false; + } + + if (FAILED(swapChain1->QueryInterface(IID_PPV_ARGS(&swapChain)))) + { + LOGF(FATAL, "Failed to query IDXGISwapChain3 from the dummy swapchain."); + cleanupDummyWindow(); + swapChain1->Release(); + commandQueue->Release(); + factory->Release(); + device->Release(); + return false; + } + + m_CommandQueueOffset = 0; + + for (uint32_t i = 0; i < 512 * sizeof(void*); i += sizeof(void*)) + { + auto base = reinterpret_cast(swapChain1) + i; + if (IsBadReadPtr(reinterpret_cast(base), sizeof(void*))) + break; + + if (*reinterpret_cast(base) == commandQueue) + { + m_CommandQueueOffset = i; + LOGF(INFO, "Found command queue offset at {:#x}.", i); + break; + } + } + + auto targetSwapchain = swapChain; + + if (m_CommandQueueOffset == 0) + { + bool shouldBreak = false; + for (uint32_t base = 0; base < 512 * sizeof(void*) && !shouldBreak; base += sizeof(void*)) + { + auto preScanBase = reinterpret_cast(swapChain1) + base; + if (IsBadReadPtr(reinterpret_cast(preScanBase), sizeof(void*))) + break; + + auto scanBase = *reinterpret_cast(preScanBase); + if (scanBase == 0 || IsBadReadPtr(reinterpret_cast(scanBase), sizeof(void*))) + continue; + + for (uint32_t i = 0; i < 512 * sizeof(void*); i += sizeof(void*)) + { + auto preData = scanBase + i; + if (IsBadReadPtr(reinterpret_cast(preData), sizeof(void*))) + break; + + if (*reinterpret_cast(preData) == commandQueue) + { + targetSwapchain = reinterpret_cast(scanBase); + m_CommandQueueOffset = i; + shouldBreak = true; + + LOGF(INFO, "Found nested command queue offset at {:#x}.", i); + break; + } + } + } + } + + if (m_CommandQueueOffset == 0) + { + LOGF(FATAL, "Failed to find the command queue offset."); + cleanupDummyWindow(); + swapChain->Release(); + swapChain1->Release(); + commandQueue->Release(); + factory->Release(); + device->Release(); + return false; + } + + m_SwapchainVtable = *reinterpret_cast(targetSwapchain); + m_FactoryVtable = *reinterpret_cast(factory); + InstallHooks(); + + commandQueue->Release(); + swapChain1->Release(); + swapChain->Release(); + device->Release(); + factory->Release(); + cleanupDummyWindow(); + + if (!m_Hooked) + return false; + + LOGF(INFO, "D3D12 Hook initialized."); + return true; + } + + bool D3D12Hook::DestroyImpl(bool unloading) + { + std::scoped_lock hookLock{m_HookMutex}; + + if (!m_Hooked) + return true; + + m_PresentPatch.Uninstall(); + m_SwapchainVmt.reset(); + + if (unloading) + m_CreateSwapchainPatch.Uninstall(); + + m_Hooked = false; + m_IsPhase1 = true; + + return true; + } + + void D3D12Hook::InstallHooks() + { + m_PresentPatch.Uninstall(); + m_SwapchainVmt.reset(); + + m_IsPhase1 = true; + + if (!m_PresentPatch.Install(m_SwapchainVtable, 8, reinterpret_cast(&D3D12Hook::Present))) + { + LOGF(FATAL, "Failed to install the phase 1 Present vtable patch."); + m_Hooked = false; + return; + } + + if (!m_CreateSwapchainPatch.Installed) + { + if (!m_CreateSwapchainPatch.Install(m_FactoryVtable, 15, reinterpret_cast(&D3D12Hook::CreateSwapchain))) + LOGF(FATAL, "Failed to install the CreateSwapChain patch."); + } + + m_Hooked = true; + } + + HRESULT D3D12Hook::Present(IDXGISwapChain3* swapChain, UINT syncInterval, UINT flags, void* r9) + { + std::scoped_lock hookLock{GetInstance().m_HookMutex}; + + auto presentFn = GetInstance().m_IsPhase1 ? GetInstance().m_PresentPatch.Original() : GetInstance().m_SwapchainVmt->Original(8); + + HWND swapchainWnd = nullptr; + { + DXGI_SWAP_CHAIN_DESC swapchainDesc{}; + if (SUCCEEDED(swapChain->GetDesc(&swapchainDesc))) + swapchainWnd = swapchainDesc.OutputWindow; + } + + if (!GetInstance().m_IsPhase1 && swapChain != GetInstance().m_SwapChain) + return presentFn(swapChain, syncInterval, flags, r9); + + if (GetInstance().m_IsPhase1) // caught the real present, switch to per instance vmt hook + { + GetInstance().m_PresentPatch.Uninstall(); + + GetInstance().m_SwapchainVmt = std::make_unique("D3D12SwapChain", swapChain, 64); // should be enough + GetInstance().m_SwapchainVmt->Hook(8, reinterpret_cast(&D3D12Hook::Present)); + GetInstance().m_SwapchainVmt->Hook(13, reinterpret_cast(&D3D12Hook::ResizeBuffers)); + GetInstance().m_SwapchainVmt->Enable(); + + GetInstance().m_IsPhase1 = false; + presentFn = GetInstance().m_SwapchainVmt->Original(8); + } + + GetInstance().m_Window = swapchainWnd; + GetInstance().m_SwapChain = swapChain; + + { + Microsoft::WRL::ComPtr tempDevice{}; + swapChain->GetDevice(IID_PPV_ARGS(&tempDevice)); + GetInstance().m_Device = tempDevice.Get(); + } + + GetInstance().m_CommandQueue = *reinterpret_cast(reinterpret_cast(swapChain) + GetInstance().m_CommandQueueOffset); + + if (GetInstance().m_OnPresent) + GetInstance().m_OnPresent(); + + return presentFn(swapChain, syncInterval, flags, r9); + } + + HRESULT D3D12Hook::ResizeBuffers(IDXGISwapChain3* swapChain, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags) + { + std::scoped_lock hookLock{GetInstance().m_HookMutex}; + + auto resizeBuffersFn = GetInstance().m_SwapchainVmt->Original(13); + + if (GetInstance().m_OnResizeBuffers) + GetInstance().m_OnResizeBuffers(); + + return resizeBuffersFn(swapChain, bufferCount, width, height, newFormat, swapChainFlags); + } + + HRESULT D3D12Hook::CreateSwapchain(IDXGIFactory4* factory, IUnknown* device, HWND hwnd, const DXGI_SWAP_CHAIN_DESC* desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, IDXGIOutput* pRestrictToOutput, IDXGISwapChain** swapChain) + { + auto createSwapChainFn = GetInstance().m_CreateSwapchainPatch.Original(); + + if (g_InsideD3d12Hook) + return createSwapChainFn(factory, device, hwnd, desc, pFullscreenDesc, pRestrictToOutput, swapChain); + + LOGF(INFO, "CreateSwapChain called, a new swapchain is being created."); + + std::scoped_lock hookLock{GetInstance().m_HookMutex}; + + bool wasHooked = GetInstance().m_Hooked; + + if (wasHooked) + { + if (GetInstance().m_OnDeviceReset) + GetInstance().m_OnDeviceReset(); + + GetInstance().Destroy(); + } + + if (wasHooked) + GetInstance().Init(); + + return createSwapChainFn(factory, device, hwnd, desc, pFullscreenDesc, pRestrictToOutput, swapChain); + } +} \ No newline at end of file diff --git a/src/core/renderer/D3D12Hook.hpp b/src/core/renderer/D3D12Hook.hpp new file mode 100644 index 0000000..45abf32 --- /dev/null +++ b/src/core/renderer/D3D12Hook.hpp @@ -0,0 +1,111 @@ +// Credits to: +// https://github.com/praydog/REFramework/blob/master/src/D3D12Hook.cpp +// https://github.com/praydog/REFramework/blob/master/src/D3D12Hook.hpp +#pragma once +#include "core/hooking/VMTHook.hpp" +#include +#include +#include + +namespace YimMenu +{ + class D3D12Hook + { + public: + static bool Init() + { + return GetInstance().InitImpl(); + } + + static bool Destroy(bool unloading = false) + { + return GetInstance().DestroyImpl(unloading); + } + + static void OnPresent(std::function fn) + { + GetInstance().m_OnPresent = std::move(fn); + } + + static void OnResizeBuffers(std::function fn) + { + GetInstance().m_OnResizeBuffers = std::move(fn); + } + + static void OnDeviceReset(std::function fn) + { + GetInstance().m_OnDeviceReset = std::move(fn); + } + + static ID3D12Device4* GetDevice() + { + return GetInstance().m_Device; + } + + static IDXGISwapChain3* GetSwapChain() + { + return GetInstance().m_SwapChain; + } + + static ID3D12CommandQueue* GetCommandQueue() + { + return GetInstance().m_CommandQueue; + } + + static HWND GetWindow() + { + return GetInstance().m_Window; + } + + private: + // VMTHook clones the vtable per-instance (so hooking one object never + // affects another), which is exactly the opposite of what phase 1 needs, so + // this one raw patch is done by hand. + struct RawVtablePatch + { + bool Install(void** vtable, size_t index, void* newFn); + void Uninstall(); + + template + T Original() const + { + return reinterpret_cast(OriginalFn); + } + + void** Slot = nullptr; + void* OriginalFn = nullptr; + bool Installed = false; + }; + + static D3D12Hook& GetInstance() + { + static D3D12Hook instance; + return instance; + } + + bool InitImpl(); + bool DestroyImpl(bool unloading); + void InstallHooks(); + + static HRESULT Present(IDXGISwapChain3* swapChain, UINT syncInterval, UINT flags, void* r9); + static HRESULT ResizeBuffers(IDXGISwapChain3* swapChain, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags); + static HRESULT CreateSwapchain(IDXGIFactory4* factory, IUnknown* device, HWND hwnd, const DXGI_SWAP_CHAIN_DESC* desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, IDXGIOutput* pRestrictToOutput, IDXGISwapChain** swapChain); + + std::function m_OnPresent = nullptr; + std::function m_OnResizeBuffers = nullptr; + std::function m_OnDeviceReset = nullptr; + ID3D12Device4* m_Device = nullptr; + ID3D12CommandQueue* m_CommandQueue = nullptr; + IDXGISwapChain3* m_SwapChain = nullptr; + HWND m_Window = nullptr; + bool m_Hooked = false; + bool m_IsPhase1 = true; + uint32_t m_CommandQueueOffset = 0; + void** m_FactoryVtable = nullptr; + void** m_SwapchainVtable = nullptr; + RawVtablePatch m_CreateSwapchainPatch = {}; + RawVtablePatch m_PresentPatch = {}; + std::unique_ptr m_SwapchainVmt = {}; + std::recursive_mutex m_HookMutex = {}; + }; +} \ No newline at end of file diff --git a/src/core/renderer/Renderer.cpp b/src/core/renderer/Renderer.cpp index d7c365f..5f16881 100644 --- a/src/core/renderer/Renderer.cpp +++ b/src/core/renderer/Renderer.cpp @@ -1,394 +1,391 @@ #include "Renderer.hpp" - -#include "core/memory/ModuleMgr.hpp" -#include "core/memory/PatternScanner.hpp" -#include "game/frontend/GUI.hpp" +#include "D3D12Hook.hpp" +#include "core/frontend/manager/styles/Themes.hpp" #include "game/frontend/Menu.hpp" -#include "game/pointers/Pointers.hpp" - #include #include #include +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM); namespace YimMenu { - Renderer::Renderer() : - m_Initialized(false), - m_Resizing(false), - m_FontsUpdated(false), - m_SafeToRender(false) - { - } + void Renderer::InitImpl() + { + D3D12Hook::OnPresent([this] { + if (!m_Initialized) + { + if (!InitInternal()) + return; + } - Renderer::~Renderer() - { - } + Render(); + }); - void Renderer::DestroyImpl() - { - if (!m_Initialized) - return; + D3D12Hook::OnResizeBuffers([this] { + Resize(); + }); - // TODO: we aren't destroying resources properly - ImGui_ImplWin32_Shutdown(); + D3D12Hook::OnDeviceReset([this] { + Destroy(); + }); + } + bool Renderer::InitInternal() + { + auto device = D3D12Hook::GetDevice(); + auto swapChain = D3D12Hook::GetSwapChain(); + auto queue = D3D12Hook::GetCommandQueue(); + auto window = D3D12Hook::GetWindow(); + if (!device || !swapChain || !queue || !window) + { + //LOGF(FATAL, "Invalid D3D12Hook data."); + return false; + } - WaitForLastFrame(); - ImGui_ImplDX12_InvalidateDeviceObjects(); + DXGI_SWAP_CHAIN_DESC desc{}; + swapChain->GetDesc(&desc); + UINT bufferCount = desc.BufferCount > 0 ? desc.BufferCount : 3; - for (size_t i{}; i != GetInstance().m_SwapChainDesc.BufferCount; ++i) - { - REL(GetInstance().m_FrameContext[i].Resource); - } + if (!CreateDeviceObjects(device, queue, bufferCount)) + { + LOGF(FATAL, "Failed to create device objects."); + return false; + } - ImGui_ImplDX12_Shutdown(); + CreateRenderTargets(swapChain); - ImGui::DestroyContext(); + m_Window = window; + m_OriginalWndProc = reinterpret_cast(SetWindowLongPtrA(m_Window, GWLP_WNDPROC, reinterpret_cast(&Renderer::WndProc))); -#if 0 - // manually destroy the allocators we created for the rest of the frame contexts - for (size_t i = 1; i < m_SwapChainDesc.BufferCount; ++i) - if (m_FrameContext[i].CommandAllocator) - m_FrameContext[i].CommandAllocator->Release(); -#endif - } + ImGui::CreateContext(); - bool Renderer::InitDX12() - { - if (!Pointers.SwapChain) - { - LOG(WARNING) << "SwapChain pointer is invalid!"; + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + ImGui::StyleColorsDark(); + + ImGui_ImplWin32_Init(m_Window); + + ImGui_ImplDX12_InitInfo initInfo{}; + initInfo.Device = device; + initInfo.CommandQueue = queue; + initInfo.NumFramesInFlight = static_cast(bufferCount); + initInfo.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + initInfo.DSVFormat = DXGI_FORMAT_UNKNOWN; + initInfo.SrvDescriptorHeap = m_SrvHeap.Get(); + initInfo.UserData = &m_SrvAllocator; + initInfo.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE* outCpu, D3D12_GPU_DESCRIPTOR_HANDLE* outGpu) { + static_cast(info->UserData)->Alloc(outCpu, outGpu); + }; + initInfo.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu) { + static_cast(info->UserData)->Free(cpu, gpu); + }; + + if (!ImGui_ImplDX12_Init(&initInfo)) + { + LOGF(FATAL, "ImGui_ImplDX12_Init failed."); + + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + + if (m_Window && m_OriginalWndProc) + SetWindowLongPtrA(m_Window, GWLP_WNDPROC, reinterpret_cast(m_OriginalWndProc)); + + m_Window = nullptr; + m_OriginalWndProc = nullptr; return false; - } + } - if (!Pointers.CommandQueue) + Menu::SetupFonts(); + SetupStyle(); + GUI::ToggleMouse(); + + m_Initialized = true; + LOGF(INFO, "Renderer initialized."); + return true; + } + + void Renderer::DestroyImpl() + { + if (!m_Initialized && !m_DeviceObjectsValid) + return; + + ReleaseRenderTargets(); + + if (m_Initialized) + { + ImGui_ImplDX12_Shutdown(); + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + } + + if (m_Window && m_OriginalWndProc) + SetWindowLongPtrA(m_Window, GWLP_WNDPROC, reinterpret_cast(m_OriginalWndProc)); + + if (m_FenceEvent) + { + CloseHandle(m_FenceEvent); + m_FenceEvent = nullptr; + } + + m_FrameContexts.clear(); + m_BackBuffers.clear(); + m_RtvHandles.clear(); + m_CommandList.Reset(); + m_Fence.Reset(); + m_RtvHeap.Reset(); + m_SrvHeap.Reset(); + + m_Initialized = false; + m_DeviceObjectsValid = false; + m_Window = nullptr; + m_OriginalWndProc = nullptr; + } + + void Renderer::Render() + { + auto device = D3D12Hook::GetDevice(); + auto swapChain = D3D12Hook::GetSwapChain(); + auto queue = D3D12Hook::GetCommandQueue(); + if (!swapChain) + return; + + // if a resize happened, our cached backbuffer resources are stale, refresh them + ComPtr currentAtIndex{}; + auto index = swapChain->GetCurrentBackBufferIndex(); + + if (index >= m_BackBuffers.size() || m_BackBuffers[index] == nullptr) + { + CreateRenderTargets(swapChain); + } + else + { + swapChain->GetBuffer(index, IID_PPV_ARGS(¤tAtIndex)); + if (currentAtIndex.Get() != m_BackBuffers[index].Get()) + CreateRenderTargets(swapChain); + } + + auto& frame = m_FrameContexts[index]; + + // wait for the GPU to be done with this frame context's allocator before reusing it + if (m_Fence->GetCompletedValue() < frame.FenceValue) + { + m_Fence->SetEventOnCompletion(frame.FenceValue, m_FenceEvent); + WaitForSingleObject(m_FenceEvent, INFINITE); + } + + frame.Allocator->Reset(); + m_CommandList->Reset(frame.Allocator.Get(), nullptr); + + if (m_SafeToRender) + { + D3D12_RESOURCE_BARRIER toRt{}; + toRt.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + toRt.Transition.pResource = m_BackBuffers[index].Get(); + toRt.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + toRt.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + toRt.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + m_CommandList->ResourceBarrier(1, &toRt); + + m_CommandList->OMSetRenderTargets(1, &m_RtvHandles[index], FALSE, nullptr); + ID3D12DescriptorHeap* heaps[] = {m_SrvHeap.Get()}; + m_CommandList->SetDescriptorHeaps(1, heaps); + + if (m_FontsUpdated) + { + InvalidateDeviceObjects(); + DXGI_SWAP_CHAIN_DESC desc{}; + swapChain->GetDesc(&desc); + UINT bufferCount = desc.BufferCount > 0 ? desc.BufferCount : 3; + CreateDeviceObjects(device, queue, bufferCount); + ImGui_ImplDX12_CreateDeviceObjects(); + m_FontsUpdated = false; + } + + ImGui_ImplDX12_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + for (const auto& callback : m_RendererCallbacks | std::views::values) + callback(); + + ImGui::Render(); + ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_CommandList.Get()); + + D3D12_RESOURCE_BARRIER toPresent{}; + toPresent.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + toPresent.Transition.pResource = m_BackBuffers[index].Get(); + toPresent.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + toPresent.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + toPresent.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + m_CommandList->ResourceBarrier(1, &toPresent); + + m_CommandList->Close(); + + ID3D12CommandList* lists[] = {m_CommandList.Get()}; + D3D12Hook::GetCommandQueue()->ExecuteCommandLists(1, lists); + + ++m_FenceValue; + D3D12Hook::GetCommandQueue()->Signal(m_Fence.Get(), m_FenceValue); + frame.FenceValue = m_FenceValue; + } + else { - LOG(WARNING) << "CommandQueue pointer is invalid!"; - - return false; + m_CommandList->Close(); } + } + + void Renderer::Resize() + { + if (!m_Initialized) + return; - //This is required. In order to stop ComPtr from releasing the original pointer, we create a new ComPtr with the ptr as the intializer. (The '=' operator uses swap which releases the object passed into it) - if (m_GameSwapChain = ComPtr(*Pointers.SwapChain); !m_GameSwapChain.Get()) - { - LOG(WARNING) << "Dereferenced SwapChain pointer is invalid!"; + ReleaseRenderTargets(); + } + + bool Renderer::CreateDeviceObjects(ID3D12Device* device, ID3D12CommandQueue* queue, UINT bufferCount) + { + m_BufferCount = bufferCount; - return false; - } + D3D12_DESCRIPTOR_HEAP_DESC rtvDesc{}; + rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtvDesc.NumDescriptors = bufferCount; + rtvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - if (m_CommandQueue = ComPtr(*Pointers.CommandQueue); !m_CommandQueue.Get()) - { - LOG(WARNING) << "Dereferenced CommandQueue pointer is invalid!"; + if (FAILED(device->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(&m_RtvHeap)))) + return false; - return false; - } - - m_GameSwapChain.As(&m_SwapChain); //We need GetCurrentBackBufferIndex from IDXGISwapChain3 + D3D12_DESCRIPTOR_HEAP_DESC srvDesc{}; + srvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + srvDesc.NumDescriptors = 64; + srvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (const auto result = m_SwapChain->GetDevice(__uuidof(ID3D12Device), reinterpret_cast(m_Device.GetAddressOf())); result < 0) - { - LOG(WARNING) << "Failed to get D3D Device with result: [" << result << "]"; - - return false; - } - - if (const auto result = m_SwapChain->GetDesc(&m_SwapChainDesc); result < 0) - { - LOG(WARNING) << "Failed to get SwapChain Description with result: [" << result << "]"; - - return false; - } - - if (const auto result = m_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), (void**)m_Fence.GetAddressOf()); result < 0) - { - LOG(WARNING) << "Failed to create Fence with result: [" << result << "]"; - - return false; - } - - if (const auto result = m_FenceEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr); !result) - { - LOG(WARNING) << "Failed to create Fence Event!"; - - return false; - } - - m_FrameContext.resize(m_SwapChainDesc.BufferCount); - - D3D12_DESCRIPTOR_HEAP_DESC DescriptorDesc{D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_SwapChainDesc.BufferCount, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE}; - if (const auto result = - m_Device->CreateDescriptorHeap(&DescriptorDesc, __uuidof(ID3D12DescriptorHeap), (void**)m_DescriptorHeap.GetAddressOf()); - result < 0) - { - LOG(WARNING) << "Failed to create Descriptor Heap with result: [" << result << "]"; - - return false; - } - - if (const auto result = m_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, - __uuidof(ID3D12CommandAllocator), - (void**)m_CommandAllocator.GetAddressOf()); - result < 0) - { - LOG(WARNING) << "Failed to create primary Command Allocator with result: [" << result << "]"; - - return false; - } - - m_FrameContext[0].CommandAllocator = m_CommandAllocator.Get(); // set initial command allocator - - // create the rest of the allocators - for (size_t i = 1; i < m_SwapChainDesc.BufferCount; ++i) - { - if (const auto result = m_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void**)&m_FrameContext[i].CommandAllocator); result < 0) - { - LOG(WARNING) << "Failed to create secondary Command Allocator with result: [" << result << "]"; - - return false; - } - } - - if (const auto result = m_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_CommandAllocator.Get(), NULL, __uuidof(ID3D12GraphicsCommandList), (void**)m_CommandList.GetAddressOf()); result < 0) - { - LOG(WARNING) << "Failed to create Command List with result: [" << result << "]"; - - return false; - } - - if (const auto result = m_CommandList->Close(); result < 0) - { - LOG(WARNING) << "Failed to finalize the creation of Command List with result: [" << result << "]"; - - return false; - } - - D3D12_DESCRIPTOR_HEAP_DESC DescriptorBackbufferDesc{D3D12_DESCRIPTOR_HEAP_TYPE_RTV, m_SwapChainDesc.BufferCount, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1}; - if (const auto result = m_Device->CreateDescriptorHeap(&DescriptorBackbufferDesc, - __uuidof(ID3D12DescriptorHeap), - (void**)m_BackbufferDescriptorHeap.GetAddressOf()); - result < 0) - { - LOG(WARNING) << "Failed to create Backbuffer Descriptor Heap with result: [" << result << "]"; - - return false; - } - - const auto RTVDescriptorSize{m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)}; - D3D12_CPU_DESCRIPTOR_HANDLE RTVHandle{m_BackbufferDescriptorHeap->GetCPUDescriptorHandleForHeapStart()}; - for (size_t i = 0; i < m_SwapChainDesc.BufferCount; ++i) - { - ComPtr BackBuffer{}; - m_FrameContext[i].Descriptor = RTVHandle; - m_SwapChain->GetBuffer(i, __uuidof(ID3D12Resource), (void**)BackBuffer.GetAddressOf()); - m_Device->CreateRenderTargetView(BackBuffer.Get(), nullptr, RTVHandle); - m_FrameContext[i].Resource = BackBuffer.Get(); - RTVHandle.ptr += RTVDescriptorSize; - } - - m_HeapAllocator.Create(m_Device.Get(), m_DescriptorHeap.Get()); - - // never returns false, useless to check return - ImGui::CreateContext(); - ImGui_ImplWin32_Init(*Pointers.Hwnd); - - ImGui_ImplDX12_InitInfo init_info = {}; - init_info.Device = m_Device.Get(); - init_info.CommandQueue = m_CommandQueue.Get(); - init_info.NumFramesInFlight = m_SwapChainDesc.BufferCount; - init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM; - init_info.DSVFormat = DXGI_FORMAT_UNKNOWN; - // Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks. - // (current version of the backend will only allocate one descriptor, future versions will need to allocate more) - init_info.SrvDescriptorHeap = m_DescriptorHeap.Get(); - init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) { - return GetInstance().m_HeapAllocator.Alloc(out_cpu_handle, out_gpu_handle); - }; - init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle) { - return GetInstance().m_HeapAllocator.Free(cpu_handle, gpu_handle); - }; - ImGui_ImplDX12_Init(&init_info); - - ImGui::StyleColorsDark(); - - LOG(INFO) << "DirectX 12 renderer has finished initializing."; - m_Initialized = true; - return true; - } - - bool Renderer::InitImpl() - { - while (!*Pointers.Hwnd || !*Pointers.ScreenResX) - { - std::this_thread::sleep_for(1s); - } - - LOG(INFO) << "Using DX12, clear shader cache if you're having issues."; - return InitDX12(); - } - - bool Renderer::AddRendererCallBackImpl(RendererCallBack&& callback, std::uint32_t priority) - { - return m_RendererCallBacks.insert({priority, callback}).second; - } - - void Renderer::AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback) - { - return m_WindowProcedureCallbacks.push_back(callback); - } - - void Renderer::DX12OnPresentImpl() - { - if (!m_SafeToRender) - return; - - Renderer::DX12NewFrame(); - for (const auto& callback : m_RendererCallBacks | std::views::values) - callback(); - Renderer::DX12EndFrame(); - } - - LRESULT Renderer::WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) - { - for (const auto& callback : m_WindowProcedureCallbacks) - callback(hwnd, msg, wparam, lparam); - - return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam); - } - - void Renderer::ResizeImpl(float scale) - { - DX12PreResize(); - - if (scale != 1.0f) - ImGui::GetStyle().ScaleAllSizes(scale); - ImGui::GetStyle().MouseCursorScale = 1.0f; - ImGui::GetIO().FontGlobalScale = scale; - DX12PostResize(); - } - - void Renderer::WaitForLastFrame() - { - FrameContext FrameCtx = GetInstance().m_FrameContext[GetInstance().m_FrameIndex % GetInstance().m_SwapChainDesc.BufferCount]; - - UINT64 FenceValue = FrameCtx.FenceValue; - - if (FenceValue == 0) - { - return; - } - - FrameCtx.FenceValue = 0; - - if (GetInstance().m_Fence->GetCompletedValue() >= FenceValue) - { - return; - } - - GetInstance().m_Fence->SetEventOnCompletion(FenceValue, GetInstance().m_FenceEvent); - - WaitForSingleObject(GetInstance().m_FenceEvent, INFINITE); - } - - void Renderer::WaitForNextFrame() - { - UINT NextFrameIndex = GetInstance().m_FrameIndex + 1; - GetInstance().m_FrameIndex = NextFrameIndex; - - HANDLE WaitableObjects[] = {GetInstance().m_SwapchainWaitableObject, nullptr}; - DWORD NumWaitableObjets = 1; - - FrameContext FrameCtx = GetInstance().m_FrameContext[NextFrameIndex % GetInstance().m_SwapChainDesc.BufferCount]; - UINT64 FenceValue = FrameCtx.FenceValue; - if (FenceValue != 0) // means no fence was signaled - { - FrameCtx.FenceValue = 0; - GetInstance().m_Fence->SetEventOnCompletion(FenceValue, GetInstance().m_FenceEvent); - WaitableObjects[1] = GetInstance().m_FenceEvent; - NumWaitableObjets = 2; - } - - WaitForMultipleObjects(NumWaitableObjets, WaitableObjects, TRUE, INFINITE); - } - - void Renderer::DX12PreResize() - { - SetResizing(true); - - WaitForLastFrame(); - - ImGui_ImplDX12_InvalidateDeviceObjects(); - - for (size_t i{}; i != GetInstance().m_SwapChainDesc.BufferCount; ++i) - { - REL(GetInstance().m_FrameContext[i].Resource); - } - } - - void Renderer::DX12PostResize() - { - //Recreate our pointers and ImGui's - ImGui_ImplDX12_CreateDeviceObjects(); - const auto RTVDescriptorSize{GetInstance().m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)}; - D3D12_CPU_DESCRIPTOR_HANDLE RTVHandle{GetInstance().m_BackbufferDescriptorHeap->GetCPUDescriptorHandleForHeapStart()}; - for (size_t i{}; i != GetInstance().m_SwapChainDesc.BufferCount; ++i) - { - ComPtr BackBuffer{}; - GetInstance().m_FrameContext[i].Descriptor = RTVHandle; - GetInstance().m_SwapChain->GetBuffer(i, __uuidof(ID3D12Resource), (void**)BackBuffer.GetAddressOf()); - GetInstance().m_Device->CreateRenderTargetView(BackBuffer.Get(), nullptr, RTVHandle); - GetInstance().m_FrameContext[i].Resource = BackBuffer.Get(); - RTVHandle.ptr += RTVDescriptorSize; - } - - SetResizing(false); - } - - void Renderer::DX12NewFrame() - { - if (GetInstance().m_FontsUpdated) - { - DX12PreResize(); - DX12PostResize(); - GetInstance().m_FontsUpdated = false; - } - - ImGui_ImplDX12_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - } - - void Renderer::DX12EndFrame() - { - WaitForNextFrame(); - - FrameContext& CurrentFrameContext{GetInstance().m_FrameContext[GetInstance().m_SwapChain->GetCurrentBackBufferIndex()]}; - CurrentFrameContext.CommandAllocator->Reset(); - - D3D12_RESOURCE_BARRIER Barrier{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, - D3D12_RESOURCE_BARRIER_FLAG_NONE, - {{CurrentFrameContext.Resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET}}}; - GetInstance().m_CommandList->Reset(CurrentFrameContext.CommandAllocator, nullptr); - GetInstance().m_CommandList->ResourceBarrier(1, &Barrier); - GetInstance().m_CommandList->OMSetRenderTargets(1, &CurrentFrameContext.Descriptor, FALSE, nullptr); - GetInstance().m_CommandList->SetDescriptorHeaps(1, GetInstance().m_DescriptorHeap.GetAddressOf()); - - ImGui::Render(); - - ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), GetInstance().m_CommandList.Get()); - - Barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - Barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - GetInstance().m_CommandList->ResourceBarrier(1, &Barrier); - GetInstance().m_CommandList->Close(); - - ID3D12CommandList* CommandLists[]{GetInstance().m_CommandList.Get()}; - GetInstance().m_CommandQueue->ExecuteCommandLists(_countof(CommandLists), CommandLists); - - UINT64 FenceValue = GetInstance().m_FenceLastSignaledValue + 1; - GetInstance().m_CommandQueue->Signal(GetInstance().m_Fence.Get(), FenceValue); - GetInstance().m_FenceLastSignaledValue = FenceValue; - CurrentFrameContext.FenceValue = FenceValue; - } + if (FAILED(device->CreateDescriptorHeap(&srvDesc, IID_PPV_ARGS(&m_SrvHeap)))) + return false; + + m_SrvAllocator.Init(device, m_SrvHeap.Get()); + + auto rtvIncrement = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + auto rtvStart = m_RtvHeap->GetCPUDescriptorHandleForHeapStart(); + + m_FrameContexts.resize(bufferCount); + m_RtvHandles.resize(bufferCount); + m_BackBuffers.resize(bufferCount); + + for (UINT i = 0; i < bufferCount; i++) + { + if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_FrameContexts[i].Allocator)))) + return false; + + m_RtvHandles[i] = {rtvStart.ptr + static_cast(i) * rtvIncrement}; + } + + if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_FrameContexts[0].Allocator.Get(), nullptr, IID_PPV_ARGS(&m_CommandList)))) + return false; + + m_CommandList->Close(); + + if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_Fence)))) + return false; + + m_FenceEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr); + m_DeviceObjectsValid = true; + return true; + } + + void Renderer::CreateRenderTargets(IDXGISwapChain3* swapChain) + { + ComPtr owningDevice{}; + swapChain->GetDevice(IID_PPV_ARGS(&owningDevice)); + + for (UINT i = 0; i < m_BufferCount; ++i) + { + if (FAILED(swapChain->GetBuffer(i, IID_PPV_ARGS(&m_BackBuffers[i])))) + continue; + + owningDevice->CreateRenderTargetView(m_BackBuffers[i].Get(), nullptr, m_RtvHandles[i]); + } + } + + void Renderer::ReleaseRenderTargets() + { + if (m_Fence && m_FenceEvent) + { + auto value = ++m_FenceValue; + if (SUCCEEDED(m_Fence->Signal(value)) && m_Fence->GetCompletedValue() < value) + { + m_Fence->SetEventOnCompletion(value, m_FenceEvent); + WaitForSingleObject(m_FenceEvent, 2000); + } + } + + for (auto& bb : m_BackBuffers) + bb.Reset(); + } + + LRESULT Renderer::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) + { + if (GetInstance().m_Initialized) + { + for (const auto& callback : GetInstance().m_WindowProcedureCallbacks) + callback(hwnd, msg, wparam, lparam); + + ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam); + } + + if (GetInstance().m_OriginalWndProc) + return CallWindowProcA(GetInstance().m_OriginalWndProc, hwnd, msg, wparam, lparam); + + return DefWindowProcA(hwnd, msg, wparam, lparam); + } + + void Renderer::InvalidateDeviceObjects() + { + if (!m_DeviceObjectsValid) + return; + + ReleaseRenderTargets(); + + if (m_Initialized) + ImGui_ImplDX12_InvalidateDeviceObjects(); + + m_DeviceObjectsValid = false; + } + + void Renderer::SrvHeapAllocator::Init(ID3D12Device* device, ID3D12DescriptorHeap* srvHeap) + { + Heap = srvHeap; + DescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + HeapStartCpu = Heap->GetCPUDescriptorHandleForHeapStart(); + HeapStartGpu = Heap->GetGPUDescriptorHandleForHeapStart(); + + FreeIndices.clear(); + FreeIndices.reserve(64); + for (int i = 64 - 1; i >= 0; --i) + FreeIndices.push_back(i); + } + + void Renderer::SrvHeapAllocator::Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* outCpu, D3D12_GPU_DESCRIPTOR_HANDLE* outGpu) + { + if (FreeIndices.empty()) + { + LOGF(FATAL, "SRV heap exhausted."); + *outCpu = HeapStartCpu; + *outGpu = HeapStartGpu; + return; + } + + int index = FreeIndices.back(); + FreeIndices.pop_back(); + + outCpu->ptr = HeapStartCpu.ptr + static_cast(index) * DescriptorSize; + outGpu->ptr = HeapStartGpu.ptr + static_cast(index) * DescriptorSize; + } + + void Renderer::SrvHeapAllocator::Free(D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu) + { + int index = static_cast((cpu.ptr - HeapStartCpu.ptr) / DescriptorSize); + FreeIndices.push_back(index); + } } \ No newline at end of file diff --git a/src/core/renderer/Renderer.hpp b/src/core/renderer/Renderer.hpp index 2e6962c..71cccac 100644 --- a/src/core/renderer/Renderer.hpp +++ b/src/core/renderer/Renderer.hpp @@ -1,159 +1,39 @@ #pragma once -#include "core/util/Joaat.hpp" - -#include -#include #include #include -#include -#include -#include #include - -#define REL(o) \ - o->Release(); \ - if (o) \ - { \ - o = nullptr; \ - } - namespace YimMenu { - using namespace Microsoft::WRL; - using RendererCallBack = std::function; - using WindowProcedureCallback = std::function; + class Renderer + { + public: + using RendererCallback = std::function; + using WindowProcedureCallback = std::function; - struct FrameContext - { - ID3D12CommandAllocator* CommandAllocator; - ID3D12Resource* Resource; - D3D12_CPU_DESCRIPTOR_HANDLE Descriptor; - UINT64 FenceValue; - }; + static void Init() + { + GetInstance().InitImpl(); + } - struct ExampleDescriptorHeapAllocator - { - ID3D12DescriptorHeap* Heap = nullptr; - D3D12_DESCRIPTOR_HEAP_TYPE HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; - D3D12_CPU_DESCRIPTOR_HANDLE HeapStartCpu; - D3D12_GPU_DESCRIPTOR_HANDLE HeapStartGpu; - UINT HeapHandleIncrement; - ImVector FreeIndices; + static void Destroy() + { + GetInstance().DestroyImpl(); + } - void Create(ID3D12Device* device, ID3D12DescriptorHeap* heap) + static void AddRendererCallback(RendererCallback&& callback, uint32_t priority) + { + GetInstance().m_RendererCallbacks.insert({priority, callback}); + } + + static void AddWindowProcedureCallback(WindowProcedureCallback&& callback) + { + GetInstance().m_WindowProcedureCallbacks.push_back(callback); + } + + static bool IsInitialized() { - IM_ASSERT(Heap == nullptr && FreeIndices.empty()); - Heap = heap; - D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc(); - HeapType = desc.Type; - HeapStartCpu = Heap->GetCPUDescriptorHandleForHeapStart(); - HeapStartGpu = Heap->GetGPUDescriptorHandleForHeapStart(); - HeapHandleIncrement = device->GetDescriptorHandleIncrementSize(HeapType); - FreeIndices.reserve((int)desc.NumDescriptors); - for (int n = desc.NumDescriptors; n > 0; n--) - FreeIndices.push_back(n - 1); - } - void Destroy() - { - Heap = nullptr; - FreeIndices.clear(); - } - void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle) - { - IM_ASSERT(FreeIndices.Size > 0); - int idx = FreeIndices.back(); - FreeIndices.pop_back(); - out_cpu_desc_handle->ptr = HeapStartCpu.ptr + (idx * HeapHandleIncrement); - out_gpu_desc_handle->ptr = HeapStartGpu.ptr + (idx * HeapHandleIncrement); - } - void Free(D3D12_CPU_DESCRIPTOR_HANDLE out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE out_gpu_desc_handle) - { - int cpu_idx = (int)((out_cpu_desc_handle.ptr - HeapStartCpu.ptr) / HeapHandleIncrement); - int gpu_idx = (int)((out_gpu_desc_handle.ptr - HeapStartGpu.ptr) / HeapHandleIncrement); - IM_ASSERT(cpu_idx == gpu_idx); - FreeIndices.push_back(cpu_idx); - } - }; - - class Renderer final - { - private: - Renderer(); - - public: - ~Renderer(); - - Renderer(const Renderer&) = delete; - Renderer(Renderer&&) noexcept = delete; - Renderer& operator=(const Renderer&) = delete; - Renderer& operator=(Renderer&&) noexcept = delete; - - static void Destroy() - { - GetInstance().DestroyImpl(); - } - static bool Init() - { - return GetInstance().InitImpl(); - } - - /** - * @brief Add a callback function to draw using ImGui - * - * @param callback Callback function - * @param priority Low values will be drawn before higher values. - * @return true Successfully added callback. - * @return false Duplicate render priority was given. - */ - static bool AddRendererCallBack(RendererCallBack&& callback, std::uint32_t priority) - { - return GetInstance().AddRendererCallBackImpl(std::move(callback), priority); - } - /** - * @brief Add a callback function to handle Windows WindowProcedure - * - * @param callback Callback function - */ - static void AddWindowProcedureCallback(WindowProcedureCallback&& callback) - { - GetInstance().AddWindowProcedureCallbackImpl(std::move(callback)); - } - - static void DX12OnPresent() - { - GetInstance().DX12OnPresentImpl(); - } - - static LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) - { - return GetInstance().WndProcImpl(hwnd, msg, wparam, lparam); - } - - static void WaitForLastFrame(); - static void WaitForNextFrame(); - - static void DX12PreResize(); - static void DX12PostResize(); - - static bool IsResizing() - { - return GetInstance().m_Resizing; - } - - static void SetResizing(const bool& status) - { - GetInstance().m_Resizing = status; - } - - static void Resize(float scale) - { - GetInstance().ResizeImpl(scale); - } - - static void SetFontsUpdated() - { - GetInstance().m_FontsUpdated = true; + return GetInstance().m_Initialized; } static void SetSafeToRender() @@ -161,70 +41,70 @@ namespace YimMenu GetInstance().m_SafeToRender = true; } - private: - static void DX12NewFrame(); - static void DX12EndFrame(); - - private: - void DestroyImpl(); - bool InitImpl(); - - bool InitDX12(); - - void ResizeImpl(float scale); - - bool AddRendererCallBackImpl(RendererCallBack&& callback, std::uint32_t priority); - void AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback); - - void DX12OnPresentImpl(); - - LRESULT WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - - static Renderer& GetInstance() + static void SetFontsUpdated() { - static Renderer i{}; - - return i; + GetInstance().m_FontsUpdated = true; } - public: - ExampleDescriptorHeapAllocator m_HeapAllocator; + private: + template + using ComPtr = Microsoft::WRL::ComPtr; - private: - bool m_Initialized; - bool m_Resizing; - bool m_SafeToRender; + struct SrvHeapAllocator + { + void Init(ID3D12Device* device, ID3D12DescriptorHeap* heap); + void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* outCpu, D3D12_GPU_DESCRIPTOR_HANDLE* outGpu); + void Free(D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu); - // Shared - ImFontAtlas m_FontAtlas; + ID3D12DescriptorHeap* Heap = nullptr; + UINT DescriptorSize = 0; + D3D12_CPU_DESCRIPTOR_HANDLE HeapStartCpu = {}; + D3D12_GPU_DESCRIPTOR_HANDLE HeapStartGpu = {}; + std::vector FreeIndices = {}; + }; - //DX12 - std::vector m_FrameContext; + struct FrameContext + { + ComPtr Allocator = {}; + UINT64 FenceValue = 0; + }; - DXGI_SWAP_CHAIN_DESC m_SwapChainDesc; - ComPtr m_GameSwapChain; - ComPtr m_SwapChain; - ComPtr m_Device; - ComPtr m_CommandQueue; - ComPtr m_CommandAllocator; - ComPtr m_CommandList; - ComPtr m_BackbufferDescriptorHeap; - ComPtr m_DescriptorHeap; + static Renderer& GetInstance() + { + static Renderer instance; + return instance; + } - ComPtr m_Fence; - HANDLE m_FenceEvent{}; - UINT64 m_FenceLastSignaledValue{}; - HANDLE m_SwapchainWaitableObject{}; - UINT64 m_FrameIndex{}; + void InitImpl(); + void DestroyImpl(); + bool InitInternal(); + void Render(); + void Resize(); + bool CreateDeviceObjects(ID3D12Device* device, ID3D12CommandQueue* queue, UINT bufferCount); + void CreateRenderTargets(IDXGISwapChain3* swapChain); + void ReleaseRenderTargets(); + void InvalidateDeviceObjects(); - bool m_FontsUpdated; + static LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - private: - //Other - std::map m_RendererCallBacks; - std::vector m_WindowProcedureCallbacks; - }; -} - -// Make our linker aware of the ImGui WndProcHandler -extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); \ No newline at end of file + bool m_Initialized = false; + bool m_DeviceObjectsValid = false; + bool m_SafeToRender = false; + bool m_FontsUpdated = false; + HWND m_Window = nullptr; + WNDPROC m_OriginalWndProc = nullptr; + ComPtr m_RtvHeap = {}; + ComPtr m_SrvHeap = {}; + SrvHeapAllocator m_SrvAllocator = {}; + std::vector m_FrameContexts = {}; + std::vector> m_BackBuffers = {}; + std::vector m_RtvHandles = {}; + UINT m_BufferCount = 0; + ComPtr m_CommandList = {}; + ComPtr m_Fence = {}; + HANDLE m_FenceEvent = nullptr; + UINT64 m_FenceValue = 0; + std::map m_RendererCallbacks; + std::vector m_WindowProcedureCallbacks; + }; +} \ No newline at end of file diff --git a/src/game/frontend/GUI.cpp b/src/game/frontend/GUI.cpp index fb8002a..f10d67f 100644 --- a/src/game/frontend/GUI.cpp +++ b/src/game/frontend/GUI.cpp @@ -11,49 +11,45 @@ #include "game/frontend/ChatDisplay.hpp" #include "game/gta/Natives.hpp" #include "types/pad/ControllerInputs.hpp" -#include "core/frontend/manager/styles/Themes.hpp" namespace YimMenu { - GUI::GUI() : - m_IsOpen(false) + void GUI::InitImpl() { - Menu::SetupFonts(); - SetupStyle(); Menu::Init(); Renderer::AddWindowProcedureCallback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { GUI::WndProc(hwnd, msg, wparam, lparam); }); - Renderer::AddRendererCallBack( + Renderer::AddRendererCallback( [&] { Notifications::Draw(); }, -2); - Renderer::AddRendererCallBack( + Renderer::AddRendererCallback( [&] { ESP::Draw(); }, -3); - Renderer::AddRendererCallBack( + Renderer::AddRendererCallback( [&] { ChatDisplay::Draw(); }, -5); - Renderer::AddRendererCallBack( + Renderer::AddRendererCallback( [&] { Overlay::Draw(); }, -6); - Renderer::AddRendererCallBack( + Renderer::AddRendererCallback( [] { LuaManager::ForAllLoadedScripts([](std::shared_ptr& script) { script->GetUserInterface().DrawAlwaysDrawImGuiCallbacks(); }); }, -7); - Renderer::AddRendererCallBack( + Renderer::AddRendererCallback( [] { if (!GUI::IsOpen()) return; @@ -66,10 +62,6 @@ namespace YimMenu Renderer::SetSafeToRender(); } - GUI::~GUI() - { - } - void GUI::ToggleMouse() { auto& io = ImGui::GetIO(); @@ -110,7 +102,7 @@ namespace YimMenu { while (g_Running) { - if (GUI::IsOpen()) + if (Renderer::IsInitialized() && GUI::IsOpen()) { if (UIManager::ShowingContentWindow()) { diff --git a/src/game/frontend/GUI.hpp b/src/game/frontend/GUI.hpp index 81438c6..28abde2 100644 --- a/src/game/frontend/GUI.hpp +++ b/src/game/frontend/GUI.hpp @@ -1,24 +1,13 @@ #pragma once -#include namespace YimMenu { class GUI final { - private: - GUI(); - public: - ~GUI(); - - GUI(const GUI&) = delete; - GUI(GUI&&) noexcept = delete; - GUI& operator=(const GUI&) = delete; - GUI& operator=(GUI&&) noexcept = delete; - static void Init() { - GetInstance().m_IsOpen = false; + GetInstance().InitImpl(); } static bool IsOpen() @@ -51,13 +40,15 @@ namespace YimMenu return GetInstance().m_Onboarding; } + static void ToggleMouse(); + private: void WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + void InitImpl(); void SetOnboardingImpl(bool state); void RunScriptImpl(); - static void ToggleMouse(); static GUI& GetInstance() { static GUI i{}; @@ -65,7 +56,6 @@ namespace YimMenu return i; } - private: bool m_IsOpen; bool m_Onboarding; }; diff --git a/src/game/frontend/Menu.cpp b/src/game/frontend/Menu.cpp index fb4ac81..8b55d5b 100644 --- a/src/game/frontend/Menu.cpp +++ b/src/game/frontend/Menu.cpp @@ -1,13 +1,7 @@ #include "Menu.hpp" -#include "imgui.h" -#include "imgui_internal.h" -#include "core/commands/Commands.hpp" #include "core/frontend/manager/UIManager.hpp" #include "core/renderer/Renderer.hpp" -#include "core/backend/FiberPool.hpp" -#include "core/backend/ScriptMgr.hpp" #include "game/frontend/fonts/Fonts.hpp" -#include "game/pointers/Pointers.hpp" #include "submenus/Self.hpp" #include "submenus/Vehicle.hpp" #include "submenus/Teleport.hpp" @@ -17,16 +11,12 @@ #include "submenus/Settings.hpp" #include "submenus/Debug.hpp" #include "submenus/World.hpp" -#include "core/filemgr/FileMgr.hpp" -#include "core/memory/ModuleMgr.hpp" #include "Onboarding.hpp" -#include "core/frontend/manager/styles/Themes.hpp" namespace YimMenu { void Menu::Init() { - SetupStyle(); // Arguably the only place this file should be edited at for more menus UIManager::AddSubmenu(std::make_shared()); UIManager::AddSubmenu(std::make_shared()); @@ -38,7 +28,7 @@ namespace YimMenu UIManager::AddSubmenu(std::make_shared()); UIManager::AddSubmenu(std::make_shared()); - Renderer::AddRendererCallBack( + Renderer::AddRendererCallback( [&] { ProcessOnboarding(); if (!GUI::IsOpen()) diff --git a/src/game/frontend/Menu.hpp b/src/game/frontend/Menu.hpp index 65ae018..8ba60ad 100644 --- a/src/game/frontend/Menu.hpp +++ b/src/game/frontend/Menu.hpp @@ -1,5 +1,4 @@ #pragma once -#include "game/frontend/GUI.hpp" namespace YimMenu::Menu { diff --git a/src/game/hooks/GUI/RawInput.cpp b/src/game/hooks/GUI/RawInput.cpp index 008d038..0c7a50b 100644 --- a/src/game/hooks/GUI/RawInput.cpp +++ b/src/game/hooks/GUI/RawInput.cpp @@ -1,25 +1,26 @@ #include "core/hooking/DetourHook.hpp" -#include "game/frontend/GUI.hpp" +#include "core/renderer/Renderer.hpp" #include "game/hooks/Hooks.hpp" -#include - namespace YimMenu::Hooks { UINT RawInput::GetRawInputData(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader) { auto result = BaseHook::Get>()->Original()(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader); - if (result > 0 && pData && uiCommand == RID_INPUT && ImGui::GetIO().WantCaptureMouse) + if (result > 0 && pData && uiCommand == RID_INPUT) { - auto& raw = *(RAWINPUT*)pData; - if (raw.header.dwType == RIM_TYPEMOUSE && raw.data.mouse.usButtonFlags) + if (Renderer::IsInitialized() && ImGui::GetIO().WantCaptureMouse) { - // Zero out button flags to prevent game from seeing clicks while menu is open - raw.data.mouse.usButtonFlags = 0; + auto& raw = *(RAWINPUT*)pData; + if (raw.header.dwType == RIM_TYPEMOUSE && raw.data.mouse.usButtonFlags) + { + // Zero out button flags to prevent game from seeing clicks while menu is open + raw.data.mouse.usButtonFlags = 0; + } } } return result; } -} +} \ No newline at end of file diff --git a/src/game/hooks/GUI/SwapChain.cpp b/src/game/hooks/GUI/SwapChain.cpp deleted file mode 100644 index 4d8a012..0000000 --- a/src/game/hooks/GUI/SwapChain.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "core/hooking/DetourHook.hpp" -#include "core/renderer/Renderer.hpp" -#include "game/hooks/Hooks.hpp" - -namespace YimMenu::Hooks -{ - HRESULT SwapChain::Present(IDXGISwapChain* that, UINT syncInterval, UINT flags) - { - if (g_Running && !Renderer::IsResizing()) - { - Renderer::DX12OnPresent(); - } - return BaseHook::Get>()->Original()(that, syncInterval, flags); - } - - HRESULT SwapChain::ResizeBuffers(IDXGISwapChain* that, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags) - { - if (g_Running) - { - Renderer::DX12PreResize(); - const auto result = - BaseHook::Get>()->Original()(that, bufferCount, width, height, newFormat, swapChainFlags); - Renderer::DX12PostResize(); - return result; - } - return BaseHook::Get>()->Original()(that, bufferCount, width, height, newFormat, swapChainFlags); - } -} \ No newline at end of file diff --git a/src/game/hooks/GUI/Window.cpp b/src/game/hooks/GUI/Window.cpp deleted file mode 100644 index cc86360..0000000 --- a/src/game/hooks/GUI/Window.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "core/hooking/DetourHook.hpp" -#include "core/renderer/Renderer.hpp" -#include "game/hooks/Hooks.hpp" - -namespace YimMenu::Hooks -{ - LRESULT Window::WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) - { - if (g_Running) - Renderer::WndProc(hwnd, umsg, wparam, lparam); - - return BaseHook::Get>()->Original()(hwnd, umsg, wparam, lparam); - } -} \ No newline at end of file diff --git a/src/game/hooks/Hooks.hpp b/src/game/hooks/Hooks.hpp index ed001f4..cecd4c1 100644 --- a/src/game/hooks/Hooks.hpp +++ b/src/game/hooks/Hooks.hpp @@ -1,5 +1,4 @@ #pragma once -#include class CNetworkPlayerMgr; class CNetGamePlayer; @@ -34,14 +33,6 @@ namespace YimMenu namespace YimMenu::Hooks { - namespace SwapChain - { - constexpr auto VMTPresentIdx = 8; - constexpr auto VMTResizeBuffersIdx = 13; - extern HRESULT Present(IDXGISwapChain* that, UINT syncInterval, UINT flags); - extern HRESULT ResizeBuffers(IDXGISwapChain* that, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags); - } - namespace Anticheat { extern void QueueDependency(__int64 a1); diff --git a/src/game/pointers/Pointers.cpp b/src/game/pointers/Pointers.cpp index e20c167..141ee6c 100644 --- a/src/game/pointers/Pointers.cpp +++ b/src/game/pointers/Pointers.cpp @@ -28,22 +28,6 @@ namespace YimMenu auto scanner = PatternScanner(gta5); - constexpr auto swapchainPtrn = Pattern<"72 C7 EB 02 31 C0 8B 0D">("IDXGISwapChain"); - scanner.Add(swapchainPtrn, [this](PointerCalculator ptr) { - CommandQueue = ptr.Add(0x1A).Add(3).Rip().As(); - SwapChain = ptr.Add(0x21).Add(3).Rip().As(); - }); - - constexpr auto wndProcPtrn = Pattern<"3D 85 00 00 00 0F 87 2D 02 00 00">("WndProc"); - scanner.Add(wndProcPtrn, [this](PointerCalculator ptr) { - WndProc = ptr.Sub(0x4F).As(); - }); - - constexpr auto hWndPtrn = Pattern<"E8 ? ? ? ? 84 C0 74 25 48 8B 0D">("HWND"); - scanner.Add(hWndPtrn, [this](PointerCalculator ptr) { - Hwnd = ptr.Add(9).Add(3).Rip().As(); - }); - constexpr auto screenResPtrn = Pattern<"75 39 0F 57 C0 F3 0F 2A 05">("ScreenRes"); scanner.Add(screenResPtrn, [this](PointerCalculator ptr) { ScreenResX = ptr.Add(0x5).Add(4).Rip().As(); diff --git a/src/game/pointers/Pointers.hpp b/src/game/pointers/Pointers.hpp index ac5553c..a8f812b 100644 --- a/src/game/pointers/Pointers.hpp +++ b/src/game/pointers/Pointers.hpp @@ -1,7 +1,4 @@ #pragma once -#include -#include -#include #include "types/script/scrNativeHandler.hpp" #include "types/rage/ObfVar.hpp" #include "core/memory/BytePatches.hpp" @@ -74,10 +71,6 @@ namespace YimMenu struct PointerData { - IDXGISwapChain1** SwapChain; - ID3D12CommandQueue** CommandQueue; - HWND* Hwnd; - PVOID WndProc; std::uint32_t* ScreenResX; std::uint32_t* ScreenResY; const char* GameVersion; diff --git a/src/main.cpp b/src/main.cpp index b4b6265..30fb8c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,7 @@ #include "core/hooking/Hooking.hpp" #include "core/hooking/CallHook.hpp" #include "core/memory/ModuleMgr.hpp" +#include "core/renderer/D3D12Hook.hpp" #include "core/renderer/Renderer.hpp" #include "core/util/Wine.hpp" #include "core/scripting/LuaManager.hpp" @@ -52,9 +53,6 @@ namespace YimMenu AnticheatBypass::RunOnStartup(); - if (!Renderer::Init()) - goto EARLY_UNLOAD; - Players::Init(); Hooking::Init(); @@ -64,6 +62,11 @@ namespace YimMenu ScriptPointers::Init(); + if (!D3D12Hook::Init()) + goto EARLY_UNLOAD; + Renderer::Init(); + while (!Renderer::IsInitialized()) + std::this_thread::sleep_for(100ms); GUI::Init(); ScriptMgr::AddScript(std::make_unique