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:
Kawe Mazidjatari 2024-02-25 20:12:56 +01:00
parent a1e4500b98
commit e967cb374b
21 changed files with 480 additions and 286 deletions

View File

@ -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()

View File

@ -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"

View File

@ -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

View File

@ -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();

View File

@ -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/" )

View File

@ -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;

View File

@ -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;

View File

@ -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());

View File

@ -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
View 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
View 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

View File

@ -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
}

View File

@ -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);

View File

@ -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 { }
};

View File

@ -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;
};

View File

@ -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"

View 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);
}
};

View 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)); }
};

View File

@ -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*>();

View File

@ -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)

View File

@ -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