mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
@@ -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)
|
||||
{
|
||||
|
||||
@@ -10,14 +10,8 @@ namespace YimMenu
|
||||
{
|
||||
Hooking::Hooking()
|
||||
{
|
||||
BaseHook::Add<Hooks::Window::WndProc>(new DetourHook("WndProc", Pointers.WndProc, Hooks::Window::WndProc));
|
||||
|
||||
BaseHook::Add<Hooks::RawInput::GetRawInputData>(new DetourHook("GetRawInputData", reinterpret_cast<void*>(GetProcAddress(LoadLibraryA("user32.dll"), "GetRawInputData")), Hooks::RawInput::GetRawInputData));
|
||||
|
||||
auto swapchain_vft = *reinterpret_cast<void***>(*Pointers.SwapChain);
|
||||
BaseHook::Add<Hooks::SwapChain::Present>(new DetourHook("Present", swapchain_vft[Hooks::SwapChain::VMTPresentIdx], Hooks::SwapChain::Present));
|
||||
BaseHook::Add<Hooks::SwapChain::ResizeBuffers>(new DetourHook("ResizeBuffers", swapchain_vft[Hooks::SwapChain::VMTResizeBuffersIdx], Hooks::SwapChain::ResizeBuffers));
|
||||
|
||||
// BaseHook::Add<Hooks::Anticheat::QueueDependency>(new DetourHook("QueueDependency", Pointers.QueueDependency, Hooks::Anticheat::QueueDependency));
|
||||
BaseHook::Add<Hooks::Anticheat::GameSkeletonUpdate>(new DetourHook("GameSkeletonUpdate", Pointers.GameSkeletonUpdate, Hooks::Anticheat::GameSkeletonUpdate));
|
||||
BaseHook::Add<Hooks::Anticheat::PrepareMetricForSending>(new DetourHook("PrepareMetricForSending", Pointers.PrepareMetricForSending, Hooks::Anticheat::PrepareMetricForSending));
|
||||
|
||||
@@ -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<decltype(D3D12CreateDevice)*>(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<decltype(CreateDXGIFactory)*>(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<std::function<bool()>> 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<uintptr_t>(swapChain1) + i;
|
||||
if (IsBadReadPtr(reinterpret_cast<void*>(base), sizeof(void*)))
|
||||
break;
|
||||
|
||||
if (*reinterpret_cast<ID3D12CommandQueue**>(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<uintptr_t>(swapChain1) + base;
|
||||
if (IsBadReadPtr(reinterpret_cast<void*>(preScanBase), sizeof(void*)))
|
||||
break;
|
||||
|
||||
auto scanBase = *reinterpret_cast<uintptr_t*>(preScanBase);
|
||||
if (scanBase == 0 || IsBadReadPtr(reinterpret_cast<void*>(scanBase), sizeof(void*)))
|
||||
continue;
|
||||
|
||||
for (uint32_t i = 0; i < 512 * sizeof(void*); i += sizeof(void*))
|
||||
{
|
||||
auto preData = scanBase + i;
|
||||
if (IsBadReadPtr(reinterpret_cast<void*>(preData), sizeof(void*)))
|
||||
break;
|
||||
|
||||
if (*reinterpret_cast<ID3D12CommandQueue**>(preData) == commandQueue)
|
||||
{
|
||||
targetSwapchain = reinterpret_cast<IDXGISwapChain3*>(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<void***>(targetSwapchain);
|
||||
m_FactoryVtable = *reinterpret_cast<void***>(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<void*>(&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<void*>(&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<decltype(&D3D12Hook::Present)>() : GetInstance().m_SwapchainVmt->Original<decltype(&D3D12Hook::Present)>(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<VMTHook>("D3D12SwapChain", swapChain, 64); // should be enough
|
||||
GetInstance().m_SwapchainVmt->Hook(8, reinterpret_cast<void*>(&D3D12Hook::Present));
|
||||
GetInstance().m_SwapchainVmt->Hook(13, reinterpret_cast<void*>(&D3D12Hook::ResizeBuffers));
|
||||
GetInstance().m_SwapchainVmt->Enable();
|
||||
|
||||
GetInstance().m_IsPhase1 = false;
|
||||
presentFn = GetInstance().m_SwapchainVmt->Original<decltype(&D3D12Hook::Present)>(8);
|
||||
}
|
||||
|
||||
GetInstance().m_Window = swapchainWnd;
|
||||
GetInstance().m_SwapChain = swapChain;
|
||||
|
||||
{
|
||||
Microsoft::WRL::ComPtr<ID3D12Device4> tempDevice{};
|
||||
swapChain->GetDevice(IID_PPV_ARGS(&tempDevice));
|
||||
GetInstance().m_Device = tempDevice.Get();
|
||||
}
|
||||
|
||||
GetInstance().m_CommandQueue = *reinterpret_cast<ID3D12CommandQueue**>(reinterpret_cast<uintptr_t>(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<decltype(&D3D12Hook::ResizeBuffers)>(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<decltype(&D3D12Hook::CreateSwapchain)>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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 <d3d12.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
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<void()> fn)
|
||||
{
|
||||
GetInstance().m_OnPresent = std::move(fn);
|
||||
}
|
||||
|
||||
static void OnResizeBuffers(std::function<void()> fn)
|
||||
{
|
||||
GetInstance().m_OnResizeBuffers = std::move(fn);
|
||||
}
|
||||
|
||||
static void OnDeviceReset(std::function<void()> 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 <typename T>
|
||||
T Original() const
|
||||
{
|
||||
return reinterpret_cast<T>(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<void()> m_OnPresent = nullptr;
|
||||
std::function<void()> m_OnResizeBuffers = nullptr;
|
||||
std::function<void()> 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<VMTHook> m_SwapchainVmt = {};
|
||||
std::recursive_mutex m_HookMutex = {};
|
||||
};
|
||||
}
|
||||
+361
-364
@@ -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 <backends/imgui_impl_dx12.h>
|
||||
#include <backends/imgui_impl_win32.h>
|
||||
#include <imgui.h>
|
||||
|
||||
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<WNDPROC>(SetWindowLongPtrA(m_Window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&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<int>(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<SrvHeapAllocator*>(info->UserData)->Alloc(outCpu, outGpu);
|
||||
};
|
||||
initInfo.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu) {
|
||||
static_cast<SrvHeapAllocator*>(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<LONG_PTR>(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<LONG_PTR>(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<ID3D12Resource> 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<IDXGISwapChain1>(*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<ID3D12CommandQueue>(*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<void**>(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<ID3D12Resource> 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<ID3D12Resource> 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<SIZE_T>(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<ID3D12Device> 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<SIZE_T>(index) * DescriptorSize;
|
||||
outGpu->ptr = HeapStartGpu.ptr + static_cast<UINT64>(index) * DescriptorSize;
|
||||
}
|
||||
|
||||
void Renderer::SrvHeapAllocator::Free(D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu)
|
||||
{
|
||||
int index = static_cast<int>((cpu.ptr - HeapStartCpu.ptr) / DescriptorSize);
|
||||
FreeIndices.push_back(index);
|
||||
}
|
||||
}
|
||||
+82
-202
@@ -1,159 +1,39 @@
|
||||
#pragma once
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
#include <backends/imgui_impl_dx12.h>
|
||||
#include <comdef.h>
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <windows.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
|
||||
#define REL(o) \
|
||||
o->Release(); \
|
||||
if (o) \
|
||||
{ \
|
||||
o = nullptr; \
|
||||
}
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
using namespace Microsoft::WRL;
|
||||
using RendererCallBack = std::function<void()>;
|
||||
using WindowProcedureCallback = std::function<void(HWND, UINT, WPARAM, LPARAM)>;
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
using RendererCallback = std::function<void()>;
|
||||
using WindowProcedureCallback = std::function<void(HWND, UINT, WPARAM, LPARAM)>;
|
||||
|
||||
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<int> 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 <typename T>
|
||||
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||
|
||||
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<int> FreeIndices = {};
|
||||
};
|
||||
|
||||
//DX12
|
||||
std::vector<FrameContext> m_FrameContext;
|
||||
struct FrameContext
|
||||
{
|
||||
ComPtr<ID3D12CommandAllocator> Allocator = {};
|
||||
UINT64 FenceValue = 0;
|
||||
};
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC m_SwapChainDesc;
|
||||
ComPtr<IDXGISwapChain1> m_GameSwapChain;
|
||||
ComPtr<IDXGISwapChain3> m_SwapChain;
|
||||
ComPtr<ID3D12Device> m_Device;
|
||||
ComPtr<ID3D12CommandQueue> m_CommandQueue;
|
||||
ComPtr<ID3D12CommandAllocator> m_CommandAllocator;
|
||||
ComPtr<ID3D12GraphicsCommandList> m_CommandList;
|
||||
ComPtr<ID3D12DescriptorHeap> m_BackbufferDescriptorHeap;
|
||||
ComPtr<ID3D12DescriptorHeap> m_DescriptorHeap;
|
||||
static Renderer& GetInstance()
|
||||
{
|
||||
static Renderer instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Fence> 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<joaat_t, RendererCallBack> m_RendererCallBacks;
|
||||
std::vector<WindowProcedureCallback> m_WindowProcedureCallbacks;
|
||||
};
|
||||
}
|
||||
|
||||
// Make our linker aware of the ImGui WndProcHandler
|
||||
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
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<ID3D12DescriptorHeap> m_RtvHeap = {};
|
||||
ComPtr<ID3D12DescriptorHeap> m_SrvHeap = {};
|
||||
SrvHeapAllocator m_SrvAllocator = {};
|
||||
std::vector<FrameContext> m_FrameContexts = {};
|
||||
std::vector<ComPtr<ID3D12Resource>> m_BackBuffers = {};
|
||||
std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> m_RtvHandles = {};
|
||||
UINT m_BufferCount = 0;
|
||||
ComPtr<ID3D12GraphicsCommandList> m_CommandList = {};
|
||||
ComPtr<ID3D12Fence> m_Fence = {};
|
||||
HANDLE m_FenceEvent = nullptr;
|
||||
UINT64 m_FenceValue = 0;
|
||||
std::map<uint32_t, RendererCallback> m_RendererCallbacks;
|
||||
std::vector<WindowProcedureCallback> m_WindowProcedureCallbacks;
|
||||
};
|
||||
}
|
||||
@@ -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<LuaScript>& 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())
|
||||
{
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -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<Submenus::Self>());
|
||||
UIManager::AddSubmenu(std::make_shared<Submenus::Vehicle>());
|
||||
@@ -38,7 +28,7 @@ namespace YimMenu
|
||||
UIManager::AddSubmenu(std::make_shared<Submenus::Settings>());
|
||||
UIManager::AddSubmenu(std::make_shared<Submenus::Debug>());
|
||||
|
||||
Renderer::AddRendererCallBack(
|
||||
Renderer::AddRendererCallback(
|
||||
[&] {
|
||||
ProcessOnboarding();
|
||||
if (!GUI::IsOpen())
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "game/frontend/GUI.hpp"
|
||||
|
||||
namespace YimMenu::Menu
|
||||
{
|
||||
|
||||
@@ -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 <imgui.h>
|
||||
|
||||
namespace YimMenu::Hooks
|
||||
{
|
||||
UINT RawInput::GetRawInputData(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader)
|
||||
{
|
||||
auto result = BaseHook::Get<RawInput::GetRawInputData, DetourHook<decltype(&RawInput::GetRawInputData)>>()->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<SwapChain::Present, DetourHook<decltype(&SwapChain::Present)>>()->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<SwapChain::ResizeBuffers, DetourHook<decltype(&SwapChain::ResizeBuffers)>>()->Original()(that, bufferCount, width, height, newFormat, swapChainFlags);
|
||||
Renderer::DX12PostResize();
|
||||
return result;
|
||||
}
|
||||
return BaseHook::Get<SwapChain::ResizeBuffers, DetourHook<decltype(&SwapChain::ResizeBuffers)>>()->Original()(that, bufferCount, width, height, newFormat, swapChainFlags);
|
||||
}
|
||||
}
|
||||
@@ -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<Window::WndProc, DetourHook<WNDPROC>>()->Original()(hwnd, umsg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <d3d11.h>
|
||||
|
||||
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);
|
||||
|
||||
@@ -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<ID3D12CommandQueue**>();
|
||||
SwapChain = ptr.Add(0x21).Add(3).Rip().As<IDXGISwapChain1**>();
|
||||
});
|
||||
|
||||
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<PVOID>();
|
||||
});
|
||||
|
||||
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<HWND*>();
|
||||
});
|
||||
|
||||
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<std::uint32_t*>();
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#pragma once
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <windows.h>
|
||||
#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;
|
||||
|
||||
+7
-3
@@ -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<Script>(&NativeHooks::RunScript)); // runs once
|
||||
@@ -103,6 +106,7 @@ namespace YimMenu
|
||||
|
||||
EARLY_UNLOAD:
|
||||
g_Running = false;
|
||||
D3D12Hook::Destroy(true);
|
||||
Renderer::Destroy();
|
||||
LogHelper::Destroy();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user