mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Engine: render ImGui in main thread and fix many threading bugs
ImGui drawing code now takes place in the main thread, a snapshot of the render data is created in CMaterialSystem::SwapBuffers(), and is being rendered in the render thread right before SpinPresent(). The reason why this was necessary, is because ConVar::GetString() isn't thread safe if its not marked FCVAR_MATERIAL_SYSTEM_THREAD or FCVAR_ACCESSIBLE_FROM_THREADS, and we used it for the console suggestions window, which iterates over every ConVar, accessible from threads or not. This is overall also a better code architecture. This change also forced changes in the ImGui server browser window, which contained a bunch of threading bugs too. Engine changes: - g_pCVar is no longer exported, as this wasn't necessary in the end. - g_ThreadMainThreadID and g_ThreadServerFrameThreadID are now exported from the game executable, as these are required as soon as the DLL is loaded.
This commit is contained in:
parent
a1e4500b98
commit
e967cb374b
@ -161,6 +161,9 @@ add_sources( SOURCE_GROUP "GameUI"
|
||||
"${ENGINE_SOURCE_DIR}/gameui/IBrowser.h"
|
||||
"${ENGINE_SOURCE_DIR}/gameui/IConsole.cpp"
|
||||
"${ENGINE_SOURCE_DIR}/gameui/IConsole.h"
|
||||
|
||||
"${ENGINE_SOURCE_DIR}/gameui/imgui_system.cpp"
|
||||
"${ENGINE_SOURCE_DIR}/gameui/imgui_system.h"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#ifndef DEDICATED
|
||||
#include "vgui/vgui_baseui_interface.h"
|
||||
#include "client/vengineclient_impl.h"
|
||||
#include "gameui/imgui_system.h"
|
||||
#endif // DEDICATED
|
||||
#include "networksystem/pylon.h"
|
||||
#ifndef CLIENT_DLL
|
||||
@ -124,6 +125,8 @@ void CHostState::FrameUpdate(CHostState* pHostState, double flCurrentTime, float
|
||||
#endif // !CLIENT_DLL
|
||||
#ifndef DEDICATED
|
||||
RCONClient()->RunFrame();
|
||||
|
||||
ImguiSystem_SampleFrame();
|
||||
#endif // !DEDICATED
|
||||
|
||||
// Disable "warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable"
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "core/stdafx.h"
|
||||
#include "windows/id3dx.h"
|
||||
#include "engine/sys_getmodes.h"
|
||||
#include "gameui/imgui_system.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: creates the game window, obtains the rect and plays the startup movie.
|
||||
@ -14,7 +15,13 @@ bool HCVideoMode_Common__CreateGameWindow(int* pnRect)
|
||||
{
|
||||
g_nWindowRect[0] = pnRect[0];
|
||||
g_nWindowRect[1] = pnRect[1];
|
||||
return CVideoMode_Common__CreateGameWindow(pnRect);
|
||||
|
||||
const bool ret = CVideoMode_Common__CreateGameWindow(pnRect);
|
||||
|
||||
if (!ImguiSystem_Init())
|
||||
Error(eDLL_T::MS, 0, "ImGui system initialization failed!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HVideoMode_Common::Detour(const bool bAttach) const
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "engine/keys.h"
|
||||
#include "gameui/IConsole.h"
|
||||
#include "gameui/IBrowser.h"
|
||||
#include "gameui/imgui_system.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: plays the startup video's
|
||||
@ -29,7 +30,7 @@ void CGame::PlayStartupVideos(void)
|
||||
//-----------------------------------------------------------------------------
|
||||
int CGame::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (!g_bImGuiInitialized)
|
||||
if (!ImguiSystem_IsInitialized())
|
||||
return CGame__WindowProc(hWnd, uMsg, wParam, lParam);
|
||||
|
||||
const IEngine::EngineState_t state = g_pEngine->GetState();
|
||||
|
@ -1,15 +0,0 @@
|
||||
cmake_minimum_required( VERSION 3.16 )
|
||||
add_module( "lib" "gameui" "vpc" ${FOLDER_CONTEXT} TRUE TRUE )
|
||||
|
||||
start_sources()
|
||||
|
||||
add_sources( SOURCE_GROUP "Core"
|
||||
"IBrowser.cpp"
|
||||
"IBrowser.h"
|
||||
"IConsole.cpp"
|
||||
"IConsole.h"
|
||||
)
|
||||
|
||||
end_sources()
|
||||
|
||||
target_include_directories( ${PROJECT_NAME} PRIVATE "${ENGINE_SOURCE_DIR}/tier0/" "${ENGINE_SOURCE_DIR}/tier1/" )
|
@ -51,7 +51,7 @@ CBrowser::CBrowser(void)
|
||||
, m_bQueryListNonRecursive(false)
|
||||
, m_bQueryGlobalBanList(true)
|
||||
, m_flFadeAlpha(0.f)
|
||||
, m_HostRequestMessageColor(1.00f, 1.00f, 1.00f, 1.00f)
|
||||
, m_HostMessageColor(1.00f, 1.00f, 1.00f, 1.00f)
|
||||
, m_ivHiddenServerMessageColor(0.00f, 1.00f, 0.00f, 1.00f)
|
||||
, m_Style(ImGuiStyle_t::NONE)
|
||||
{
|
||||
@ -155,7 +155,6 @@ void CBrowser::RunFrame(void)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: runs tasks for the browser while not being drawn
|
||||
// (!!! RunTask and RunFrame must be called from the same thread !!!)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBrowser::RunTask()
|
||||
{
|
||||
@ -178,10 +177,8 @@ void CBrowser::RunTask()
|
||||
{
|
||||
if (m_bQueryListNonRecursive)
|
||||
{
|
||||
std::thread refresh(&CBrowser::RefreshServerList, this);
|
||||
refresh.detach();
|
||||
|
||||
m_bQueryListNonRecursive = false;
|
||||
RefreshServerList();
|
||||
}
|
||||
}
|
||||
else // Refresh server list the next time 'm_bActivate' evaluates to true.
|
||||
@ -220,8 +217,6 @@ void CBrowser::Think(void)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBrowser::DrawSurface(void)
|
||||
{
|
||||
AUTO_LOCK(m_Mutex);
|
||||
|
||||
ImGui::BeginTabBar("CompMenu");
|
||||
if (ImGui::BeginTabItem("Browsing"))
|
||||
{
|
||||
@ -250,9 +245,7 @@ void CBrowser::BrowserPanel(void)
|
||||
if (ImGui::Button("Refresh"))
|
||||
{
|
||||
m_svServerListMessage.clear();
|
||||
|
||||
std::thread refresh(&CBrowser::RefreshServerList, this);
|
||||
refresh.detach();
|
||||
RefreshServerList();
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
@ -369,11 +362,19 @@ void CBrowser::RefreshServerList(void)
|
||||
{
|
||||
Msg(eDLL_T::CLIENT, "Refreshing server list with matchmaking host '%s'\n", pylon_matchmaking_hostname.GetString());
|
||||
|
||||
std::string svServerListMessage;
|
||||
g_ServerListManager.RefreshServerList(svServerListMessage);
|
||||
// Thread the request, and let the main thread assign status message back
|
||||
std::thread request([&]
|
||||
{
|
||||
std::string svServerListMessage;
|
||||
g_ServerListManager.RefreshServerList(svServerListMessage);
|
||||
|
||||
AUTO_LOCK(m_Mutex);
|
||||
m_svServerListMessage = svServerListMessage;
|
||||
g_TaskScheduler->Dispatch([&, svServerListMessage]
|
||||
{
|
||||
SetServerListMessage(svServerListMessage.c_str());
|
||||
}, 0);
|
||||
}
|
||||
);
|
||||
request.detach();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -415,9 +416,11 @@ void CBrowser::HiddenServersModal(void)
|
||||
ImGui::Text("Enter token to connect");
|
||||
|
||||
const ImVec2 contentRegionMax = ImGui::GetContentRegionAvail();
|
||||
|
||||
ImGui::PushItemWidth(contentRegionMax.x); // Override item width.
|
||||
ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token (required)", &m_svHiddenServerToken);
|
||||
|
||||
string hiddenServerToken;
|
||||
ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token (required)", &hiddenServerToken);
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (m_bReclaimFocusTokenField)
|
||||
@ -436,10 +439,10 @@ void CBrowser::HiddenServersModal(void)
|
||||
m_svHiddenServerRequestMessage.clear();
|
||||
m_bReclaimFocusTokenField = true;
|
||||
|
||||
if (!m_svHiddenServerToken.empty())
|
||||
if (!hiddenServerToken.empty())
|
||||
{
|
||||
NetGameServer_t server;
|
||||
bool result = g_MasterServer.GetServerByToken(server, m_svHiddenServerRequestMessage, m_svHiddenServerToken); // Send token connect request.
|
||||
bool result = g_MasterServer.GetServerByToken(server, m_svHiddenServerRequestMessage, hiddenServerToken); // Send token connect request.
|
||||
|
||||
if (result && !server.name.empty())
|
||||
{
|
||||
@ -467,6 +470,7 @@ void CBrowser::HiddenServersModal(void)
|
||||
m_ivHiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Button("Close", ImVec2(contentRegionMax.x, 24)))
|
||||
{
|
||||
m_svHiddenServerRequestMessage.clear();
|
||||
@ -558,7 +562,7 @@ void CBrowser::HostPanel(void)
|
||||
g_ServerListManager.m_ServerVisibility = EServerVisibility_t::PUBLIC;
|
||||
}
|
||||
|
||||
ImGui::TextColored(m_HostRequestMessageColor, "%s", m_svHostRequestMessage.c_str());
|
||||
ImGui::TextColored(m_HostMessageColor, "%s", m_svHostMessage.c_str());
|
||||
if (!m_svHostToken.empty())
|
||||
{
|
||||
ImGui::InputText("##ServerHost_HostToken", &m_svHostToken, ImGuiInputTextFlags_ReadOnly);
|
||||
@ -573,7 +577,7 @@ void CBrowser::HostPanel(void)
|
||||
{
|
||||
if (ImGui::Button("Start server", ImVec2(contentRegionMax.x, 32)))
|
||||
{
|
||||
m_svHostRequestMessage.clear();
|
||||
m_svHostMessage.clear();
|
||||
|
||||
bool bEnforceField = g_ServerListManager.m_ServerVisibility == EServerVisibility_t::OFFLINE ? true : !g_ServerListManager.m_Server.name.empty();
|
||||
if (bEnforceField && !g_ServerListManager.m_Server.playlist.empty() && !g_ServerListManager.m_Server.map.empty())
|
||||
@ -584,36 +588,30 @@ void CBrowser::HostPanel(void)
|
||||
{
|
||||
if (g_ServerListManager.m_Server.name.empty())
|
||||
{
|
||||
m_svHostRequestMessage = "Server name is required.";
|
||||
m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
m_svHostMessage = "Server name is required.";
|
||||
m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
}
|
||||
else if (g_ServerListManager.m_Server.playlist.empty())
|
||||
{
|
||||
m_svHostRequestMessage = "Playlist is required.";
|
||||
m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
m_svHostMessage = "Playlist is required.";
|
||||
m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
}
|
||||
else if (g_ServerListManager.m_Server.map.empty())
|
||||
{
|
||||
m_svHostRequestMessage = "Level name is required.";
|
||||
m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
m_svHostMessage = "Level name is required.";
|
||||
m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Reload playlist", ImVec2(contentRegionMax.x, 32)))
|
||||
{
|
||||
g_TaskScheduler->Dispatch([]()
|
||||
{
|
||||
v_Playlists_Download_f();
|
||||
Playlists_SDKInit(); // Re-Init playlist.
|
||||
}, 0);
|
||||
v_Playlists_Download_f();
|
||||
Playlists_SDKInit(); // Re-Init playlist.
|
||||
}
|
||||
|
||||
if (ImGui::Button("Reload banlist", ImVec2(contentRegionMax.x, 32)))
|
||||
{
|
||||
g_TaskScheduler->Dispatch([]()
|
||||
{
|
||||
g_BanSystem.LoadList();
|
||||
}, 0);
|
||||
g_BanSystem.LoadList();
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -621,11 +619,7 @@ void CBrowser::HostPanel(void)
|
||||
if (ImGui::Button("Stop server", ImVec2(contentRegionMax.x, 32)))
|
||||
{
|
||||
ProcessCommand("LeaveMatch"); // TODO: use script callback instead.
|
||||
g_TaskScheduler->Dispatch([]()
|
||||
{
|
||||
// Force CHostState::FrameUpdate to shutdown the server for dedicated.
|
||||
g_pHostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN;
|
||||
}, 0);
|
||||
g_pHostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Change level", ImVec2(contentRegionMax.x, 32)))
|
||||
@ -636,8 +630,8 @@ void CBrowser::HostPanel(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_svHostRequestMessage = "Failed to change level: 'levelname' was empty.";
|
||||
m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
m_svHostMessage = "Failed to change level: 'levelname' was empty.";
|
||||
m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,17 +691,16 @@ void CBrowser::UpdateHostingStatus(void)
|
||||
{
|
||||
case EHostStatus_t::NOT_HOSTING:
|
||||
{
|
||||
AUTO_LOCK(m_Mutex);
|
||||
if (!m_svHostToken.empty())
|
||||
{
|
||||
m_svHostToken.clear();
|
||||
}
|
||||
|
||||
if (ImGui::ColorConvertFloat4ToU32(m_HostRequestMessageColor) == // Only clear if this is green (a valid hosting message).
|
||||
if (ImGui::ColorConvertFloat4ToU32(m_HostMessageColor) == // Only clear if this is green (a valid hosting message).
|
||||
ImGui::ColorConvertFloat4ToU32(ImVec4(0.00f, 1.00f, 0.00f, 1.00f)))
|
||||
{
|
||||
m_svHostRequestMessage.clear();
|
||||
m_HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
m_svHostMessage.clear();
|
||||
m_HostMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -737,33 +730,26 @@ void CBrowser::UpdateHostingStatus(void)
|
||||
break;
|
||||
}
|
||||
|
||||
g_TaskScheduler->Dispatch([this]()
|
||||
NetGameServer_t netGameServer
|
||||
{
|
||||
std::lock_guard<std::mutex> f(g_ServerListManager.m_Mutex);
|
||||
NetGameServer_t netGameServer
|
||||
{
|
||||
g_ServerListManager.m_Server.name,
|
||||
g_ServerListManager.m_Server.description,
|
||||
g_ServerListManager.m_Server.hidden,
|
||||
g_pHostState->m_levelName,
|
||||
v_Playlists_GetCurrent(),
|
||||
hostip->GetString(),
|
||||
hostport->GetInt(),
|
||||
g_pNetKey->GetBase64NetKey(),
|
||||
*g_nServerRemoteChecksum,
|
||||
SDK_VERSION,
|
||||
g_pServer->GetNumClients(),
|
||||
g_ServerGlobalVariables->m_nMaxClients,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()
|
||||
).count()
|
||||
};
|
||||
|
||||
std::thread post(&CBrowser::SendHostingPostRequest, this, netGameServer);
|
||||
post.detach();
|
||||
|
||||
}, 0);
|
||||
g_ServerListManager.m_Server.name,
|
||||
g_ServerListManager.m_Server.description,
|
||||
g_ServerListManager.m_Server.hidden,
|
||||
g_pHostState->m_levelName,
|
||||
v_Playlists_GetCurrent(),
|
||||
hostip->GetString(),
|
||||
hostport->GetInt(),
|
||||
g_pNetKey->GetBase64NetKey(),
|
||||
*g_nServerRemoteChecksum,
|
||||
SDK_VERSION,
|
||||
g_pServer->GetNumClients(),
|
||||
g_ServerGlobalVariables->m_nMaxClients,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()
|
||||
).count()
|
||||
};
|
||||
|
||||
SendHostingPostRequest(netGameServer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -773,59 +759,74 @@ void CBrowser::UpdateHostingStatus(void)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: sends the hosting POST request to the comp server
|
||||
// Purpose: sends the hosting POST request to the comp server and installs the
|
||||
// host data on the server browser
|
||||
// Input : &gameServer -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBrowser::SendHostingPostRequest(const NetGameServer_t& gameServer)
|
||||
{
|
||||
#ifndef CLIENT_DLL
|
||||
string svHostRequestMessage;
|
||||
string svHostToken;
|
||||
string svHostIp;
|
||||
std::thread request([&, gameServer]
|
||||
{
|
||||
string hostRequestMessage;
|
||||
string hostToken;
|
||||
string hostIp;
|
||||
|
||||
const bool result = g_MasterServer.PostServerHost(svHostRequestMessage, svHostToken, svHostIp, gameServer);
|
||||
const bool result = g_MasterServer.PostServerHost(hostRequestMessage, hostToken, hostIp, gameServer);
|
||||
|
||||
AUTO_LOCK(m_Mutex);
|
||||
g_TaskScheduler->Dispatch([&, result, hostRequestMessage, hostToken, hostIp]
|
||||
{
|
||||
InstallHostingDetails(result, hostRequestMessage.c_str(), hostToken.c_str(), hostIp);
|
||||
}, 0);
|
||||
}
|
||||
);
|
||||
request.detach();
|
||||
#endif // !CLIENT_DLL
|
||||
}
|
||||
|
||||
m_svHostRequestMessage = svHostRequestMessage;
|
||||
m_svHostToken = svHostToken;
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: installs the host data on the server browser
|
||||
// Input : postFailed -
|
||||
// *hostMessage -
|
||||
// *hostToken -
|
||||
// &hostIp -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBrowser::InstallHostingDetails(const bool postFailed, const char* const hostMessage, const char* const hostToken, const string& hostIp)
|
||||
{
|
||||
#ifndef CLIENT_DLL
|
||||
m_svHostMessage = hostMessage;
|
||||
m_svHostToken = hostToken;
|
||||
|
||||
if (!svHostIp.empty())
|
||||
if (!hostIp.empty())
|
||||
{
|
||||
// Must be set from the main thread, dispatch it off
|
||||
// and set it at the start of the next frame.
|
||||
g_TaskScheduler->Dispatch([svHostIp]()
|
||||
{
|
||||
g_MasterServer.SetHostIP(svHostIp);
|
||||
}, 0);
|
||||
g_MasterServer.SetHostIP(hostIp);
|
||||
}
|
||||
|
||||
if (result)
|
||||
if (postFailed)
|
||||
{
|
||||
m_HostRequestMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f);
|
||||
m_HostMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f);
|
||||
stringstream ssMessage;
|
||||
ssMessage << "Broadcasting: ";
|
||||
if (!m_svHostToken.empty())
|
||||
{
|
||||
ssMessage << "share the following token for clients to connect: ";
|
||||
}
|
||||
m_svHostRequestMessage = ssMessage.str();
|
||||
m_svHostMessage = ssMessage.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
}
|
||||
#endif // !CLIENT_DLL
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: processes submitted commands for the main thread
|
||||
// Purpose: processes submitted commands
|
||||
// Input : *pszCommand -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBrowser::ProcessCommand(const char* pszCommand) const
|
||||
{
|
||||
Cbuf_AddText(Cbuf_GetCurrentPlayer(), pszCommand, cmd_source_t::kCommandSrcCode);
|
||||
//g_TaskScheduler->Dispatch(Cbuf_Execute, 0); // Run in main thread.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -848,4 +849,4 @@ void CBrowser::ToggleBrowser_f()
|
||||
ResetInput(); // Disable input to game when browser is drawn.
|
||||
}
|
||||
|
||||
CBrowser g_Browser;
|
||||
CBrowser g_Browser;
|
||||
|
@ -28,6 +28,7 @@ public:
|
||||
void HostPanel(void);
|
||||
|
||||
void UpdateHostingStatus(void);
|
||||
void InstallHostingDetails(const bool postFailed, const char* const hostMessage, const char* const hostToken, const string& hostIp);
|
||||
void SendHostingPostRequest(const NetGameServer_t& gameServer);
|
||||
|
||||
void ProcessCommand(const char* pszCommand) const;
|
||||
@ -43,6 +44,11 @@ public:
|
||||
const char* m_pszBrowserLabel;
|
||||
bool m_bActivate;
|
||||
|
||||
private:
|
||||
inline void SetServerListMessage(const char* const message) { m_svServerListMessage = message; };
|
||||
inline void SetHostMessage(const char* const message) { m_svHostMessage = message; }
|
||||
inline void SetHostToken(const char* const message) { m_svHostToken = message; }
|
||||
|
||||
private:
|
||||
bool m_bInitialized;
|
||||
bool m_bReclaimFocus;
|
||||
@ -55,7 +61,6 @@ private:
|
||||
|
||||
ID3D11ShaderResourceView* m_idLockedIcon;
|
||||
MODULERESOURCE m_rLockedIconBlob;
|
||||
mutable CThreadFastMutex m_Mutex;
|
||||
|
||||
////////////////////
|
||||
// Server List //
|
||||
@ -66,14 +71,13 @@ private:
|
||||
////////////////////
|
||||
// Host Server //
|
||||
////////////////////
|
||||
string m_svHostRequestMessage;
|
||||
string m_svHostMessage;
|
||||
string m_svHostToken;
|
||||
ImVec4 m_HostRequestMessageColor;
|
||||
ImVec4 m_HostMessageColor;
|
||||
|
||||
////////////////////
|
||||
// Private Server //
|
||||
////////////////////
|
||||
string m_svHiddenServerToken;
|
||||
string m_svHiddenServerRequestMessage;
|
||||
ImVec4 m_ivHiddenServerMessageColor;
|
||||
|
||||
|
@ -26,12 +26,13 @@ History:
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console variables
|
||||
//-----------------------------------------------------------------------------
|
||||
static ConVar con_max_lines("con_max_lines", "1024", FCVAR_DEVELOPMENTONLY | FCVAR_MATERIAL_SYSTEM_THREAD, "Maximum number of lines in the console before cleanup starts", true, 1.f, false, 0.f);
|
||||
static ConVar con_max_history("con_max_history", "512", FCVAR_DEVELOPMENTONLY | FCVAR_MATERIAL_SYSTEM_THREAD, "Maximum number of command submission items before history cleanup starts", true, 0.f, false, 0.f);
|
||||
static ConVar con_suggest_limit("con_suggest_limit", "128", FCVAR_DEVELOPMENTONLY | FCVAR_MATERIAL_SYSTEM_THREAD, "Maximum number of suggestions the autocomplete window will show for the console", true, 0.f, false, 0.f);
|
||||
static ConVar con_max_lines("con_max_lines", "1024", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "Maximum number of lines in the console before cleanup starts", true, 1.f, false, 0.f);
|
||||
|
||||
static ConVar con_suggest_showhelptext("con_suggest_showhelptext", "1", FCVAR_DEVELOPMENTONLY | FCVAR_MATERIAL_SYSTEM_THREAD, "Show CommandBase help text in autocomplete window");
|
||||
static ConVar con_suggest_showflags("con_suggest_showflags", "1", FCVAR_DEVELOPMENTONLY | FCVAR_MATERIAL_SYSTEM_THREAD, "Show CommandBase flags in autocomplete window");
|
||||
static ConVar con_max_history("con_max_history", "512", FCVAR_DEVELOPMENTONLY, "Maximum number of command submission items before history cleanup starts", true, 0.f, false, 0.f);
|
||||
static ConVar con_suggest_limit("con_suggest_limit", "128", FCVAR_DEVELOPMENTONLY, "Maximum number of suggestions the autocomplete window will show for the console", true, 0.f, false, 0.f);
|
||||
|
||||
static ConVar con_suggest_showhelptext("con_suggest_showhelptext", "1", FCVAR_DEVELOPMENTONLY, "Show CommandBase help text in autocomplete window");
|
||||
static ConVar con_suggest_showflags("con_suggest_showflags", "1", FCVAR_DEVELOPMENTONLY, "Show CommandBase flags in autocomplete window");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console commands
|
||||
@ -207,15 +208,9 @@ void CConsole::RunFrame(void)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: runs tasks for the console while not being drawn
|
||||
// (!!! RunTask and RunFrame must be called from the same thread !!!)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::RunTask(void)
|
||||
{
|
||||
// m_Logger and m_vHistory are modified.
|
||||
AUTO_LOCK(m_Mutex);
|
||||
|
||||
ClampLogSize();
|
||||
ClampHistorySize();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -297,10 +292,7 @@ void CConsole::DrawSurface(void)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 1.f }); iVars++;
|
||||
ImGui::BeginChild(m_pszLoggingLabel, ImVec2(0, -flFooterHeightReserve), true, m_nLoggingFlags);
|
||||
|
||||
// Mutex is locked here, as we start using/modifying
|
||||
// non-atomic members that are used from several threads.
|
||||
AUTO_LOCK(m_Mutex);
|
||||
|
||||
m_Logger.Render();
|
||||
|
||||
if (m_bCopyToClipBoard)
|
||||
@ -335,6 +327,8 @@ void CConsole::DrawSurface(void)
|
||||
ImGui::PushItemWidth(flFooterWidthReserve - 80);
|
||||
if (ImGui::InputText("##input", m_szInputBuf, IM_ARRAYSIZE(m_szInputBuf), m_nInputFlags, &TextEditCallbackStub, reinterpret_cast<void*>(this)))
|
||||
{
|
||||
// If we selected something in the suggestions window, create the
|
||||
// command from that instead
|
||||
if (m_nSuggestPos > PositionMode_t::kPark)
|
||||
{
|
||||
BuildInputFromSelected(m_vSuggest[m_nSuggestPos], m_svInputConVar);
|
||||
@ -552,12 +546,14 @@ bool CConsole::AutoComplete(void)
|
||||
// command string.
|
||||
for (; i < sizeof(m_szInputBuf); i++)
|
||||
{
|
||||
if (isspace(m_szInputBuf[i]))
|
||||
const char c = m_szInputBuf[i];
|
||||
|
||||
if (c == '\0' || isspace(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
szCommand[i] = m_szInputBuf[i];
|
||||
szCommand[i] = c;
|
||||
}
|
||||
|
||||
szCommand[i] = '\0';
|
||||
@ -642,7 +638,7 @@ void CConsole::FindFromPartial(void)
|
||||
{
|
||||
const ConVar* pConVar = reinterpret_cast<const ConVar*>(pCommandBase);
|
||||
|
||||
svValue = " = ["; // Assign default value to string if its a ConVar.
|
||||
svValue = " = ["; // Assign current value to string if its a ConVar.
|
||||
svValue.append(pConVar->GetString());
|
||||
svValue.append("]");
|
||||
}
|
||||
@ -681,16 +677,7 @@ void CConsole::ProcessCommand(string svCommand)
|
||||
Cbuf_AddText(Cbuf_GetCurrentPlayer(), svCommand.c_str(), cmd_source_t::kCommandSrcCode);
|
||||
m_nHistoryPos = PositionMode_t::kPark;
|
||||
|
||||
for (size_t i = m_vHistory.size(); i-- > 0;)
|
||||
{
|
||||
if (m_vHistory[i].compare(svCommand) == 0)
|
||||
{
|
||||
m_vHistory.erase(m_vHistory.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_vHistory.push_back(svCommand);
|
||||
AddHistory(svCommand.c_str());
|
||||
m_Logger.ShouldScrollToBottom(true);
|
||||
}
|
||||
|
||||
@ -756,38 +743,6 @@ void CConsole::BuildSuggestPanelRect(void)
|
||||
m_ivSuggestWindowSize = ImVec2(600, flWindowHeight);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: clamps the size of the log vector
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::ClampLogSize(void)
|
||||
{
|
||||
const int nMaxLines = con_max_lines.GetInt();
|
||||
|
||||
if (m_Logger.GetTotalLines() > nMaxLines)
|
||||
{
|
||||
while (m_Logger.GetTotalLines() > nMaxLines)
|
||||
{
|
||||
m_Logger.RemoveLine(0);
|
||||
m_nScrollBack++;
|
||||
m_nSelectBack++;
|
||||
}
|
||||
m_Logger.MoveSelection(m_nSelectBack, false);
|
||||
m_Logger.MoveCursor(m_nSelectBack, false);
|
||||
m_nSelectBack = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: clamps the size of the history vector
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::ClampHistorySize(void)
|
||||
{
|
||||
while (m_vHistory.size() > con_max_history.GetInt())
|
||||
{
|
||||
m_vHistory.erase(m_vHistory.begin());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: loads flag images from resource section (must be aligned with resource.h!)
|
||||
// Output : true on success, false on failure
|
||||
@ -1065,18 +1020,21 @@ int CConsole::TextEditCallbackStub(ImGuiInputTextCallbackData* iData)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: adds logs to the vector
|
||||
// Purpose: adds logs to the console; this is the only place text is added to
|
||||
// the vector, do not call 'm_Logger.InsertText' elsewhere as we also manage
|
||||
// the size of the vector here !!!
|
||||
// Input : &conLog -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::AddLog(const ConLog_t& conLog)
|
||||
{
|
||||
AUTO_LOCK(m_Mutex);
|
||||
|
||||
m_Logger.InsertText(conLog);
|
||||
ClampLogSize();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: adds logs to the vector (internal)
|
||||
// Only call when mutex lock is obtained!
|
||||
// Purpose: adds logs to the console (internal)
|
||||
// Input : &color -
|
||||
// *fmt -
|
||||
// ... -
|
||||
@ -1090,7 +1048,7 @@ void CConsole::AddLog(const ImVec4& color, const char* fmt, ...) /*IM_FMTARGS(2)
|
||||
result = FormatV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
m_Logger.InsertText(ConLog_t(result, color));
|
||||
AddLog(ConLog_t(result, color));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1146,13 +1104,56 @@ void CConsole::ClearLog(void)
|
||||
m_Logger.RemoveLine(0, (m_Logger.GetTotalLines() - 1));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: clamps the size of the log vector
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::ClampLogSize(void)
|
||||
{
|
||||
const int nMaxLines = con_max_lines.GetInt();
|
||||
|
||||
if (m_Logger.GetTotalLines() > nMaxLines)
|
||||
{
|
||||
while (m_Logger.GetTotalLines() > nMaxLines)
|
||||
{
|
||||
m_Logger.RemoveLine(0);
|
||||
m_nScrollBack++;
|
||||
m_nSelectBack++;
|
||||
}
|
||||
|
||||
m_Logger.MoveSelection(m_nSelectBack, false);
|
||||
m_Logger.MoveCursor(m_nSelectBack, false);
|
||||
m_nSelectBack = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: adds a command to the history vector; this is the only place text
|
||||
// is added to the vector, do not call 'm_History.push_back' elsewhere as we
|
||||
// also manage the size of the vector here !!!
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::AddHistory(const char* const command)
|
||||
{
|
||||
// If this command was already in the history, remove it so when we push it
|
||||
// in, it would appear all the way at the top of the list
|
||||
for (size_t i = m_vHistory.size(); i-- > 0;)
|
||||
{
|
||||
if (m_vHistory[i].compare(command) == 0)
|
||||
{
|
||||
m_vHistory.erase(m_vHistory.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_vHistory.push_back(command);
|
||||
ClampHistorySize();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: gets all console submissions
|
||||
// Output : vector of strings
|
||||
//-----------------------------------------------------------------------------
|
||||
vector<string> CConsole::GetHistory(void) const
|
||||
const vector<string>& CConsole::GetHistory(void) const
|
||||
{
|
||||
AUTO_LOCK(m_Mutex);
|
||||
return m_vHistory;
|
||||
}
|
||||
|
||||
@ -1161,12 +1162,21 @@ vector<string> CConsole::GetHistory(void) const
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::ClearHistory(void)
|
||||
{
|
||||
AUTO_LOCK(m_Mutex);
|
||||
|
||||
m_vHistory.clear();
|
||||
BuildSummary();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: clamps the size of the history vector
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::ClampHistorySize(void)
|
||||
{
|
||||
while (m_vHistory.size() > con_max_history.GetInt())
|
||||
{
|
||||
m_vHistory.erase(m_vHistory.begin());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: sets the console front-end style
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1192,7 +1202,7 @@ void CConsole::ToggleConsole_f()
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConsole::LogHistory_f()
|
||||
{
|
||||
const vector<string> vHistory = g_Console.GetHistory();
|
||||
const vector<string>& vHistory = g_Console.GetHistory();
|
||||
for (size_t i = 0, nh = vHistory.size(); i < nh; i++)
|
||||
{
|
||||
Msg(eDLL_T::COMMON, "%3d: %s\n", i, vHistory[i].c_str());
|
||||
|
@ -42,9 +42,6 @@ private:
|
||||
void BuildInputFromSelected(const CSuggest& suggest, string& svInput);
|
||||
void BuildSuggestPanelRect(void);
|
||||
|
||||
void ClampLogSize(void);
|
||||
void ClampHistorySize(void);
|
||||
|
||||
bool LoadFlagIcons(void);
|
||||
int GetFlagTextureIndex(int nFlags) const;
|
||||
|
||||
@ -57,7 +54,8 @@ public:
|
||||
void RemoveLog(int nStart, int nEnd);
|
||||
void ClearLog(void);
|
||||
|
||||
vector<string> GetHistory(void) const;
|
||||
void AddHistory(const char* const command);
|
||||
const vector<string>& GetHistory(void) const;
|
||||
void ClearHistory(void);
|
||||
|
||||
inline bool IsVisible() { return m_flFadeAlpha > 0.0f; }
|
||||
@ -70,9 +68,12 @@ public:
|
||||
static void ClearLines_f();
|
||||
static void ClearHistory_f();
|
||||
|
||||
private: // Internal only.
|
||||
private: // Internals.
|
||||
void AddLog(const ImVec4& color, const char* fmt, ...) /*IM_FMTARGS(2)*/;
|
||||
|
||||
void ClampLogSize(void);
|
||||
void ClampHistorySize(void);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
virtual void SetStyleVar(void);
|
||||
|
||||
@ -111,6 +112,7 @@ private:
|
||||
ImGuiStyle_t m_Style;
|
||||
ImVec2 m_ivSuggestWindowPos;
|
||||
ImVec2 m_ivSuggestWindowSize;
|
||||
|
||||
CTextLogger m_Logger;
|
||||
mutable CThreadFastMutex m_Mutex;
|
||||
|
||||
|
105
src/gameui/imgui_system.cpp
Normal file
105
src/gameui/imgui_system.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
//=============================================================================//
|
||||
|
||||
#include "imgui/misc/imgui_snapshot.h"
|
||||
#include "engine/sys_mainwind.h"
|
||||
#include "windows/id3dx.h"
|
||||
|
||||
#include "IBrowser.h"
|
||||
#include "IConsole.h"
|
||||
|
||||
#include "imgui_system.h"
|
||||
|
||||
static ImDrawDataSnapshot s_imguiSnapshotData;
|
||||
static bool s_imguiSystemInitialized = false;
|
||||
|
||||
bool ImguiSystem_IsInitialized()
|
||||
{
|
||||
return s_imguiSystemInitialized;
|
||||
}
|
||||
|
||||
bool ImguiSystem_Init()
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGuiContext* const context = ImGui::CreateContext();
|
||||
|
||||
if (!context)
|
||||
return false;
|
||||
|
||||
// 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/4828
|
||||
context->ConfigNavWindowingKeyNext = ImGuiMod_Shift | ImGuiKey_Space;
|
||||
context->ConfigNavWindowingKeyPrev = 0;
|
||||
|
||||
ImGuiViewport* const vp = ImGui::GetMainViewport();
|
||||
vp->PlatformHandleRaw = g_pGame->GetWindow();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_IsSRGB;
|
||||
|
||||
const bool win32ImplInit = ImGui_ImplWin32_Init(g_pGame->GetWindow());
|
||||
|
||||
if (!win32ImplInit)
|
||||
return false;
|
||||
|
||||
const bool dx11ImplInit = ImGui_ImplDX11_Init(D3D11Device(), D3D11DeviceContext());
|
||||
|
||||
if (!dx11ImplInit)
|
||||
return false;
|
||||
|
||||
s_imguiSystemInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImguiSystem_Shutdown()
|
||||
{
|
||||
if (!s_imguiSystemInitialized)
|
||||
return;
|
||||
|
||||
s_imguiSystemInitialized = false;
|
||||
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
s_imguiSnapshotData.Clear();
|
||||
}
|
||||
|
||||
void ImguiSystem_SwapBuffers()
|
||||
{
|
||||
if (!s_imguiSystemInitialized)
|
||||
return;
|
||||
|
||||
ImDrawData* const drawData = ImGui::GetDrawData();
|
||||
|
||||
if (drawData)
|
||||
s_imguiSnapshotData.SnapUsingSwap(drawData, ImGui::GetTime());
|
||||
}
|
||||
|
||||
void ImguiSystem_SampleFrame()
|
||||
{
|
||||
if (!s_imguiSystemInitialized)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
g_Browser.RunTask();
|
||||
g_Browser.RunFrame();
|
||||
|
||||
g_Console.RunTask();
|
||||
g_Console.RunFrame();
|
||||
|
||||
ImGui::EndFrame();
|
||||
ImGui::Render();
|
||||
}
|
||||
|
||||
void ImguiSystem_RenderFrame()
|
||||
{
|
||||
if (!s_imguiSystemInitialized)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX11_RenderDrawData(&s_imguiSnapshotData.DrawData);
|
||||
}
|
14
src/gameui/imgui_system.h
Normal file
14
src/gameui/imgui_system.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef IMGUI_SYSTEM_H
|
||||
#define IMGUI_SYSTEM_H
|
||||
|
||||
extern bool ImguiSystem_IsInitialized();
|
||||
|
||||
extern bool ImguiSystem_Init();
|
||||
extern void ImguiSystem_Shutdown();
|
||||
|
||||
extern void ImguiSystem_SwapBuffers();
|
||||
|
||||
extern void ImguiSystem_SampleFrame();
|
||||
extern void ImguiSystem_RenderFrame();
|
||||
|
||||
#endif // IMGUI_SYSTEM_H
|
@ -13,7 +13,7 @@
|
||||
#include "engine/sys_engine.h"
|
||||
#include "geforce/reflex.h"
|
||||
#ifndef MATERIALSYSTEM_NODX
|
||||
#include "windows/id3dx.h"
|
||||
#include "gameui/imgui_system.h"
|
||||
#include "materialsystem/cmaterialglue.h"
|
||||
#endif // !MATERIALSYSTEM_NODX
|
||||
#include "materialsystem/cmaterialsystem.h"
|
||||
@ -22,6 +22,20 @@
|
||||
PCLSTATS_DEFINE()
|
||||
#endif // MATERIALSYSTEM_NODX
|
||||
|
||||
bool CMaterialSystem::Connect(CMaterialSystem* thisptr, const CreateInterfaceFn factory)
|
||||
{
|
||||
const bool result = CMaterialSystem__Connect(thisptr, factory);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CMaterialSystem::Disconnect(CMaterialSystem* thisptr)
|
||||
{
|
||||
#ifndef MATERIALSYSTEM_NODX
|
||||
ImguiSystem_Shutdown();
|
||||
#endif
|
||||
CMaterialSystem__Disconnect(thisptr);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: initialization of the material system
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -111,24 +125,18 @@ void* __fastcall DispatchDrawCall(int64_t a1, uint64_t a2, int a3, int a4, int64
|
||||
//---------------------------------------------------------------------------------
|
||||
ssize_t SpinPresent(void)
|
||||
{
|
||||
// TODO[ AMOS ]: move imgui code to a separate file.
|
||||
extern void DrawImGui();
|
||||
extern void ImGui_Init();
|
||||
|
||||
if (!g_bImGuiInitialized)
|
||||
{
|
||||
ImGui_Init();
|
||||
g_ThreadRenderThreadID = GetCurrentThreadId();
|
||||
g_bImGuiInitialized = true;
|
||||
}
|
||||
|
||||
if (g_pEngine->GetQuitting() == IEngine::QUIT_NOTQUITTING)
|
||||
DrawImGui();
|
||||
ImguiSystem_RenderFrame();
|
||||
|
||||
const ssize_t val = v_SpinPresent();
|
||||
return val;
|
||||
}
|
||||
|
||||
void* CMaterialSystem::SwapBuffers(CMaterialSystem* pMatSys)
|
||||
{
|
||||
ImguiSystem_SwapBuffers();
|
||||
return CMaterialSystem__SwapBuffers(pMatSys);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: finds a material
|
||||
// Input : *pMatSys -
|
||||
@ -171,10 +179,16 @@ void VMaterialSystem::Detour(const bool bAttach) const
|
||||
{
|
||||
DetourSetup(&CMaterialSystem__Init, &CMaterialSystem::Init, bAttach);
|
||||
DetourSetup(&CMaterialSystem__Shutdown, &CMaterialSystem::Shutdown, bAttach);
|
||||
|
||||
DetourSetup(&CMaterialSystem__Connect, &CMaterialSystem::Connect, bAttach);
|
||||
DetourSetup(&CMaterialSystem__Disconnect, &CMaterialSystem::Disconnect, bAttach);
|
||||
|
||||
#ifndef MATERIALSYSTEM_NODX
|
||||
DetourSetup(&CMaterialSystem__SwapBuffers, &CMaterialSystem::SwapBuffers, bAttach);
|
||||
DetourSetup(&CMaterialSystem__FindMaterialEx, &CMaterialSystem::FindMaterialEx, bAttach);
|
||||
|
||||
DetourSetup(&v_StreamDB_Init, &StreamDB_Init, bAttach);
|
||||
DetourSetup(&v_DispatchDrawCall, &DispatchDrawCall, bAttach);
|
||||
DetourSetup(&v_SpinPresent, &SpinPresent, bAttach);
|
||||
DetourSetup(&CMaterialSystem__FindMaterialEx, &CMaterialSystem::FindMaterialEx, bAttach);
|
||||
#endif // !MATERIALSYSTEM_NODX
|
||||
}
|
||||
|
@ -8,9 +8,13 @@
|
||||
class CMaterialSystem
|
||||
{
|
||||
public:
|
||||
static bool Connect(CMaterialSystem* thisptr, const CreateInterfaceFn factory);
|
||||
static void Disconnect(CMaterialSystem* thisptr);
|
||||
|
||||
static InitReturnVal_t Init(CMaterialSystem* thisptr);
|
||||
static int Shutdown(CMaterialSystem* thisptr);
|
||||
#ifndef MATERIALSYSTEM_NODX
|
||||
static void* SwapBuffers(CMaterialSystem* pMatSys);
|
||||
static CMaterialGlue* FindMaterialEx(CMaterialSystem* pMatSys, const char* pMaterialName, uint8_t nMaterialType, int nUnk, bool bComplain);
|
||||
static Vector2D GetScreenSize(CMaterialSystem* pMatSys = nullptr);
|
||||
#endif // !MATERIALSYSTEM_NODX
|
||||
@ -59,11 +63,15 @@ inline CMaterialDeviceMgr* g_pMaterialAdapterMgr = nullptr;
|
||||
/* ==== MATERIALSYSTEM ================================================================================================================================================== */
|
||||
inline InitReturnVal_t(*CMaterialSystem__Init)(CMaterialSystem* thisptr);
|
||||
inline int(*CMaterialSystem__Shutdown)(CMaterialSystem* thisptr);
|
||||
inline void(*CMaterialSystem__Disconnect)(void);
|
||||
|
||||
inline bool(*CMaterialSystem__Connect)(CMaterialSystem*, const CreateInterfaceFn);
|
||||
inline void(*CMaterialSystem__Disconnect)(CMaterialSystem*);
|
||||
|
||||
inline CMaterialSystem* g_pMaterialSystem = nullptr;
|
||||
inline void* g_pMaterialVFTable = nullptr;
|
||||
#ifndef MATERIALSYSTEM_NODX
|
||||
inline void*(*CMaterialSystem__SwapBuffers)(CMaterialSystem* pMatSys);
|
||||
|
||||
inline CMaterialGlue*(*CMaterialSystem__FindMaterialEx)(CMaterialSystem* pMatSys, const char* pMaterialName, uint8_t nMaterialType, int nUnk, bool bComplain);
|
||||
inline void(*CMaterialSystem__GetScreenSize)(CMaterialSystem* pMatSys, float* outX, float* outY);
|
||||
|
||||
@ -98,8 +106,10 @@ class VMaterialSystem : public IDetour
|
||||
LogConAdr("CMaterial::`vftable'", g_pMaterialVFTable);
|
||||
LogFunAdr("CMaterialSystem::Init", CMaterialSystem__Init);
|
||||
LogFunAdr("CMaterialSystem::Shutdown", CMaterialSystem__Shutdown);
|
||||
LogFunAdr("CMaterialSystem::Connect", CMaterialSystem__Connect);
|
||||
LogFunAdr("CMaterialSystem::Disconnect", CMaterialSystem__Disconnect);
|
||||
#ifndef MATERIALSYSTEM_NODX
|
||||
LogFunAdr("CMaterialSystem::SwapBuffers", CMaterialSystem__SwapBuffers);
|
||||
LogFunAdr("CMaterialSystem::FindMaterialEx", CMaterialSystem__FindMaterialEx);
|
||||
LogFunAdr("CMaterialSystem::GetScreenSize", CMaterialSystem__GetScreenSize);
|
||||
LogFunAdr("CMaterialSystem::GetStreamOverlay", CMaterialSystem__GetStreamOverlay);
|
||||
@ -122,8 +132,12 @@ class VMaterialSystem : public IDetour
|
||||
{
|
||||
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 70 48 83 3D ?? ?? ?? ?? ??").GetPtr(CMaterialSystem__Init);
|
||||
g_GameDll.FindPatternSIMD("48 83 EC 58 48 89 6C 24 ??").GetPtr(CMaterialSystem__Shutdown);
|
||||
|
||||
g_GameDll.FindPatternSIMD("48 89 54 24 ?? 56 48 83 EC 50").GetPtr(CMaterialSystem__Connect);
|
||||
g_GameDll.FindPatternSIMD("48 83 EC 28 8B 0D ?? ?? ?? ?? 48 89 6C 24 ??").GetPtr(CMaterialSystem__Disconnect);
|
||||
#ifndef MATERIALSYSTEM_NODX
|
||||
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 40 65 48 8B 04 25 ?? ?? ?? ??").GetPtr(CMaterialSystem__SwapBuffers);
|
||||
|
||||
g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 44 88 44 24 ?? 48 89 4C 24 ??").GetPtr(CMaterialSystem__FindMaterialEx);
|
||||
g_GameDll.FindPatternSIMD("8B 05 ?? ?? ?? ?? 89 02 8B 05 ?? ?? ?? ?? 41 89 ?? C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC 8B 05 ?? ?? ?? ??").GetPtr(CMaterialSystem__GetScreenSize);
|
||||
g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 0F 84 ?? ?? ?? ?? 48 89 9C 24 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(CMaterialSystem__GetStreamOverlay);
|
||||
|
@ -97,9 +97,8 @@ FORCEINLINE bool ThreadInterlockedAssignIf64(int64 volatile* pDest, int64 value,
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef BUILDING_MATHLIB
|
||||
|
||||
inline ThreadId_t* g_ThreadMainThreadID = nullptr;
|
||||
inline ThreadId_t g_ThreadRenderThreadID = NULL;
|
||||
inline ThreadId_t* g_ThreadServerFrameThreadID = nullptr;
|
||||
extern ThreadId_t* g_ThreadMainThreadID;
|
||||
extern ThreadId_t* g_ThreadServerFrameThreadID;
|
||||
|
||||
FORCEINLINE ThreadId_t ThreadGetCurrentId()
|
||||
{
|
||||
@ -123,11 +122,6 @@ FORCEINLINE bool ThreadInMainThread()
|
||||
return (ThreadGetCurrentId() == (*g_ThreadMainThreadID));
|
||||
}
|
||||
|
||||
FORCEINLINE bool ThreadInRenderThread()
|
||||
{
|
||||
return (ThreadGetCurrentId() == g_ThreadRenderThreadID);
|
||||
}
|
||||
|
||||
FORCEINLINE bool ThreadInServerFrameThread()
|
||||
{
|
||||
return (ThreadGetCurrentId() == (*g_ThreadServerFrameThreadID));
|
||||
@ -332,7 +326,7 @@ private:
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int size> struct CAutoLockTypeDeducer {};
|
||||
template <size_t size> struct CAutoLockTypeDeducer {};
|
||||
template <> struct CAutoLockTypeDeducer<sizeof(CThreadFastMutex)> { typedef CThreadFastMutex Type_t; };
|
||||
|
||||
#define AUTO_LOCK_( type, mutex ) \
|
||||
@ -360,11 +354,7 @@ class VThreadTools : public IDetour
|
||||
{
|
||||
g_GameDll.FindPatternSIMD("48 83 EC 28 FF 15 ?? ?? ?? ?? 89 05 ?? ?? ?? ?? 48 83 C4 28").GetPtr(v_DeclareCurrentThreadIsMainThread);
|
||||
}
|
||||
virtual void GetVar(void) const
|
||||
{
|
||||
g_ThreadMainThreadID = CMemory(v_DeclareCurrentThreadIsMainThread).FindPattern("89 05").ResolveRelativeAddressSelf(0x2, 0x6).RCast<ThreadId_t*>();
|
||||
g_ThreadServerFrameThreadID = g_GameDll.FindPatternSIMD("83 79 ?? ?? 75 28 8B").FindPatternSelf("8B 05").ResolveRelativeAddressSelf(0x2, 0x6).RCast<ThreadId_t*>();
|
||||
}
|
||||
virtual void GetVar(void) const { }
|
||||
virtual void GetCon(void) const { }
|
||||
virtual void Detour(const bool /*bAttach*/) const { }
|
||||
};
|
||||
|
@ -185,7 +185,11 @@ class VCVar : public IDetour
|
||||
|
||||
g_GameDll.FindPatternSIMD("B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 01 48 89 9C 24 ?? ?? ?? ??").GetPtr(v_ConVar_PrintDescription);
|
||||
}
|
||||
virtual void GetVar(void) const { }
|
||||
virtual void GetVar(void) const
|
||||
{
|
||||
g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 0F 45 C8 FF 05 ?? ?? ?? ?? 48 89 0D ?? ?? ?? ??")
|
||||
.FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(3, 7).GetPtr(g_pCVar);
|
||||
}
|
||||
virtual void GetCon(void) const { }
|
||||
virtual void Detour(const bool bAttach) const;
|
||||
};
|
||||
|
2
src/thirdparty/imgui/CMakeLists.txt
vendored
2
src/thirdparty/imgui/CMakeLists.txt
vendored
@ -29,6 +29,8 @@ add_sources( SOURCE_GROUP "Misc"
|
||||
"misc/imgui_editor.h"
|
||||
"misc/imgui_logger.cpp"
|
||||
"misc/imgui_logger.h"
|
||||
"misc/imgui_snapshot.cpp"
|
||||
"misc/imgui_snapshot.h"
|
||||
"misc/imgui_utility.cpp"
|
||||
"misc/imgui_utility.h"
|
||||
"misc/cpp/imgui_stdlib.cpp"
|
||||
|
57
src/thirdparty/imgui/misc/imgui_snapshot.cpp
vendored
Normal file
57
src/thirdparty/imgui/misc/imgui_snapshot.cpp
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
// ImGui snapshot class taken from:
|
||||
// https://github.com/ocornut/imgui/issues/1860
|
||||
#include "imgui.h"
|
||||
#include "imgui_snapshot.h"
|
||||
|
||||
void ImDrawDataSnapshot::Clear()
|
||||
{
|
||||
for (int n = 0; n < Cache.GetMapSize(); n++)
|
||||
if (ImDrawDataSnapshotEntry* entry = Cache.TryGetMapData(n))
|
||||
IM_DELETE(entry->OurCopy);
|
||||
Cache.Clear();
|
||||
DrawData.Clear();
|
||||
}
|
||||
|
||||
void ImDrawDataSnapshot::SnapUsingSwap(ImDrawData* src, double current_time)
|
||||
{
|
||||
ImDrawData* dst = &DrawData;
|
||||
IM_ASSERT(src != dst && src->Valid);
|
||||
|
||||
// Copy all fields except CmdLists[]
|
||||
ImVector<ImDrawList*> backup_draw_list;
|
||||
backup_draw_list.swap(src->CmdLists);
|
||||
IM_ASSERT(src->CmdLists.Data == NULL);
|
||||
*dst = *src;
|
||||
backup_draw_list.swap(src->CmdLists);
|
||||
|
||||
// Swap and mark as used
|
||||
for (ImDrawList* src_list : src->CmdLists)
|
||||
{
|
||||
ImDrawDataSnapshotEntry* entry = GetOrAddEntry(src_list);
|
||||
if (entry->OurCopy == NULL)
|
||||
{
|
||||
entry->SrcCopy = src_list;
|
||||
entry->OurCopy = IM_NEW(ImDrawList)(src_list->_Data);
|
||||
}
|
||||
IM_ASSERT(entry->SrcCopy == src_list);
|
||||
entry->SrcCopy->CmdBuffer.swap(entry->OurCopy->CmdBuffer); // Cheap swap
|
||||
entry->SrcCopy->IdxBuffer.swap(entry->OurCopy->IdxBuffer);
|
||||
entry->SrcCopy->VtxBuffer.swap(entry->OurCopy->VtxBuffer);
|
||||
entry->SrcCopy->CmdBuffer.reserve(entry->OurCopy->CmdBuffer.Capacity); // Preserve bigger size to avoid reallocs for two consecutive frames
|
||||
entry->SrcCopy->IdxBuffer.reserve(entry->OurCopy->IdxBuffer.Capacity);
|
||||
entry->SrcCopy->VtxBuffer.reserve(entry->OurCopy->VtxBuffer.Capacity);
|
||||
entry->LastUsedTime = current_time;
|
||||
dst->CmdLists.push_back(entry->OurCopy);
|
||||
}
|
||||
|
||||
// Cleanup unused data
|
||||
const double gc_threshold = current_time - MemoryCompactTimer;
|
||||
for (int n = 0; n < Cache.GetMapSize(); n++)
|
||||
if (ImDrawDataSnapshotEntry* entry = Cache.TryGetMapData(n))
|
||||
{
|
||||
if (entry->LastUsedTime > gc_threshold)
|
||||
continue;
|
||||
IM_DELETE(entry->OurCopy);
|
||||
Cache.Remove(GetDrawListID(entry->SrcCopy), entry);
|
||||
}
|
||||
};
|
33
src/thirdparty/imgui/misc/imgui_snapshot.h
vendored
Normal file
33
src/thirdparty/imgui/misc/imgui_snapshot.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// Usage:
|
||||
// static ImDrawDataSnapshot snapshot; // Important: make persistent across frames to reuse buffers.
|
||||
// snapshot.SnapUsingSwap(ImGui::GetDrawData(), ImGui::GetTime());
|
||||
// [...]
|
||||
// ImGui_ImplDX11_RenderDrawData(&snapshot.DrawData);
|
||||
|
||||
// FIXME: Could store an ID in ImDrawList to make this easier for user.
|
||||
#include "imgui_internal.h" // ImPool<>, ImHashData
|
||||
|
||||
struct ImDrawDataSnapshotEntry
|
||||
{
|
||||
ImDrawList* SrcCopy = NULL; // Drawlist owned by main context
|
||||
ImDrawList* OurCopy = NULL; // Our copy
|
||||
double LastUsedTime = 0.0;
|
||||
};
|
||||
|
||||
struct ImDrawDataSnapshot
|
||||
{
|
||||
// Members
|
||||
ImDrawData DrawData;
|
||||
ImPool<ImDrawDataSnapshotEntry> Cache;
|
||||
float MemoryCompactTimer = 20.0f; // Discard unused data after 20 seconds
|
||||
|
||||
// Functions
|
||||
~ImDrawDataSnapshot() { Clear(); }
|
||||
void Clear();
|
||||
void SnapUsingSwap(ImDrawData* src, double current_time); // Efficient snapshot by swapping data, meaning "src_list" is unusable.
|
||||
//void SnapUsingCopy(ImDrawData* src, double current_time); // Deep-copy snapshot
|
||||
|
||||
// Internals
|
||||
ImGuiID GetDrawListID(ImDrawList* src_list) { return ImHashData(&src_list, sizeof(src_list)); } // Hash pointer
|
||||
ImDrawDataSnapshotEntry* GetOrAddEntry(ImDrawList* src_list) { return Cache.GetOrAddByKey(GetDrawListID(src_list)); }
|
||||
};
|
@ -96,3 +96,9 @@ int CThreadFastMutex::Unlock()
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: originally the game exported 'ThreadInMainThread()' and ThreadInServerFrameThread(),
|
||||
// but since the game is built static, and all instances of said functions are inline, we had
|
||||
// to export the variable symbols instead and get them here to reimplement said functions.
|
||||
ThreadId_t* g_ThreadMainThreadID = CModule::GetExportedSymbol(CModule::GetProcessEnvironmentBlock()->ImageBaseAddress, "g_ThreadMainThreadID").RCast<ThreadId_t*>();
|
||||
ThreadId_t* g_ThreadServerFrameThreadID = CModule::GetExportedSymbol(CModule::GetProcessEnvironmentBlock()->ImageBaseAddress, "g_ThreadServerFrameThreadID").RCast<ThreadId_t*>();
|
||||
|
@ -354,8 +354,8 @@ static bool ConCommandBaseLessFunc(ConCommandBase* const& lhs, ConCommandBase* c
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton CCvarUtilities
|
||||
//-----------------------------------------------------------------------------
|
||||
static CCvarUtilities g_CvarUtilities;
|
||||
CCvarUtilities* cv = &g_CvarUtilities;
|
||||
static CCvarUtilities s_CvarUtilities;
|
||||
CCvarUtilities* cv = &s_CvarUtilities;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
@ -709,7 +709,7 @@ static void CON_Help_f()
|
||||
static ConCommand con_help("con_help", CON_Help_f, "Shows the colors and description of each context", FCVAR_RELEASE);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
CCvar* g_pCVar = CModule::GetExportedSymbol(CModule::GetProcessEnvironmentBlock()->ImageBaseAddress, "g_pCVar").RCast<CCvar*>();
|
||||
CCvar* g_pCVar = nullptr;
|
||||
|
||||
|
||||
static bool CVar_Connect(CCvar* thisptr, CreateInterfaceFn factory)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "geforce/reflex.h"
|
||||
#include "gameui/IConsole.h"
|
||||
#include "gameui/IBrowser.h"
|
||||
#include "gameui/imgui_system.h"
|
||||
#include "engine/framelimit.h"
|
||||
#include "engine/sys_mainwind.h"
|
||||
#include "inputsystem/inputsystem.h"
|
||||
@ -34,7 +35,6 @@ typedef BOOL(WINAPI* IPostMessageA)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM l
|
||||
typedef BOOL(WINAPI* IPostMessageW)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
extern BOOL g_bImGuiInitialized = FALSE;
|
||||
extern UINT g_nWindowRect[2] = { NULL, NULL };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
@ -81,66 +81,13 @@ BOOL WINAPI HPostMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
return s_oPostMessageW(hWnd, Msg, wParam, lParam);
|
||||
}
|
||||
|
||||
//#################################################################################
|
||||
// IMGUI
|
||||
//#################################################################################
|
||||
|
||||
void ImGui_Init()
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiViewport* const vp = ImGui::GetMainViewport();
|
||||
vp->PlatformHandleRaw = g_pGame->GetWindow();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_IsSRGB;
|
||||
|
||||
ImGui_ImplWin32_Init(g_pGame->GetWindow());
|
||||
ImGui_ImplDX11_Init(D3D11Device(), D3D11DeviceContext());
|
||||
}
|
||||
|
||||
void ImGui_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
void DrawImGui()
|
||||
{
|
||||
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_Browser.RunTask();
|
||||
g_Browser.RunFrame();
|
||||
|
||||
g_Console.RunTask();
|
||||
g_Console.RunFrame();
|
||||
|
||||
ImGui::EndFrame();
|
||||
ImGui::Render();
|
||||
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
//#################################################################################
|
||||
// IDXGI
|
||||
//#################################################################################
|
||||
|
||||
static ConVar fps_max_rt("fps_max_rt", "0", FCVAR_RELEASE, "Frame rate limiter within the render thread. -1 indicates the use of desktop refresh. 0 is disabled.", true, -1.f, true, 295.f);
|
||||
static ConVar fps_max_rt_tolerance("fps_max_rt_tolerance", "0.25", FCVAR_RELEASE, "Maximum amount of frame time before frame limiter restarts.", true, 0.f, false, 0.f);
|
||||
static ConVar fps_max_rt_sleep_threshold("fps_max_rt_sleep_threshold", "0.016666667", FCVAR_RELEASE, "Frame limiter starts to sleep when frame time exceeds this threshold.", true, 0.f, false, 0.f);
|
||||
static ConVar fps_max_rt("fps_max_rt", "0", FCVAR_RELEASE | FCVAR_MATERIAL_SYSTEM_THREAD, "Frame rate limiter within the render thread. -1 indicates the use of desktop refresh. 0 is disabled.", true, -1.f, true, 295.f);
|
||||
static ConVar fps_max_rt_tolerance("fps_max_rt_tolerance", "0.25", FCVAR_RELEASE | FCVAR_MATERIAL_SYSTEM_THREAD, "Maximum amount of frame time before frame limiter restarts.", true, 0.f, false, 0.f);
|
||||
static ConVar fps_max_rt_sleep_threshold("fps_max_rt_sleep_threshold", "0.016666667", FCVAR_RELEASE | FCVAR_MATERIAL_SYSTEM_THREAD, "Frame limiter starts to sleep when frame time exceeds this threshold.", true, 0.f, false, 0.f);
|
||||
|
||||
HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags)
|
||||
{
|
||||
@ -420,14 +367,6 @@ void DirectX_Shutdown()
|
||||
|
||||
// Commit the transaction
|
||||
DetourTransactionCommit();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Shutdown ImGui
|
||||
if (g_bImGuiInitialized)
|
||||
{
|
||||
ImGui_Shutdown();
|
||||
g_bImGuiInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void VDXGI::GetAdr(void) const
|
||||
|
Loading…
x
Reference in New Issue
Block a user