mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Fix a rare crash that occurs in AMD driver code, when ImGui shutdown was called. The crash did not occur if the library was shutdown after having rendered one of the ImGui panels for one frame. The fix is to just never call 'ImGui_ImplDX11_NewFrame()', 'ImGui_ImplWin32_NewFrame()' and 'ImGui::NewFrame()', 'ImGui::EndFrame()', 'ImGui::Render()' if none of the windows are visible. code has been tested on a system that would trigger the crash, and after the patch, the crash no longer happened.
449 lines
16 KiB
C++
449 lines
16 KiB
C++
#include "core/stdafx.h"
|
|
#ifndef DEDICATED // This file should not be compiled for DEDICATED!
|
|
//------------------------------
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include "tier0/threadtools.h"
|
|
#include "tier1/cvar.h"
|
|
#include "windows/id3dx.h"
|
|
#include "windows/input.h"
|
|
#include "gameui/IConsole.h"
|
|
#include "gameui/IBrowser.h"
|
|
#include "engine/sys_mainwind.h"
|
|
#include "inputsystem/inputsystem.h"
|
|
#include "public/bitmap/stb_image.h"
|
|
#include "public/rendersystem/schema/texture.g.h"
|
|
|
|
/**********************************************************************************
|
|
-----------------------------------------------------------------------------------
|
|
File : id3dx.cpp
|
|
Date : 15:06:2021
|
|
Author : Kawe Mazidjatari
|
|
Purpose: Microsoft DirectX 11 'IDXGISwapChain::Present' hook implementation
|
|
-----------------------------------------------------------------------------------
|
|
History:
|
|
- 15:06:2021 | 14:56 : Created by Kawe Mazidjatari
|
|
- 17:06:2021 | 13:12 : Destroy / release objects with 'GetResizeBuffers' callback
|
|
|
|
**********************************************************************************/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
typedef BOOL(WINAPI* IPostMessageA)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
|
|
typedef BOOL(WINAPI* IPostMessageW)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
static BOOL s_bInitialized = false;
|
|
static BOOL s_bImGuiInitialized = false;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
static IPostMessageA s_oPostMessageA = NULL;
|
|
static IPostMessageW s_oPostMessageW = NULL;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
static IDXGIResizeBuffers s_fnResizeBuffers = NULL;
|
|
static IDXGISwapChainPresent s_fnSwapChainPresent = NULL;
|
|
|
|
//#################################################################################
|
|
// WINDOW PROCEDURE
|
|
//#################################################################################
|
|
|
|
LRESULT CALLBACK DXGIMsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
//#################################################################################
|
|
// POST MESSAGE
|
|
//#################################################################################
|
|
|
|
BOOL WINAPI HPostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (g_bBlockInput && Msg == WM_MOUSEMOVE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return s_oPostMessageA(hWnd, Msg, wParam, lParam);
|
|
}
|
|
|
|
BOOL WINAPI HPostMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (g_bBlockInput && Msg == WM_MOUSEMOVE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return s_oPostMessageW(hWnd, Msg, wParam, lParam);
|
|
}
|
|
|
|
//#################################################################################
|
|
// IMGUI
|
|
//#################################################################################
|
|
|
|
void SetupImGui()
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGui_ImplWin32_Init(*g_pGameWindow);
|
|
ImGui_ImplDX11_Init(*g_ppGameDevice, *g_ppImmediateContext);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
io.ImeWindowHandle = *g_pGameWindow;
|
|
io.ConfigFlags |= ImGuiConfigFlags_IsSRGB;
|
|
|
|
s_bImGuiInitialized = true;
|
|
}
|
|
|
|
void DrawImGui()
|
|
{
|
|
// Only render if the ImGui panels are visible.
|
|
if (g_pBrowser->IsVisible() || g_pConsole->IsVisible())
|
|
{
|
|
ImGui_ImplDX11_NewFrame();
|
|
ImGui_ImplWin32_NewFrame();
|
|
|
|
ImGui::NewFrame();
|
|
|
|
// This is required to disable the ctrl+tab menu as some users use this shortcut for other things in-game.
|
|
// See https://github.com/ocornut/imgui/issues/5641 for more details.
|
|
if (GImGui->ConfigNavWindowingKeyNext)
|
|
ImGui::SetShortcutRouting(GImGui->ConfigNavWindowingKeyNext, ImGuiKeyOwner_None);
|
|
if (GImGui->ConfigNavWindowingKeyPrev)
|
|
ImGui::SetShortcutRouting(GImGui->ConfigNavWindowingKeyPrev, ImGuiKeyOwner_None);
|
|
|
|
g_pBrowser->RunFrame();
|
|
g_pConsole->RunFrame();
|
|
|
|
ImGui::EndFrame();
|
|
ImGui::Render();
|
|
|
|
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
|
}
|
|
|
|
g_pBrowser->RunTask();
|
|
g_pConsole->RunTask();
|
|
}
|
|
|
|
//#################################################################################
|
|
// IDXGI
|
|
//#################################################################################
|
|
|
|
HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags)
|
|
{
|
|
if (!s_bInitialized)
|
|
{
|
|
SetupImGui();
|
|
g_ThreadRenderThreadID = GetCurrentThreadId();
|
|
s_bInitialized = true;
|
|
}
|
|
|
|
DrawImGui();
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
return s_fnSwapChainPresent(pSwapChain, nSyncInterval, nFlags);
|
|
}
|
|
|
|
HRESULT __stdcall ResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags)
|
|
{
|
|
g_nWindowRect[0] = nWidth;
|
|
g_nWindowRect[1] = nHeight;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
return s_fnResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags);
|
|
}
|
|
|
|
//#################################################################################
|
|
// INTERNALS
|
|
//#################################################################################
|
|
|
|
#pragma warning( push )
|
|
// Disable stack warning, tells us to move more data to the heap instead. Not really possible with 'initialData' here. Since its parallel processed.
|
|
// Also disable 6378, complains that there is no control path where it would use 'nullptr', if that happens 'Error' will be called though.
|
|
#pragma warning( disable : 6262 6387)
|
|
inline CMemory p_CreateTextureResource;
|
|
inline auto v_CreateTextureResource = p_CreateTextureResource.RCast<void(*)(TextureHeader_t*, INT_PTR)>();
|
|
constexpr uint32_t ALIGNMENT_SIZE = 15; // Creates 2D texture and shader resource from textureHeader and imageData.
|
|
void CreateTextureResource(TextureHeader_t* textureHeader, INT_PTR imageData)
|
|
{
|
|
if (textureHeader->m_nDepth && !textureHeader->m_nHeight) // Return never gets hit. Maybe its some debug check?
|
|
return;
|
|
|
|
__int64 initialData[4096]{};
|
|
textureHeader->m_nTextureMipLevels = textureHeader->m_nPermanentMipCount;
|
|
|
|
const int totalStreamedMips = textureHeader->m_nOptStreamedMipCount + textureHeader->m_nStreamedMipCount;
|
|
int mipLevel = textureHeader->m_nPermanentMipCount + totalStreamedMips;
|
|
if (mipLevel != totalStreamedMips)
|
|
{
|
|
do
|
|
{
|
|
--mipLevel;
|
|
if (textureHeader->m_nArraySize)
|
|
{
|
|
int mipWidth = 0;
|
|
if (textureHeader->m_nWidth >> mipLevel > 1)
|
|
mipWidth = (textureHeader->m_nWidth >> mipLevel) - 1;
|
|
|
|
int mipHeight = 0;
|
|
if (textureHeader->m_nHeight >> mipLevel > 1)
|
|
mipHeight = (textureHeader->m_nHeight >> mipLevel) - 1;
|
|
|
|
uint8_t x = s_pBytesPerPixel[textureHeader->m_nImageFormat].first;
|
|
uint8_t y = s_pBytesPerPixel[textureHeader->m_nImageFormat].second;
|
|
|
|
uint32_t bppWidth = (y + mipWidth) >> (y >> 1);
|
|
uint32_t bppHeight = (y + mipHeight) >> (y >> 1);
|
|
uint32_t sliceWidth = x * (y >> (y >> 1));
|
|
|
|
uint32_t rowPitch = sliceWidth * bppWidth;
|
|
uint32_t slicePitch = x * bppWidth * bppHeight;
|
|
|
|
uint32_t subResourceEntry = mipLevel;
|
|
for (int i = 0; i < textureHeader->m_nArraySize; i++)
|
|
{
|
|
uint32_t offsetCurrentResourceData = subResourceEntry << 4u;
|
|
|
|
*(int64_t*)((uint8_t*)initialData + offsetCurrentResourceData) = imageData;
|
|
*(uint32_t*)((uint8_t*)&initialData[1] + offsetCurrentResourceData) = rowPitch;
|
|
*(uint32_t*)((uint8_t*)&initialData[1] + offsetCurrentResourceData + 4) = slicePitch;
|
|
|
|
imageData += (slicePitch + ALIGNMENT_SIZE) & ~ALIGNMENT_SIZE;
|
|
subResourceEntry += textureHeader->m_nPermanentMipCount;
|
|
}
|
|
}
|
|
} while (mipLevel != totalStreamedMips);
|
|
}
|
|
|
|
const DXGI_FORMAT dxgiFormat = g_TxtrAssetToDxgiFormat[textureHeader->m_nImageFormat]; // Get dxgi format
|
|
|
|
D3D11_TEXTURE2D_DESC textureDesc{};
|
|
textureDesc.Width = textureHeader->m_nWidth >> mipLevel;
|
|
textureDesc.Height = textureHeader->m_nHeight >> mipLevel;
|
|
textureDesc.MipLevels = textureHeader->m_nPermanentMipCount;
|
|
textureDesc.ArraySize = textureHeader->m_nArraySize;
|
|
textureDesc.Format = dxgiFormat;
|
|
textureDesc.SampleDesc.Count = 1;
|
|
textureDesc.SampleDesc.Quality = 0;
|
|
textureDesc.Usage = textureHeader->m_nCPUAccessFlag != 2 ? D3D11_USAGE_IMMUTABLE : D3D11_USAGE_DEFAULT;
|
|
textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
textureDesc.MiscFlags = 0;
|
|
|
|
const uint32_t offsetStartResourceData = mipLevel << 4u;
|
|
const D3D11_SUBRESOURCE_DATA* subResData = (D3D11_SUBRESOURCE_DATA*)((uint8_t*)initialData + offsetStartResourceData);
|
|
const HRESULT createTextureRes = (*g_ppGameDevice)->CreateTexture2D(&textureDesc, subResData, &textureHeader->m_ppTexture);
|
|
if (createTextureRes < S_OK)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Couldn't create texture \"%s\": error code = %08x\n", textureHeader->m_pDebugName, createTextureRes);
|
|
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResource{};
|
|
shaderResource.Format = dxgiFormat;
|
|
shaderResource.Texture2D.MipLevels = textureHeader->m_nTextureMipLevels;
|
|
if (textureHeader->m_nArraySize > 1) // Do we have a texture array?
|
|
{
|
|
shaderResource.Texture2DArray.FirstArraySlice = 0;
|
|
shaderResource.Texture2DArray.ArraySize = textureHeader->m_nArraySize;
|
|
shaderResource.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2DARRAY;
|
|
}
|
|
else
|
|
{
|
|
shaderResource.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D;
|
|
}
|
|
|
|
const HRESULT createShaderResourceRes = (*g_ppGameDevice)->CreateShaderResourceView(textureHeader->m_ppTexture, &shaderResource, &textureHeader->m_ppShaderResourceView);
|
|
if (createShaderResourceRes < S_OK)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Couldn't create shader resource view for texture \"%s\": error code = %08x\n", textureHeader->m_pDebugName, createShaderResourceRes);
|
|
}
|
|
#pragma warning( pop )
|
|
|
|
bool LoadTextureBuffer(unsigned char* buffer, int len, ID3D11ShaderResourceView** out_srv, int* out_width, int* out_height)
|
|
{
|
|
// Load PNG buffer to a raw RGBA buffer
|
|
int nImageWidth = 0;
|
|
int nImageHeight = 0;
|
|
unsigned char* pImageData = stbi_load_from_memory(buffer, len, &nImageWidth, &nImageHeight, NULL, 4);
|
|
|
|
if (!pImageData)
|
|
{
|
|
assert(pImageData);
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
ID3D11Texture2D* pTexture = nullptr;
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
D3D11_SUBRESOURCE_DATA subResource;
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
ZeroMemory(&desc, sizeof(desc));
|
|
desc.Width = nImageWidth;
|
|
desc.Height = nImageHeight;
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
desc.CPUAccessFlags = 0;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
subResource.pSysMem = pImageData;
|
|
subResource.SysMemPitch = desc.Width * 4;
|
|
subResource.SysMemSlicePitch = 0;
|
|
(*g_ppGameDevice)->CreateTexture2D(&desc, &subResource, &pTexture);
|
|
|
|
// Create texture view
|
|
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
|
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
|
|
|
if (pTexture)
|
|
{
|
|
(*g_ppGameDevice)->CreateShaderResourceView(pTexture, &srvDesc, out_srv);
|
|
pTexture->Release();
|
|
}
|
|
|
|
*out_width = nImageWidth;
|
|
*out_height = nImageHeight;
|
|
stbi_image_free(pImageData);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ResetInput()
|
|
{
|
|
g_pInputSystem->EnableInput( // Enables the input system when both are not drawn.
|
|
!g_pBrowser->m_bActivate && !g_pConsole->m_bActivate);
|
|
}
|
|
|
|
bool PanelsVisible()
|
|
{
|
|
if (g_pBrowser->m_bActivate || g_pConsole->m_bActivate)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//#################################################################################
|
|
// ENTRYPOINT
|
|
//#################################################################################
|
|
|
|
void DirectX_Init()
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
s_oPostMessageA = (IPostMessageA)DetourFindFunction("user32.dll", "PostMessageA");
|
|
s_oPostMessageW = (IPostMessageW)DetourFindFunction("user32.dll", "PostMessageW");
|
|
|
|
// Begin the detour transaction
|
|
DetourTransactionBegin();
|
|
DetourUpdateThread(GetCurrentThread());
|
|
|
|
// Hook PostMessage
|
|
DetourAttach(&(LPVOID&)s_oPostMessageA, (PBYTE)HPostMessageA);
|
|
DetourAttach(&(LPVOID&)s_oPostMessageW, (PBYTE)HPostMessageW);
|
|
|
|
// Hook SwapChain
|
|
|
|
DWORD_PTR* pSwapChainVtable = *reinterpret_cast<DWORD_PTR**>(g_ppSwapChain[0]);
|
|
|
|
int pIDX = static_cast<int>(DXGISwapChainVTbl::Present);
|
|
s_fnSwapChainPresent = reinterpret_cast<IDXGISwapChainPresent>(pSwapChainVtable[pIDX]);
|
|
|
|
int rIDX = static_cast<int>(DXGISwapChainVTbl::ResizeBuffers);
|
|
s_fnResizeBuffers = reinterpret_cast<IDXGIResizeBuffers>(pSwapChainVtable[rIDX]);
|
|
|
|
DetourAttach(&(LPVOID&)s_fnSwapChainPresent, (PBYTE)Present);
|
|
DetourAttach(&(LPVOID&)s_fnResizeBuffers, (PBYTE)ResizeBuffers);
|
|
|
|
// Commit the transaction
|
|
HRESULT hr = DetourTransactionCommit();
|
|
if (hr != NO_ERROR)
|
|
{
|
|
// Failed to hook into the process, terminate
|
|
Error(eDLL_T::COMMON, 0xBAD0C0DE, "Failed to detour process: error code = %08x\n", hr);
|
|
}
|
|
}
|
|
|
|
void DirectX_Shutdown()
|
|
{
|
|
// Begin the detour transaction
|
|
DetourTransactionBegin();
|
|
DetourUpdateThread(GetCurrentThread());
|
|
|
|
// Unhook PostMessage
|
|
DetourDetach(&(LPVOID&)s_oPostMessageA, (PBYTE)HPostMessageA);
|
|
DetourDetach(&(LPVOID&)s_oPostMessageW, (PBYTE)HPostMessageW);
|
|
|
|
// Unhook SwapChain
|
|
DetourDetach(&(LPVOID&)s_fnSwapChainPresent, (PBYTE)Present);
|
|
DetourDetach(&(LPVOID&)s_fnResizeBuffers, (PBYTE)ResizeBuffers);
|
|
|
|
// Commit the transaction
|
|
DetourTransactionCommit();
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Shutdown ImGui
|
|
if (s_bImGuiInitialized)
|
|
{
|
|
ImGui_ImplWin32_Shutdown();
|
|
ImGui_ImplDX11_Shutdown();
|
|
s_bImGuiInitialized = false;
|
|
}
|
|
s_bInitialized = false;
|
|
}
|
|
|
|
void VDXGI::GetAdr(void) const
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LogFunAdr("IDXGISwapChain::Present", reinterpret_cast<uintptr_t>(s_fnSwapChainPresent));
|
|
LogFunAdr("CreateTextureResource", p_CreateTextureResource.GetPtr());
|
|
LogVarAdr("g_pSwapChain", reinterpret_cast<uintptr_t>(g_ppSwapChain));
|
|
LogVarAdr("g_pGameDevice", reinterpret_cast<uintptr_t>(g_ppGameDevice));
|
|
LogVarAdr("g_pImmediateContext", reinterpret_cast<uintptr_t>(g_ppImmediateContext));
|
|
}
|
|
|
|
void VDXGI::GetFun(void) const
|
|
{
|
|
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
|
|
p_CreateTextureResource = g_GameDll.FindPatternSIMD("48 8B C4 48 89 48 08 53 55 41 55");
|
|
v_CreateTextureResource = p_CreateTextureResource.RCast<void(*)(TextureHeader_t*, int64_t)>(); /*48 8B C4 48 89 48 08 53 55 41 55*/
|
|
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
|
|
p_CreateTextureResource = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 4C 8B C7 48 8B D5 48 8B CB 48 83 C4 60").FollowNearCallSelf();
|
|
v_CreateTextureResource = p_CreateTextureResource.RCast<void(*)(TextureHeader_t*, int64_t)>(); /*E8 ? ? ? ? 4C 8B C7 48 8B D5 48 8B CB 48 83 C4 60*/
|
|
#endif
|
|
}
|
|
|
|
void VDXGI::GetVar(void) const
|
|
{
|
|
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
|
|
CMemory pBase = g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 53 48 83 EC 50 48 8B 05 ?? ?? ?? ??");
|
|
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
|
|
CMemory pBase = g_GameDll.FindPatternSIMD("4C 8B DC 49 89 4B 08 48 83 EC 58");
|
|
#endif
|
|
// Grab device pointers..
|
|
g_ppGameDevice = pBase.FindPattern("48 8D 05").ResolveRelativeAddressSelf(0x3, 0x7).RCast<ID3D11Device**>();
|
|
g_ppImmediateContext = pBase.FindPattern("48 89 0D", CMemory::Direction::DOWN, 512, 3).ResolveRelativeAddressSelf(0x3, 0x7).RCast<ID3D11DeviceContext**>();
|
|
|
|
// Grab swap chain..
|
|
pBase = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 45 33 C0 33 D2");
|
|
g_ppSwapChain = pBase.FindPattern("48 8B 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast<IDXGISwapChain**>();
|
|
}
|
|
|
|
void VDXGI::Attach(void) const
|
|
{
|
|
#ifdef GAMEDLL_S3
|
|
DetourAttach(&v_CreateTextureResource, &CreateTextureResource);
|
|
#endif // GAMEDLL_S3
|
|
}
|
|
|
|
void VDXGI::Detach(void) const
|
|
{
|
|
#ifdef GAMEDLL_S3
|
|
DetourDetach(&v_CreateTextureResource, &CreateTextureResource);
|
|
#endif // GAMEDLL_S3
|
|
}
|
|
|
|
#endif // !DEDICATED
|