diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index a1baa689..3dc4ea0c 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -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() diff --git a/src/engine/host_state.cpp b/src/engine/host_state.cpp index 070958f2..000d7db1 100644 --- a/src/engine/host_state.cpp +++ b/src/engine/host_state.cpp @@ -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" diff --git a/src/engine/sys_getmodes.cpp b/src/engine/sys_getmodes.cpp index bd4e9615..6df451ea 100644 --- a/src/engine/sys_getmodes.cpp +++ b/src/engine/sys_getmodes.cpp @@ -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 diff --git a/src/engine/sys_mainwind.cpp b/src/engine/sys_mainwind.cpp index 2df00d2a..ba8a8d78 100644 --- a/src/engine/sys_mainwind.cpp +++ b/src/engine/sys_mainwind.cpp @@ -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(); diff --git a/src/gameui/CMakeLists.txt b/src/gameui/CMakeLists.txt deleted file mode 100644 index 6b282605..00000000 --- a/src/gameui/CMakeLists.txt +++ /dev/null @@ -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/" ) diff --git a/src/gameui/IBrowser.cpp b/src/gameui/IBrowser.cpp index 94ecc27e..09cd72f9 100644 --- a/src/gameui/IBrowser.cpp +++ b/src/gameui/IBrowser.cpp @@ -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 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::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::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; \ No newline at end of file +CBrowser g_Browser; diff --git a/src/gameui/IBrowser.h b/src/gameui/IBrowser.h index 16b97ee8..428614bb 100644 --- a/src/gameui/IBrowser.h +++ b/src/gameui/IBrowser.h @@ -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; diff --git a/src/gameui/IConsole.cpp b/src/gameui/IConsole.cpp index fdd98411..8fa6662d 100644 --- a/src/gameui/IConsole.cpp +++ b/src/gameui/IConsole.cpp @@ -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(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(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 CConsole::GetHistory(void) const +const vector& CConsole::GetHistory(void) const { - AUTO_LOCK(m_Mutex); return m_vHistory; } @@ -1161,12 +1162,21 @@ vector 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 vHistory = g_Console.GetHistory(); + const vector& 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()); diff --git a/src/gameui/IConsole.h b/src/gameui/IConsole.h index bb974da9..8ef2e09d 100644 --- a/src/gameui/IConsole.h +++ b/src/gameui/IConsole.h @@ -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 GetHistory(void) const; + void AddHistory(const char* const command); + const vector& 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; diff --git a/src/gameui/imgui_system.cpp b/src/gameui/imgui_system.cpp new file mode 100644 index 00000000..06598238 --- /dev/null +++ b/src/gameui/imgui_system.cpp @@ -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); +} diff --git a/src/gameui/imgui_system.h b/src/gameui/imgui_system.h new file mode 100644 index 00000000..b79a311f --- /dev/null +++ b/src/gameui/imgui_system.h @@ -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 diff --git a/src/materialsystem/cmaterialsystem.cpp b/src/materialsystem/cmaterialsystem.cpp index 5e8e82fe..af4ad1d1 100644 --- a/src/materialsystem/cmaterialsystem.cpp +++ b/src/materialsystem/cmaterialsystem.cpp @@ -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 } diff --git a/src/materialsystem/cmaterialsystem.h b/src/materialsystem/cmaterialsystem.h index 8f599e89..78718d6d 100644 --- a/src/materialsystem/cmaterialsystem.h +++ b/src/materialsystem/cmaterialsystem.h @@ -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); diff --git a/src/public/tier0/threadtools.h b/src/public/tier0/threadtools.h index 73901fff..1ce388aa 100644 --- a/src/public/tier0/threadtools.h +++ b/src/public/tier0/threadtools.h @@ -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 struct CAutoLockTypeDeducer {}; +template struct CAutoLockTypeDeducer {}; template <> struct CAutoLockTypeDeducer { 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(); - g_ThreadServerFrameThreadID = g_GameDll.FindPatternSIMD("83 79 ?? ?? 75 28 8B").FindPatternSelf("8B 05").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - } + virtual void GetVar(void) const { } virtual void GetCon(void) const { } virtual void Detour(const bool /*bAttach*/) const { } }; diff --git a/src/public/tier1/cvar.h b/src/public/tier1/cvar.h index d422470e..c1eefe2d 100644 --- a/src/public/tier1/cvar.h +++ b/src/public/tier1/cvar.h @@ -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; }; diff --git a/src/thirdparty/imgui/CMakeLists.txt b/src/thirdparty/imgui/CMakeLists.txt index 1bec7d7b..d03c6cae 100644 --- a/src/thirdparty/imgui/CMakeLists.txt +++ b/src/thirdparty/imgui/CMakeLists.txt @@ -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" diff --git a/src/thirdparty/imgui/misc/imgui_snapshot.cpp b/src/thirdparty/imgui/misc/imgui_snapshot.cpp new file mode 100644 index 00000000..343dbe90 --- /dev/null +++ b/src/thirdparty/imgui/misc/imgui_snapshot.cpp @@ -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 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); + } +}; diff --git a/src/thirdparty/imgui/misc/imgui_snapshot.h b/src/thirdparty/imgui/misc/imgui_snapshot.h new file mode 100644 index 00000000..c709e302 --- /dev/null +++ b/src/thirdparty/imgui/misc/imgui_snapshot.h @@ -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 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)); } +}; diff --git a/src/tier0/threadtools.cpp b/src/tier0/threadtools.cpp index 7918d6b0..edb33318 100644 --- a/src/tier0/threadtools.cpp +++ b/src/tier0/threadtools.cpp @@ -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* g_ThreadServerFrameThreadID = CModule::GetExportedSymbol(CModule::GetProcessEnvironmentBlock()->ImageBaseAddress, "g_ThreadServerFrameThreadID").RCast(); diff --git a/src/tier1/cvar.cpp b/src/tier1/cvar.cpp index 81c2addd..f9e94928 100644 --- a/src/tier1/cvar.cpp +++ b/src/tier1/cvar.cpp @@ -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* g_pCVar = nullptr; static bool CVar_Connect(CCvar* thisptr, CreateInterfaceFn factory) diff --git a/src/windows/id3dx.cpp b/src/windows/id3dx.cpp index 23459527..19c0d55b 100644 --- a/src/windows/id3dx.cpp +++ b/src/windows/id3dx.cpp @@ -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